first pass

This commit is contained in:
JFH
2020-12-20 19:12:43 +01:00
parent 53e7c91731
commit 88690b6a2b
7 changed files with 348 additions and 341 deletions

View File

@@ -61,7 +61,6 @@ export default class ConfigObj {
* @property {boolean} [avoidClientSideOpen=false] Used by `ext-server_opensave.js`; set to `true` if you wish to always open from the server and not only as fallback when FileReader client support is lacking * @property {boolean} [avoidClientSideOpen=false] Used by `ext-server_opensave.js`; set to `true` if you wish to always open from the server and not only as fallback when FileReader client support is lacking
* @property {string[]} [extensions=[]] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included. See {@link module:SVGEditor~defaultExtensions}. * @property {string[]} [extensions=[]] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included. See {@link module:SVGEditor~defaultExtensions}.
* @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage * @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage
* @property {null|PlainObject} [colorPickerCSS=null] Object of CSS properties mapped to values (for jQuery) to apply to the color picker. See {@link http://api.jquery.com/css/#css-properties}. A `null` value (the default) will cause the CSS to default to `left` with a position equal to that of the `fill_color` or `stroke_color` element minus 140, and a `bottom` equal to 40
* @property {string} [paramurl] This was available via URL only. Allowed an un-encoded URL within the query string (use "url" or "source" with a data: URI instead) * @property {string} [paramurl] This was available via URL only. Allowed an un-encoded URL within the query string (use "url" or "source" with a data: URI instead)
* @property {Float} [canvas_expansion=3] The minimum area visible outside the canvas, as a multiple of the image dimensions. The larger the number, the more one can scroll outside the canvas. * @property {Float} [canvas_expansion=3] The minimum area visible outside the canvas, as a multiple of the image dimensions. The larger the number, the more one can scroll outside the canvas.
* @property {PlainObject} [initFill] Init fill properties * @property {PlainObject} [initFill] Init fill properties
@@ -89,8 +88,7 @@ export default class ConfigObj {
* @property {boolean} [showGrid=false] Set by `ext-grid.js`; determines whether or not to show the grid by default * @property {boolean} [showGrid=false] Set by `ext-grid.js`; determines whether or not to show the grid by default
* @property {boolean} [show_outside_canvas=true] Defines whether or not elements outside the canvas should be visible. Set and used in `svgcanvas.js`. * @property {boolean} [show_outside_canvas=true] Defines whether or not elements outside the canvas should be visible. Set and used in `svgcanvas.js`.
* @property {boolean} [selectNew=true] If true, will replace the selection with the current element and automatically select element objects (when not in "path" mode) after they are created, showing their grips (v2.6). Set and used in `svgcanvas.js` (`mouseUp`). * @property {boolean} [selectNew=true] If true, will replace the selection with the current element and automatically select element objects (when not in "path" mode) after they are created, showing their grips (v2.6). Set and used in `svgcanvas.js` (`mouseUp`).
* @todo Some others could be preferences as well (e.g., preventing URL changing of extensions, defaultExtensions, stylesheets, colorPickerCSS); Change the following to preferences and add pref controls where missing to the UI (e.g., `canvas_expansion`, `initFill`, `initStroke`, `text`, `initOpacity`, `dimensions`, `initTool`, `wireframe`, `showlayers`, `gridSnapping`, `gridColor`, `baseUnit`, `snappingStep`, `showRulers`, `exportWindowType`, `showGrid`, `show_outside_canvas`, `selectNew`)? */
*/
this.defaultConfig = { this.defaultConfig = {
canvasName: 'default', canvasName: 'default',
canvas_expansion: 3, canvas_expansion: 3,
@@ -109,7 +107,6 @@ export default class ConfigObj {
font_family: 'serif' font_family: 'serif'
}, },
initOpacity: 1, initOpacity: 1,
colorPickerCSS: null, // Defaults to 'left' with a position equal to that of the fill_color or stroke_color element minus 140, and a 'bottom' equal to 40
initTool: 'select', initTool: 'select',
exportWindowType: 'new', // 'same' (todo: also support 'download') exportWindowType: 'new', // 'same' (todo: also support 'download')
wireframe: false, wireframe: false,

View File

@@ -0,0 +1,172 @@
/* globals $ */
/**
*
*/
class PaintBox {
/**
* @param {string|Element|external:jQuery} container
* @param {PlainObject} svgcanvas
* @param {"fill"} type
* @param {string} color
* @param {number} opacity
*/
constructor (container, svgcanvas, type, color, opacity) {
// set up gradients to be used for the buttons
const svgdocbox = new DOMParser().parseFromString(
`<svg xmlns="http://www.w3.org/2000/svg" width="16.5" height="16.5">
<rect
fill="#${color}" opacity="${opacity}"/>
<defs><linearGradient id="gradbox_${PaintBox.ctr++}"/></defs>
</svg>`,
'text/xml'
);
let docElem = svgdocbox.documentElement;
docElem = $(container)[0].appendChild(document.importNode(docElem, true));
docElem.setAttribute('width', 16.5);
this.rect = docElem.firstElementChild;
this.defs = docElem.getElementsByTagName('defs')[0];
this.grad = this.defs.firstElementChild;
this.paint = new $.jGraduate.Paint({solidColor: color});
this.type = type;
this.svgcanvas = svgcanvas;
}
/**
* @param {module:jGraduate~Paint} paint
* @param {boolean} apply
* @returns {void}
*/
setPaint (paint, apply) {
this.paint = paint;
const ptype = paint.type;
const opac = paint.alpha / 100;
let fillAttr = 'none';
switch (ptype) {
case 'solidColor':
fillAttr = (paint[ptype] !== 'none') ? '#' + paint[ptype] : paint[ptype];
break;
case 'linearGradient':
case 'radialGradient': {
this.grad.remove();
this.grad = this.defs.appendChild(paint[ptype]);
const id = this.grad.id = 'gradbox_' + this.type;
fillAttr = 'url(#' + id + ')';
break;
}
}
this.rect.setAttribute('fill', fillAttr);
this.rect.setAttribute('opacity', opac);
if (apply) {
this.svgCanvas.setColor(this.type, this._paintColor, true);
this.svgCanvas.setPaintOpacity(this.type, this._paintOpacity, true);
}
}
/**
* @param {string} color
* @param {Float} opac
* @param {string} type
* @returns {module:jGraduate~Paint}
*/
getPaint (color, opac, type) {
// update the editor's fill paint
const opts = {alpha: opac};
if (color.startsWith('url(#')) {
let refElem = this.svgCanvas.getRefElem(color);
refElem = (refElem) ? refElem.cloneNode(true) : $('#' + type + '_color defs *')[0];
opts[refElem.tagName] = refElem;
} else if (color.startsWith('#')) {
opts.solidColor = color.substr(1);
} else {
opts.solidColor = 'none';
}
return new $.jGraduate.Paint(opts);
}
/**
* @param {PlainObject} selectedElement
* @param {boolean} apply
* @returns {void}
*/
update (selectedElement, apply) {
if (!selectedElement) { return; }
const {type} = this;
switch (selectedElement.tagName) {
case 'use':
case 'image':
case 'foreignObject':
// These elements don't have fill or stroke, so don't change
// the current value
return;
case 'g':
case 'a': {
const childs = selectedElement.getElementsByTagName('*');
let gPaint = null;
for (let i = 0, len = childs.length; i < len; i++) {
const elem = childs[i];
const p = elem.getAttribute(type);
if (i === 0) {
gPaint = p;
} else if (gPaint !== p) {
gPaint = null;
break;
}
}
if (gPaint === null) {
// No common color, don't update anything
this._paintColor = null;
return;
}
this._paintColor = gPaint;
this._paintOpacity = 1;
break;
} default: {
this._paintOpacity = Number.parseFloat(selectedElement.getAttribute(type + '-opacity'));
if (Number.isNaN(this._paintOpacity)) {
this._paintOpacity = 1.0;
}
const defColor = type === 'fill' ? 'black' : 'none';
this._paintColor = selectedElement.getAttribute(type) || defColor;
}
}
if (apply) {
this.svgCanvas.setColor(type, this._paintColor, true);
this.svgCanvas.setPaintOpacity(type, this._paintOpacity, true);
}
this._paintOpacity *= 100;
const paint = this.getPaint(this._paintColor, this._paintOpacity, type);
// update the rect inside #fill_color/#stroke_color
this.setPaint(paint);
}
/**
* @returns {void}
*/
prep () {
const ptype = this.paint.type;
switch (ptype) {
case 'linearGradient':
case 'radialGradient': {
const paint = new $.jGraduate.Paint({copy: this.paint});
this.svgCanvas.setPaint(this.type, paint);
break;
}
}
}
}
PaintBox.ctr = 0;
export default PaintBox;

View File

@@ -7,3 +7,4 @@ import './seSpinInput.js';
import './sePalette.js'; import './sePalette.js';
import './seMenu.js'; import './seMenu.js';
import './seMenuItem.js'; import './seMenuItem.js';
import './seColorPicker.js';

View File

@@ -0,0 +1,146 @@
/* globals jQuery */
import jQueryPluginJGraduate from '../jgraduate/jQuery.jGraduate.js';
import jQueryPluginJPicker from '../jgraduate/jQuery.jPicker.js';
import PaintBox from './PaintBox.js';
const $ = [
jQueryPluginJGraduate,
jQueryPluginJPicker
].reduce((jq, func) => func(jq), jQuery);
const template = document.createElement('template');
template.innerHTML = `
<style>
</style>
<div id="picker">
<img src="./images/logo.svg" alt="icon">
<label for="color" title="Change xxx color"></label>
<div class="block">
<div id="bg"></div>
<div id="color" class="block"></div>
</div>
</div>
<!-- hidden div -->
<div id="color_picker"></div>
`;
/**
* @class SeMenuItem
*/
export class SeColorPicker 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.$img = this._shadowRoot.querySelector('img');
this.$label = this._shadowRoot.querySelector('label');
this.$picker = this._shadowRoot.getElementById('picker');
this.paintBox = null;
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return ['label', 'src', 'value', 'picker'];
}
/**
* @function attributeChangedCallback
* @param {string} name
* @param {string} oldValue
* @param {string} newValue
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
switch (name) {
case 'src':
this.$img.setAttribute('src', newValue);
break;
case 'label':
this.$label.setAttribute('title', 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 get
* @returns {any}
*/
get src () {
return this.getAttribute('src');
}
/**
* @function set
* @returns {void}
*/
set src (value) {
this.setAttribute('src', value);
}
/**
* @param {PlainObject} selectedElement
* @param {bool} apply
* @returns {void}
*/
update (selectedElement, apply) {
this.paintBox.update(selectedElement, apply);
}
/**
* @function connectedCallback
* @returns {void}
*/
connectedCallback () {
this.paintBox = new PaintBox(this, this.type);
let {paint} = this.paintBox;
$('#color_picker')
.draggable({
cancel: '.jGraduate_tabs, .jGraduate_colPick, .jGraduate_gradPick, .jPicker',
containment: 'window'
})
.jGraduate(
{
images: {clientPath: './jgraduate/images/'},
paint,
window: {pickerTitle: this.label},
// images: {clientPath: configObj.curConfig.imgPath},
newstop: 'inverse'
},
function (p) {
paint = new $.jGraduate.Paint(p);
this.paintBox.setPaint(paint);
this.svgCanvas.setPaint(this.picker, paint);
$('#color_picker').hide();
},
() => {
$('#color_picker').hide();
}
);
}
}
// Register
customElements.define('se-colorpicker', SeColorPicker);

View File

@@ -340,20 +340,8 @@
<div value="layer">Fit to layer content</div> <div value="layer">Fit to layer content</div>
<div value="content">Fit to all content</div> <div value="content">Fit to all content</div>
</se-dropdown> </se-dropdown>
<div id="tool_fill"> <se-colorpicker id="fill_color" src="fill.svg" title="Change fill color" type="fill"></se-colorpicker>
<label for="fill_color" title="Change fill color"></label> <se-colorpicker id="stroke_color" src="stroke.svg" title="Change stroke color" type="stroke"></se-colorpicker>
<div class="color_block">
<div id="fill_bg"></div>
<div id="fill_color" class="color_block"></div>
</div>
</div> <!-- tool_fill -->
<div id="tool_stroke">
<label title="Change stroke color"></label>
<div class="color_block">
<div id="stroke_bg"></div>
<div id="stroke_color" class="color_block" title="Change stroke color" src="./images/fill.svg"></div>
</div>
</div>
<se-spin-input id="stroke_width" min=0 max=99 step=1 title="Change stroke width by 1" label=""></se-spin-input> <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"> <label class="stroke_tool">
<select id="stroke_style" title="Change stroke dash style"> <select id="stroke_style" title="Change stroke dash style">
@@ -410,8 +398,6 @@
<li class="push_button" id="tool_posbottom" title="Align Bottom"></li> <li class="push_button" id="tool_posbottom" title="Align Bottom"></li>
</ul> </ul>
</div> </div>
<!-- hidden divs -->
<div id="color_picker"></div>
</div> <!-- svg_editor --> </div> <!-- svg_editor -->
<div id="svg_source_editor"> <div id="svg_source_editor">
<div class="overlay"></div> <div class="overlay"></div>

View File

@@ -58,7 +58,6 @@ svgEditor.setConfig({
// opacity: 1 // opacity: 1
// }, // },
// initOpacity: 1, // initOpacity: 1,
// colorPickerCSS: null,
// initTool: 'select', // initTool: 'select',
// exportWindowType: 'new', // 'same' // exportWindowType: 'new', // 'same'
// wireframe: false, // wireframe: false,

View File

@@ -17,7 +17,6 @@
*/ */
import './touch.js'; import './touch.js';
import {NS} from '../common/namespaces.js';
import {isChrome, isGecko, isMac} from '../common/browser.js'; import {isChrome, isGecko, isMac} from '../common/browser.js';
// Until we split this into smaller files, this helps distinguish utilities // Until we split this into smaller files, this helps distinguish utilities
@@ -31,9 +30,7 @@ import {
import SvgCanvas from '../svgcanvas/svgcanvas.js'; import SvgCanvas from '../svgcanvas/svgcanvas.js';
import jQueryPluginJSHotkeys from './js-hotkeys/jquery.hotkeys.min.js'; import jQueryPluginJSHotkeys from './js-hotkeys/jquery.hotkeys.min.js';
import jQueryPluginJGraduate from './jgraduate/jQuery.jGraduate.js';
import jQueryPluginContextMenu from './contextmenu/jQuery.contextMenu.js'; import jQueryPluginContextMenu from './contextmenu/jQuery.contextMenu.js';
import jQueryPluginJPicker from './jgraduate/jQuery.jPicker.js';
import jQueryPluginDBox from '../svgcanvas/dbox.js'; import jQueryPluginDBox from '../svgcanvas/dbox.js';
import ConfigObj from './ConfigObj.js'; import ConfigObj from './ConfigObj.js';
@@ -76,10 +73,7 @@ const editor = {
setStrings setStrings
}; };
const $ = [ const $ = [jQueryPluginJSHotkeys, jQueryPluginContextMenu].reduce((jq, func) => func(jq), jQuery);
jQueryPluginJSHotkeys, jQueryPluginJGraduate,
jQueryPluginContextMenu, jQueryPluginJPicker
].reduce((jq, func) => func(jq), jQuery);
const homePage = 'https://github.com/SVG-Edit/svgedit'; const homePage = 'https://github.com/SVG-Edit/svgedit';
@@ -338,26 +332,6 @@ editor.init = () => {
} }
}; };
/**
* @type {string}
*/
const uaPrefix = (function () {
const regex = /^(?:Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/;
const someScript = document.getElementsByTagName('script')[0];
for (const prop in someScript.style) {
if (regex.test(prop)) {
// test is faster than match, so it's better to perform
// that on the lot and match only when necessary
return prop.match(regex)[0];
}
}
// Nothing found so far?
if ('WebkitOpacity' in someScript.style) { return 'Webkit'; }
if ('KhtmlOpacity' in someScript.style) { return 'Khtml'; }
return '';
}());
/** /**
* @name module:SVGEditor.canvas * @name module:SVGEditor.canvas
* @type {module:svgcanvas.SvgCanvas} * @type {module:svgcanvas.SvgCanvas}
@@ -579,13 +553,12 @@ editor.init = () => {
const {undoMgr} = svgCanvas; const {undoMgr} = svgCanvas;
const workarea = $('#workarea'); const workarea = $('#workarea');
const canvMenu = $('#cmenu_canvas'); const canvMenu = $('#cmenu_canvas');
const paintBox = {fill: null, stroke: null};
let exportWindow = null, let exportWindow = null;
defaultImageURL = configObj.curConfig.imgPath + 'logo.svg', let defaultImageURL = configObj.curConfig.imgPath + 'logo.svg';
zoomInIcon = 'crosshair', const zoomInIcon = 'crosshair';
zoomOutIcon = 'crosshair', const zoomOutIcon = 'crosshair';
uiContext = 'toolbars'; let uiContext = 'toolbars';
// For external openers // For external openers
(function () { (function () {
@@ -1156,6 +1129,11 @@ editor.init = () => {
); );
}; };
const updateColorpickers = (apply) => {
$id('fill_color').update(selectedElement, apply);
$id('stroke_color').update(selectedElement, apply);
};
/** /**
* Updates the toolbar (colors, opacity, etc) based on the selected element. * Updates the toolbar (colors, opacity, etc) based on the selected element.
* This function also updates the opacity and id elements that are in the * This function also updates the opacity and id elements that are in the
@@ -1186,14 +1164,10 @@ editor.init = () => {
} }
$('#stroke_width').val(gWidth === null ? '' : gWidth); $('#stroke_width').val(gWidth === null ? '' : gWidth);
updateColorpickers(true);
paintBox.fill.update(true);
paintBox.stroke.update(true);
break; break;
} default: { } default: {
paintBox.fill.update(true); updateColorpickers(true);
paintBox.stroke.update(true);
$('#stroke_width').val(selectedElement.getAttribute('stroke-width') || 1); $('#stroke_width').val(selectedElement.getAttribute('stroke-width') || 1);
$('#stroke_style').val(selectedElement.getAttribute('stroke-dasharray') || 'none'); $('#stroke_style').val(selectedElement.getAttribute('stroke-dasharray') || 'none');
@@ -1230,9 +1204,6 @@ editor.init = () => {
* @returns {void} * @returns {void}
*/ */
const updateWireFrame = () => { const updateWireFrame = () => {
// Test support
if (supportsNonSS) { return; }
const rule = ` const rule = `
#workarea.wireframe #svgcontent * { #workarea.wireframe #svgcontent * {
stroke-width: ${1 / svgCanvas.getZoom()}px; stroke-width: ${1 / svgCanvas.getZoom()}px;
@@ -1373,8 +1344,7 @@ editor.init = () => {
// In the event a gradient was flipped: // In the event a gradient was flipped:
if (selectedElement && mode === 'select') { if (selectedElement && mode === 'select') {
paintBox.fill.update(); updateColorpickers();
paintBox.stroke.update();
} }
svgCanvas.runExtensions('elementChanged', /** @type {module:svgcanvas.SvgCanvas#event:ext_elementChanged} */ { svgCanvas.runExtensions('elementChanged', /** @type {module:svgcanvas.SvgCanvas#event:ext_elementChanged} */ {
@@ -1517,8 +1487,8 @@ editor.init = () => {
* @returns {void} * @returns {void}
*/ */
const prepPaints = () => { const prepPaints = () => {
paintBox.fill.prep(); $id('fill_color').prep();
paintBox.stroke.prep(); $id('stroke_color').prep();
}; };
/** /**
@@ -1649,27 +1619,6 @@ editor.init = () => {
return runCallback(); return runCallback();
}; };
/**
* @param {string} color
* @param {Float} opac
* @param {string} type
* @returns {module:jGraduate~Paint}
*/
const getPaint = function (color, opac, type) {
// update the editor's fill paint
const opts = {alpha: opac};
if (color.startsWith('url(#')) {
let refElem = svgCanvas.getRefElem(color);
refElem = (refElem) ? refElem.cloneNode(true) : $('#' + type + '_color defs *')[0];
opts[refElem.tagName] = refElem;
} else if (color.startsWith('#')) {
opts.solidColor = color.substr(1);
} else {
opts.solidColor = 'none';
}
return new $.jGraduate.Paint(opts);
};
// bind the selected event to our function that handles updates to the UI // bind the selected event to our function that handles updates to the UI
svgCanvas.bind('selected', selectedChanged); svgCanvas.bind('selected', selectedChanged);
svgCanvas.bind('transition', elementTransition); svgCanvas.bind('transition', elementTransition);
@@ -2737,7 +2686,6 @@ editor.init = () => {
$id('tool_wireframe').pressed = !$id('tool_wireframe').pressed; $id('tool_wireframe').pressed = !$id('tool_wireframe').pressed;
workarea.toggleClass('wireframe'); workarea.toggleClass('wireframe');
if (supportsNonSS) { return; }
const wfRules = $('#wireframe_rules'); const wfRules = $('#wireframe_rules');
if (!wfRules.length) { if (!wfRules.length) {
/* wfRules = */ $('<style id="wireframe_rules"></style>').appendTo('head'); /* wfRules = */ $('<style id="wireframe_rules"></style>').appendTo('head');
@@ -2754,7 +2702,11 @@ editor.init = () => {
const {picker, color} = e.detail; const {picker, color} = e.detail;
// Webkit-based browsers returned 'initial' here for no stroke // 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)}); const paint = color === 'none' ? new $.jGraduate.Paint() : new $.jGraduate.Paint({alpha: 100, solidColor: color.substr(1)});
paintBox[picker].setPaint(paint); if (picker === 'fill') {
$id('fill_color').setPaint(paint);
} else {
$id('stroke_color').setPaint(paint);
}
svgCanvas.setColor(picker, color); svgCanvas.setColor(picker, color);
if (color !== 'none' && svgCanvas.getPaintOpacity(picker) !== 1) { if (color !== 'none' && svgCanvas.getPaintOpacity(picker) !== 1) {
svgCanvas.setPaintOpacity(picker, 1.0); svgCanvas.setPaintOpacity(picker, 1.0);
@@ -2988,246 +2940,16 @@ editor.init = () => {
$('#change_image_url').click(promptImgURL); $('#change_image_url').click(promptImgURL);
/**
* @param {external:jQuery} elem
* @todo Go back to the color boxes having white background-color and then setting
* background-image to none.png (otherwise partially transparent gradients look weird)
* @returns {void}
*/
const colorPicker = function (elem) {
const picker = elem.attr('id') === 'stroke_color' ? 'stroke' : 'fill';
// const opacity = (picker == 'stroke' ? $('#stroke_opacity') : $('#fill_opacity'));
const title = picker === 'stroke'
? uiStrings.ui.pick_stroke_paint_opacity
: uiStrings.ui.pick_fill_paint_opacity;
// let wasNone = false; // Currently unused
const pos = elem.offset();
let {paint} = paintBox[picker];
$('#color_picker')
.draggable({
cancel: '.jGraduate_tabs, .jGraduate_colPick, .jGraduate_gradPick, .jPicker',
containment: 'window'
})
.css(configObj.curConfig.colorPickerCSS || {left: pos.left - 140, bottom: 40})
.jGraduate(
{
images: {clientPath: './jgraduate/images/'},
paint,
window: {pickerTitle: title},
// images: {clientPath: configObj.curConfig.imgPath},
newstop: 'inverse'
},
function (p) {
paint = new $.jGraduate.Paint(p);
paintBox[picker].setPaint(paint);
svgCanvas.setPaint(picker, paint);
$('#color_picker').hide();
},
() => {
$('#color_picker').hide();
}
);
};
/**
* Paint box class.
*/
class PaintBox {
/**
* @param {string|Element|external:jQuery} container
* @param {"fill"} type
*/
constructor (container, type) {
const cur = configObj.curConfig[type === 'fill' ? 'initFill' : 'initStroke'];
// set up gradients to be used for the buttons
const svgdocbox = new DOMParser().parseFromString(
`<svg xmlns="http://www.w3.org/2000/svg" width="16.5" height="16.5">
<rect
fill="#${cur.color}" opacity="${cur.opacity}"/>
<defs><linearGradient id="gradbox_${PaintBox.ctr++}"/></defs>
</svg>`,
'text/xml'
);
let docElem = svgdocbox.documentElement;
docElem = $(container)[0].appendChild(document.importNode(docElem, true));
docElem.setAttribute('width', 16.5);
this.rect = docElem.firstElementChild;
this.defs = docElem.getElementsByTagName('defs')[0];
this.grad = this.defs.firstElementChild;
this.paint = new $.jGraduate.Paint({solidColor: cur.color});
this.type = type;
}
/**
* @param {module:jGraduate~Paint} paint
* @param {boolean} apply
* @returns {void}
*/
setPaint (paint, apply) {
this.paint = paint;
const ptype = paint.type;
const opac = paint.alpha / 100;
let fillAttr = 'none';
switch (ptype) {
case 'solidColor':
fillAttr = (paint[ptype] !== 'none') ? '#' + paint[ptype] : paint[ptype];
break;
case 'linearGradient':
case 'radialGradient': {
this.grad.remove();
this.grad = this.defs.appendChild(paint[ptype]);
const id = this.grad.id = 'gradbox_' + this.type;
fillAttr = 'url(#' + id + ')';
break;
}
}
this.rect.setAttribute('fill', fillAttr);
this.rect.setAttribute('opacity', opac);
if (apply) {
svgCanvas.setColor(this.type, this._paintColor, true);
svgCanvas.setPaintOpacity(this.type, this._paintOpacity, true);
}
}
/**
* @param {boolean} apply
* @returns {void}
*/
update (apply) {
if (!selectedElement) { return; }
const {type} = this;
switch (selectedElement.tagName) {
case 'use':
case 'image':
case 'foreignObject':
// These elements don't have fill or stroke, so don't change
// the current value
return;
case 'g':
case 'a': {
const childs = selectedElement.getElementsByTagName('*');
let gPaint = null;
for (let i = 0, len = childs.length; i < len; i++) {
const elem = childs[i];
const p = elem.getAttribute(type);
if (i === 0) {
gPaint = p;
} else if (gPaint !== p) {
gPaint = null;
break;
}
}
if (gPaint === null) {
// No common color, don't update anything
this._paintColor = null;
return;
}
this._paintColor = gPaint;
this._paintOpacity = 1;
break;
} default: {
this._paintOpacity = Number.parseFloat(selectedElement.getAttribute(type + '-opacity'));
if (Number.isNaN(this._paintOpacity)) {
this._paintOpacity = 1.0;
}
const defColor = type === 'fill' ? 'black' : 'none';
this._paintColor = selectedElement.getAttribute(type) || defColor;
}
}
if (apply) {
svgCanvas.setColor(type, this._paintColor, true);
svgCanvas.setPaintOpacity(type, this._paintOpacity, true);
}
this._paintOpacity *= 100;
const paint = getPaint(this._paintColor, this._paintOpacity, type);
// update the rect inside #fill_color/#stroke_color
this.setPaint(paint);
}
/**
* @returns {void}
*/
prep () {
const ptype = this.paint.type;
switch (ptype) {
case 'linearGradient':
case 'radialGradient': {
const paint = new $.jGraduate.Paint({copy: this.paint});
svgCanvas.setPaint(this.type, paint);
break;
}
}
}
}
PaintBox.ctr = 0;
paintBox.fill = new PaintBox('#fill_color', 'fill');
paintBox.stroke = new PaintBox('#stroke_color', 'stroke');
$('#stroke_width').val(configObj.curConfig.initStroke.width); $('#stroke_width').val(configObj.curConfig.initStroke.width);
$('#group_opacity').val(configObj.curConfig.initOpacity * 100); $('#group_opacity').val(configObj.curConfig.initOpacity * 100);
// Use this SVG elem to test vectorEffect support const handleColorPicker = (type, evt) => {
const testEl = paintBox.fill.rect.cloneNode(false); // const {paint} = evt.detail;
testEl.setAttribute('style', 'vector-effect:non-scaling-stroke');
const supportsNonSS = (testEl.style.vectorEffect === 'non-scaling-stroke');
testEl.removeAttribute('style');
const svgdocbox = paintBox.fill.rect.ownerDocument;
// Use this to test support for blur element. Seems to work to test support in Webkit
const blurTest = svgdocbox.createElementNS(NS.SVG, 'feGaussianBlur');
if (blurTest.stdDeviationX === undefined) {
$('#blur').hide();
}
$(blurTest).remove();
// Test for zoom icon support
(function () {
const pre = '-' + uaPrefix.toLowerCase() + '-zoom-';
const zoom = pre + 'in';
workarea.css('cursor', zoom);
if (workarea.css('cursor') === zoom) {
zoomInIcon = zoom;
zoomOutIcon = pre + 'out';
}
workarea.css('cursor', 'auto');
}());
// Test for embedImage support (use timeout to not interfere with page load)
setTimeout(() => {
svgCanvas.embedImage('images/logo.svg', function (datauri) {
if (!datauri) {
// Disable option
const $imgDialog = document.getElementById('se-img-prop');
editor.pref('img_save', 'ref');
$imgDialog.setAttribute('save', 'ref');
$imgDialog.setAttribute('embed', 'one|' + uiStrings.notification.featNotSupported);
}
});
}, 1000);
$('#fill_color, #tool_fill').click(() => {
colorPicker($('#fill_color'));
updateToolButtonState(); updateToolButtonState();
}); };
$('#stroke_color, #tool_stroke').click(() => { $id('stroke_color').addEventListener('change', (evt) => handleColorPicker('stroke', evt));
colorPicker($('#stroke_color')); $id('fill_color').addEventListener('change', (evt) => handleColorPicker('stroke', evt));
updateToolButtonState();
});
$('#group_opacityLabel').click(() => { $('#group_opacityLabel').click(() => {
$('#opacity_dropdown button').mousedown(); $('#opacity_dropdown button').mousedown();
@@ -3329,22 +3051,6 @@ editor.init = () => {
$(window).bind('load resize', centerCanvas); $(window).bind('load resize', centerCanvas);
// function setResolution (w, h, center) {
// updateCanvas();
// // w -= 0; h -= 0;
// // $('#svgcanvas').css({width: w, height: h});
// // $('#canvas_width').val(w);
// // $('#canvas_height').val(h);
// //
// // if (center) {
// // const wArea = workarea;
// // const scrollY = h/2 - wArea.height()/2;
// // const scrollX = w/2 - wArea.width()/2;
// // wArea[0].scrollTop = scrollY;
// // wArea[0].scrollLeft = scrollX;
// // }
// }
// Prevent browser from erroneously repopulating fields // Prevent browser from erroneously repopulating fields
$('input,select').attr('autocomplete', 'off'); $('input,select').attr('autocomplete', 'off');