remove jquery in updatetoolbar

master
JFH 2020-12-13 12:40:06 +01:00
parent bdb51c478c
commit a489cd17da
12 changed files with 74 additions and 1122 deletions

View File

@ -640,13 +640,13 @@ export default {
return {remove};
},
toolButtonStateUpdate (opts) {
const button = document.getElementById('mode_connect');
if (opts.nostroke) {
if ($('#mode_connect').hasClass('tool_button_current')) {
if (button.pressed === true) {
svgEditor.clickSelect();
}
}
$('#mode_connect')
.toggleClass('disabled', opts.nostroke);
button.disabled = opts.nostroke;
}
};
}

View File

@ -146,24 +146,10 @@ export default {
updateGrid(svgCanvas.getZoom());
}
$('#canvasGrid').toggle(showGrid);
$('#view_grid').toggleClass('push_button_pressed tool_button');
document.getElementById('view_grid').pressed = showGrid;
}
const buttons = [{
id: 'view_grid',
icon: 'grid.png',
type: 'context',
panel: 'editor_panel',
events: {
click () {
svgEditor.curConfig.showGrid = showGrid = !showGrid;
gridUpdate();
}
}
}];
return {
name: strings.name,
svgicons: 'grid-icon.xml',
zoomChanged (zoom) {
if (showGrid) { updateGrid(zoom); }
},
@ -172,9 +158,13 @@ export default {
gridUpdate();
}
},
buttons: strings.buttons.map((button, i) => {
return Object.assign(buttons[i], button);
})
events: {
id: 'view_grid',
click () {
svgEditor.curConfig.showGrid = showGrid = !showGrid;
gridUpdate();
}
}
};
}
};

View File

@ -33,33 +33,16 @@ export default {
const svgCanvas = svgEditor.canvas;
return {
name: strings.name,
// For more notes on how to make an icon file, see the source of
// the helloworld-icon.xml
svgicons: 'helloworld-icon.xml',
// Multiple buttons can be added in this array
buttons: [{
events: [{
// Must match the icon ID in helloworld-icon.xml
id: 'hello_world',
// Fallback, e.g., for `file:///` access
icon: 'helloworld.png',
// This indicates that the button will be added to the "mode"
// button panel on the left side
type: 'mode',
// Tooltip text
title: strings.buttons[0].title,
// Events
events: {
click () {
// The action taken when the button is clicked on.
// For "mode" buttons, any other button will
// automatically be de-pressed.
svgCanvas.setMode('hello_world');
}
click () {
// The action taken when the button is clicked on.
// For "mode" buttons, any other button will
// automatically be de-pressed.
svgCanvas.setMode('hello_world');
}
}],
// This is triggered when the main mouse button is pressed down

View File

@ -121,7 +121,7 @@ export default {
function setIcon (pos, id) {
if (id.substr(0, 1) !== '\\') { id = '\\textmarker'; }
const ci = '#' + idPrefix + pos + '_' + id.substr(1);
svgEditor.setIcon('#cur_' + pos + '_marker_list', $(ci).children());
// svgEditor.setIcon('#cur_' + pos + '_marker_list', $(ci).children());
$(ci).addClass('current').siblings().removeClass('current');
}

View File

@ -33,22 +33,6 @@ export default {
// edg = 0,
// undoCommand = 'Not image';
started, newFO;
// const ccZoom;
// const wEl, hEl;
// const wOffset, hOffset;
// const ccRBG;
// const ccOpacity;
// const brushW, brushH;
// const ccDebug = document.getElementById('debugpanel');
/* const properlySourceSizeTextArea = function(){
// TODO: remove magic numbers here and get values from CSS
const height = $('#svg_source_container').height() - 80;
$('#svg_source_textarea').css('height', height);
}; */
/**
* @param {boolean} on
* @returns {void}
@ -102,55 +86,13 @@ export default {
* @param {string} tex The itex text.
* @returns {boolean} This function returns false if the set was unsuccessful, true otherwise.
*/
/*
function setItexString(tex) {
const mathns = 'http://www.w3.org/1998/Math/MathML',
xmlnsns = 'http://www.w3.org/2000/xmlns/',
ajaxEndpoint = '../../itex';
const elt = selElems[0];
try {
const math = svgdoc.createElementNS(mathns, 'math');
math.setAttributeNS(xmlnsns, 'xmlns', mathns);
math.setAttribute('display', 'inline');
const semantics = document.createElementNS(mathns, 'semantics');
const annotation = document.createElementNS(mathns, 'annotation');
annotation.setAttribute('encoding', 'application/x-tex');
annotation.textContent = tex;
const mrow = document.createElementNS(mathns, 'mrow');
semantics.append(mrow, annotation);
math.append(semantics);
// make an AJAX request to the server, to get the MathML
$.post(ajaxEndpoint, {tex, display: 'inline'}, function(data){
const children = data.documentElement.childNodes;
while (children.length > 0) {
mrow.append(svgdoc.adoptNode(children[0], true));
}
svgCanvas.sanitizeSvg(math);
svgCanvas.call('changed', [elt]);
});
elt.firstChild.replaceWith(math);
svgCanvas.call('changed', [elt]);
svgCanvas.clearSelection();
} catch(e) {
console.log(e);
return false;
}
return true;
}
*/
const buttons = [{
const events = {
id: 'tool_polygon',
icon: 'polygon.png',
type: 'mode',
position: 11,
events: {
click () {
svgCanvas.setMode('polygon');
showPanel(true);
}
click () {
svgCanvas.setMode('polygon');
showPanel(true);
}
}];
};
const contextTools = [{
type: 'input',
panel: 'polygon_panel',
@ -164,11 +106,8 @@ export default {
}
}];
return {
newUI: true,
name: strings.name,
buttons: strings.buttons.map((button, i) => {
return Object.assign(buttons[i], button);
}),
events,
context_tools: strings.contextTools.map((contextTool, i) => {
return Object.assign(contextTools[i], contextTool);
}),

View File

@ -7,23 +7,10 @@
*
*/
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`);
}
return translationModule.default;
};
export default {
name: 'shapes',
async init ({$}) {
init ({$}) {
const svgEditor = this;
const strings = await loadExtensionTranslation(svgEditor.curPrefs.lang);
const canv = svgEditor.canvas;
const svgroot = canv.getRootElem();
let lastBBox = {};
@ -35,21 +22,15 @@ export default {
let startX;
let startY;
const buttons = [{
id: 'tool_shapelib_show',
type: 'mode_flyout',
events: {
click () {
canv.setMode(modeId);
}
const events = {
id: 'tool_shapelib',
click () {
canv.setMode(modeId);
}
}];
};
return {
newUI: true,
buttons: strings.buttons.map((button, i) => {
return Object.assign(buttons[i], button);
}),
events,
callback () {
// should later register the event
},
@ -57,7 +38,7 @@ export default {
const mode = canv.getMode();
if (mode !== modeId) { return undefined; }
const currentD = document.getElementById('tool_shapelib_show').dataset.draw;
const currentD = document.getElementById('tool_shapelib').dataset.draw;
startX = opts.start_x;
const x = startX;
startY = opts.start_y;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -1,13 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg">
<!--
Sample icons file. This file looks like an SVG file with groups as its
children. Each group element has an ID that must match the ID of the button given
in the extension. The SVG inside the group makes up the actual icon, and
needs use a viewBox instead of width/height for it to scale properly.
Multiple icons can be included, each within their own group.
-->
<g id="view_grid">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<rect fill="#ffffff" stroke="#848484" x="2" y="2" width="20" height="20"/>
@ -25,6 +15,4 @@
<rect fill="#d8d8d8" stroke="#000000" stroke-width="0" x="10.0625" y="16.59375" width="4" height="4"/>
<rect fill="#d8d8d8" stroke="#000000" stroke-width="0" x="16.8125" y="16.59375" width="4" height="4"/>
</g>
</svg>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -96,6 +96,7 @@
<div class="tool_sep"></div>
<se-button id="tool_source" title="Edit Source" shortcut="U" src="./images/source.svg"></se-button>
<se-button id="tool_wireframe" title="Wireframe Mode" shortcut="F" src="./images/wireframe.svg"></se-button>
<se-button id="view_grid" title="Show grid" src="./images/grid.svg"></se-button>
</div> <!-- editor_panel -->
<div id="history_panel">
<div class="tool_sep"></div>
@ -299,7 +300,7 @@
<div id="tools_left">
<se-button id="tool_select" title="Select Tool" src="./images/select.svg"></se-button>
<se-button id="tool_fhpath" title="Pencil Tool" src="./images/pencil.svg" shortcut="Q"></se-button>
<se-flyingbutton id="tools_line_show" title="Line Tool (L)/Connect two objects">
<se-flyingbutton id="tools_line" title="Line Tool (L)/Connect two objects">
<se-button id="tool_line" title="Line Tool" src="./images/pen.svg" shortcut="L"></se-button>
<se-button id="mode_connect" title="Connect two objects" src="./images/conn.svg"></se-button>
</se-flyingbutton>
@ -308,13 +309,13 @@
<se-button id="tool_square" title="Square" src="./images/square.svg"></se-button>
<se-button id="tool_fhrect" title="Free-Hand Rectangle" src="./images/fh_rect.svg"></se-button>
</se-flyingbutton>
<se-flyingbutton id="tools_ellipse_show" title="Ellipse/Circle Tool">
<se-flyingbutton id="tools_ellipse" title="Ellipse/Circle Tool">
<se-button id="tool_ellipse" title="Rectangle" src="./images/ellipse.svg" shortcut="E"></se-button>
<se-button id="tool_circle" title="Square" src="./images/circle.svg"></se-button>
<se-button id="tool_fhellipse" title="Free-Hand Rectangle" src="./images/fh_ellipse.svg"></se-button>
</se-flyingbutton>
<se-button id="tool_path" title="Path Tool" src="./images/path.svg" shortcut="P"></se-button>
<se-explorerbutton id="tool_shapelib_show" title="Shape library" lib="./extensions/ext-shapes/shapelib/"
<se-explorerbutton id="tool_shapelib" title="Shape library" lib="./extensions/ext-shapes/shapelib/"
src="./images/shapelib.svg"></se-explorerbutton>
<se-button id="tool_text" title="Text Tool" src="./images/text.svg" shortcut="T"></se-button>
<se-button id="tool_image" title="Image Tool" src="./images/image.svg"></se-button>

View File

@ -535,7 +535,7 @@ div.toolset label span {
margin-left: 5px;
}
#tools_top input {
#tools_top se-input, se-spin-input {
margin-top: 5px;
height: 15px;
}

View File

@ -20,7 +20,7 @@ import deparam from 'deparam';
import './touch.js';
import {NS} from '../common/namespaces.js';
import {isWebkit, isChrome, isGecko, isIE, isMac, isTouch} from '../common/browser.js';
import {isChrome, isGecko, isIE, isMac, isTouch} from '../common/browser.js';
// Until we split this into smaller files, this helps distinguish utilities
// from local methods
@ -34,7 +34,6 @@ import SvgCanvas from '../svgcanvas/svgcanvas.js';
import Layer from '../common/layer.js';
import jQueryPluginJSHotkeys from './js-hotkeys/jquery.hotkeys.min.js';
import jQueryPluginSVGIcons from './svgicons/jQuery.svgIcons.js';
import jQueryPluginJGraduate from './jgraduate/jQuery.jGraduate.js';
import jQueryPluginContextMenu from './contextmenu/jQuery.contextMenu.js';
import jQueryPluginJPicker from './jgraduate/jQuery.jPicker.js';
@ -50,7 +49,7 @@ const {$q, $qq, $id} = Utils;
const editor = {};
const $ = [
jQueryPluginJSHotkeys, jQueryPluginSVGIcons, jQueryPluginJGraduate,
jQueryPluginJSHotkeys, jQueryPluginJGraduate,
jQueryPluginContextMenu, jQueryPluginJPicker
].reduce((jq, func) => func(jq), jQuery);
@ -731,24 +730,6 @@ editor.init = () => {
})();
setupCurPrefs();
/**
* Called internally.
* @function module:SVGEditor.setIcon
* @param {string|Element|external:jQuery} elem
* @param {string|external:jQuery} iconId
* @param {Float} forcedSize Not in use
* @returns {void}
*/
const setIcon = editor.setIcon = function (elem, iconId, forcedSize) {
const icon = (typeof iconId === 'string') ? $.getSvgIcon(iconId, true) : iconId.clone();
if (!icon) {
// Todo: Investigate why this still occurs in some cases
console.log('NOTE: Icon image missing: ' + iconId); // eslint-disable-line no-console
return;
}
$(elem).empty().append(icon);
};
/**
* @fires module:svgcanvas.SvgCanvas#event:ext_addLangData
* @fires module:svgcanvas.SvgCanvas#event:ext_langReady
@ -847,8 +828,6 @@ editor.init = () => {
}
};
const stateObj = {tool_scale: editor.tool_scale};
/**
* @type {string}
*/
@ -869,87 +848,6 @@ editor.init = () => {
return '';
}());
/**
* Called internally.
* @function module:SVGEditor.setIconSize
* @param {module:SVGEditor.IconSize} size
* @returns {void}
*/
const setIconSize = editor.setIconSize = function (size) {
const selToscale = '#tools_top .toolset, #editor_panel > *, #history_panel > *,' +
' #main_button, #tools_left > *, #path_node_panel > *, #multiselected_panel > *,' +
' #g_panel > *, #tool_font_size > *';
const elems = $(selToscale);
let scale = 1;
if (typeof size === 'number') {
scale = size;
} else {
const iconSizes = {s: 0.75, m: 1, l: 1.25, xl: 1.5};
scale = iconSizes[size];
}
stateObj.tool_scale = editor.tool_scale = scale;
const hiddenPs = elems.parents(':hidden');
hiddenPs.css('visibility', 'hidden').show();
hiddenPs.css('visibility', 'visible').hide();
// return;
editor.pref('iconsize', size);
const $editDialog = document.getElementById('se-edit-prefs');
$editDialog.setAttribute('iconsize', size);
// Note that all rules will be prefixed with '#svg_editor' when parsed
const cssResizeRules = {
'#tools_top': {
left: 50 + $('#main_button').width(),
height: 72
},
'#tools_left': {
width: 31,
top: 74
},
'div#workarea': {
left: 38,
top: 74
}
};
let ruleElem = $('#tool_size_rules');
if (!ruleElem.length) {
ruleElem = $('<style id="tool_size_rules"></style>').appendTo('head');
} else {
ruleElem.empty();
}
if (size !== 'm') {
let styleStr = '';
$.each(cssResizeRules, function (selector, rules) {
selector = '#svg_editor ' + selector.replace(/,/g, ', #svg_editor');
styleStr += selector + '{';
$.each(rules, function (prop, values) {
let val;
if (typeof values === 'number') {
val = (values * scale) + 'px';
} else if (values[size] || values.all) {
val = (values[size] || values.all);
}
styleStr += (prop + ':' + val + ';');
});
styleStr += '}';
});
// this.style[uaPrefix + 'Transform'] = 'scale(' + scale + ')';
const prefix = '-' + uaPrefix.toLowerCase() + '-';
styleStr += (selToscale + '{' + prefix + 'transform: scale(' + scale + ');}' +
' #svg_editor div.toolset .toolset {' + prefix + 'transform: scale(1); margin: 1px !important;}' + // Hack for markers
' #svg_editor .ui-slider {' + prefix + 'transform: scale(' + (1 / scale) + ');}' // Hack for sliders
);
ruleElem.text(styleStr);
}
};
/**
* @name module:SVGEditor.canvas
* @type {module:svgcanvas.SvgCanvas}
@ -966,7 +864,6 @@ editor.init = () => {
const canvMenu = $('#cmenu_canvas');
const paintBox = {fill: null, stroke: null};
let resizeTimer;
let exportWindow = null,
defaultImageURL = curConfig.imgPath + 'logo.svg',
zoomInIcon = 'crosshair',
@ -1045,7 +942,6 @@ editor.init = () => {
const selLayerNames = $('#selLayerNames').empty();
const drawing = svgCanvas.getCurrentDrawing();
const currentLayerName = drawing.getCurrentLayerName();
const icon = $.getSvgIcon('eye');
let layer = svgCanvas.getCurrentDrawing().getNumLayers();
// we get the layers in the reverse z-order (the layer rendered on top is listed first)
while (layer--) {
@ -1056,11 +952,6 @@ editor.init = () => {
layerlist.append(layerTr.append(layerVis, layerName));
selLayerNames.append('<option value="' + name + '">' + name + '</option>');
}
if (icon !== undefined) {
const copy = icon.clone();
$('td.layervis', layerlist).append(copy);
$.resizeSvgIcons({'td.layervis .svg_icon': 14});
}
// handle selection of layer
$('#layerlist td.layername')
.mouseup(function (evt) {
@ -1240,7 +1131,6 @@ editor.init = () => {
if (changeElem) {
svgCanvas.setStrokeAttr('stroke-' + pre, val);
}
setIcon('#cur_' + pre, id, 20);
$(opt).addClass('current').siblings().removeClass('current');
}
@ -1585,38 +1475,40 @@ editor.init = () => {
* @fires module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate
* @returns {void}
*/
const updateToolButtonState = function () {
const updateToolButtonState = () => {
const bNoFill = (svgCanvas.getColor('fill') === 'none');
const bNoStroke = (svgCanvas.getColor('stroke') === 'none');
const buttonsNeedingStroke = ['#tool_fhpath', '#tool_line'];
const buttonsNeedingStroke = ['tool_fhpath', 'tool_line'];
const buttonsNeedingFillAndStroke = [
'#tools_rect .tool_button', '#tools_ellipse .tool_button',
'#tool_text', '#tool_path'
'tools_rect', 'tools_ellipse',
'tool_text', 'tool_path'
];
if (bNoStroke) {
buttonsNeedingStroke.forEach((btn) => {
if ($(btn).hasClass('tool_button_current')) {
// if btn is pressed, change to select button
if ($id(btn).pressed) {
clickSelect();
}
$(btn).addClass('disabled');
$(btn).disabled = true;
});
} else {
buttonsNeedingStroke.forEach((btn) => {
$(btn).removeClass('disabled');
$id(btn).disabled = false;
});
}
if (bNoStroke && bNoFill) {
buttonsNeedingFillAndStroke.forEach((btn) => {
if ($(btn).hasClass('tool_button_current')) {
// if btn is pressed, change to select button
if ($id(btn).pressed) {
clickSelect();
}
$(btn).addClass('disabled');
$(btn).disabled = true;
});
} else {
buttonsNeedingFillAndStroke.forEach((btn) => {
$(btn).removeClass('disabled');
$id(btn).disabled = false;
});
}
@ -1771,17 +1663,16 @@ editor.init = () => {
}
// Elements in this array cannot be converted to a path
const noPath = !['image', 'text', 'path', 'g', 'use'].includes(elname);
$('#tool_topath').toggle(noPath);
$('#tool_reorient').toggle(elname === 'path');
$('#tool_reorient').toggleClass('disabled', angle === 0);
$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 = path.getNodePoint();
$('#tool_add_subpath').pressed = false;
$('#tool_node_delete').toggleClass('disabled', !path.canDeleteNodes);
// Show open/close button based on selected point
setIcon('#tool_openclose_path', path.closed_subpath ? 'open_path' : 'close_path');
// setIcon('#tool_openclose_path', path.closed_subpath ? 'open_path' : 'close_path');
if (point) {
const segType = $('#seg_type');
@ -2222,75 +2113,6 @@ editor.init = () => {
paintBox.stroke.prep();
};
/**
* @param {string} elemSel
* @param {string} listSel
* @param {external:jQuery.Function} callback
* @param {PlainObject} opts
* @param {boolean} opts.dropUp
* @param {boolean} opts.seticon
* @param {boolean} opts.multiclick
* @todo Combine this with `addDropDown` or find other way to optimize.
* @returns {void}
*/
const addAltDropDown = function (elemSel, listSel, callback, opts) {
const button = $(elemSel);
const {dropUp} = opts;
const list = $(listSel);
if (dropUp) {
$(elemSel).addClass('dropup');
}
list.find('li').bind('mouseup', function (...args) {
if (opts.seticon) {
setIcon('#cur_' + button[0].id, $(this).children());
$(this).addClass('current').siblings().removeClass('current');
}
callback.apply(this, ...args);
});
let onButton = false;
$(window).mouseup(function (evt) {
if (!onButton) {
button.removeClass('down');
list.hide();
list.css({top: 0, left: 0});
}
onButton = false;
});
// const height = list.height(); // Currently unused
button.bind('mousedown', function () {
const off = button.offset();
if (dropUp) {
off.top -= list.height();
off.left += 8;
} else {
off.top += button.height();
}
list.offset(off);
if (!button.hasClass('down')) {
list.show();
onButton = true;
} else {
// CSS position must be reset for Webkit
list.hide();
list.css({top: 0, left: 0});
}
button.toggleClass('down');
}).hover(function () {
onButton = true;
}).mouseout(function () {
onButton = false;
});
if (opts.multiclick) {
list.mousedown(function () {
onButton = true;
});
}
};
/**
* @param {external:Window} win
* @param {module:svgcanvas.SvgCanvas#event:extension_added} ext
@ -2302,7 +2124,6 @@ editor.init = () => {
return undefined;
}
let cbCalled = false;
let resizeDone = false;
if (ext.langReady) {
if (editor.langChanged) { // We check for this since the "lang" pref could have been set by storage
@ -2310,23 +2131,6 @@ editor.init = () => {
await ext.langReady({lang});
}
}
/**
* Clear resize timer if present and if not previously performed,
* perform an icon resize.
* @returns {void}
*/
function prepResize () {
if (resizeTimer) {
clearTimeout(resizeTimer);
resizeTimer = null;
}
if (!resizeDone) {
resizeTimer = setTimeout(function () {
resizeDone = true;
setIconSize(editor.pref('iconsize'));
}, 50);
}
}
/**
*
@ -2339,8 +2143,6 @@ editor.init = () => {
}
};
const btnSelects = [];
/**
* @typedef {PlainObject} module:SVGEditor.ContextTool
* @property {string} panel The ID of the existing panel to which the tool is being added. Required.
@ -2405,15 +2207,6 @@ editor.init = () => {
// Creates the tool, hides & adds it, returns the select element
/* const dropdown = */ $(html).appendTo(panel).children();
btnSelects.push({
elem: ('#' + tool.id),
list: ('#' + tool.id + '_opts'),
title: tool.title,
callback: tool.events.change,
cur: ('#cur_' + tool.id)
});
break;
} case 'input': {
html = '<label' + contId + '>' +
@ -2440,184 +2233,10 @@ editor.init = () => {
});
}
const {svgicons} = ext;
if (ext.buttons && !ext.newUI) {
const fallbackObj = {},
altsObj = {},
placementObj = {};
/**
* @typedef {GenericArray} module:SVGEditor.KeyArray
* @property {string} 0 The key to bind (on `keydown`)
* @property {boolean} 1 Whether to `preventDefault` on the `keydown` event
* @property {boolean} 2 Not apparently in use (NoDisableInInput)
*/
/**
* @typedef {string|module:SVGEditor.KeyArray} module:SVGEditor.Key
*/
/**
* @typedef {PlainObject} module:SVGEditor.Button
* @property {string} id A unique identifier for this button. If SVG icons are used, this must match the ID used in the icon file. Required.
* @property {"mode_flyout"|"mode"|"context"|"app_menu"} type Type of button. Required.
* @property {string} title The tooltip text that will appear when the user hovers over the icon. Required.
* @property {PlainObject<string, external:jQuery.Function>|PlainObject<"click", external:jQuery.Function>} events DOM event names with associated functions. Example: `{click () { alert('Button was clicked') } }`. Click is used with `includeWith` and `type` of "mode_flyout" (and "mode"); any events may be added if `list` is not present. Expected.
* @property {string} panel The ID of the context panel to be included, if type is "context". Required only if type is "context".
* @property {string} icon The file path to the raster version of the icon image source. Required only if no `svgicons` is supplied from [ExtensionInitResponse]{@link module:svgcanvas.ExtensionInitResponse}.
* @property {string} [svgicon] If absent, will utilize the button "id"; used to set "placement" on the `svgIcons` call
* @property {string} [list] Points to the "id" of a `context_tools` item of type "button-select" into which the button will be added as a panel list item
* @property {Integer} [position] The numeric index for placement; defaults to last position (as of the time of extension addition) if not present. For use with {@link http://api.jquery.com/eq/}.
* @property {boolean} [isDefault] Whether or not the button is the default. Used with `list`.
* @property {module:SVGEditor.Key} [key] The key to bind to the button
*/
// Add buttons given by extension
$.each(ext.buttons, function (i, /** @type {module:SVGEditor.Button} */ btn) {
let {id} = btn;
let num = i;
// Give button a unique ID
while ($('#' + id).length) {
id = btn.id + '_' + (++num);
}
let icon;
if (!svgicons) {
icon = $(
'<img src="' + btn.icon +
(btn.title ? '" alt="' + btn.title : '') +
'">'
);
} else {
fallbackObj[id] = btn.icon;
altsObj[id] = btn.title;
const svgicon = btn.svgicon || btn.id;
if (btn.type === 'app_menu') {
placementObj['#' + id + ' > div'] = svgicon;
} else {
placementObj['#' + id] = svgicon;
}
}
let cls, parent;
// Set button up according to its type
switch (btn.type) {
case 'mode_flyout':
case 'mode':
cls = 'tool_button';
parent = '#tools_left';
break;
case 'context':
cls = 'tool_button';
parent = '#' + btn.panel;
// create the panel if it doesn't exist
if (!$(parent).length) {
$('<div>', {id: btn.panel}).appendTo('#tools_top');
}
break;
case 'app_menu':
cls = '';
parent = '#main_menu ul';
break;
}
const button = $((btn.list || btn.type === 'app_menu') ? '<li/>' : '<div/>')
.attr('id', id)
.attr('title', btn.title)
.addClass(cls);
if (!btn.includeWith && !btn.list) {
if ('position' in btn) {
if ($(parent).children().eq(btn.position).length) {
$(parent).children().eq(btn.position).before(button);
} else {
$(parent).children().last().after(button);
}
} else {
button.appendTo(parent);
}
if (btn.type === 'app_menu') {
button.append('<div>').append(btn.title);
}
} else if (btn.list) {
// Add button to list
button.addClass('push_button');
$('#' + btn.list + '_opts').append(button);
if (btn.isDefault) {
$('#cur_' + btn.list).append(button.children().clone());
const svgicon = btn.svgicon || btn.id;
placementObj['#cur_' + btn.list] = svgicon;
}
}
if (!svgicons) {
button.append(icon);
}
if (!btn.list) {
// Add given events to button
$.each(btn.events, function (name, func) {
if (name === 'click' && btn.type === 'mode') {
// `touch.js` changes `touchstart` to `mousedown`,
// so we must map extension click events as well
if (isTouch() && name === 'click') {
name = 'mousedown';
}
if (btn.includeWith) {
button.bind(name, func);
} else {
button.bind(name, function () {
if (updateLeftPanel(button)) {
func();
}
});
}
if (btn.key) {
$(document).bind('keydown', btn.key, func);
if (btn.title) {
button.attr('title', btn.title + ' [' + btn.key + ']');
}
}
} else {
button.bind(name, func);
}
});
}
});
$.each(btnSelects, function () {
addAltDropDown(this.elem, this.list, this.callback, {seticon: true});
});
if (svgicons) {
return new Promise((resolve, reject) => { // eslint-disable-line promise/avoid-new
$.svgIcons(`${curConfig.imgPath}${svgicons}`, {
w: 24, h: 24,
id_match: false,
no_img: (!isWebkit()),
fallback: fallbackObj,
placement: placementObj,
callback (icons) {
// Non-ideal hack to make the icon match the current size
// if (curPrefs.iconsize && curPrefs.iconsize !== 'm') {
if (editor.pref('iconsize') !== 'm') {
prepResize();
}
runCallback();
resolve();
}
});
});
}
}
if (ext.buttons && ext.newUI) {
ext.buttons.forEach((btn) => {
// Set button up according to its type
switch (btn.type) {
case 'mode_flyout':
case 'mode':
$id(btn.id).addEventListener('click', () => {
if (updateLeftPanel(btn.id)) {
btn.events.click();
}
});
break;
if (ext.events) {
$id(ext.events.id).addEventListener('click', () => {
if (updateLeftPanel(ext.events.id)) {
ext.events.click();
}
});
}
@ -3117,6 +2736,7 @@ editor.init = () => {
}
});
/*
addAltDropDown('#stroke_linecap', '#linecap_opts', function () {
setStrokeOpt(this, true);
}, {dropUp: true});
@ -3129,6 +2749,7 @@ editor.init = () => {
const letter = this.id.replace('tool_pos', '').charAt(0);
svgCanvas.alignSelectedElements(letter, 'page');
}, {multiclick: true});
*/
// Unfocus text input when workarea is mousedowned.
(function () {
@ -3261,8 +2882,8 @@ editor.init = () => {
* @returns {void}
*/
const zoomImage = function (multiplier) {
const res = svgCanvas.getResolution();
multiplier = multiplier ? res.zoom * multiplier : 1;
const resolution = svgCanvas.getResolution();
multiplier = multiplier ? resolution.zoom * multiplier : 1;
// setResolution(res.w * multiplier, res.h * multiplier, true);
$id('zoom').value = (multiplier * 100).toFixed(1);
svgCanvas.setZoom(multiplier);
@ -3735,11 +3356,6 @@ editor.init = () => {
updateToolButtonState();
};
/* $('#svg_prefs_container').draggable({
cancel: 'button,fieldset',
containment: 'window'
}).css('position', 'absolute'); */
let docprops = false;
let preferences = false;
@ -3753,14 +3369,14 @@ editor.init = () => {
const $imgDialog = document.getElementById('se-img-prop');
// update resolution option with actual resolution
const res = svgCanvas.getResolution();
const resolution = svgCanvas.getResolution();
if (curConfig.baseUnit !== 'px') {
res.w = convertUnit(res.w) + curConfig.baseUnit;
res.h = convertUnit(res.h) + curConfig.baseUnit;
resolution.w = convertUnit(resolution.w) + curConfig.baseUnit;
resolution.h = convertUnit(resolution.h) + curConfig.baseUnit;
}
$imgDialog.setAttribute('save', editor.pref('img_save'));
$imgDialog.setAttribute('width', res.w);
$imgDialog.setAttribute('height', res.h);
$imgDialog.setAttribute('width', resolution.w);
$imgDialog.setAttribute('height', resolution.h);
$imgDialog.setAttribute('title', svgCanvas.getDocumentTitle());
$imgDialog.setAttribute('dialog', 'open');
};
@ -3855,7 +3471,7 @@ editor.init = () => {
};
/**
*
* @param {Event} e
* @returns {boolean} Whether there were problems saving the document properties
*/
const saveDocProperties = function (e) {
@ -3885,11 +3501,12 @@ editor.init = () => {
/**
* Save user preferences based on current values in the UI.
* @param {Event} e
* @function module:SVGEditor.savePreferences
* @returns {Promise<void>}
*/
const savePreferences = editor.savePreferences = async function (e) {
const {lang, iconsize, bgcolor, bgurl, gridsnappingon, gridsnappingstep, gridcolor, showrulers, baseunit} = e.detail;
const {lang, bgcolor, bgurl, gridsnappingon, gridsnappingstep, gridcolor, showrulers, baseunit} = e.detail;
// Set background
setBackground(bgcolor, bgurl);
@ -3899,9 +3516,6 @@ editor.init = () => {
await setLang(langParam, langData);
}
// set icon size
setIconSize(iconsize);
// set grid setting
curConfig.gridSnapping = gridsnappingon;
curConfig.snappingStep = gridsnappingstep;
@ -4631,8 +4245,8 @@ editor.init = () => {
// Tooltips not directly associated with a single function
const keyAssocs = {
'4/Shift+4': '#tools_rect_show',
'5/Shift+5': '#tools_ellipse_show'
'4/Shift+4': '#tools_rect',
'5/Shift+5': '#tools_ellipse'
};
return {

View File

@ -1,544 +0,0 @@
/**
* @file SVG Icon Loader 2.0
*
* jQuery Plugin for loading SVG icons from a single file
*
* Adds {@link external:jQuery.svgIcons}, {@link external:jQuery.getSvgIcon}, {@link external:jQuery.resizeSvgIcons}
*
* How to use:
1. Create the SVG master file that includes all icons:
The master SVG icon-containing file is an SVG file that contains
`<g>` elements. Each `<g>` element should contain the markup of an SVG
icon. The `<g>` element has an ID that should
correspond with the ID of the HTML element used on the page that should contain
or optionally be replaced by the icon. Additionally, one empty element should be
added at the end with id "svg_eof".
2. Optionally create fallback raster images for each SVG icon.
3. Include the jQuery and the SVG Icon Loader scripts on your page.
4. Run `$.svgIcons()` when the document is ready. See its signature
5. To access an icon at a later point without using the callback, use this:
`$.getSvgIcon(id (string), uniqueClone (boolean))`;
This will return the icon (as jQuery object) with a given ID.
6. To resize icons at a later point without using the callback, use this:
`$.resizeSvgIcons(resizeOptions)` (use the same way as the "resize" parameter)
*
* @module jQuerySVGIcons
* @license MIT
* @copyright (c) 2009 Alexis Deveria
* {@link http://a.deveria.com}
* @example
$(function () {
$.svgIcons('my_icon_set.svg'); // The SVG file that contains all icons
// No options have been set, so all icons will automatically be inserted
// into HTML elements that match the same IDs.
});
* @example
$(function () {
// The SVG file that contains all icons
$.svgIcons('my_icon_set.svg', {
callback (icons) { // Custom callback function that sets click
// events for each icon
$.each(icons, function (id, icon) {
icon.click(function () {
alert('You clicked on the icon with id ' + id);
});
});
}
});
});
* @example
$(function () {
// The SVGZ file that contains all icons
$.svgIcons('my_icon_set.svgz', {
w: 32, // All icons will be 32px wide
h: 32, // All icons will be 32px high
fallback_path: 'icons/', // All fallback files can be found here
fallback: {
'#open_icon': 'open.png', // The "open.png" will be appended to the
// HTML element with ID "open_icon"
'#close_icon': 'close.png',
'#save_icon': 'save.png'
},
placement: {'.open_icon': 'open'}, // The "open" icon will be added
// to all elements with class "open_icon"
resize: {
'#save_icon .svg_icon': 64 // The "save" icon will be resized to 64 x 64px
},
callback (icons) { // Sets background color for "close" icon
icons.close.css('background', 'red');
},
svgz: true // Indicates that an SVGZ file is being used
});
});
*/
// Todo: Move to own module (and have it import a modular base64 encoder)
import {encode64} from '../../common/utilities.js';
const isOpera = Boolean(window.opera);
const fixIDs = function (svgEl, svgNum, force) {
const defs = svgEl.find('defs');
if (!defs.length) return svgEl;
const idElems = (isOpera)
? defs.find('*').filter(function () {
return Boolean(this.id);
})
: defs.find('[id]');
const allElems = svgEl[0].getElementsByTagName('*'),
len = allElems.length;
idElems.each(function (i) {
const {id} = this;
/*
const noDupes = ($(svgdoc).find('#' + id).length <= 1);
if (isOpera) noDupes = false; // Opera didn't clone svgEl, so not reliable
if(!force && noDupes) return;
*/
const newId = 'x' + id + svgNum + i;
this.id = newId;
const oldVal = 'url(#' + id + ')';
const newVal = 'url(#' + newId + ')';
// Selector method, possibly faster but fails in Opera / jQuery 1.4.3
// svgEl.find('[fill="url(#' + id + ')"]').each(function() {
// this.setAttribute('fill', 'url(#' + newId + ')');
// }).end().find('[stroke="url(#' + id + ')"]').each(function() {
// this.setAttribute('stroke', 'url(#' + newId + ')');
// }).end().find('use').each(function() {
// if(this.getAttribute('xlink:href') == '#' + id) {
// this.setAttributeNS(xlinkns,'href','#' + newId);
// }
// }).end().find('[filter="url(#' + id + ')"]').each(function() {
// this.setAttribute('filter', 'url(#' + newId + ')');
// });
for (i = 0; i < len; i++) {
const elem = allElems[i];
if (elem.getAttribute('fill') === oldVal) {
elem.setAttribute('fill', newVal);
}
if (elem.getAttribute('stroke') === oldVal) {
elem.setAttribute('stroke', newVal);
}
if (elem.getAttribute('filter') === oldVal) {
elem.setAttribute('filter', newVal);
}
}
});
return svgEl;
};
/**
* @callback module:jQuerySVGIcons.SVGIconsLoadedCallback
* @param {PlainObject<string, external:jQuery>} svgIcons IDs keyed to jQuery objects of images
* @returns {void}
*/
/**
* @function module:jQuerySVGIcons.jQuerySVGIcons
* @param {external:jQuery} $ Its keys include all icon IDs and the values, the icon as a jQuery object
* @returns {external:jQuery} The enhanced jQuery object
*/
export default function jQueryPluginSVGIcons ($) {
const svgIcons = {};
/**
* Map of raster images with each key being the SVG icon ID
* to replace, and the value the image file name.
* @typedef {PlainObject<string, string>} external:jQuery.svgIcons.Fallback
*/
/**
* Map of raster images with each key being the SVG icon ID
* whose `alt` will be set, and the value being the `alt` text.
* @typedef {PlainObject<string, string>} external:jQuery.svgIcons.Alts
*/
/**
* @function external:jQuery.svgIcons
* @param {string} file The location of a local SVG or SVGz file
* @param {PlainObject} [opts]
* @param {Float} [opts.w] The icon widths
* @param {Float} [opts.h] The icon heights
* @param {external:jQuery.svgIcons.Fallback} [opts.fallback]
* @param {string} [opts.fallback_path] The path to use for all images
* listed under "fallback"
* @param {boolean} [opts.replace] If set to `true`, HTML elements will
* be replaced by, rather than include the SVG icon.
* @param {PlainObject<string, string>} [opts.placement] Map with selectors
* for keys and SVG icon ids as values. This provides a custom method of
* adding icons.
* @param {PlainObject<string, module:jQuerySVGIcons.Size>} [opts.resize] Map
* with selectors for keys and numbers as values. This allows an easy way to
* resize specific icons.
* @param {module:jQuerySVGIcons.SVGIconsLoadedCallback} [opts.callback] A
* function to call when all icons have been loaded.
* @param {boolean} [opts.id_match=true] Automatically attempt to match
* SVG icon ids with corresponding HTML id
* @param {boolean} [opts.no_img] Prevent attempting to convert the icon
* into an `<img>` element (may be faster, help for browser consistency)
* @param {boolean} [opts.svgz] Indicate that the file is an SVGZ file, and
* thus not to parse as XML. SVGZ files add compression benefits, but
* getting data from them fails in Firefox 2 and older.
* @param {jQuery.svgIcons.Alts} [opts.alts] Map of images with each key
* being the SVG icon ID whose `alt` will be set, and the value being
* the `alt` text
* @param {string} [opts.testIconAlt="icon"] Alt text for the injected test image.
* In case wish to ensure have one for accessibility
* @returns {void}
*/
$.svgIcons = function (file, opts = {}) {
const svgns = 'http://www.w3.org/2000/svg',
xlinkns = 'http://www.w3.org/1999/xlink',
iconW = opts.w || 24,
iconH = opts.h || 24;
let elems, svgdoc, testImg,
iconsMade = false,
dataLoaded = false,
loadAttempts = 0;
const // ua = navigator.userAgent,
// isSafari = (ua.includes('Safari/') && !ua.includes('Chrome/')),
dataPre = 'data:image/svg+xml;charset=utf-8;base64,';
let dataEl;
if (opts.svgz) {
dataEl = $('<object data="' + file + '" type=image/svg+xml>').appendTo('body').hide();
try {
svgdoc = dataEl[0].contentDocument;
dataEl.load(getIcons);
getIcons(0, true); // Opera will not run "load" event if file is already cached
} catch (err1) {
useFallback();
}
} else {
const parser = new DOMParser();
$.ajax({
url: file,
dataType: 'string',
success (data) {
if (!data) {
$(useFallback);
return;
}
svgdoc = parser.parseFromString(data, 'text/xml');
$(function () {
getIcons('ajax');
});
},
error (err) {
// TODO: Fix Opera widget icon bug
if (window.opera) {
$(function () {
useFallback();
});
} else if (err.responseText) {
svgdoc = parser.parseFromString(err.responseText, 'text/xml');
if (!svgdoc.childNodes.length) {
$(useFallback);
}
$(function () {
getIcons('ajax');
});
} else {
$(useFallback);
}
}
});
}
/**
*
* @param {"ajax"|0|void} evt
* @param {boolean} [noWait]
* @returns {void}
*/
function getIcons (evt, noWait) {
if (evt !== 'ajax') {
if (dataLoaded) return;
// Webkit sometimes says svgdoc is undefined, other times
// it fails to load all nodes. Thus we must make sure the "eof"
// element is loaded.
svgdoc = dataEl[0].contentDocument; // Needed again for Webkit
const isReady = (svgdoc && svgdoc.getElementById('svg_eof'));
if (!isReady && !(noWait && isReady)) {
loadAttempts++;
if (loadAttempts < 50) {
setTimeout(getIcons, 20);
} else {
useFallback();
dataLoaded = true;
}
return;
}
dataLoaded = true;
}
elems = $(svgdoc.firstChild).children(); // .getElementsByTagName('foreignContent');
if (!opts.no_img) {
const testSrc = dataPre +
'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zd' +
'mciIHdpZHRoPSIyNzUiIGhlaWdodD0iMjc1Ij48L3N2Zz4%3D';
testImg = $(new Image()).attr({
src: testSrc,
width: 0,
height: 0,
alt: opts.testIconAlt || 'icon'
}).appendTo('body')
.load(function () {
// Safari 4 crashes, Opera and Chrome don't
makeIcons(true);
}).error(function () {
makeIcons();
});
} else {
setTimeout(function () {
if (!iconsMade) makeIcons();
}, 500);
}
}
/**
*
* @param {external:jQuery} target
* @param {external:jQuery} icon A wrapped `defs` or Image
* @param {string} id SVG icon ID
* @param {boolean} setID Whether to set the ID attribute (with `id`)
* @returns {void}
*/
function setIcon (target, icon, id, setID) {
if (isOpera) icon.css('visibility', 'hidden');
if (opts.replace) {
if (setID) icon.attr('id', id);
const cl = target.attr('class');
if (cl) icon.attr('class', 'svg_icon ' + cl);
if (!target.alt) {
let alt = 'icon';
if (opts.alts) {
alt = opts.alts[id] || alt;
}
icon.attr('alt', alt);
}
target.replaceWith(icon);
} else {
target.append(icon);
}
if (isOpera) {
setTimeout(function () {
icon.removeAttr('style');
}, 1);
}
}
let holder;
/**
* @param {external:jQuery} icon A wrapped `defs` or Image
* @param {string} id SVG icon ID
* @returns {void}
*/
function addIcon (icon, id) {
if (opts.id_match === undefined || opts.id_match !== false) {
setIcon(holder, icon, id, true);
}
svgIcons[id] = icon;
}
/**
*
* @param {boolean} [toImage]
* @param {external:jQuery.svgIcons.Fallback} [fallback=false]
* @returns {void}
*/
function makeIcons (toImage = false, fallback = false) {
if (iconsMade) return;
if (opts.no_img) toImage = false;
let tempHolder;
if (toImage) {
tempHolder = $(document.createElement('div'));
tempHolder.hide().appendTo('body');
}
if (fallback) {
const path = opts.fallback_path || '';
$.each(fallback, function (id, imgsrc) {
holder = $('#' + id);
let alt = 'icon';
if (opts.alts) {
alt = opts.alts[id] || alt;
}
const icon = $(new Image())
.attr({
class: 'svg_icon',
src: path + imgsrc,
width: iconW,
height: iconH,
alt
});
addIcon(icon, id);
});
} else {
const len = elems.length;
for (let i = 0; i < len; i++) {
const elem = elems[i];
const {id} = elem;
if (id === 'svg_eof') break;
holder = $('#' + id);
const svgroot = document.createElementNS(svgns, 'svg');
// Per https://www.w3.org/TR/xml-names11/#defaulting, the namespace for
// attributes should have no value.
svgroot.setAttribute('viewBox', [0, 0, iconW, iconH].join(' '));
let svg = elem.getElementsByTagNameNS(svgns, 'svg')[0];
// Make flexible by converting width/height to viewBox
const w = svg.getAttribute('width');
const h = svg.getAttribute('height');
svg.removeAttribute('width');
svg.removeAttribute('height');
const vb = svg.getAttribute('viewBox');
if (!vb) {
svg.setAttribute('viewBox', [0, 0, w, h].join(' '));
}
// Not using jQuery to be a bit faster
svgroot.setAttribute('xmlns', svgns);
svgroot.setAttribute('width', iconW);
svgroot.setAttribute('height', iconH);
svgroot.setAttribute('xmlns:xlink', xlinkns);
svgroot.setAttribute('class', 'svg_icon');
// Without cloning, Firefox will make another GET request.
// With cloning, causes issue in Opera/Win/Non-EN
if (!isOpera) svg = svg.cloneNode(true);
svgroot.append(svg);
let icon;
if (toImage) {
tempHolder.empty().append(svgroot);
const str = dataPre + encode64(unescape(encodeURIComponent(
new XMLSerializer().serializeToString(svgroot)
)));
let alt = 'icon';
if (opts.alts) {
alt = opts.alts[id] || alt;
}
icon = $(new Image())
.attr({class: 'svg_icon', src: str, alt});
} else {
icon = fixIDs($(svgroot), i);
}
addIcon(icon, id);
}
}
if (opts.placement) {
$.each(opts.placement, function (sel, id) {
if (!svgIcons[id]) return;
$(sel).each(function (i) {
let copy = svgIcons[id].clone();
if (i > 0 && !toImage) copy = fixIDs(copy, i, true);
setIcon($(this), copy, id);
});
});
}
if (!fallback) {
if (toImage) tempHolder.remove();
if (dataEl) dataEl.remove();
if (testImg) testImg.remove();
}
if (opts.resize) $.resizeSvgIcons(opts.resize);
iconsMade = true;
if (opts.callback) opts.callback(svgIcons);
}
/**
* @returns {void}
*/
function useFallback () {
if (file.includes('.svgz')) {
const regFile = file.replace('.svgz', '.svg');
if (window.console) {
console.log('.svgz failed, trying with .svg'); // eslint-disable-line no-console
}
$.svgIcons(regFile, opts);
} else if (opts.fallback) {
makeIcons(false, opts.fallback);
}
}
};
/**
* @function external:jQuery.getSvgIcon
* @param {string} id
* @param {boolean} uniqueClone Whether to clone
* @returns {external:jQuery} The icon (optionally cloned)
*/
$.getSvgIcon = function (id, uniqueClone) {
let icon = svgIcons[id];
if (uniqueClone && icon) {
icon = fixIDs(icon, 0, true).clone(true);
}
return icon;
};
/**
* @typedef {GenericArray} module:jQuerySVGIcons.Dimensions
* @property {Integer} length 2
* @property {Float} 0 Width
* @property {Float} 1 Height
*/
/**
* If a Float is used, it will represent width and height. Arrays contain
* the width and height.
* @typedef {module:jQuerySVGIcons.Dimensions|Float} module:jQuerySVGIcons.Size
*/
/**
* @function external:jQuery.resizeSvgIcons
* @param {PlainObject<string, module:jQuerySVGIcons.Size>} obj Object with
* selectors as keys. The values are sizes.
* @returns {void}
*/
$.resizeSvgIcons = function (obj) {
// FF2 and older don't detect .svg_icon, so we change it detect svg elems instead
const changeSel = !$('.svg_icon:first').length;
$.each(obj, function (sel, size) {
const arr = Array.isArray(size);
const w = arr ? size[0] : size,
h = arr ? size[1] : size;
if (changeSel) {
sel = sel.replace(/\.svg_icon/g, 'svg');
}
$(sel).each(function () {
this.setAttribute('width', w);
this.setAttribute('height', h);
if (window.opera && window.widget) {
this.parentNode.style.width = w + 'px';
this.parentNode.style.height = h + 'px';
}
});
});
};
return $;
}