- Breaking change: loadSvgString now returns a Promise rather than accepting a callback
- Breaking change: Treat callbacks to `editor.ready` as Promises, only resolving after all resolve - Breaking change: Make `editor.runCallbacks` return a `Promise` which resolves upon all callbacks resolving - Breaking change: Require `npx` (used with `babel-node`) to allow Node files for HTML building and JSDoc type checking to be expressed as ESM. - Breaking change: `addExtension` now throws upon a repeated attempt to add an already-added extension - Breaking change (storage preference cookies): Namespace the cookie as "svgeditstore" instead of just "store" - Breaking change (API): Remove `svgCanvas.rasterExport` fourth (callback) argument, collapsing fifth (options) to fourth - Breaking change (API): Remove `svgCanvas.exportPDF` third (callback) argument - Breaking change (API): `editor/contextmenu.js` `add` now throws instead of giving a console error only upon detecting a bad menuitem or preexisting context menu - Breaking change (API): Remove `svgCanvas.embedImage` second (callback) argument - Breaking change (API): Make `getHelpXML` a class instead of instance method of `RGBColor` - Breaking change (internal API): Refactor `dbox` (and `alert`/`confirm`/`process`/`prompt`/`select`) to avoid a callback argument in favor of return a Promise - Fix: Avoid running in extension `langReady` multiple times or serially - Enhancement (API): Add svgCanvas.runExtension to run just one extension and add `nameFilter` callback to `runExtensions` - Enhancement (API): Supply `$` (our wrapped jQuery) to extensions so can use its plugins, e.g., dbox with its `alert` - Enhancement: Use alert dialog in place of `alert` in webappfind - Enhancement: `editor.ready` now returns a Promise resolving when all callbacks have resolved - Enhancement: Allow `noAlert` option as part of second argument to `loadSvgString` (and `loadFromURL` and `loadFromDataURI`) to avoid UI alert (and trigger promise rejection) - Enhancement: Make `dbox` as a separate module for alert, prompt, etc. dialogs - Refactoring: Internal `PaintBox` as class; other misc. tweaks; no bitwise in canvg - Linting (ESLint): Further linting changes (for editor); rename `.eslintrc` -> `.eslintrc.json` per recommendation - Optimization: Recompress images (imageoptim-cli updated) - npm: Update devDeps - npm: Bump to 4.0.0
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-arrows.js
|
||||
*
|
||||
@@ -13,22 +12,30 @@ export default {
|
||||
const strings = await S.importLocale();
|
||||
const svgEditor = this;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
const $ = jQuery;
|
||||
const // {svgcontent} = S,
|
||||
addElem = svgCanvas.addSVGElementFromJson,
|
||||
{nonce} = S,
|
||||
{nonce, $} = S,
|
||||
prefix = 'se_arrow_';
|
||||
|
||||
let selElems, arrowprefix, randomizeIds = S.randomize_ids;
|
||||
|
||||
function setArrowNonce (window, n) {
|
||||
/**
|
||||
* @param {Window} win
|
||||
* @param {!(string|Integer)} n
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function setArrowNonce (win, n) {
|
||||
randomizeIds = true;
|
||||
arrowprefix = prefix + n + '_';
|
||||
pathdata.fw.id = arrowprefix + 'fw';
|
||||
pathdata.bk.id = arrowprefix + 'bk';
|
||||
}
|
||||
|
||||
function unsetArrowNonce (window) {
|
||||
/**
|
||||
* @param {Window} win
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function unsetArrowNonce (win) {
|
||||
randomizeIds = false;
|
||||
arrowprefix = prefix;
|
||||
pathdata.fw.id = arrowprefix + 'fw';
|
||||
@@ -49,6 +56,12 @@ export default {
|
||||
bk: {d: 'm10,0l-10,5l10,5l-5,-5l5,-5z', refx: 2, id: arrowprefix + 'bk'}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets linked element.
|
||||
* @param {Element} elem
|
||||
* @param {string} attr
|
||||
* @returns {Element}
|
||||
*/
|
||||
function getLinked (elem, attr) {
|
||||
const str = elem.getAttribute(attr);
|
||||
if (!str) { return null; }
|
||||
@@ -59,6 +72,10 @@ export default {
|
||||
return svgCanvas.getElem(m[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} on
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function showPanel (on) {
|
||||
$('#arrow_panel').toggle(on);
|
||||
if (on) {
|
||||
@@ -88,6 +105,10 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function resetMarker () {
|
||||
const el = selElems[0];
|
||||
el.removeAttribute('marker-start');
|
||||
@@ -95,6 +116,12 @@ export default {
|
||||
el.removeAttribute('marker-end');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {"bk"|"fw"} dir
|
||||
* @param {"both"|"mid"|"end"|"start"} type
|
||||
* @param {string} id
|
||||
* @returns {Element}
|
||||
*/
|
||||
function addMarker (dir, type, id) {
|
||||
// TODO: Make marker (or use?) per arrow type, since refX can be different
|
||||
id = id || arrowprefix + dir;
|
||||
@@ -136,6 +163,10 @@ export default {
|
||||
return marker;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function setArrow () {
|
||||
resetMarker();
|
||||
|
||||
@@ -163,6 +194,10 @@ export default {
|
||||
svgCanvas.call('changed', selElems);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} elem
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function colorChanged (elem) {
|
||||
const color = elem.getAttribute('stroke');
|
||||
const mtypes = ['start', 'mid', 'end'];
|
||||
@@ -183,7 +218,7 @@ export default {
|
||||
const attrs = $(this).children().attr(['fill', 'd']);
|
||||
if (attrs.fill === color && attrs.d === curD) {
|
||||
// Found another marker with this color and this path
|
||||
newMarker = this;
|
||||
newMarker = this; // eslint-disable-line consistent-this
|
||||
}
|
||||
});
|
||||
|
||||
@@ -202,14 +237,16 @@ export default {
|
||||
// Check if last marker can be removed
|
||||
let remove = true;
|
||||
$(S.svgcontent).find('line, polyline, path, polygon').each(function () {
|
||||
const elem = this;
|
||||
const element = this; // eslint-disable-line consistent-this
|
||||
$.each(mtypes, function (j, mtype) {
|
||||
if ($(elem).attr('marker-' + mtype) === 'url(#' + marker.id + ')') {
|
||||
if ($(element).attr('marker-' + mtype) === 'url(#' + marker.id + ')') {
|
||||
remove = false;
|
||||
return remove;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
if (!remove) { return false; }
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// Not found, so can safely remove
|
||||
@@ -242,9 +279,9 @@ export default {
|
||||
$('#arrow_list option')[0].id = 'connector_no_arrow';
|
||||
},
|
||||
async addLangData ({lang, importLocale}) {
|
||||
const strings = await importLocale();
|
||||
const {langList} = await importLocale();
|
||||
return {
|
||||
data: strings.langList
|
||||
data: langList
|
||||
};
|
||||
},
|
||||
selectedChanged (opts) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-closepath.js
|
||||
*
|
||||
@@ -13,9 +12,8 @@ import '../svgpathseg.js';
|
||||
// The button toggles whether the path is open or closed
|
||||
export default {
|
||||
name: 'closepath',
|
||||
async init ({importLocale}) {
|
||||
async init ({importLocale, $}) {
|
||||
const strings = await importLocale();
|
||||
const $ = jQuery;
|
||||
const svgEditor = this;
|
||||
let selElems;
|
||||
const updateButton = function (path) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-connector.js
|
||||
*
|
||||
@@ -11,11 +10,10 @@
|
||||
export default {
|
||||
name: 'connector',
|
||||
async init (S) {
|
||||
const $ = jQuery;
|
||||
const svgEditor = this;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
const {getElem} = svgCanvas;
|
||||
const {svgroot, importLocale} = S,
|
||||
const {$, svgroot, importLocale} = S,
|
||||
addElem = svgCanvas.addSVGElementFromJson,
|
||||
selManager = S.selectorManager,
|
||||
connSel = '.se_connector',
|
||||
@@ -34,6 +32,14 @@ export default {
|
||||
connections = [],
|
||||
selElems = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Float} x
|
||||
* @param {Float} y
|
||||
* @param {module:utilities.BBoxObject} bb
|
||||
* @param {Float} offset
|
||||
* @returns {module:math.XYObject}
|
||||
*/
|
||||
function getBBintersect (x, y, bb, offset) {
|
||||
if (offset) {
|
||||
offset -= 0;
|
||||
@@ -66,6 +72,11 @@ export default {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {"start"|"end"} side
|
||||
* @param {Element} line
|
||||
* @returns {Float}
|
||||
*/
|
||||
function getOffset (side, line) {
|
||||
const giveOffset = line.getAttribute('marker-' + side);
|
||||
// const giveOffset = $(line).data(side+'_off');
|
||||
@@ -75,6 +86,10 @@ export default {
|
||||
return giveOffset ? size : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} on
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function showPanel (on) {
|
||||
let connRules = $('#connector_rules');
|
||||
if (!connRules.length) {
|
||||
@@ -84,6 +99,14 @@ export default {
|
||||
$('#connector_panel').toggle(on);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} elem
|
||||
* @param {Integer|"end"} pos
|
||||
* @param {Float} x
|
||||
* @param {Float} y
|
||||
* @param {boolean} [setMid]
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function setPoint (elem, pos, x, y, setMid) {
|
||||
const pts = elem.points;
|
||||
const pt = svgroot.createSVGPoint();
|
||||
@@ -112,6 +135,11 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Float} diffX
|
||||
* @param {Float} diffY
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function updateLine (diffX, diffY) {
|
||||
// Update line with element
|
||||
let i = connections.length;
|
||||
@@ -158,6 +186,10 @@ export default {
|
||||
// Loop through connectors to see if one is connected to the element
|
||||
connectors.each(function () {
|
||||
let addThis;
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function add () {
|
||||
if (elems.includes(this)) {
|
||||
// Pretend this element is selected
|
||||
@@ -205,6 +237,10 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element[]} [elems=selElems]
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function updateConnectors (elems) {
|
||||
// Updates connector lines based on selected elements
|
||||
// Is not used on mousemove, as it runs getStrokedBBox every time,
|
||||
@@ -278,7 +314,10 @@ export default {
|
||||
seNs = svgCanvas.getEditorNS();
|
||||
}());
|
||||
|
||||
// Do on reset
|
||||
/**
|
||||
* Do on reset.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function init () {
|
||||
// Make sure all connectors have data set
|
||||
$(svgcontent).find('*').each(function () {
|
||||
@@ -331,7 +370,7 @@ export default {
|
||||
buttons: strings.buttons.map((button, i) => {
|
||||
return Object.assign(buttons[i], button);
|
||||
}),
|
||||
/* async */ addLangData ({lang, importLocale}) {
|
||||
/* async */ addLangData ({lang}) { // , importLocale: importLoc
|
||||
return {
|
||||
data: strings.langList
|
||||
};
|
||||
@@ -344,7 +383,7 @@ export default {
|
||||
const {curConfig: {initStroke}} = svgEditor;
|
||||
|
||||
if (mode === 'connector') {
|
||||
if (started) { return; }
|
||||
if (started) { return undefined; }
|
||||
|
||||
const mouseTarget = e.target;
|
||||
|
||||
@@ -386,6 +425,7 @@ export default {
|
||||
if (mode === 'select') {
|
||||
findConnectors();
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
mouseMove (opts) {
|
||||
const zoom = svgCanvas.getZoom();
|
||||
@@ -433,7 +473,7 @@ export default {
|
||||
let mouseTarget = e.target;
|
||||
|
||||
if (svgCanvas.getMode() !== 'connector') {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
const fo = $(mouseTarget).closest('foreignObject');
|
||||
if (fo.length) { mouseTarget = fo[0]; }
|
||||
@@ -469,6 +509,7 @@ export default {
|
||||
const dupe = $(svgcontent).find(connSel).filter(function () {
|
||||
const conn = this.getAttributeNS(seNs, 'connector');
|
||||
if (conn === connStr || conn === altStr) { return true; }
|
||||
return false;
|
||||
});
|
||||
if (dupe.length) {
|
||||
$(curLine).remove();
|
||||
@@ -596,7 +637,7 @@ export default {
|
||||
|
||||
// Check validity - the field would be something like 'svg_21 svg_22', but
|
||||
// if one end is missing, it would be 'svg_21' and therefore fail this test
|
||||
if (!/. ./.test(elem.attr['se:connector'])) {
|
||||
if (!(/. ./).test(elem.attr['se:connector'])) {
|
||||
remove.push(elem.attr.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-eyedropper.js
|
||||
*
|
||||
@@ -13,8 +12,7 @@ export default {
|
||||
async init (S) {
|
||||
const strings = await S.importLocale();
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const {ChangeElementCommand} = S, // , svgcontent,
|
||||
const {$, ChangeElementCommand} = S, // , svgcontent,
|
||||
// svgdoc = S.svgroot.parentNode.ownerDocument,
|
||||
svgCanvas = svgEditor.canvas,
|
||||
addToHistory = function (cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd); },
|
||||
@@ -27,6 +25,11 @@ export default {
|
||||
strokeLinejoin: 'miter'
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {module:svgcanvas.SvgCanvas#event:ext-selectedChanged|module:svgcanvas.SvgCanvas#event:ext-elementChanged} opts
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function getStyle (opts) {
|
||||
// if we are in eyedropper mode, we don't want to disable the eye-dropper tool
|
||||
const mode = svgCanvas.getMode();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-foreignobject.js
|
||||
*
|
||||
@@ -12,8 +11,7 @@ export default {
|
||||
name: 'foreignobject',
|
||||
async init (S) {
|
||||
const svgEditor = this;
|
||||
const {text2xml, NS, importLocale} = S;
|
||||
const $ = jQuery;
|
||||
const {$, text2xml, NS, importLocale} = S;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
const
|
||||
// {svgcontent} = S,
|
||||
@@ -27,6 +25,10 @@ export default {
|
||||
$('#svg_source_textarea').css('height', height);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean} on
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function showPanel (on) {
|
||||
let fcRules = $('#fc_rules');
|
||||
if (!fcRules.length) {
|
||||
@@ -36,6 +38,10 @@ export default {
|
||||
$('#foreignObject_panel').toggle(on);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} on
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function toggleSourceButtons (on) {
|
||||
$('#tool_source_save, #tool_source_cancel').toggle(!on);
|
||||
$('#foreign_save, #foreign_cancel').toggle(on);
|
||||
@@ -62,13 +68,18 @@ export default {
|
||||
svgCanvas.call('changed', [elt]);
|
||||
svgCanvas.clearSelection();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
// Todo: Surface error to user
|
||||
console.log(e); // eslint-disable-line no-console
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function showForeignEditor () {
|
||||
const elt = selElems[0];
|
||||
if (!elt || editingforeign) { return; }
|
||||
@@ -83,6 +94,11 @@ export default {
|
||||
$('#svg_source_textarea').focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} attr
|
||||
* @param {string|Float} val
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function setAttr (attr, val) {
|
||||
svgCanvas.changeSelectedAttribute(attr, val);
|
||||
svgCanvas.call('changed', selElems);
|
||||
@@ -167,14 +183,13 @@ export default {
|
||||
// Create source save/cancel buttons
|
||||
/* const save = */ $('#tool_source_save').clone()
|
||||
.hide().attr('id', 'foreign_save').unbind()
|
||||
.appendTo('#tool_source_back').click(function () {
|
||||
.appendTo('#tool_source_back').click(async function () {
|
||||
if (!editingforeign) { return; }
|
||||
|
||||
if (!setForeignString($('#svg_source_textarea').val())) {
|
||||
$.confirm('Errors found. Revert to original?', function (ok) {
|
||||
if (!ok) { return false; }
|
||||
endChanges();
|
||||
});
|
||||
const ok = await $.confirm('Errors found. Revert to original?');
|
||||
if (!ok) { return; }
|
||||
endChanges();
|
||||
} else {
|
||||
endChanges();
|
||||
}
|
||||
@@ -190,50 +205,51 @@ export default {
|
||||
},
|
||||
mouseDown (opts) {
|
||||
// const e = opts.event;
|
||||
|
||||
if (svgCanvas.getMode() === 'foreign') {
|
||||
started = true;
|
||||
newFO = svgCanvas.addSVGElementFromJson({
|
||||
element: 'foreignObject',
|
||||
attr: {
|
||||
x: opts.start_x,
|
||||
y: opts.start_y,
|
||||
id: svgCanvas.getNextId(),
|
||||
'font-size': 16, // cur_text.font_size,
|
||||
width: '48',
|
||||
height: '20',
|
||||
style: 'pointer-events:inherit'
|
||||
}
|
||||
});
|
||||
const m = svgdoc.createElementNS(NS.MATH, 'math');
|
||||
m.setAttributeNS(NS.XMLNS, 'xmlns', NS.MATH);
|
||||
m.setAttribute('display', 'inline');
|
||||
const mi = svgdoc.createElementNS(NS.MATH, 'mi');
|
||||
mi.setAttribute('mathvariant', 'normal');
|
||||
mi.textContent = '\u03A6';
|
||||
const mo = svgdoc.createElementNS(NS.MATH, 'mo');
|
||||
mo.textContent = '\u222A';
|
||||
const mi2 = svgdoc.createElementNS(NS.MATH, 'mi');
|
||||
mi2.textContent = '\u2133';
|
||||
m.append(mi, mo, mi2);
|
||||
newFO.append(m);
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
if (svgCanvas.getMode() !== 'foreign') {
|
||||
return undefined;
|
||||
}
|
||||
started = true;
|
||||
newFO = svgCanvas.addSVGElementFromJson({
|
||||
element: 'foreignObject',
|
||||
attr: {
|
||||
x: opts.start_x,
|
||||
y: opts.start_y,
|
||||
id: svgCanvas.getNextId(),
|
||||
'font-size': 16, // cur_text.font_size,
|
||||
width: '48',
|
||||
height: '20',
|
||||
style: 'pointer-events:inherit'
|
||||
}
|
||||
});
|
||||
const m = svgdoc.createElementNS(NS.MATH, 'math');
|
||||
m.setAttributeNS(NS.XMLNS, 'xmlns', NS.MATH);
|
||||
m.setAttribute('display', 'inline');
|
||||
const mi = svgdoc.createElementNS(NS.MATH, 'mi');
|
||||
mi.setAttribute('mathvariant', 'normal');
|
||||
mi.textContent = '\u03A6';
|
||||
const mo = svgdoc.createElementNS(NS.MATH, 'mo');
|
||||
mo.textContent = '\u222A';
|
||||
const mi2 = svgdoc.createElementNS(NS.MATH, 'mi');
|
||||
mi2.textContent = '\u2133';
|
||||
m.append(mi, mo, mi2);
|
||||
newFO.append(m);
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
},
|
||||
mouseUp (opts) {
|
||||
// const e = opts.event;
|
||||
if (svgCanvas.getMode() === 'foreign' && started) {
|
||||
const attrs = $(newFO).attr(['width', 'height']);
|
||||
const keep = (attrs.width !== '0' || attrs.height !== '0');
|
||||
svgCanvas.addToSelection([newFO], true);
|
||||
|
||||
return {
|
||||
keep,
|
||||
element: newFO
|
||||
};
|
||||
if (svgCanvas.getMode() !== 'foreign' || !started) {
|
||||
return undefined;
|
||||
}
|
||||
const attrs = $(newFO).attr(['width', 'height']);
|
||||
const keep = (attrs.width !== '0' || attrs.height !== '0');
|
||||
svgCanvas.addToSelection([newFO], true);
|
||||
|
||||
return {
|
||||
keep,
|
||||
element: newFO
|
||||
};
|
||||
},
|
||||
selectedChanged (opts) {
|
||||
// Use this to update the current selected elements
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-grid.js
|
||||
*
|
||||
@@ -10,10 +9,9 @@
|
||||
|
||||
export default {
|
||||
name: 'grid',
|
||||
async init ({NS, getTypeMap, importLocale}) {
|
||||
async init ({$, NS, getTypeMap, importLocale}) {
|
||||
const strings = await importLocale();
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
const svgdoc = document.getElementById('svgcanvas').ownerDocument,
|
||||
{assignAttributes} = svgCanvas,
|
||||
@@ -72,6 +70,11 @@ export default {
|
||||
});
|
||||
$('#canvasGrid').append(gridBox);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Float} zoom
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function updateGrid (zoom) {
|
||||
// TODO: Try this with <line> elements, then compare performance difference
|
||||
const unit = units[svgEditor.curConfig.baseUnit]; // 1 = 1px
|
||||
@@ -124,6 +127,10 @@ export default {
|
||||
svgCanvas.setHref(gridimg, datauri);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function gridUpdate () {
|
||||
if (showGrid) {
|
||||
updateGrid(svgCanvas.getZoom());
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-helloworld.js
|
||||
*
|
||||
@@ -15,11 +14,10 @@
|
||||
*/
|
||||
export default {
|
||||
name: 'helloworld',
|
||||
async init ({importLocale}) {
|
||||
async init ({$, importLocale}) {
|
||||
// See `/editor/extensions/ext-locale/helloworld/`
|
||||
const strings = await importLocale();
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
return {
|
||||
name: strings.name,
|
||||
@@ -61,6 +59,7 @@ export default {
|
||||
// a value of true in order for mouseUp to be triggered
|
||||
return {started: true};
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
// This is triggered from anywhere, but "started" must have been set
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-imagelib.js
|
||||
*
|
||||
@@ -9,7 +8,7 @@
|
||||
*/
|
||||
export default {
|
||||
name: 'imagelib',
|
||||
async init ({decode64, importLocale, dropXMLInternalSubset}) {
|
||||
async init ({$, decode64, importLocale, dropXMLInternalSubset}) {
|
||||
const imagelibStrings = await importLocale();
|
||||
|
||||
const modularVersion = !('svgEditor' in window) ||
|
||||
@@ -18,16 +17,17 @@ export default {
|
||||
|
||||
const svgEditor = this;
|
||||
|
||||
const $ = jQuery;
|
||||
const {uiStrings, canvas: svgCanvas, curConfig: {extIconsPath}} = svgEditor;
|
||||
|
||||
imagelibStrings.imgLibs = imagelibStrings.imgLibs.map(({name, url, description}) => {
|
||||
// Todo: Adopt some standard formatting library like `fluent.js` instead
|
||||
url = url
|
||||
.replace(/\{path\}/g, extIconsPath)
|
||||
.replace(/\{modularVersion\}/g, modularVersion
|
||||
? (imagelibStrings.moduleEnding || '-es')
|
||||
: ''
|
||||
.replace(
|
||||
/\{modularVersion\}/g,
|
||||
modularVersion
|
||||
? (imagelibStrings.moduleEnding || '-es')
|
||||
: ''
|
||||
);
|
||||
return {name, url, description};
|
||||
});
|
||||
@@ -39,10 +39,18 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function closeBrowser () {
|
||||
$('#imgbrowse_holder').hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function importImage (url) {
|
||||
const newImage = svgCanvas.addSVGElementFromJson({
|
||||
element: 'image',
|
||||
@@ -68,7 +76,7 @@ export default {
|
||||
let preview, submit;
|
||||
|
||||
// Receive `postMessage` data
|
||||
window.addEventListener('message', function ({origin, data: response}) {
|
||||
window.addEventListener('message', async function ({origin, data: response}) { // eslint-disable-line no-shadow
|
||||
if (!response || !['string', 'object'].includes(typeof response)) {
|
||||
// Do nothing
|
||||
return;
|
||||
@@ -84,7 +92,8 @@ export default {
|
||||
return;
|
||||
}
|
||||
if (!allowedImageLibOrigins.includes('*') && !allowedImageLibOrigins.includes(origin)) {
|
||||
console.log(`Origin ${origin} not whitelisted for posting to ${window.origin}`);
|
||||
// Todo: Surface this error to user?
|
||||
console.log(`Origin ${origin} not whitelisted for posting to ${window.origin}`); // eslint-disable-line no-console
|
||||
return;
|
||||
}
|
||||
const hasName = 'name' in response;
|
||||
@@ -140,12 +149,11 @@ export default {
|
||||
const message = uiStrings.notification.retrieving.replace('%s', name);
|
||||
|
||||
if (mode !== 'm') {
|
||||
$.process_cancel(message, function () {
|
||||
transferStopped = true;
|
||||
// Should a message be sent back to the frame?
|
||||
await $.process_cancel(message);
|
||||
transferStopped = true;
|
||||
// Should a message be sent back to the frame?
|
||||
|
||||
$('#dialog_box').hide();
|
||||
});
|
||||
$('#dialog_box').hide();
|
||||
} else {
|
||||
entry = $('<div>').text(message).data('id', curMeta.id);
|
||||
preview.append(entry);
|
||||
@@ -183,7 +191,7 @@ export default {
|
||||
} else {
|
||||
pending[id].entry.remove();
|
||||
}
|
||||
// $.alert('Unexpected data was returned: ' + response, function() {
|
||||
// await $.alert('Unexpected data was returned: ' + response, function() {
|
||||
// if (mode !== 'm') {
|
||||
// closeBrowser();
|
||||
// } else {
|
||||
@@ -203,7 +211,7 @@ export default {
|
||||
}
|
||||
closeBrowser();
|
||||
break;
|
||||
case 'm':
|
||||
case 'm': {
|
||||
// Import multiple
|
||||
multiArr.push([(svgStr ? 'svg' : 'img'), response]);
|
||||
curMeta = pending[id];
|
||||
@@ -265,20 +273,24 @@ export default {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
} case 'o': {
|
||||
// Open
|
||||
if (!svgStr) { break; }
|
||||
svgEditor.openPrep(function (ok) {
|
||||
if (!ok) { return; }
|
||||
svgCanvas.clear();
|
||||
svgCanvas.setSvgString(response);
|
||||
// updateCanvas();
|
||||
});
|
||||
closeBrowser();
|
||||
const ok = await svgEditor.openPrep();
|
||||
if (!ok) { return; }
|
||||
svgCanvas.clear();
|
||||
svgCanvas.setSvgString(response);
|
||||
// updateCanvas();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
/**
|
||||
* @param {boolean} show
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function toggleMulti (show) {
|
||||
$('#lib_framewrap, #imglib_opts').css({right: (show ? 200 : 10)});
|
||||
if (!preview) {
|
||||
@@ -319,6 +331,10 @@ export default {
|
||||
submit.toggle(show);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function showBrowser () {
|
||||
let browser = $('#imgbrowse');
|
||||
if (!browser.length) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-markers.js
|
||||
*
|
||||
@@ -34,7 +33,7 @@ export default {
|
||||
async init (S) {
|
||||
const strings = await S.importLocale();
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const {$} = S;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
const // {svgcontent} = S,
|
||||
addElem = svgCanvas.addSVGElementFromJson;
|
||||
@@ -94,6 +93,12 @@ export default {
|
||||
return svgCanvas.getElem(m[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {"start"|"mid"|"end"} pos
|
||||
* @param {string} id
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function setIcon (pos, id) {
|
||||
if (id.substr(0, 1) !== '\\') { id = '\\textmarker'; }
|
||||
const ci = '#' + idPrefix + pos + '_' + id.substr(1);
|
||||
@@ -102,8 +107,12 @@ export default {
|
||||
}
|
||||
|
||||
let selElems;
|
||||
// toggles context tool panel off/on
|
||||
// sets the controls with the selected element's settings
|
||||
/**
|
||||
* Toggles context tool panel off/on. Sets the controls with the
|
||||
* selected element's settings.
|
||||
* @param {boolean} on
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function showPanel (on) {
|
||||
$('#marker_panel').toggle(on);
|
||||
|
||||
@@ -135,15 +144,20 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {""|"\\nomarker"|"nomarker"|"leftarrow"|"rightarrow"|"textmarker"|"forwardslash"|"reverseslash"|"verticalslash"|"box"|"star"|"xmark"|"triangle"|"mcircle"} val
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function addMarker (id, val) {
|
||||
const txtBoxBg = '#ffffff';
|
||||
const txtBoxBorder = 'none';
|
||||
const txtBoxStrokeWidth = 0;
|
||||
|
||||
let marker = svgCanvas.getElem(id);
|
||||
if (marker) { return; }
|
||||
if (marker) { return undefined; }
|
||||
|
||||
if (val === '' || val === '\\nomarker') { return; }
|
||||
if (val === '' || val === '\\nomarker') { return undefined; }
|
||||
|
||||
const el = selElems[0];
|
||||
const color = el.getAttribute('stroke');
|
||||
@@ -161,7 +175,7 @@ export default {
|
||||
seType = val.substr(1);
|
||||
} else { seType = 'textmarker'; }
|
||||
|
||||
if (!markerTypes[seType]) { return; } // an unknown type!
|
||||
if (!markerTypes[seType]) { return undefined; } // an unknown type!
|
||||
|
||||
// create a generic marker
|
||||
marker = addElem({
|
||||
@@ -233,6 +247,10 @@ export default {
|
||||
return marker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} elem
|
||||
* @returns {SVGPolylineElement}
|
||||
*/
|
||||
function convertline (elem) {
|
||||
// this routine came from the connectors extension
|
||||
// it is needed because midpoint markers don't work with line elements
|
||||
@@ -275,6 +293,10 @@ export default {
|
||||
return pline;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function setMarker () {
|
||||
const poslist = {start_marker: 'start', mid_marker: 'mid', end_marker: 'end'};
|
||||
const pos = poslist[this.id];
|
||||
@@ -301,8 +323,12 @@ export default {
|
||||
setIcon(pos, val);
|
||||
}
|
||||
|
||||
// called when the main system modifies an object
|
||||
// this routine changes the associated markers to be the same color
|
||||
/**
|
||||
* Called when the main system modifies an object. This routine changes
|
||||
* the associated markers to be the same color.
|
||||
* @param {Element} elem
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function colorChanged (elem) {
|
||||
const color = elem.getAttribute('stroke');
|
||||
|
||||
@@ -319,8 +345,12 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
// called when the main system creates or modifies an object
|
||||
// primary purpose is create new markers for cloned objects
|
||||
/**
|
||||
* Called when the main system creates or modifies an object.
|
||||
* Its primary purpose is to create new markers for cloned objects.
|
||||
* @param {Element} el
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function updateReferences (el) {
|
||||
$.each(mtypes, function (i, pos) {
|
||||
const id = markerPrefix + pos + '_' + el.id;
|
||||
@@ -343,6 +373,11 @@ export default {
|
||||
}
|
||||
|
||||
// simulate a change event a text box that stores the current element's marker type
|
||||
/**
|
||||
* @param {"start"|"mid"|"end"} pos
|
||||
* @param {string} val
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function triggerTextEntry (pos, val) {
|
||||
$('#' + pos + '_marker').val(val);
|
||||
$('#' + pos + '_marker').change();
|
||||
@@ -351,12 +386,17 @@ export default {
|
||||
// else {txtbox.show();}
|
||||
}
|
||||
|
||||
function showTextPrompt (pos) {
|
||||
/**
|
||||
* @param {"start"|"mid"|"end"} pos
|
||||
* @returns {Promise} Resolves to `undefined`
|
||||
*/
|
||||
async function showTextPrompt (pos) {
|
||||
let def = $('#' + pos + '_marker').val();
|
||||
if (def.substr(0, 1) === '\\') { def = ''; }
|
||||
$.prompt('Enter text for ' + pos + ' marker', def, function (txt) {
|
||||
if (txt) { triggerTextEntry(pos, txt); }
|
||||
});
|
||||
const txt = await $.prompt('Enter text for ' + pos + ' marker', def);
|
||||
if (txt) {
|
||||
triggerTextEntry(pos, txt);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -372,18 +412,23 @@ export default {
|
||||
case 'dimension':
|
||||
triggerTextEntry('start','\\leftarrow');
|
||||
triggerTextEntry('end','\\rightarrow');
|
||||
showTextPrompt('mid');
|
||||
await showTextPrompt('mid');
|
||||
break;
|
||||
case 'label':
|
||||
triggerTextEntry('mid','\\nomarker');
|
||||
triggerTextEntry('end','\\rightarrow');
|
||||
showTextPrompt('start');
|
||||
await showTextPrompt('start');
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// callback function for a toolbar button click
|
||||
function setArrowFromButton (obj) {
|
||||
/**
|
||||
* @param {Event} ev
|
||||
* @returns {Promise} Resolves to `undefined`
|
||||
*/
|
||||
async function setArrowFromButton (ev) {
|
||||
const parts = this.id.split('_');
|
||||
const pos = parts[1];
|
||||
let val = parts[2];
|
||||
@@ -392,20 +437,27 @@ export default {
|
||||
if (val !== 'textmarker') {
|
||||
triggerTextEntry(pos, '\\' + val);
|
||||
} else {
|
||||
showTextPrompt(pos);
|
||||
await showTextPrompt(pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {"nomarker"|"leftarrow"|"rightarrow"|"textmarker"|"forwardslash"|"reverseslash"|"verticalslash"|"box"|"star"|"xmark"|"triangle"|"mcircle"} id
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function getTitle (id) {
|
||||
const {langList} = strings;
|
||||
const item = langList.find((item) => {
|
||||
return item.id === id;
|
||||
const item = langList.find((itm) => {
|
||||
return itm.id === id;
|
||||
});
|
||||
return item ? item.title : id;
|
||||
}
|
||||
|
||||
// build the toolbar button array from the marker definitions
|
||||
function buildButtonList (lang) {
|
||||
/**
|
||||
* Build the toolbar button array from the marker definitions.
|
||||
* @returns {module:SVGEditor.Button[]}
|
||||
*/
|
||||
function buildButtonList () {
|
||||
const buttons = [];
|
||||
// const i = 0;
|
||||
/*
|
||||
@@ -500,7 +552,7 @@ export default {
|
||||
callback () {
|
||||
$('#marker_panel').addClass('toolset').hide();
|
||||
},
|
||||
async addLangData ({importLocale, lang}) {
|
||||
/* async */ addLangData ({importLocale, lang}) {
|
||||
return {data: strings.langList};
|
||||
},
|
||||
selectedChanged (opts) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* globals jQuery, MathJax */
|
||||
/* globals MathJax */
|
||||
/**
|
||||
* ext-mathjax.js
|
||||
*
|
||||
@@ -12,10 +12,9 @@ import {importScript} from '../external/dynamic-import-polyfill/importModule.js'
|
||||
|
||||
export default {
|
||||
name: 'mathjax',
|
||||
async init ({importLocale}) {
|
||||
async init ({$, importLocale}) {
|
||||
const strings = await importLocale();
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
|
||||
// Configuration of the MathJax extention.
|
||||
@@ -54,7 +53,8 @@ export default {
|
||||
locationY,
|
||||
mathjaxLoaded = false;
|
||||
|
||||
// TODO: Implement language support. Move these uiStrings to the locale files and the code to the langReady callback.
|
||||
// TODO: Implement language support. Move these uiStrings to the locale files and
|
||||
// the code to the langReady callback. Also i18nize alert and HTML below
|
||||
$.extend(uiStrings, {
|
||||
mathjax: {
|
||||
embed_svg: 'Save as mathematics',
|
||||
@@ -65,6 +65,10 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function saveMath () {
|
||||
const code = $('#mathjax_code_textarea').val();
|
||||
// displaystyle to force MathJax NOT to use the inline style. Because it is
|
||||
@@ -122,11 +126,15 @@ export default {
|
||||
type: 'mode',
|
||||
icon: svgEditor.curConfig.extIconsPath + 'mathjax.png',
|
||||
events: {
|
||||
click () {
|
||||
async click () {
|
||||
// Set the mode.
|
||||
svgCanvas.setMode('mathjax');
|
||||
|
||||
// Only load Mathjax when needed, we don't want to strain Svg-Edit any more.
|
||||
// From this point on it is very probable that it will be needed, so load it.
|
||||
if (mathjaxLoaded === false) {
|
||||
$('<div id="mathjax">' +
|
||||
$(
|
||||
'<div id="mathjax">' +
|
||||
'<!-- Here is where MathJax creates the math -->' +
|
||||
'<div id="mathjax_creator" class="tex2jax_process" style="display:none">' +
|
||||
'$${}$$' +
|
||||
@@ -191,21 +199,20 @@ export default {
|
||||
*/
|
||||
// We use `extIconsPath` here for now as it does not vary with
|
||||
// the modular type as does `extPath`
|
||||
importScript(svgEditor.curConfig.extIconsPath + mathjaxSrcSecure).then(() => {
|
||||
try {
|
||||
await importScript(svgEditor.curConfig.extIconsPath + mathjaxSrcSecure);
|
||||
// When MathJax is loaded get the div where the math will be rendered.
|
||||
MathJax.Hub.queue.Push(function () {
|
||||
math = MathJax.Hub.getAllJax('#mathjax_creator')[0];
|
||||
console.log(math);
|
||||
console.log(math); // eslint-disable-line no-console
|
||||
mathjaxLoaded = true;
|
||||
console.log('MathJax Loaded');
|
||||
console.log('MathJax Loaded'); // eslint-disable-line no-console
|
||||
});
|
||||
}).catch(() => {
|
||||
console.log('Failed loadeing MathJax.');
|
||||
} catch (e) {
|
||||
console.log('Failed loading MathJax.'); // eslint-disable-line no-console
|
||||
$.alert('Failed loading MathJax. You will not be able to change the mathematics.');
|
||||
});
|
||||
}
|
||||
}
|
||||
// Set the mode.
|
||||
svgCanvas.setMode('mathjax');
|
||||
}
|
||||
}
|
||||
}];
|
||||
@@ -221,6 +228,7 @@ export default {
|
||||
if (svgCanvas.getMode() === 'mathjax') {
|
||||
return {started: true};
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
mouseUp (opts) {
|
||||
if (svgCanvas.getMode() === 'mathjax') {
|
||||
@@ -233,6 +241,7 @@ export default {
|
||||
$('#mathjax').show();
|
||||
return {started: false}; // Otherwise the last selected object dissapears.
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
callback () {
|
||||
$('<style>').text(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-overview_window.js
|
||||
*
|
||||
@@ -9,8 +8,7 @@
|
||||
*/
|
||||
export default {
|
||||
name: 'overview_window',
|
||||
init ({isChrome, isIE}) {
|
||||
const $ = jQuery;
|
||||
init ({$, isChrome, isIE}) {
|
||||
const overviewWindowGlobals = {};
|
||||
// Disabled in Chrome 48-, see https://github.com/SVG-Edit/svgedit/issues/26 and
|
||||
// https://code.google.com/p/chromium/issues/detail?id=565120.
|
||||
@@ -18,7 +16,7 @@ export default {
|
||||
const verIndex = navigator.userAgent.indexOf('Chrome/') + 7;
|
||||
const chromeVersion = parseInt(navigator.userAgent.substring(verIndex));
|
||||
if (chromeVersion < 49) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ export default {
|
||||
svgEditor.setPanning(true);
|
||||
return {started: true};
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
mouseUp () {
|
||||
if (svgCanvas.getMode() === 'ext-panning') {
|
||||
@@ -45,6 +46,7 @@ export default {
|
||||
element: null
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
/* globals jQuery */
|
||||
// TODO: Might add support for "exportImage" custom
|
||||
// handler as in "ext-server_opensave.js" (and in savefile.php)
|
||||
|
||||
export default {
|
||||
name: 'php_savefile',
|
||||
init () {
|
||||
init ({$}) {
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
/**
|
||||
* Get file name out of SVGEdit document title.
|
||||
* @returns {string}
|
||||
*/
|
||||
function getFileNameFromTitle () {
|
||||
const title = svgCanvas.getDocumentTitle();
|
||||
return title.trim();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-polygon.js
|
||||
*
|
||||
@@ -10,9 +9,8 @@ export default {
|
||||
name: 'polygon',
|
||||
async init (S) {
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
const {importLocale} = S, // {svgcontent}
|
||||
const {$, importLocale} = S, // {svgcontent}
|
||||
// addElem = svgCanvas.addSVGElementFromJson,
|
||||
editingitex = false;
|
||||
const strings = await importLocale();
|
||||
@@ -37,6 +35,11 @@ export default {
|
||||
const height = $('#svg_source_container').height() - 80;
|
||||
$('#svg_source_textarea').css('height', height);
|
||||
}; */
|
||||
|
||||
/**
|
||||
* @param {boolean} on
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function showPanel (on) {
|
||||
let fcRules = $('#fc_rules');
|
||||
if (!fcRules.length) {
|
||||
@@ -53,15 +56,28 @@ export default {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} attr
|
||||
* @param {string|Float} val
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function setAttr (attr, val) {
|
||||
svgCanvas.changeSelectedAttribute(attr, val);
|
||||
svgCanvas.call('changed', selElems);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Float} n
|
||||
* @returns {Float}
|
||||
*/
|
||||
function cot (n) {
|
||||
return 1 / Math.tan(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Float} n
|
||||
* @returns {Float}
|
||||
*/
|
||||
function sec (n) {
|
||||
return 1 / Math.cos(n);
|
||||
}
|
||||
@@ -148,6 +164,7 @@ export default {
|
||||
$('#polygon_panel').hide();
|
||||
|
||||
const endChanges = function () {
|
||||
// Todo: Missing?
|
||||
};
|
||||
|
||||
// TODO: Needs to be done after orig icon loads
|
||||
@@ -160,12 +177,11 @@ export default {
|
||||
// Todo: Uncomment the setItexString() function above and handle ajaxEndpoint?
|
||||
/*
|
||||
if (!setItexString($('#svg_source_textarea').val())) {
|
||||
$.confirm('Errors found. Revert to original?', function (ok) {
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
endChanges();
|
||||
});
|
||||
const ok = await $.confirm('Errors found. Revert to original?', function (ok) {
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
endChanges();
|
||||
} else { */
|
||||
endChanges();
|
||||
// }
|
||||
@@ -178,6 +194,9 @@ export default {
|
||||
}, 3000);
|
||||
},
|
||||
mouseDown (opts) {
|
||||
if (svgCanvas.getMode() !== 'polygon') {
|
||||
return undefined;
|
||||
}
|
||||
// const e = opts.event;
|
||||
const rgb = svgCanvas.getColor('fill');
|
||||
// const ccRgbEl = rgb.substring(1, rgb.length);
|
||||
@@ -185,79 +204,76 @@ export default {
|
||||
// ccSRgbEl = sRgb.substring(1, rgb.length);
|
||||
const sWidth = svgCanvas.getStrokeWidth();
|
||||
|
||||
if (svgCanvas.getMode() === 'polygon') {
|
||||
started = true;
|
||||
started = true;
|
||||
|
||||
newFO = svgCanvas.addSVGElementFromJson({
|
||||
element: 'polygon',
|
||||
attr: {
|
||||
cx: opts.start_x,
|
||||
cy: opts.start_y,
|
||||
id: svgCanvas.getNextId(),
|
||||
shape: 'regularPoly',
|
||||
sides: document.getElementById('polySides').value,
|
||||
orient: 'x',
|
||||
edge: 0,
|
||||
fill: rgb,
|
||||
strokecolor: sRgb,
|
||||
strokeWidth: sWidth
|
||||
}
|
||||
});
|
||||
newFO = svgCanvas.addSVGElementFromJson({
|
||||
element: 'polygon',
|
||||
attr: {
|
||||
cx: opts.start_x,
|
||||
cy: opts.start_y,
|
||||
id: svgCanvas.getNextId(),
|
||||
shape: 'regularPoly',
|
||||
sides: document.getElementById('polySides').value,
|
||||
orient: 'x',
|
||||
edge: 0,
|
||||
fill: rgb,
|
||||
strokecolor: sRgb,
|
||||
strokeWidth: sWidth
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
},
|
||||
mouseMove (opts) {
|
||||
if (!started) {
|
||||
return;
|
||||
if (!started || svgCanvas.getMode() !== 'polygon') {
|
||||
return undefined;
|
||||
}
|
||||
if (svgCanvas.getMode() === 'polygon') {
|
||||
// const e = opts.event;
|
||||
const c = $(newFO).attr(['cx', 'cy', 'sides', 'orient', 'fill', 'strokecolor', 'strokeWidth']);
|
||||
let x = opts.mouse_x;
|
||||
let y = opts.mouse_y;
|
||||
const {cx, cy, fill, strokecolor, strokeWidth, sides} = c, // {orient} = c,
|
||||
edg = (Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy))) / 1.5;
|
||||
newFO.setAttribute('edge', edg);
|
||||
// const e = opts.event;
|
||||
const c = $(newFO).attr(['cx', 'cy', 'sides', 'orient', 'fill', 'strokecolor', 'strokeWidth']);
|
||||
let x = opts.mouse_x;
|
||||
let y = opts.mouse_y;
|
||||
const {cx, cy, fill, strokecolor, strokeWidth, sides} = c, // {orient} = c,
|
||||
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 = '';
|
||||
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 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;
|
||||
|
||||
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('transform', 'rotate(-90)');
|
||||
// const shape = newFO.getAttribute('shape');
|
||||
// newFO.append(poly);
|
||||
// DrawPoly(cx, cy, sides, edg, orient);
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
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('transform', 'rotate(-90)');
|
||||
// const shape = newFO.getAttribute('shape');
|
||||
// newFO.append(poly);
|
||||
// DrawPoly(cx, cy, sides, edg, orient);
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
},
|
||||
|
||||
mouseUp (opts) {
|
||||
if (svgCanvas.getMode() === 'polygon') {
|
||||
const attrs = $(newFO).attr('edge');
|
||||
const keep = (attrs.edge !== '0');
|
||||
// svgCanvas.addToSelection([newFO], true);
|
||||
return {
|
||||
keep,
|
||||
element: newFO
|
||||
};
|
||||
if (svgCanvas.getMode() !== 'polygon') {
|
||||
return undefined;
|
||||
}
|
||||
const attrs = $(newFO).attr('edge');
|
||||
const keep = (attrs.edge !== '0');
|
||||
// svgCanvas.addToSelection([newFO], true);
|
||||
return {
|
||||
keep,
|
||||
element: newFO
|
||||
};
|
||||
},
|
||||
selectedChanged (opts) {
|
||||
// Use this to update the current selected elements
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-server_moinsave.js
|
||||
*
|
||||
@@ -12,10 +11,9 @@ import {canvg} from '../canvg/canvg.js';
|
||||
|
||||
export default {
|
||||
name: 'server_moinsave',
|
||||
async init ({encode64, importLocale}) {
|
||||
async init ({$, encode64, importLocale}) {
|
||||
const strings = await importLocale();
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
const saveSvgAction = '/+modify';
|
||||
|
||||
@@ -48,7 +46,7 @@ export default {
|
||||
.append('<input type="hidden" name="contenttype" value="application/x-svgdraw">')
|
||||
.appendTo('body')
|
||||
.submit().remove();
|
||||
alert(strings.saved);
|
||||
$.alert(strings.saved);
|
||||
top.window.location = '/' + name;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-server_opensave.js
|
||||
*
|
||||
@@ -11,19 +10,35 @@ import {canvg} from '../canvg/canvg.js';
|
||||
|
||||
export default {
|
||||
name: 'server_opensave',
|
||||
async init ({decode64, encode64, importLocale}) {
|
||||
async init ({$, decode64, encode64, importLocale}) {
|
||||
const strings = await importLocale();
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function getFileNameFromTitle () {
|
||||
const title = svgCanvas.getDocumentTitle();
|
||||
// We convert (to underscore) only those disallowed Win7 file name characters
|
||||
return title.trim().replace(/[/\\:*?"<>|]/g, '_');
|
||||
}
|
||||
/**
|
||||
* Escapes XML predefined entities for quoted attributes.
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function xhtmlEscape (str) {
|
||||
return str.replace(/&(?!amp;)/g, '&').replace(/"/g, '"').replace(/</g, '<'); // < is actually disallowed above anyways
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} [filename='image']
|
||||
* @param {string} suffix To add to file name
|
||||
* @param {string} uri
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function clientDownloadSupport (filename, suffix, uri) {
|
||||
const support = $('<a>')[0].download === '';
|
||||
let a;
|
||||
@@ -35,6 +50,7 @@ export default {
|
||||
a[0].click();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const
|
||||
saveSvgAction = svgEditor.curConfig.extPath + 'filesave.php',
|
||||
@@ -101,7 +117,7 @@ export default {
|
||||
}
|
||||
|
||||
if (note.length) {
|
||||
alert(note);
|
||||
await $.alert(note);
|
||||
}
|
||||
|
||||
const filename = getFileNameFromTitle();
|
||||
@@ -178,36 +194,45 @@ export default {
|
||||
|
||||
// It appears necessary to rebuild this input every time a file is
|
||||
// selected so the same file can be picked and the change event can fire.
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {external:jQuery} form
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function rebuildInput (form) {
|
||||
form.empty();
|
||||
const inp = $('<input type="file" name="svg_file">').appendTo(form);
|
||||
|
||||
function submit () {
|
||||
// This submits the form, which returns the file data using svgEditor.processFile()
|
||||
/**
|
||||
* Submit the form, empty its contents for reuse and show
|
||||
* uploading message.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function submit () {
|
||||
// This submits the form, which returns the file data using `svgEditor.processFile()`
|
||||
form.submit();
|
||||
|
||||
rebuildInput(form);
|
||||
$.process_cancel(strings.uploading, function () {
|
||||
cancelled = true;
|
||||
$('#dialog_box').hide();
|
||||
});
|
||||
await $.process_cancel(strings.uploading);
|
||||
cancelled = true;
|
||||
$('#dialog_box').hide();
|
||||
}
|
||||
|
||||
if (form[0] === openSvgForm[0]) {
|
||||
inp.change(function () {
|
||||
inp.change(async function () {
|
||||
// This takes care of the "are you sure" dialog box
|
||||
svgEditor.openPrep(function (ok) {
|
||||
if (!ok) {
|
||||
rebuildInput(form);
|
||||
return;
|
||||
}
|
||||
submit();
|
||||
});
|
||||
const ok = await svgEditor.openPrep();
|
||||
if (!ok) {
|
||||
rebuildInput(form);
|
||||
return;
|
||||
}
|
||||
await submit();
|
||||
});
|
||||
} else {
|
||||
inp.change(function () {
|
||||
inp.change(async function () {
|
||||
// This submits the form, which returns the file data using svgEditor.processFile()
|
||||
submit();
|
||||
await submit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-shapes.js
|
||||
*
|
||||
@@ -9,10 +8,9 @@
|
||||
*/
|
||||
export default {
|
||||
name: 'shapes',
|
||||
async init ({importLocale}) {
|
||||
async init ({$, importLocale}) {
|
||||
const strings = await importLocale();
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const canv = svgEditor.canvas;
|
||||
const svgroot = canv.getRootElem();
|
||||
let lastBBox = {};
|
||||
@@ -63,10 +61,26 @@ export default {
|
||||
let currentD, curShapeId, curShape, startX, startY;
|
||||
let curLib = library.basic;
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function loadIcons () {
|
||||
$('#shape_buttons').empty().append(curLib.buttons);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {PlainObject} module:Extension.Shapes.Shapes
|
||||
* @property {PlainObject.<string, string>} data
|
||||
* @property {Integer} [size]
|
||||
* @property {boolean} [fill]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string|"basic"} cat Category ID
|
||||
* @param {module:Extension.Shapes.Shapes} shapes
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function makeButtons (cat, shapes) {
|
||||
const size = curLib.size || 300;
|
||||
const fill = curLib.fill || false;
|
||||
@@ -78,7 +92,8 @@ export default {
|
||||
'<svg viewBox="' + vb + '">' +
|
||||
'<path fill="' + (fill ? '#333' : 'none') +
|
||||
'" stroke="#000" stroke-width="' + stroke + '" /></svg></svg>',
|
||||
'text/xml');
|
||||
'text/xml'
|
||||
);
|
||||
|
||||
const width = 24;
|
||||
const height = 24;
|
||||
@@ -88,9 +103,7 @@ export default {
|
||||
|
||||
const {data} = shapes;
|
||||
|
||||
curLib.buttons = [];
|
||||
for (const id in data) {
|
||||
const pathD = data[id];
|
||||
curLib.buttons = Object.entries(data).map(([id, pathD]) => {
|
||||
const icon = svgElem.clone();
|
||||
icon.find('path').attr('d', pathD);
|
||||
|
||||
@@ -99,10 +112,14 @@ export default {
|
||||
title: id
|
||||
});
|
||||
// Store for later use
|
||||
curLib.buttons.push(iconBtn[0]);
|
||||
}
|
||||
return iconBtn[0];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string|"basic"} catId
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function loadLibrary (catId) {
|
||||
const lib = library[catId];
|
||||
|
||||
@@ -141,33 +158,34 @@ export default {
|
||||
return Object.assign(buttons[i], button);
|
||||
}),
|
||||
callback () {
|
||||
$('<style>').text(
|
||||
'#shape_buttons {' +
|
||||
'overflow: auto;' +
|
||||
'width: 180px;' +
|
||||
'max-height: 300px;' +
|
||||
'display: table-cell;' +
|
||||
'vertical-align: middle;' +
|
||||
'}' +
|
||||
'#shape_cats {' +
|
||||
'min-width: 110px;' +
|
||||
'display: table-cell;' +
|
||||
'vertical-align: middle;' +
|
||||
'height: 300px;' +
|
||||
'}' +
|
||||
'#shape_cats > div {' +
|
||||
'line-height: 1em;' +
|
||||
'padding: .5em;' +
|
||||
'border:1px solid #B0B0B0;' +
|
||||
'background: #E8E8E8;' +
|
||||
'margin-bottom: -1px;' +
|
||||
'}' +
|
||||
'#shape_cats div:hover {' +
|
||||
'background: #FFFFCC;' +
|
||||
'}' +
|
||||
'#shape_cats div.current {' +
|
||||
'font-weight: bold;' +
|
||||
'}').appendTo('head');
|
||||
$('<style>').text(`
|
||||
#shape_buttons {
|
||||
overflow: auto;
|
||||
width: 180px;
|
||||
max-height: 300px;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#shape_cats {
|
||||
min-width: 110px;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
height: 300px;
|
||||
}
|
||||
#shape_cats > div {
|
||||
line-height: 1em;
|
||||
padding: .5em;
|
||||
border:1px solid #B0B0B0;
|
||||
background: #E8E8E8;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
#shape_cats div:hover {
|
||||
background: #FFFFCC;
|
||||
}
|
||||
#shape_cats div.current {
|
||||
font-weight: bold;
|
||||
}
|
||||
`).appendTo('head');
|
||||
|
||||
const btnDiv = $('<div id="shape_buttons">');
|
||||
$('#tools_shapelib > *').wrapAll(btnDiv);
|
||||
@@ -229,14 +247,14 @@ export default {
|
||||
});
|
||||
// Now add shape categories from locale
|
||||
const cats = {};
|
||||
for (const o in categories) {
|
||||
cats['#shape_cats [data-cat="' + o + '"]'] = categories[o];
|
||||
}
|
||||
Object.entries(categories).forEach(([o, categoryName]) => {
|
||||
cats['#shape_cats [data-cat="' + o + '"]'] = categoryName;
|
||||
});
|
||||
this.setStrings('content', cats);
|
||||
},
|
||||
mouseDown (opts) {
|
||||
const mode = canv.getMode();
|
||||
if (mode !== modeId) { return; }
|
||||
if (mode !== modeId) { return undefined; }
|
||||
|
||||
startX = opts.start_x;
|
||||
const x = startX;
|
||||
@@ -259,7 +277,7 @@ export default {
|
||||
});
|
||||
|
||||
// Make sure shape uses absolute values
|
||||
if (/[a-z]/.test(currentD)) {
|
||||
if ((/[a-z]/).test(currentD)) {
|
||||
currentD = curLib.data[curShapeId] = canv.pathActions.convertPath(curShape);
|
||||
curShape.setAttribute('d', currentD);
|
||||
canv.pathActions.fixEnd(curShape);
|
||||
@@ -343,7 +361,7 @@ export default {
|
||||
},
|
||||
mouseUp (opts) {
|
||||
const mode = canv.getMode();
|
||||
if (mode !== modeId) { return; }
|
||||
if (mode !== modeId) { return undefined; }
|
||||
|
||||
const keepObject = (opts.event.clientX !== startClientPos.x && opts.event.clientY !== startClientPos.y);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-star.js
|
||||
*
|
||||
@@ -10,10 +9,9 @@ export default {
|
||||
name: 'star',
|
||||
async init (S) {
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
|
||||
const {importLocale} = S; // {svgcontent},
|
||||
const {$, importLocale} = S; // {svgcontent},
|
||||
let
|
||||
selElems,
|
||||
// editingitex = false,
|
||||
@@ -25,6 +23,12 @@ export default {
|
||||
// undoCommand = 'Not image',
|
||||
// modeChangeG, ccZoom, wEl, hEl, wOffset, hOffset, ccRgbEl, brushW, brushH;
|
||||
const strings = await importLocale();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {boolean} on
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function showPanel (on) {
|
||||
let fcRules = $('#fc_rules');
|
||||
if (!fcRules.length) {
|
||||
@@ -40,6 +44,12 @@ export default {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} attr
|
||||
* @param {string|Float} val
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function setAttr (attr, val) {
|
||||
svgCanvas.changeSelectedAttribute(attr, val);
|
||||
svgCanvas.call('changed', selElems);
|
||||
@@ -140,10 +150,11 @@ export default {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
mouseMove (opts) {
|
||||
if (!started) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
if (svgCanvas.getMode() === 'star') {
|
||||
const c = $(newFO).attr(['cx', 'cy', 'point', 'orient', 'fill', 'strokecolor', 'strokeWidth', 'radialshift']);
|
||||
@@ -195,6 +206,7 @@ export default {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
mouseUp () {
|
||||
if (svgCanvas.getMode() === 'star') {
|
||||
@@ -205,6 +217,7 @@ export default {
|
||||
element: newFO
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
selectedChanged (opts) {
|
||||
// Use this to update the current selected elements
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* ext-storage.js
|
||||
*
|
||||
@@ -23,9 +22,8 @@
|
||||
*/
|
||||
export default {
|
||||
name: 'storage',
|
||||
init () {
|
||||
init ({$}) {
|
||||
const svgEditor = this;
|
||||
const $ = jQuery;
|
||||
const svgCanvas = svgEditor.canvas;
|
||||
|
||||
// We could empty any already-set data for users when they decline storage,
|
||||
@@ -49,6 +47,11 @@ export default {
|
||||
} = svgEditor.curConfig;
|
||||
const {storage, updateCanvas} = svgEditor;
|
||||
|
||||
/**
|
||||
* Replace `storagePrompt` parameter within URL.
|
||||
* @param {string} val
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function replaceStoragePrompt (val) {
|
||||
val = val ? 'storagePrompt=' + val : '';
|
||||
const loc = top.location; // Allow this to work with the embedded editor as well
|
||||
@@ -60,6 +63,13 @@ export default {
|
||||
loc.href += (loc.href.includes('?') ? '&' : '?') + val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets SVG content as a string with "svgedit-" and the current
|
||||
* canvas name as namespace.
|
||||
* @param {string} val
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function setSVGContentStorage (val) {
|
||||
if (storage) {
|
||||
const name = 'svgedit-' + svgEditor.curConfig.canvasName;
|
||||
@@ -71,25 +81,36 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cookie to expire.
|
||||
* @param {string} cookie
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function expireCookie (cookie) {
|
||||
document.cookie = encodeURIComponent(cookie) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire the storage cookie.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function removeStoragePrefCookie () {
|
||||
expireCookie('store');
|
||||
expireCookie('svgeditstore');
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties storage for each of the current preferences.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function emptyStorage () {
|
||||
setSVGContentStorage('');
|
||||
for (let name in svgEditor.curPrefs) {
|
||||
if (svgEditor.curPrefs.hasOwnProperty(name)) {
|
||||
name = 'svg-edit-' + name;
|
||||
if (storage) {
|
||||
storage.removeItem(name);
|
||||
}
|
||||
expireCookie(name);
|
||||
Object.keys(svgEditor.curPrefs).forEach((name) => {
|
||||
name = 'svg-edit-' + name;
|
||||
if (storage) {
|
||||
storage.removeItem(name);
|
||||
}
|
||||
}
|
||||
expireCookie(name);
|
||||
});
|
||||
}
|
||||
|
||||
// emptyStorage();
|
||||
@@ -106,10 +127,10 @@ export default {
|
||||
function setupBeforeUnloadListener () {
|
||||
window.addEventListener('beforeunload', function (e) {
|
||||
// Don't save anything unless the user opted in to storage
|
||||
if (!document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/)) {
|
||||
if (!document.cookie.match(/(?:^|;\s*)svgeditstore=(?:prefsAndContent|prefsOnly)/)) {
|
||||
return;
|
||||
}
|
||||
if (document.cookie.match(/(?:^|;\s*)store=prefsAndContent/)) {
|
||||
if (document.cookie.match(/(?:^|;\s*)svgeditstore=prefsAndContent/)) {
|
||||
setSVGContentStorage(svgCanvas.getSvgString());
|
||||
}
|
||||
|
||||
@@ -118,24 +139,21 @@ export default {
|
||||
|
||||
const {curPrefs} = svgEditor;
|
||||
|
||||
for (let key in curPrefs) {
|
||||
if (curPrefs.hasOwnProperty(key)) { // It's our own config, so we don't need to iterate up the prototype chain
|
||||
let val = curPrefs[key];
|
||||
const store = (val !== undefined);
|
||||
key = 'svg-edit-' + key;
|
||||
if (!store) {
|
||||
continue;
|
||||
}
|
||||
if (storage) {
|
||||
storage.setItem(key, val);
|
||||
} else if (window.widget) {
|
||||
window.widget.setPreferenceForKey(val, key);
|
||||
} else {
|
||||
val = encodeURIComponent(val);
|
||||
document.cookie = encodeURIComponent(key) + '=' + val + '; expires=Fri, 31 Dec 9999 23:59:59 GMT';
|
||||
}
|
||||
Object.entries(curPrefs).forEach(([key, val]) => {
|
||||
const store = (val !== undefined);
|
||||
key = 'svg-edit-' + key;
|
||||
if (!store) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (storage) {
|
||||
storage.setItem(key, val);
|
||||
} else if (window.widget) {
|
||||
window.widget.setPreferenceForKey(val, key);
|
||||
} else {
|
||||
val = encodeURIComponent(val);
|
||||
document.cookie = encodeURIComponent(key) + '=' + val + '; expires=Fri, 31 Dec 9999 23:59:59 GMT';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -175,7 +193,7 @@ export default {
|
||||
// continual prompts about it)...
|
||||
storagePrompt !== false &&
|
||||
// ...and this user hasn't previously indicated a desire for storage
|
||||
!document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/)
|
||||
!document.cookie.match(/(?:^|;\s*)svgeditstore=(?:prefsAndContent|prefsOnly)/)
|
||||
)
|
||||
// ...then show the storage prompt.
|
||||
)) {
|
||||
@@ -205,60 +223,10 @@ export default {
|
||||
|
||||
// Open select-with-checkbox dialog
|
||||
// From svg-editor.js
|
||||
$.select(
|
||||
svgEditor.storagePromptState = 'waiting';
|
||||
const {response: pref, checked} = await $.select(
|
||||
message,
|
||||
options,
|
||||
function (pref, checked) {
|
||||
if (pref && pref !== 'noPrefsOrContent') {
|
||||
// Regardless of whether the user opted
|
||||
// to remember the choice (and move to a URL which won't
|
||||
// ask them again), we have to assume the user
|
||||
// doesn't even want to remember their not wanting
|
||||
// storage, so we don't set the cookie or continue on with
|
||||
// setting storage on beforeunload
|
||||
document.cookie = 'store=' + encodeURIComponent(pref) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'; // 'prefsAndContent' | 'prefsOnly'
|
||||
// If the URL was configured to always insist on a prompt, if
|
||||
// the user does indicate a wish to store their info, we
|
||||
// don't want ask them again upon page refresh so move
|
||||
// them instead to a URL which does not always prompt
|
||||
if (storagePrompt === true && checked) {
|
||||
replaceStoragePrompt();
|
||||
return;
|
||||
}
|
||||
} else { // The user does not wish storage (or cancelled, which we treat equivalently)
|
||||
removeStoragePrefCookie();
|
||||
if (pref && // If the user explicitly expresses wish for no storage
|
||||
emptyStorageOnDecline
|
||||
) {
|
||||
emptyStorage();
|
||||
}
|
||||
if (pref && checked) {
|
||||
// Open a URL which won't set storage and won't prompt user about storage
|
||||
replaceStoragePrompt('false');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset width/height of dialog (e.g., for use by Export)
|
||||
$('#dialog_container')[0].style.width = oldContainerWidth;
|
||||
$('#dialog_container')[0].style.marginLeft = oldContainerMarginLeft;
|
||||
$('#dialog_content')[0].style.height = oldContentHeight;
|
||||
$('#dialog_container')[0].style.height = oldContainerHeight;
|
||||
|
||||
// It should be enough to (conditionally) add to storage on
|
||||
// beforeunload, but if we wished to update immediately,
|
||||
// we might wish to try setting:
|
||||
// svgEditor.setConfig({noStorageOnLoad: true});
|
||||
// and then call:
|
||||
// svgEditor.loadContentAndPrefs();
|
||||
|
||||
// We don't check for noStorageOnLoad here because
|
||||
// the prompt gives the user the option to store data
|
||||
setupBeforeUnloadListener();
|
||||
|
||||
svgEditor.storagePromptState = 'closed';
|
||||
updateCanvas(true);
|
||||
},
|
||||
null,
|
||||
null,
|
||||
{
|
||||
@@ -267,7 +235,55 @@ export default {
|
||||
tooltip: rememberTooltip
|
||||
}
|
||||
);
|
||||
svgEditor.storagePromptState = 'waiting';
|
||||
if (pref && pref !== 'noPrefsOrContent') {
|
||||
// Regardless of whether the user opted
|
||||
// to remember the choice (and move to a URL which won't
|
||||
// ask them again), we have to assume the user
|
||||
// doesn't even want to remember their not wanting
|
||||
// storage, so we don't set the cookie or continue on with
|
||||
// setting storage on beforeunload
|
||||
document.cookie = 'svgeditstore=' + encodeURIComponent(pref) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'; // 'prefsAndContent' | 'prefsOnly'
|
||||
// If the URL was configured to always insist on a prompt, if
|
||||
// the user does indicate a wish to store their info, we
|
||||
// don't want ask them again upon page refresh so move
|
||||
// them instead to a URL which does not always prompt
|
||||
if (storagePrompt === true && checked) {
|
||||
replaceStoragePrompt();
|
||||
return;
|
||||
}
|
||||
} else { // The user does not wish storage (or cancelled, which we treat equivalently)
|
||||
removeStoragePrefCookie();
|
||||
if (pref && // If the user explicitly expresses wish for no storage
|
||||
emptyStorageOnDecline
|
||||
) {
|
||||
emptyStorage();
|
||||
}
|
||||
if (pref && checked) {
|
||||
// Open a URL which won't set storage and won't prompt user about storage
|
||||
replaceStoragePrompt('false');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset width/height of dialog (e.g., for use by Export)
|
||||
$('#dialog_container')[0].style.width = oldContainerWidth;
|
||||
$('#dialog_container')[0].style.marginLeft = oldContainerMarginLeft;
|
||||
$('#dialog_content')[0].style.height = oldContentHeight;
|
||||
$('#dialog_container')[0].style.height = oldContainerHeight;
|
||||
|
||||
// It should be enough to (conditionally) add to storage on
|
||||
// beforeunload, but if we wished to update immediately,
|
||||
// we might wish to try setting:
|
||||
// svgEditor.setConfig({noStorageOnLoad: true});
|
||||
// and then call:
|
||||
// svgEditor.loadContentAndPrefs();
|
||||
|
||||
// We don't check for noStorageOnLoad here because
|
||||
// the prompt gives the user the option to store data
|
||||
setupBeforeUnloadListener();
|
||||
|
||||
svgEditor.storagePromptState = 'closed';
|
||||
updateCanvas(true);
|
||||
} else if (!noStorageOnLoad || forceStorage) {
|
||||
setupBeforeUnloadListener();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
export default {
|
||||
name: 'webappfind',
|
||||
async init ({importLocale}) {
|
||||
async init ({importLocale, $}) {
|
||||
const strings = await importLocale();
|
||||
const svgEditor = this;
|
||||
const saveMessage = 'save',
|
||||
@@ -48,7 +48,7 @@ export default {
|
||||
} */
|
||||
break;
|
||||
case 'save-end':
|
||||
alert(`save complete for pathID ${pathID}!`);
|
||||
$.alert(`save complete for pathID ${pathID}!`);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unexpected WebAppFind event type');
|
||||
|
||||
@@ -23,7 +23,7 @@ export default {
|
||||
// to configure
|
||||
const {allowedOrigins} = svgEditor.curConfig;
|
||||
if (!allowedOrigins.includes('*') && !allowedOrigins.includes(e.origin)) {
|
||||
console.log(`Origin ${e.origin} not whitelisted for posting to ${window.origin}`);
|
||||
console.log(`Origin ${e.origin} not whitelisted for posting to ${window.origin}`); // eslint-disable-line no-console
|
||||
return;
|
||||
}
|
||||
const cbid = data.id;
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
e.source.postMessage(JSON.stringify(message), '*');
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('Error with xdomain message listener: ' + err);
|
||||
console.log('Error with xdomain message listener: ' + err); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -32,7 +32,8 @@ $('a').click(function () {
|
||||
data = canvas.toDataURL();
|
||||
} catch (err) {
|
||||
// This fails in Firefox with `file:///` URLs :(
|
||||
alert('Data URL conversion failed: ' + err);
|
||||
// Todo: This could use a generic alert library instead
|
||||
alert('Data URL conversion failed: ' + err); // eslint-disable-line no-alert
|
||||
data = '';
|
||||
}
|
||||
post({href, data});
|
||||
|
||||
@@ -6,6 +6,8 @@ manipulation($, jml);
|
||||
|
||||
const baseAPIURL = 'https://openclipart.org/search/json/';
|
||||
|
||||
const jsVoid = 'javascript: void(0);'; // eslint-disable-line no-script-url
|
||||
|
||||
/**
|
||||
* Shows results after query submission.
|
||||
* @param {string} url
|
||||
@@ -18,7 +20,7 @@ async function processResults (url) {
|
||||
*/
|
||||
function queryLink (query) {
|
||||
return ['a', {
|
||||
href: 'javascript: void(0);',
|
||||
href: jsVoid,
|
||||
dataset: {value: query},
|
||||
$on: {click (e) {
|
||||
e.preventDefault();
|
||||
@@ -34,7 +36,8 @@ async function processResults (url) {
|
||||
// console.log('json', json);
|
||||
|
||||
if (!json || json.msg !== 'success') {
|
||||
alert('There was a problem downloading the results');
|
||||
// Todo: This could use a generic alert library instead
|
||||
alert('There was a problem downloading the results'); // eslint-disable-line no-alert
|
||||
return;
|
||||
}
|
||||
const {payload, info: {
|
||||
@@ -82,8 +85,8 @@ async function processResults (url) {
|
||||
['button', {style: 'margin-right: 8px; border: 2px solid black;', dataset: {id, value: svgURL}, $on: {
|
||||
async click (e) {
|
||||
e.preventDefault();
|
||||
const {value: svgURL, id} = this.dataset;
|
||||
// console.log('this', id, svgURL);
|
||||
const {value: svgurl} = this.dataset;
|
||||
// console.log('this', id, svgurl);
|
||||
const post = (message) => {
|
||||
// Todo: Make origin customizable as set by opening window
|
||||
// Todo: If dropping IE9, avoid stringifying
|
||||
@@ -95,13 +98,13 @@ async function processResults (url) {
|
||||
// Send metadata (also indicates file is about to be sent)
|
||||
post({
|
||||
name: title,
|
||||
id: svgURL
|
||||
id: svgurl
|
||||
});
|
||||
const result = await fetch(svgURL);
|
||||
const result = await fetch(svgurl);
|
||||
const svg = await result.text();
|
||||
// console.log('url and svg', svgURL, svg);
|
||||
// console.log('url and svg', svgurl, svg);
|
||||
post({
|
||||
href: svgURL,
|
||||
href: svgurl,
|
||||
data: svg
|
||||
});
|
||||
}
|
||||
@@ -117,7 +120,7 @@ async function processResults (url) {
|
||||
['span', [
|
||||
'(ID: ',
|
||||
['a', {
|
||||
href: 'javascript: void(0);',
|
||||
href: jsVoid,
|
||||
dataset: {value: id},
|
||||
$on: {
|
||||
click (e) {
|
||||
@@ -172,7 +175,7 @@ async function processResults (url) {
|
||||
? ''
|
||||
: ['span', [
|
||||
['a', {
|
||||
href: 'javascript: void(0);',
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault();
|
||||
@@ -188,7 +191,7 @@ async function processResults (url) {
|
||||
? ''
|
||||
: ['span', [
|
||||
['a', {
|
||||
href: 'javascript: void(0);',
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault();
|
||||
@@ -204,7 +207,7 @@ async function processResults (url) {
|
||||
? ''
|
||||
: ['span', [
|
||||
['a', {
|
||||
href: 'javascript: void(0);',
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault();
|
||||
@@ -220,7 +223,7 @@ async function processResults (url) {
|
||||
? ''
|
||||
: ['span', [
|
||||
['a', {
|
||||
href: 'javascript: void(0);',
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user