V7 preview 2 (#463)

* commit toward svgcanvas/svgedit isolation

* jquery removal, isolate svgcavas from svgedit, tests

* refactor(panels)

* fix update of colorpickers

* update cypress

* #tool_imagelib image library menu missing in main menu

* #tool_imagelib lint issue fixed

* #seConfirmDialog confirm change to elix alertdialog

* #seConfirmDialog alert change to elix alert dialog

* #seConfirmDialog remove super.attributeChangedCallback

* #process_cancel prompt changes to alertDialog and seConfirmDialog

* refactor to class step 1

* make load faster

* #storageDialog dialog separate moved dialog

* #process_cancel alert and process_cancel changes

* #process_cancel lint issue fixed

* add seList component

* merge

* fixes

* storagedialog

* move all storage related code to ext-storage

* fix ruler

* Update ConfigObj.js

* fix resize

* Update ext-storage.js

* picker starts withthe right color

* fix prefs

* fix initial content load

* npm update and fix some tests

* npm run build
This commit is contained in:
JFH
2021-01-02 00:15:23 +01:00
committed by GitHub
parent 797e021dba
commit 53b22a46d6
223 changed files with 5359 additions and 14849 deletions

View File

@@ -6,9 +6,6 @@
* @copyright 2010 Jeff Schiller, 2010 Alexis Deveria
*/
// Dependencies:
// 1) jQuery (for $.alert())
import 'pathseg';
import {NS} from './namespaces.js';

View File

@@ -2,7 +2,17 @@
// eslint-disable-next-line node/no-unpublished-import
import deparam from 'deparam';
import * as Utils from '../common/utilities.js';
/**
* Escapes special characters in a regular expression.
* @function regexEscape
* @param {string} str
* @returns {string}
*/
export const regexEscape = function (str) {
// Originally from: http://phpjs.org/functions
return String(str).replace(/[.\\+*?[^\]$(){}=!<>|:-]/g, '\\$&');
};
/**
* @class configObj
*/
@@ -147,27 +157,6 @@ export default class ConfigObj {
// Note: The difference between Prefs and Config is that Prefs
// can be changed in the UI and are stored in the browser,
// while config cannot
this.curConfig = {
// We do not put on defaultConfig to simplify object copying
// procedures (we obtain instead from defaultExtensions)
extensions: [],
userExtensions: [],
/**
* Can use `location.origin` to indicate the current
* origin. Can contain a '*' to allow all domains or 'null' (as
* a string) to support all `file:///` URLs. Cannot be set by
* URL for security reasons (not safe, at least for
* privacy or data integrity of SVG content).
* Might have been fairly safe to allow
* `new URL(location.href).origin` by default but
* avoiding it ensures some more security that even third
* party apps on the same domain also cannot communicate
* with this app by default.
* For use with `ext-xdomain-messaging.js`
* @todo We might instead make as a user-facing preference.
*/
allowedOrigins: []
};
this.urldata = {};
/**
* @name module:SVGEditor~defaultExtensions
@@ -186,6 +175,27 @@ export default class ConfigObj {
'ext-star',
'ext-storage'
];
this.curConfig = {
// We do not put on defaultConfig to simplify object copying
// procedures (we obtain instead from defaultExtensions)
extensions: [],
userExtensions: [],
/**
* Can use `location.origin` to indicate the current
* origin. Can contain a '*' to allow all domains or 'null' (as
* a string) to support all `file:///` URLs. Cannot be set by
* URL for security reasons (not safe, at least for
* privacy or data integrity of SVG content).
* Might have been fairly safe to allow
* `new URL(location.href).origin` by default but
* avoiding it ensures some more security that even third
* party apps on the same domain also cannot communicate
* with this app by default.
* For use with `ext-xdomain-messaging.js`
* @todo We might instead make as a user-facing preference.
*/
allowedOrigins: []
};
this.editor = editor;
}
/**
@@ -206,14 +216,16 @@ export default class ConfigObj {
// Now deal with extensions and other array config
if (!curConfig.noDefaultExtensions) {
curConfig.extensions = curConfig.extensions.concat(this.defaultExtensions);
curConfig.extensions = [...this.defaultExtensions];
}
// ...and remove any dupes
/*
['extensions', 'allowedOrigins'].forEach(function (cfg) {
curConfig[cfg] = $.grep(curConfig[cfg], function (n, i) { // Supposedly faster than filter per http://amandeep1986.blogspot.hk/2015/02/jquery-grep-vs-js-filter.html
return i === curConfig[cfg].indexOf(n);
});
});
*/
// Export updated config
this.curConfig = curConfig;
}
@@ -345,7 +357,7 @@ export default class ConfigObj {
this.defaultPrefs[key] = window.widget.preferenceForKey(storeKey);
} else {
const result = document.cookie.match(
new RegExp('(?:^|;\\s*)' + Utils.regexEscape(
new RegExp('(?:^|;\\s*)' + regexEscape(
encodeURIComponent(storeKey)
) + '=([^;]+)')
);
@@ -450,10 +462,10 @@ export default class ConfigObj {
* @returns {string|void} If val is missing or falsey and `mayBeEmpty` is not set, the
* value of the previously stored preference will be returned.
* @todo Review whether any remaining existing direct references to
* getting `curPrefs` can be changed to use `svgEditor.pref()` getting to ensure
* getting `curPrefs` can be changed to use `svgEditor.configObj.pref()` getting to ensure
* `defaultPrefs` fallback (also for sake of `allowInitialUserOverride`);
* specifically, `bkgd_color` could be changed so that the pref dialog has a
* button to auto-calculate background, but otherwise uses `svgEditor.pref()` to
* button to auto-calculate background, but otherwise uses `svgEditor.configObj.pref()` to
* be able to get default prefs or overridable settings
*/
pref (key, val, mayBeEmpty) {

190
src/editor/Rulers.js Normal file
View File

@@ -0,0 +1,190 @@
/* globals $ */
import {getTypeMap} from '../common/units.js';
/**
*
*/
class Rulers {
/**
* @type {Module}
*/
constructor (editor) {
// Make [1,2,5] array
this.rulerIntervals = [];
for (let i = 0.1; i < 1e5; i *= 10) {
this.rulerIntervals.push(i);
this.rulerIntervals.push(2 * i);
this.rulerIntervals.push(5 * i);
}
this.svgCanvas = editor.svgCanvas;
this.editor = editor;
}
/**
* @type {Module}
*/
manageScroll () {
const rulerX = document.getElementById('ruler_x');
const rulerY = document.getElementById('ruler_y');
if (rulerX) rulerX.scrollLeft = this.editor.workarea[0].scrollLeft;
if (rulerY) rulerY.scrollTop = this.editor.workarea[0].scrollTop;
}
/**
*
* @param {HTMLDivElement} [scanvas]
* @param {Float} [zoom]
* @returns {void}
*/
updateRulers (scanvas, zoom) {
if (!zoom) { zoom = this.svgCanvas.getZoom(); }
if (!scanvas) { scanvas = $('#svgcanvas'); }
let d, i;
const limit = 30000;
const contentElem = this.svgCanvas.getContentElem();
const units = getTypeMap();
const unit = units[this.editor.configObj.curConfig.baseUnit]; // 1 = 1px
// draw x ruler then y ruler
for (d = 0; d < 2; d++) {
const isX = (d === 0);
const dim = isX ? 'x' : 'y';
const lentype = isX ? 'width' : 'height';
const contentDim = Number(contentElem.getAttribute(dim));
const $hcanvOrig = $('#ruler_' + dim + ' canvas:first');
// Bit of a hack to fully clear the canvas in Safari & IE9
const $hcanv = $hcanvOrig.clone();
$hcanvOrig.replaceWith($hcanv);
const hcanv = $hcanv[0];
// Set the canvas size to the width of the container
let rulerLen = scanvas[lentype]();
const totalLen = rulerLen;
hcanv.parentNode.style[lentype] = totalLen + 'px';
let ctx = hcanv.getContext('2d');
let ctxArr, num, ctxArrNum;
ctx.fillStyle = 'rgb(200,0,0)';
ctx.fillRect(0, 0, hcanv.width, hcanv.height);
// Remove any existing canvasses
$hcanv.siblings().remove();
// Create multiple canvases when necessary (due to browser limits)
if (rulerLen >= limit) {
ctxArrNum = Number.parseInt(rulerLen / limit) + 1;
ctxArr = [];
ctxArr[0] = ctx;
let copy;
for (i = 1; i < ctxArrNum; i++) {
hcanv[lentype] = limit;
copy = hcanv.cloneNode(true);
hcanv.parentNode.append(copy);
ctxArr[i] = copy.getContext('2d');
}
copy[lentype] = rulerLen % limit;
// set copy width to last
rulerLen = limit;
}
hcanv[lentype] = rulerLen;
const uMulti = unit * zoom;
// Calculate the main number interval
const rawM = 50 / uMulti;
let multi = 1;
for (i = 0; i < this.rulerIntervals.length; i++) {
num = this.rulerIntervals[i];
multi = num;
if (rawM <= num) {
break;
}
}
const bigInt = multi * uMulti;
ctx.font = '9px sans-serif';
let rulerD = ((contentDim / uMulti) % multi) * uMulti;
let labelPos = rulerD - bigInt;
// draw big intervals
let ctxNum = 0;
while (rulerD < totalLen) {
labelPos += bigInt;
// const realD = rulerD - contentDim; // Currently unused
const curD = Math.round(rulerD) + 0.5;
if (isX) {
ctx.moveTo(curD, 15);
ctx.lineTo(curD, 0);
} else {
ctx.moveTo(15, curD);
ctx.lineTo(0, curD);
}
num = (labelPos - contentDim) / uMulti;
let label;
if (multi >= 1) {
label = Math.round(num);
} else {
const decs = String(multi).split('.')[1].length;
label = num.toFixed(decs);
}
// Change 1000s to Ks
if (label !== 0 && label !== 1000 && label % 1000 === 0) {
label = (label / 1000) + 'K';
}
if (isX) {
ctx.fillText(label, rulerD + 2, 8);
} else {
// draw label vertically
const str = String(label).split('');
for (i = 0; i < str.length; i++) {
ctx.fillText(str[i], 1, (rulerD + 9) + i * 9);
}
}
const part = bigInt / 10;
// draw the small intervals
for (i = 1; i < 10; i++) {
let subD = Math.round(rulerD + part * i) + 0.5;
if (ctxArr && subD > rulerLen) {
ctxNum++;
ctx.stroke();
if (ctxNum >= ctxArrNum) {
i = 10;
rulerD = totalLen;
continue;
}
ctx = ctxArr[ctxNum];
rulerD -= limit;
subD = Math.round(rulerD + part * i) + 0.5;
}
// odd lines are slighly longer
const lineNum = (i % 2) ? 12 : 10;
if (isX) {
ctx.moveTo(subD, 15);
ctx.lineTo(subD, lineNum);
} else {
ctx.moveTo(15, subD);
ctx.lineTo(lineNum, subD);
}
}
rulerD += bigInt;
}
ctx.strokeStyle = '#000';
ctx.stroke();
}
}
}
export default Rulers;

View File

@@ -6,15 +6,13 @@ class PaintBox {
/**
* @param {string|Element|external:jQuery} container
* @param {"fill"} type
* @param {string} color
* @param {number} opacity
*/
constructor (container, type, color, opacity) {
constructor (container, type) {
// set up gradients to be used for the buttons
const svgdocbox = new DOMParser().parseFromString(
`<svg xmlns="http://www.w3.org/2000/svg">
<rect
fill="#${color}" opacity="${opacity}" width="22" height="22"/>
fill="#000000" opacity="1" width="22" height="22"/>
<defs><linearGradient id="gradbox_${PaintBox.ctr++}"/></defs>
</svg>`,
'text/xml'
@@ -26,7 +24,7 @@ class PaintBox {
this.rect = docElem.firstElementChild;
this.defs = docElem.getElementsByTagName('defs')[0];
this.grad = this.defs.firstElementChild;
this.paint = new $.jGraduate.Paint({solidColor: color});
// this.paint = new $.jGraduate.Paint({solidColor: color});
this.type = type;
}
@@ -138,22 +136,6 @@ class PaintBox {
this.setPaint(paint);
return (paint);
}
/**
* @returns {void}
*/
prep () {
const ptype = this.paint.type;
switch (ptype) {
case 'linearGradient':
case 'radialGradient': {
const paint = new $.jGraduate.Paint({copy: this.paint});
this.setPaint(this.type, paint);
break;
}
}
}
}
PaintBox.ctr = 0;

View File

@@ -1,10 +1,12 @@
import './seButton.js';
import './seFlyingButton.js';
import './seExplorerButton.js';
import './seDropdown.js';
import './seZoom.js';
import './seInput.js';
import './seSpinInput.js';
import './sePalette.js';
import './seMenu.js';
import './seMenuItem.js';
import './seList.js';
import './seListItem.js';
import './seColorPicker.js';

View File

@@ -283,7 +283,8 @@ export default function jQueryPluginJGraduate ($) {
const $wc = (selector) => $($shadowRoot.querySelectorAll(selector));
if (!idref) {
/* await */ $.alert('Container element must have an id attribute to maintain unique id strings for sub-elements.');
// eslint-disable-next-line no-alert
alert('Container element must have an id attribute to maintain unique id strings for sub-elements.');
return;
}

View File

@@ -0,0 +1,99 @@
/* eslint-disable node/no-unpublished-import */
import 'elix/define/DropdownList.js';
const template = document.createElement('template');
template.innerHTML = `
<style>
[part~="source"] {
grid-template-columns: 20px 1fr auto;
}
::slotted(*) {
padding: 4px;
background: #E8E8E8;
border: 1px solid #B0B0B0;
width: 100%;
}
[part~="popup"] {
width: 150%;
}
</style>
<label>Label</label>
<elix-dropdown-list>
<slot></slot>
</elix-dropdown-list>
`;
/**
* @class SeList
*/
export class SeList extends HTMLElement {
/**
* @function constructor
*/
constructor () {
super();
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({mode: 'open'});
this._shadowRoot.appendChild(template.content.cloneNode(true));
this.$dropdown = this._shadowRoot.querySelector('elix-dropdown-list');
this.$label = this._shadowRoot.querySelector('label');
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return ['label'];
}
/**
* @function attributeChangedCallback
* @param {string} name
* @param {string} oldValue
* @param {string} newValue
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
switch (name) {
case 'label':
this.$label.textContent = newValue;
break;
default:
// eslint-disable-next-line no-console
console.error(`unknown attribute: ${name}`);
break;
}
}
/**
* @function get
* @returns {any}
*/
get label () {
return this.getAttribute('label');
}
/**
* @function set
* @returns {void}
*/
set label (value) {
this.setAttribute('label', value);
}
/**
* @function connectedCallback
* @returns {void}
*/
connectedCallback () {
this.$dropdown.addEventListener('change', (e) => {
e.preventDefault();
const selectedItem = e?.detail?.closeResult;
if (selectedItem !== undefined && selectedItem?.id !== undefined) {
document.getElementById(selectedItem.id).click();
}
});
}
}
// Register
customElements.define('se-list', SeList);

View File

@@ -0,0 +1,71 @@
/* eslint-disable node/no-unpublished-import */
import 'elix/define/Option.js';
const template = document.createElement('template');
template.innerHTML = `
<style>
</style>
<elix-option aria-label="option">
<slot></slot>
</elix-option>
`;
/**
* @class SeMenu
*/
export class SeListItem extends HTMLElement {
/**
* @function constructor
*/
constructor () {
super();
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({mode: 'open'});
this._shadowRoot.appendChild(template.content.cloneNode(true));
this.$menuitem = this._shadowRoot.querySelector('elix-menu-item');
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return ['option'];
}
/**
* @function attributeChangedCallback
* @param {string} name
* @param {string} oldValue
* @param {string} newValue
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
switch (name) {
case 'option':
this.$menuitem.setAttribute('option', newValue);
break;
default:
// eslint-disable-next-line no-console
console.error(`unknown attribute: ${name}`);
break;
}
}
/**
* @function get
* @returns {any}
*/
get option () {
return this.getAttribute('option');
}
/**
* @function set
* @returns {void}
*/
set option (value) {
this.setAttribute('option', value);
}
}
// Register
customElements.define('se-list-item', SeListItem);

View File

@@ -8,7 +8,7 @@ template.innerHTML = `
</style>
<elix-menu-item>
<div style="display:inline-block;">
<img src="" alt="icon" style="display:none;" />
<img src="./images/logo.svg" alt="icon" style="display:none;" />
<span></span>
</div>
</elix-menu-item>

View File

@@ -18,7 +18,7 @@ template.innerHTML = `
padding: 0px;
}
</style>
<img src="./images/logo.svg" alt="icon" width="12" height="12" />
<img src="./images/logo.svg" alt="icon" width="12" height="12" aria-labelledby="label" />
<span id="label">label</span>
<elix-number-spin-box min="1" step="1"></elix-number-spin-box>
`;

View File

@@ -0,0 +1,179 @@
/* eslint-disable node/no-unpublished-import */
import ListComboBox from 'elix/define/ListComboBox.js';
import NumberSpinBox from 'elix/define/NumberSpinBox.js';
// import Input from 'elix/src/base/Input.js';
import * as internal from 'elix/src/base/internal.js';
import {templateFrom, fragmentFrom} from 'elix/src/core/htmlLiterals.js';
/**
* @class Dropdown
*/
class Zoom extends ListComboBox {
/**
* @function get
* @returns {PlainObject}
*/
get [internal.defaultState] () {
return Object.assign(super[internal.defaultState], {
inputPartType: NumberSpinBox,
src: './images/logo.svg',
inputsize: '100%'
});
}
/**
* @function get
* @returns {PlainObject}
*/
get [internal.template] () {
const result = super[internal.template];
const source = result.content.getElementById('source');
// add a icon before our dropdown
source.prepend(fragmentFrom.html`
<img src="./images/logo.svg" alt="icon" width="18" height="18"></img>
`.cloneNode(true));
// change the style so it fits in our toolbar
result.content.append(
templateFrom.html`
<style>
[part~="source"] {
grid-template-columns: 20px 1fr auto;
}
::slotted(*) {
padding: 4px;
background: #E8E8E8;
border: 1px solid #B0B0B0;
width: 100%;
}
[part~="popup"] {
width: 150%;
}
</style>
`.content
);
return result;
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return ['title', 'src', 'inputsize', 'value'];
}
/**
* @function attributeChangedCallback
* @param {string} name
* @param {string} oldValue
* @param {string} newValue
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
switch (name) {
case 'title':
// this.$span.setAttribute('title', `${newValue} ${shortcut ? `[${shortcut}]` : ''}`);
break;
case 'src':
this.src = newValue;
break;
case 'inputsize':
this.inputsize = newValue;
break;
default:
super.attributeChangedCallback(name, oldValue, newValue);
break;
}
}
/**
* @function [internal.render]
* @param {PlainObject} changed
* @returns {void}
*/
[internal.render] (changed) {
super[internal.render](changed);
if (this[internal.firstRender]) {
this.$img = this.shadowRoot.querySelector('img');
this.$input = this.shadowRoot.getElementById('input');
}
if (changed.src) {
this.$img.setAttribute('src', this[internal.state].src);
}
if (changed.inputsize) {
this.$input.shadowRoot.querySelector('[part~="input"]').style.width = this[internal.state].inputsize;
}
if (changed.inputPartType) {
// Wire up handler on new input.
this.addEventListener('close', (e) => {
e.preventDefault();
const value = e.detail?.closeResult?.getAttribute('value');
if (value) {
const closeEvent = new CustomEvent('change', {detail: {value}});
this.dispatchEvent(closeEvent);
}
});
}
}
/**
* @function src
* @returns {string} src
*/
get src () {
return this[internal.state].src;
}
/**
* @function src
* @returns {void}
*/
set src (src) {
this[internal.setState]({src});
}
/**
* @function inputsize
* @returns {string} src
*/
get inputsize () {
return this[internal.state].inputsize;
}
/**
* @function src
* @returns {void}
*/
set inputsize (inputsize) {
this[internal.setState]({inputsize});
}
/**
* @function value
* @returns {string} src
*/
get value () {
return this[internal.state].value;
}
/**
* @function value
* @returns {void}
*/
set value (value) {
this[internal.setState]({value});
}
}
// Register
customElements.define('se-zoom', Zoom);
/*
{TODO
min: 0.001, max: 10000, step: 50, stepfunc: stepZoom,
function stepZoom (elem, step) {
const origVal = Number(elem.value);
if (origVal === 0) { return 100; }
const sugVal = origVal + step;
if (step === 0) { return origVal; }
if (origVal >= 100) {
return sugVal;
}
if (sugVal >= origVal) {
return origVal * 2;
}
return origVal / 2;
}
*/

View File

@@ -184,7 +184,7 @@ export class SeCMenuDialog extends HTMLElement {
});
break;
default:
super.attributeChangedCallback(name, oldValue, newValue);
// super.attributeChangedCallback(name, oldValue, newValue);
break;
}
}

View File

@@ -115,7 +115,7 @@ export class SeCMenuLayerDialog extends HTMLElement {
}
break;
default:
super.attributeChangedCallback(name, oldValue, newValue);
// super.attributeChangedCallback(name, oldValue, newValue);
break;
}
}

View File

@@ -194,11 +194,11 @@ template.innerHTML = `
</fieldset>
<fieldset id="change_grid">
<legend id="svginfo_grid_settings">Grid</legend>
<label>
<label for="svginfo_snap_onoff">
<span id="svginfo_snap_onoff">Snapping on/off</span>
<input type="checkbox" value="snapping_on" id="grid_snapping_on" />
</label>
<label>
<label for="grid_snapping_step">
<span id="svginfo_snap_step">Snapping Step-Size:</span>
<input type="text" id="grid_snapping_step" size="3" value="10" />
</label>

View File

@@ -3,3 +3,7 @@ import './editorPreferencesDialog.js';
import './svgSourceDialog.js';
import './cmenuDialog.js';
import './cmenuLayersDialog.js';
import './seSelectDialog.js';
import './seConfirmDialog.js';
import './sePromptDialog.js';
import './seAlertDialog.js';

View File

@@ -0,0 +1,11 @@
// eslint-disable-next-line node/no-unpublished-import
import AlertDialog from 'elix/define/AlertDialog.js';
const dialog = new AlertDialog();
const seAlert = (text) => {
dialog.textContent = text;
dialog.choices = ['Ok'];
dialog.open();
};
window.seAlert = seAlert;

View File

@@ -0,0 +1,13 @@
// eslint-disable-next-line node/no-unpublished-import
import AlertDialog from 'elix/define/AlertDialog.js';
const dialog = new AlertDialog();
const seConfirm = async (text, choices) => {
dialog.textContent = text;
dialog.choices = (choices === undefined) ? ['Ok', 'Cancel'] : choices;
dialog.open();
const response = await dialog.whenClosed();
return response.choice;
};
window.seConfirm = seConfirm;

View File

@@ -0,0 +1,83 @@
// eslint-disable-next-line node/no-unpublished-import
import AlertDialog from 'elix/define/AlertDialog.js';
/**
* @class SePromptDialog
*/
export class SePromptDialog extends HTMLElement {
/**
* @function constructor
*/
constructor () {
super();
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({mode: 'open'});
this.dialog = new AlertDialog();
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return ['title', 'close'];
}
/**
* @function attributeChangedCallback
* @param {string} name
* @param {string} oldValue
* @param {string} newValue
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
switch (name) {
case 'title':
if (this.dialog.opened) {
this.dialog.close();
}
this.dialog.textContent = newValue;
this.dialog.choices = ['Cancel'];
this.dialog.open();
break;
case 'close':
if (this.dialog.opened) {
this.dialog.close();
}
break;
default:
console.error('unkonw attr for:', name, 'newValue =', newValue);
break;
}
}
/**
* @function get
* @returns {any}
*/
get title () {
return this.getAttribute('title');
}
/**
* @function set
* @returns {void}
*/
set title (value) {
this.setAttribute('title', value);
}
/**
* @function get
* @returns {any}
*/
get close () {
return this.getAttribute('close');
}
/**
* @function set
* @returns {void}
*/
set close (value) {
this.setAttribute('close', value);
}
}
// Register
customElements.define('se-prompt-dialog', SePromptDialog);

View File

@@ -0,0 +1,13 @@
// eslint-disable-next-line node/no-unpublished-import
import AlertDialog from 'elix/define/AlertDialog.js';
const dialog = new AlertDialog();
const seSelect = async (text, choices) => {
dialog.textContent = text;
dialog.choices = choices;
dialog.open();
const response = await dialog.whenClosed();
return response.choice;
};
window.seSelect = seSelect;

View File

@@ -362,7 +362,7 @@ class EmbeddedSVGEdit {
let sameOriginWithGlobal = false;
try {
sameOriginWithGlobal = window.location.origin === that.frame.contentWindow.location.origin &&
that.frame.contentWindow.svgEditor.canvas;
that.frame.contentWindow.svgEditor.svgCanvas;
} catch (err) {}
if (sameOriginWithGlobal) {

View File

@@ -23,8 +23,8 @@ export default {
name: 'arrows',
async init (S) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const svgCanvas = svgEditor.canvas;
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {svgCanvas} = svgEditor;
const
addElem = svgCanvas.addSVGElementFromJson,
{nonce, $} = S,

View File

@@ -25,7 +25,7 @@ export default {
name: 'closepath',
async init ({importLocale, $}) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
let selElems;
const updateButton = function (path) {
const seglist = path.pathSegList,

View File

@@ -23,7 +23,7 @@ export default {
name: 'connector',
async init (S) {
const svgEditor = this;
const svgCanvas = svgEditor.canvas;
const {svgCanvas} = svgEditor;
const {getElem} = svgCanvas;
const {$, svgroot} = S,
addElem = svgCanvas.addSVGElementFromJson,
@@ -356,7 +356,7 @@ export default {
}
}
}];
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
return {
/** @todo JFH special flag */
newUI: true,
@@ -374,7 +374,7 @@ export default {
startX = opts.start_x;
startY = opts.start_y;
const mode = svgCanvas.getMode();
const {curConfig: {initStroke}} = svgEditor;
const {curConfig: {initStroke}} = svgEditor.configObj;
if (mode === 'connector') {
if (started) { return undefined; }

View File

@@ -23,10 +23,10 @@ export default {
name: 'eyedropper',
async init (S) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {$, ChangeElementCommand} = S, // , svgcontent,
// svgdoc = S.svgroot.parentNode.ownerDocument,
svgCanvas = svgEditor.canvas,
{svgCanvas} = svgEditor,
addToHistory = function (cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd); },
currentStyle = {
fillPaint: 'red', fillOpacity: 1.0,

View File

@@ -1,3 +1,4 @@
/* globals seConfirm */
/**
* @file ext-foreignobject.js
*
@@ -24,13 +25,13 @@ export default {
async init (S) {
const svgEditor = this;
const {$, text2xml, NS} = S;
const svgCanvas = svgEditor.canvas;
const {svgCanvas} = svgEditor;
const
// {svgcontent} = S,
// addElem = svgCanvas.addSVGElementFromJson,
svgdoc = S.svgroot.parentNode.ownerDocument;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const properlySourceSizeTextArea = function () {
// TODO: remove magic numbers here and get values from CSS
@@ -196,11 +197,11 @@ export default {
// Create source save/cancel buttons
/* const save = */ $('#tool_source_save').clone()
.hide().attr('id', 'foreign_save').unbind()
.appendTo('#tool_source_back').click(async function () {
.appendTo('#tool_source_back').click(function () {
if (!editingforeign) { return; }
if (!setForeignString($('#svg_source_textarea').val())) {
const ok = await $.confirm('Errors found. Revert to original?');
const ok = seConfirm('Errors found. Revert to original?');
if (!ok) { return; }
endChanges();
} else {

View File

@@ -23,15 +23,15 @@ export default {
name: 'grid',
async init ({$, NS, getTypeMap}) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const svgCanvas = svgEditor.canvas;
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {svgCanvas} = svgEditor;
const svgdoc = document.getElementById('svgcanvas').ownerDocument,
{assignAttributes} = svgCanvas,
hcanvas = document.createElement('canvas'),
canvBG = $('#canvasBackground'),
units = getTypeMap(), // Assumes prior `init()` call on `units.js` module
intervals = [0.01, 0.1, 1, 10, 100, 1000];
let showGrid = svgEditor.curConfig.showGrid || false;
let showGrid = svgEditor.configObj.curConfig.showGrid || false;
$(hcanvas).hide().appendTo('body');
@@ -90,7 +90,7 @@ export default {
*/
function updateGrid (zoom) {
// TODO: Try this with <line> elements, then compare performance difference
const unit = units[svgEditor.curConfig.baseUnit]; // 1 = 1px
const unit = units[svgEditor.configObj.curConfig.baseUnit]; // 1 = 1px
const uMulti = unit * zoom;
// Calculate the main number interval
const rawM = 100 / uMulti;
@@ -109,7 +109,7 @@ export default {
const part = bigInt / 10;
ctx.globalAlpha = 0.2;
ctx.strokeStyle = svgEditor.curConfig.gridColor;
ctx.strokeStyle = svgEditor.configObj.curConfig.gridColor;
for (let i = 1; i < 10; i++) {
const subD = Math.round(part * i) + 0.5;
// const lineNum = (i % 2)?12:10;
@@ -161,7 +161,7 @@ export default {
events: {
id: 'view_grid',
click () {
svgEditor.curConfig.showGrid = showGrid = !showGrid;
svgEditor.configObj.curConfig.showGrid = showGrid = !showGrid;
gridUpdate();
}
}

View File

@@ -29,8 +29,8 @@ export default {
name: 'helloworld',
async init ({$, importLocale}) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const svgCanvas = svgEditor.canvas;
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {svgCanvas} = svgEditor;
return {
name: strings.name,
events: [{
@@ -78,7 +78,7 @@ export default {
});
// Show the text using the custom alert function
$.alert(text);
alert(text); // eslint-disable-line no-alert
}
}
};

View File

@@ -23,7 +23,7 @@ export default {
name: 'imagelib',
async init ({$, decode64, dropXMLInternalSubset}) {
const svgEditor = this;
const imagelibStrings = await loadExtensionTranslation(svgEditor.pref('lang'));
const imagelibStrings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {uiStrings, canvas: svgCanvas} = svgEditor;
@@ -223,7 +223,7 @@ export default {
} else {
pending[id].entry.remove();
}
// await $.alert('Unexpected data was returned: ' + response, function() {
// await alert('Unexpected data was returned: ' + response, function() {
// if (mode !== 'm') {
// closeBrowser();
// } else {
@@ -372,7 +372,7 @@ export default {
let browser = $('#imgbrowse');
if (!browser.length) {
$('<div id=imgbrowse_holder><div id=imgbrowse class=toolbar_button>' +
'</div></div>').insertAfter('#svg_docprops');
'</div></div>').insertAfter('#svg_editor');
browser = $('#imgbrowse');
const allLibs = imagelibStrings.select_lib;
@@ -386,8 +386,8 @@ export default {
left: 0,
width: '100%'
});
const cancel = $('<button>' + uiStrings.common.cancel + '</button>')
// eslint-disable-next-line max-len
$('<button><img class="svg_icon" src="./images/cancel.svg" alt="icon" width="16" height="16" />' + uiStrings.common.cancel + '</button>')
.appendTo(browser)
.on('click touchend', function () {
$('#imgbrowse_holder').hide();
@@ -398,8 +398,8 @@ export default {
});
const leftBlock = $('<span>').css({position: 'absolute', top: 5, left: 10}).appendTo(browser);
const back = $('<button hidden>' + imagelibStrings.show_list + '</button>')
// eslint-disable-next-line max-len
const back = $('<button hidden><img class="svg_icon" src="./images/library.svg" alt="icon" width="16" height="16" />' + imagelibStrings.show_list + '</button>')
.appendTo(leftBlock)
.on('click touchend', function () {
frame.attr('src', 'about:blank').hide();
@@ -430,9 +430,6 @@ export default {
'margin-top': 10
});
cancel.prepend($.getSvgIcon('cancel', true));
back.prepend($.getSvgIcon('tool_imagelib', true));
imagelibStrings.imgLibs.forEach(function ({name, url, description}) {
$('<li>')
.appendTo(libOpts)
@@ -452,21 +449,16 @@ export default {
}
}
const buttons = [{
const events = {
id: 'tool_imagelib',
type: 'app_menu',
icon: 'imagelib.png',
position: 4,
events: {
mouseup: showBrowser
click () {
showBrowser();
}
}];
};
return {
svgicons: 'ext-imagelib.xml',
buttons: imagelibStrings.buttons.map((button, i) => {
return Object.assign(buttons[i], button);
}),
events,
callback () {
$('<style>').text(
'#imgbrowse_holder {' +

View File

@@ -45,9 +45,9 @@ export default {
name: 'markers',
async init (S) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {$} = S;
const svgCanvas = svgEditor.canvas;
const {svgCanvas} = svgEditor;
const // {svgcontent} = S,
addElem = svgCanvas.addSVGElementFromJson;
const mtypes = ['start', 'mid', 'end'];
@@ -404,12 +404,13 @@ export default {
/**
* @param {"start"|"mid"|"end"} pos
* @returns {Promise<void>} Resolves to `undefined`
* @returns {void} Resolves to `undefined`
*/
async function showTextPrompt (pos) {
function showTextPrompt (pos) {
let def = $('#' + pos + '_marker').val();
if (def.substr(0, 1) === '\\') { def = ''; }
const txt = await $.prompt('Enter text for ' + pos + ' marker', def);
// eslint-disable-next-line no-alert
const txt = prompt('Enter text for ' + pos + ' marker', def);
if (txt) {
triggerTextEntry(pos, txt);
}

View File

@@ -24,8 +24,8 @@ export default {
name: 'mathjax',
async init ({$}) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const svgCanvas = svgEditor.canvas;
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {svgCanvas} = svgEditor;
// Configuration of the MathJax extention.
@@ -202,7 +202,8 @@ export default {
});
} 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.');
// eslint-disable-next-line no-alert
alert('Failed loading MathJax. You will not be able to change the mathematics.');
}
}
}

View File

@@ -26,8 +26,8 @@ export default {
name: 'panning',
async init ({importLocale}) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const svgCanvas = svgEditor.canvas;
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {svgCanvas} = svgEditor;
const buttons = [{
id: 'ext-panning',
icon: 'panning.png',

View File

@@ -22,7 +22,7 @@ export default {
name: 'placemark',
async init (S) {
const svgEditor = this;
const svgCanvas = svgEditor.canvas;
const {svgCanvas} = svgEditor;
const addElem = svgCanvas.addSVGElementFromJson;
const {$} = S; // {svgcontent},
let
@@ -35,7 +35,7 @@ export default {
// newFOG, newFOGParent, newDef, newImageName, newMaskID,
// undoCommand = 'Not image',
// modeChangeG, ccZoom, wEl, hEl, wOffset, hOffset, ccRgbEl, brushW, brushH;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const markerTypes = {
nomarker: {},
forwardslash:

View File

@@ -22,11 +22,11 @@ export default {
name: 'polygon',
async init (S) {
const svgEditor = this;
const svgCanvas = svgEditor.canvas;
const {svgCanvas} = svgEditor;
const {$} = S, // {svgcontent}
// addElem = svgCanvas.addSVGElementFromJson,
editingitex = false;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
let selElems,
// svgdoc = S.svgroot.parentNode.ownerDocument,
// newFOG, newFOGParent, newDef, newImageName, newMaskID, modeChangeG,
@@ -133,7 +133,7 @@ export default {
// Todo: Uncomment the setItexString() function above and handle ajaxEndpoint?
/*
if (!setItexString($('#svg_source_textarea').val())) {
const ok = await $.confirm('Errors found. Revert to original?', function (ok) {
const ok = seConfirm('Errors found. Revert to original?', function (ok) {
if (!ok) {
return false;
}

View File

@@ -25,8 +25,8 @@ export default {
name: 'server_moinsave',
async init ({$, encode64, importLocale}) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const svgCanvas = svgEditor.canvas;
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {svgCanvas} = svgEditor;
const saveSvgAction = '/+modify';
// Create upload target (hidden iframe)
@@ -64,7 +64,8 @@ export default {
<input type="hidden" name="contenttype" value="application/x-svgdraw">
`).appendTo('body')
.submit().remove();
$.alert(strings.saved);
// eslint-disable-next-line no-alert
alert(strings.saved);
top.window.location = '/' + name;
}
});

View File

@@ -24,7 +24,7 @@ export default {
name: 'server_opensave',
async init ({$, decode64, encode64}) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const {
curConfig: {
avoidClientSide, // Deprecated
@@ -146,7 +146,8 @@ export default {
}
if (note.length) {
await $.alert(note);
// eslint-disable-next-line no-alert
alert(note);
}
const filename = getFileNameFromTitle();

View File

@@ -11,7 +11,7 @@ export default {
name: 'shapes',
init ({$}) {
const svgEditor = this;
const canv = svgEditor.canvas;
const canv = svgEditor.svgCanvas;
const svgroot = canv.getRootElem();
let lastBBox = {};

View File

@@ -22,7 +22,7 @@ export default {
name: 'star',
async init (S) {
const svgEditor = this;
const svgCanvas = svgEditor.canvas;
const {svgCanvas} = svgEditor;
const {$} = S; // {svgcontent},
let
@@ -35,7 +35,7 @@ export default {
// newFOG, newFOGParent, newDef, newImageName, newMaskID,
// undoCommand = 'Not image',
// modeChangeG, ccZoom, wEl, hEl, wOffset, hOffset, ccRgbEl, brushW, brushH;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
/**
*

View File

@@ -18,24 +18,47 @@
* @todo We might provide control of storage settings through the UI besides the
* initial (or URL-forced) dialog. *
*/
import './storageDialog.js';
const loadExtensionTranslation = async function (lang) {
let translationModule;
try {
translationModule = await import(`./locale/${encodeURIComponent(lang)}.js`);
} catch (_error) {
// eslint-disable-next-line no-console
console.error(`Missing translation (${lang}) - using 'en'`);
translationModule = await import(`./locale/en.js`);
/**
* Expire the storage cookie.
* @returns {void}
*/
const removeStoragePrefCookie = () => {
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';
};
/**
* Replace `storagePrompt` parameter within URL.
* @param {string} val
* @returns {void}
* @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
if (loc.href.includes('storagePrompt=')) {
loc.href = loc.href.replace(/([&?])storagePrompt=[^&]*(&?)/, function (n0, n1, amp) {
return (val ? n1 : '') + val + (!val && amp ? n1 : (amp || ''));
});
} else {
loc.href += (loc.href.includes('?') ? '&' : '?') + val;
}
return translationModule.default;
};
export default {
name: 'storage',
init ({$}) {
const svgEditor = this;
const svgCanvas = svgEditor.canvas;
const {svgCanvas} = 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
@@ -43,7 +66,6 @@ export default {
// to change, set the "emptyStorageOnDecline" config setting to true
// in svgedit-config-iife.js/svgedit-config-es.js.
const {
emptyStorageOnDecline,
// When the code in svg-editor.js prevents local storage on load per
// user request, we also prevent storing on unload here so as to
// avoid third-party sites making XSRF requests or providing links
@@ -55,31 +77,50 @@ export default {
// the "noStorageOnLoad" config setting to true in svgedit-config-*.js.
noStorageOnLoad,
forceStorage
} = svgEditor.curConfig;
const {storage, updateCanvas} = svgEditor;
} = svgEditor.configObj.curConfig;
const {storage} = svgEditor;
/**
* Replace `storagePrompt` parameter within URL.
* @param {string} val
* @returns {void}
* @todo Replace the string manipulation with `searchParams.set`
*/
function replaceStoragePrompt (val) {
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(/(?<sep>[&?])storagePrompt=[^&]*(?<amp>&?)/, function (n0, sep, amp) {
return (val ? sep : '') + val + (!val && amp ? sep : (amp || ''));
});
*/
loc.href = loc.href.replace(/([&?])storagePrompt=[^&]*(&?)/, function (n0, n1, amp) {
return (val ? n1 : '') + val + (!val && amp ? n1 : (amp || ''));
});
} else {
loc.href += (loc.href.includes('?') ? '&' : '?') + val;
// storageDialog added to DOM
const storageBox = document.createElement('se-storage-dialog');
storageBox.setAttribute('id', 'se-storage-dialog');
document.body.append(storageBox);
// manage the change in the storageDialog
storageBox.addEventListener('change', (e) => {
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';
if (storagePrompt === 'true' && e?.detail?.checkbox) {
replaceStoragePrompt();
return;
}
} else {
removeStoragePrefCookie();
if (svgEditor.configObj.curConfig.emptyStorageOnDecline && e?.detail?.checkbox) {
this.setSVGContentStorage('');
Object.keys(svgEditor.curPrefs).forEach((name) => {
name = 'svg-edit-' + name;
if (svgEditor.storage) {
svgEditor.storage.removeItem(name);
}
expireCookie(name);
});
}
if (e?.detail?.select && e?.detail?.checkbox) {
replaceStoragePrompt('false');
return;
}
}
} else if (e?.detail?.trigger === 'cancel') {
removeStoragePrefCookie();
}
}
setupBeforeUnloadListener();
svgEditor.storagePromptState = 'closed';
svgEditor.updateCanvas(true);
});
/**
* Sets SVG content as a string with "svgedit-" and the current
@@ -89,7 +130,7 @@ export default {
*/
function setSVGContentStorage (val) {
if (storage) {
const name = 'svgedit-' + svgEditor.curConfig.canvasName;
const name = 'svgedit-' + svgEditor.configObj.curConfig.canvasName;
if (!val) {
storage.removeItem(name);
} else {
@@ -98,40 +139,6 @@ export default {
}
}
/**
* Set the cookie to expire.
* @param {string} cookie
* @returns {void}
*/
function expireCookie (cookie) {
document.cookie = encodeURIComponent(cookie) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
}
/**
* Expire the storage cookie.
* @returns {void}
*/
function removeStoragePrefCookie () {
expireCookie('svgeditstore');
}
/**
* Empties storage for each of the current preferences.
* @returns {void}
*/
function emptyStorage () {
setSVGContentStorage('');
Object.keys(svgEditor.curPrefs).forEach((name) => {
name = 'svg-edit-' + name;
if (storage) {
storage.removeItem(name);
}
expireCookie(name);
});
}
// emptyStorage();
/**
* Listen for unloading: If and only if opted in by the user, set the content
* document and preferences into storage:
@@ -154,7 +161,7 @@ export default {
svgEditor.setConfig({no_save_warning: true}); // No need for explicit saving at all once storage is on
// svgEditor.showSaveWarning = false;
const {curPrefs} = svgEditor;
const {curPrefs} = svgEditor.configObj;
Object.entries(curPrefs).forEach(([key, val]) => {
const store = (val !== undefined);
@@ -177,15 +184,8 @@ export default {
let loaded = false;
return {
name: 'storage',
async langReady ({lang}) {
langReady ({lang}) {
const storagePrompt = new URL(top.location).searchParams.get('storagePrompt');
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const {
message, storagePrefsAndContent, storagePrefsOnly,
storagePrefs, storageNoPrefsOrContent, storageNoPrefs,
rememberLabel, rememberTooltip
} = strings;
// No need to run this one-time dialog again just because the user
// changes the language
if (loaded) {
@@ -213,93 +213,13 @@ export default {
)
// ...then show the storage prompt.
)) {
const options = [];
if (storage) {
options.unshift(
{value: 'prefsAndContent', text: storagePrefsAndContent},
{value: 'prefsOnly', text: storagePrefsOnly},
{value: 'noPrefsOrContent', text: storageNoPrefsOrContent}
);
} else {
options.unshift(
{value: 'prefsOnly', text: storagePrefs},
{value: 'noPrefsOrContent', text: storageNoPrefs}
);
}
// Hack to temporarily provide a wide and high enough dialog
const oldContainerWidth = $('#dialog_container')[0].style.width,
oldContainerMarginLeft = $('#dialog_container')[0].style.marginLeft,
oldContentHeight = $('#dialog_content')[0].style.height,
oldContainerHeight = $('#dialog_container')[0].style.height;
$('#dialog_content')[0].style.height = '120px';
$('#dialog_container')[0].style.height = '170px';
$('#dialog_container')[0].style.width = '800px';
$('#dialog_container')[0].style.marginLeft = '-400px';
const options = Boolean(storage);
// Open select-with-checkbox dialog
// From svg-editor.js
svgEditor.storagePromptState = 'waiting';
const {response: pref, checked} = await $.select(
message,
options,
null,
null,
{
label: rememberLabel,
checked: true,
tooltip: rememberTooltip
}
);
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);
const $storageDialog = document.getElementById('se-storage-dialog');
$storageDialog.setAttribute('dialog', 'open');
$storageDialog.setAttribute('storage', options);
} else if (!noStorageOnLoad || forceStorage) {
setupBeforeUnloadListener();
}

View File

@@ -0,0 +1,183 @@
/* eslint-disable max-len */
/* eslint-disable node/no-unpublished-import */
import 'elix/define/Dialog.js';
const template = document.createElement('template');
template.innerHTML = `
<style>
#dialog_content {
margin: 10px 10px 5px 10px;
background: #DDD;
overflow: auto;
text-align: left;
border: 1px solid #B0B0B0;
}
#dialog_content p, #dialog_content select, #dialog_content label {
margin: 10px;
line-height: 1.3em;
}
#dialog_container {
font-family: Verdana;
text-align: center;
left: 50%;
top: 50%;
max-width: 400px;
z-index: 50001;
background: #CCC;
border: 1px outset #777;
font-family:Verdana,Helvetica,sans-serif;
font-size:0.8em;
}
#dialog_container, #dialog_content {
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
#dialog_buttons input[type=text] {
width: 90%;
display: block;
margin: 0 0 5px 11px;
}
#dialog_buttons input[type=button] {
margin: 0 1em;
}
</style>
<elix-dialog id="dialog_box" aria-label="SVG-Edit storage preferences" closed>
<div class="overlay"></div>
<div id="dialog_container">
<div id="dialog_content">
<p>
By default and where supported, SVG-Edit can store your editor preferences and SVG content locally on your machine so you do not need to add these back each time you load SVG-Edit. If, for privacy reasons, you do not wish to store this information on your machine, you can change away from the default option below.
</p>
<select id="se-storage-pref">
<option value="prefsAndContent">Store preferences and SVG content locally</option>
<option value="prefsOnly">Only store preferences locally</option>
<option value="noPrefsOrContent">Do not store my preferences or SVG content locally</option>
</select>
<label title="If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again.">
Remember this choice?<input type="checkbox" id="se-remember" value="" checked>
</label>
</div>
<div id="dialog_buttons">
<button id="storage_ok">
<img class="svg_icon" src="./images/ok.svg" alt="icon" width="16" height="16" />
Ok
</button>
<button id="storage_cancel">
<img class="svg_icon" src="./images/cancel.svg" alt="icon" width="16" height="16" />
Cancel
</button>
</div>
</div>
</elix-dialog>
`;
/**
* @class SeStorageDialog
*/
export class SeStorageDialog extends HTMLElement {
/**
* @function constructor
*/
constructor () {
super();
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({mode: 'open'});
this._shadowRoot.appendChild(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 observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return ['dialog', 'storage'];
}
/**
* @function attributeChangedCallback
* @param {string} name
* @param {string} oldValue
* @param {string} newValue
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
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;
default:
// super.attributeChangedCallback(name, oldValue, newValue);
break;
}
}
/**
* @function get
* @returns {any}
*/
get dialog () {
return this.getAttribute('dialog');
}
/**
* @function set
* @returns {void}
*/
set 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'));
}
/**
* Sets SVG content as a string with "svgedit-" and the current
* canvas name as namespace.
* @param {string} val
* @returns {void}
*/
setSVGContentStorage (val) {
if (this.storage) {
const name = 'svgedit-' + this.configObj.curConfig.canvasName;
if (!val) {
this.storage.removeItem(name);
} else {
this.storage.setItem(name, val);
}
}
}
}
// Register
customElements.define('se-storage-dialog', SeStorageDialog);

View File

@@ -22,7 +22,7 @@ export default {
name: 'webappfind',
async init ({$}) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.pref('lang'));
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
const saveMessage = 'save',
readMessage = 'read',
excludedMessages = [readMessage, saveMessage];
@@ -63,7 +63,8 @@ export default {
} */
break;
case 'save-end':
$.alert(`save complete for pathID ${pathID}!`);
// eslint-disable-next-line no-alert
alert(`save complete for pathID ${pathID}!`);
break;
default:
throw new Error('Unexpected WebAppFind event type');
@@ -98,7 +99,7 @@ export default {
webappfind: {
type: saveMessage,
pathID,
content: svgEditor.canvas.getSvgString()
content: svgEditor.svgCanvas.getSvgString()
}
}, window.location.origin === 'null'
// Avoid "null" string error for `file:` protocol (even

View File

@@ -8,7 +8,7 @@ export default {
name: 'xdomain-messaging',
init () {
const svgEditor = this;
const svgCanvas = svgEditor.canvas;
const {svgCanvas} = svgEditor;
try {
window.addEventListener('message', function (e) {
// We accept and post strings for the sake of IE9 support
@@ -22,7 +22,7 @@ export default {
// The default is not to allow any origins, including even the same domain or
// if run on a `file:///` URL. See `svgedit-config-es.js` for an example of how
// to configure
const {allowedOrigins} = svgEditor.curConfig;
const {allowedOrigins} = svgEditor.configObj.curConfig;
if (!allowedOrigins.includes('*') && !allowedOrigins.includes(e.origin)) {
console.log(`Origin ${e.origin} not whitelisted for posting to ${window.origin}`); // eslint-disable-line no-console
return;

View File

@@ -0,0 +1,10 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 201 211">
<g>
<path fill="#efe8b8" stroke="#d6c47c" stroke-linecap="round" d="m2.75,49.51761l56.56,-46.26761c12.73,8.25 25.71001,7 46.44,0.75l-56.03999,47.23944l-22.72002,25.01056l-24.23999,-26.73239z" id="svg_2" stroke-width="7"/>
<path fill="#a03333" stroke="#3f3f3f" d="m3.75,203.25002c14.33301,7 30.66699,7 46,0l0,-152.00002c-14.66699,8 -32.33301,8 -47,0l1,152.00002zm45.75,-152.25002l56.25,-46.75l0,151l-56,48.00002m-47.25,-154.25002l57.25,-46.5" id="svg_1" stroke-width="7" stroke-linecap="round"/>
<path fill="#efe8b8" stroke="#d6c47c" stroke-linecap="round" d="m49.75,49.51801l56.56,-46.26801c12.72998,8.25 25.71002,7 46.44,0.75l-56.03998,47.239l-22.72003,25.011l-24.23999,-26.73199z" stroke-width="7" id="svg_5"/>
<path fill="#2f8e2f" stroke="#3f3f3f" d="m50.75,202.25c14.33301,7 30.66699,7.04253 46,0.04253l0,-151.04253c-14.66699,8 -32.33301,8 -47,0l1,151zm45.75,-151.25l56.25,-46.75l0,144.01219l-56,51.98782m-47.25,-151.25002l57.25,-46.5" stroke-width="7" stroke-linecap="round" id="svg_6"/>
<path fill="#efe8b8" stroke="#d6c47c" stroke-linecap="round" d="m95.75,49.51801l56.56,-46.26801c12.72998,8.25 25.71002,7 46.44,0.75l-56.03998,47.239l-22.72003,25.011l-24.23999,-26.73199z" stroke-width="7" id="svg_10"/>
<path fill="#336393" stroke="#3f3f3f" d="m96.75,200.29445c14.33301,7 30.66699,7 46,0l0,-149.04445c-14.66699,8 -32.33301,8 -47,0l1,149.04445zm45.75,-149.29445l56.25,-46.75l0,148.04445l-56,48m-47.25,-151.29445l57.25,-46.5" stroke-width="7" stroke-linecap="round" id="svg_11"/>
</g>
</svg></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 839 B

After

Width:  |  Height:  |  Size: 839 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -74,19 +74,20 @@
<div id="sidepanel_handle" title="Drag left/right to resize side panel [X]">L a y e r s
</div>
</div>
<se-menu id="main_button" label="SVG-Edit" src="./images/logo.svg">
<se-menu id="main_button" label="SVG-Edit" src="./images/logo.svg" alt="logo">
<!-- File-like buttons: New, Save, Source -->
<se-menu-item id="tool_clear" label="New Image" shortcut="N" src="./images/new.svg"></se-menu-item>
<se-menu-item id="tool_open" label="Open SVG" src="./images/open.svg"></se-menu-item>
<se-menu-item id="tool_import" label="Import Image" src="./images/importImg.svg"></se-menu-item>
<se-menu-item id="tool_save" label="Save Image" shortcut="S" src="./images/saveImg.svg"></se-menu-item>
<se-menu-item id="tool_imagelib" label="Image library" src="./images/library.svg"></se-menu-item>
<se-menu-item id="tool_export" label="Export" src="./images/export.svg"></se-menu-item>
<se-menu-item id="tool_docprops" label="Document Properties" shortcut="D" src="./images/docprop.svg"></se-menu-item>
<se-menu-item id="tool_editor_prefs" label="Editor Preferences" src="./images/editPref.svg"></se-menu-item>
<se-menu-item id="tool_editor_homepage" label="SVG-Edit Home Page" src="./images/svg-edit-home.svg"></se-menu-item>
<div style="width:50%;margin:auto;">
<a href="https://www.netlify.com">
<img style="height:25px;" src="https://www.netlify.com/img/global/badges/netlify-dark.svg"
<img style="height:25px;" src="./images/netlify-dark.svg"
alt="Deploys by Netlify" />
</a>
</div>
@@ -126,15 +127,23 @@
title="Change rotation angle"></se-spin-input>
<se-spin-input size="2" id="blur" min=0 max=100 step=5 src="./images/blur.svg"
title="Change gaussian blur value"></se-spin-input>
<se-list>
<se-list-item id="tool_posleft">Align Left</se-list-item>
<se-list-item id="tool_poscenter">Align Center</se-list-item>
<se-list-item id="tool_posright">Align Right</se-list-item>
<se-list-item id="tool_postop">Align Top</se-list-item>
<se-list-item id="tool_posmiddle">Align Middle</se-list-item>
<se-list-item id="tool_posbottom">Align Bottom</se-list-item>
</se-list>
<div class="dropdown toolset" id="tool_position" title="Align Element to Page">
<div id="cur_position" class="icon_label"></div>
<button></button>
</div>
<div id="xy_panel" class="toolset">
<se-input id="selected_x" data-attr:="x" size="4" type="text" label="x:" title="Change X coordinate">
</se-input>
<se-input id="selected_y" data-attr="y" size="4" type="text" label="y:" title="Change Y coordinate">
</se-input>
<se-spin-input id="selected_x" data-attr:="x" size="4" type="text" label="x:" title="Change X coordinate">
</se-spin-input>
<se-spin-input id="selected_y" data-attr="y" size="4" type="text" label="y:" title="Change Y coordinate">
</se-spin-input>
</div>
</div> <!-- selected_panel -->
<!-- Buttons when multiple elements are selected -->
@@ -153,33 +162,30 @@
<se-button id="tool_align_top" title="Align Top" src="./images/align_top.svg"></se-button>
<se-button id="tool_align_middle" title="Align Middle" src="./images/align_middle.svg"></se-button>
<se-button id="tool_align_bottom" title="Align Bottom" src="./images/align_bottom.svg"></se-button>
<label id="tool_align_relative">
<span id="relativeToLabel">relative to:</span>
<select id="align_relative_to" title="Align relative to ...">
<option id="selected_objects" value="selected">selected objects</option>
<option id="largest_object" value="largest">largest object</option>
<option id="smallest_object" value="smallest">smallest object</option>
<option id="page" value="page">page</option>
</select>
</label>
<se-list id="tool_align_relative" label="relative to:">
<se-list-item id="selected_objects" value="selected">selected objects</se-list-item>
<se-list-item id="largest_object" value="largest">largest object</se-list-item>
<se-list-item id="smallest_object" value="smallest">smallest object</se-list-item>
<se-list-item id="page" value="page">page</se-list-item>
</se-list>
<div class="tool_sep"></div>
</div> <!-- multiselected_panel -->
<div id="rect_panel">
<div class="toolset">
<se-input id="rect_width" data-attr="width" size="4" src="./images/width.svg"
title="Change rectangle width"></se-input>
<se-input id="rect_height" data-attr="height" size="4" src="./images/height.svg"
title="Change rectangle height"></se-input>
<se-spin-input id="rect_width" data-attr="width" size="4" src="./images/width.svg"
title="Change rectangle width"></se-spin-input>
<se-spin-input id="rect_height" data-attr="height" size="4" src="./images/height.svg"
title="Change rectangle height"></se-spin-input>
</div>
<se-spin-input id="rect_rx" min=0 max=1000 step=1 size="3" title="Change Rectangle Corner Radius"
data-attr="Corner Radius" src="./images/c_radius.svg"></se-spin-input>
</div> <!-- rect_panel -->
<div id="image_panel">
<div class="toolset">
<se-input id="image_width" data-attr="width" size="4" type="text" src="./images/width.svg"
title="Change image width"></se-input>
<se-input id="image_height" data-attr="height" size="4" type="text" src="./images/height.svg"
title="Change image height"></se-input>
<se-spin-input id="image_width" data-attr="width" size="4" type="text" src="./images/width.svg"
title="Change image width"></se-spin-input>
<se-spin-input id="image_height" data-attr="height" size="4" type="text" src="./images/height.svg"
title="Change image height"></se-spin-input>
</div>
<div class="toolset">
<label id="tool_image_url">url:
@@ -194,37 +200,35 @@
</div> <!-- image_panel -->
<div id="circle_panel">
<div class="toolset">
<se-input id="circle_cx" data-attr="cx" size="4" label="cx:"></se-input>
<se-input id="circle_cy" data-attr="cy" size="4" label="cy:"></se-input>
<se-spin-input id="circle_cx" data-attr="cx" size="4" label="cx:"></se-spin-input>
<se-spin-input id="circle_cy" data-attr="cy" size="4" label="cy:"></se-spin-input>
</div>
<div class="toolset">
<se-input id="circle_r" data-attr="r" size="4" label="r:"></se-input>
<se-spin-input id="circle_r" data-attr="r" size="4" label="r:"></se-spin-input>
</div>
</div> <!-- circle_panel -->
<div id="ellipse_panel">
<div class="toolset">
<se-input id="ellipse_cx" data-attr="cx" size="4" title="Change ellipse's cx coordinate" label="cx:">
</se-input>
<se-input id="ellipse_cy" data-attr="cy" size="4" title="Change ellipse's cy coordinate" label="cy:">
</se-input>
<se-spin-input id="ellipse_cx" data-attr="cx" size="4" title="Change ellipse's cx coordinate" label="cx:">
</se-spin-input>
<se-spin-input id="ellipse_cy" data-attr="cy" size="4" title="Change ellipse's cy coordinate" label="cy:">
</se-spin-input>
</div>
<div class="toolset">
<se-input id="ellipse_rx" data-attr="rx" size="4" title="Change ellipse's x radius" label="rx:"></se-input>
<se-input id="ellipse_ry" data-attr="ry" size="4" title="Change ellipse's y radius" label="ry:"></se-input>
<se-spin-input id="ellipse_rx" data-attr="rx" size="4" title="Change ellipse's x radius" label="rx:"></se-spin-input>
<se-spin-input id="ellipse_ry" data-attr="ry" size="4" title="Change ellipse's y radius" label="ry:"></se-spin-input>
</div>
</div> <!-- ellipse_panel -->
<div id="line_panel">
<div class="toolset">
<se-input id="line_x1" data-attr="x1" size="4" title="Change line's starting x coordinate" label="x1:">
</se-input>
<se-input id="line_y1" data-attr="y1" size="4" title="Change line's starting y coordinate" label="y1:">
</se-input>
</div>
<div class="toolset">
<se-input id="line_x2" data-attr="x2" size="4" title="Change line's ending x coordinate" label="x2:">
</se-input>
<se-input id="line_y2" data-attr="y2" size="4" title="Change line's ending y coordinate" label="y2:">
</se-input>
<se-spin-input id="line_x1" data-attr="x1" size="4" title="Change line's starting x coordinate" label="x1:">
</se-spin-input>
<se-spin-input id="line_y1" data-attr="y1" size="4" title="Change line's starting y coordinate" label="y1:">
</se-spin-input>
<se-spin-input id="line_x2" data-attr="x2" size="4" title="Change line's ending x coordinate" label="x2:">
</se-spin-input>
<se-spin-input id="line_y2" data-attr="y2" size="4" title="Change line's ending y coordinate" label="y2:">
</se-spin-input>
</div>
</div> <!-- line_panel -->
<div id="text_panel">
@@ -235,25 +239,16 @@
<se-button id="tool_text_anchor_middle" title="Align the text from middle" src="./images/anchor_middle.svg"></se-button>
<se-button id="tool_text_anchor_end" title="Align the text from end" src="./images/anchor_end.svg"></se-button>
</div>
<div class="toolset" id="tool_font_family">
<label>
<!-- Font family -->
<input id="font_family" type="text" title="Change Font Family" size="12" />
</label>
<div id="font_family_dropdown" class="dropdown">
<button></button>
<ul>
<li style="font-family:serif">Serif</li>
<li style="font-family:sans-serif">Sans-serif</li>
<li style="font-family:cursive">Cursive</li>
<li style="font-family:fantasy">Fantasy</li>
<li style="font-family:monospace">Monospace</li>
<li style="font-family:courier">Courier</li>
<li style="font-family:helvetica">Helvetica</li>
<li style="font-family:times">Times</li>
</ul>
</div>
</div>
<se-list id="tool_font_family" label="Font:">
<se-list-item value="Sans-serif"> <div style="font-family:serif">Sans-serif</div></se-list-item>
<se-list-item value="Serif"> <div style="font-family:serif">Serif</div></se-list-item>
<se-list-item value="Cursive"> <div style="font-family:serif">Cursive</div></se-list-item>
<se-list-item value="Fantasy"> <div style="font-family:serif">Fantasy</div></se-list-item>
<se-list-item value="Monospace"> <div style="font-family:serif">Monospace</div></se-list-item>
<se-list-item value="Courier"> <div style="font-family:serif">Courier</div></se-list-item>
<se-list-item value="Helvetica"> <div style="font-family:serif">Helvetica</div></se-list-item>
<se-list-item value="Times"> <div style="font-family:serif">Times</div></se-list-item>
</se-list>
<se-spin-input size="2" id="font_size" min=1 max=1000 step=1 title="Change Font Size"
src="./images/fontsize.svg"></se-spin-input>
<!-- Not visible, but still used -->
@@ -285,8 +280,8 @@
<div class="tool_sep"></div>
<se-button id="tool_node_link" title="Link Control Points" src="./images/tool_node_link.svg" pressed></se-button>
<div class="tool_sep"></div>
<se-input id="path_node_x" data-attr="x" size="4" title="Change node's x coordinate" label="x:"></se-input>
<se-input id="path_node_y" data-attr="y" size="4" title="Change node's y coordinate" label="y:"></se-input>
<se-spin-input id="path_node_x" data-attr="x" size="4" title="Change node's x coordinate" label="x:"></se-spin-input>
<se-spin-input id="path_node_y" data-attr="y" size="4" title="Change node's y coordinate" label="y:"></se-spin-input>
<select id="seg_type" title="Change Segment type">
<option id="straight_segments" selected="selected" value="4">Straight</option>
<option id="curve_segments" value="6">Curve</option>
@@ -331,7 +326,7 @@
</div> <!-- tools_left -->
<div id="tools_bottom">
<!-- Zoom buttons -->
<se-dropdown id="zoom" src="./images/zoom.svg" title="Change zoom level" inputsize="40px">
<se-zoom id="zoom" src="./images/zoom.svg" title="Change zoom level" inputsize="40px">
<div value="1000">1000</div>
<div value="400">400</div>
<div value="200">200</div>
@@ -342,66 +337,37 @@
<div value="selection">Fit to selection</div>
<div value="layer">Fit to layer content</div>
<div value="content">Fit to all content</div>
</se-dropdown>
</se-zoom>
<se-colorpicker id="fill_color" src="./images/fill.svg" title="Change fill color" type="fill"></se-colorpicker>
<se-colorpicker id="stroke_color" src="./images/stroke.svg" title="Change stroke color" type="stroke"></se-colorpicker>
<se-spin-input id="stroke_width" min=0 max=99 step=1 title="Change stroke width by 1" label=""></se-spin-input>
<label class="stroke_tool">
<select id="stroke_style" title="Change stroke dash style">
<option selected="selected" value="none">&#8212;</option>
<option value="2,2">...</option>
<option value="5,5">- -</option>
<option value="5,2,2,2">- .</option>
<option value="5,2,2,2,2,2">- ..</option>
</select>
</label>
<div class="stroke_tool dropdown" id="stroke_linejoin">
<div id="cur_linejoin" title="Linejoin: Miter"></div>
<button></button>
</div>
<div class="stroke_tool dropdown" id="stroke_linecap">
<div id="cur_linecap" title="Linecap: Butt"></div>
<button></button>
</div>
<div id="tool_opacity">
<se-spin-input size="3" id="group_opacity" min=0 max=100 step=5 title="Change selected item opacity" label=""></se-spin-input>
<div id="opacity_dropdown" class="dropdown">
<button></button>
<ul>
<li>0%</li>
<li>25%</li>
<li>50%</li>
<li>75%</li>
<li>100%</li>
<li class="special">
<div id="opac_slider"></div>
</li>
</ul>
</div>
</div> <!-- tool_opacity -->
<se-list id="stroke_style" title="Change stroke dash style" label="">
<se-list-item value="none">&#8212;</se-list-item>
<se-list-item value="2,2">...</se-list-item>
<se-list-item value="5,5">- -</se-list-item>
<se-list-item value="5,2,2,2">- .</se-list-item>
<se-list-item value="5,2,2,2,2,2">- ..</se-list-item>
</se-list>
<se-list id="stroke_linejoin" title="Linejoin: Miter" label="">
<se-list-item id="linejoin_miter"><se-button size="small" title="Linejoin: Miter" src="./images/linejoin_miter.svg"></se-button></se-list-item>
<se-list-item id="linejoin_round"><se-button size="small" title="Linejoin: Round" src="./images/linejoin_round.svg"></se-button></se-list-item>
<se-list-item id="linejoin_bevel"><se-button size="small" title="Linejoin: Bevel" src="./images/linejoin_bevel.svg"></se-button></se-list-item>
</se-list>
<se-list id="stroke_linecap" title="Linecap: Butt" label="">
<se-list-item id="linecap_butt"><se-button size="small" title="Linecap: Butt" src="./images/linecap_butt.svg"></se-button></se-list-item>
<se-list-item id="linecap_square"><se-button size="small" title="Linecap: Square" src="./images/linecap_square.svg"></se-button></se-list-item>
<se-list-item id="linecap_round"><se-button size="small" title="Linecap: Round" src="./images/linecap_round.svg"></se-button></se-list-item>
</se-list>
<se-list id="opacity_dropdown" label="">
<se-list-item>0%</se-list-item>
<se-list-item>25%</se-list-item>
<se-list-item>50%</se-list-item>
<se-list-item>75%</se-list-item>
<se-list-item>100%</se-list-item>
</se-list>
<se-spin-input size="3" id="group_opacity" min=0 max=100 step=5 title="Change selected item opacity" label=""></se-spin-input>
<se-palette id="palette"></se-palette>
</div> <!-- tools_bottom -->
<div id="option_lists" class="dropdown">
<ul id="linejoin_opts">
<li class="tool_button current" id="linejoin_miter" title="Linejoin: Miter"></li>
<li class="tool_button" id="linejoin_round" title="Linejoin: Round"></li>
<li class="tool_button" id="linejoin_bevel" title="Linejoin: Bevel"></li>
</ul>
<ul id="linecap_opts">
<li class="tool_button current" id="linecap_butt" title="Linecap: Butt"></li>
<li class="tool_button" id="linecap_square" title="Linecap: Square"></li>
<li class="tool_button" id="linecap_round" title="Linecap: Round"></li>
</ul>
<ul id="position_opts" class="optcols3">
<li class="push_button" id="tool_posleft" title="Align Left"></li>
<li class="push_button" id="tool_poscenter" title="Align Center"></li>
<li class="push_button" id="tool_posright" title="Align Right"></li>
<li class="push_button" id="tool_postop" title="Align Top"></li>
<li class="push_button" id="tool_posmiddle" title="Align Middle"></li>
<li class="push_button" id="tool_posbottom" title="Align Bottom"></li>
</ul>
</div>
</div>
<div id="dialog_box">
<div class="overlay"></div>
<div id="dialog_container">

View File

@@ -0,0 +1,196 @@
/* globals $ */
import SvgCanvas from '../../svgcanvas/svgcanvas.js';
const {$id} = SvgCanvas;
/*
* register actions for left panel
*/
/**
*
*/
class BottomPanelHandlers {
/**
* @param {PlainObject} editor svgedit handler
*/
constructor (editor) {
this.editor = editor;
this.svgCanvas = editor.svgCanvas;
}
/**
* @type {module}
*/
get selectedElement () {
return this.editor.selectedElement;
}
/**
* @type {module}
*/
get multiselected () {
return this.editor.multiselected;
}
/**
* @type {module}
*/
changeStrokeWidth (e) {
let val = e.target.value;
if (val === 0 && this.selectedElement && ['line', 'polyline'].includes(this.selectedElement.nodeName)) {
val = 1;
}
this.svgCanvas.setStrokeWidth(val);
}
/**
* @type {module}
*/
changeZoom (value) {
switch (value) {
case 'canvas':
case 'selection':
case 'layer':
case 'content':
this.editor.zoomChanged(window, value);
break;
default:
{
const zoomlevel = Number(value) / 100;
if (zoomlevel < 0.001) {
value = 0.1;
return;
}
const zoom = this.svgCanvas.getZoom();
const wArea = this.editor.workarea;
this.editor.zoomChanged(window, {
width: 0,
height: 0,
// center pt of scroll position
x: (wArea[0].scrollLeft + wArea.width() / 2) / zoom,
y: (wArea[0].scrollTop + wArea.height() / 2) / zoom,
zoom: zoomlevel
}, true);
}
}
}
/**
* @fires module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate
* @returns {void}
*/
updateToolButtonState () {
const bNoFill = (this.svgCanvas.getColor('fill') === 'none');
const bNoStroke = (this.svgCanvas.getColor('stroke') === 'none');
const buttonsNeedingStroke = ['tool_fhpath', 'tool_line'];
const buttonsNeedingFillAndStroke = [
'tools_rect', 'tools_ellipse',
'tool_text', 'tool_path'
];
if (bNoStroke) {
buttonsNeedingStroke.forEach((btn) => {
// if btn is pressed, change to select button
if ($id(btn).pressed) {
this.editor.leftPanelHandlers.clickSelect();
}
$(btn).disabled = true;
});
} else {
buttonsNeedingStroke.forEach((btn) => {
$id(btn).disabled = false;
});
}
if (bNoStroke && bNoFill) {
buttonsNeedingFillAndStroke.forEach((btn) => {
// if btn is pressed, change to select button
if ($id(btn).pressed) {
this.editor.leftPanelHandlers.clickSelect();
}
$(btn).disabled = true;
});
} else {
buttonsNeedingFillAndStroke.forEach((btn) => {
$id(btn).disabled = false;
});
}
this.svgCanvas.runExtensions(
'toolButtonStateUpdate',
/** @type {module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate} */ {
nofill: bNoFill,
nostroke: bNoStroke
}
);
}
/**
* @type {module}
*/
updateColorpickers (apply) {
$id('fill_color').update(this.svgCanvas, this.selectedElement, apply);
$id('stroke_color').update(this.svgCanvas, this.selectedElement, apply);
}
/**
* @type {module}
*/
handleColorPicker (type, evt) {
const {paint} = evt.detail;
this.svgCanvas.setPaint(type, paint);
this.updateToolButtonState();
}
/**
* @type {module}
*/
handleStrokeAttr (type, evt) {
this.svgCanvas.setStrokeAttr(type, evt.currentTarget.value);
}
/**
* @type {module}
*/
handleOpacity (evt) {
// if ($(this).find('div').length) { return; }
const val = Number.parseInt(evt.currentTarget.value.split('%')[0]);
this.svgCanvas.setOpacity(val / 100);
}
/**
* @type {module}
*/
handlePalette (e) {
e.preventDefault();
// shift key or right click for stroke
const {picker, color} = e.detail;
// Webkit-based browsers returned 'initial' here for no stroke
const paint = color === 'none' ? new $.jGraduate.Paint() : new $.jGraduate.Paint({alpha: 100, solidColor: color.substr(1)});
if (picker === 'fill') {
$id('fill_color').setPaint(paint);
} else {
$id('stroke_color').setPaint(paint);
}
this.svgCanvas.setColor(picker, color);
if (color !== 'none' && this.svgCanvas.getPaintOpacity(picker) !== 1) {
this.svgCanvas.setPaintOpacity(picker, 1.0);
}
this.updateToolButtonState();
}
/**
* @type {module}
*/
init () {
// register actions for bottom panel
$id('zoom').addEventListener('change', (e) => this.changeZoom.bind(this)(e.detail.value));
$id('stroke_color').addEventListener('change', (evt) => this.handleColorPicker.bind(this)('stroke', evt));
$id('fill_color').addEventListener('change', (evt) => this.handleColorPicker.bind(this)('fill', evt));
$id('stroke_width').addEventListener('change', this.changeStrokeWidth.bind(this));
$id('stroke_style').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-dasharray', evt));
$id('stroke_linejoin').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-linejoin', evt));
$id('stroke_linecap').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-linecap', evt));
$id('opacity_dropdown').addEventListener('change', this.handleOpacity.bind(this));
$id('palette').addEventListener('change', this.handlePalette.bind(this));
const {curConfig} = this.editor.configObj;
$id('fill_color').setPaint(new $.jGraduate.Paint({alpha: 100, solidColor: curConfig.initFill.color}));
$id('stroke_color').setPaint(new $.jGraduate.Paint({alpha: 100, solidColor: curConfig.initStroke.color}));
}
}
export default BottomPanelHandlers;

View File

@@ -1,43 +1,162 @@
/* eslint-disable no-alert */
/* globals $ */
import SvgCanvas from '../../svgcanvas/svgcanvas.js';
const SIDEPANEL_MAXWIDTH = 300;
const SIDEPANEL_OPENWIDTH = 150;
const {$id} = SvgCanvas;
/**
*
*/
class LayersPanel {
/**
* @param {PlainObject} svgCanvas svgCanvas
* @param {PlainObject} uiStrings uiStrings
* @param {GenericCallBack} updateContextPanel updateContextPanel
* @param {PlainObject} editor
*/
constructor (svgCanvas, uiStrings, updateContextPanel) {
this.svgCanvas = svgCanvas;
this.uiStrings = uiStrings;
this.updateContextPanel = updateContextPanel;
constructor (editor) {
this.svgCanvas = editor.svgCanvas;
this.uiStrings = editor.uiStrings;
this.updateContextPanel = editor.topPanelHandlers.updateContextPanel;
this.sidedrag = -1;
this.sidedragging = false;
this.allowmove = false;
this.editor = editor;
}
/**
* @param {Float} delta
* @fires module:svgcanvas.SvgCanvas#event:ext_workareaResized
* @returns {void}
*/
changeSidePanelWidth (delta) {
const rulerX = $('#ruler_x');
$('#sidepanels').width('+=' + delta);
$('#layerpanel').width('+=' + delta);
rulerX.css('right', Number.parseInt(rulerX.css('right')) + delta);
this.editor.workarea.css('right', Number.parseInt(this.editor.workarea.css('right')) + delta);
this.svgCanvas.runExtensions('workareaResized');
}
/**
* @param {Event} evt
* @returns {void}
*/
resizeSidePanel (evt) {
if (!this.allowmove) { return; }
if (this.sidedrag === -1) { return; }
this.sidedragging = true;
let deltaX = this.sidedrag - evt.pageX;
const sideWidth = $('#sidepanels').width();
if (sideWidth + deltaX > SIDEPANEL_MAXWIDTH) {
deltaX = SIDEPANEL_MAXWIDTH - sideWidth;
// sideWidth = SIDEPANEL_MAXWIDTH;
} else if (sideWidth + deltaX < 2) {
deltaX = 2 - sideWidth;
// sideWidth = 2;
}
if (deltaX === 0) { return; }
this.sidedrag -= deltaX;
this.changeSidePanelWidth(deltaX);
}
/**
* If width is non-zero, then fully close it; otherwise fully open it.
* @param {boolean} close Forces the side panel closed
* @returns {void}
*/
toggleSidePanel (close) {
const dpr = window.devicePixelRatio || 1;
const w = $('#sidepanels').width();
const isOpened = (dpr < 1 ? w : w / dpr) > 2;
const zoomAdjustedSidepanelWidth = (dpr < 1 ? 1 : dpr) * SIDEPANEL_OPENWIDTH;
const deltaX = (isOpened || close ? 0 : zoomAdjustedSidepanelWidth) - w;
this.changeSidePanelWidth(deltaX);
}
/**
* @param {PlainObject} e event
* @returns {void}
*/
lmenuFunc (e) {
const action = e?.detail?.trigger;
switch (action) {
case 'dupe':
this.cloneLayer();
break;
case 'delete':
this.deleteLayer();
break;
case 'merge_down':
this.mergeLayer();
break;
case 'merge_all':
this.svgCanvas.mergeAllLayers();
this.updateContextPanel();
this.populateLayers();
break;
}
}
/**
* @returns {void}
*/
addEvents () {
init () {
// layer menu added to DOM
const menuMore = document.createElement('se-cmenu-layers');
menuMore.setAttribute('id', 'se-cmenu-layers-more');
menuMore.value = 'layer_moreopts';
menuMore.setAttribute('leftclick', true);
document.body.append(menuMore);
const menuLayerBox = document.createElement('se-cmenu-layers');
menuLayerBox.setAttribute('id', 'se-cmenu-layers-list');
menuLayerBox.value = 'layerlist';
menuLayerBox.setAttribute('leftclick', false);
document.body.append(menuLayerBox);
document.getElementById('layer_new').addEventListener('click', this.newLayer.bind(this));
document.getElementById('layer_delete').addEventListener('click', this.deleteLayer.bind(this));
document.getElementById('layer_up').addEventListener('click', () => this.moveLayer.bind(this)(-1));
document.getElementById('layer_down').addEventListener('click', () => this.moveLayer.bind(this)(1));
document.getElementById('layer_rename').addEventListener('click', this.layerRename.bind(this));
$id('se-cmenu-layers-more').addEventListener('change', this.lmenuFunc.bind(this));
$id('se-cmenu-layers-list').addEventListener('change', (e) => {
this.lmenuFunc.bind(this)(e?.detail?.trigger, e?.detail?.source);
});
$id('sidepanel_handle').addEventListener('click', this.toggleSidePanel.bind(this));
if (this.editor.configObj.curConfig.showlayers) {
this.toggleSidePanel();
}
$id('sidepanel_handle').addEventListener('mousedown', (evt) => {
this.sidedrag = evt.pageX;
window.addEventListener('mousemove', this.resizeSidePanel.bind(this));
this.allowmove = false;
// Silly hack for Chrome, which always runs mousemove right after mousedown
setTimeout(() => {
this.allowmove = true;
}, 20);
});
$id('sidepanel_handle').addEventListener('mouseup', (evt) => {
if (!this.sidedragging) { this.toggleSidePanel(); }
this.sidedrag = -1;
this.sidedragging = false;
});
window.addEventListener('mouseup', (evt) => {
this.sidedrag = -1;
this.sidedragging = false;
$id('svg_editor').removeEventListener('mousemove', this.resizeSidePanel.bind(this));
});
}
/**
* @returns {void}
*/
async newLayer () {
newLayer () {
let uniqName;
let i = this.svgCanvas.getCurrentDrawing().getNumLayers();
do {
uniqName = this.uiStrings.layers.layer + ' ' + (++i);
} while (this.svgCanvas.getCurrentDrawing().hasLayer(uniqName));
const newName = await $.prompt(this.uiStrings.notification.enterUniqueLayerName, uniqName);
const newName = prompt(this.uiStrings.notification.enterUniqueLayerName, uniqName);
if (!newName) { return; }
if (this.svgCanvas.getCurrentDrawing().hasLayer(newName)) {
/* await */ $.alert(this.uiStrings.notification.dupeLayerName);
alert(this.uiStrings.notification.dupeLayerName);
return;
}
this.svgCanvas.createLayer(newName);
@@ -63,15 +182,15 @@ class LayersPanel {
/**
*
* @returns {Promise<void>}
* @returns {void}
*/
async cloneLayer () {
cloneLayer () {
const name = this.svgCanvas.getCurrentDrawing().getCurrentLayerName() + ' copy';
const newName = await $.prompt(this.uiStrings.notification.enterUniqueLayerName, name);
const newName = prompt(this.uiStrings.notification.enterUniqueLayerName, name);
if (!newName) { return; }
if (this.svgCanvas.getCurrentDrawing().hasLayer(newName)) {
/* await */ $.alert(this.uiStrings.notification.dupeLayerName);
alert(this.uiStrings.notification.dupeLayerName);
return;
}
this.svgCanvas.cloneLayer(newName);
@@ -110,13 +229,13 @@ class LayersPanel {
/**
* @returns {void}
*/
async layerRename () {
layerRename () {
// const curIndex = $('#layerlist tr.layersel').prevAll().length; // Currently unused
const oldName = $('#layerlist tr.layersel td.layername').text();
const newName = await $.prompt(this.uiStrings.notification.enterNewLayerName, '');
const newName = prompt(this.uiStrings.notification.enterNewLayerName, '');
if (!newName) { return; }
if (oldName === newName || this.svgCanvas.getCurrentDrawing().hasLayer(newName)) {
/* await */ $.alert(this.uiStrings.notification.layerHasThatName);
alert(this.uiStrings.notification.layerHasThatName);
return;
}
this.svgCanvas.renameCurrentLayer(newName);

View File

@@ -0,0 +1,216 @@
import SvgCanvas from '../../svgcanvas/svgcanvas.js';
const {$id, $qa} = SvgCanvas;
/*
* register actions for left panel
*/
/**
* @type {module}
*/
class LeftPanelHandlers {
/**
* @param {PlainObject} editor svgedit handler
*/
constructor (editor) {
this.editor = editor;
this.svgCanvas = editor.svgCanvas;
}
/**
* This is a common function used when a tool has been clicked (chosen).
* It does several common things:
* - Removes the pressed button from whatever tool currently has it.
* - Adds the the pressed button to the button passed in.
* @function this.updateLeftPanel
* @param {string|Element} button The DOM element or string selector representing the toolbar button
* @returns {boolean} Whether the button was disabled or not
*/
// eslint-disable-next-line class-methods-use-this
updateLeftPanel (button) {
if (button.disabled) return false;
// remove the pressed state on other(s) button(s)
$qa('#tools_left *[pressed]').forEach((b) => { b.pressed = false; });
// pressed state for the clicked button
$id(button).pressed = true;
return true;
}
/**
* Unless the select toolbar button is disabled, sets the button
* and sets the select mode and cursor styles.
* @function module:SVGEditor.clickSelect
* @returns {void}
*/
clickSelect () {
if (this.updateLeftPanel('tool_select')) {
this.editor.workarea.css('cursor', 'auto');
this.svgCanvas.setMode('select');
}
}
/**
*
* @returns {void}
*/
clickFHPath () {
if (this.updateLeftPanel('tool_fhpath')) {
this.svgCanvas.setMode('fhpath');
}
}
/**
*
* @returns {void}
*/
clickLine () {
if (this.updateLeftPanel('tool_line')) {
this.svgCanvas.setMode('line');
}
}
/**
*
* @returns {void}
*/
clickSquare () {
if (this.updateLeftPanel('tool_square')) {
this.svgCanvas.setMode('square');
}
}
/**
*
* @returns {void}
*/
clickRect () {
if (this.updateLeftPanel('tool_rect')) {
this.svgCanvas.setMode('rect');
}
}
/**
*
* @returns {void}
*/
clickFHRect () {
if (this.updateLeftPanel('tool_fhrect')) {
this.svgCanvas.setMode('fhrect');
}
}
/**
*
* @returns {void}
*/
clickCircle () {
if (this.updateLeftPanel('tool_circle')) {
this.svgCanvas.setMode('circle');
}
}
/**
*
* @returns {void}
*/
clickEllipse () {
if (this.updateLeftPanel('tool_ellipse')) {
this.svgCanvas.setMode('ellipse');
}
}
/**
*
* @returns {void}
*/
clickFHEllipse () {
if (this.updateLeftPanel('tool_fhellipse')) {
this.svgCanvas.setMode('fhellipse');
}
}
/**
*
* @returns {void}
*/
clickImage () {
if (this.updateLeftPanel('tool_image')) {
this.svgCanvas.setMode('image');
}
}
/**
*
* @returns {void}
*/
clickZoom () {
if (this.updateLeftPanel('tool_zoom')) {
this.svgCanvas.setMode('zoom');
this.editor.workarea.css('cursor', this.editor.zoomInIcon);
}
}
/**
*
* @returns {void}
*/
dblclickZoom () {
if (this.updateLeftPanel('tool_zoom')) {
this.editor.zoomImage();
this.clickSelect();
}
}
/**
*
* @returns {void}
*/
clickText () {
if (this.updateLeftPanel('tool_text')) {
this.svgCanvas.setMode('text');
}
}
/**
*
* @returns {void}
*/
clickPath () {
if (this.updateLeftPanel('tool_path')) {
this.svgCanvas.setMode('path');
}
}
/**
* @type {module}
*/
add (id, handler) {
$id(id).addEventListener('click', () => {
if (this.updateLeftPanel(id)) {
handler();
}
});
}
/**
* @type {module}
*/
init () {
// register actions for left panel
$id('tool_select').addEventListener('click', this.clickSelect.bind(this));
$id('tool_fhpath').addEventListener('click', this.clickFHPath.bind(this));
$id('tool_text').addEventListener('click', this.clickText.bind(this));
$id('tool_image').addEventListener('click', this.clickImage.bind(this));
$id('tool_zoom').addEventListener('click', this.clickZoom.bind(this));
$id('tool_zoom').addEventListener('dblclick', this.dblclickZoom.bind(this));
$id('tool_path').addEventListener('click', this.clickPath.bind(this));
$id('tool_line').addEventListener('click', this.clickLine.bind(this));
// flyout
$id('tool_rect').addEventListener('click', this.clickRect.bind(this));
$id('tool_square').addEventListener('click', this.clickSquare.bind(this));
$id('tool_fhrect').addEventListener('click', this.clickFHRect.bind(this));
$id('tool_ellipse').addEventListener('click', this.clickEllipse.bind(this));
$id('tool_circle').addEventListener('click', this.clickCircle.bind(this));
$id('tool_fhellipse').addEventListener('click', this.clickFHEllipse.bind(this));
}
}
export default LeftPanelHandlers;

View File

@@ -0,0 +1,632 @@
/* globals $ */
import SvgCanvas from '../../svgcanvas/svgcanvas.js';
import {isValidUnit, getTypeMap, convertUnit} from '../../common/units.js';
const {$id, isNullish} = SvgCanvas;
/*
* register actions for left panel
*/
/**
*
*/
class TopPanelHandlers {
/**
* @param {PlainObject} editor svgedit handler
*/
constructor (editor) {
this.editor = editor;
this.svgCanvas = editor.svgCanvas;
this.uiStrings = editor.uiStrings;
}
/**
* @type {module}
*/
get selectedElement () {
return this.editor.selectedElement;
}
/**
* @type {module}
*/
get multiselected () {
return this.editor.multiselected;
}
/**
* @type {module}
*/
get path () {
return this.svgCanvas.pathActions;
}
/**
* @param {PlainObject} [opts={}]
* @param {boolean} [opts.cancelDeletes=false]
* @returns {void} Resolves to `undefined`
*/
promptImgURL ({cancelDeletes = false} = {}) {
let curhref = this.svgCanvas.getHref(this.selectedElement);
curhref = curhref.startsWith('data:') ? '' : curhref;
// eslint-disable-next-line no-alert
const url = prompt(this.editor.uiStrings.notification.enterNewImgURL, curhref);
if (url) {
this.editor.setImageURL(url);
} else if (cancelDeletes) {
this.svgCanvas.deleteSelectedElements();
}
}
/**
* Updates the context panel tools based on the selected element.
* @returns {void}
*/
updateContextPanel () {
const setInputWidth = (elem) => {
const w = Math.min(Math.max(12 + elem.value.length * 6, 50), 300);
$(elem).width(w);
};
let elem = this.selectedElement;
// If element has just been deleted, consider it null
if (!isNullish(elem) && !elem.parentNode) { elem = null; }
const currentLayerName = this.svgCanvas.getCurrentDrawing().getCurrentLayerName();
const currentMode = this.svgCanvas.getMode();
const unit = this.editor.configObj.curConfig.baseUnit !== 'px' ? this.editor.configObj.curConfig.baseUnit : null;
const isNode = currentMode === 'pathedit'; // elem ? (elem.id && elem.id.startsWith('pathpointgrip')) : false;
const menuItems = document.getElementById('se-cmenu_canvas');
$('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,' +
'#ellipse_panel, #line_panel, #text_panel, #image_panel, #container_panel,' +
' #use_panel, #a_panel').hide();
if (!isNullish(elem)) {
const elname = elem.nodeName;
// If this is a link with no transform and one child, pretend
// its child is selected
// if (elname === 'a') { // && !$(elem).attr('transform')) {
// elem = elem.firstChild;
// }
const angle = this.svgCanvas.getRotationAngle(elem);
$('#angle').val(angle);
const blurval = this.svgCanvas.getBlur(elem) * 10;
$id('blur').value = blurval;
if (this.svgCanvas.addedNew &&
elname === 'image' &&
this.svgCanvas.getMode() === 'image' &&
!this.svgCanvas.getHref(elem).startsWith('data:')) {
/* await */ this.promptImgURL({cancelDeletes: true});
}
if (!isNode && currentMode !== 'pathedit') {
$('#selected_panel').show();
// Elements in this array already have coord fields
if (['line', 'circle', 'ellipse'].includes(elname)) {
$('#xy_panel').hide();
} else {
let x, y;
// Get BBox vals for g, polyline and path
if (['g', 'polyline', 'path'].includes(elname)) {
const bb = this.svgCanvas.getStrokedBBox([elem]);
if (bb) {
({x, y} = bb);
}
} else {
x = elem.getAttribute('x');
y = elem.getAttribute('y');
}
if (unit) {
x = convertUnit(x);
y = convertUnit(y);
}
$('#selected_x').val(x || 0);
$('#selected_y').val(y || 0);
$('#xy_panel').show();
}
// Elements in this array cannot be converted to a path
$id('tool_topath').style.display = ['image', 'text', 'path', 'g', 'use'].includes(elname) ? 'none' : 'block';
$id('tool_reorient').style.display = (elname === 'path') ? 'block' : 'none';
$id('tool_reorient').disabled = (angle === 0);
} else {
const point = this.path.getNodePoint();
$('#tool_add_subpath').pressed = false;
$('#tool_node_delete').toggleClass('disabled', !this.path.canDeleteNodes);
// Show open/close button based on selected point
// setIcon('#tool_openclose_path', path.closed_subpath ? 'open_path' : 'close_path');
if (point) {
const segType = $('#seg_type');
if (unit) {
point.x = convertUnit(point.x);
point.y = convertUnit(point.y);
}
$('#path_node_x').val(point.x);
$('#path_node_y').val(point.y);
if (point.type) {
segType.val(point.type).removeAttr('disabled');
} else {
segType.val(4).attr('disabled', 'disabled');
}
}
return;
}
// update contextual tools here
const panels = {
g: [],
a: [],
rect: ['rx', 'width', 'height'],
image: ['width', 'height'],
circle: ['cx', 'cy', 'r'],
ellipse: ['cx', 'cy', 'rx', 'ry'],
line: ['x1', 'y1', 'x2', 'y2'],
text: [],
use: []
};
const {tagName} = elem;
// if ($(elem).data('gsvg')) {
// $('#g_panel').show();
// }
let linkHref = null;
if (tagName === 'a') {
linkHref = this.svgCanvas.getHref(elem);
$('#g_panel').show();
}
if (elem.parentNode.tagName === 'a' && !$(elem).siblings().length) {
$('#a_panel').show();
linkHref = this.svgCanvas.getHref(elem.parentNode);
}
// Hide/show the make_link buttons
$('#tool_make_link, #tool_make_link_multi').toggle(!linkHref);
if (linkHref) {
$('#link_url').val(linkHref);
}
if (panels[tagName]) {
const curPanel = panels[tagName];
$('#' + tagName + '_panel').show();
curPanel.forEach((item) => {
let attrVal = elem.getAttribute(item);
if (this.editor.configObj.curConfig.baseUnit !== 'px' && elem[item]) {
const bv = elem[item].baseVal.value;
attrVal = convertUnit(bv);
}
$id(`${tagName}_${item}`).value = attrVal || 0;
});
if (tagName === 'text') {
$('#text_panel').css('display', 'inline');
$('#tool_font_size').css('display', 'inline');
$id('tool_italic').pressed = this.svgCanvas.getItalic();
$id('tool_bold').pressed = this.svgCanvas.getBold();
$('#font_family').val(elem.getAttribute('font-family'));
$('#font_size').val(elem.getAttribute('font-size'));
$('#text').val(elem.textContent);
const textAnchorStart = $id('tool_text_anchor_start');
const textAnchorMiddle = $id('tool_text_anchor_middle');
const textAnchorEnd = $id('tool_text_anchor_end');
switch (elem.getAttribute('text-anchor')) {
case 'start':
textAnchorStart.pressed = true;
textAnchorMiddle.pressed = false;
textAnchorEnd.pressed = false;
break;
case 'middle':
textAnchorStart.pressed = false;
textAnchorMiddle.pressed = true;
textAnchorEnd.pressed = false;
break;
case 'end':
textAnchorStart.pressed = false;
textAnchorMiddle.pressed = false;
textAnchorEnd.pressed = true;
break;
}
if (this.svgCanvas.addedNew) {
// Timeout needed for IE9
setTimeout(() => {
$('#text').focus().select();
}, 100);
}
// text
} else if (tagName === 'image' && this.svgCanvas.getMode() === 'image') {
this.svgCanvas.setImageURL(this.svgCanvas.getHref(elem));
// image
} else if (tagName === 'g' || tagName === 'use') {
$('#container_panel').show();
const title = this.svgCanvas.getTitle();
const label = $('#g_title')[0];
label.value = title;
setInputWidth(label);
$('#g_title').prop('disabled', tagName === 'use');
}
}
menuItems.setAttribute((tagName === 'g' ? 'en' : 'dis') + 'ablemenuitems', '#ungroup');
menuItems.setAttribute(((tagName === 'g' || !this.multiselected) ? 'dis' : 'en') + 'ablemenuitems', '#group');
// if (!isNullish(elem))
} else if (this.multiselected) {
$('#multiselected_panel').show();
menuItems.setAttribute('enablemenuitems', '#group');
menuItems.setAttribute('disablemenuitems', '#ungroup');
} else {
menuItems.setAttribute('disablemenuitems', '#delete,#cut,#copy,#group,#ungroup,#move_front,#move_up,#move_down,#move_back');
}
// update history buttons
$id('tool_undo').disabled = (this.svgCanvas.undoMgr.getUndoStackSize() === 0);
$id('tool_redo').disabled = (this.svgCanvas.undoMgr.getRedoStackSize() === 0);
this.svgCanvas.addedNew = false;
if ((elem && !isNode) || this.multiselected) {
// update the selected elements' layer
$('#selLayerNames').removeAttr('disabled').val(currentLayerName);
// Enable regular menu options
const canCMenu = document.getElementById('se-cmenu_canvas');
canCMenu.setAttribute('enablemenuitems', '#delete,#cut,#copy,#move_front,#move_up,#move_down,#move_back');
} else {
$('#selLayerNames').attr('disabled', 'disabled');
}
}
/**
* @param {Event} [e] Not used.
* @param {boolean} forSaving
* @returns {void}
*/
showSourceEditor (e, forSaving) {
const $editorDialog = document.getElementById('se-svg-editor-dialog');
if ($editorDialog.getAttribute('dialog') === 'open') return;
const origSource = this.svgCanvas.getSvgString();
$editorDialog.setAttribute('dialog', 'open');
$editorDialog.setAttribute('value', origSource);
$editorDialog.setAttribute('copysec', Boolean(forSaving));
$editorDialog.setAttribute('applysec', !forSaving);
}
/**
*
* @returns {void}
*/
clickWireframe () {
$id('tool_wireframe').pressed = !$id('tool_wireframe').pressed;
this.editor.workarea.toggleClass('wireframe');
const wfRules = $('#wireframe_rules');
if (!wfRules.length) {
/* wfRules = */ $('<style id="wireframe_rules"></style>').appendTo('head');
} else {
wfRules.empty();
}
this.editor.updateWireFrame();
}
/**
*
* @returns {void}
*/
clickUndo () {
const {undoMgr} = this.editor.svgCanvas;
if (undoMgr.getUndoStackSize() > 0) {
undoMgr.undo();
this.editor.layersPanel.populateLayers();
}
}
/**
*
* @returns {void}
*/
clickRedo () {
const {undoMgr} = this.editor.svgCanvas;
if (undoMgr.getRedoStackSize() > 0) {
undoMgr.redo();
this.editor.layersPanel.populateLayers();
}
}
/**
* @type {module}
*/
changeRectRadius (e) {
this.svgCanvas.setRectRadius(e.target.value);
}
/**
* @type {module}
*/
changeFontSize (e) {
this.svgCanvas.setFontSize(e.target.value);
}
/**
* @type {module}
*/
changeRotationAngle (e) {
this.svgCanvas.setRotationAngle(e.target.value);
$('#tool_reorient').toggleClass('disabled', Number.parseInt(e.target.value) === 0);
}
/**
* @param {PlainObject} e
* @returns {void}
*/
changeBlur (e) {
this.svgCanvas.setBlur(e.target.value / 10, true);
}
/**
*
* @returns {void}
*/
clickGroup () {
// group
if (this.editor.multiselected) {
this.svgCanvas.groupSelectedElements();
// ungroup
} else if (this.editor.selectedElement) {
this.svgCanvas.ungroupSelectedElement();
}
}
/**
*
* @returns {void}
*/
clickClone () {
this.svgCanvas.cloneSelectedElements(20, 20);
}
/**
* @param {string} pos indicate the alignment relative to top, bottom, middle etc..
* @returns {void}
*/
clickAlign (pos) {
this.svgCanvas.alignSelectedElements(pos, $('#align_relative_to').val());
}
/**
*
* @type {module}
*/
attrChanger (e) {
const attr = e.target.getAttribute('data-attr');
let val = e.target.value;
const valid = isValidUnit(attr, val, this.selectedElement);
if (!valid) {
e.target.value = this.selectedElement().getAttribute(attr);
// eslint-disable-next-line no-alert
alert(this.uiStrings.notification.invalidAttrValGiven);
return false;
}
if (attr !== 'id' && attr !== 'class') {
if (isNaN(val)) {
val = this.svgCanvas.convertToNum(attr, val);
} else if (this.editor.configObj.curConfig.baseUnit !== 'px') {
// Convert unitless value to one with given unit
const unitData = getTypeMap();
if (this.selectedElement[attr] || this.svgCanvas.getMode() === 'pathedit' || attr === 'x' || attr === 'y') {
val *= unitData[this.editor.configObj.curConfig.baseUnit];
}
}
}
// if the user is changing the id, then de-select the element first
// change the ID, then re-select it with the new ID
if (attr === 'id') {
const elem = this.selectedElement;
this.svgCanvas.clearSelection();
elem.id = val;
this.svgCanvas.addToSelection([elem], true);
} else {
this.svgCanvas.changeSelectedAttribute(attr, val);
}
return true;
}
/**
*
* @returns {void}
*/
convertToPath () {
if (!isNullish(this.selectedElement)) {
this.svgCanvas.convertToPath();
}
}
/**
*
* @returns {void}
*/
reorientPath () {
if (!isNullish(this.selectedElement)) {
this.path.reorient();
}
}
/**
*
* @returns {void} Resolves to `undefined`
*/
makeHyperlink () {
if (!isNullish(this.selectedElement) || this.multiselected) {
// eslint-disable-next-line no-alert
const url = prompt(this.uiStrings.notification.enterNewLinkURL, 'http://');
if (url) {
this.svgCanvas.makeHyperlink(url);
}
}
}
/**
*
* @returns {void}
*/
linkControlPoints () {
const linked = $id('tool_node_link').pressed;
$id('tool_node_link').pressed = !linked;
this.path.linkControlPoints(linked);
}
/**
*
* @returns {void}
*/
clonePathNode () {
if (this.path.getNodePoint()) {
this.path.clonePathNode();
}
}
/**
*
* @returns {void}
*/
deletePathNode () {
if (this.path.getNodePoint()) {
this.path.deletePathNode();
}
}
/**
*
* @returns {void}
*/
addSubPath () {
const button = $('#tool_add_subpath');
const sp = !button.hasClass('pressed');
button.pressed = sp;
// button.toggleClass('push_button_pressed tool_button');
this.path.addSubPath(sp);
}
/**
*
* @returns {void}
*/
opencloseSubPath () {
this.path.opencloseSubPath();
}
/**
* Delete is a contextual tool that only appears in the ribbon if
* an element has been selected.
* @returns {void}
*/
deleteSelected () {
if (!isNullish(this.selectedElement) || this.multiselected) {
this.svgCanvas.deleteSelectedElements();
}
}
/**
*
* @returns {void}
*/
moveToTopSelected () {
if (!isNullish(this.selectedElement)) {
this.svgCanvas.moveToTopSelectedElement();
}
}
/**
*
* @returns {void}
*/
moveToBottomSelected () {
if (!isNullish(this.selectedElement)) {
this.svgCanvas.moveToBottomSelectedElement();
}
}
/**
*
* @returns {false}
*/
clickBold () {
this.svgCanvas.setBold(!this.svgCanvas.getBold());
this.updateContextPanel();
return false;
}
/**
*
* @returns {false}
*/
clickItalic () {
this.svgCanvas.setItalic(!this.svgCanvas.getItalic());
this.updateContextPanel();
return false;
}
/**
*
* @param {string} value "start","end" or "middle"
* @returns {false}
*/
clickTextAnchor (value) {
this.svgCanvas.setTextAnchor(value);
this.updateContextPanel();
return false;
}
/**
* @type {module}
*/
init () {
// svg editor source dialoag added to DOM
const newSeEditorDialog = document.createElement('se-svg-source-editor-dialog');
newSeEditorDialog.setAttribute('id', 'se-svg-editor-dialog');
document.body.append(newSeEditorDialog);
// register action to top panel buttons
$id('tool_source').addEventListener('click', this.showSourceEditor.bind(this));
$id('tool_wireframe').addEventListener('click', this.clickWireframe.bind(this));
$id('tool_undo').addEventListener('click', this.clickUndo.bind(this));
$id('tool_redo').addEventListener('click', this.clickRedo.bind(this));
$id('tool_clone').addEventListener('click', this.clickClone.bind(this));
$id('tool_clone_multi').addEventListener('click', this.clickClone.bind(this));
$id('tool_delete').addEventListener('click', this.deleteSelected.bind(this));
$id('tool_delete_multi').addEventListener('click', this.deleteSelected.bind(this));
$id('tool_move_top').addEventListener('click', this.moveToTopSelected.bind(this));
$id('tool_move_bottom').addEventListener('click', this.moveToBottomSelected.bind(this));
$id('tool_topath').addEventListener('click', this.convertToPath.bind(this));
$id('tool_make_link').addEventListener('click', this.makeHyperlink.bind(this));
$id('tool_make_link_multi').addEventListener('click', this.makeHyperlink.bind(this));
$id('tool_reorient').addEventListener('click', this.reorientPath.bind(this));
$id('tool_group_elements').addEventListener('click', this.clickGroup.bind(this));
$id('tool_align_left').addEventListener('click', () => this.clickAlign.bind(this)('left'));
$id('tool_align_right').addEventListener('click', () => this.clickAlign.bind(this)('right'));
$id('tool_align_center').addEventListener('click', () => this.clickAlign.bind(this)('center'));
$id('tool_align_top').addEventListener('click', () => this.clickAlign.bind(this)('top'));
$id('tool_align_bottom').addEventListener('click', () => this.clickAlign.bind(this)('bottom'));
$id('tool_align_middle').addEventListener('click', () => this.clickAlign.bind(this)('middle'));
$id('tool_node_clone').addEventListener('click', this.clonePathNode.bind(this));
$id('tool_node_delete').addEventListener('click', this.deletePathNode.bind(this));
$id('tool_openclose_path').addEventListener('click', this.opencloseSubPath.bind(this));
$id('tool_add_subpath').addEventListener('click', this.addSubPath.bind(this));
$id('tool_node_link').addEventListener('click', this.linkControlPoints.bind(this));
$id('angle').addEventListener('change', this.changeRotationAngle.bind(this));
$id('blur').addEventListener('change', this.changeBlur.bind(this));
$id('rect_rx').addEventListener('change', this.changeRectRadius.bind(this));
$id('font_size').addEventListener('change', this.changeFontSize.bind(this));
$id('tool_ungroup').addEventListener('click', this.clickGroup.bind(this));
$id('tool_bold').addEventListener('click', this.clickBold.bind(this));
$id('tool_italic').addEventListener('click', this.clickItalic.bind(this));
$id('tool_text_anchor_start').addEventListener('click', () => this.clickTextAnchor.bind(this)('start'));
$id('tool_text_anchor_middle').addEventListener('click', () => this.clickTextAnchor.bind(this)('middle'));
$id('tool_text_anchor_end').addEventListener('click', () => this.clickTextAnchor.bind(this)('end'));
$id('tool_unlink_use').addEventListener('click', this.clickGroup.bind(this));
$id('change_image_url').addEventListener('click', this.promptImgURL.bind(this));
// all top panel attributes
['elem_id', 'elem_class', 'circle_cx', 'circle_cy', 'circle_r', 'ellipse_cx',
'ellipse_cy', 'ellipse_rx', 'ellipse_ry', 'selected_x', 'selected_y', 'rect_width',
'rect_height', 'line_x1', 'line_x2', 'line_y2', 'image_width', 'image_height', 'path_node_x',
'path_node_y'].forEach((attrId) => $id(attrId).addEventListener('change', this.attrChanger.bind(this)));
}
}
export default TopPanelHandlers;

View File

@@ -379,25 +379,6 @@ hr {
/*—————————————————————————————*/
.tool_button:hover,
.push_button:hover,
.buttonup:hover,
.buttondown,
.tool_button_current,
.push_button_pressed
{
background-color: #ffc !important;
}
.tool_button_current,
.push_button_pressed,
.buttondown {
background-color: #f4e284 !important;
-webkit-box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4), 1px 1px 0 white !important;
-moz-box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4), 1px 1px 0 white !important;
box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4), 1px 1px 0 white !important;
}
#tools_top {
position: absolute;
left: 108px;
@@ -405,7 +386,6 @@ hr {
top: 2px;
height: 40px;
border-bottom: none;
overflow: auto;
}
#tools_top .tool_sep {
@@ -515,12 +495,6 @@ input[type=text] {
padding: 2px;
}
#tools_left .tool_button,
#tools_left .tool_button_current {
position: relative;
z-index: 11;
}
.dropdown {
position: relative;
}
@@ -592,21 +566,6 @@ input[type=text] {
margin-right: 0;
}
.tool_button,
.push_button,
.tool_button_current,
.push_button_pressed
{
height: 24px;
width: 24px;
margin: 2px 2px 4px 2px;
padding: 3px;
box-shadow: inset 1px 1px 2px white, 1px 1px 1px rgba(0,0,0,0.3);
background-color: #E8E8E8;
cursor: pointer;
border-radius: 3px;
}
#main_menu li#tool_open, #main_menu li#tool_import {
position: relative;
overflow: hidden;
@@ -683,10 +642,6 @@ input[type=text] {
float: right;
}
.dropdown li.tool_button {
width: 24px;
}
#stroke_expand {
width: 0;
overflow: hidden;

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
* @license MIT
* @copyright 2011 Jeff Schiller
*/
import jQueryPluginSVG from '../common/jQuery.attr.js';
import jQueryPluginSVG from './jQuery.attr.js';
import {NS} from '../common/namespaces.js';
const $ = jQueryPluginSVG(jQuery);

View File

@@ -7,11 +7,11 @@
import {
snapToGrid, assignAttributes, getBBox, getRefElem, findDefs
} from '../common/utilities.js';
} from './utilities.js';
import {
transformPoint, transformListToTransform, matrixMultiply, transformBox
} from '../common/math.js';
import {getTransformList} from '../common/svgtransformlist.js';
} from './math.js';
import {getTransformList} from './svgtransformlist.js';
const $ = jQuery;

View File

@@ -1,9 +1,9 @@
/* globals jQuery */
import jQueryPluginSVG from '../common/jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import {isWebkit} from '../common/browser.js';
import {convertPath} from './path.js';
import {preventClickDefault} from '../common/utilities.js';
import {preventClickDefault} from './utilities.js';
// Constants
const $ = jQueryPluginSVG(jQuery);

View File

@@ -1,178 +0,0 @@
/**
* @module jQueryPluginDBox
*/
/**
* @param {external:jQuery} $
* @param {PlainObject} [strings]
* @param {PlainObject} [strings.ok]
* @param {PlainObject} [strings.cancel]
* @returns {external:jQuery}
*/
export default function jQueryPluginDBox ($, {
ok: okString = 'Ok',
cancel: cancelString = 'Cancel'
} = {}) {
// This sets up alternative dialog boxes. They mostly work the same way as
// their UI counterparts, expect instead of returning the result, a callback
// needs to be included that returns the result as its first parameter.
// In the future we may want to add additional types of dialog boxes, since
// they should be easy to handle this way.
$('#dialog_container').draggable({
cancel: '#dialog_content, #dialog_buttons *',
containment: 'window'
}).css('position', 'absolute');
const box = $('#dialog_box'),
btnHolder = $('#dialog_buttons'),
dialogContent = $('#dialog_content');
/**
* @typedef {PlainObject} module:jQueryPluginDBox.PromiseResultObject
* @property {string|true} response
* @property {boolean} checked
*/
/**
* Resolves to `false` (if cancelled), for prompts and selects
* without checkboxes, it resolves to the value of the form control. For other
* types without checkboxes, it resolves to `true`. For checkboxes, it resolves
* to an object with the `response` key containing the same value as the previous
* mentioned (string or `true`) and a `checked` (boolean) property.
* @typedef {Promise<boolean|string|module:jQueryPluginDBox.PromiseResultObject>} module:jQueryPluginDBox.ResultPromise
*/
/**
* @typedef {PlainObject} module:jQueryPluginDBox.SelectOption
* @property {string} text
* @property {string} value
*/
/**
* @typedef {PlainObject} module:jQueryPluginDBox.CheckboxInfo
* @property {string} label Label for the checkbox
* @property {string} value Value of the checkbox
* @property {string} tooltip Tooltip on the checkbox label
* @property {boolean} checked Whether the checkbox is checked by default
*/
/**
* Triggered upon a change of value for the select pull-down.
* @callback module:jQueryPluginDBox.SelectChangeListener
* @returns {void}
*/
/**
* Creates a dialog of the specified type with a given message
* and any defaults and type-specific metadata. Returns a `Promise`
* which resolves differently depending on whether the dialog
* was cancelled or okayed (with the response and any checked state).
* @param {"alert"|"prompt"|"select"|"process"} type
* @param {string} msg
* @param {string} [defaultVal]
* @param {module:jQueryPluginDBox.SelectOption[]} [opts]
* @param {module:jQueryPluginDBox.SelectChangeListener} [changeListener]
* @param {module:jQueryPluginDBox.CheckboxInfo} [checkbox]
* @returns {jQueryPluginDBox.ResultPromise}
*/
function dbox (type, msg, defaultVal, opts, changeListener, checkbox) {
dialogContent.html('<p>' + msg.replace(/\n/g, '</p><p>') + '</p>')
.toggleClass('prompt', (type === 'prompt'));
btnHolder.empty();
const ok = $('<input type="button" data-ok="" value="' + okString + '">').appendTo(btnHolder);
return new Promise((resolve, reject) => { // eslint-disable-line promise/avoid-new
if (type !== 'alert') {
$('<input type="button" value="' + cancelString + '">')
.appendTo(btnHolder)
.click(function () {
box.hide();
resolve(false);
});
}
let ctrl, chkbx;
if (type === 'prompt') {
ctrl = $('<input type="text">').prependTo(btnHolder);
ctrl.val(defaultVal || '');
ctrl.bind('keydown', 'return', function () { ok.click(); });
} else if (type === 'select') {
const div = $('<div style="text-align:center;">');
ctrl = $(`<select aria-label="${msg}">`).appendTo(div);
if (checkbox) {
const label = $('<label>').text(checkbox.label);
chkbx = $('<input type="checkbox">').appendTo(label);
chkbx.val(checkbox.value);
if (checkbox.tooltip) {
label.attr('title', checkbox.tooltip);
}
chkbx.prop('checked', Boolean(checkbox.checked));
div.append($('<div>').append(label));
}
$.each(opts || [], function (opt, val) {
if (typeof val === 'object') {
ctrl.append($('<option>').val(val.value).html(val.text));
} else {
ctrl.append($('<option>').html(val));
}
});
dialogContent.append(div);
if (defaultVal) {
ctrl.val(defaultVal);
}
if (changeListener) {
ctrl.bind('change', 'return', changeListener);
}
ctrl.bind('keydown', 'return', function () { ok.click(); });
} else if (type === 'process') {
ok.hide();
}
box.show();
ok.click(function () {
box.hide();
const response = (type === 'prompt' || type === 'select') ? ctrl.val() : true;
if (chkbx) {
resolve({response, checked: chkbx.prop('checked')});
return;
}
resolve(response);
}).focus();
if (type === 'prompt' || type === 'select') {
ctrl.focus();
}
});
}
/**
* @param {string} msg Message to alert
* @returns {jQueryPluginDBox.ResultPromise}
*/
$.alert = function (msg) {
return dbox('alert', msg);
};
/**
* @param {string} msg Message for which to ask confirmation
* @returns {jQueryPluginDBox.ResultPromise}
*/
$.confirm = function (msg) {
return dbox('confirm', msg);
};
/**
* @param {string} msg Message to indicate upon cancelable indicator
* @returns {jQueryPluginDBox.ResultPromise}
*/
$.process_cancel = function (msg) {
return dbox('process', msg);
};
/**
* @param {string} msg Message to accompany the prompt
* @param {string} [defaultText=""] The default text to show for the prompt
* @returns {jQueryPluginDBox.ResultPromise}
*/
$.prompt = function (msg, defaultText = '') {
return dbox('prompt', msg, defaultText);
};
$.select = function (msg, opts, changeListener, txt, checkbox) {
return dbox('select', msg, txt, opts, changeListener, checkbox);
};
return $;
}

View File

@@ -13,7 +13,7 @@ import {NS} from '../common/namespaces.js';
import {isOpera} from '../common/browser.js';
import {
toXml, getElem
} from '../common/utilities.js';
} from './utilities.js';
import {
copyElem as utilCopyElem
} from './copy-elem.js';

View File

@@ -6,12 +6,12 @@
*/
import * as hstry from './history.js';
import jQueryPluginSVG from '../common/jQuery.attr.js';
import jQueryPluginSVG from './jQuery.attr.js';
import {NS} from '../common/namespaces.js';
import {
getVisibleElements, getStrokedBBoxDefaultVisible, findDefs,
walkTree, isNullish, getHref, setHref, getElem
} from '../common/utilities.js';
} from './utilities.js';
import {
convertToNum
} from '../common/units.js';

View File

@@ -5,20 +5,20 @@
* @license MIT
* @copyright 2011 Jeff Schiller
*/
import jQueryPluginSVG from '../common/jQuery.attr.js'; // Needed for SVG attribute
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute
import {
assignAttributes, cleanupElement, getElem, getRotationAngle, snapToGrid, walkTree,
getBBox as utilsGetBBox, isNullish, preventClickDefault, setHref
} from '../common/utilities.js';
} from './utilities.js';
import {
convertAttrs
} from '../common/units.js';
import {
transformPoint, hasMatrixTransform, getMatrix, snapToAngle
} from '../common/math.js';
} from './math.js';
import {
getTransformList
} from '../common/svgtransformlist.js';
} from './svgtransformlist.js';
import {
supportsNonScalingStroke, isWebkit
} from '../common/browser.js';

View File

@@ -6,8 +6,8 @@
* @copyright 2010 Jeff Schiller
*/
import {getHref, setHref, getRotationAngle, isNullish} from '../common/utilities.js';
import {removeElementFromListMap} from '../common/svgtransformlist.js';
import {getHref, setHref, getRotationAngle, isNullish} from './utilities.js';
import {removeElementFromListMap} from './svgtransformlist.js';
/**
* Group: Undo/Redo history management.

View File

@@ -5,7 +5,7 @@
*
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
*/
import {getElem, assignAttributes, cleanupElement} from '../common/utilities.js';
import {getElem, assignAttributes, cleanupElement} from './utilities.js';
import {NS} from '../common/namespaces.js';
let jsonContext_ = null;

View File

@@ -8,7 +8,7 @@
*/
import {NS} from '../common/namespaces.js';
import {toXml, walkTree, isNullish} from '../common/utilities.js';
import {toXml, walkTree, isNullish} from './utilities.js';
const $ = jQuery;

View File

@@ -19,7 +19,7 @@
* @property {Float} y
*/
import {NS} from './namespaces.js';
import {NS} from '../common/namespaces.js';
import {getTransformList} from './svgtransformlist.js';
// Constants

View File

@@ -1,9 +1,9 @@
/* globals jQuery */
import jQueryPluginSVG from '../common/jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import {
getStrokedBBoxDefaultVisible
} from '../common/utilities.js';
} from './utilities.js';
import * as hstry from './history.js';
// Constants
const $ = jQueryPluginSVG(jQuery);

View File

@@ -9,16 +9,16 @@
import {NS} from '../common/namespaces.js';
import {shortFloat} from '../common/units.js';
import {getTransformList} from '../common/svgtransformlist.js';
import {getTransformList} from './svgtransformlist.js';
import {ChangeElementCommand, BatchCommand} from './history.js';
import {
transformPoint, snapToAngle, rectsIntersect,
transformListToTransform
} from '../common/math.js';
} from './math.js';
import {
assignAttributes, getElem, getRotationAngle, snapToGrid, isNullish,
getBBox as utilsGetBBox
} from '../common/utilities.js';
} from './utilities.js';
import {
isWebkit
} from '../common/browser.js';

View File

@@ -11,11 +11,11 @@ import {NS} from '../common/namespaces.js';
import {ChangeElementCommand} from './history.js';
import {
transformPoint, getMatrix
} from '../common/math.js';
} from './math.js';
import {
assignAttributes, getRotationAngle, isNullish,
getElem
} from '../common/utilities.js';
} from './utilities.js';
import {
supportsPathInsertItemBefore, supportsPathReplaceItem, isWebkit
} from '../common/browser.js';

View File

@@ -7,14 +7,14 @@
* @copyright 2011 Alexis Deveria, 2011 Jeff Schiller
*/
import {getTransformList} from '../common/svgtransformlist.js';
import {getTransformList} from './svgtransformlist.js';
import {shortFloat} from '../common/units.js';
import {transformPoint} from '../common/math.js';
import {transformPoint} from './math.js';
import {
getRotationAngle, getBBox,
getRefElem, findDefs, isNullish,
getBBox as utilsGetBBox
} from '../common/utilities.js';
} from './utilities.js';
import {
init as pathMethodInit, insertItemBeforeMethod, ptObjToArrMethod, getGripPtMethod,
getPointFromGripMethod, addPointGripMethod, getGripContainerMethod, addCtrlGripMethod,

View File

@@ -5,18 +5,18 @@
* @license MIT
*/
import jQueryPluginSVG from '../common/jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import {NS} from '../common/namespaces.js';
import {convertToNum} from '../common/units.js';
import {isWebkit} from '../common/browser.js';
import {getTransformList} from '../common/svgtransformlist.js';
import {getRotationAngle, getHref, getBBox, getRefElem, isNullish} from '../common/utilities.js';
import {getTransformList} from './svgtransformlist.js';
import {getRotationAngle, getHref, getBBox, getRefElem, isNullish} from './utilities.js';
import {BatchCommand, ChangeElementCommand} from './history.js';
import {remapElement} from './coords.js';
import {
isIdentity, matrixMultiply, transformPoint, transformListToTransform,
hasMatrixTransform
} from '../common/math.js';
} from './math.js';
const $ = jQueryPluginSVG(jQuery);

View File

@@ -8,7 +8,7 @@
import {getReverseNS, NS} from '../common/namespaces.js';
import {isGecko} from '../common/browser.js';
import {getHref, setHref, getUrlFromAttr} from '../common/utilities.js';
import {getHref, setHref, getUrlFromAttr} from './utilities.js';
const REVERSE_NS = getReverseNS();

View File

@@ -8,9 +8,9 @@
*/
import {isTouch, isWebkit} from '../common/browser.js'; // , isOpera
import {getRotationAngle, getBBox, getStrokedBBox, isNullish} from '../common/utilities.js';
import {transformListToTransform, transformBox, transformPoint} from '../common/math.js';
import {getTransformList} from '../common/svgtransformlist.js';
import {getRotationAngle, getBBox, getStrokedBBox, isNullish} from './utilities.js';
import {transformListToTransform, transformBox, transformPoint} from './math.js';
import {getTransformList} from './svgtransformlist.js';
const $ = jQuery;

View File

@@ -6,20 +6,20 @@
*
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
*/
import jQueryPluginSVG from '../common/jQuery.attr.js'; // Needed for SVG attribute
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute
import {NS} from '../common/namespaces.js';
import * as hstry from './history.js';
import * as pathModule from './path.js';
import {
isNullish, getStrokedBBoxDefaultVisible, setHref, getElem, getHref, getVisibleElements,
findDefs, getRotationAngle, getRefElem, getBBox as utilsGetBBox, walkTreePost, assignAttributes
} from '../common/utilities.js';
} from './utilities.js';
import {
transformPoint, matrixMultiply, transformListToTransform
} from '../common/math.js';
} from './math.js';
import {
getTransformList
} from '../common/svgtransformlist.js';
} from './svgtransformlist.js';
import {
recalculateDimensions
} from './recalculate.js';

View File

@@ -9,12 +9,12 @@
import {NS} from '../common/namespaces.js';
import {
isNullish, getBBox as utilsGetBBox, getStrokedBBoxDefaultVisible
} from '../common/utilities.js';
import {transformPoint, transformListToTransform, rectsIntersect} from '../common/math.js';
import jQueryPluginSVG from '../common/jQuery.attr.js';
} from './utilities.js';
import {transformPoint, transformListToTransform, rectsIntersect} from './math.js';
import jQueryPluginSVG from './jQuery.attr.js';
import {
getTransformList
} from '../common/svgtransformlist.js';
} from './svgtransformlist.js';
import * as hstry from './history.js';
const {BatchCommand} = hstry;

View File

@@ -8,17 +8,17 @@
import {jsPDF} from 'jspdf/dist/jspdf.es.min.js';
import 'svg2pdf.js/dist/svg2pdf.es.js';
import jQueryPluginSVG from '../common/jQuery.attr.js';
import jQueryPluginSVG from './jQuery.attr.js';
import * as hstry from './history.js';
import {
text2xml, cleanupElement, findDefs, getHref, preventClickDefault,
toXml, getStrokedBBoxDefaultVisible, encode64, createObjectURL,
dataURLToObjectURL, walkTree, getBBox as utilsGetBBox
} from '../common/utilities.js';
} from './utilities.js';
import {
transformPoint, transformListToTransform
} from '../common/math.js';
import {resetListMap} from '../common/svgtransformlist.js';
} from './math.js';
import {resetListMap} from './svgtransformlist.js';
import {
convertUnit, shortFloat, convertToNum
} from '../common/units.js';

View File

@@ -17,8 +17,7 @@
import {Canvg as canvg} from 'canvg';
import 'pathseg';
import jQueryPluginSVG from '../common/jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import jQueryPluginDBox from './dbox.js';
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import * as pathModule from './path.js';
import * as hstry from './history.js';
@@ -78,12 +77,13 @@ import {
findDefs, getHref, setHref, getRefElem, getRotationAngle, getPathBBox,
preventClickDefault, walkTree, getBBoxOfElementAsPath, convertToPath, encode64, decode64,
getVisibleElements, dropXMLInternalSubset, init as utilsInit,
getBBox as utilsGetBBox, getStrokedBBoxDefaultVisible, isNullish
} from '../common/utilities.js';
getBBox as utilsGetBBox, getStrokedBBoxDefaultVisible, isNullish, blankPageObjectURL,
$id, $qa, $qq
} from './utilities.js';
import {
transformPoint, matrixMultiply, hasMatrixTransform, transformListToTransform,
isIdentity, transformBox
} from '../common/math.js';
} from './math.js';
import {
convertToNum, getTypeMap, init as unitsInit
} from '../common/units.js';
@@ -97,7 +97,7 @@ import {
} from '../common/browser.js'; // , supportsEditableText
import {
getTransformList, SVGTransformList as SVGEditTransformList
} from '../common/svgtransformlist.js';
} from './svgtransformlist.js';
import {
remapElement,
init as coordsInit
@@ -116,7 +116,7 @@ import {
init as clearInit
} from './clear.js';
let $ = jQueryPluginSVG(jQuery);
const $ = jQueryPluginSVG(jQuery);
const {
MoveElementCommand, InsertElementCommand, RemoveElementCommand,
ChangeElementCommand, BatchCommand
@@ -168,7 +168,7 @@ if (window.opera) {
class SvgCanvas {
/**
* @param {HTMLElement} container - The container HTML element that should hold the SVG root element
* @param {module:SVGEditor.curConfig} config - An object that contains configuration data
* @param {module:SVGeditor.configObj.curConfig} config - An object that contains configuration data
*/
constructor (container, config) {
// Alias Namespace constants
@@ -1740,7 +1740,6 @@ class SvgCanvas {
*/
this.setUiStrings = function (strs) {
Object.assign(uiStrings, strs.notification);
$ = jQueryPluginDBox($, strs.common);
pathModule.setUiStrings(strs);
};
@@ -2743,4 +2742,14 @@ class SvgCanvas {
} // End constructor
} // End class
// attach utilities function to the class that are used by SvgEdit so
// we can avoid using the whole utilities.js file in svgEdit.js
SvgCanvas.isNullish = isNullish;
SvgCanvas.encode64 = encode64;
SvgCanvas.decode64 = decode64;
SvgCanvas.$id = $id;
SvgCanvas.$qq = $qq;
SvgCanvas.$qa = $qa;
SvgCanvas.blankPageObjectURL = blankPageObjectURL;
export default SvgCanvas;

View File

@@ -6,7 +6,7 @@
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
*/
import {NS} from '../common/namespaces.js';
import {text2xml} from '../common/utilities.js';
import {text2xml} from './utilities.js';
/**
* @function module:svgcanvas.svgRootElement svgRootElement the svg node and its children.

View File

@@ -7,8 +7,8 @@
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
*/
import {NS} from './namespaces.js';
import {supportsNativeTransformLists} from './browser.js';
import {NS} from '../common/namespaces.js';
import {supportsNativeTransformLists} from '../common/browser.js';
const svgroot = document.createElementNS(NS.SVG, 'svg');

View File

@@ -6,14 +6,14 @@
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
*/
import jQueryPluginSVG from '../common/jQuery.attr.js';
import jQueryPluginSVG from './jQuery.attr.js';
import {NS} from '../common/namespaces.js';
import {
transformPoint, getMatrix
} from '../common/math.js';
} from './math.js';
import {
assignAttributes, getElem, getBBox as utilsGetBBox
} from '../common/utilities.js';
} from './utilities.js';
import {
supportsGoodTextCharPos
} from '../common/browser.js';
@@ -263,15 +263,6 @@ export const textActionsMethod = (function () {
return out;
}
/*
// Not currently in use
function hideCursor () {
if (cursor) {
cursor.setAttribute('visibility', 'hidden');
}
}
*/
/**
*
* @param {Event} evt

View File

@@ -8,16 +8,16 @@ import * as draw from './draw.js';
import * as hstry from './history.js';
import {
getRotationAngle, getBBox as utilsGetBBox, isNullish, setHref, getStrokedBBoxDefaultVisible
} from '../common/utilities.js';
} from './utilities.js';
import {
isGecko
} from '../common/browser.js';
import {
transformPoint, transformListToTransform
} from '../common/math.js';
} from './math.js';
import {
getTransformList
} from '../common/svgtransformlist.js';
} from './svgtransformlist.js';
const {
UndoManager, HistoryEventTypes

View File

@@ -8,16 +8,16 @@
*/
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import {NS} from './namespaces.js';
import {NS} from '../common/namespaces.js';
import {getTransformList} from './svgtransformlist.js';
import {setUnitAttr, getTypeMap} from './units.js';
import {setUnitAttr, getTypeMap} from '../common/units.js';
import {
hasMatrixTransform, transformListToTransform, transformBox
} from './math.js';
import {
isWebkit, supportsHVLineContainerBBox, supportsPathBBox, supportsXpath,
supportsSelectors
} from './browser.js';
} from '../common/browser.js';
// Constants
const $ = jQueryPluginSVG(jQuery);
@@ -1298,17 +1298,6 @@ export const snapToGrid = function (value) {
return value;
};
/**
* Escapes special characters in a regular expression.
* @function module:utilities.regexEscape
* @param {string} str
* @returns {string}
*/
export const regexEscape = function (str) {
// Originally from: http://phpjs.org/functions
return String(str).replace(/[.\\+*?[^\]$(){}=!<>|:-]/g, '\\$&');
};
/**
* Prevents default browser click behaviour on the given element.
* @function module:utilities.preventClickDefault
@@ -1350,6 +1339,7 @@ export const mock = ({
getRotationAngle = getRotationAngleUser;
};
// shortcuts to common DOM functions
export const $id = (id) => document.getElementById(id);
export const $q = (sel) => document.querySelector(sel);
export const $qq = (sel) => [...document.querySelectorAll(sel)];
export const $qq = (sel) => document.querySelector(sel);
export const $qa = (sel) => [...document.querySelectorAll(sel)];