Merge pull request #687 from SVG-Edit/refactor-svgcanvas

Refactor svgcanvas and move to standard lint
master
JFH 2021-12-30 08:01:29 -03:00 committed by GitHub
commit 88207acc98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
259 changed files with 24001 additions and 26812 deletions

View File

@ -1,30 +0,0 @@
node_modules
ignore
coverage
instrumented
dist
docs/jsdoc
archive
jsconfig.json
releases
!.eslintrc.js
!.ncurc.js
es-dev-server.config.js
nyc.config.js
svgedit-custom.css
# Vendor/minified files
src/editor/jquery.min.js
# Previously minified though exporting
src/editor/js-hotkeys
src/editor/extensions/ext-mathjax/mathjax
# jquery files
src/editor/jgraduate/jQuery.jPicker.js

View File

@ -1,96 +0,0 @@
"use strict";
module.exports = {
extends: [
"plugin:compat/recommended",
"plugin:node/recommended",
"plugin:no-unsanitized/DOM",
"plugin:promise/recommended",
"plugin:import/errors",
"plugin:markdown/recommended",
"eslint:recommended"
],
plugins: [ "jsdoc", "promise", "html", "import" ],
parserOptions: {
ecmaVersion: 2020,
sourceType: "module"
},
env: {
browser: true,
es6: true
},
rules: {
/** @todo len should probably more 120-150 */
"max-len": [ "warn", { "code": 250 } ],
"indent": [ "error", 2 ],
"no-var": "error",
/** @todo this rule should be actived. needs some courage as this rule is broken in many places... */
"one-var": [ "error", "never" ],
/** @todo jsdoc should be made warn or error */
"valid-jsdoc": "off",
/** @todo no param reassign creates too many warnings but should be a warning */
"no-param-reassign": "off",
/** @todo no use before define creates too many warnings but should be a warning */
"no-use-before-define": "off",
/** @todo camel case creates too many warnings but should be a warning */
"camelcase": "off",
"comma-dangle": [ "error" ],
"node/no-unsupported-features/es-syntax": 0,
"no-unused-vars": [ "error", { "argsIgnorePattern": "^_" } ],
"semi" : "error",
"prefer-const": "error",
"no-trailing-spaces": "error",
"array-bracket-spacing": [ "error", "always" ],
"comma-spacing": "error",
"object-curly-spacing": [ "error", "always" ],
"no-console": [
"warn",
{ "allow": [ "warn", "error", "info", "table" ] }
],
"arrow-parens": [ "error", "always" ]
},
overrides: [
{
files: [ 'cypress/**/*' ],
extends: [
"plugin:cypress/recommended"
],
env: {
mocha: true,
node: true
},
globals: { "assert": true },
rules: {
// with ci, instrumented is not created before linter
"import/no-unresolved": [ 2, { ignore: [ 'instrumented' ] } ],
"node/no-missing-import": 0,
"node/no-unpublished-import": 0,
"node/no-unpublished-require": 0
}
},
{
files: [ 'docs/**/*' ],
rules: { // md files have example that don't need a strict checking
"no-undef": 0,
"import/no-unresolved": 0,
"node/no-missing-import": 0,
"jsdoc/check-examples": [
"warn",
{
rejectExampleCodeRegex: "^`",
checkDefaults: true,
checkParams: true,
checkProperties: true
}
]
}
},
{
files: [ 'src/editor/locale/*.js' ],
rules: { // lang files may have long length
"max-len": "off",
"camelcase": "off"
}
}
]
};

1
.gitignore vendored
View File

@ -9,7 +9,6 @@ coverage
instrumented
.nyc_output
.vscode
.eslintcache
.DS_Store
.idea
dist

View File

@ -2,5 +2,4 @@ lgtm.yml
coverage/**
.nyc_output
instrumented/**
.eslintcache
node_modules/**

File diff suppressed because it is too large Load Diff

View File

@ -1,91 +1,78 @@
exports[`use all parts of svg-edit > check tool_source_set #0`] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="640"
height="480"
id="svgcontent"
overflow="visible"
x="640"
y="480"
viewBox="0 0 640 480"
>
<g class="layer"><title>Layer 1</title></g>
</svg>
<body>
<svg
width="640"
height="480"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<g class="layer">
<title>Layer 1</title>
</g>
</svg>
</body>
`;
exports[`use all parts of svg-edit > check tool_shape #0`] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="640"
height="480"
id="svgcontent"
overflow="visible"
x="640"
y="480"
viewBox="0 0 640 480"
>
<g class="layer" style="pointer-events:all">
<title style="pointer-events:inherit">Layer 1</title>
<path
fill="#FF0000"
stroke="#000000"
stroke-width="5"
style="pointer-events:inherit"
d="m208.99746606586632,203.82033218045854 c4.117685576066451,-11.81303239035457 20.250912669179264,0 0,15.18818450188445 c-20.250912669179264,-15.18818450188445 -4.117685576066451,-27.00121689223902 0,-15.18818450188445 z"
id="svg_1"
fill-opacity="1"
stroke-opacity="1"
transform="rotate(43.2545 208.997 209.008)"
></path>
</g>
</svg>
<body>
<svg
width="640"
height="480"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<g class="layer">
<title>Layer 1</title>
<path
d="m208.99747,203.82033c4.11769,-11.81303 20.25091,0 0,15.18818c-20.25091,-15.18818 -4.11769,-27.00122 0,-15.18818z"
fill="#FF0000"
id="svg_1"
stroke="#000000"
stroke-width="5"
transform="rotate(43.2545 208.997 209.008)"
></path>
</g>
</svg>
</body>
`;
exports[`use all parts of svg-edit > check tool_image #0`] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="640"
height="480"
id="svgcontent"
overflow="visible"
x="640"
y="480"
viewBox="0 0 640 480"
>
<g class="layer" style="pointer-events:all">
<title style="pointer-events:inherit">Layer 1</title>
<path
fill="#FF0000"
stroke="#000000"
stroke-width="5"
style="pointer-events:inherit"
d="m208.99746606586632,203.82033218045854 c4.117685576066451,-11.81303239035457 20.250912669179264,0 0,15.18818450188445 c-20.250912669179264,-15.18818450188445 -4.117685576066451,-27.00121689223902 0,-15.18818450188445 z"
id="svg_1"
fill-opacity="1"
stroke-opacity="1"
transform="rotate(43.2545 208.997 209.008)"
></path>
<image
x="295"
y="295"
width="20"
height="20"
id="svg_2"
opacity="0.5"
style="pointer-events:inherit"
xlink:href="./images/logo.svg"
>
<animate
attributeName="opacity"
begin="indefinite"
dur="0.2"
fill="freeze"
to="1"
></animate>
</image>
</g>
</svg>
<body>
<svg
width="640"
height="480"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g class="layer">
<title>Layer 1</title>
<path
d="m208.99747,203.82033c4.11769,-11.81303 20.25091,0 0,15.18818c-20.25091,-15.18818 -4.11769,-27.00122 0,-15.18818z"
fill="#FF0000"
id="svg_1"
stroke="#000000"
stroke-width="5"
transform="rotate(43.2545 208.997 209.008)"
></path>
<image
height="20"
id="svg_2"
opacity="0.5"
width="20"
x="295"
xlink:href="./images/logo.svg"
y="295"
>
<animate
attributeName="opacity"
begin="indefinite"
dur="0.2"
fill="freeze"
to="1"
></animate>
</image>
</g>
</svg>
</body>
`;

File diff suppressed because it is too large Load Diff

View File

@ -1,158 +1,133 @@
exports[`use all parts of svg-edit > check tool_source_set #0`] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="640"
height="480"
id="svgcontent"
overflow="visible"
x="640"
y="480"
viewBox="0 0 640 480"
>
<g class="layer"><title>Layer 1</title></g>
</svg>
<body>
<svg
width="640"
height="480"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<g class="layer">
<title>Layer 1</title>
</g>
</svg>
</body>
`;
exports[`use all parts of svg-edit > check tool_path #0`] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="640"
height="480"
id="svgcontent"
overflow="visible"
x="640"
y="480"
viewBox="0 0 640 480"
>
<g class="layer" style="pointer-events:all">
<title style="pointer-events:inherit">Layer 1</title>
<path
fill="#FF0000"
stroke="#000000"
stroke-width="5"
opacity="0.5"
style="pointer-events:inherit"
d="M 50 50 L 150 100 L 125 200 L 50 50 z"
id="svg_1"
>
<animate
attributeName="opacity"
begin="indefinite"
dur="0.2"
fill="freeze"
to="1"
></animate>
</path>
</g>
</svg>
<body>
<svg
width="640"
height="480"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<g class="layer">
<title>Layer 1</title>
<path
d="m50,50l100,50l-25,100l-75,-150z"
fill="#FF0000"
id="svg_1"
opacity="0.5"
stroke="#000000"
stroke-width="5"
>
<animate
attributeName="opacity"
begin="indefinite"
dur="0.2"
fill="freeze"
to="1"
></animate>
</path>
</g>
</svg>
</body>
`;
exports[`use all parts of svg-edit > check tool_path_change_node_xy #0`] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="640"
height="480"
id="svgcontent"
overflow="visible"
x="640"
y="480"
viewBox="0 0 640 480"
>
<g class="layer" style="pointer-events:all">
<title style="pointer-events:inherit">Layer 1</title>
<path
fill="#FF0000"
stroke="#000000"
stroke-width="5"
d="M 75 75 L 150 100 L 125 200 L 75 75 z"
id="svg_1"
fill-opacity="1"
stroke-opacity="1"
></path>
</g>
</svg>
`;
exports[`use all parts of svg-edit > check tool_path_change_seg_type #0`] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="640"
height="480"
id="svgcontent"
overflow="visible"
x="640"
y="480"
viewBox="0 0 640 480"
>
<g class="layer" style="pointer-events:all">
<title style="pointer-events:inherit">Layer 1</title>
<path
fill="#FF0000"
stroke="#000000"
stroke-width="5"
d="M 75 75 L 150 100 L 125 200 C 235.33333333333331 330.33333333333337 91.66666666666667 116.66666666666666 75 75 z"
id="svg_1"
fill-opacity="1"
stroke-opacity="1"
></path>
</g>
</svg>
`;
exports[`use all parts of svg-edit > check tool_path_change_clone_node #0`] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="640"
height="480"
id="svgcontent"
overflow="visible"
x="640"
y="480"
viewBox="0 0 640 480"
>
<g class="layer" style="pointer-events:all">
<title style="pointer-events:inherit">Layer 1</title>
<path
fill="#FF0000"
stroke="#000000"
stroke-width="5"
d="M 201 246 L 150 100 L 125 200 C 180.16666666666666 265.1666666666667 297.8333333333333 415.33333333333337 273.625 373 C 249.41666666666669 330.66666666666663 209.33333333333334 266.8333333333333 201 246 z"
id="svg_1"
fill-opacity="1"
stroke-opacity="1"
></path>
</g>
</svg>
<body>
<svg
width="640"
height="480"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<g class="layer">
<title>Layer 1</title>
<path
d="m75,75l75,25l-25,100l-50,-125z"
fill="#FF0000"
id="svg_1"
stroke="#000000"
stroke-width="5"
></path>
</g>
</svg>
</body>
`;
exports[`use all parts of svg-edit > check tool_path_openclose #0`] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
width="640"
height="480"
id="svgcontent"
overflow="visible"
x="640"
y="480"
viewBox="0 0 640 480"
>
<g class="layer" style="pointer-events:all">
<title style="pointer-events:inherit">Layer 1</title>
<path
fill="#FF0000"
stroke="#000000"
stroke-width="5"
d="M 201 246 L 150 100 L 125 200 C 180.16666666666666 265.1666666666667 297.8333333333333 415.33333333333337 273.625 373 C 249.41666666666669 330.66666666666663 209.33333333333334 266.8333333333333 201 246 z"
id="svg_1"
fill-opacity="1"
stroke-opacity="1"
></path>
</g>
</svg>
<body>
<svg
width="640"
height="480"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<g class="layer">
<title>Layer 1</title>
<path
d="m201,246l-51,-146l-25,100c55.16667,65.16667 172.83333,215.33333 148.625,173c-24.20833,-42.33333 -64.29167,-106.16667 -72.625,-127z"
fill="#FF0000"
id="svg_1"
stroke="#000000"
stroke-width="5"
></path>
</g>
</svg>
</body>
`;
exports[`use all parts of svg-edit > check tool_path_change_seg_type #0`] = `
<body>
<svg
width="640"
height="480"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<g class="layer">
<title>Layer 1</title>
<path
d="m75,75l75,25l-25,100c110.33333,130.33333 -33.33333,-83.33333 -50,-125z"
fill="#FF0000"
id="svg_1"
stroke="#000000"
stroke-width="5"
></path>
</g>
</svg>
</body>
`;
exports[`use all parts of svg-edit > check tool_path_change_clone_node #0`] = `
<body>
<svg
width="640"
height="480"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<g class="layer">
<title>Layer 1</title>
<path
d="m201,246l-51,-146l-25,100c55.16667,65.16667 172.83333,215.33333 148.625,173c-24.20833,-42.33333 -64.29167,-106.16667 -72.625,-127z"
fill="#FF0000"
id="svg_1"
stroke="#000000"
stroke-width="5"
></path>
</g>
</svg>
</body>
`;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,14 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
} from '../../support/ui-test-helper.js'
describe('UI - Clipboard', function () {
beforeEach(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('Editor - Copy and paste', () => {
cy.get('#tool_source').click();
cy.get('#tool_source').click()
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
@ -17,47 +17,47 @@ describe('UI - Clipboard', function () {
<title>Layer 1</title>
<circle cx="100" cy="100" r="50" fill="#FF0000" id="testCircle" stroke="#000000" stroke-width="5"/>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click();
cy.get('#testCircle').should('exist');
cy.get('#svg_1').should('not.exist');
cy.get('#svg_2').should('not.exist');
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click()
cy.get('#testCircle').should('exist')
cy.get('#svg_1').should('not.exist')
cy.get('#svg_2').should('not.exist')
// Copy.
cy.get('#testCircle').click().rightclick();
cy.get('#cmenu_canvas a[href="#copy"]').click({ force: true });
cy.get('#testCircle').click().rightclick()
cy.get('#cmenu_canvas a[href="#copy"]').click({ force: true })
// Paste.
// Scrollbars fail to recenter in Cypress test. Works fine in reality.
// Thus forcing click is needed since workspace is mostly offscreen.
cy.get('#svgroot').rightclick({ force: true });
cy.get('#cmenu_canvas a[href="#paste"]').click({ force: true });
cy.get('#testCircle').should('exist');
cy.get('#svg_1').should('exist');
cy.get('#svg_2').should('not.exist');
cy.get('#svgroot').rightclick({ force: true })
cy.get('#cmenu_canvas a[href="#paste"]').click({ force: true })
cy.get('#testCircle').should('exist')
cy.get('#svg_1').should('exist')
cy.get('#svg_2').should('not.exist')
// Cut.
cy.get('#testCircle').click().rightclick();
cy.get('#cmenu_canvas a[href="#cut"]').click({ force: true });
cy.get('#testCircle').should('not.exist');
cy.get('#svg_1').should('exist');
cy.get('#svg_2').should('not.exist');
cy.get('#testCircle').click().rightclick()
cy.get('#cmenu_canvas a[href="#cut"]').click({ force: true })
cy.get('#testCircle').should('not.exist')
cy.get('#svg_1').should('exist')
cy.get('#svg_2').should('not.exist')
// Paste.
// Scrollbars fail to recenter in Cypress test. Works fine in reality.
// Thus forcing click is needed since workspace is mostly offscreen.
cy.get('#svgroot').rightclick({ force: true });
cy.get('#cmenu_canvas a[href="#paste"]').click({ force: true });
cy.get('#testCircle').should('not.exist');
cy.get('#svg_1').should('exist');
cy.get('#svg_2').should('exist');
cy.get('#svgroot').rightclick({ force: true })
cy.get('#cmenu_canvas a[href="#paste"]').click({ force: true })
cy.get('#testCircle').should('not.exist')
cy.get('#svg_1').should('exist')
cy.get('#svg_2').should('exist')
// Delete.
cy.get('#svg_2').click().rightclick();
cy.get('#cmenu_canvas a[href="#delete"]').click({ force: true });
cy.get('#svg_1').click().rightclick();
cy.get('#cmenu_canvas a[href="#delete"]').click({ force: true });
cy.get('#svg_1').should('not.exist');
cy.get('#svg_2').should('not.exist');
});
});
cy.get('#svg_2').click().rightclick()
cy.get('#cmenu_canvas a[href="#delete"]').click({ force: true })
cy.get('#svg_1').click().rightclick()
cy.get('#cmenu_canvas a[href="#delete"]').click({ force: true })
cy.get('#svg_1').should('not.exist')
cy.get('#svg_2').should('not.exist')
})
})

View File

@ -1,15 +1,15 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
} from '../../support/ui-test-helper.js'
describe('UI - Control Points', function () {
beforeEach(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('Editor - No parameters: Drag control point of arc path', () => {
const randomOffset = () => 2 + Math.round(10 + Math.random() * 40);
cy.get('#tool_source').click();
const randomOffset = () => 2 + Math.round(10 + Math.random() * 40)
cy.get('#tool_source').click()
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
@ -17,18 +17,18 @@ describe('UI - Control Points', function () {
<title>Layer 1</title>
<path d="m187,194a114,62 0 1 0 219,2" id="svg_1" fill="#FF0000" stroke="#000000" stroke-width="5"/>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
cy.get('#svg_1').click({ force: true }).click({ force: true });
cy.get('#svg_1').click({ force: true }).click({ force: true })
cy.get('#pathpointgrip_0').trigger('mousedown', { which: 1, force: true })
.trigger('mousemove', randomOffset(), randomOffset(), { force: true })
.trigger('mouseup', { force: true });
.trigger('mouseup', { force: true })
cy.get('#pathpointgrip_1').trigger('mousedown', { which: 1, force: true })
.trigger('mousemove', randomOffset(), randomOffset(), { force: true })
.trigger('mouseup', { force: true });
.trigger('mouseup', { force: true })
cy.get('#svg_1[d]').should('not.contain', 'NaN');
});
});
cy.get('#svg_1[d]').should('not.contain', 'NaN')
})
})

View File

@ -1,20 +1,20 @@
import {
visitAndApproveStorage, openMainMenu
} from '../../support/ui-test-helper.js';
} from '../../support/ui-test-helper.js'
describe('UI - Export tests', function () {
beforeEach(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('Editor - No parameters: Has export button', () => {
openMainMenu();
cy.get('#tool_export');
});
openMainMenu()
cy.get('#tool_export')
})
it('Editor - No parameters: Export button clicking; dialog opens', () => {
openMainMenu();
cy.get('#tool_export').click({ force: true });
cy.get('#dialog_content select');
});
});
openMainMenu()
cy.get('#tool_export').click({ force: true })
cy.get('#dialog_content select')
})
})

View File

@ -1,15 +1,15 @@
import {
visitAndApproveStorage
} from '../../../support/ui-test-helper.js';
} from '../../../support/ui-test-helper.js'
// See https://github.com/SVG-Edit/svgedit/issues/359
describe('Fix issue 359', function () {
beforeEach(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('can undo without throwing', function () {
cy.get('#tool_source').click();
cy.get('#tool_source').click()
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
@ -17,10 +17,10 @@ describe('Fix issue 359', function () {
<title>Layer 1</title>
<rect fill="#ffff00" height="70" width="165" x="179.5" y="146.5"/>
</g>
</svg>`, { parseSpecialCharSequences: false, force: true });
cy.get('#tool_source_save').click();
cy.get('#tool_undo').click();
cy.get('#tool_redo').click(); // test also redo to make the test more comprehensive
</svg>`, { parseSpecialCharSequences: false, force: true })
cy.get('#tool_source_save').click()
cy.get('#tool_undo').click()
cy.get('#tool_redo').click() // test also redo to make the test more comprehensive
// if the undo throws an error to the console, the test will fail
});
});
})
})

View File

@ -1,15 +1,15 @@
import {
visitAndApproveStorage
} from '../../../support/ui-test-helper.js';
} from '../../../support/ui-test-helper.js'
// See https://github.com/SVG-Edit/svgedit/issues/407
describe('Fix issue 407', function () {
beforeEach(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('can enter edit on text child', function () {
cy.get('#tool_source').click();
cy.get('#tool_source').click()
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
@ -20,16 +20,16 @@ describe('Fix issue 407', function () {
<text fill="#000000" id="a_text" text-anchor="middle" x="260.5" xml:space="preserve" y="192.5">hello</text>
</g>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click();
cy.get('#svg_1').click().dblclick();
cy.get('#a_text').should('exist');
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click()
cy.get('#svg_1').click().dblclick()
cy.get('#a_text').should('exist')
cy.get('#a_text')
.trigger('mousedown', { which: 1, force: true })
.trigger('mouseup', { force: true })
.dblclick({ force: true });
.dblclick({ force: true })
// svgedit use the #text text field to capture the text
cy.get('#text').type('1234', { force: true });
cy.get('#a_text').should('have.text', 'he1234llo');
});
});
cy.get('#text').type('1234', { force: true })
cy.get('#a_text').should('have.text', 'he1234llo')
})
})

View File

@ -1,15 +1,15 @@
import {
visitAndApproveStorage
} from '../../../support/ui-test-helper.js';
} from '../../../support/ui-test-helper.js'
// See https://github.com/SVG-Edit/svgedit/issues/408
describe('Fix issue 408', function () {
beforeEach(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('should not throw when showing/saving svg content', function () {
cy.get('#tool_source').click();
cy.get('#tool_source').click()
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
@ -20,10 +20,10 @@ describe('Fix issue 408', function () {
<circle cx="117.5" cy="87.5" fill="#ffff00" r="19.84943" stroke="#000000" />
</g>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click();
cy.get('#svg_6').click().dblclick(); // change context
cy.get('#tool_source').click(); // reopen tool_source
cy.get('#tool_source_save').should('exist'); // The save button should be here if it does not throw
});
});
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click()
cy.get('#svg_6').click().dblclick() // change context
cy.get('#tool_source').click() // reopen tool_source
cy.get('#tool_source_save').should('exist') // The save button should be here if it does not throw
})
})

View File

@ -1,15 +1,15 @@
import {
visitAndApproveStorage
} from '../../../support/ui-test-helper.js';
} from '../../../support/ui-test-helper.js'
// See https://github.com/SVG-Edit/svgedit/issues/423
describe('Fix issue 423', function () {
beforeEach(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('should not throw when undoing the move', function () {
cy.get('#tool_source').click();
cy.get('#tool_source').click()
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="300" height="300" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
@ -22,12 +22,12 @@ describe('Fix issue 423', function () {
<rect clip-path="url(#svg_2)" fill="#0033b5" height="174.9" id="TANK1" width="78" x="77.5" y="29"/>
</g>
</g>
</svg>`, { parseSpecialCharSequences: false, force: true });
cy.get('#tool_source_save').click({ force: true });
</svg>`, { parseSpecialCharSequences: false, force: true })
cy.get('#tool_source_save').click({ force: true })
cy.get('#TANK1')
.trigger('mousedown', { force: true })
.trigger('mousemove', 50, 0, { force: true })
.trigger('mouseup', { force: true });
cy.get('#tool_undo').click({ force: true });
});
});
.trigger('mouseup', { force: true })
cy.get('#tool_undo').click({ force: true })
})
})

View File

@ -1,16 +1,16 @@
import {
visitAndApproveStorage
} from '../../../support/ui-test-helper.js';
} from '../../../support/ui-test-helper.js'
// See https://github.com/SVG-Edit/svgedit/issues/660
describe('Fix issue 660', function () {
beforeEach(() => {
visitAndApproveStorage();
cy.viewport(512, 512);
});
visitAndApproveStorage()
cy.viewport(512, 512)
})
/** @todo: reenable this test when we understand why it is passing locally but not on ci */
it.skip('can resize text', function () {
cy.get('#tool_source').click();
cy.get('#tool_source').click()
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
@ -18,18 +18,18 @@ describe('Fix issue 660', function () {
<title>Layer 1</title>
<text fill="#000000" id="a_text" text-anchor="middle" x="260.5" xml:space="preserve" y="192.5" font-size="40">hello</text>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
cy.get('#a_text').should('exist');
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
cy.get('#a_text').should('exist')
cy.get('#a_text')
.trigger('mousedown', { which: 1, force: true })
.trigger('mouseup', { force: true });
.trigger('mouseup', { force: true })
cy.get('#selectorGrip_resize_s')
.trigger('mousedown', { which: 1, force: true })
.trigger('mousemove', { clientX: 0, clientY: 600 })
.trigger('mouseup', { force: true });
.trigger('mouseup', { force: true })
// svgedit use the #text text field to capture the text
cy.get('#a_text').should('have.attr', 'transform')
.and('equal', 'matrix(1 0 0 4.54639 0 -540.825)'); // Chrome 96 is matrix(1 0 0 4.17431 0 -325.367)
});
});
.and('equal', 'matrix(1 0 0 4.54639 0 -540.825)') // Chrome 96 is matrix(1 0 0 4.17431 0 -325.367)
})
})

View File

@ -1,14 +1,14 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
} from '../../support/ui-test-helper.js'
// See https://github.com/SVG-Edit/svgedit/issues/364
describe('Key commands', function () {
beforeEach(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it.skip('cmd-A on empty canvas should not cause an error', function () {
cy.get('body').type('{cmd}a');
});
});
it('cmd-A on empty canvas should not cause an error', function () {
cy.get('body').type('{cmd}a')
})
})

View File

@ -1,188 +1,184 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
const testSnapshot = () => {
cy.get('#svgcontent').cleanSnapshot();
};
visitAndApproveStorage, testSnapshot
} from '../../support/ui-test-helper.js'
describe('use various parts of svg-edit', function () {
before(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('check tool_source', function () {
cy.get('#tool_source').click({ force: true });
cy.get('#tool_source').click({ force: true })
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
testSnapshot();
});
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
testSnapshot()
})
it('check tool_fhpath', function () {
cy.get('#tool_fhpath')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousemove', 200, 200, { force: true })
.trigger('mousedown', 200, 200, { force: true })
.trigger('mousemove', 20, 20, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_text', function () {
cy.get('#tool_text')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 46, 35, { force: true })
.trigger('mouseup', { force: true });
.trigger('mousedown', 46, 35, { force: true })
.trigger('mouseup', { force: true })
// svgedit use the #text text field to capture the text
cy.get('#text').type('AB', { force: true });
testSnapshot();
});
cy.get('#text').type('AB', { force: true })
testSnapshot()
})
it('check tool_clone', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#tool_clone')
.click({ force: true });
testSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_italic', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#tool_italic')
.click({ force: true });
testSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_bold', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#tool_bold')
.click({ force: true });
testSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_text_change_x_y_coordinate', function () {
cy.get('#svg_2').click({ force: true });
for(let n = 0; n < 25; n ++){
cy.get('#svg_2').click({ force: true })
for (let n = 0; n < 25; n++) {
cy.get('#selected_x').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
for(let n = 0; n < 25; n ++){
for (let n = 0; n < 25; n++) {
cy.get('#selected_y').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_text_change_font_size', function () {
cy.get('#svg_1').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_1').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#font_size').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_text_change_stroke_width', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#stroke_width').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_text_change_stoke_fill_color', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#stroke_color').shadow().find('#picker').eq(0).click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#stroke_color').shadow().find('#picker').eq(0).click({ force: true })
cy.get('#stroke_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('.QuickColor').eq(51).click({ force: true });
.find('.QuickColor').eq(51).click({ force: true })
cy.get('#stroke_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('#Ok').eq(0).click({ force: true });
cy.get('#fill_color').shadow().find('#picker').eq(0).click({ force: true });
.find('#Ok').eq(0).click({ force: true })
cy.get('#fill_color').shadow().find('#picker').eq(0).click({ force: true })
cy.get('#fill_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('.QuickColor').eq(3).click({ force: true });
.find('.QuickColor').eq(3).click({ force: true })
cy.get('#fill_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('#Ok').eq(0).click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.find('#Ok').eq(0).click({ force: true })
testSnapshot()
})
it('check tool_text_anchor_start', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#tool_text_anchor_start')
.click({ force: true });
testSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_text_anchor_middle', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#tool_text_anchor_middle')
.click({ force: true });
testSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_text_anchor_end', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#tool_text_anchor_end')
.click({ force: true });
testSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_text_change_rotation', function () {
cy.get('#svg_2').click({ force: true });
for(let n = 0; n < 5; n ++){
cy.get('#svg_2').click({ force: true })
for (let n = 0; n < 5; n++) {
cy.get('#angle').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_text_change_blur', function () {
cy.get('#svg_2').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_2').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#blur').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_text_change_opacity', function () {
cy.get('#svg_2').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_2').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#opacity').shadow().find('elix-number-spin-box').eq(0).shadow().find('#downButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_text_align_to_page', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#tool_position').shadow().find('elix-dropdown-list').eq(0).invoke('attr', 'opened', 'opened');
cy.get('#svg_2').click({ force: true })
cy.get('#tool_position').shadow().find('elix-dropdown-list').eq(0).invoke('attr', 'opened', 'opened')
cy.get('#tool_position').find('se-list-item').eq(2).shadow().find('elix-option').eq(0)
.click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_text_change_class', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#svg_2').click({ force: true })
cy.get('#elem_class').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('svg_2_class{enter}', { force: true });
.type('svg_2_class{enter}', { force: true })
cy.get('#svg_2')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_2_class');
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_2_class')
})
})
it('check tool_text_change_id', function () {
cy.get('#svg_2').click({ force: true }).click({ force: true });
cy.get('#svg_2').click({ force: true }).click({ force: true })
cy.get('#elem_id').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('_id{enter}', { force: true });
.type('_id{enter}', { force: true })
cy.get('#svg_2_id')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_2_class');
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_2_class')
})
})
it('check tool_text_delete', function () {
cy.get('#svg_2_id').click({ force: true });
cy.get('#tool_delete').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2_id').click({ force: true })
cy.get('#tool_delete').click({ force: true })
testSnapshot()
})
it('check tool_text_change_font_family', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#tool_font_family').shadow().find('select').select("Serif");
cy.get('#svgcontent').toMatchSnapshot();
});
});
cy.get('#svg_1').click({ force: true })
cy.get('#tool_font_family').shadow().find('select').select('Serif')
testSnapshot()
})
})

View File

@ -1,55 +1,51 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
const testSnapshot = () => {
cy.get('#svgcontent').cleanSnapshot();
};
visitAndApproveStorage, testSnapshot
} from '../../support/ui-test-helper.js'
describe('use all parts of svg-edit', function () {
before(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('check tool_source_set', function () {
cy.get('#tool_source').click({ force: true });
cy.get('#tool_source').click({ force: true })
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
testSnapshot();
});
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
testSnapshot()
})
it('check tool_shape', function () {
cy.get('#tool_shapelib').shadow().find('.overall').eq(0).click({ force: true });
cy.get('[data-shape="heart"]').click({ force: true });
cy.get('#tool_shapelib').shadow().find('.overall').eq(0).click({ force: true })
cy.get('[data-shape="heart"]').click({ force: true })
cy.get('#svgcontent')
.trigger('mousemove', 200, 200, { force: true })
.trigger('mousedown', 200, 200, { force: true })
.trigger('mousemove', 20, 20, { force: true })
.trigger('mouseup', { force: true });
.trigger('mouseup', { force: true })
cy.get('#selectorGrip_rotate')
.trigger('mousedown')
.trigger('mousemove', 20, 20, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_image', function () {
cy.get('#tool_image').click({ force: true });
cy.get('#tool_image').click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 100, 100, { force: true })
.trigger('mousemove', 120, 120, { force: true })
.trigger('mouseup', { force: true });
.trigger('mouseup', { force: true })
// eslint-disable-next-line promise/catch-or-return
cy.window()
// eslint-disable-next-line promise/always-return
.then(($win) => {
cy.stub($win, 'prompt').returns('./images/logo.svg');
cy.contains('OK');
});
cy.get('#svgcontent').toMatchSnapshot();
});
});
cy.stub($win, 'prompt').returns('./images/logo.svg')
cy.contains('OK')
})
testSnapshot()
})
})

View File

@ -1,124 +1,120 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
const testSnapshot = () => {
cy.get('#svgcontent').cleanSnapshot();
};
visitAndApproveStorage, testSnapshot
} from '../../support/ui-test-helper.js'
describe('use all parts of svg-edit', function () {
before(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('check tool_source_set', function () {
cy.get('#tool_source').click({ force: true });
cy.get('#tool_source').click({ force: true })
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
testSnapshot();
});
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
testSnapshot()
})
it('check tool_circle', function () {
cy.get('#tool_circle')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 150, 150, { force: true })
.trigger('mousemove', 250, 200, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_fhellipse', function () {
cy.get('#tool_fhellipse')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 200, 80, { force: true })
.trigger('mousemove', 320, 80, { force: true })
.trigger('mousemove', 320, 180, { force: true })
.trigger('mousemove', 200, 180, { force: true })
.trigger('mousemove', 200, 80, { force: true })
.trigger('mouseup', 200, 80, { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', 200, 80, { force: true })
testSnapshot()
})
it('check tool_ellipse', function () {
cy.get('#tool_ellipse').click({ force: true });
cy.get('#tool_ellipse').click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 75, 150, { force: true })
.trigger('mousemove', 130, 175, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_circle_change_fill_color', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#svg_2').click({ force: true })
cy.get('#js-se-palette').find('.square').eq(8)
.click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_circle_change_opacity', function () {
cy.get('#svg_2').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_2').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#opacity').shadow().find('elix-number-spin-box').eq(0).shadow().find('#downButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_ellipse_change_rotation', function () {
cy.get('#svg_3').click({ force: true });
for(let n = 0; n < 5; n ++){
cy.get('#svg_3').click({ force: true })
for (let n = 0; n < 5; n++) {
cy.get('#angle').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_ellipse_change_blur', function () {
cy.get('#svg_3').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_3').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#blur').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_ellipse_change_cx_cy_coordinate', function () {
cy.get('#svg_3').click({ force: true });
for(let n = 0; n < 25; n ++){
cy.get('#svg_3').click({ force: true })
for (let n = 0; n < 25; n++) {
cy.get('#ellipse_cx').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
for(let n = 0; n < 25; n ++){
for (let n = 0; n < 25; n++) {
cy.get('#ellipse_cy').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_ellipse_change_rx_ry_radius', function () {
cy.get('#svg_3').click({ force: true });
for(let n = 0; n < 25; n ++){
cy.get('#svg_3').click({ force: true })
for (let n = 0; n < 25; n++) {
cy.get('#ellipse_rx').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
for(let n = 0; n < 25; n ++){
for (let n = 0; n < 25; n++) {
cy.get('#ellipse_ry').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_ellipse_bring_to_back', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#tool_move_bottom').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2').click({ force: true })
cy.get('#tool_move_bottom').click({ force: true })
testSnapshot()
})
it('check tool_ellipse_bring_to_front', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#tool_move_top').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2').click({ force: true })
cy.get('#tool_move_top').click({ force: true })
testSnapshot()
})
it('check tool_ellipse_clone', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#tool_clone').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
});
cy.get('#svg_2').click({ force: true })
cy.get('#tool_clone').click({ force: true })
testSnapshot()
})
})

View File

@ -1,31 +1,27 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
const testSnapshot = () => {
cy.get('#svgcontent').cleanSnapshot();
};
visitAndApproveStorage, testSnapshot
} from '../../support/ui-test-helper.js'
describe('use all parts of svg-edit', function () {
before(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('check tool_source_set', function () {
cy.get('#tool_source').click({ force: true });
cy.get('#tool_source').click({ force: true })
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
testSnapshot();
});
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
testSnapshot()
})
it('check tool_path', function () {
cy.get('#tool_path')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 50, 50, { force: true })
.trigger('mouseup', { force: true })
@ -37,49 +33,49 @@ describe('use all parts of svg-edit', function () {
.trigger('mouseup', { force: true })
.trigger('mousemove', 0, 0, { force: true })
.trigger('mousedown', 0, 0, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_path_change_node_xy', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').dblclick({ force: true });
for(let n = 0; n < 25; n ++){
cy.get('#svg_1').click({ force: true })
cy.get('#svg_1').dblclick({ force: true })
for (let n = 0; n < 25; n++) {
cy.get('#path_node_x').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
for(let n = 0; n < 25; n ++){
for (let n = 0; n < 25; n++) {
cy.get('#path_node_y').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_path_change_seg_type', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').dblclick({ force: true });
cy.get('#seg_type').shadow().find('select').select('6').should('have.value', '6');
// cy.get('#svg_1').click({ force: true })
cy.get('#svg_1').dblclick({ force: true })
cy.get('#seg_type').shadow().find('select').select('6', { force: true }).should('have.value', '6')
cy.get('#ctrlpointgrip_3c1')
.trigger('mousedown', { force: true })
.trigger('mousemove', 130, 175, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_path_change_clone_node', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').dblclick({ force: true });
cy.get('#tool_node_clone').click({ force: true });
// cy.get('#svg_1').click({ force: true })
cy.get('#svg_1').dblclick({ force: true })
cy.get('#tool_node_clone').click({ force: true })
cy.get('#pathpointgrip_4')
.trigger('mousedown', { force: true })
.trigger('mousemove', 130, 175, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_path_openclose', function () {
cy.get('#tool_select').click({ force: true });
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').dblclick({ force: true });
cy.get('#tool_openclose_path').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#tool_select').click({ force: true })
cy.get('#svg_1').click({ force: true })
cy.get('#svg_1').dblclick({ force: true })
cy.get('#tool_openclose_path').click({ force: true })
testSnapshot()
})
/* it('check tool_path_add_subpath', function () {
cy.get('#tool_add_subpath').click({ force: true });
cy.get('#svgcontent')
@ -95,6 +91,6 @@ describe('use all parts of svg-edit', function () {
.trigger('mousedown', 0, 0, { force: true })
.trigger('mouseup', { force: true });
cy.get('#tool_select').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
testSnapshot();
}); */
});
})

View File

@ -1,164 +1,160 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
const testSnapshot = () => {
cy.get('#svgcontent').cleanSnapshot();
};
visitAndApproveStorage, testSnapshot
} from '../../support/ui-test-helper.js'
describe('use all parts of svg-edit', function () {
before(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('check tool_source_set', function () {
cy.get('#tool_source').click({ force: true });
cy.get('#tool_source').click({ force: true })
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
testSnapshot();
});
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
testSnapshot()
})
it('check tool_rect', function () {
cy.get('#tool_rect')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 150, 150, { force: true })
.trigger('mousemove', 250, 200, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_fhrect', function () {
cy.get('#tool_fhrect')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 200, 80, { force: true })
.trigger('mousemove', 320, 80, { force: true })
.trigger('mousemove', 320, 180, { force: true })
.trigger('mousemove', 200, 180, { force: true })
.trigger('mousemove', 200, 80, { force: true })
.trigger('mouseup', 200, 80, { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', 200, 80, { force: true })
testSnapshot()
})
it('check tool_square', function () {
cy.get('#tool_square').click({ force: true });
cy.get('#tool_square').click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 75, 150, { force: true })
.trigger('mousemove', 125, 200, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_rect_change_fill_color', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#js-se-palette').find('.square').eq(8)
.click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_rect_change_rotation', function () {
cy.get('#svg_1').click({ force: true });
for(let n = 0; n < 5; n ++){
cy.get('#svg_1').click({ force: true })
for (let n = 0; n < 5; n++) {
cy.get('#angle').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_rect_change_blur', function () {
cy.get('#svg_1').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_1').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#blur').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_rect_change_opacity', function () {
cy.get('#svg_1').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_1').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#opacity').shadow().find('elix-number-spin-box').eq(0).shadow().find('#downButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_fhrect_change_x_y_coordinate', function () {
cy.get('#svg_2').click({ force: true });
for(let n = 0; n < 25; n ++){
cy.get('#svg_2').click({ force: true })
for (let n = 0; n < 25; n++) {
cy.get('#selected_x').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
for(let n = 0; n < 25; n ++){
for (let n = 0; n < 25; n++) {
cy.get('#selected_y').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_fhrect_change_width_height', function () {
cy.get('#svg_2').click({ force: true });
for(let n = 0; n < 25; n ++){
cy.get('#svg_2').click({ force: true })
for (let n = 0; n < 25; n++) {
cy.get('#rect_width').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
for(let n = 0; n < 25; n ++){
for (let n = 0; n < 25; n++) {
cy.get('#rect_height').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_square_clone', function () {
cy.get('#svg_3').click({ force: true });
cy.get('#tool_clone').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_3').click({ force: true })
cy.get('#tool_clone').click({ force: true })
testSnapshot()
})
it('check tool_square_bring_to_back', function () {
cy.get('#svg_3').click({ force: true });
cy.get('#tool_move_bottom').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_3').click({ force: true })
cy.get('#tool_move_bottom').click({ force: true })
testSnapshot()
})
it('check tool_square_bring_to_front', function () {
cy.get('#svg_3').click({ force: true });
cy.get('#tool_move_top').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_3').click({ force: true })
cy.get('#tool_move_top').click({ force: true })
testSnapshot()
})
it('check tool_square_change_corner_radius', function () {
cy.get('#svg_4').click({ force: true });
for(let n = 0; n < 25; n ++){
cy.get('#svg_4').click({ force: true })
for (let n = 0; n < 25; n++) {
cy.get('#rect_rx').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_rect_change_to_path', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#tool_topath').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2').click({ force: true })
cy.get('#tool_topath').click({ force: true })
testSnapshot()
})
it('check tool_rect_delete', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#tool_delete').click({ force: true });
cy.get('#svg_3').click({ force: true });
cy.get('#tool_delete').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_1').click({ force: true })
cy.get('#tool_delete').click({ force: true })
cy.get('#svg_3').click({ force: true })
cy.get('#tool_delete').click({ force: true })
testSnapshot()
})
it('check tool_rect_change_class', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#svg_2').click({ force: true })
cy.get('#elem_class').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('svg_2_class{enter}', { force: true });
.type('svg_2_class{enter}', { force: true })
cy.get('#svg_2')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_2_class');
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_2_class')
})
})
it('check tool_rect_change_id', function () {
cy.get('#svg_2').click({ force: true }).click({ force: true });
cy.get('#svg_2').click({ force: true }).click({ force: true })
cy.get('#elem_id').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('_id{enter}', { force: true });
.type('_id{enter}', { force: true })
cy.get('#svg_2_id')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_2_class');
});
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_2_class')
})
})
})

View File

@ -1,153 +1,149 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
const testSnapshot = () => {
cy.get('#svgcontent').cleanSnapshot();
};
visitAndApproveStorage, testSnapshot
} from '../../support/ui-test-helper.js'
describe('use all parts of svg-edit', function () {
before(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('check tool_source_set', function () {
cy.get('#tool_source').click({ force: true });
cy.get('#tool_source').click({ force: true })
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
testSnapshot();
});
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
testSnapshot()
})
it('check tool_line', function () {
cy.get('#tool_line')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousemove', 200, 200, { force: true })
.trigger('mousedown', 200, 200, { force: true })
.trigger('mousemove', 250, 250, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_line_change_class', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#elem_class').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('svg_1_class{enter}', { force: true });
.type('svg_1_class{enter}', { force: true })
cy.get('#svg_1')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_1_class');
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_1_class')
})
})
it('check tool_line_change_id', function () {
cy.get('#svg_1').click({ force: true }).click({ force: true });
cy.get('#svg_1').click({ force: true }).click({ force: true })
cy.get('#elem_id').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('_id{enter}', { force: true });
.type('_id{enter}', { force: true })
cy.get('#svg_1_id')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_1_class');
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_1_class')
})
})
it('check tool_line_change_rotation', function () {
cy.get('#svg_1_id').click({ force: true });
for(let n = 0; n < 5; n ++){
cy.get('#svg_1_id').click({ force: true })
for (let n = 0; n < 5; n++) {
cy.get('#angle').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_line_change_blur', function () {
cy.get('#svg_1_id').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_1_id').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#blur').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_line_change_opacity', function () {
cy.get('#svg_1_id').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_1_id').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#opacity').shadow().find('elix-number-spin-box').eq(0).shadow().find('#downButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_line_delete', function () {
cy.get('#svg_1_id').click({ force: true });
cy.get('#tool_delete').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_1_id').click({ force: true })
cy.get('#tool_delete').click({ force: true })
testSnapshot()
})
it('check tool_line_clone', function () {
cy.get('#tool_line')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousemove', 200, 200, { force: true })
.trigger('mousedown', 200, 200, { force: true })
.trigger('mousemove', 250, 250, { force: true })
.trigger('mouseup', { force: true });
cy.get('#svg_2').click({ force: true });
cy.get('#tool_clone').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.trigger('mouseup', { force: true })
cy.get('#svg_2').click({ force: true })
cy.get('#tool_clone').click({ force: true })
testSnapshot()
})
it('check tool_line_bring_to_back', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#tool_move_bottom').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2').click({ force: true })
cy.get('#tool_move_bottom').click({ force: true })
testSnapshot()
})
it('check tool_line_bring_to_front', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#tool_move_top').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2').click({ force: true })
cy.get('#tool_move_top').click({ force: true })
testSnapshot()
})
it('check tool_line_change_x_y_coordinate', function () {
cy.get('#svg_2').click({ force: true });
for(let n = 0; n < 25; n ++){
cy.get('#svg_2').click({ force: true })
for (let n = 0; n < 25; n++) {
cy.get('#line_x1').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
for(let n = 0; n < 25; n ++){
for (let n = 0; n < 25; n++) {
cy.get('#line_y1').shadow().find('elix-number-spin-box').eq(0).shadow().find('#downButton').eq(0)
.click({ force: true });
.click({ force: true })
}
for(let n = 0; n < 25; n ++){
for (let n = 0; n < 25; n++) {
cy.get('#line_x2').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
for(let n = 0; n < 25; n ++){
for (let n = 0; n < 25; n++) {
cy.get('#line_y2').shadow().find('elix-number-spin-box').eq(0).shadow().find('#downButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_line_change_stroke_width', function () {
cy.get('#svg_2').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_2').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#stroke_width').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_line_change_stoke_color', function () {
cy.get('#svg_3').click({ force: true });
cy.get('#stroke_color').shadow().find('#picker').eq(0).click({ force: true });
cy.get('#svg_3').click({ force: true })
cy.get('#stroke_color').shadow().find('#picker').eq(0).click({ force: true })
cy.get('#stroke_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('.QuickColor').eq(9).click({ force: true });
.find('.QuickColor').eq(9).click({ force: true })
cy.get('#stroke_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('#Ok').eq(0).click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.find('#Ok').eq(0).click({ force: true })
testSnapshot()
})
it('check tool_line_align_to_page', function () {
cy.get('#svg_3').click({ force: true });
cy.get('#tool_position').shadow().find('elix-dropdown-list').eq(0).invoke('attr', 'opened', 'opened');
cy.get('#svg_3').click({ force: true })
cy.get('#tool_position').shadow().find('elix-dropdown-list').eq(0).invoke('attr', 'opened', 'opened')
cy.get('#tool_position').find('se-list-item').eq(2).shadow().find('elix-option').eq(0)
.click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
});
.click({ force: true })
testSnapshot()
})
})

View File

@ -1,108 +1,104 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
const testSnapshot = () => {
cy.get('#svgcontent').cleanSnapshot();
};
visitAndApproveStorage, testSnapshot
} from '../../support/ui-test-helper.js'
describe('use all parts of svg-edit', function () {
before(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('check tool_source_set', function () {
cy.get('#tool_source').click({ force: true });
cy.get('#tool_source').click({ force: true })
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
testSnapshot();
});
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
testSnapshot()
})
it('check tool_polygon', function () {
cy.get('#tool_polygon')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 325, 250, { force: true })
.trigger('mousemove', 325, 345, { force: true })
.trigger('mouseup', { force: true });
testSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_polygon_clone', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#tool_clone').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_1').click({ force: true })
cy.get('#tool_clone').click({ force: true })
testSnapshot()
})
it('check tool_polygon_change_class', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#svg_2').click({ force: true })
cy.get('#elem_class').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('svg_2_class{enter}', { force: true });
.type('svg_2_class{enter}', { force: true })
cy.get('#svg_2')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_2_class');
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_2_class')
})
})
it('check tool_polygon_change_id', function () {
cy.get('#svg_2').click({ force: true }).click({ force: true });
cy.get('#svg_2').click({ force: true }).click({ force: true })
cy.get('#elem_id').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('_id{enter}', { force: true });
.type('_id{enter}', { force: true })
cy.get('#svg_2_id')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_2_class');
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_2_class')
})
})
it('check tool_polygon_change_rotation', function () {
cy.get('#svg_2_id').click({ force: true });
for(let n = 0; n < 5; n ++){
cy.get('#svg_2_id').click({ force: true })
for (let n = 0; n < 5; n++) {
cy.get('#angle').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_polygon_change_blur', function () {
cy.get('#svg_2_id').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_2_id').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#blur').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_polygon_change_opacity', function () {
cy.get('#svg_2_id').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_2_id').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#opacity').shadow().find('elix-number-spin-box').eq(0).shadow().find('#downButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_polygon_bring_to_back', function () {
cy.get('#svg_2_id').click({ force: true });
cy.get('#tool_move_bottom').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2_id').click({ force: true })
cy.get('#tool_move_bottom').click({ force: true })
testSnapshot()
})
it('check tool_polygon_bring_to_front', function () {
cy.get('#svg_2_id').click({ force: true });
cy.get('#tool_move_top').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2_id').click({ force: true })
cy.get('#tool_move_top').click({ force: true })
testSnapshot()
})
it('check tool_polygon_delete', function () {
cy.get('#svg_2_id').click({ force: true });
cy.get('#tool_delete').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2_id').click({ force: true })
cy.get('#tool_delete').click({ force: true })
testSnapshot()
})
it('check tool_polygon_align_to_page', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#tool_position').shadow().find('elix-dropdown-list').eq(0).invoke('attr', 'opened', 'opened');
cy.get('#svg_1').click({ force: true })
cy.get('#tool_position').shadow().find('elix-dropdown-list').eq(0).invoke('attr', 'opened', 'opened')
cy.get('#tool_position').find('se-list-item').eq(0).shadow().find('elix-option').eq(0)
.click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.click({ force: true })
testSnapshot()
})
/* it('check tool_polygon_change_x_y_coordinate', function () {
cy.get('#svg_1').click({ force: true });
for(let n = 0; n < 25; n ++){
@ -113,38 +109,38 @@ describe('use all parts of svg-edit', function () {
cy.get('#selected_y').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
}
cy.get('#svgcontent').toMatchSnapshot();
testSnapshot();
}); */
it('check tool_polygon_change_stroke_width', function () {
cy.get('#svg_1').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_1').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#stroke_width').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_polygon_change_stoke_fill_color', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#stroke_color').shadow().find('#picker').eq(0).click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#stroke_color').shadow().find('#picker').eq(0).click({ force: true })
cy.get('#stroke_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('.QuickColor').eq(51).click({ force: true });
.find('.QuickColor').eq(51).click({ force: true })
cy.get('#stroke_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('#Ok').eq(0).click({ force: true });
cy.get('#fill_color').shadow().find('#picker').eq(0).click({ force: true });
.find('#Ok').eq(0).click({ force: true })
cy.get('#fill_color').shadow().find('#picker').eq(0).click({ force: true })
cy.get('#fill_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('.QuickColor').eq(3).click({ force: true });
.find('.QuickColor').eq(3).click({ force: true })
cy.get('#fill_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('#Ok').eq(0).click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.find('#Ok').eq(0).click({ force: true })
testSnapshot()
})
it('check tool_polygon_change_sides', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#polySides').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
});
.click({ force: true })
testSnapshot()
})
})

View File

@ -1,138 +1,134 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
const testSnapshot = () => {
cy.get('#svgcontent').cleanSnapshot();
};
visitAndApproveStorage, testSnapshot
} from '../../support/ui-test-helper.js'
describe('use all parts of svg-edit', function () {
before(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('check tool_source_set', function () {
cy.get('#tool_source').click({ force: true });
cy.get('#tool_source').click({ force: true })
cy.get('#svg_source_textarea')
.type('{selectall}', { force: true })
.type(`<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
</g>
</svg>`, { force: true, parseSpecialCharSequences: false });
cy.get('#tool_source_save').click({ force: true });
testSnapshot();
});
</svg>`, { force: true, parseSpecialCharSequences: false })
cy.get('#tool_source_save').click({ force: true })
testSnapshot()
})
it('check tool_star', function () {
cy.get('#tool_star')
.click({ force: true });
.click({ force: true })
cy.get('#svgcontent')
.trigger('mousedown', 300, 150, { force: true })
.trigger('mousemove', 300, 250, { force: true })
.trigger('mouseup', { force: true });
testSnapshot();
});
.trigger('mouseup', { force: true })
testSnapshot()
})
it('check tool_star_clone', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#tool_clone').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_1').click({ force: true })
cy.get('#tool_clone').click({ force: true })
testSnapshot()
})
it('check tool_star_change_class', function () {
cy.get('#svg_2').click({ force: true });
cy.get('#svg_2').click({ force: true })
cy.get('#elem_class').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('svg_2_class{enter}', { force: true });
.type('svg_2_class{enter}', { force: true })
cy.get('#svg_2')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_2_class');
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_2_class')
})
})
it('check tool_star_change_id', function () {
cy.get('#svg_2').click({ force: true }).click({ force: true });
cy.get('#svg_2').click({ force: true }).click({ force: true })
cy.get('#elem_id').shadow().find('elix-input').eq(0).shadow().find('#inner').eq(0)
.type('_id{enter}', { force: true });
.type('_id{enter}', { force: true })
cy.get('#svg_2_id')
.should('satisfy', ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes('svg_2_class');
});
});
const classList = Array.from($el[0].classList)
return classList.includes('svg_2_class')
})
})
it('check tool_star_change_rotation', function () {
cy.get('#svg_2_id').click({ force: true });
for(let n = 0; n < 5; n ++){
cy.get('#svg_2_id').click({ force: true })
for (let n = 0; n < 5; n++) {
cy.get('#angle').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_star_change_blur', function () {
cy.get('#svg_2_id').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_2_id').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#blur').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_star_change_opacity', function () {
cy.get('#svg_2_id').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_2_id').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#opacity').shadow().find('elix-number-spin-box').eq(0).shadow().find('#downButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_star_bring_to_back', function () {
cy.get('#svg_2_id').click({ force: true });
cy.get('#tool_move_bottom').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2_id').click({ force: true })
cy.get('#tool_move_bottom').click({ force: true })
testSnapshot()
})
it('check tool_star_bring_to_front', function () {
cy.get('#svg_2_id').click({ force: true });
cy.get('#tool_move_top').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2_id').click({ force: true })
cy.get('#tool_move_top').click({ force: true })
testSnapshot()
})
it('check tool_star_delete', function () {
cy.get('#svg_2_id').click({ force: true });
cy.get('#tool_delete').click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
cy.get('#svg_2_id').click({ force: true })
cy.get('#tool_delete').click({ force: true })
testSnapshot()
})
it('check tool_star_align_to_page', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#tool_position').shadow().find('elix-dropdown-list').eq(0).invoke('attr', 'opened', 'opened');
cy.get('#svg_1').click({ force: true })
cy.get('#tool_position').shadow().find('elix-dropdown-list').eq(0).invoke('attr', 'opened', 'opened')
cy.get('#tool_position').find('se-list-item').eq(0).shadow().find('elix-option').eq(0)
.click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.click({ force: true })
testSnapshot()
})
it('check tool_star_change_stroke_width', function () {
cy.get('#svg_1').click({ force: true });
for(let n = 0; n < 10; n ++){
cy.get('#svg_1').click({ force: true })
for (let n = 0; n < 10; n++) {
cy.get('#stroke_width').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
.click({ force: true })
}
cy.get('#svgcontent').toMatchSnapshot();
});
testSnapshot()
})
it('check tool_star_change_stoke_fill_color', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#stroke_color').shadow().find('#picker').eq(0).click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#stroke_color').shadow().find('#picker').eq(0).click({ force: true })
cy.get('#stroke_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('.QuickColor').eq(51).click({ force: true });
.find('.QuickColor').eq(51).click({ force: true })
cy.get('#stroke_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('#Ok').eq(0).click({ force: true });
cy.get('#fill_color').shadow().find('#picker').eq(0).click({ force: true });
.find('#Ok').eq(0).click({ force: true })
cy.get('#fill_color').shadow().find('#picker').eq(0).click({ force: true })
cy.get('#fill_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('.QuickColor').eq(3).click({ force: true });
.find('.QuickColor').eq(3).click({ force: true })
cy.get('#fill_color').shadow().find('#color_picker').eq(0)
.find('#jGraduate_colPick').eq(0).find('#jPicker-table').eq(0)
.find('#Ok').eq(0).click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
.find('#Ok').eq(0).click({ force: true })
testSnapshot()
})
it('check tool_star_change_sides', function () {
cy.get('#svg_1').click({ force: true });
cy.get('#svg_1').click({ force: true })
cy.get('#starNumPoints').shadow().find('elix-number-spin-box').eq(0).shadow().find('#upButton').eq(0)
.click({ force: true });
cy.get('#svgcontent').toMatchSnapshot();
});
});
.click({ force: true })
testSnapshot()
})
})

View File

@ -1,17 +1,17 @@
import {
visitAndApproveStorage
} from '../../support/ui-test-helper.js';
} from '../../support/ui-test-helper.js'
describe('UI - Tool selection', function () {
beforeEach(() => {
visitAndApproveStorage();
});
visitAndApproveStorage()
})
it('should set rectangle selection by click', function () {
cy.get('#tools_rect')
.should('not.have.attr', 'pressed');
.should('not.have.attr', 'pressed')
cy.get('#tools_rect')
.trigger('click', { force: true })
.should('have.attr', 'pressed');
});
});
.should('have.attr', 'pressed')
})
})

View File

@ -2,10 +2,10 @@
describe('Browser bugs', function () {
it('removeItem and setAttribute test (Chromium 843901; now fixed)', function () {
// See https://bugs.chromium.org/p/chromium/issues/detail?id=843901
const elem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
elem.setAttribute('transform', 'matrix(1,0,0,1,0,0)');
elem.transform.baseVal.removeItem(0);
elem.removeAttribute('transform');
assert.equal(elem.hasAttribute('transform'), false);
});
});
const elem = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
elem.setAttribute('transform', 'matrix(1,0,0,1,0,0)')
elem.transform.baseVal.removeItem(0)
elem.removeAttribute('transform')
assert.equal(elem.hasAttribute('transform'), false)
})
})

View File

@ -1,4 +1,4 @@
import * as contextmenu from '../../../instrumented/editor/contextmenu.js';
import * as contextmenu from '../../../instrumented/editor/contextmenu.js'
describe('contextmenu', function () {
/**
@ -6,53 +6,53 @@ describe('contextmenu', function () {
* @returns {void}
*/
afterEach(() => {
contextmenu.resetCustomMenus();
});
contextmenu.resetCustomMenus()
})
it('Test svgedit.contextmenu package', function () {
assert.ok(contextmenu, 'contextmenu registered correctly');
assert.ok(contextmenu.add, 'add registered correctly');
assert.ok(contextmenu.hasCustomHandler, 'contextmenu hasCustomHandler registered correctly');
assert.ok(contextmenu.getCustomHandler, 'contextmenu getCustomHandler registered correctly');
});
assert.ok(contextmenu, 'contextmenu registered correctly')
assert.ok(contextmenu.add, 'add registered correctly')
assert.ok(contextmenu.hasCustomHandler, 'contextmenu hasCustomHandler registered correctly')
assert.ok(contextmenu.getCustomHandler, 'contextmenu getCustomHandler registered correctly')
})
it('Test svgedit.contextmenu does not add invalid menu item', function () {
assert.throws(
() => contextmenu.add({ id: 'justanid' }),
null, null,
'menu item with just an id is invalid'
);
)
assert.throws(
() => contextmenu.add({ id: 'idandlabel', label: 'anicelabel' }),
null, null,
'menu item with just an id and label is invalid'
);
)
assert.throws(
() => contextmenu.add({ id: 'idandlabel', label: 'anicelabel', action: 'notafunction' }),
null, null,
'menu item with action that is not a function is invalid'
);
});
)
})
it('Test svgedit.contextmenu adds valid menu item', function () {
const validItem = { id: 'valid', label: 'anicelabel', action () { /* empty fn */ } };
contextmenu.add(validItem);
const validItem = { id: 'valid', label: 'anicelabel', action () { /* empty fn */ } }
contextmenu.add(validItem)
assert.ok(contextmenu.hasCustomHandler('valid'), 'Valid menu item is added.');
assert.equal(contextmenu.getCustomHandler('valid'), validItem.action, 'Valid menu action is added.');
});
assert.ok(contextmenu.hasCustomHandler('valid'), 'Valid menu item is added.')
assert.equal(contextmenu.getCustomHandler('valid'), validItem.action, 'Valid menu action is added.')
})
it('Test svgedit.contextmenu rejects valid duplicate menu item id', function () {
const validItem1 = { id: 'valid', label: 'anicelabel', action () { /* empty fn */ } };
const validItem2 = { id: 'valid', label: 'anicelabel', action () { /* empty fn */ } };
contextmenu.add(validItem1);
const validItem1 = { id: 'valid', label: 'anicelabel', action () { /* empty fn */ } }
const validItem2 = { id: 'valid', label: 'anicelabel', action () { /* empty fn */ } }
contextmenu.add(validItem1)
assert.throws(
() => contextmenu.add(validItem2),
null, null,
'duplicate menu item is rejected.'
);
});
});
)
})
})

View File

@ -1,25 +1,25 @@
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as coords from '../../../instrumented/svgcanvas/coords.js';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
import * as utilities from '../../../instrumented/svgcanvas/utilities.js'
import * as coords from '../../../instrumented/svgcanvas/coords.js'
describe('coords', function () {
let elemId = 1;
let elemId = 1
const root = document.createElement('div');
root.id = 'root';
root.style.visibility = 'hidden';
document.body.append(root);
const root = document.createElement('div')
root.id = 'root'
root.style.visibility = 'hidden'
document.body.append(root)
/**
* Set up tests with mock data.
* @returns {void}
*/
beforeEach(function () {
const svgroot = document.createElementNS(NS.SVG, 'svg');
svgroot.id = 'svgroot';
root.append(svgroot);
this.svg = document.createElementNS(NS.SVG, 'svg');
svgroot.append(this.svg);
const svgroot = document.createElementNS(NS.SVG, 'svg')
svgroot.id = 'svgroot'
root.append(svgroot)
this.svg = document.createElementNS(NS.SVG, 'svg')
svgroot.append(this.svg)
// Mock out editor context.
utilities.init(
@ -27,25 +27,25 @@ describe('coords', function () {
* @implements {module:utilities.EditorContext}
*/
{
getSVGRoot: () => { return this.svg; },
getDOMDocument () { return null; },
getDOMContainer () { return null; }
getSvgRoot: () => { return this.svg },
getDOMDocument () { return null },
getDOMContainer () { return null }
}
);
)
coords.init(
/**
* @implements {module:coords.EditorContext}
*/
{
getGridSnapping () { return false; },
getGridSnapping () { return false },
getDrawing () {
return {
getNextId () { return String(elemId++); }
};
getNextId () { return String(elemId++) }
}
}
}
);
});
)
})
/**
* Tear down tests, removing elements.
@ -53,255 +53,255 @@ describe('coords', function () {
*/
afterEach(function () {
while (this.svg.hasChildNodes()) {
this.svg.firstChild.remove();
this.svg.firstChild.remove()
}
});
})
it('Test remapElement(translate) for rect', function () {
const rect = document.createElementNS(NS.SVG, 'rect');
rect.setAttribute('x', '200');
rect.setAttribute('y', '150');
rect.setAttribute('width', '250');
rect.setAttribute('height', '120');
this.svg.append(rect);
const rect = document.createElementNS(NS.SVG, 'rect')
rect.setAttribute('x', '200')
rect.setAttribute('y', '150')
rect.setAttribute('width', '250')
rect.setAttribute('height', '120')
this.svg.append(rect)
const attrs = {
x: '200',
y: '150',
width: '125',
height: '75'
};
}
// Create a translate.
const m = this.svg.createSVGMatrix();
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = 100; m.f = -50;
const m = this.svg.createSVGMatrix()
m.a = 1; m.b = 0
m.c = 0; m.d = 1
m.e = 100; m.f = -50
coords.remapElement(rect, attrs, m);
coords.remapElement(rect, attrs, m)
assert.equal(rect.getAttribute('x'), '300');
assert.equal(rect.getAttribute('y'), '100');
assert.equal(rect.getAttribute('width'), '125');
assert.equal(rect.getAttribute('height'), '75');
});
assert.equal(rect.getAttribute('x'), '300')
assert.equal(rect.getAttribute('y'), '100')
assert.equal(rect.getAttribute('width'), '125')
assert.equal(rect.getAttribute('height'), '75')
})
it('Test remapElement(scale) for rect', function () {
const rect = document.createElementNS(NS.SVG, 'rect');
rect.setAttribute('width', '250');
rect.setAttribute('height', '120');
this.svg.append(rect);
const rect = document.createElementNS(NS.SVG, 'rect')
rect.setAttribute('width', '250')
rect.setAttribute('height', '120')
this.svg.append(rect)
const attrs = {
x: '0',
y: '0',
width: '250',
height: '120'
};
}
// Create a translate.
const m = this.svg.createSVGMatrix();
m.a = 2; m.b = 0;
m.c = 0; m.d = 0.5;
m.e = 0; m.f = 0;
const m = this.svg.createSVGMatrix()
m.a = 2; m.b = 0
m.c = 0; m.d = 0.5
m.e = 0; m.f = 0
coords.remapElement(rect, attrs, m);
coords.remapElement(rect, attrs, m)
assert.equal(rect.getAttribute('x'), '0');
assert.equal(rect.getAttribute('y'), '0');
assert.equal(rect.getAttribute('width'), '500');
assert.equal(rect.getAttribute('height'), '60');
});
assert.equal(rect.getAttribute('x'), '0')
assert.equal(rect.getAttribute('y'), '0')
assert.equal(rect.getAttribute('width'), '500')
assert.equal(rect.getAttribute('height'), '60')
})
it('Test remapElement(translate) for circle', function () {
const circle = document.createElementNS(NS.SVG, 'circle');
circle.setAttribute('cx', '200');
circle.setAttribute('cy', '150');
circle.setAttribute('r', '125');
this.svg.append(circle);
const circle = document.createElementNS(NS.SVG, 'circle')
circle.setAttribute('cx', '200')
circle.setAttribute('cy', '150')
circle.setAttribute('r', '125')
this.svg.append(circle)
const attrs = {
cx: '200',
cy: '150',
r: '125'
};
}
// Create a translate.
const m = this.svg.createSVGMatrix();
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = 100; m.f = -50;
const m = this.svg.createSVGMatrix()
m.a = 1; m.b = 0
m.c = 0; m.d = 1
m.e = 100; m.f = -50
coords.remapElement(circle, attrs, m);
coords.remapElement(circle, attrs, m)
assert.equal(circle.getAttribute('cx'), '300');
assert.equal(circle.getAttribute('cy'), '100');
assert.equal(circle.getAttribute('r'), '125');
});
assert.equal(circle.getAttribute('cx'), '300')
assert.equal(circle.getAttribute('cy'), '100')
assert.equal(circle.getAttribute('r'), '125')
})
it('Test remapElement(scale) for circle', function () {
const circle = document.createElementNS(NS.SVG, 'circle');
circle.setAttribute('cx', '200');
circle.setAttribute('cy', '150');
circle.setAttribute('r', '250');
this.svg.append(circle);
const circle = document.createElementNS(NS.SVG, 'circle')
circle.setAttribute('cx', '200')
circle.setAttribute('cy', '150')
circle.setAttribute('r', '250')
this.svg.append(circle)
const attrs = {
cx: '200',
cy: '150',
r: '250'
};
}
// Create a translate.
const m = this.svg.createSVGMatrix();
m.a = 2; m.b = 0;
m.c = 0; m.d = 0.5;
m.e = 0; m.f = 0;
const m = this.svg.createSVGMatrix()
m.a = 2; m.b = 0
m.c = 0; m.d = 0.5
m.e = 0; m.f = 0
coords.remapElement(circle, attrs, m);
coords.remapElement(circle, attrs, m)
assert.equal(circle.getAttribute('cx'), '400');
assert.equal(circle.getAttribute('cy'), '75');
assert.equal(circle.getAttribute('cx'), '400')
assert.equal(circle.getAttribute('cy'), '75')
// Radius is the minimum that fits in the new bounding box.
assert.equal(circle.getAttribute('r'), '125');
});
assert.equal(circle.getAttribute('r'), '125')
})
it('Test remapElement(translate) for ellipse', function () {
const ellipse = document.createElementNS(NS.SVG, 'ellipse');
ellipse.setAttribute('cx', '200');
ellipse.setAttribute('cy', '150');
ellipse.setAttribute('rx', '125');
ellipse.setAttribute('ry', '75');
this.svg.append(ellipse);
const ellipse = document.createElementNS(NS.SVG, 'ellipse')
ellipse.setAttribute('cx', '200')
ellipse.setAttribute('cy', '150')
ellipse.setAttribute('rx', '125')
ellipse.setAttribute('ry', '75')
this.svg.append(ellipse)
const attrs = {
cx: '200',
cy: '150',
rx: '125',
ry: '75'
};
}
// Create a translate.
const m = this.svg.createSVGMatrix();
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = 100; m.f = -50;
const m = this.svg.createSVGMatrix()
m.a = 1; m.b = 0
m.c = 0; m.d = 1
m.e = 100; m.f = -50
coords.remapElement(ellipse, attrs, m);
coords.remapElement(ellipse, attrs, m)
assert.equal(ellipse.getAttribute('cx'), '300');
assert.equal(ellipse.getAttribute('cy'), '100');
assert.equal(ellipse.getAttribute('rx'), '125');
assert.equal(ellipse.getAttribute('ry'), '75');
});
assert.equal(ellipse.getAttribute('cx'), '300')
assert.equal(ellipse.getAttribute('cy'), '100')
assert.equal(ellipse.getAttribute('rx'), '125')
assert.equal(ellipse.getAttribute('ry'), '75')
})
it('Test remapElement(scale) for ellipse', function () {
const ellipse = document.createElementNS(NS.SVG, 'ellipse');
ellipse.setAttribute('cx', '200');
ellipse.setAttribute('cy', '150');
ellipse.setAttribute('rx', '250');
ellipse.setAttribute('ry', '120');
this.svg.append(ellipse);
const ellipse = document.createElementNS(NS.SVG, 'ellipse')
ellipse.setAttribute('cx', '200')
ellipse.setAttribute('cy', '150')
ellipse.setAttribute('rx', '250')
ellipse.setAttribute('ry', '120')
this.svg.append(ellipse)
const attrs = {
cx: '200',
cy: '150',
rx: '250',
ry: '120'
};
}
// Create a translate.
const m = this.svg.createSVGMatrix();
m.a = 2; m.b = 0;
m.c = 0; m.d = 0.5;
m.e = 0; m.f = 0;
const m = this.svg.createSVGMatrix()
m.a = 2; m.b = 0
m.c = 0; m.d = 0.5
m.e = 0; m.f = 0
coords.remapElement(ellipse, attrs, m);
coords.remapElement(ellipse, attrs, m)
assert.equal(ellipse.getAttribute('cx'), '400');
assert.equal(ellipse.getAttribute('cy'), '75');
assert.equal(ellipse.getAttribute('rx'), '500');
assert.equal(ellipse.getAttribute('ry'), '60');
});
assert.equal(ellipse.getAttribute('cx'), '400')
assert.equal(ellipse.getAttribute('cy'), '75')
assert.equal(ellipse.getAttribute('rx'), '500')
assert.equal(ellipse.getAttribute('ry'), '60')
})
it('Test remapElement(translate) for line', function () {
const line = document.createElementNS(NS.SVG, 'line');
line.setAttribute('x1', '50');
line.setAttribute('y1', '100');
line.setAttribute('x2', '120');
line.setAttribute('y2', '200');
this.svg.append(line);
const line = document.createElementNS(NS.SVG, 'line')
line.setAttribute('x1', '50')
line.setAttribute('y1', '100')
line.setAttribute('x2', '120')
line.setAttribute('y2', '200')
this.svg.append(line)
const attrs = {
x1: '50',
y1: '100',
x2: '120',
y2: '200'
};
}
// Create a translate.
const m = this.svg.createSVGMatrix();
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = 100; m.f = -50;
const m = this.svg.createSVGMatrix()
m.a = 1; m.b = 0
m.c = 0; m.d = 1
m.e = 100; m.f = -50
coords.remapElement(line, attrs, m);
coords.remapElement(line, attrs, m)
assert.equal(line.getAttribute('x1'), '150');
assert.equal(line.getAttribute('y1'), '50');
assert.equal(line.getAttribute('x2'), '220');
assert.equal(line.getAttribute('y2'), '150');
});
assert.equal(line.getAttribute('x1'), '150')
assert.equal(line.getAttribute('y1'), '50')
assert.equal(line.getAttribute('x2'), '220')
assert.equal(line.getAttribute('y2'), '150')
})
it('Test remapElement(scale) for line', function () {
const line = document.createElementNS(NS.SVG, 'line');
line.setAttribute('x1', '50');
line.setAttribute('y1', '100');
line.setAttribute('x2', '120');
line.setAttribute('y2', '200');
this.svg.append(line);
const line = document.createElementNS(NS.SVG, 'line')
line.setAttribute('x1', '50')
line.setAttribute('y1', '100')
line.setAttribute('x2', '120')
line.setAttribute('y2', '200')
this.svg.append(line)
const attrs = {
x1: '50',
y1: '100',
x2: '120',
y2: '200'
};
}
// Create a translate.
const m = this.svg.createSVGMatrix();
m.a = 2; m.b = 0;
m.c = 0; m.d = 0.5;
m.e = 0; m.f = 0;
const m = this.svg.createSVGMatrix()
m.a = 2; m.b = 0
m.c = 0; m.d = 0.5
m.e = 0; m.f = 0
coords.remapElement(line, attrs, m);
coords.remapElement(line, attrs, m)
assert.equal(line.getAttribute('x1'), '100');
assert.equal(line.getAttribute('y1'), '50');
assert.equal(line.getAttribute('x2'), '240');
assert.equal(line.getAttribute('y2'), '100');
});
assert.equal(line.getAttribute('x1'), '100')
assert.equal(line.getAttribute('y1'), '50')
assert.equal(line.getAttribute('x2'), '240')
assert.equal(line.getAttribute('y2'), '100')
})
it('Test remapElement(translate) for text', function () {
const text = document.createElementNS(NS.SVG, 'text');
text.setAttribute('x', '50');
text.setAttribute('y', '100');
this.svg.append(text);
const text = document.createElementNS(NS.SVG, 'text')
text.setAttribute('x', '50')
text.setAttribute('y', '100')
this.svg.append(text)
const attrs = {
x: '50',
y: '100'
};
}
// Create a translate.
const m = this.svg.createSVGMatrix();
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = 100; m.f = -50;
const m = this.svg.createSVGMatrix()
m.a = 1; m.b = 0
m.c = 0; m.d = 1
m.e = 100; m.f = -50
coords.remapElement(text, attrs, m);
coords.remapElement(text, attrs, m)
assert.equal(text.getAttribute('x'), '150');
assert.equal(text.getAttribute('y'), '50');
});
});
assert.equal(text.getAttribute('x'), '150')
assert.equal(text.getAttribute('y'), '50')
})
})

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,34 @@
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as hstory from '../../../instrumented/svgcanvas/history.js';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
import * as utilities from '../../../instrumented/svgcanvas/utilities.js'
import * as history from '../../../instrumented/svgcanvas/history.js'
describe('history', function () {
// TODO(codedread): Write tests for handling history events.
utilities.mock({
getHref () { return '#foo'; },
getHref () { return '#foo' },
setHref () { /* empty fn */ },
getRotationAngle () { return 0; }
});
getRotationAngle () { return 0 }
})
// const svg = document.createElementNS(NS.SVG, 'svg');
let undoMgr = null;
let undoMgr = null
class MockCommand extends hstory.Command {
class MockCommand extends history.Command {
constructor (optText) {
super();
this.text = optText;
super()
this.text = optText
}
apply (handler) {
super.apply(handler, () => { /* empty fn */ });
super.apply(handler, () => { /* empty fn */ })
}
unapply (handler) {
super.unapply(handler, () => { /* empty fn */ });
super.unapply(handler, () => { /* empty fn */ })
}
elements () { return []; }
elements () { return [] }
}
/*
@ -39,479 +42,479 @@ describe('history', function () {
* @returns {void}
*/
beforeEach(function () {
undoMgr = new hstory.UndoManager();
undoMgr = new history.UndoManager()
document.body.textContent = '';
this.divparent = document.createElement('div');
this.divparent.id = 'divparent';
this.divparent.style.visibility = 'hidden';
document.body.textContent = ''
this.divparent = document.createElement('div')
this.divparent.id = 'divparent'
this.divparent.style.visibility = 'hidden'
for (let i = 1; i <= 5; i++) {
const div = document.createElement('div');
const id = `div${i}`;
div.id = id;
this[id] = div;
const div = document.createElement('div')
const id = `div${i}`
div.id = id
this[id] = div
}
this.divparent.append(this.div1, this.div2, this.div3);
this.divparent.append(this.div1, this.div2, this.div3)
this.div4.style.visibility = 'hidden';
this.div4.append(this.div5);
this.div4.style.visibility = 'hidden'
this.div4.append(this.div5)
document.body.append(this.divparent, this.div);
});
document.body.append(this.divparent, this.div)
})
/**
* Tear down tests, destroying undo manager.
* @returns {void}
*/
afterEach(() => {
undoMgr = null;
});
undoMgr = null
})
it('Test svgedit.history package', function () {
assert.ok(hstory);
assert.ok(hstory.MoveElementCommand);
assert.ok(hstory.InsertElementCommand);
assert.ok(hstory.ChangeElementCommand);
assert.ok(hstory.RemoveElementCommand);
assert.ok(hstory.BatchCommand);
assert.ok(hstory.UndoManager);
assert.equal(typeof hstory.MoveElementCommand, typeof function () { /* empty fn */ });
assert.equal(typeof hstory.InsertElementCommand, typeof function () { /* empty fn */ });
assert.equal(typeof hstory.ChangeElementCommand, typeof function () { /* empty fn */ });
assert.equal(typeof hstory.RemoveElementCommand, typeof function () { /* empty fn */ });
assert.equal(typeof hstory.BatchCommand, typeof function () { /* empty fn */ });
assert.equal(typeof hstory.UndoManager, typeof function () { /* empty fn */ });
});
assert.ok(history)
assert.ok(history.MoveElementCommand)
assert.ok(history.InsertElementCommand)
assert.ok(history.ChangeElementCommand)
assert.ok(history.RemoveElementCommand)
assert.ok(history.BatchCommand)
assert.ok(history.UndoManager)
assert.equal(typeof history.MoveElementCommand, typeof function () { /* empty fn */ })
assert.equal(typeof history.InsertElementCommand, typeof function () { /* empty fn */ })
assert.equal(typeof history.ChangeElementCommand, typeof function () { /* empty fn */ })
assert.equal(typeof history.RemoveElementCommand, typeof function () { /* empty fn */ })
assert.equal(typeof history.BatchCommand, typeof function () { /* empty fn */ })
assert.equal(typeof history.UndoManager, typeof function () { /* empty fn */ })
})
it('Test UndoManager methods', function () {
assert.ok(undoMgr);
assert.ok(undoMgr.addCommandToHistory);
assert.ok(undoMgr.getUndoStackSize);
assert.ok(undoMgr.getRedoStackSize);
assert.ok(undoMgr.resetUndoStack);
assert.ok(undoMgr.getNextUndoCommandText);
assert.ok(undoMgr.getNextRedoCommandText);
assert.ok(undoMgr)
assert.ok(undoMgr.addCommandToHistory)
assert.ok(undoMgr.getUndoStackSize)
assert.ok(undoMgr.getRedoStackSize)
assert.ok(undoMgr.resetUndoStack)
assert.ok(undoMgr.getNextUndoCommandText)
assert.ok(undoMgr.getNextRedoCommandText)
assert.equal(typeof undoMgr, typeof {});
assert.equal(typeof undoMgr.addCommandToHistory, typeof function () { /* empty fn */ });
assert.equal(typeof undoMgr.getUndoStackSize, typeof function () { /* empty fn */ });
assert.equal(typeof undoMgr.getRedoStackSize, typeof function () { /* empty fn */ });
assert.equal(typeof undoMgr.resetUndoStack, typeof function () { /* empty fn */ });
assert.equal(typeof undoMgr.getNextUndoCommandText, typeof function () { /* empty fn */ });
assert.equal(typeof undoMgr.getNextRedoCommandText, typeof function () { /* empty fn */ });
});
assert.equal(typeof undoMgr, typeof {})
assert.equal(typeof undoMgr.addCommandToHistory, typeof function () { /* empty fn */ })
assert.equal(typeof undoMgr.getUndoStackSize, typeof function () { /* empty fn */ })
assert.equal(typeof undoMgr.getRedoStackSize, typeof function () { /* empty fn */ })
assert.equal(typeof undoMgr.resetUndoStack, typeof function () { /* empty fn */ })
assert.equal(typeof undoMgr.getNextUndoCommandText, typeof function () { /* empty fn */ })
assert.equal(typeof undoMgr.getNextRedoCommandText, typeof function () { /* empty fn */ })
})
it('Test UndoManager.addCommandToHistory() function', function () {
assert.equal(undoMgr.getUndoStackSize(), 0);
undoMgr.addCommandToHistory(new MockCommand());
assert.equal(undoMgr.getUndoStackSize(), 1);
undoMgr.addCommandToHistory(new MockCommand());
assert.equal(undoMgr.getUndoStackSize(), 2);
});
assert.equal(undoMgr.getUndoStackSize(), 0)
undoMgr.addCommandToHistory(new MockCommand())
assert.equal(undoMgr.getUndoStackSize(), 1)
undoMgr.addCommandToHistory(new MockCommand())
assert.equal(undoMgr.getUndoStackSize(), 2)
})
it('Test UndoManager.getUndoStackSize() and getRedoStackSize() functions', function () {
undoMgr.addCommandToHistory(new MockCommand());
undoMgr.addCommandToHistory(new MockCommand());
undoMgr.addCommandToHistory(new MockCommand());
undoMgr.addCommandToHistory(new MockCommand())
undoMgr.addCommandToHistory(new MockCommand())
undoMgr.addCommandToHistory(new MockCommand())
assert.equal(undoMgr.getUndoStackSize(), 3);
assert.equal(undoMgr.getRedoStackSize(), 0);
assert.equal(undoMgr.getUndoStackSize(), 3)
assert.equal(undoMgr.getRedoStackSize(), 0)
undoMgr.undo();
assert.equal(undoMgr.getUndoStackSize(), 2);
assert.equal(undoMgr.getRedoStackSize(), 1);
undoMgr.undo()
assert.equal(undoMgr.getUndoStackSize(), 2)
assert.equal(undoMgr.getRedoStackSize(), 1)
undoMgr.undo();
assert.equal(undoMgr.getUndoStackSize(), 1);
assert.equal(undoMgr.getRedoStackSize(), 2);
undoMgr.undo()
assert.equal(undoMgr.getUndoStackSize(), 1)
assert.equal(undoMgr.getRedoStackSize(), 2)
undoMgr.undo();
assert.equal(undoMgr.getUndoStackSize(), 0);
assert.equal(undoMgr.getRedoStackSize(), 3);
undoMgr.undo()
assert.equal(undoMgr.getUndoStackSize(), 0)
assert.equal(undoMgr.getRedoStackSize(), 3)
undoMgr.undo();
assert.equal(undoMgr.getUndoStackSize(), 0);
assert.equal(undoMgr.getRedoStackSize(), 3);
undoMgr.undo()
assert.equal(undoMgr.getUndoStackSize(), 0)
assert.equal(undoMgr.getRedoStackSize(), 3)
undoMgr.redo();
assert.equal(undoMgr.getUndoStackSize(), 1);
assert.equal(undoMgr.getRedoStackSize(), 2);
undoMgr.redo()
assert.equal(undoMgr.getUndoStackSize(), 1)
assert.equal(undoMgr.getRedoStackSize(), 2)
undoMgr.redo();
assert.equal(undoMgr.getUndoStackSize(), 2);
assert.equal(undoMgr.getRedoStackSize(), 1);
undoMgr.redo()
assert.equal(undoMgr.getUndoStackSize(), 2)
assert.equal(undoMgr.getRedoStackSize(), 1)
undoMgr.redo();
assert.equal(undoMgr.getUndoStackSize(), 3);
assert.equal(undoMgr.getRedoStackSize(), 0);
undoMgr.redo()
assert.equal(undoMgr.getUndoStackSize(), 3)
assert.equal(undoMgr.getRedoStackSize(), 0)
undoMgr.redo();
assert.equal(undoMgr.getUndoStackSize(), 3);
assert.equal(undoMgr.getRedoStackSize(), 0);
});
undoMgr.redo()
assert.equal(undoMgr.getUndoStackSize(), 3)
assert.equal(undoMgr.getRedoStackSize(), 0)
})
it('Test UndoManager.resetUndoStackSize() function', function () {
undoMgr.addCommandToHistory(new MockCommand());
undoMgr.addCommandToHistory(new MockCommand());
undoMgr.addCommandToHistory(new MockCommand());
undoMgr.undo();
undoMgr.addCommandToHistory(new MockCommand())
undoMgr.addCommandToHistory(new MockCommand())
undoMgr.addCommandToHistory(new MockCommand())
undoMgr.undo()
assert.equal(undoMgr.getUndoStackSize(), 2);
assert.equal(undoMgr.getRedoStackSize(), 1);
assert.equal(undoMgr.getUndoStackSize(), 2)
assert.equal(undoMgr.getRedoStackSize(), 1)
undoMgr.resetUndoStack();
undoMgr.resetUndoStack()
assert.equal(undoMgr.getUndoStackSize(), 0);
assert.equal(undoMgr.getRedoStackSize(), 0);
});
assert.equal(undoMgr.getUndoStackSize(), 0)
assert.equal(undoMgr.getRedoStackSize(), 0)
})
it('Test UndoManager.getNextUndoCommandText() function', function () {
assert.equal(undoMgr.getNextUndoCommandText(), '');
assert.equal(undoMgr.getNextUndoCommandText(), '')
undoMgr.addCommandToHistory(new MockCommand('First'));
undoMgr.addCommandToHistory(new MockCommand('Second'));
undoMgr.addCommandToHistory(new MockCommand('Third'));
undoMgr.addCommandToHistory(new MockCommand('First'))
undoMgr.addCommandToHistory(new MockCommand('Second'))
undoMgr.addCommandToHistory(new MockCommand('Third'))
assert.equal(undoMgr.getNextUndoCommandText(), 'Third');
assert.equal(undoMgr.getNextUndoCommandText(), 'Third')
undoMgr.undo();
assert.equal(undoMgr.getNextUndoCommandText(), 'Second');
undoMgr.undo()
assert.equal(undoMgr.getNextUndoCommandText(), 'Second')
undoMgr.undo();
assert.equal(undoMgr.getNextUndoCommandText(), 'First');
undoMgr.undo()
assert.equal(undoMgr.getNextUndoCommandText(), 'First')
undoMgr.undo();
assert.equal(undoMgr.getNextUndoCommandText(), '');
undoMgr.undo()
assert.equal(undoMgr.getNextUndoCommandText(), '')
undoMgr.redo();
assert.equal(undoMgr.getNextUndoCommandText(), 'First');
undoMgr.redo()
assert.equal(undoMgr.getNextUndoCommandText(), 'First')
undoMgr.redo();
assert.equal(undoMgr.getNextUndoCommandText(), 'Second');
undoMgr.redo()
assert.equal(undoMgr.getNextUndoCommandText(), 'Second')
undoMgr.redo();
assert.equal(undoMgr.getNextUndoCommandText(), 'Third');
undoMgr.redo()
assert.equal(undoMgr.getNextUndoCommandText(), 'Third')
undoMgr.redo();
assert.equal(undoMgr.getNextUndoCommandText(), 'Third');
});
undoMgr.redo()
assert.equal(undoMgr.getNextUndoCommandText(), 'Third')
})
it('Test UndoManager.getNextRedoCommandText() function', function () {
assert.equal(undoMgr.getNextRedoCommandText(), '');
assert.equal(undoMgr.getNextRedoCommandText(), '')
undoMgr.addCommandToHistory(new MockCommand('First'));
undoMgr.addCommandToHistory(new MockCommand('Second'));
undoMgr.addCommandToHistory(new MockCommand('Third'));
undoMgr.addCommandToHistory(new MockCommand('First'))
undoMgr.addCommandToHistory(new MockCommand('Second'))
undoMgr.addCommandToHistory(new MockCommand('Third'))
assert.equal(undoMgr.getNextRedoCommandText(), '');
assert.equal(undoMgr.getNextRedoCommandText(), '')
undoMgr.undo();
assert.equal(undoMgr.getNextRedoCommandText(), 'Third');
undoMgr.undo()
assert.equal(undoMgr.getNextRedoCommandText(), 'Third')
undoMgr.undo();
assert.equal(undoMgr.getNextRedoCommandText(), 'Second');
undoMgr.undo()
assert.equal(undoMgr.getNextRedoCommandText(), 'Second')
undoMgr.undo();
assert.equal(undoMgr.getNextRedoCommandText(), 'First');
undoMgr.undo()
assert.equal(undoMgr.getNextRedoCommandText(), 'First')
undoMgr.redo();
assert.equal(undoMgr.getNextRedoCommandText(), 'Second');
undoMgr.redo()
assert.equal(undoMgr.getNextRedoCommandText(), 'Second')
undoMgr.redo();
assert.equal(undoMgr.getNextRedoCommandText(), 'Third');
undoMgr.redo()
assert.equal(undoMgr.getNextRedoCommandText(), 'Third')
undoMgr.redo();
assert.equal(undoMgr.getNextRedoCommandText(), '');
});
undoMgr.redo()
assert.equal(undoMgr.getNextRedoCommandText(), '')
})
it('Test UndoManager.undo() and redo() functions', function () {
let lastCalled = null;
const cmd1 = new MockCommand();
const cmd2 = new MockCommand();
const cmd3 = new MockCommand();
cmd1.apply = function () { lastCalled = 'cmd1.apply'; };
cmd2.apply = function () { lastCalled = 'cmd2.apply'; };
cmd3.apply = function () { lastCalled = 'cmd3.apply'; };
cmd1.unapply = function () { lastCalled = 'cmd1.unapply'; };
cmd2.unapply = function () { lastCalled = 'cmd2.unapply'; };
cmd3.unapply = function () { lastCalled = 'cmd3.unapply'; };
let lastCalled = null
const cmd1 = new MockCommand()
const cmd2 = new MockCommand()
const cmd3 = new MockCommand()
cmd1.apply = function () { lastCalled = 'cmd1.apply' }
cmd2.apply = function () { lastCalled = 'cmd2.apply' }
cmd3.apply = function () { lastCalled = 'cmd3.apply' }
cmd1.unapply = function () { lastCalled = 'cmd1.unapply' }
cmd2.unapply = function () { lastCalled = 'cmd2.unapply' }
cmd3.unapply = function () { lastCalled = 'cmd3.unapply' }
undoMgr.addCommandToHistory(cmd1);
undoMgr.addCommandToHistory(cmd2);
undoMgr.addCommandToHistory(cmd3);
undoMgr.addCommandToHistory(cmd1)
undoMgr.addCommandToHistory(cmd2)
undoMgr.addCommandToHistory(cmd3)
assert.ok(!lastCalled);
assert.ok(!lastCalled)
undoMgr.undo();
assert.equal(lastCalled, 'cmd3.unapply');
undoMgr.undo()
assert.equal(lastCalled, 'cmd3.unapply')
undoMgr.redo();
assert.equal(lastCalled, 'cmd3.apply');
undoMgr.redo()
assert.equal(lastCalled, 'cmd3.apply')
undoMgr.undo();
undoMgr.undo();
assert.equal(lastCalled, 'cmd2.unapply');
undoMgr.undo()
undoMgr.undo()
assert.equal(lastCalled, 'cmd2.unapply')
undoMgr.undo();
assert.equal(lastCalled, 'cmd1.unapply');
lastCalled = null;
undoMgr.undo()
assert.equal(lastCalled, 'cmd1.unapply')
lastCalled = null
undoMgr.undo();
assert.ok(!lastCalled);
undoMgr.undo()
assert.ok(!lastCalled)
undoMgr.redo();
assert.equal(lastCalled, 'cmd1.apply');
undoMgr.redo()
assert.equal(lastCalled, 'cmd1.apply')
undoMgr.redo();
assert.equal(lastCalled, 'cmd2.apply');
undoMgr.redo()
assert.equal(lastCalled, 'cmd2.apply')
undoMgr.redo();
assert.equal(lastCalled, 'cmd3.apply');
lastCalled = null;
undoMgr.redo()
assert.equal(lastCalled, 'cmd3.apply')
lastCalled = null
undoMgr.redo();
assert.ok(!lastCalled);
});
undoMgr.redo()
assert.ok(!lastCalled)
})
it('Test MoveElementCommand', function () {
let move = new hstory.MoveElementCommand(this.div3, this.div1, this.divparent);
assert.ok(move.unapply);
assert.ok(move.apply);
assert.equal(typeof move.unapply, typeof function () { /* empty fn */ });
assert.equal(typeof move.apply, typeof function () { /* empty fn */ });
let move = new history.MoveElementCommand(this.div3, this.div1, this.divparent)
assert.ok(move.unapply)
assert.ok(move.apply)
assert.equal(typeof move.unapply, typeof function () { /* empty fn */ })
assert.equal(typeof move.apply, typeof function () { /* empty fn */ })
move.unapply();
assert.equal(this.divparent.firstElementChild, this.div3);
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div1);
assert.equal(this.divparent.lastElementChild, this.div2);
move.unapply()
assert.equal(this.divparent.firstElementChild, this.div3)
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div1)
assert.equal(this.divparent.lastElementChild, this.div2)
move.apply();
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div2);
assert.equal(this.divparent.lastElementChild, this.div3);
move.apply()
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div2)
assert.equal(this.divparent.lastElementChild, this.div3)
move = new hstory.MoveElementCommand(this.div1, null, this.divparent);
move = new history.MoveElementCommand(this.div1, null, this.divparent)
move.unapply();
assert.equal(this.divparent.firstElementChild, this.div2);
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div3);
assert.equal(this.divparent.lastElementChild, this.div1);
move.unapply()
assert.equal(this.divparent.firstElementChild, this.div2)
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div3)
assert.equal(this.divparent.lastElementChild, this.div1)
move.apply();
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div2);
assert.equal(this.divparent.lastElementChild, this.div3);
move.apply()
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div2)
assert.equal(this.divparent.lastElementChild, this.div3)
move = new hstory.MoveElementCommand(this.div2, this.div5, this.div4);
move = new history.MoveElementCommand(this.div2, this.div5, this.div4)
move.unapply();
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div3);
assert.equal(this.divparent.lastElementChild, this.div3);
assert.equal(this.div4.firstElementChild, this.div2);
assert.equal(this.div4.firstElementChild.nextElementSibling, this.div5);
move.unapply()
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div3)
assert.equal(this.divparent.lastElementChild, this.div3)
assert.equal(this.div4.firstElementChild, this.div2)
assert.equal(this.div4.firstElementChild.nextElementSibling, this.div5)
move.apply();
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div2);
assert.equal(this.divparent.lastElementChild, this.div3);
assert.equal(this.div4.firstElementChild, this.div5);
assert.equal(this.div4.lastElementChild, this.div5);
});
move.apply()
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.divparent.firstElementChild.nextElementSibling, this.div2)
assert.equal(this.divparent.lastElementChild, this.div3)
assert.equal(this.div4.firstElementChild, this.div5)
assert.equal(this.div4.lastElementChild, this.div5)
})
it('Test InsertElementCommand', function () {
let insert = new hstory.InsertElementCommand(this.div3);
assert.ok(insert.unapply);
assert.ok(insert.apply);
assert.equal(typeof insert.unapply, typeof function () { /* empty fn */ });
assert.equal(typeof insert.apply, typeof function () { /* empty fn */ });
let insert = new history.InsertElementCommand(this.div3)
assert.ok(insert.unapply)
assert.ok(insert.apply)
assert.equal(typeof insert.unapply, typeof function () { /* empty fn */ })
assert.equal(typeof insert.apply, typeof function () { /* empty fn */ })
insert.unapply();
assert.equal(this.divparent.childElementCount, 2);
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.div1.nextElementSibling, this.div2);
assert.equal(this.divparent.lastElementChild, this.div2);
insert.unapply()
assert.equal(this.divparent.childElementCount, 2)
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.div1.nextElementSibling, this.div2)
assert.equal(this.divparent.lastElementChild, this.div2)
insert.apply();
assert.equal(this.divparent.childElementCount, 3);
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.div1.nextElementSibling, this.div2);
assert.equal(this.div2.nextElementSibling, this.div3);
insert.apply()
assert.equal(this.divparent.childElementCount, 3)
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.div1.nextElementSibling, this.div2)
assert.equal(this.div2.nextElementSibling, this.div3)
insert = new hstory.InsertElementCommand(this.div2);
insert = new history.InsertElementCommand(this.div2)
insert.unapply();
assert.equal(this.divparent.childElementCount, 2);
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.div1.nextElementSibling, this.div3);
assert.equal(this.divparent.lastElementChild, this.div3);
insert.unapply()
assert.equal(this.divparent.childElementCount, 2)
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.div1.nextElementSibling, this.div3)
assert.equal(this.divparent.lastElementChild, this.div3)
insert.apply();
assert.equal(this.divparent.childElementCount, 3);
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.div1.nextElementSibling, this.div2);
assert.equal(this.div2.nextElementSibling, this.div3);
});
insert.apply()
assert.equal(this.divparent.childElementCount, 3)
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.div1.nextElementSibling, this.div2)
assert.equal(this.div2.nextElementSibling, this.div3)
})
it('Test RemoveElementCommand', function () {
const div6 = document.createElement('div');
div6.id = 'div6';
const div6 = document.createElement('div')
div6.id = 'div6'
let remove = new hstory.RemoveElementCommand(div6, null, this.divparent);
assert.ok(remove.unapply);
assert.ok(remove.apply);
assert.equal(typeof remove.unapply, typeof function () { /* empty fn */ });
assert.equal(typeof remove.apply, typeof function () { /* empty fn */ });
let remove = new history.RemoveElementCommand(div6, null, this.divparent)
assert.ok(remove.unapply)
assert.ok(remove.apply)
assert.equal(typeof remove.unapply, typeof function () { /* empty fn */ })
assert.equal(typeof remove.apply, typeof function () { /* empty fn */ })
remove.unapply();
assert.equal(this.divparent.childElementCount, 4);
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.div1.nextElementSibling, this.div2);
assert.equal(this.div2.nextElementSibling, this.div3);
assert.equal(this.div3.nextElementSibling, div6);
remove.unapply()
assert.equal(this.divparent.childElementCount, 4)
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.div1.nextElementSibling, this.div2)
assert.equal(this.div2.nextElementSibling, this.div3)
assert.equal(this.div3.nextElementSibling, div6)
remove.apply();
assert.equal(this.divparent.childElementCount, 3);
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.div1.nextElementSibling, this.div2);
assert.equal(this.div2.nextElementSibling, this.div3);
remove.apply()
assert.equal(this.divparent.childElementCount, 3)
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.div1.nextElementSibling, this.div2)
assert.equal(this.div2.nextElementSibling, this.div3)
remove = new hstory.RemoveElementCommand(div6, this.div2, this.divparent);
remove = new history.RemoveElementCommand(div6, this.div2, this.divparent)
remove.unapply();
assert.equal(this.divparent.childElementCount, 4);
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.div1.nextElementSibling, div6);
assert.equal(div6.nextElementSibling, this.div2);
assert.equal(this.div2.nextElementSibling, this.div3);
remove.unapply()
assert.equal(this.divparent.childElementCount, 4)
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.div1.nextElementSibling, div6)
assert.equal(div6.nextElementSibling, this.div2)
assert.equal(this.div2.nextElementSibling, this.div3)
remove.apply();
assert.equal(this.divparent.childElementCount, 3);
assert.equal(this.divparent.firstElementChild, this.div1);
assert.equal(this.div1.nextElementSibling, this.div2);
assert.equal(this.div2.nextElementSibling, this.div3);
});
remove.apply()
assert.equal(this.divparent.childElementCount, 3)
assert.equal(this.divparent.firstElementChild, this.div1)
assert.equal(this.div1.nextElementSibling, this.div2)
assert.equal(this.div2.nextElementSibling, this.div3)
})
it('Test ChangeElementCommand', function () {
this.div1.setAttribute('title', 'new title');
let change = new hstory.ChangeElementCommand(this.div1,
{ title: 'old title', class: 'foo' });
assert.ok(change.unapply);
assert.ok(change.apply);
assert.equal(typeof change.unapply, typeof function () { /* empty fn */ });
assert.equal(typeof change.apply, typeof function () { /* empty fn */ });
this.div1.setAttribute('title', 'new title')
let change = new history.ChangeElementCommand(this.div1,
{ title: 'old title', class: 'foo' })
assert.ok(change.unapply)
assert.ok(change.apply)
assert.equal(typeof change.unapply, typeof function () { /* empty fn */ })
assert.equal(typeof change.apply, typeof function () { /* empty fn */ })
change.unapply();
assert.equal(this.div1.getAttribute('title'), 'old title');
assert.equal(this.div1.getAttribute('class'), 'foo');
change.unapply()
assert.equal(this.div1.getAttribute('title'), 'old title')
assert.equal(this.div1.getAttribute('class'), 'foo')
change.apply();
assert.equal(this.div1.getAttribute('title'), 'new title');
assert.ok(!this.div1.getAttribute('class'));
change.apply()
assert.equal(this.div1.getAttribute('title'), 'new title')
assert.ok(!this.div1.getAttribute('class'))
this.div1.textContent = 'inner text';
change = new hstory.ChangeElementCommand(this.div1,
{ '#text': null });
this.div1.textContent = 'inner text'
change = new history.ChangeElementCommand(this.div1,
{ '#text': null })
change.unapply();
assert.ok(!this.div1.textContent);
change.unapply()
assert.ok(!this.div1.textContent)
change.apply();
assert.equal(this.div1.textContent, 'inner text');
change.apply()
assert.equal(this.div1.textContent, 'inner text')
this.div1.textContent = '';
change = new hstory.ChangeElementCommand(this.div1,
{ '#text': 'old text' });
this.div1.textContent = ''
change = new history.ChangeElementCommand(this.div1,
{ '#text': 'old text' })
change.unapply();
assert.equal(this.div1.textContent, 'old text');
change.unapply()
assert.equal(this.div1.textContent, 'old text')
change.apply();
assert.ok(!this.div1.textContent);
change.apply()
assert.ok(!this.div1.textContent)
// TODO(codedread): Refactor this #href stuff in history.js and svgcanvas.js
const rect = document.createElementNS(NS.SVG, 'rect');
let justCalled = null;
let gethrefvalue = null;
let sethrefvalue = null;
const rect = document.createElementNS(NS.SVG, 'rect')
let justCalled = null
let gethrefvalue = null
let sethrefvalue = null
utilities.mock({
getHref (elem) {
assert.equal(elem, rect);
justCalled = 'getHref';
return gethrefvalue;
assert.equal(elem, rect)
justCalled = 'getHref'
return gethrefvalue
},
setHref (elem, val) {
assert.equal(elem, rect);
assert.equal(val, sethrefvalue);
justCalled = 'setHref';
assert.equal(elem, rect)
assert.equal(val, sethrefvalue)
justCalled = 'setHref'
},
getRotationAngle () { return 0; }
});
getRotationAngle () { return 0 }
})
gethrefvalue = '#newhref';
change = new hstory.ChangeElementCommand(rect,
{ '#href': '#oldhref' });
assert.equal(justCalled, 'getHref');
gethrefvalue = '#newhref'
change = new history.ChangeElementCommand(rect,
{ '#href': '#oldhref' })
assert.equal(justCalled, 'getHref')
justCalled = null;
sethrefvalue = '#oldhref';
change.unapply();
assert.equal(justCalled, 'setHref');
justCalled = null
sethrefvalue = '#oldhref'
change.unapply()
assert.equal(justCalled, 'setHref')
justCalled = null;
sethrefvalue = '#newhref';
change.apply();
assert.equal(justCalled, 'setHref');
justCalled = null
sethrefvalue = '#newhref'
change.apply()
assert.equal(justCalled, 'setHref')
const line = document.createElementNS(NS.SVG, 'line');
line.setAttribute('class', 'newClass');
change = new hstory.ChangeElementCommand(line, { class: 'oldClass' });
const line = document.createElementNS(NS.SVG, 'line')
line.setAttribute('class', 'newClass')
change = new history.ChangeElementCommand(line, { class: 'oldClass' })
assert.ok(change.unapply);
assert.ok(change.apply);
assert.equal(typeof change.unapply, typeof function () { /* empty fn */ });
assert.equal(typeof change.apply, typeof function () { /* empty fn */ });
assert.ok(change.unapply)
assert.ok(change.apply)
assert.equal(typeof change.unapply, typeof function () { /* empty fn */ })
assert.equal(typeof change.apply, typeof function () { /* empty fn */ })
change.unapply();
assert.equal(line.getAttribute('class'), 'oldClass');
change.unapply()
assert.equal(line.getAttribute('class'), 'oldClass')
change.apply();
assert.equal(line.getAttribute('class'), 'newClass');
});
change.apply()
assert.equal(line.getAttribute('class'), 'newClass')
})
it('Test BatchCommand', function () {
let concatResult = '';
MockCommand.prototype.apply = function () { concatResult += this.text; };
let concatResult = ''
MockCommand.prototype.apply = function () { concatResult += this.text }
const batch = new hstory.BatchCommand();
assert.ok(batch.unapply);
assert.ok(batch.apply);
assert.ok(batch.addSubCommand);
assert.ok(batch.isEmpty);
assert.equal(typeof batch.unapply, 'function');
assert.equal(typeof batch.apply, 'function');
assert.equal(typeof batch.addSubCommand, 'function');
assert.equal(typeof batch.isEmpty, 'function');
const batch = new history.BatchCommand()
assert.ok(batch.unapply)
assert.ok(batch.apply)
assert.ok(batch.addSubCommand)
assert.ok(batch.isEmpty)
assert.equal(typeof batch.unapply, 'function')
assert.equal(typeof batch.apply, 'function')
assert.equal(typeof batch.addSubCommand, 'function')
assert.equal(typeof batch.isEmpty, 'function')
assert.ok(batch.isEmpty());
assert.ok(batch.isEmpty())
batch.addSubCommand(new MockCommand('a'));
assert.ok(!batch.isEmpty());
batch.addSubCommand(new MockCommand('b'));
batch.addSubCommand(new MockCommand('c'));
batch.addSubCommand(new MockCommand('a'))
assert.ok(!batch.isEmpty())
batch.addSubCommand(new MockCommand('b'))
batch.addSubCommand(new MockCommand('c'))
assert.ok(!concatResult);
batch.apply();
assert.equal(concatResult, 'abc');
assert.ok(!concatResult)
batch.apply()
assert.equal(concatResult, 'abc')
MockCommand.prototype.apply = function () { /* empty fn */ };
MockCommand.prototype.unapply = function () { concatResult += this.text; };
concatResult = '';
assert.ok(!concatResult);
batch.unapply();
assert.equal(concatResult, 'cba');
MockCommand.prototype.apply = function () { /* empty fn */ }
MockCommand.prototype.unapply = function () { concatResult += this.text }
concatResult = ''
assert.ok(!concatResult)
batch.unapply()
assert.equal(concatResult, 'cba')
MockCommand.prototype.unapply = function () { /* empty fn */ };
});
});
MockCommand.prototype.unapply = function () { /* empty fn */ }
})
})

View File

@ -1,106 +1,106 @@
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as math from '../../../instrumented/svgcanvas/math.js';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
import * as math from '../../../instrumented/svgcanvas/math.js'
describe('math', function () {
const svg = document.createElementNS(NS.SVG, 'svg');
const svg = document.createElementNS(NS.SVG, 'svg')
it('Test svgedit.math package', function () {
assert.ok(math);
assert.ok(math.transformPoint);
assert.ok(math.isIdentity);
assert.ok(math.matrixMultiply);
assert.equal(typeof math.transformPoint, typeof function () { /* empty fn */ });
assert.equal(typeof math.isIdentity, typeof function () { /* empty fn */ });
assert.equal(typeof math.matrixMultiply, typeof function () { /* empty fn */ });
});
assert.ok(math)
assert.ok(math.transformPoint)
assert.ok(math.isIdentity)
assert.ok(math.matrixMultiply)
assert.equal(typeof math.transformPoint, typeof function () { /* empty fn */ })
assert.equal(typeof math.isIdentity, typeof function () { /* empty fn */ })
assert.equal(typeof math.matrixMultiply, typeof function () { /* empty fn */ })
})
it('Test svgedit.math.transformPoint() function', function () {
const { transformPoint } = math;
const { transformPoint } = math
const m = svg.createSVGMatrix();
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = 0; m.f = 0;
let pt = transformPoint(100, 200, m);
assert.equal(pt.x, 100);
assert.equal(pt.y, 200);
const m = svg.createSVGMatrix()
m.a = 1; m.b = 0
m.c = 0; m.d = 1
m.e = 0; m.f = 0
let pt = transformPoint(100, 200, m)
assert.equal(pt.x, 100)
assert.equal(pt.y, 200)
m.e = 300; m.f = 400;
pt = transformPoint(100, 200, m);
assert.equal(pt.x, 400);
assert.equal(pt.y, 600);
m.e = 300; m.f = 400
pt = transformPoint(100, 200, m)
assert.equal(pt.x, 400)
assert.equal(pt.y, 600)
m.a = 0.5; m.b = 0.75;
m.c = 1.25; m.d = 2;
pt = transformPoint(100, 200, m);
assert.equal(pt.x, 100 * m.a + 200 * m.c + m.e);
assert.equal(pt.y, 100 * m.b + 200 * m.d + m.f);
});
m.a = 0.5; m.b = 0.75
m.c = 1.25; m.d = 2
pt = transformPoint(100, 200, m)
assert.equal(pt.x, 100 * m.a + 200 * m.c + m.e)
assert.equal(pt.y, 100 * m.b + 200 * m.d + m.f)
})
it('Test svgedit.math.isIdentity() function', function () {
assert.ok(math.isIdentity(svg.createSVGMatrix()));
assert.ok(math.isIdentity(svg.createSVGMatrix()))
const m = svg.createSVGMatrix();
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = 0; m.f = 0;
assert.ok(math.isIdentity(m));
});
const m = svg.createSVGMatrix()
m.a = 1; m.b = 0
m.c = 0; m.d = 1
m.e = 0; m.f = 0
assert.ok(math.isIdentity(m))
})
it('Test svgedit.math.matrixMultiply() function', function () {
const mult = math.matrixMultiply;
const { isIdentity } = math;
const mult = math.matrixMultiply
const { isIdentity } = math
// translate there and back
const tr1 = svg.createSVGMatrix().translate(100, 50);
const tr2 = svg.createSVGMatrix().translate(-90, 0);
const tr3 = svg.createSVGMatrix().translate(-10, -50);
let I = mult(tr1, tr2, tr3);
assert.ok(isIdentity(I), 'Expected identity matrix when translating there and back');
const tr1 = svg.createSVGMatrix().translate(100, 50)
const tr2 = svg.createSVGMatrix().translate(-90, 0)
const tr3 = svg.createSVGMatrix().translate(-10, -50)
let I = mult(tr1, tr2, tr3)
assert.ok(isIdentity(I), 'Expected identity matrix when translating there and back')
// rotate there and back
// TODO: currently Mozilla fails this when rotating back at -50 and then -40 degrees
// (b and c are *almost* zero, but not zero)
const rotThere = svg.createSVGMatrix().rotate(90);
const rotBack = svg.createSVGMatrix().rotate(-90); // TODO: set this to -50
const rotBackMore = svg.createSVGMatrix().rotate(0); // TODO: set this to -40
I = mult(rotThere, rotBack, rotBackMore);
assert.ok(isIdentity(I), 'Expected identity matrix when rotating there and back');
const rotThere = svg.createSVGMatrix().rotate(90)
const rotBack = svg.createSVGMatrix().rotate(-90) // TODO: set this to -50
const rotBackMore = svg.createSVGMatrix().rotate(0) // TODO: set this to -40
I = mult(rotThere, rotBack, rotBackMore)
assert.ok(isIdentity(I), 'Expected identity matrix when rotating there and back')
// scale up and down
const scaleUp = svg.createSVGMatrix().scale(4);
const scaleDown = svg.createSVGMatrix().scaleNonUniform(0.25, 1);
const scaleDownMore = svg.createSVGMatrix().scaleNonUniform(1, 0.25);
I = mult(scaleUp, scaleDown, scaleDownMore);
assert.ok(isIdentity(I), 'Expected identity matrix when scaling up and down');
const scaleUp = svg.createSVGMatrix().scale(4)
const scaleDown = svg.createSVGMatrix().scaleNonUniform(0.25, 1)
const scaleDownMore = svg.createSVGMatrix().scaleNonUniform(1, 0.25)
I = mult(scaleUp, scaleDown, scaleDownMore)
assert.ok(isIdentity(I), 'Expected identity matrix when scaling up and down')
// test multiplication with its inverse
I = mult(rotThere, rotThere.inverse());
assert.ok(isIdentity(I), 'Expected identity matrix when multiplying a matrix by its inverse');
I = mult(rotThere.inverse(), rotThere);
assert.ok(isIdentity(I), 'Expected identity matrix when multiplying a matrix by its inverse');
});
I = mult(rotThere, rotThere.inverse())
assert.ok(isIdentity(I), 'Expected identity matrix when multiplying a matrix by its inverse')
I = mult(rotThere.inverse(), rotThere)
assert.ok(isIdentity(I), 'Expected identity matrix when multiplying a matrix by its inverse')
})
it('Test svgedit.math.transformBox() function', function () {
const { transformBox } = math;
const { transformBox } = math
const m = svg.createSVGMatrix();
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = 0; m.f = 0;
const m = svg.createSVGMatrix()
m.a = 1; m.b = 0
m.c = 0; m.d = 1
m.e = 0; m.f = 0
const r = transformBox(10, 10, 200, 300, m);
assert.equal(r.tl.x, 10);
assert.equal(r.tl.y, 10);
assert.equal(r.tr.x, 210);
assert.equal(r.tr.y, 10);
assert.equal(r.bl.x, 10);
assert.equal(r.bl.y, 310);
assert.equal(r.br.x, 210);
assert.equal(r.br.y, 310);
assert.equal(r.aabox.x, 10);
assert.equal(r.aabox.y, 10);
assert.equal(r.aabox.width, 200);
assert.equal(r.aabox.height, 300);
});
});
const r = transformBox(10, 10, 200, 300, m)
assert.equal(r.tl.x, 10)
assert.equal(r.tl.y, 10)
assert.equal(r.tr.x, 210)
assert.equal(r.tr.y, 10)
assert.equal(r.bl.x, 10)
assert.equal(r.bl.y, 310)
assert.equal(r.br.x, 210)
assert.equal(r.br.y, 310)
assert.equal(r.aabox.x, 10)
assert.equal(r.aabox.y, 10)
assert.equal(r.aabox.width, 200)
assert.equal(r.aabox.height, 300)
})
})

View File

@ -1,10 +1,10 @@
/* globals SVGPathSeg */
import 'pathseg';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as pathModule from '../../../instrumented/svgcanvas/path.js';
import { Path, Segment } from '../../../instrumented/svgcanvas/path-method.js';
import { init as unitsInit } from '../../../instrumented/common/units.js';
import 'pathseg'
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
import * as utilities from '../../../instrumented/svgcanvas/utilities.js'
import * as pathModule from '../../../instrumented/svgcanvas/path.js'
import { Path, Segment } from '../../../instrumented/svgcanvas/path-method.js'
import { init as unitsInit } from '../../../instrumented/common/units.js'
describe('path', function () {
/**
@ -18,165 +18,165 @@ describe('path', function () {
* @returns {EditorContexts}
*/
function getMockContexts (svg) {
svg = svg || document.createElementNS(NS.SVG, 'svg');
const selectorParentGroup = document.createElementNS(NS.SVG, 'g');
selectorParentGroup.setAttribute('id', 'selectorParentGroup');
svg.append(selectorParentGroup);
svg = svg || document.createElementNS(NS.SVG, 'svg')
const selectorParentGroup = document.createElementNS(NS.SVG, 'g')
selectorParentGroup.setAttribute('id', 'selectorParentGroup')
svg.append(selectorParentGroup)
return [
/**
* @implements {module:path.EditorContext}
*/
{
getSVGRoot () { return svg; },
getCurrentZoom () { return 1; }
getSvgRoot () { return svg },
getZoom () { return 1 }
},
/**
* @implements {module:utilities.EditorContext}
*/
{
getDOMDocument () { return svg; },
getDOMContainer () { return svg; },
getSVGRoot () { return svg; }
getDOMDocument () { return svg },
getDOMContainer () { return svg },
getSvgRoot () { return svg }
}
];
]
}
it('Test svgedit.path.replacePathSeg', function () {
const path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M0,0 L10,11 L20,21Z');
const path = document.createElementNS(NS.SVG, 'path')
path.setAttribute('d', 'M0,0 L10,11 L20,21Z')
const [ mockPathContext, mockUtilitiesContext ] = getMockContexts();
pathModule.init(mockPathContext);
utilities.init(mockUtilitiesContext);
new Path(path);
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
pathModule.init(mockPathContext)
utilities.init(mockUtilitiesContext)
new Path(path) // eslint-disable-line no-new
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L');
assert.equal(path.pathSegList.getItem(1).x, 10);
assert.equal(path.pathSegList.getItem(1).y, 11);
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L')
assert.equal(path.pathSegList.getItem(1).x, 10)
assert.equal(path.pathSegList.getItem(1).y, 11)
pathModule.replacePathSeg(SVGPathSeg.PATHSEG_LINETO_REL, 1, [ 30, 31 ], path);
pathModule.replacePathSeg(SVGPathSeg.PATHSEG_LINETO_REL, 1, [30, 31], path)
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'l');
assert.equal(path.pathSegList.getItem(1).x, 30);
assert.equal(path.pathSegList.getItem(1).y, 31);
});
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'l')
assert.equal(path.pathSegList.getItem(1).x, 30)
assert.equal(path.pathSegList.getItem(1).y, 31)
})
it('Test svgedit.path.Segment.setType simple', function () {
const path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M0,0 L10,11 L20,21Z');
const path = document.createElementNS(NS.SVG, 'path')
path.setAttribute('d', 'M0,0 L10,11 L20,21Z')
const [ mockPathContext, mockUtilitiesContext ] = getMockContexts();
pathModule.init(mockPathContext);
utilities.init(mockUtilitiesContext);
new Path(path);
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
pathModule.init(mockPathContext)
utilities.init(mockUtilitiesContext)
new Path(path) // eslint-disable-line no-new
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L');
assert.equal(path.pathSegList.getItem(1).x, 10);
assert.equal(path.pathSegList.getItem(1).y, 11);
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L')
assert.equal(path.pathSegList.getItem(1).x, 10)
assert.equal(path.pathSegList.getItem(1).y, 11)
const segment = new Segment(1, path.pathSegList.getItem(1));
segment.setType(SVGPathSeg.PATHSEG_LINETO_REL, [ 30, 31 ]);
assert.equal(segment.item.pathSegTypeAsLetter, 'l');
assert.equal(segment.item.x, 30);
assert.equal(segment.item.y, 31);
const segment = new Segment(1, path.pathSegList.getItem(1))
segment.setType(SVGPathSeg.PATHSEG_LINETO_REL, [30, 31])
assert.equal(segment.item.pathSegTypeAsLetter, 'l')
assert.equal(segment.item.x, 30)
assert.equal(segment.item.y, 31)
// Also verify that the actual path changed.
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'l');
assert.equal(path.pathSegList.getItem(1).x, 30);
assert.equal(path.pathSegList.getItem(1).y, 31);
});
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'l')
assert.equal(path.pathSegList.getItem(1).x, 30)
assert.equal(path.pathSegList.getItem(1).y, 31)
})
it('Test svgedit.path.Segment.setType with control points', function () {
// Setup the dom for a mock control group.
const svg = document.createElementNS(NS.SVG, 'svg');
const path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M0,0 C11,12 13,14 15,16 Z');
svg.append(path);
const svg = document.createElementNS(NS.SVG, 'svg')
const path = document.createElementNS(NS.SVG, 'path')
path.setAttribute('d', 'M0,0 C11,12 13,14 15,16 Z')
svg.append(path)
const [ mockPathContext, mockUtilitiesContext ] = getMockContexts(svg);
pathModule.init(mockPathContext);
utilities.init(mockUtilitiesContext);
const segment = new Segment(1, path.pathSegList.getItem(1));
segment.path = new Path(path);
const [mockPathContext, mockUtilitiesContext] = getMockContexts(svg)
pathModule.init(mockPathContext)
utilities.init(mockUtilitiesContext)
const segment = new Segment(1, path.pathSegList.getItem(1))
segment.path = new Path(path)
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'C');
assert.equal(path.pathSegList.getItem(1).x1, 11);
assert.equal(path.pathSegList.getItem(1).y1, 12);
assert.equal(path.pathSegList.getItem(1).x2, 13);
assert.equal(path.pathSegList.getItem(1).y2, 14);
assert.equal(path.pathSegList.getItem(1).x, 15);
assert.equal(path.pathSegList.getItem(1).y, 16);
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'C')
assert.equal(path.pathSegList.getItem(1).x1, 11)
assert.equal(path.pathSegList.getItem(1).y1, 12)
assert.equal(path.pathSegList.getItem(1).x2, 13)
assert.equal(path.pathSegList.getItem(1).y2, 14)
assert.equal(path.pathSegList.getItem(1).x, 15)
assert.equal(path.pathSegList.getItem(1).y, 16)
segment.setType(SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, [ 30, 31, 32, 33, 34, 35 ]);
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'c');
assert.equal(path.pathSegList.getItem(1).x1, 32);
assert.equal(path.pathSegList.getItem(1).y1, 33);
assert.equal(path.pathSegList.getItem(1).x2, 34);
assert.equal(path.pathSegList.getItem(1).y2, 35);
assert.equal(path.pathSegList.getItem(1).x, 30);
assert.equal(path.pathSegList.getItem(1).y, 31);
});
segment.setType(SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, [30, 31, 32, 33, 34, 35])
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'c')
assert.equal(path.pathSegList.getItem(1).x1, 32)
assert.equal(path.pathSegList.getItem(1).y1, 33)
assert.equal(path.pathSegList.getItem(1).x2, 34)
assert.equal(path.pathSegList.getItem(1).y2, 35)
assert.equal(path.pathSegList.getItem(1).x, 30)
assert.equal(path.pathSegList.getItem(1).y, 31)
})
it('Test svgedit.path.Segment.move', function () {
const path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M0,0 L10,11 L20,21Z');
const path = document.createElementNS(NS.SVG, 'path')
path.setAttribute('d', 'M0,0 L10,11 L20,21Z')
const [ mockPathContext, mockUtilitiesContext ] = getMockContexts();
pathModule.init(mockPathContext);
utilities.init(mockUtilitiesContext);
new Path(path);
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
pathModule.init(mockPathContext)
utilities.init(mockUtilitiesContext)
new Path(path) // eslint-disable-line no-new
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L');
assert.equal(path.pathSegList.getItem(1).x, 10);
assert.equal(path.pathSegList.getItem(1).y, 11);
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L')
assert.equal(path.pathSegList.getItem(1).x, 10)
assert.equal(path.pathSegList.getItem(1).y, 11)
const segment = new Segment(1, path.pathSegList.getItem(1));
segment.move(-3, 4);
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L');
assert.equal(path.pathSegList.getItem(1).x, 7);
assert.equal(path.pathSegList.getItem(1).y, 15);
});
const segment = new Segment(1, path.pathSegList.getItem(1))
segment.move(-3, 4)
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'L')
assert.equal(path.pathSegList.getItem(1).x, 7)
assert.equal(path.pathSegList.getItem(1).y, 15)
})
it('Test svgedit.path.Segment.moveCtrl', function () {
const path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M0,0 C11,12 13,14 15,16 Z');
const path = document.createElementNS(NS.SVG, 'path')
path.setAttribute('d', 'M0,0 C11,12 13,14 15,16 Z')
const [ mockPathContext, mockUtilitiesContext ] = getMockContexts();
pathModule.init(mockPathContext);
utilities.init(mockUtilitiesContext);
new Path(path);
const [mockPathContext, mockUtilitiesContext] = getMockContexts()
pathModule.init(mockPathContext)
utilities.init(mockUtilitiesContext)
new Path(path) // eslint-disable-line no-new
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'C');
assert.equal(path.pathSegList.getItem(1).x1, 11);
assert.equal(path.pathSegList.getItem(1).y1, 12);
assert.equal(path.pathSegList.getItem(1).x2, 13);
assert.equal(path.pathSegList.getItem(1).y2, 14);
assert.equal(path.pathSegList.getItem(1).x, 15);
assert.equal(path.pathSegList.getItem(1).y, 16);
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'C')
assert.equal(path.pathSegList.getItem(1).x1, 11)
assert.equal(path.pathSegList.getItem(1).y1, 12)
assert.equal(path.pathSegList.getItem(1).x2, 13)
assert.equal(path.pathSegList.getItem(1).y2, 14)
assert.equal(path.pathSegList.getItem(1).x, 15)
assert.equal(path.pathSegList.getItem(1).y, 16)
const segment = new Segment(1, path.pathSegList.getItem(1));
segment.moveCtrl(1, 100, -200);
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'C');
assert.equal(path.pathSegList.getItem(1).x1, 111);
assert.equal(path.pathSegList.getItem(1).y1, -188);
assert.equal(path.pathSegList.getItem(1).x2, 13);
assert.equal(path.pathSegList.getItem(1).y2, 14);
assert.equal(path.pathSegList.getItem(1).x, 15);
assert.equal(path.pathSegList.getItem(1).y, 16);
});
const segment = new Segment(1, path.pathSegList.getItem(1))
segment.moveCtrl(1, 100, -200)
assert.equal(path.pathSegList.getItem(1).pathSegTypeAsLetter, 'C')
assert.equal(path.pathSegList.getItem(1).x1, 111)
assert.equal(path.pathSegList.getItem(1).y1, -188)
assert.equal(path.pathSegList.getItem(1).x2, 13)
assert.equal(path.pathSegList.getItem(1).y2, 14)
assert.equal(path.pathSegList.getItem(1).x, 15)
assert.equal(path.pathSegList.getItem(1).y, 16)
})
it('Test svgedit.path.convertPath', function () {
unitsInit({
getRoundDigits () { return 5; }
});
getRoundDigits () { return 5 }
})
const path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M40,55h20v20');
const path = document.createElementNS(NS.SVG, 'path')
path.setAttribute('d', 'M40,55h20v20')
const abs = pathModule.convertPath(path);
assert.equal(abs, 'M40,55L60,55L60,75');
const abs = pathModule.convertPath(path)
assert.equal(abs, 'M40,55L60,55L60,75')
const rel = pathModule.convertPath(path, true);
assert.equal(rel, 'm40,55l20,0l0,20');
});
});
const rel = pathModule.convertPath(path, true)
assert.equal(rel, 'm40,55l20,0l0,20')
})
})

View File

@ -1,122 +1,121 @@
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as coords from '../../../instrumented/svgcanvas/coords.js';
import * as recalculate from '../../../instrumented/svgcanvas/recalculate.js';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
import * as utilities from '../../../instrumented/svgcanvas/utilities.js'
import * as coords from '../../../instrumented/svgcanvas/coords.js'
import * as recalculate from '../../../instrumented/svgcanvas/recalculate.js'
describe('recalculate', function () {
const root = document.createElement('div');
root.id = 'root';
root.style.visibility = 'hidden';
const root = document.createElement('div')
root.id = 'root'
root.style.visibility = 'hidden'
const svgroot = document.createElementNS(NS.SVG, 'svg');
svgroot.id = 'svgroot';
root.append(svgroot);
const svg = document.createElementNS(NS.SVG, 'svg');
svgroot.append(svg);
const svgroot = document.createElementNS(NS.SVG, 'svg')
svgroot.id = 'svgroot'
root.append(svgroot)
const svg = document.createElementNS(NS.SVG, 'svg')
svgroot.append(svg)
const dataStorage = {
_storage: new WeakMap(),
put: function (element, key, obj) {
if (!this._storage.has(element)) {
this._storage.set(element, new Map());
this._storage.set(element, new Map())
}
this._storage.get(element).set(key, obj);
this._storage.get(element).set(key, obj)
},
get: function (element, key) {
return this._storage.get(element).get(key);
return this._storage.get(element).get(key)
},
has: function (element, key) {
return this._storage.has(element) && this._storage.get(element).has(key);
return this._storage.has(element) && this._storage.get(element).has(key)
},
remove: function (element, key) {
const ret = this._storage.get(element).delete(key);
const ret = this._storage.get(element).delete(key)
if (!this._storage.get(element).size === 0) {
this._storage.delete(element);
this._storage.delete(element)
}
return ret;
return ret
}
};
}
let elemId = 1;
let elemId = 1
/**
* Initilize modules to set up the tests.
* @returns {void}
*/
function setUp() {
function setUp () {
utilities.init(
/**
* @implements {module:utilities.EditorContext}
*/
{
getSVGRoot() { return svg; },
getDOMDocument() { return null; },
getDOMContainer() { return null; },
getDataStorage() { return dataStorage; }
getSvgRoot () { return svg },
getDOMDocument () { return null },
getDOMContainer () { return null },
getDataStorage () { return dataStorage }
}
);
)
coords.init(
/**
* @implements {module:coords.EditorContext}
*/
{
getGridSnapping() { return false; },
getDrawing() {
getGridSnapping () { return false },
getDrawing () {
return {
getNextId() { return String(elemId++); }
};
getNextId () { return String(elemId++) }
}
},
getDataStorage() { return dataStorage; }
getDataStorage () { return dataStorage }
}
);
)
recalculate.init(
/**
* @implements {module:recalculate.EditorContext}
*/
{
getSVGRoot() { return svg; },
getStartTransform() { return ''; },
setStartTransform() { /* empty fn */ },
getDataStorage() { return dataStorage; }
getSvgRoot () { return svg },
getStartTransform () { return '' },
setStartTransform () { /* empty fn */ },
getDataStorage () { return dataStorage }
}
);
)
}
let elem;
let elem
/**
* Initialize for tests and set up `rect` element.
* @returns {void}
*/
function setUpRect() {
setUp();
elem = document.createElementNS(NS.SVG, 'rect');
elem.setAttribute('x', '200');
elem.setAttribute('y', '150');
elem.setAttribute('width', '250');
elem.setAttribute('height', '120');
svg.append(elem);
function setUpRect () {
setUp()
elem = document.createElementNS(NS.SVG, 'rect')
elem.setAttribute('x', '200')
elem.setAttribute('y', '150')
elem.setAttribute('width', '250')
elem.setAttribute('height', '120')
svg.append(elem)
}
/**
* Initialize for tests and set up `text` element with `tspan` child.
* @returns {void}
*/
function setUpTextWithTspan() {
setUp();
elem = document.createElementNS(NS.SVG, 'text');
elem.setAttribute('x', '200');
elem.setAttribute('y', '150');
function setUpTextWithTspan () {
setUp()
elem = document.createElementNS(NS.SVG, 'text')
elem.setAttribute('x', '200')
elem.setAttribute('y', '150')
const tspan = document.createElementNS(NS.SVG, 'tspan');
tspan.setAttribute('x', '200');
tspan.setAttribute('y', '150');
const tspan = document.createElementNS(NS.SVG, 'tspan')
tspan.setAttribute('x', '200')
tspan.setAttribute('y', '150')
const theText = 'Foo bar';
tspan.append(theText);
elem.append(tspan);
svg.append(elem);
const theText = 'Foo bar'
tspan.append(theText)
elem.append(tspan)
svg.append(elem)
}
/**
@ -125,53 +124,53 @@ describe('recalculate', function () {
*/
afterEach(() => {
while (svg.hasChildNodes()) {
svg.firstChild.remove();
svg.firstChild.remove()
}
});
})
it('Test recalculateDimensions() on rect with identity matrix', function () {
setUpRect();
elem.setAttribute('transform', 'matrix(1,0,0,1,0,0)');
setUpRect()
elem.setAttribute('transform', 'matrix(1,0,0,1,0,0)')
recalculate.recalculateDimensions(elem);
recalculate.recalculateDimensions(elem)
// Ensure that the identity matrix is swallowed and the element has no
// transform on it.
assert.equal(elem.hasAttribute('transform'), false);
});
assert.equal(elem.hasAttribute('transform'), false)
})
it('Test recalculateDimensions() on rect with simple translate', function () {
setUpRect();
elem.setAttribute('transform', 'translate(100,50)');
setUpRect()
elem.setAttribute('transform', 'translate(100,50)')
recalculate.recalculateDimensions(elem);
recalculate.recalculateDimensions(elem)
assert.equal(elem.hasAttribute('transform'), false);
assert.equal(elem.getAttribute('x'), '300');
assert.equal(elem.getAttribute('y'), '200');
assert.equal(elem.getAttribute('width'), '250');
assert.equal(elem.getAttribute('height'), '120');
});
assert.equal(elem.hasAttribute('transform'), false)
assert.equal(elem.getAttribute('x'), '300')
assert.equal(elem.getAttribute('y'), '200')
assert.equal(elem.getAttribute('width'), '250')
assert.equal(elem.getAttribute('height'), '120')
})
it('Test recalculateDimensions() on text w/tspan with simple translate', function () {
setUpTextWithTspan();
elem.setAttribute('transform', 'translate(100,50)');
setUpTextWithTspan()
elem.setAttribute('transform', 'translate(100,50)')
recalculate.recalculateDimensions(elem);
recalculate.recalculateDimensions(elem)
// Ensure that the identity matrix is swallowed and the element has no
// transform on it.
assert.equal(elem.hasAttribute('transform'), false);
assert.equal(elem.getAttribute('x'), '300');
assert.equal(elem.getAttribute('y'), '200');
assert.equal(elem.hasAttribute('transform'), false)
assert.equal(elem.getAttribute('x'), '300')
assert.equal(elem.getAttribute('y'), '200')
const tspan = elem.firstElementChild;
assert.equal(tspan.getAttribute('x'), '300');
assert.equal(tspan.getAttribute('y'), '200');
});
const tspan = elem.firstElementChild
assert.equal(tspan.getAttribute('x'), '300')
assert.equal(tspan.getAttribute('y'), '200')
})
// TODO: Since recalculateDimensions() and surrounding code is
// probably the largest, most complicated and strange piece of
// code in SVG-edit, we need to write a whole lot of unit tests
// for it here.
});
})

View File

@ -1,17 +1,17 @@
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as sanitize from '../../../instrumented/svgcanvas/sanitize.js';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
import * as sanitize from '../../../instrumented/svgcanvas/sanitize.js'
describe('sanitize', function () {
const svg = document.createElementNS(NS.SVG, 'svg');
const svg = document.createElementNS(NS.SVG, 'svg')
it('Test sanitizeSvg() strips ws from style attr', function () {
const rect = document.createElementNS(NS.SVG, 'rect');
rect.setAttribute('style', 'stroke: blue ;\t\tstroke-width :\t\t40;');
const rect = document.createElementNS(NS.SVG, 'rect')
rect.setAttribute('style', 'stroke: blue ;\t\tstroke-width :\t\t40;')
// sanitizeSvg() requires the node to have a parent and a document.
svg.append(rect);
sanitize.sanitizeSvg(rect);
svg.append(rect)
sanitize.sanitizeSvg(rect)
assert.equal(rect.getAttribute('stroke'), 'blue');
assert.equal(rect.getAttribute('stroke-width'), '40');
});
});
assert.equal(rect.getAttribute('stroke'), 'blue')
assert.equal(rect.getAttribute('stroke-width'), '40')
})
})

View File

@ -1,71 +1,72 @@
import * as select from '../../../instrumented/svgcanvas/select.js';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as select from '../../../instrumented/svgcanvas/select.js'
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
describe('select', function () {
const sandbox = document.createElement('div');
sandbox.id = 'sandbox';
const sandbox = document.createElement('div')
sandbox.id = 'sandbox'
let svgroot;
let svgcontent;
let svgroot
let svgContent
const mockConfig = {
dimensions: [ 640, 480 ]
};
dimensions: [640, 480]
}
const dataStorage = {
_storage: new WeakMap(),
put: function (element, key, obj) {
if (!this._storage.has(element)) {
this._storage.set(element, new Map());
this._storage.set(element, new Map())
}
this._storage.get(element).set(key, obj);
this._storage.get(element).set(key, obj)
},
get: function (element, key) {
return this._storage.get(element).get(key);
return this._storage.get(element).get(key)
},
has: function (element, key) {
return this._storage.has(element) && this._storage.get(element).has(key);
return this._storage.has(element) && this._storage.get(element).has(key)
},
remove: function (element, key) {
const ret = this._storage.get(element).delete(key);
const ret = this._storage.get(element).delete(key)
if (!this._storage.get(element).size === 0) {
this._storage.delete(element);
this._storage.delete(element)
}
return ret;
return ret
}
};
}
/**
* @implements {module:select.SVGFactory}
*/
const mockFactory = {
const mockSvgCanvas = {
curConfig: mockConfig,
createSVGElement (jsonMap) {
const elem = document.createElementNS(NS.SVG, jsonMap.element);
Object.entries(jsonMap.attr).forEach(([ attr, value ]) => {
elem.setAttribute(attr, value);
});
return elem;
const elem = document.createElementNS(NS.SVG, jsonMap.element)
Object.entries(jsonMap.attr).forEach(([attr, value]) => {
elem.setAttribute(attr, value)
})
return elem
},
svgRoot () { return svgroot; },
svgContent () { return svgcontent; },
getDataStorage () { return dataStorage; }
};
getSvgRoot () { return svgroot },
getSvgContent () { return svgContent },
getDataStorage () { return dataStorage }
}
/**
* Potentially reusable test set-up.
* @returns {void}
*/
beforeEach(() => {
svgroot = mockFactory.createSVGElement({
svgroot = mockSvgCanvas.createSVGElement({
element: 'svg',
attr: { id: 'svgroot' }
});
svgcontent = mockFactory.createSVGElement({
})
svgContent = mockSvgCanvas.createSVGElement({
element: 'svg',
attr: { id: 'svgcontent' }
});
})
svgroot.append(svgcontent);
/* const rect = */ svgcontent.append(
mockFactory.createSVGElement({
svgroot.append(svgContent)
/* const rect = */ svgContent.append(
mockSvgCanvas.createSVGElement({
element: 'rect',
attr: {
id: 'rect',
@ -75,9 +76,9 @@ describe('select', function () {
height: '100'
}
})
);
sandbox.append(svgroot);
});
)
sandbox.append(svgroot)
})
/*
function setUpWithInit () {
@ -91,61 +92,61 @@ describe('select', function () {
*/
afterEach(() => {
while (sandbox.hasChildNodes()) {
sandbox.firstChild.remove();
sandbox.firstChild.remove()
}
});
})
it('Test svgedit.select package', function () {
assert.ok(select);
assert.ok(select.Selector);
assert.ok(select.SelectorManager);
assert.ok(select.init);
assert.ok(select.getSelectorManager);
assert.equal(typeof select, typeof {});
assert.equal(typeof select.Selector, typeof function () { /* empty fn */ });
assert.equal(typeof select.SelectorManager, typeof function () { /* empty fn */ });
assert.equal(typeof select.init, typeof function () { /* empty fn */ });
assert.equal(typeof select.getSelectorManager, typeof function () { /* empty fn */ });
});
assert.ok(select)
assert.ok(select.Selector)
assert.ok(select.SelectorManager)
assert.ok(select.init)
assert.ok(select.getSelectorManager)
assert.equal(typeof select, typeof {})
assert.equal(typeof select.Selector, typeof function () { /* empty fn */ })
assert.equal(typeof select.SelectorManager, typeof function () { /* empty fn */ })
assert.equal(typeof select.init, typeof function () { /* empty fn */ })
assert.equal(typeof select.getSelectorManager, typeof function () { /* empty fn */ })
})
it('Test Selector DOM structure', function () {
assert.ok(svgroot);
assert.ok(svgroot.hasChildNodes());
assert.ok(svgroot)
assert.ok(svgroot.hasChildNodes())
// Verify non-existence of Selector DOM nodes
assert.equal(svgroot.childNodes.length, 1);
assert.equal(svgroot.childNodes.item(0), svgcontent);
assert.ok(!svgroot.querySelector('#selectorParentGroup'));
assert.equal(svgroot.childNodes.length, 1)
assert.equal(svgroot.childNodes.item(0), svgContent)
assert.ok(!svgroot.querySelector('#selectorParentGroup'))
select.init(mockConfig, mockFactory);
select.init(mockSvgCanvas)
assert.equal(svgroot.childNodes.length, 3);
assert.equal(svgroot.childNodes.length, 3)
// Verify existence of canvas background.
const cb = svgroot.childNodes.item(0);
assert.ok(cb);
assert.equal(cb.id, 'canvasBackground');
const cb = svgroot.childNodes.item(0)
assert.ok(cb)
assert.equal(cb.id, 'canvasBackground')
assert.ok(svgroot.childNodes.item(1));
assert.equal(svgroot.childNodes.item(1), svgcontent);
assert.ok(svgroot.childNodes.item(1))
assert.equal(svgroot.childNodes.item(1), svgContent)
// Verify existence of selectorParentGroup.
const spg = svgroot.childNodes.item(2);
assert.ok(spg);
assert.equal(svgroot.querySelector('#selectorParentGroup'), spg);
assert.equal(spg.id, 'selectorParentGroup');
assert.equal(spg.tagName, 'g');
const spg = svgroot.childNodes.item(2)
assert.ok(spg)
assert.equal(svgroot.querySelector('#selectorParentGroup'), spg)
assert.equal(spg.id, 'selectorParentGroup')
assert.equal(spg.tagName, 'g')
// Verify existence of all grip elements.
assert.ok(spg.querySelector('#selectorGrip_resize_nw'));
assert.ok(spg.querySelector('#selectorGrip_resize_n'));
assert.ok(spg.querySelector('#selectorGrip_resize_ne'));
assert.ok(spg.querySelector('#selectorGrip_resize_e'));
assert.ok(spg.querySelector('#selectorGrip_resize_se'));
assert.ok(spg.querySelector('#selectorGrip_resize_s'));
assert.ok(spg.querySelector('#selectorGrip_resize_sw'));
assert.ok(spg.querySelector('#selectorGrip_resize_w'));
assert.ok(spg.querySelector('#selectorGrip_rotateconnector'));
assert.ok(spg.querySelector('#selectorGrip_rotate'));
});
});
assert.ok(spg.querySelector('#selectorGrip_resize_nw'))
assert.ok(spg.querySelector('#selectorGrip_resize_n'))
assert.ok(spg.querySelector('#selectorGrip_resize_ne'))
assert.ok(spg.querySelector('#selectorGrip_resize_e'))
assert.ok(spg.querySelector('#selectorGrip_resize_se'))
assert.ok(spg.querySelector('#selectorGrip_resize_s'))
assert.ok(spg.querySelector('#selectorGrip_resize_sw'))
assert.ok(spg.querySelector('#selectorGrip_resize_w'))
assert.ok(spg.querySelector('#selectorGrip_rotateconnector'))
assert.ok(spg.querySelector('#selectorGrip_rotate'))
})
})

View File

@ -1,5 +1,5 @@
/* eslint-disable max-len, no-console */
import SvgCanvas from '../../../instrumented/svgcanvas/svgcanvas.js';
import SvgCanvas from '../../../instrumented/svgcanvas/svgcanvas.js'
describe('Basic Module', function () {
// helper functions
@ -12,34 +12,34 @@ describe('Basic Module', function () {
};
*/
let svgCanvas;
let svgCanvas
const
// svgroot = document.getElementById('svgroot'),
// svgdoc = svgroot.documentElement,
svgns = 'http://www.w3.org/2000/svg';
const xlinkns = 'http://www.w3.org/1999/xlink';
svgns = 'http://www.w3.org/2000/svg'
const xlinkns = 'http://www.w3.org/1999/xlink'
beforeEach(() => {
document.body.textContent = '';
const svgEditor = document.createElement('div');
svgEditor.id = 'svg_editor';
const svgcanvas = document.createElement('div');
svgcanvas.style.visibility = 'hidden';
svgcanvas.id = 'svgcanvas';
const workarea = document.createElement('div');
workarea.id = 'workarea';
workarea.append(svgcanvas);
const toolsLeft = document.createElement('div');
toolsLeft.id = 'tools_left';
document.body.textContent = ''
const svgEditor = document.createElement('div')
svgEditor.id = 'svg_editor'
const svgcanvas = document.createElement('div')
svgcanvas.style.visibility = 'hidden'
svgcanvas.id = 'svgcanvas'
const workarea = document.createElement('div')
workarea.id = 'workarea'
workarea.append(svgcanvas)
const toolsLeft = document.createElement('div')
toolsLeft.id = 'tools_left'
svgEditor.append(workarea, toolsLeft);
document.body.append(svgEditor);
svgEditor.append(workarea, toolsLeft)
document.body.append(svgEditor)
svgCanvas = new SvgCanvas(
document.getElementById('svgcanvas'), {
canvas_expansion: 3,
dimensions: [ 640, 480 ],
dimensions: [640, 480],
initFill: {
color: 'FF0000', // solid red
opacity: 1
@ -53,20 +53,20 @@ describe('Basic Module', function () {
imgPath: '../editor/images',
langPath: 'locale/',
extPath: 'extensions/',
extensions: [ 'ext-arrows.js', 'ext-eyedropper.js' ],
extensions: ['ext-arrows.js', 'ext-eyedropper.js'],
initTool: 'select',
wireframe: false
}
);
});
)
})
it('Test existence of SvgCanvas object', function () {
assert.equal(typeof {}, typeof svgCanvas);
});
assert.equal(typeof {}, typeof svgCanvas)
})
describe('Path Module', function () {
it('Test path conversion from absolute to relative', function () {
const convert = svgCanvas.pathActions.convertPath;
const convert = svgCanvas.pathActions.convertPath
// TODO: Test these paths:
// "m400.00491,625.01379a1.78688,1.78688 0 1 1-3.57373,0a1.78688,1.78688 0 1 13.57373,0z"
@ -78,36 +78,36 @@ describe('Basic Module', function () {
"<path id='p1' d='M100,100 L200,100 L100,100Z'/>" +
"<path id='p2' d='m 0,0 l 200,0 l 0,100 L 0,100'/>" +
'</svg>'
);
)
const p1 = document.getElementById('p1');
const p2 = document.getElementById('p2');
const dAbs = p1.getAttribute('d');
const seglist = p1.pathSegList;
const p1 = document.getElementById('p1')
const p2 = document.getElementById('p2')
const dAbs = p1.getAttribute('d')
const seglist = p1.pathSegList
assert.equal(p1.nodeName, 'path', "Expected 'path', got");
assert.equal(p1.nodeName, 'path', "Expected 'path', got")
assert.equal(seglist.numberOfItems, 4, 'Number of segments before conversion');
assert.equal(seglist.numberOfItems, 4, 'Number of segments before conversion')
// verify segments before conversion
let curseg = seglist.getItem(0);
assert.equal(curseg.pathSegTypeAsLetter.toUpperCase(), 'M', 'Before conversion, segment #1 type');
curseg = seglist.getItem(1);
assert.equal(curseg.pathSegTypeAsLetter.toUpperCase(), 'L', 'Before conversion, segment #2 type');
curseg = seglist.getItem(3);
assert.equal(curseg.pathSegTypeAsLetter.toUpperCase(), 'Z', 'Before conversion, segment #3 type' + dAbs);
let curseg = seglist.getItem(0)
assert.equal(curseg.pathSegTypeAsLetter.toUpperCase(), 'M', 'Before conversion, segment #1 type')
curseg = seglist.getItem(1)
assert.equal(curseg.pathSegTypeAsLetter.toUpperCase(), 'L', 'Before conversion, segment #2 type')
curseg = seglist.getItem(3)
assert.equal(curseg.pathSegTypeAsLetter.toUpperCase(), 'Z', 'Before conversion, segment #3 type' + dAbs)
// convert and verify segments
let d = convert(p1, true);
assert.equal(d, 'm100,100l100,0l-100,0z', 'Converted path to relative string');
let d = convert(p1, true)
assert.equal(d, 'm100,100l100,0l-100,0z', 'Converted path to relative string')
// TODO: see why this isn't working in SVG-edit
d = convert(p2, true);
console.log('Convert true', d);
d = convert(p2, false);
console.log('Convert false', d);
});
});
d = convert(p2, true)
console.log('Convert true', d)
d = convert(p2, false)
console.log('Convert false', d)
})
})
describe('Import Module', function () {
it('Test import use', function () {
@ -118,16 +118,16 @@ describe('Basic Module', function () {
"<use id='foreign-use' xlink:href='somefile.svg#the-rect'/>" +
"<use id='no-use'/>" +
'</svg>'
);
)
const u = document.getElementById('the-use');
const fu = document.getElementById('foreign-use');
const nfu = document.getElementById('no-use');
const u = document.getElementById('the-use')
const fu = document.getElementById('foreign-use')
const nfu = document.getElementById('no-use')
assert.equal((u && u.nodeName), 'use', 'Did not import <use> element');
assert.equal(fu, null, 'Removed <use> element that had a foreign href');
assert.equal(nfu, null, 'Removed <use> element that had no href');
});
assert.equal((u && u.nodeName), 'use', 'Did not import <use> element')
assert.equal(fu, null, 'Removed <use> element that had a foreign href')
assert.equal(nfu, null, 'Removed <use> element that had no href')
})
// This test shows that an element with an invalid attribute is still parsed in properly
// and only the attribute is not imported
@ -136,13 +136,13 @@ describe('Basic Module', function () {
'<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">' +
'<text x="182.75" y="173.5" id="the-text" fill="#008000" font-size="150" font-family="serif" text-anchor="middle" d="M116,222 L110,108">words</text>' +
'</svg>'
);
)
const t = document.getElementById('the-text');
const t = document.getElementById('the-text')
assert.equal((t && t.nodeName), 'text', 'Did not import <text> element');
assert.equal(t.getAttribute('d'), null, 'Imported a <text> with a d attribute');
});
assert.equal((t && t.nodeName), 'text', 'Did not import <text> element')
assert.equal(t.getAttribute('d'), null, 'Imported a <text> with a d attribute')
})
// This test makes sure import/export properly handles namespaced attributes
it('Test importing/exporting namespaced attributes', function () {
@ -151,22 +151,22 @@ describe('Basic Module', function () {
'<image xlink:href="../editor/images/logo.png"/>' +
'<polyline id="se_test_elem" se:foo="bar" foo:bar="baz"/>' +
'</svg>'
);
const attrVal = document.getElementById('se_test_elem').getAttributeNS('http://svg-edit.googlecode.com', 'foo');
)
const attrVal = document.getElementById('se_test_elem').getAttributeNS('http://svg-edit.googlecode.com', 'foo')
assert.strictEqual(attrVal, 'bar', true, 'Preserved namespaced attribute on import');
assert.strictEqual(attrVal, 'bar', true, 'Preserved namespaced attribute on import')
const output = svgCanvas.getSvgString();
const hasXlink = output.includes('xmlns:xlink="http://www.w3.org/1999/xlink"');
const hasSe = output.includes('xmlns:se=');
const hasFoo = output.includes('xmlns:foo=');
const hasAttr = output.includes('se:foo="bar"');
const output = svgCanvas.getSvgString()
const hasXlink = output.includes('xmlns:xlink="http://www.w3.org/1999/xlink"')
const hasSe = output.includes('xmlns:se=')
const hasFoo = output.includes('xmlns:foo=')
const hasAttr = output.includes('se:foo="bar"')
assert.equal(hasAttr, true, 'Preserved namespaced attribute on export');
assert.equal(hasXlink, true, 'Included xlink: xmlns');
assert.equal(hasSe, true, 'Included se: xmlns');
assert.equal(hasFoo, false, 'Did not include foo: xmlns');
});
assert.equal(hasAttr, true, 'Preserved namespaced attribute on export')
assert.equal(hasXlink, true, 'Included xlink: xmlns')
assert.equal(hasSe, true, 'Included se: xmlns')
assert.equal(hasFoo, false, 'Did not include foo: xmlns')
})
it('Test import math elements inside a foreignObject', function () {
/* const set = */ svgCanvas.setSvgString(
@ -179,17 +179,17 @@ describe('Basic Module', function () {
'</math>' +
'</foreignObject>' +
'</svg>'
);
const fo = document.getElementById('fo');
)
const fo = document.getElementById('fo')
// we cannot use getElementById('math') because not all browsers understand MathML and do not know to use the @id attribute
// see Bug https://bugs.webkit.org/show_bug.cgi?id=35042
const math = fo.firstChild;
const math = fo.firstChild
assert.equal(Boolean(math), true, 'Math element exists');
assert.equal(math.nodeName, 'math', 'Math element has the proper nodeName');
assert.equal(math.getAttribute('id'), 'm', 'Math element has an id');
assert.equal(math.namespaceURI, 'http://www.w3.org/1998/Math/MathML', 'Preserved MathML namespace');
});
assert.equal(Boolean(math), true, 'Math element exists')
assert.equal(math.nodeName, 'math', 'Math element has the proper nodeName')
assert.equal(math.getAttribute('id'), 'm', 'Math element has an id')
assert.equal(math.namespaceURI, 'http://www.w3.org/1998/Math/MathML', 'Preserved MathML namespace')
})
it('Test importing SVG into existing drawing', function () {
/* const doc = */ svgCanvas.setSvgString(
@ -199,23 +199,23 @@ describe('Basic Module', function () {
'<ellipse cx="300" cy="100" rx="40" ry="30" fill="green"/>' +
'</g>' +
'</svg>'
);
)
svgCanvas.importSvgString(
'<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">' +
'<circle cx="50" cy="50" r="40" fill="yellow"/>' +
'<rect width="20" height="20" fill="blue"/>' +
'</svg>'
);
)
const svgcontent = document.getElementById('svgcontent');
const circles = svgcontent.getElementsByTagNameNS(svgns, 'circle');
const rects = svgcontent.getElementsByTagNameNS(svgns, 'rect');
const ellipses = svgcontent.getElementsByTagNameNS(svgns, 'ellipse');
assert.equal(circles.length, 2, 'Found two circles upon importing');
assert.equal(rects.length, 1, 'Found one rectangle upon importing');
assert.equal(ellipses.length, 1, 'Found one ellipse upon importing');
});
const svgContent = document.getElementById('svgcontent')
const circles = svgContent.getElementsByTagNameNS(svgns, 'circle')
const rects = svgContent.getElementsByTagNameNS(svgns, 'rect')
const ellipses = svgContent.getElementsByTagNameNS(svgns, 'ellipse')
assert.equal(circles.length, 2, 'Found two circles upon importing')
assert.equal(rects.length, 1, 'Found one rectangle upon importing')
assert.equal(ellipses.length, 1, 'Found one ellipse upon importing')
})
it('Test importing SVG remaps IDs', function () {
/* const doc = */ svgCanvas.setSvgString(
@ -226,7 +226,7 @@ describe('Basic Module', function () {
'<ellipse id="svg_3" cx="300" cy="100" rx="40" ry="30" fill="green"/>' +
'</g>' +
'</svg>'
);
)
svgCanvas.importSvgString(
'<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink">' +
@ -240,24 +240,24 @@ describe('Basic Module', function () {
'<circle id="svg_1" cx="50" cy="50" r="40" fill="url(#svg_2)"/>' +
'<use id="svg_4" width="30" height="30" xl:href="#svg_3"/>' +
'</svg>'
);
)
const svgcontent = document.getElementById('svgcontent');
const circles = svgcontent.getElementsByTagNameNS(svgns, 'circle');
const rects = svgcontent.getElementsByTagNameNS(svgns, 'rect');
// ellipses = svgcontent.getElementsByTagNameNS(svgns, 'ellipse'),
const defs = svgcontent.getElementsByTagNameNS(svgns, 'defs');
// grads = svgcontent.getElementsByTagNameNS(svgns, 'linearGradient'),
const uses = svgcontent.getElementsByTagNameNS(svgns, 'use');
assert.notEqual(circles.item(0).id, 'svg_1', 'Circle not re-identified');
assert.notEqual(rects.item(0).id, 'svg_3', 'Rectangle not re-identified');
const svgContent = document.getElementById('svgcontent')
const circles = svgContent.getElementsByTagNameNS(svgns, 'circle')
const rects = svgContent.getElementsByTagNameNS(svgns, 'rect')
// ellipses = svgContent.getElementsByTagNameNS(svgns, 'ellipse'),
const defs = svgContent.getElementsByTagNameNS(svgns, 'defs')
// grads = svgContent.getElementsByTagNameNS(svgns, 'linearGradient'),
const uses = svgContent.getElementsByTagNameNS(svgns, 'use')
assert.notEqual(circles.item(0).id, 'svg_1', 'Circle not re-identified')
assert.notEqual(rects.item(0).id, 'svg_3', 'Rectangle not re-identified')
// TODO: determine why this test fails in WebKit browsers
// assert.equal(grads.length, 1, 'Linear gradient imported');
const grad = defs.item(0).firstChild;
assert.notEqual(grad.id, 'svg_2', 'Linear gradient not re-identified');
assert.notEqual(circles.item(0).getAttribute('fill'), 'url(#svg_2)', 'Circle fill value not remapped');
assert.notEqual(rects.item(0).getAttribute('stroke'), 'url(#svg_2)', 'Rectangle stroke value not remapped');
assert.notEqual(uses.item(0).getAttributeNS(xlinkns, 'href'), '#svg_3');
});
});
});
const grad = defs.item(0).firstChild
assert.notEqual(grad.id, 'svg_2', 'Linear gradient not re-identified')
assert.notEqual(circles.item(0).getAttribute('fill'), 'url(#svg_2)', 'Circle fill value not remapped')
assert.notEqual(rects.item(0).getAttribute('stroke'), 'url(#svg_2)', 'Rectangle stroke value not remapped')
assert.notEqual(uses.item(0).getAttributeNS(xlinkns, 'href'), '#svg_3')
})
})
})

View File

@ -1,4 +1,4 @@
import * as units from '../../../instrumented/common/units.js';
import * as units from '../../../instrumented/common/units.js'
describe('units', function () {
/**
@ -6,86 +6,86 @@ describe('units', function () {
* @returns {void}
*/
beforeEach(() => {
document.body.textContent = '';
const anchor = document.createElement('div');
anchor.id = 'anchor';
anchor.style.visibility = 'hidden';
document.body.textContent = ''
const anchor = document.createElement('div')
anchor.id = 'anchor'
anchor.style.visibility = 'hidden'
const elementsContainer = document.createElement('div');
elementsContainer.id = 'elementsContainer';
const elementsContainer = document.createElement('div')
elementsContainer.id = 'elementsContainer'
const uniqueId = document.createElement('div');
uniqueId.id = 'uniqueId';
uniqueId.style.visibility = 'hidden';
const uniqueId = document.createElement('div')
uniqueId.id = 'uniqueId'
uniqueId.style.visibility = 'hidden'
const nonUniqueId = document.createElement('div');
nonUniqueId.id = 'nonUniqueId';
nonUniqueId.style.visibility = 'hidden';
const nonUniqueId = document.createElement('div')
nonUniqueId.id = 'nonUniqueId'
nonUniqueId.style.visibility = 'hidden'
elementsContainer.append(uniqueId, nonUniqueId);
elementsContainer.append(uniqueId, nonUniqueId)
document.body.append(anchor, elementsContainer);
document.body.append(anchor, elementsContainer)
units.init(
/**
* @implements {module:units.ElementContainer}
*/
{
getBaseUnit () { return 'cm'; },
getHeight () { return 600; },
getWidth () { return 800; },
getRoundDigits () { return 4; },
getElement (elementId) { return document.getElementById(elementId); }
getBaseUnit () { return 'cm' },
getHeight () { return 600 },
getWidth () { return 800 },
getRoundDigits () { return 4 },
getElement (elementId) { return document.getElementById(elementId) }
}
);
});
)
})
it('Test svgedit.units package', function () {
assert.ok(units);
assert.equal(typeof units, typeof {});
});
assert.ok(units)
assert.equal(typeof units, typeof {})
})
it('Test svgedit.units.shortFloat()', function () {
assert.ok(units.shortFloat);
assert.equal(typeof units.shortFloat, typeof function () { /* empty fn */ });
assert.ok(units.shortFloat)
assert.equal(typeof units.shortFloat, typeof function () { /* empty fn */ })
const { shortFloat } = units;
assert.equal(shortFloat(0.00000001), 0);
assert.equal(shortFloat(1), 1);
assert.equal(shortFloat(3.45678), 3.4568);
assert.equal(shortFloat(1.23443), 1.2344);
assert.equal(shortFloat(1.23455), 1.2346);
});
const { shortFloat } = units
assert.equal(shortFloat(0.00000001), 0)
assert.equal(shortFloat(1), 1)
assert.equal(shortFloat(3.45678), 3.4568)
assert.equal(shortFloat(1.23443), 1.2344)
assert.equal(shortFloat(1.23455), 1.2346)
})
it('Test svgedit.units.isValidUnit()', function () {
assert.ok(units.isValidUnit);
assert.equal(typeof units.isValidUnit, typeof function () { /* empty fn */ });
assert.ok(units.isValidUnit)
assert.equal(typeof units.isValidUnit, typeof function () { /* empty fn */ })
const { isValidUnit } = units;
assert.ok(isValidUnit('0'));
assert.ok(isValidUnit('1'));
assert.ok(isValidUnit('1.1'));
assert.ok(isValidUnit('-1.1'));
assert.ok(isValidUnit('.6mm'));
assert.ok(isValidUnit('-.6cm'));
assert.ok(isValidUnit('6000in'));
assert.ok(isValidUnit('6px'));
assert.ok(isValidUnit('6.3pc'));
assert.ok(isValidUnit('-0.4em'));
assert.ok(isValidUnit('-0.ex'));
assert.ok(isValidUnit('40.123%'));
const { isValidUnit } = units
assert.ok(isValidUnit('0'))
assert.ok(isValidUnit('1'))
assert.ok(isValidUnit('1.1'))
assert.ok(isValidUnit('-1.1'))
assert.ok(isValidUnit('.6mm'))
assert.ok(isValidUnit('-.6cm'))
assert.ok(isValidUnit('6000in'))
assert.ok(isValidUnit('6px'))
assert.ok(isValidUnit('6.3pc'))
assert.ok(isValidUnit('-0.4em'))
assert.ok(isValidUnit('-0.ex'))
assert.ok(isValidUnit('40.123%'))
assert.equal(isValidUnit('id', 'uniqueId', document.getElementById('uniqueId')), true);
assert.equal(isValidUnit('id', 'newId', document.getElementById('uniqueId')), true);
assert.equal(isValidUnit('id', 'uniqueId'), false);
assert.equal(isValidUnit('id', 'uniqueId', document.getElementById('nonUniqueId')), false);
});
assert.equal(isValidUnit('id', 'uniqueId', document.getElementById('uniqueId')), true)
assert.equal(isValidUnit('id', 'newId', document.getElementById('uniqueId')), true)
assert.equal(isValidUnit('id', 'uniqueId'), false)
assert.equal(isValidUnit('id', 'uniqueId', document.getElementById('nonUniqueId')), false)
})
it('Test svgedit.units.convertUnit()', function () {
assert.ok(units.convertUnit);
assert.equal(typeof units.convertUnit, typeof function () { /* empty fn */ });
assert.ok(units.convertUnit)
assert.equal(typeof units.convertUnit, typeof function () { /* empty fn */ })
// cm in default setup
assert.equal(units.convertUnit(42), 1.1113);
assert.equal(units.convertUnit(42, 'px'), 42);
});
});
assert.equal(units.convertUnit(42), 1.1113)
assert.equal(units.convertUnit(42, 'px'), 42)
})
})

View File

@ -1,12 +1,13 @@
import 'pathseg';
import 'pathseg'
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as math from '../../../instrumented/svgcanvas/math.js';
import * as path from '../../../instrumented/svgcanvas/path.js';
import setAssertionMethods from '../../support/assert-close.js';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
import * as utilities from '../../../instrumented/svgcanvas/utilities.js'
import * as math from '../../../instrumented/svgcanvas/math.js'
import * as path from '../../../instrumented/svgcanvas/path.js'
import setAssertionMethods from '../../support/assert-close.js'
chai.use(setAssertionMethods);
// eslint-disable-next-line
chai.use(setAssertionMethods)
describe('utilities bbox', function () {
/**
@ -15,439 +16,449 @@ describe('utilities bbox', function () {
* @returns {SVGElement}
*/
function mockCreateSVGElement (jsonMap) {
const elem = document.createElementNS(NS.SVG, jsonMap.element);
Object.entries(jsonMap.attr).forEach(([ attr, value ]) => {
elem.setAttribute(attr, value);
});
return elem;
const elem = document.createElementNS(NS.SVG, jsonMap.element)
Object.entries(jsonMap.attr).forEach(([attr, value]) => {
elem.setAttribute(attr, value)
})
return elem
}
let mockaddSVGElementFromJsonCallCount = 0;
let mockaddSVGElementsFromJsonCallCount = 0
/**
* Mock of {@link module:utilities.EditorContext#addSVGElementFromJson}.
* Mock of {@link module:utilities.EditorContext#addSVGElementsFromJson}.
* @param {module:utilities.SVGElementJSON} json
* @returns {SVGElement}
*/
function mockaddSVGElementFromJson (json) {
const elem = mockCreateSVGElement(json);
svgroot.append(elem);
mockaddSVGElementFromJsonCallCount++;
return elem;
function mockaddSVGElementsFromJson (json) {
const elem = mockCreateSVGElement(json)
svgroot.append(elem)
mockaddSVGElementsFromJsonCallCount++
return elem
}
const mockPathActions = {
resetOrientation (pth) {
if (utilities.isNullish(pth) || pth.nodeName !== 'path') { return false; }
const tlist = pth.transform.baseVal;
const m = math.transformListToTransform(tlist).matrix;
tlist.clear();
pth.removeAttribute('transform');
const segList = pth.pathSegList;
if (utilities.isNullish(pth) || pth.nodeName !== 'path') { return false }
const tlist = pth.transform.baseVal
const m = math.transformListToTransform(tlist).matrix
tlist.clear()
pth.removeAttribute('transform')
const segList = pth.pathSegList
const len = segList.numberOfItems;
const len = segList.numberOfItems
// let lastX, lastY;
for (let i = 0; i < len; ++i) {
const seg = segList.getItem(i);
const type = seg.pathSegType;
if (type === 1) { continue; }
const seg = segList.getItem(i)
const type = seg.pathSegType
if (type === 1) { continue }
const pts = [];
[ '', 1, 2 ].forEach(function (n) {
const x = seg['x' + n]; const y = seg['y' + n];
['', 1, 2].forEach(function (n) {
const x = seg['x' + n]; const y = seg['y' + n]
if (x !== undefined && y !== undefined) {
const pt = math.transformPoint(x, y, m);
pts.splice(pts.length, 0, pt.x, pt.y);
const pt = math.transformPoint(x, y, m)
pts.splice(pts.length, 0, pt.x, pt.y)
}
});
path.replacePathSeg(type, i, pts, pth);
})
path.replacePathSeg(type, i, pts, pth)
}
// path.reorientGrads(pth, m);
return undefined;
return undefined
}
};
}
const EPSILON = 0.001;
const EPSILON = 0.001
let svgroot;
let svgroot
beforeEach(() => {
document.body.textContent = '';
document.body.textContent = ''
// const svg = document.createElementNS(NS.SVG, 'svg');
const sandbox = document.createElement('div');
sandbox.id = 'sandbox';
document.body.append(sandbox);
const sandbox = document.createElement('div')
sandbox.id = 'sandbox'
document.body.append(sandbox)
svgroot = mockCreateSVGElement({
element: 'svg',
attr: { id: 'svgroot' }
});
sandbox.append(svgroot);
})
sandbox.append(svgroot)
path.init(null);
mockaddSVGElementFromJsonCallCount = 0;
});
const mockSvgCanvas = {
createSVGElement (jsonMap) {
const elem = document.createElementNS(NS.SVG, jsonMap.element)
Object.entries(jsonMap.attr).forEach(([attr, value]) => {
elem.setAttribute(attr, value)
})
return elem
},
getSvgRoot () { return svgroot }
}
path.init(mockSvgCanvas)
mockaddSVGElementsFromJsonCallCount = 0
})
it('Test svgedit.utilities package', function () {
assert.ok(utilities);
assert.ok(utilities.getBBoxWithTransform);
assert.ok(utilities.getStrokedBBox);
assert.ok(utilities.getRotationAngleFromTransformList);
assert.ok(utilities.getRotationAngle);
});
assert.ok(utilities)
assert.ok(utilities.getBBoxWithTransform)
assert.ok(utilities.getStrokedBBox)
assert.ok(utilities.getRotationAngleFromTransformList)
assert.ok(utilities.getRotationAngle)
})
it('Test getBBoxWithTransform and no transform', function () {
const { getBBoxWithTransform } = utilities;
const { getBBoxWithTransform } = utilities
let elem = mockCreateSVGElement({
element: 'path',
attr: { id: 'path', d: 'M0,1 L2,3' }
});
svgroot.append(elem);
let bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 2, height: 2 });
assert.equal(mockaddSVGElementFromJsonCallCount, 0);
elem.remove();
})
svgroot.append(elem)
let bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 2, height: 2 })
assert.equal(mockaddSVGElementsFromJsonCallCount, 0)
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10' }
});
svgroot.append(elem);
bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 });
assert.equal(mockaddSVGElementFromJsonCallCount, 0);
elem.remove();
})
svgroot.append(elem)
bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 })
assert.equal(mockaddSVGElementsFromJsonCallCount, 0)
elem.remove()
elem = mockCreateSVGElement({
element: 'line',
attr: { id: 'line', x1: '0', y1: '1', x2: '5', y2: '6' }
});
svgroot.append(elem);
bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 5 });
assert.equal(mockaddSVGElementFromJsonCallCount, 0);
elem.remove();
})
svgroot.append(elem)
bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 5 })
assert.equal(mockaddSVGElementsFromJsonCallCount, 0)
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10' }
});
})
const g = mockCreateSVGElement({
element: 'g',
attr: {}
});
g.append(elem);
svgroot.append(g);
bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 });
assert.equal(mockaddSVGElementFromJsonCallCount, 0);
g.remove();
});
})
g.append(elem)
svgroot.append(g)
bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 })
assert.equal(mockaddSVGElementsFromJsonCallCount, 0)
g.remove()
})
it('Test getBBoxWithTransform and a rotation transform', function () {
const { getBBoxWithTransform } = utilities;
const { getBBoxWithTransform } = utilities
let elem = mockCreateSVGElement({
element: 'path',
attr: { id: 'path', d: 'M10,10 L20,20', transform: 'rotate(45 10,10)' }
});
svgroot.append(elem);
let bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
assert.close(bbox.x, 10, EPSILON);
assert.close(bbox.y, 10, EPSILON);
assert.close(bbox.width, 0, EPSILON);
assert.close(bbox.height, Math.sqrt(100 + 100), EPSILON);
elem.remove();
})
svgroot.append(elem)
let bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.close(bbox.x, 10, EPSILON)
assert.close(bbox.y, 10, EPSILON)
assert.close(bbox.width, 0, EPSILON)
assert.close(bbox.height, Math.sqrt(100 + 100), EPSILON)
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '10', y: '10', width: '10', height: '20', transform: 'rotate(90 15,20)' }
});
svgroot.append(elem);
bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
assert.close(bbox.x, 5, EPSILON);
assert.close(bbox.y, 15, EPSILON);
assert.close(bbox.width, 20, EPSILON);
assert.close(bbox.height, 10, EPSILON);
assert.equal(mockaddSVGElementFromJsonCallCount, 1);
elem.remove();
})
svgroot.append(elem)
bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.close(bbox.x, 5, EPSILON)
assert.close(bbox.y, 15, EPSILON)
assert.close(bbox.width, 20, EPSILON)
assert.close(bbox.height, 10, EPSILON)
assert.equal(mockaddSVGElementsFromJsonCallCount, 1)
elem.remove()
const rect = { x: 10, y: 10, width: 10, height: 20 };
const angle = 45;
const origin = { x: 15, y: 20 };
const rect = { x: 10, y: 10, width: 10, height: 20 }
const angle = 45
const origin = { x: 15, y: 20 }
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect2', x: rect.x, y: rect.y, width: rect.width, height: rect.height, transform: 'rotate(' + angle + ' ' + origin.x + ',' + origin.y + ')' }
});
svgroot.append(elem);
mockaddSVGElementFromJsonCallCount = 0;
bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
const r2 = rotateRect(rect, angle, origin);
assert.close(bbox.x, r2.x, EPSILON, 'rect2 x is ' + r2.x);
assert.close(bbox.y, r2.y, EPSILON, 'rect2 y is ' + r2.y);
assert.close(bbox.width, r2.width, EPSILON, 'rect2 width is' + r2.width);
assert.close(bbox.height, r2.height, EPSILON, 'rect2 height is ' + r2.height);
assert.equal(mockaddSVGElementFromJsonCallCount, 0);
elem.remove();
})
svgroot.append(elem)
mockaddSVGElementsFromJsonCallCount = 0
bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
const r2 = rotateRect(rect, angle, origin)
assert.close(bbox.x, r2.x, EPSILON, 'rect2 x is ' + r2.x)
assert.close(bbox.y, r2.y, EPSILON, 'rect2 y is ' + r2.y)
assert.close(bbox.width, r2.width, EPSILON, 'rect2 width is' + r2.width)
assert.close(bbox.height, r2.height, EPSILON, 'rect2 height is ' + r2.height)
assert.equal(mockaddSVGElementsFromJsonCallCount, 0)
elem.remove()
// Same as previous but wrapped with g and the transform is with the g.
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect3', x: rect.x, y: rect.y, width: rect.width, height: rect.height }
});
})
const g = mockCreateSVGElement({
element: 'g',
attr: { transform: 'rotate(' + angle + ' ' + origin.x + ',' + origin.y + ')' }
});
g.append(elem);
svgroot.append(g);
mockaddSVGElementFromJsonCallCount = 0;
bbox = getBBoxWithTransform(g, mockaddSVGElementFromJson, mockPathActions);
assert.close(bbox.x, r2.x, EPSILON, 'rect2 x is ' + r2.x);
assert.close(bbox.y, r2.y, EPSILON, 'rect2 y is ' + r2.y);
assert.close(bbox.width, r2.width, EPSILON, 'rect2 width is' + r2.width);
assert.close(bbox.height, r2.height, EPSILON, 'rect2 height is ' + r2.height);
assert.equal(mockaddSVGElementFromJsonCallCount, 0);
g.remove();
})
g.append(elem)
svgroot.append(g)
mockaddSVGElementsFromJsonCallCount = 0
bbox = getBBoxWithTransform(g, mockaddSVGElementsFromJson, mockPathActions)
assert.close(bbox.x, r2.x, EPSILON, 'rect2 x is ' + r2.x)
assert.close(bbox.y, r2.y, EPSILON, 'rect2 y is ' + r2.y)
assert.close(bbox.width, r2.width, EPSILON, 'rect2 width is' + r2.width)
assert.close(bbox.height, r2.height, EPSILON, 'rect2 height is ' + r2.height)
assert.equal(mockaddSVGElementsFromJsonCallCount, 0)
g.remove()
elem = mockCreateSVGElement({
element: 'ellipse',
attr: { id: 'ellipse1', cx: '100', cy: '100', rx: '50', ry: '50', transform: 'rotate(45 100,100)' }
});
svgroot.append(elem);
mockaddSVGElementFromJsonCallCount = 0;
bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
})
svgroot.append(elem)
mockaddSVGElementsFromJsonCallCount = 0
bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
/** @todo: Review these test the BBox algorithm is using the bezier control points to calculate the bounding box. Should be 50, 50, 100, 100. */
// assert.ok(bbox.x > 45 && bbox.x <= 50);
assert.ok(bbox.y > 45 && bbox.y <= 50);
assert.ok(bbox.y > 45 && bbox.y <= 50)
// assert.ok(bbox.width >= 100 && bbox.width < 110);
// assert.ok(bbox.height >= 100 && bbox.height < 110);
assert.equal(mockaddSVGElementFromJsonCallCount, 1);
elem.remove();
});
assert.equal(mockaddSVGElementsFromJsonCallCount, 1)
elem.remove()
})
it('Test getBBoxWithTransform with rotation and matrix transforms', function () {
const { getBBoxWithTransform } = utilities;
const { getBBoxWithTransform } = utilities
let tx = 10; // tx right
let ty = 10; // tx down
let txInRotatedSpace = Math.sqrt(tx * tx + ty * ty); // translate in rotated 45 space.
let tyInRotatedSpace = 0;
let matrix = 'matrix(1,0,0,1,' + txInRotatedSpace + ',' + tyInRotatedSpace + ')';
let tx = 10 // tx right
let ty = 10 // tx down
let txInRotatedSpace = Math.sqrt(tx * tx + ty * ty) // translate in rotated 45 space.
let tyInRotatedSpace = 0
let matrix = 'matrix(1,0,0,1,' + txInRotatedSpace + ',' + tyInRotatedSpace + ')'
let elem = mockCreateSVGElement({
element: 'path',
attr: { id: 'path', d: 'M10,10 L20,20', transform: 'rotate(45 10,10) ' + matrix }
});
svgroot.append(elem);
let bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
assert.close(bbox.x, 10 + tx, EPSILON);
assert.close(bbox.y, 10 + ty, EPSILON);
assert.close(bbox.width, 0, EPSILON);
assert.close(bbox.height, Math.sqrt(100 + 100), EPSILON);
elem.remove();
})
svgroot.append(elem)
let bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.close(bbox.x, 10 + tx, EPSILON)
assert.close(bbox.y, 10 + ty, EPSILON)
assert.close(bbox.width, 0, EPSILON)
assert.close(bbox.height, Math.sqrt(100 + 100), EPSILON)
elem.remove()
txInRotatedSpace = tx; // translate in rotated 90 space.
tyInRotatedSpace = -ty;
matrix = 'matrix(1,0,0,1,' + txInRotatedSpace + ',' + tyInRotatedSpace + ')';
txInRotatedSpace = tx // translate in rotated 90 space.
tyInRotatedSpace = -ty
matrix = 'matrix(1,0,0,1,' + txInRotatedSpace + ',' + tyInRotatedSpace + ')'
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '10', y: '10', width: '10', height: '20', transform: 'rotate(90 15,20) ' + matrix }
});
svgroot.append(elem);
bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
assert.close(bbox.x, 5 + tx, EPSILON);
assert.close(bbox.y, 15 + ty, EPSILON);
assert.close(bbox.width, 20, EPSILON);
assert.close(bbox.height, 10, EPSILON);
elem.remove();
})
svgroot.append(elem)
bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.close(bbox.x, 5 + tx, EPSILON)
assert.close(bbox.y, 15 + ty, EPSILON)
assert.close(bbox.width, 20, EPSILON)
assert.close(bbox.height, 10, EPSILON)
elem.remove()
const rect = { x: 10, y: 10, width: 10, height: 20 };
const angle = 45;
const origin = { x: 15, y: 20 };
tx = 10; // tx right
ty = 10; // tx down
txInRotatedSpace = Math.sqrt(tx * tx + ty * ty); // translate in rotated 45 space.
tyInRotatedSpace = 0;
matrix = 'matrix(1,0,0,1,' + txInRotatedSpace + ',' + tyInRotatedSpace + ')';
const rect = { x: 10, y: 10, width: 10, height: 20 }
const angle = 45
const origin = { x: 15, y: 20 }
tx = 10 // tx right
ty = 10 // tx down
txInRotatedSpace = Math.sqrt(tx * tx + ty * ty) // translate in rotated 45 space.
tyInRotatedSpace = 0
matrix = 'matrix(1,0,0,1,' + txInRotatedSpace + ',' + tyInRotatedSpace + ')'
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect2', x: rect.x, y: rect.y, width: rect.width, height: rect.height, transform: 'rotate(' + angle + ' ' + origin.x + ',' + origin.y + ') ' + matrix }
});
svgroot.append(elem);
bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
const r2 = rotateRect(rect, angle, origin);
assert.close(bbox.x, r2.x + tx, EPSILON, 'rect2 x is ' + r2.x);
assert.close(bbox.y, r2.y + ty, EPSILON, 'rect2 y is ' + r2.y);
assert.close(bbox.width, r2.width, EPSILON, 'rect2 width is' + r2.width);
assert.close(bbox.height, r2.height, EPSILON, 'rect2 height is ' + r2.height);
elem.remove();
})
svgroot.append(elem)
bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
const r2 = rotateRect(rect, angle, origin)
assert.close(bbox.x, r2.x + tx, EPSILON, 'rect2 x is ' + r2.x)
assert.close(bbox.y, r2.y + ty, EPSILON, 'rect2 y is ' + r2.y)
assert.close(bbox.width, r2.width, EPSILON, 'rect2 width is' + r2.width)
assert.close(bbox.height, r2.height, EPSILON, 'rect2 height is ' + r2.height)
elem.remove()
// Same as previous but wrapped with g and the transform is with the g.
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect3', x: rect.x, y: rect.y, width: rect.width, height: rect.height }
});
})
const g = mockCreateSVGElement({
element: 'g',
attr: { transform: 'rotate(' + angle + ' ' + origin.x + ',' + origin.y + ') ' + matrix }
});
g.append(elem);
svgroot.append(g);
bbox = getBBoxWithTransform(g, mockaddSVGElementFromJson, mockPathActions);
assert.close(bbox.x, r2.x + tx, EPSILON, 'rect2 x is ' + r2.x);
assert.close(bbox.y, r2.y + ty, EPSILON, 'rect2 y is ' + r2.y);
assert.close(bbox.width, r2.width, EPSILON, 'rect2 width is' + r2.width);
assert.close(bbox.height, r2.height, EPSILON, 'rect2 height is ' + r2.height);
g.remove();
})
g.append(elem)
svgroot.append(g)
bbox = getBBoxWithTransform(g, mockaddSVGElementsFromJson, mockPathActions)
assert.close(bbox.x, r2.x + tx, EPSILON, 'rect2 x is ' + r2.x)
assert.close(bbox.y, r2.y + ty, EPSILON, 'rect2 y is ' + r2.y)
assert.close(bbox.width, r2.width, EPSILON, 'rect2 width is' + r2.width)
assert.close(bbox.height, r2.height, EPSILON, 'rect2 height is ' + r2.height)
g.remove()
elem = mockCreateSVGElement({
element: 'ellipse',
attr: { id: 'ellipse1', cx: '100', cy: '100', rx: '50', ry: '50', transform: 'rotate(45 100,100) ' + matrix }
});
svgroot.append(elem);
bbox = getBBoxWithTransform(elem, mockaddSVGElementFromJson, mockPathActions);
})
svgroot.append(elem)
bbox = getBBoxWithTransform(elem, mockaddSVGElementsFromJson, mockPathActions)
/** @todo: the BBox algorithm is using the bezier control points to calculate the bounding box. Should be 50, 50, 100, 100. */
// assert.ok(bbox.x > 45 + tx && bbox.x <= 50 + tx);
assert.ok(bbox.y > 45 + ty && bbox.y <= 50 + ty);
assert.ok(bbox.y > 45 + ty && bbox.y <= 50 + ty)
// assert.ok(bbox.width >= 100 && bbox.width < 110);
// assert.ok(bbox.height >= 100 && bbox.height < 110);
elem.remove();
});
elem.remove()
})
it('Test getStrokedBBox with stroke-width 10', function () {
const { getStrokedBBox } = utilities;
const { getStrokedBBox } = utilities
const strokeWidth = 10;
const strokeWidth = 10
let elem = mockCreateSVGElement({
element: 'path',
attr: { id: 'path', d: 'M0,1 L2,3', 'stroke-width': strokeWidth }
});
svgroot.append(elem);
let bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0 - strokeWidth / 2, y: 1 - strokeWidth / 2, width: 2 + strokeWidth, height: 2 + strokeWidth });
elem.remove();
})
svgroot.append(elem)
let bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0 - strokeWidth / 2, y: 1 - strokeWidth / 2, width: 2 + strokeWidth, height: 2 + strokeWidth })
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10', 'stroke-width': strokeWidth }
});
svgroot.append(elem);
bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0 - strokeWidth / 2, y: 1 - strokeWidth / 2, width: 5 + strokeWidth, height: 10 + strokeWidth });
elem.remove();
})
svgroot.append(elem)
bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0 - strokeWidth / 2, y: 1 - strokeWidth / 2, width: 5 + strokeWidth, height: 10 + strokeWidth })
elem.remove()
elem = mockCreateSVGElement({
element: 'line',
attr: { id: 'line', x1: '0', y1: '1', x2: '5', y2: '6', 'stroke-width': strokeWidth }
});
svgroot.append(elem);
bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0 - strokeWidth / 2, y: 1 - strokeWidth / 2, width: 5 + strokeWidth, height: 5 + strokeWidth });
elem.remove();
})
svgroot.append(elem)
bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0 - strokeWidth / 2, y: 1 - strokeWidth / 2, width: 5 + strokeWidth, height: 5 + strokeWidth })
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10', 'stroke-width': strokeWidth }
});
})
const g = mockCreateSVGElement({
element: 'g',
attr: {}
});
g.append(elem);
svgroot.append(g);
bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0 - strokeWidth / 2, y: 1 - strokeWidth / 2, width: 5 + strokeWidth, height: 10 + strokeWidth });
g.remove();
});
})
g.append(elem)
svgroot.append(g)
bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0 - strokeWidth / 2, y: 1 - strokeWidth / 2, width: 5 + strokeWidth, height: 10 + strokeWidth })
g.remove()
})
it("Test getStrokedBBox with stroke-width 'none'", function () {
const { getStrokedBBox } = utilities;
const { getStrokedBBox } = utilities
let elem = mockCreateSVGElement({
element: 'path',
attr: { id: 'path', d: 'M0,1 L2,3', 'stroke-width': 'none' }
});
svgroot.append(elem);
let bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 2, height: 2 });
elem.remove();
})
svgroot.append(elem)
let bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 2, height: 2 })
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10', 'stroke-width': 'none' }
});
svgroot.append(elem);
bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 });
elem.remove();
})
svgroot.append(elem)
bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 })
elem.remove()
elem = mockCreateSVGElement({
element: 'line',
attr: { id: 'line', x1: '0', y1: '1', x2: '5', y2: '6', 'stroke-width': 'none' }
});
svgroot.append(elem);
bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 5 });
elem.remove();
})
svgroot.append(elem)
bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 5 })
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10', 'stroke-width': 'none' }
});
})
const g = mockCreateSVGElement({
element: 'g',
attr: {}
});
g.append(elem);
svgroot.append(g);
bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 });
g.remove();
});
})
g.append(elem)
svgroot.append(g)
bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 })
g.remove()
})
it('Test getStrokedBBox with no stroke-width attribute', function () {
const { getStrokedBBox } = utilities;
const { getStrokedBBox } = utilities
let elem = mockCreateSVGElement({
element: 'path',
attr: { id: 'path', d: 'M0,1 L2,3' }
});
svgroot.append(elem);
let bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 2, height: 2 });
elem.remove();
})
svgroot.append(elem)
let bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 2, height: 2 })
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10' }
});
svgroot.append(elem);
bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 });
elem.remove();
})
svgroot.append(elem)
bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 })
elem.remove()
elem = mockCreateSVGElement({
element: 'line',
attr: { id: 'line', x1: '0', y1: '1', x2: '5', y2: '6' }
});
svgroot.append(elem);
bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 5 });
elem.remove();
})
svgroot.append(elem)
bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 5 })
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10' }
});
})
const g = mockCreateSVGElement({
element: 'g',
attr: {}
});
g.append(elem);
svgroot.append(g);
bbox = getStrokedBBox([ elem ], mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 });
g.remove();
});
})
g.append(elem)
svgroot.append(g)
bbox = getStrokedBBox([elem], mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 })
g.remove()
})
/**
* Returns radians for degrees.
@ -455,7 +466,7 @@ describe('utilities bbox', function () {
* @returns {Float}
*/
function radians (degrees) {
return degrees * Math.PI / 180;
return degrees * Math.PI / 180
}
/**
@ -467,15 +478,15 @@ describe('utilities bbox', function () {
*/
function rotatePoint (point, angle, origin) {
if (!origin) {
origin = { x: 0, y: 0 };
origin = { x: 0, y: 0 }
}
const x = point.x - origin.x;
const y = point.y - origin.y;
const theta = radians(angle);
const x = point.x - origin.x
const y = point.y - origin.y
const theta = radians(angle)
return {
x: x * Math.cos(theta) + y * Math.sin(theta) + origin.x,
y: x * Math.sin(theta) + y * Math.cos(theta) + origin.y
};
}
}
/**
*
@ -485,21 +496,21 @@ describe('utilities bbox', function () {
* @returns {module:utilities.BBoxObject}
*/
function rotateRect (rect, angle, origin) {
const tl = rotatePoint({ x: rect.x, y: rect.y }, angle, origin);
const tr = rotatePoint({ x: rect.x + rect.width, y: rect.y }, angle, origin);
const br = rotatePoint({ x: rect.x + rect.width, y: rect.y + rect.height }, angle, origin);
const bl = rotatePoint({ x: rect.x, y: rect.y + rect.height }, angle, origin);
const tl = rotatePoint({ x: rect.x, y: rect.y }, angle, origin)
const tr = rotatePoint({ x: rect.x + rect.width, y: rect.y }, angle, origin)
const br = rotatePoint({ x: rect.x + rect.width, y: rect.y + rect.height }, angle, origin)
const bl = rotatePoint({ x: rect.x, y: rect.y + rect.height }, angle, origin)
const minx = Math.min(tl.x, tr.x, bl.x, br.x);
const maxx = Math.max(tl.x, tr.x, bl.x, br.x);
const miny = Math.min(tl.y, tr.y, bl.y, br.y);
const maxy = Math.max(tl.y, tr.y, bl.y, br.y);
const minx = Math.min(tl.x, tr.x, bl.x, br.x)
const maxx = Math.max(tl.x, tr.x, bl.x, br.x)
const miny = Math.min(tl.y, tr.y, bl.y, br.y)
const maxy = Math.max(tl.y, tr.y, bl.y, br.y)
return {
x: minx,
y: miny,
width: (maxx - minx),
height: (maxy - miny)
};
}
}
});
})

View File

@ -1,17 +1,17 @@
/* eslint-disable max-len, no-console */
import 'pathseg';
import 'pathseg'
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as math from '../../../instrumented/svgcanvas/math.js';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
import * as utilities from '../../../instrumented/svgcanvas/utilities.js'
import * as math from '../../../instrumented/svgcanvas/math.js'
describe('utilities performance', function () {
let currentLayer; let groupWithMatrixTransform; let textWithMatrixTransform;
let currentLayer; let groupWithMatrixTransform; let textWithMatrixTransform
beforeEach(() => {
document.body.textContent = '';
const style = document.createElement('style');
style.id = 'styleoverrides';
style.media = 'screen';
document.body.textContent = ''
const style = document.createElement('style')
style.id = 'styleoverrides'
style.media = 'screen'
style.textContent = `
#svgcanvas svg * {
cursor: move;
@ -19,9 +19,9 @@ describe('utilities performance', function () {
}
#svgcanvas svg {
cursor: default
}`;
}`
document.head.append(style);
document.head.append(style)
const editor = new DOMParser().parseFromString(`<div id="svg_editor">
<div id="workarea" style="cursor: auto; overflow: scroll; line-height: 12px; right: 100px;">
@ -64,14 +64,14 @@ describe('utilities performance', function () {
</svg>
</svg>
</div>
</div></div>`, 'application/xml');
const newNode = document.body.ownerDocument.importNode(editor.documentElement, true);
document.body.append(newNode);
</div></div>`, 'application/xml')
const newNode = document.body.ownerDocument.importNode(editor.documentElement, true)
document.body.append(newNode)
currentLayer = document.getElementById('layer1');
groupWithMatrixTransform = document.getElementById('svg_group_with_matrix_transform');
textWithMatrixTransform = document.getElementById('svg_text_with_matrix_transform');
});
currentLayer = document.getElementById('layer1')
groupWithMatrixTransform = document.getElementById('svg_group_with_matrix_transform')
textWithMatrixTransform = document.getElementById('svg_text_with_matrix_transform')
})
/**
* Create an SVG element for a mock.
@ -79,22 +79,22 @@ describe('utilities performance', function () {
* @returns {SVGElement}
*/
function mockCreateSVGElement (jsonMap) {
const elem = document.createElementNS(NS.SVG, jsonMap.element);
Object.entries(jsonMap.attr).forEach(([ attr, value ]) => {
elem.setAttribute(attr, value);
});
return elem;
const elem = document.createElementNS(NS.SVG, jsonMap.element)
Object.entries(jsonMap.attr).forEach(([attr, value]) => {
elem.setAttribute(attr, value)
})
return elem
}
/**
* Mock of {@link module:utilities.EditorContext#addSVGElementFromJson}.
* Mock of {@link module:utilities.EditorContext#addSVGElementsFromJson}.
* @param {module:utilities.SVGElementJSON} json
* @returns {SVGElement}
*/
function mockaddSVGElementFromJson (json) {
const elem = mockCreateSVGElement(json);
currentLayer.append(elem);
return elem;
function mockaddSVGElementsFromJson (json) {
const elem = mockCreateSVGElement(json)
currentLayer.append(elem)
return elem
}
/**
@ -104,50 +104,49 @@ describe('utilities performance', function () {
* @returns {void}
*/
function fillDocumentByCloningElement (elem, count) {
const elemId = elem.getAttribute('id') + '-';
const elemId = elem.getAttribute('id') + '-'
for (let index = 0; index < count; index++) {
const clone = elem.cloneNode(true); // t: deep clone
const clone = elem.cloneNode(true) // t: deep clone
// Make sure you set a unique ID like a real document.
clone.setAttribute('id', elemId + index);
const { parentNode } = elem;
parentNode.append(clone);
clone.setAttribute('id', elemId + index)
const { parentNode } = elem
parentNode.append(clone)
}
}
const mockPathActions = {
resetOrientation (path) {
if (utilities.isNullish(path) || path.nodeName !== 'path') { return false; }
const tlist = path.transform.baseVal;
const m = math.transformListToTransform(tlist).matrix;
tlist.clear();
path.removeAttribute('transform');
const segList = path.pathSegList;
if (utilities.isNullish(path) || path.nodeName !== 'path') { return false }
const tlist = path.transform.baseVal
const m = math.transformListToTransform(tlist).matrix
tlist.clear()
path.removeAttribute('transform')
const segList = path.pathSegList
const len = segList.numberOfItems;
const len = segList.numberOfItems
// let lastX, lastY;
for (let i = 0; i < len; ++i) {
const seg = segList.getItem(i);
const type = seg.pathSegType;
const seg = segList.getItem(i)
const type = seg.pathSegType
if (type === 1) {
continue;
continue
}
const pts = [];
[ '', 1, 2 ].forEach(function (n) {
const x = seg['x' + n];
const y = seg['y' + n];
['', 1, 2].forEach(function (n) {
const x = seg['x' + n]
const y = seg['y' + n]
if (x !== undefined && y !== undefined) {
const pt = math.transformPoint(x, y, m);
pts.splice(pts.length, 0, pt.x, pt.y);
const pt = math.transformPoint(x, y, m)
pts.splice(pts.length, 0, pt.x, pt.y)
}
});
})
// path.replacePathSeg(type, i, pts, path);
}
// utilities.reorientGrads(path, m);
return undefined;
return undefined
}
};
}
// //////////////////////////////////////////////////////////
// Performance times with various browsers on Macbook 2011 8MB RAM OS X El Capitan 10.11.4
@ -181,57 +180,57 @@ describe('utilities performance', function () {
// Pass2 svgCanvas.getStrokedBBox total ms 17, ave ms 0.2, min/max 0 23
it('Test svgCanvas.getStrokedBBox() performance with matrix transforms', function () {
const { getStrokedBBox } = utilities;
const { children } = currentLayer;
const { getStrokedBBox } = utilities
const { children } = currentLayer
let lastTime; let now;
let min = Number.MAX_VALUE;
let max = 0;
let total = 0;
let lastTime; let now
let min = Number.MAX_VALUE
let max = 0
let total = 0
fillDocumentByCloningElement(groupWithMatrixTransform, 50);
fillDocumentByCloningElement(textWithMatrixTransform, 50);
fillDocumentByCloningElement(groupWithMatrixTransform, 50)
fillDocumentByCloningElement(textWithMatrixTransform, 50)
// The first pass through all elements is slower.
const count = children.length;
const start = lastTime = now = Date.now();
const count = children.length
const start = lastTime = now = Date.now()
// Skip the first child which is the title.
for (let index = 1; index < count; index++) {
const child = children[index];
/* const obj = */ getStrokedBBox([ child ], mockaddSVGElementFromJson, mockPathActions);
now = Date.now(); const delta = now - lastTime; lastTime = now;
total += delta;
min = Math.min(min, delta);
max = Math.max(max, delta);
const child = children[index]
/* const obj = */ getStrokedBBox([child], mockaddSVGElementsFromJson, mockPathActions)
now = Date.now(); const delta = now - lastTime; lastTime = now
total += delta
min = Math.min(min, delta)
max = Math.max(max, delta)
}
total = lastTime - start;
const ave = total / count;
assert.isBelow(ave, 20, 'svgedit.utilities.getStrokedBBox average execution time is less than 20 ms');
console.log('Pass1 svgCanvas.getStrokedBBox total ms ' + total + ', ave ms ' + ave.toFixed(1) + ',\t min/max ' + min + ' ' + max);
total = lastTime - start
const ave = total / count
assert.isBelow(ave, 20, 'svgedit.utilities.getStrokedBBox average execution time is less than 20 ms')
console.log('Pass1 svgCanvas.getStrokedBBox total ms ' + total + ', ave ms ' + ave.toFixed(1) + ',\t min/max ' + min + ' ' + max)
return new Promise((resolve) => {
// The second pass is two to ten times faster.
setTimeout(function () {
const ct = children.length;
const ct = children.length
const strt = lastTime = now = Date.now();
const strt = lastTime = now = Date.now()
// Skip the first child which is the title.
for (let index = 1; index < ct; index++) {
const child = children[index];
/* const obj = */ getStrokedBBox([ child ], mockaddSVGElementFromJson, mockPathActions);
now = Date.now(); const delta = now - lastTime; lastTime = now;
total += delta;
min = Math.min(min, delta);
max = Math.max(max, delta);
const child = children[index]
/* const obj = */ getStrokedBBox([child], mockaddSVGElementsFromJson, mockPathActions)
now = Date.now(); const delta = now - lastTime; lastTime = now
total += delta
min = Math.min(min, delta)
max = Math.max(max, delta)
}
total = lastTime - strt;
const avg = total / ct;
assert.isBelow(avg, 2, 'svgedit.utilities.getStrokedBBox average execution time is less than 1 ms');
console.log('Pass2 svgCanvas.getStrokedBBox total ms ' + total + ', ave ms ' + avg.toFixed(1) + ',\t min/max ' + min + ' ' + max);
total = lastTime - strt
const avg = total / ct
assert.isBelow(avg, 2, 'svgedit.utilities.getStrokedBBox average execution time is less than 1 ms')
console.log('Pass2 svgCanvas.getStrokedBBox total ms ' + total + ', ave ms ' + avg.toFixed(1) + ',\t min/max ' + min + ' ' + max)
resolve();
});
});
});
});
resolve()
})
})
})
})

View File

@ -1,5 +1,5 @@
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import { NS } from '../../../instrumented/svgcanvas/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js'
import { NS } from '../../../instrumented/svgcanvas/namespaces.js'
describe('utilities', function () {
/**
@ -8,326 +8,335 @@ describe('utilities', function () {
* @returns {SVGElement}
*/
function mockCreateSVGElement (jsonMap) {
const elem = document.createElementNS(NS.SVG, jsonMap.element);
Object.entries(jsonMap.attr).forEach(([ attr, value ]) => {
elem.setAttribute(attr, value);
});
return elem;
const elem = document.createElementNS(NS.SVG, jsonMap.element)
Object.entries(jsonMap.attr).forEach(([attr, value]) => {
elem.setAttribute(attr, value)
})
return elem
}
/**
* Adds SVG Element per parameters and appends to root.
* @param {module:utilities.SVGElementJSON} json
* @returns {SVGElement}
*/
function mockaddSVGElementFromJson (json) {
const elem = mockCreateSVGElement(json);
svgroot.append(elem);
return elem;
function mockaddSVGElementsFromJson (json) {
const elem = mockCreateSVGElement(json)
svgroot.append(elem)
return elem
}
const mockPathActions = { resetOrientation () { /* empty fn */ } };
let mockHistorySubCommands = [];
const mockPathActions = { resetOrientation () { /* empty fn */ } }
let mockHistorySubCommands = []
const mockHistory = {
BatchCommand: class {
addSubCommand (cmd) {
mockHistorySubCommands.push(cmd);
mockHistorySubCommands.push(cmd)
}
},
RemoveElementCommand: class {
// Longhand needed since used as a constructor
constructor (elem, nextSibling, parent) {
this.elem = elem;
this.nextSibling = nextSibling;
this.parent = parent;
this.elem = elem
this.nextSibling = nextSibling
this.parent = parent
}
},
InsertElementCommand: class {
constructor (path) { // Longhand needed since used as a constructor
this.path = path;
this.path = path
}
}
};
}
const mockCount = {
clearSelection: 0,
addToSelection: 0,
addCommandToHistory: 0
};
}
/**
* Increments clear seleciton count for mock test.
* @returns {void}
*/
function mockClearSelection () {
mockCount.clearSelection++;
mockCount.clearSelection++
}
/**
* Increments add selection count for mock test.
* @returns {void}
*/
function mockAddToSelection () {
mockCount.addToSelection++;
mockCount.addToSelection++
}
/**
* Increments add command to history count for mock test.
* @returns {void}
*/
function mockAddCommandToHistory () {
mockCount.addCommandToHistory++;
mockCount.addCommandToHistory++
}
let svg; let svgroot;
const mockSvgCanvas = {
addSVGElementsFromJson: mockaddSVGElementsFromJson,
pathActions: mockPathActions,
clearSelection: mockClearSelection,
addToSelection: mockAddToSelection,
history: mockHistory,
addCommandToHistory: mockAddCommandToHistory
}
let svg; let svgroot
beforeEach(() => {
document.body.textContent = '';
document.body.textContent = ''
mockHistorySubCommands = [];
mockCount.clearSelection = 0;
mockCount.addToSelection = 0;
mockCount.addCommandToHistory = 0;
mockHistorySubCommands = []
mockCount.clearSelection = 0
mockCount.addToSelection = 0
mockCount.addCommandToHistory = 0
const sandbox = document.createElement('div');
svg = document.createElementNS(NS.SVG, 'svg');
const sandbox = document.createElement('div')
svg = document.createElementNS(NS.SVG, 'svg')
svgroot = mockCreateSVGElement({
element: 'svg',
attr: { id: 'svgroot' }
});
sandbox.append(svgroot);
document.body.append(sandbox);
});
})
sandbox.append(svgroot)
document.body.append(sandbox)
})
it('Test svgedit.utilities package', function () {
assert.ok(utilities);
assert.ok(utilities.toXml);
assert.equal(typeof utilities.toXml, typeof function () { /* empty fn */ });
});
assert.ok(utilities)
assert.ok(utilities.toXml)
assert.equal(typeof utilities.toXml, typeof function () { /* empty fn */ })
})
it('Test svgedit.utilities.toXml() function', function () {
const { toXml } = utilities;
const { toXml } = utilities
assert.equal(toXml('a'), 'a');
assert.equal(toXml('ABC_'), 'ABC_');
assert.equal(toXml('PB&J'), 'PB&amp;J');
assert.equal(toXml('2 < 5'), '2 &lt; 5');
assert.equal(toXml('5 > 2'), '5 &gt; 2');
assert.equal(toXml('\'<&>"'), '&#x27;&lt;&amp;&gt;&quot;');
});
assert.equal(toXml('a'), 'a')
assert.equal(toXml('ABC_'), 'ABC_')
assert.equal(toXml('PB&J'), 'PB&amp;J')
assert.equal(toXml('2 < 5'), '2 &lt; 5')
assert.equal(toXml('5 > 2'), '5 &gt; 2')
assert.equal(toXml('\'<&>"'), '&#x27;&lt;&amp;&gt;&quot;')
})
it('Test svgedit.utilities.encode64() function', function () {
const { encode64 } = utilities;
const { encode64 } = utilities
assert.equal(encode64('abcdef'), 'YWJjZGVm');
assert.equal(encode64('12345'), 'MTIzNDU=');
assert.equal(encode64(' '), 'IA==');
assert.equal(encode64('`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?'), 'YH4hQCMkJV4mKigpLV89K1t7XX1cfDs6JyIsPC4+Lz8=');
});
assert.equal(encode64('abcdef'), 'YWJjZGVm')
assert.equal(encode64('12345'), 'MTIzNDU=')
assert.equal(encode64(' '), 'IA==')
assert.equal(encode64('`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?'), 'YH4hQCMkJV4mKigpLV89K1t7XX1cfDs6JyIsPC4+Lz8=')
})
it('Test svgedit.utilities.decode64() function', function () {
const { decode64 } = utilities;
const { decode64 } = utilities
assert.equal(decode64('YWJjZGVm'), 'abcdef');
assert.equal(decode64('MTIzNDU='), '12345');
assert.equal(decode64('IA=='), ' ');
assert.equal(decode64('YH4hQCMkJV4mKigpLV89K1t7XX1cfDs6JyIsPC4+Lz8='), '`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?');
});
assert.equal(decode64('YWJjZGVm'), 'abcdef')
assert.equal(decode64('MTIzNDU='), '12345')
assert.equal(decode64('IA=='), ' ')
assert.equal(decode64('YH4hQCMkJV4mKigpLV89K1t7XX1cfDs6JyIsPC4+Lz8='), '`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?')
})
it('Test svgedit.utilities.convertToXMLReferences() function', function () {
const convert = utilities.convertToXMLReferences;
assert.equal(convert('ABC'), 'ABC');
const convert = utilities.convertToXMLReferences
assert.equal(convert('ABC'), 'ABC')
// assert.equal(convert('<27>BC'), '&#192;BC');
});
})
it('Test svgedit.utilities.bboxToObj() function', function () {
const { bboxToObj } = utilities;
const { bboxToObj } = utilities
const rect = svg.createSVGRect();
rect.x = 1;
rect.y = 2;
rect.width = 3;
rect.height = 4;
const rect = svg.createSVGRect()
rect.x = 1
rect.y = 2
rect.width = 3
rect.height = 4
const obj = bboxToObj(rect);
assert.equal(typeof obj, typeof {});
assert.equal(obj.x, 1);
assert.equal(obj.y, 2);
assert.equal(obj.width, 3);
assert.equal(obj.height, 4);
});
const obj = bboxToObj(rect)
assert.equal(typeof obj, typeof {})
assert.equal(obj.x, 1)
assert.equal(obj.y, 2)
assert.equal(obj.width, 3)
assert.equal(obj.height, 4)
})
it('Test getUrlFromAttr', function () {
assert.equal(utilities.getUrlFromAttr('url(#foo)'), '#foo');
assert.equal(utilities.getUrlFromAttr('url(somefile.svg#foo)'), 'somefile.svg#foo');
assert.equal(utilities.getUrlFromAttr('url("#foo")'), '#foo');
assert.equal(utilities.getUrlFromAttr('url("#foo")'), '#foo');
});
assert.equal(utilities.getUrlFromAttr('url(#foo)'), '#foo')
assert.equal(utilities.getUrlFromAttr('url(somefile.svg#foo)'), 'somefile.svg#foo')
assert.equal(utilities.getUrlFromAttr('url("#foo")'), '#foo')
assert.equal(utilities.getUrlFromAttr('url("#foo")'), '#foo')
})
it('Test getPathDFromSegments', function () {
const { getPathDFromSegments } = utilities;
const { getPathDFromSegments } = utilities
const doc = utilities.text2xml('<svg></svg>');
const path = doc.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'm0,0l5,0l0,5l-5,0l0,-5z');
const doc = utilities.text2xml('<svg></svg>')
const path = doc.createElementNS(NS.SVG, 'path')
path.setAttribute('d', 'm0,0l5,0l0,5l-5,0l0,-5z')
let d = getPathDFromSegments([
[ 'M', [ 1, 2 ] ],
[ 'Z', [] ]
]);
assert.equal(d, 'M1,2 Z');
['M', [1, 2]],
['Z', []]
])
assert.equal(d, 'M1,2 Z')
d = getPathDFromSegments([
[ 'M', [ 1, 2 ] ],
[ 'M', [ 3, 4 ] ],
[ 'Z', [] ]
]);
assert.equal(d, 'M1,2 M3,4 Z');
['M', [1, 2]],
['M', [3, 4]],
['Z', []]
])
assert.equal(d, 'M1,2 M3,4 Z')
d = getPathDFromSegments([
[ 'M', [ 1, 2 ] ],
[ 'C', [ 3, 4, 5, 6 ] ],
[ 'Z', [] ]
]);
assert.equal(d, 'M1,2 C3,4 5,6 Z');
});
['M', [1, 2]],
['C', [3, 4, 5, 6]],
['Z', []]
])
assert.equal(d, 'M1,2 C3,4 5,6 Z')
})
it('Test getPathDFromElement', function () {
const { getPathDFromElement } = utilities;
const { getPathDFromElement } = utilities
let elem = mockCreateSVGElement({
element: 'path',
attr: { id: 'path', d: 'M0,1 Z' }
});
svgroot.append(elem);
assert.equal(getPathDFromElement(elem), 'M0,1 Z');
elem.remove();
})
svgroot.append(elem)
assert.equal(getPathDFromElement(elem), 'M0,1 Z')
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10' }
});
svgroot.append(elem);
assert.equal(getPathDFromElement(elem), 'M0,1 L5,1 L5,11 L0,11 L0,1 Z');
elem.remove();
})
svgroot.append(elem)
assert.equal(getPathDFromElement(elem), 'M0,1 L5,1 L5,11 L0,11 L0,1 Z')
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'roundrect', x: '0', y: '1', rx: '2', ry: '3', width: '10', height: '11' }
});
svgroot.append(elem);
const closeEnough = /M0,4 C0,2.3\d* 0.9\d*,1 2,1 L8,1 C9.0\d*,1 10,2.3\d* 10,4 L10,9 C10,10.6\d* 9.0\d*,12 8,12 L2,12 C0.9\d*,12 0,10.6\d* 0,9 L0,4 Z/;
assert.equal(closeEnough.test(getPathDFromElement(elem)), true);
elem.remove();
})
svgroot.append(elem)
const closeEnough = /M0,4 C0,2.3\d* 0.9\d*,1 2,1 L8,1 C9.0\d*,1 10,2.3\d* 10,4 L10,9 C10,10.6\d* 9.0\d*,12 8,12 L2,12 C0.9\d*,12 0,10.6\d* 0,9 L0,4 Z/
assert.equal(closeEnough.test(getPathDFromElement(elem)), true)
elem.remove()
elem = mockCreateSVGElement({
element: 'line',
attr: { id: 'line', x1: '0', y1: '1', x2: '5', y2: '6' }
});
svgroot.append(elem);
assert.equal(getPathDFromElement(elem), 'M0,1L5,6');
elem.remove();
})
svgroot.append(elem)
assert.equal(getPathDFromElement(elem), 'M0,1L5,6')
elem.remove()
elem = mockCreateSVGElement({
element: 'circle',
attr: { id: 'circle', cx: '10', cy: '11', rx: '5', ry: '10' }
});
svgroot.append(elem);
assert.equal(getPathDFromElement(elem), 'M5,11 C5,5.475138121546961 7.237569060773481,1 10,1 C12.762430939226519,1 15,5.475138121546961 15,11 C15,16.524861878453038 12.762430939226519,21 10,21 C7.237569060773481,21 5,16.524861878453038 5,11 Z');
elem.remove();
})
svgroot.append(elem)
assert.equal(getPathDFromElement(elem), 'M5,11 C5,5.475138121546961 7.237569060773481,1 10,1 C12.762430939226519,1 15,5.475138121546961 15,11 C15,16.524861878453038 12.762430939226519,21 10,21 C7.237569060773481,21 5,16.524861878453038 5,11 Z')
elem.remove()
elem = mockCreateSVGElement({
element: 'polyline',
attr: { id: 'polyline', points: '0,1 5,1 5,11 0,11' }
});
svgroot.append(elem);
assert.equal(getPathDFromElement(elem), 'M0,1 5,1 5,11 0,11');
elem.remove();
})
svgroot.append(elem)
assert.equal(getPathDFromElement(elem), 'M0,1 5,1 5,11 0,11')
elem.remove()
assert.equal(getPathDFromElement({ tagName: 'something unknown' }), undefined);
});
assert.equal(getPathDFromElement({ tagName: 'something unknown' }), undefined)
})
it('Test getBBoxOfElementAsPath', function () {
/**
* Wrap `utilities.getBBoxOfElementAsPath` to convert bbox to object for testing.
* @type {module:utilities.getBBoxOfElementAsPath}
*/
function getBBoxOfElementAsPath (elem, addSVGElementFromJson, pathActions) {
const bbox = utilities.getBBoxOfElementAsPath(elem, addSVGElementFromJson, pathActions);
return utilities.bboxToObj(bbox); // need this for assert.equal() to work.
function getBBoxOfElementAsPath (elem, addSVGElementsFromJson, pathActions) {
const bbox = utilities.getBBoxOfElementAsPath(elem, addSVGElementsFromJson, pathActions)
return utilities.bboxToObj(bbox) // need this for assert.equal() to work.
}
let elem = mockCreateSVGElement({
element: 'path',
attr: { id: 'path', d: 'M0,1 Z' }
});
svgroot.append(elem);
let bbox = getBBoxOfElementAsPath(elem, mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 0, height: 0 });
elem.remove();
})
svgroot.append(elem)
let bbox = getBBoxOfElementAsPath(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 0, height: 0 })
elem.remove()
elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10' }
});
svgroot.append(elem);
bbox = getBBoxOfElementAsPath(elem, mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 });
elem.remove();
})
svgroot.append(elem)
bbox = getBBoxOfElementAsPath(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 10 })
elem.remove()
elem = mockCreateSVGElement({
element: 'line',
attr: { id: 'line', x1: '0', y1: '1', x2: '5', y2: '6' }
});
svgroot.append(elem);
bbox = getBBoxOfElementAsPath(elem, mockaddSVGElementFromJson, mockPathActions);
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 5 });
elem.remove();
})
svgroot.append(elem)
bbox = getBBoxOfElementAsPath(elem, mockaddSVGElementsFromJson, mockPathActions)
assert.deepEqual(bbox, { x: 0, y: 1, width: 5, height: 5 })
elem.remove()
// TODO: test element with transform. Need resetOrientation above to be working or mock it.
});
})
it('Test convertToPath rect', function () {
const { convertToPath } = utilities;
const { convertToPath } = utilities
const attrs = {
fill: 'red',
stroke: 'white',
'stroke-width': '1',
visibility: 'hidden'
};
}
const elem = mockCreateSVGElement({
element: 'rect',
attr: { id: 'rect', x: '0', y: '1', width: '5', height: '10' }
});
svgroot.append(elem);
const path = convertToPath(elem, attrs, mockaddSVGElementFromJson, mockPathActions, mockClearSelection, mockAddToSelection, mockHistory, mockAddCommandToHistory);
assert.equal(path.getAttribute('d'), 'M0,1 L5,1 L5,11 L0,11 L0,1 Z');
assert.equal(path.getAttribute('visibilituy'), null);
assert.equal(path.id, 'rect');
assert.equal(path.parentNode, svgroot);
assert.equal(elem.parentNode, null);
assert.equal(mockHistorySubCommands.length, 2);
assert.equal(mockCount.clearSelection, 1);
assert.equal(mockCount.addToSelection, 1);
assert.equal(mockCount.addCommandToHistory, 1);
path.remove();
});
})
svgroot.append(elem)
const path = convertToPath(elem, attrs, mockSvgCanvas)
assert.equal(path.getAttribute('d'), 'M0,1 L5,1 L5,11 L0,11 L0,1 Z')
assert.equal(path.getAttribute('visibilituy'), null)
assert.equal(path.id, 'rect')
assert.equal(path.parentNode, svgroot)
assert.equal(elem.parentNode, null)
assert.equal(mockHistorySubCommands.length, 2)
assert.equal(mockCount.clearSelection, 1)
assert.equal(mockCount.addToSelection, 1)
assert.equal(mockCount.addCommandToHistory, 1)
path.remove()
})
it('Test convertToPath unknown element', function () {
const { convertToPath } = utilities;
const { convertToPath } = utilities
const attrs = {
fill: 'red',
stroke: 'white',
'stroke-width': '1',
visibility: 'hidden'
};
}
const elem = {
tagName: 'something unknown',
id: 'something-unknown',
getAttribute () { return ''; },
getAttribute () { return '' },
parentNode: svgroot
};
const path = convertToPath(elem, attrs, mockaddSVGElementFromJson, mockPathActions, mockClearSelection, mockAddToSelection, mockHistory, mockAddCommandToHistory);
assert.equal(path, null);
assert.equal(elem.parentNode, svgroot);
assert.equal(mockHistorySubCommands.length, 0);
assert.equal(mockCount.clearSelection, 0);
assert.equal(mockCount.addToSelection, 0);
assert.equal(mockCount.addCommandToHistory, 0);
});
});
}
const path = convertToPath(elem, attrs, mockSvgCanvas)
assert.equal(path, null)
assert.equal(elem.parentNode, svgroot)
assert.equal(mockHistorySubCommands.length, 0)
assert.equal(mockCount.clearSelection, 0)
assert.equal(mockCount.addToSelection, 0)
assert.equal(mockCount.addCommandToHistory, 0)
})
})

View File

@ -1,4 +1,4 @@
'use strict';
'use strict'
// ***********************************************************
// This example plugins/index.js can be used to load plugins
@ -11,6 +11,6 @@
// ***********************************************************
require('@babel/register')({
plugins: [ '@babel/plugin-transform-modules-commonjs' ]
});
module.exports = require('./main.js').default;
plugins: ['@babel/plugin-transform-modules-commonjs']
})
module.exports = require('./main.js').default

View File

@ -1,8 +1,8 @@
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
import codeCoverageTask from "@cypress/code-coverage/task.js";
import { initPlugin } from "cypress-plugin-snapshots/plugin.js";
import codeCoverageTask from '@cypress/code-coverage/task.js'
import { initPlugin } from 'cypress-plugin-snapshots/plugin.js'
export default (on, config) => {
// `on` is used to hook into various events Cypress emits
@ -12,35 +12,35 @@ export default (on, config) => {
// `config` is the resolved Cypress config
// https://docs.cypress.io/guides/tooling/code-coverage.html#Install-the-plugin
codeCoverageTask(on, config);
initPlugin(on, config);
on("before:browser:launch", (browser, launchOptions) => {
if (browser.name === "chrome" && browser.isHeadless) {
codeCoverageTask(on, config)
initPlugin(on, config)
on('before:browser:launch', (browser, launchOptions) => {
if (browser.name === 'chrome' && browser.isHeadless) {
// fullPage screenshot size is 1400x1200 on non-retina screens
// and 2800x2400 on retina screens
launchOptions.args.push("--window-size=1400,1200");
launchOptions.args.push('--window-size=1400,1200')
// force screen to be non-retina (1400x1200 size)
launchOptions.args.push("--force-device-scale-factor=1");
launchOptions.args.push('--force-device-scale-factor=1')
// force screen to be retina (2800x2400 size)
// launchOptions.args.push('--force-device-scale-factor=2')
}
if (browser.name === "electron" && browser.isHeadless) {
if (browser.name === 'electron' && browser.isHeadless) {
// fullPage screenshot size is 1400x1200
launchOptions.preferences.width = 1400;
launchOptions.preferences.height = 1200;
launchOptions.preferences.width = 1400
launchOptions.preferences.height = 1200
}
if (browser.name === "firefox" && browser.isHeadless) {
if (browser.name === 'firefox' && browser.isHeadless) {
// menubars take up height on the screen
// so fullPage screenshot size is 1400x1126
launchOptions.args.push("--width=1400");
launchOptions.args.push("--height=1200");
launchOptions.args.push('--width=1400')
launchOptions.args.push('--height=1200')
}
return launchOptions;
});
return config;
};
return launchOptions
})
return config
}

View File

@ -1,6 +1,6 @@
import assertionWrapper from './assertion-wrapper.js';
import assertionWrapper from './assertion-wrapper.js'
const NEAR_ZERO = 5e-6; // 0.000005, Firefox fails at higher levels of precision.
const NEAR_ZERO = 5e-6 // 0.000005, Firefox fails at higher levels of precision.
/**
* Checks that the supplied values are equal with a high though not absolute degree of precision.
@ -10,9 +10,9 @@ const NEAR_ZERO = 5e-6; // 0.000005, Firefox fails at higher levels of precision
* @returns {void}
*/
function almostEquals (actual, expected, message) {
message = message || (actual + ' did not equal ' + expected);
const result = Math.abs(actual - expected) < NEAR_ZERO;
return { result, message, actual, expected };
message = message || (actual + ' did not equal ' + expected)
const result = Math.abs(actual - expected) < NEAR_ZERO
return { result, message, actual, expected }
}
/**
@ -21,9 +21,9 @@ function almostEquals (actual, expected, message) {
* @returns {void}
*/
function setAssertionMethods (_chai, utils) {
const wrap = assertionWrapper(_chai, utils);
const wrap = assertionWrapper(_chai, utils)
assert.almostEquals = wrap(almostEquals);
assert.almostEquals = wrap(almostEquals)
}
export default setAssertionMethods;
export default setAssertionMethods

View File

@ -1,4 +1,4 @@
import assertionWrapper from './assertion-wrapper.js';
import assertionWrapper from './assertion-wrapper.js'
/**
* @typedef {PlainObject} InfoObject
@ -21,10 +21,10 @@ import assertionWrapper from './assertion-wrapper.js';
* @returns {InfoObject}
*/
function close (actual, expected, maxDifference, message) {
const actualDiff = (actual === expected) ? 0 : Math.abs(actual - expected);
const result = actualDiff <= maxDifference;
message = message || (actual + ' should be within ' + maxDifference + ' (inclusive) of ' + expected + (result ? '' : '. Actual: ' + actualDiff));
return { result, message, actual, expected };
const actualDiff = (actual === expected) ? 0 : Math.abs(actual - expected)
const result = actualDiff <= maxDifference
message = message || (actual + ' should be within ' + maxDifference + ' (inclusive) of ' + expected + (result ? '' : '. Actual: ' + actualDiff))
return { result, message, actual, expected }
}
/**
@ -40,21 +40,21 @@ function close (actual, expected, maxDifference, message) {
* @returns {InfoObject}
*/
function closePercent (actual, expected, maxPercentDifference, message) {
let actualDiff; let result;
let actualDiff; let result
if (actual === expected) {
actualDiff = 0;
result = actualDiff <= maxPercentDifference;
actualDiff = 0
result = actualDiff <= maxPercentDifference
} else if (actual !== 0 && expected !== 0 && expected !== Infinity && expected !== -Infinity) {
actualDiff = Math.abs(100 * (actual - expected) / expected);
result = actualDiff <= maxPercentDifference;
actualDiff = Math.abs(100 * (actual - expected) / expected)
result = actualDiff <= maxPercentDifference
} else {
// Dividing by zero (0)! Should return `false` unless the max percentage was `Infinity`
actualDiff = Infinity;
result = maxPercentDifference === Infinity;
actualDiff = Infinity
result = maxPercentDifference === Infinity
}
message = message || (actual + ' should be within ' + maxPercentDifference + '% (inclusive) of ' + expected + (result ? '' : '. Actual: ' + actualDiff + '%'));
message = message || (actual + ' should be within ' + maxPercentDifference + '% (inclusive) of ' + expected + (result ? '' : '. Actual: ' + actualDiff + '%'))
return { result, message, actual, expected };
return { result, message, actual, expected }
}
/**
@ -70,10 +70,10 @@ function closePercent (actual, expected, maxPercentDifference, message) {
* @returns {InfoObject}
*/
function notClose (actual, expected, minDifference, message) {
const actualDiff = Math.abs(actual - expected);
const result = actualDiff > minDifference;
message = message || (actual + ' should not be within ' + minDifference + ' (exclusive) of ' + expected + (result ? '' : '. Actual: ' + actualDiff));
return { result, message, actual, expected };
const actualDiff = Math.abs(actual - expected)
const result = actualDiff > minDifference
message = message || (actual + ' should not be within ' + minDifference + ' (exclusive) of ' + expected + (result ? '' : '. Actual: ' + actualDiff))
return { result, message, actual, expected }
}
/**
@ -89,21 +89,21 @@ function notClose (actual, expected, minDifference, message) {
* @returns {InfoObject}
*/
function notClosePercent (actual, expected, minPercentDifference, message) {
let actualDiff; let result;
let actualDiff; let result
if (actual === expected) {
actualDiff = 0;
result = actualDiff > minPercentDifference;
actualDiff = 0
result = actualDiff > minPercentDifference
} else if (actual !== 0 && expected !== 0 && expected !== Infinity && expected !== -Infinity) {
actualDiff = Math.abs(100 * (actual - expected) / expected);
result = actualDiff > minPercentDifference;
actualDiff = Math.abs(100 * (actual - expected) / expected)
result = actualDiff > minPercentDifference
} else {
// Dividing by zero (0)! Should only return `true` if the min percentage was `Infinity`
actualDiff = Infinity;
result = minPercentDifference !== Infinity;
actualDiff = Infinity
result = minPercentDifference !== Infinity
}
message = message || (actual + ' should not be within ' + minPercentDifference + '% (exclusive) of ' + expected + (result ? '' : '. Actual: ' + actualDiff + '%'));
message = message || (actual + ' should not be within ' + minPercentDifference + '% (exclusive) of ' + expected + (result ? '' : '. Actual: ' + actualDiff + '%'))
return { result, message, actual, expected };
return { result, message, actual, expected }
}
/**
@ -112,12 +112,12 @@ function notClosePercent (actual, expected, minPercentDifference, message) {
* @returns {void}
*/
function setAssertionMethods (_chai, utils) {
const wrap = assertionWrapper(_chai, utils);
const wrap = assertionWrapper(_chai, utils)
assert.close = wrap(close);
assert.closePercent = wrap(closePercent);
assert.notClose = wrap(notClose);
assert.notClosePercent = wrap(notClosePercent);
assert.close = wrap(close)
assert.closePercent = wrap(closePercent)
assert.notClose = wrap(notClose)
assert.notClosePercent = wrap(notClosePercent)
}
export default setAssertionMethods;
export default setAssertionMethods

View File

@ -1,4 +1,4 @@
import assertionWrapper from './assertion-wrapper.js';
import assertionWrapper from './assertion-wrapper.js'
/**
* Expects an out of bounds `INDEX_SIZE_ERR` exception.
@ -8,18 +8,18 @@ import assertionWrapper from './assertion-wrapper.js';
* @returns {void}
*/
function expectOutOfBoundsException (obj, fn, arg1) {
const expected = true;
const message = 'Caught an INDEX_SIZE_ERR exception';
let result = false;
const expected = true
const message = 'Caught an INDEX_SIZE_ERR exception'
let result = false
try {
obj[fn](arg1);
obj[fn](arg1)
} catch (e) {
if (e.code === 1) {
result = true;
result = true
}
}
const actual = result;
return { result, message, actual, expected };
const actual = result
return { result, message, actual, expected }
}
/**
@ -28,9 +28,9 @@ function expectOutOfBoundsException (obj, fn, arg1) {
* @returns {void}
*/
function setAssertionMethods (_chai, utils) {
const wrap = assertionWrapper(_chai, utils);
const wrap = assertionWrapper(_chai, utils)
assert.expectOutOfBoundsException = wrap(expectOutOfBoundsException);
assert.expectOutOfBoundsException = wrap(expectOutOfBoundsException)
}
export default setAssertionMethods;
export default setAssertionMethods

View File

@ -6,10 +6,10 @@
function setAssertionMethods (_chai, _utils) {
return (method) => {
return (...args) => {
const { result, message, actual, expected } = method(...args);
const assertion = new _chai.Assertion();
assertion.assert(result, `Expected ${actual} to be ${expected}`, message);
};
};
const { result, message, actual, expected } = method(...args)
const assertion = new _chai.Assertion()
assertion.assert(result, `Expected ${actual} to be ${expected}`, message)
}
}
}
export default setAssertionMethods;
export default setAssertionMethods

View File

@ -23,26 +23,3 @@
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
// remove the style attributes that is causing differences in snapshots
const ngAttributes = [ 'style' ];
Cypress.Commands.add(
'cleanSnapshot',
{
prevSubject: true
},
(subject, _snapshotOptions) => {
let html = subject[0].outerHTML;
for (const attribute of ngAttributes) {
const expression = new RegExp(`${attribute}[^= ]*="[^"]*"`, 'g');
html = html.replace(expression, '');
}
html = html.replace(/<!--[\s\S]*?-->/g, '');
const sanitisedBody = new DOMParser().parseFromString(html, 'text/html').querySelector('body');
return cy.wrap(sanitisedBody.firstChild).toMatchSnapshot();
}
);

View File

@ -14,7 +14,7 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands.js';
import './commands.js'
// Alternatively you can use CommonJS syntax:
// require('./commands')
@ -29,17 +29,17 @@ import './commands.js';
* @see https://github.com/cypress-io/cypress-fiddle
* @example import {testExamples} from '@cypress/fiddle';
*/
import '@cypress/fiddle';
import '@cypress/fiddle'
/**
* COVERAGE.
* @see https://docs.cypress.io/guides/tooling/code-coverage.html#Install-the-plugin
*/
import '@cypress/code-coverage/support.js';
import '@cypress/code-coverage/support.js'
/*****
* SNAPSHOTS
* @see https://www.npmjs.com/package/cypress-plugin-snapshots
*/
import 'cypress-plugin-snapshots/commands.js';
import 'cypress-plugin-snapshots/commands.js'

View File

@ -1,24 +1,31 @@
export const approveStorage = () => {
// JFH will need to be chnaged when dialog is changed...
cy.get('#storage_ok').click();
};
cy.get('#storage_ok').click()
}
export const visitAndApproveStorage = () => {
cy.visit('/instrumented/editor/index.html');
approveStorage();
};
cy.visit('/instrumented/editor/index.html')
approveStorage()
}
export const openMainMenu = () => {
return cy.get('#main_button').click({ force: true });
};
return cy.get('#main_button').click({ force: true })
}
export const openEditorPreferences = () => {
openMainMenu();
return cy.get('#tool_editor_prefs').click();
};
openMainMenu()
return cy.get('#tool_editor_prefs').click()
}
export const selectEnglish = () => {
openEditorPreferences();
cy.get('#lang_select').select('en');
cy.get('#tool_prefs_save').click();
};
openEditorPreferences()
cy.get('#lang_select').select('en')
cy.get('#tool_prefs_save').click()
}
export const testSnapshot = () => {
cy.window().then((win) => { // access to the remote Window so we can get the svgEditor variable
const svgString = win.svgEditor.svgCanvas.getSvgString()
const svgDom = new DOMParser().parseFromString(svgString, 'text/html').querySelector('body')
cy.wrap(svgDom).toMatchSnapshot()
})
}

View File

@ -27,14 +27,14 @@
<input id="text" style="width:0;height:0;opacity: 0"/>
<script type="module">
/* globals canvas */
import SvgCanvas from '../src/svgcanvas/svgcanvas.js';
import SvgCanvas from '../src/svgcanvas/svgcanvas.js'
const container = document.querySelector('#editorContainer');
const { width, height } = { width: 500, height: 300 };
window.width = width;
window.height = height;
const container = document.querySelector('#editorContainer')
const { width, height } = { width: 500, height: 300 }
window.width = width
window.height = height
const hiddenTextTagId = "text";
const hiddenTextTagId = 'text'
const config = {
initFill: { color: 'FFFFFF', opacity: 1 },
@ -44,28 +44,27 @@ const config = {
imgPath: '../src/editor/images',
dimensions: [ width, height ],
baseUnit: 'px'
};
}
window.canvas = new SvgCanvas(container, config);
canvas.updateCanvas(width, height);
window.canvas = new SvgCanvas(container, config)
canvas.updateCanvas(width, height)
window.fill = function (colour) {
canvas.getSelectedElems().forEach((el) => {
el.setAttribute('fill', colour);
});
};
canvas.getSelectedElements().forEach((el) => {
el.setAttribute('fill', colour)
})
}
const hiddenTextTag = window.canvas.$id(hiddenTextTagId);
window.canvas.textActions.setInputElem(hiddenTextTag);
const hiddenTextTag = window.canvas.$id(hiddenTextTagId)
window.canvas.textActions.setInputElem(hiddenTextTag)
const addListenerMulti = (element, eventNames, listener)=> {
eventNames.split(' ').forEach((eventName)=> element.addEventListener(eventName, listener, false));
};
const addListenerMulti = (element, eventNames, listener) => {
eventNames.split(' ').forEach((eventName) => element.addEventListener(eventName, listener, false))
}
addListenerMulti(hiddenTextTag, 'keyup input', (evt) => {
window.canvas.setTextContent(evt.currentTarget.value);
});
window.canvas.setTextContent(evt.currentTarget.value)
})
</script>
</body>
</html>

View File

@ -1,8 +1,8 @@
/* eslint-env node */
'use strict';
'use strict'
module.exports = {
plugins: [ 'plugins/markdown' ],
plugins: ['plugins/markdown'],
markdown: {},
recurseDepth: 10,
source: {
@ -33,4 +33,4 @@ module.exports = {
destination: 'docs/jsdoc',
tutorials: 'docs/tutorials'
}
};
}

View File

@ -48,7 +48,7 @@ svgEditor.setConfig({
initFill: {
color: '0000FF'
}
});
})
```
This will set the default width/height of the image, the size of the outside
@ -121,13 +121,13 @@ set if storage is found.
```js
// Serialized string:
svgEditor.loadFromString('<svg xmlns="...">...</svg>');
svgEditor.loadFromString('<svg xmlns="...">...</svg>')
// Data URI:
svgEditor.loadFromDataURI('data:image/svg+xml;base64,...');
svgEditor.loadFromDataURI('data:image/svg+xml;base64,...')
// Local URL:
svgEditor.loadFromURL('images/logo.svg');
svgEditor.loadFromURL('images/logo.svg')
```
### Preload a file (by URL)
@ -136,13 +136,13 @@ As a URL parameter, one can pre-load an SVG file in the following manner:
```js
// Data URI
location.href += '?source=' + encodeURIComponent('data:image/svg+xml;utf8,' + svgText);
location.href += '?source=' + encodeURIComponent('data:image/svg+xml;utf8,' + svgText)
// Data URI (base 64):
location.href += '?source=' + encodeURIComponent('data:image/svg+xml;base64,' + svgTextAsBase64); // data%3Aimage%2Fsvg%2Bxml%3Bbase64%2C ...
location.href += '?source=' + encodeURIComponent('data:image/svg+xml;base64,' + svgTextAsBase64) // data%3Aimage%2Fsvg%2Bxml%3Bbase64%2C ...
// Local URL:
location.href += '?url=' + encodeURIComponent('images/logo.svg'); // images%2Flogo.svg
location.href += '?url=' + encodeURIComponent('images/logo.svg') // images%2Flogo.svg
```
**Note:** There is currently a bug that prevents data URIs ending with
@ -160,7 +160,7 @@ To add your own stylesheets along with the default stylesheets, ensure
`"@default"` is present in the array along with your own. For example:
```js
svgEditor.setConfig({ stylesheets: [ '@default', 'myStylesheet.css' ] });
svgEditor.setConfig({ stylesheets: [ '@default', 'myStylesheet.css' ] })
```
(In version 2.8, the CSS file `editor/custom.css` was included by default,

View File

@ -11,7 +11,7 @@ svgEditor.setCustomHandlers({
save (_win, _data) {
// Save svg
}
});
})
```
Other methods corresponding to UI events that may be supplied are `open`
@ -40,9 +40,9 @@ $(document).bind('svgEditorReady', function () {
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="50">
<ellipse cx="50" cy="25" rx="50" ry="25" style="fill:blue;"/>
</svg>`;
$('iframe.svgedit')[0].contentWindow.svgCanvas.setSvgString(svg);
});
</svg>`
$('iframe.svgedit')[0].contentWindow.svgCanvas.setSvgString(svg)
})
```
If you are acting within the frame, you may use `svgEditor.ready`
@ -86,7 +86,7 @@ Canvas events are listened to with the bind method
([JSDocs API]{@link module:svgcanvas.SvgCanvas#bind}):
```js
canvas.bind(eventName, callback);
canvas.bind(eventName, callback)
```
Canvas events are passed between the editor and canvas and should mostly

View File

@ -28,9 +28,9 @@ This is the general format for an extension:
export default {
name: 'extensionname',
init (_methods) {
return extensionData;
return extensionData
}
};
}
```
Extensions must export an object. (For the API docs of this object, see
@ -86,9 +86,9 @@ export default {
mouseUp (_opts) {
// ...
}
};
}
}
};
}
```
Note how the returned properties may include information on the buttons,
@ -145,15 +145,15 @@ import { importSetGlobalDefault } from '../external/dynamic-import-polyfill/impo
(async () => {
const url = `${svgEditor.curConfig.extPath}ext-locale/<extNameWithoutExtPrefix>/<lang>.js`;
const url = `${svgEditor.curConfig.extPath}ext-locale/<extNameWithoutExtPrefix>/<lang>.js`
const localeStrings = await importSetGlobalDefault(url, {
global: 'svgEditorExtensionLocale_imagelib_' + lang
});
})
// Use `localeStrings`
console.info(localeStrings);
console.info(localeStrings)
})();
})()
```
In addition to your own extension's locale strings,

View File

@ -120,7 +120,7 @@ these files). The default behavior is equivalent to this:
```js
svgEditor.setConfig({
stylesheets: [ '@default', '../svgedit-custom.css' ]
});
})
```
...which indicates that all default stylesheets will be loaded and then

View File

@ -1,16 +1,16 @@
module.exports = {
"statements": 45,
"branches": 34,
"lines": 46,
"functions": 45,
statements: 45,
branches: 34,
lines: 46,
functions: 45,
exclude: [
'editor/jquery.min.js',
'editor/jgraduate/**'
],
"reporter": [
"json-summary",
"text",
"html"
reporter: [
'json-summary',
'text',
'html'
]
};
}

4403
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@
"node": ">=10"
},
"scripts": {
"lint": "eslint --ext js,html,md .",
"lint": "standard .",
"test": "run-s cypress:instrument cypress:test",
"build": "rollup -c",
"build:watch": "rollup -c --watch",
@ -67,62 +67,55 @@
"not IE 11",
"not OperaMini all"
],
"standard": {
"ignore": [
"archive/"
],
"globals": [
"cy",
"assert"
],
"env": [
"mocha",
"browser"
]
},
"dependencies": {
"@babel/polyfill": "7.12.1",
"browser-fs-access": "0.23.0",
"canvg": "3.0.9",
"core-js": "3.19.3",
"core-js": "3.20.1",
"elix": "15.0.1",
"html2canvas": "1.3.3",
"i18next": "21.6.0",
"jspdf": "2.4.0",
"i18next": "21.6.4",
"jspdf": "2.5.0",
"pathseg": "1.2.1",
"regenerator-runtime": "0.13.9",
"rollup-plugin-polyfill-node": "0.8.0",
"svg2pdf.js": "2.2.0"
},
"devDependencies": {
"@babel/core": "7.16.0",
"@babel/preset-env": "7.16.4",
"@babel/register": "7.16.0",
"@babel/runtime-corejs3": "7.16.3",
"@babel/core": "7.16.5",
"@babel/preset-env": "7.16.5",
"@babel/register": "7.16.5",
"@babel/runtime-corejs3": "7.16.5",
"@cypress/code-coverage": "3.9.12",
"@cypress/fiddle": "1.19.3",
"@fintechstudios/eslint-plugin-chai-as-promised": "3.1.0",
"@rollup/plugin-babel": "5.3.0",
"@rollup/plugin-commonjs": "^18",
"@rollup/plugin-dynamic-import-vars": "1.4.1",
"@rollup/plugin-node-resolve": "13.0.6",
"@rollup/plugin-node-resolve": "13.1.1",
"@rollup/plugin-replace": "3.0.0",
"@rollup/plugin-url": "6.1.0",
"@web/dev-server": "0.1.28",
"@web/dev-server-esbuild": "^0.2.16",
"@web/dev-server-rollup": "0.3.13",
"babel-plugin-transform-object-rest-spread": "7.0.0-beta.3",
"copyfiles": "2.4.1",
"core-js-bundle": "3.19.3",
"core-js-bundle": "3.20.1",
"cp-cli": "2.0.0",
"cypress": "9.1.1",
"cypress": "9.2.0",
"cypress-multi-reporters": "1.5.0",
"cypress-plugin-snapshots": "1.4.4",
"eslint": "^7",
"eslint-config-standard": "16.0.3",
"eslint-plugin-array-func": "3.1.7",
"eslint-plugin-chai-expect": "3.0.0",
"eslint-plugin-chai-expect-keywords": "2.1.0",
"eslint-plugin-chai-friendly": "0.7.2",
"eslint-plugin-compat": "4.0.0",
"eslint-plugin-cypress": "2.12.1",
"eslint-plugin-eslint-comments": "3.2.0",
"eslint-plugin-html": "6.2.0",
"eslint-plugin-import": "2.25.3",
"eslint-plugin-jsdoc": "37.2.0",
"eslint-plugin-markdown": "2.2.1",
"eslint-plugin-no-unsanitized": "4.0.1",
"eslint-plugin-no-use-extend-native": "0.5.0",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "5.2.0",
"eslint-plugin-standard": "4.1.0",
"jamilih": "0.54.0",
"jsdoc": "3.6.7",
"node-static": "0.7.11",
@ -135,7 +128,7 @@
"remark-cli": "10.0.1",
"remark-lint-ordered-list-marker-value": "3.1.1",
"rimraf": "3.0.2",
"rollup": "2.61.0",
"rollup": "2.62.0",
"rollup-plugin-copy": "3.4.0",
"rollup-plugin-filesize": "9.1.1",
"rollup-plugin-html": "^0.2.1",
@ -143,6 +136,7 @@
"rollup-plugin-progress": "1.1.2",
"rollup-plugin-re": "1.0.7",
"rollup-plugin-terser": "7.0.2",
"standard": "16.0.4",
"start-server-and-test": "1.14.0"
}
}

View File

@ -3,42 +3,42 @@
// This rollup script is run by the command:
// 'npm run build'
import path from 'path';
import { lstatSync, readdirSync } from 'fs';
import rimraf from 'rimraf';
import babel from '@rollup/plugin-babel';
import copy from 'rollup-plugin-copy';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import nodePolyfills from 'rollup-plugin-node-polyfills';
import url from '@rollup/plugin-url'; // for XML/SVG files
import path from 'path'
import { lstatSync, readdirSync } from 'fs'
import rimraf from 'rimraf'
import babel from '@rollup/plugin-babel'
import copy from 'rollup-plugin-copy'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import nodePolyfills from 'rollup-plugin-node-polyfills'
import url from '@rollup/plugin-url' // for XML/SVG files
// eslint-disable-next-line node/no-extraneous-import
import html from 'rollup-plugin-html';
import html from 'rollup-plugin-html'
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';
import { terser } from 'rollup-plugin-terser';
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars'
import { terser } from 'rollup-plugin-terser'
// import progress from 'rollup-plugin-progress';
import filesize from 'rollup-plugin-filesize';
import filesize from 'rollup-plugin-filesize'
// utility function
const getDirectories = (source) => {
const isDirectory = (dir) => {
return lstatSync(dir).isDirectory();
};
return readdirSync(source).map((name) => path.join(source, name)).filter((i) => isDirectory(i));
};
return lstatSync(dir).isDirectory()
}
return readdirSync(source).map((name) => path.join(source, name)).filter((i) => isDirectory(i))
}
// capture the list of files to build for extensions and ext-locales
const extensionDirs = getDirectories('src/editor/extensions');
const extensionDirs = getDirectories('src/editor/extensions')
const dest = [ 'dist/editor' ];
const dest = ['dist/editor']
// remove existing distribution
rimraf('./dist', () => console.info('recreating dist'));
rimraf('./dist', () => console.info('recreating dist'))
// config for svgedit core module
const config = [ {
input: [ 'src/editor/Editor.js' ],
const config = [{
input: ['src/editor/Editor.js'],
output: [
{
format: 'es',
@ -80,8 +80,8 @@ const config = [ {
dest: 'dist/editor',
rename: 'iife-index.html',
transform: (contents) => {
const replace1 = contents.toString().replace("import Editor from './Editor.js'", "/* import Editor from './xdomain-Editor.js' */");
return replace1.replace('<script type="module">', '<script src="./iife-Editor.js"></script><script>');
const replace1 = contents.toString().replace("import Editor from './Editor.js'", "/* import Editor from './xdomain-Editor.js' */")
return replace1.replace('<script type="module">', '<script src="./iife-Editor.js"></script><script>')
}
},
{ src: 'src/editor/images', dest },
@ -94,27 +94,29 @@ const config = [ {
{ src: 'src/editor/svgedit.css', dest }
]
}),
html({ include: [
'src/editor/panels/*.html',
'src/editor/templates/*.html',
'src/editor/dialogs/*.html'
] }),
html({
include: [
'src/editor/panels/*.html',
'src/editor/templates/*.html',
'src/editor/dialogs/*.html'
]
}),
nodeResolve({
browser: true,
preferBuiltins: false
}),
commonjs(),
dynamicImportVars({ include: `src/editor/locale.js` }),
babel({ babelHelpers: 'bundled', exclude: [ /\/core-js\// ] }), // exclude core-js to avoid circular dependencies.
dynamicImportVars({ include: 'src/editor/locale.js' }),
babel({ babelHelpers: 'bundled', exclude: [/\/core-js\//] }), // exclude core-js to avoid circular dependencies.
nodePolyfills(),
terser({ keep_fnames: true }), // keep_fnames is needed to avoid an error when calling extensions.
filesize()
]
} ];
}]
// config for dynamic extensions
extensionDirs.forEach((extensionDir) => {
const extensionName = path.basename(extensionDir);
const extensionName = path.basename(extensionDir)
extensionName && config.push(
{
input: `./src/editor/extensions/${extensionName}/${extensionName}.js`,
@ -128,25 +130,27 @@ extensionDirs.forEach((extensionDir) => {
],
plugins: [
url({
include: [ '**/*.svg', '**/*.xml' ],
include: ['**/*.svg', '**/*.xml'],
limit: 0,
fileName: '[name][extname]'
}),
html({ include: [
'src/editor/extensions/*/*.html'
] }),
html({
include: [
'src/editor/extensions/*/*.html'
]
}),
nodeResolve({
browser: true,
preferBuiltins: true
}),
commonjs({ exclude: `src/editor/extensions/${extensionName}/${extensionName}.js` }),
dynamicImportVars({ include: `src/editor/extensions/${extensionName}/${extensionName}.js` }),
babel({ babelHelpers: 'bundled', exclude: [ /\/core-js\// ] }),
babel({ babelHelpers: 'bundled', exclude: [/\/core-js\//] }),
nodePolyfills(),
terser({ keep_fnames: true })
]
}
);
});
)
})
export default config;
export default config

View File

@ -6,36 +6,36 @@
* @copyright 2010 Jeff Schiller, 2010 Alexis Deveria
*/
const NSSVG = 'http://www.w3.org/2000/svg';
const NSSVG = 'http://www.w3.org/2000/svg'
const { userAgent } = navigator;
const { userAgent } = navigator
// Note: Browser sniffing should only be used if no other detection method is possible
const isWebkit_ = userAgent.includes('AppleWebKit');
const isGecko_ = userAgent.includes('Gecko/');
const isChrome_ = userAgent.includes('Chrome/');
const isMac_ = userAgent.includes('Macintosh');
const isTouch_ = 'ontouchstart' in window;
const isWebkit_ = userAgent.includes('AppleWebKit')
const isGecko_ = userAgent.includes('Gecko/')
const isChrome_ = userAgent.includes('Chrome/')
const isMac_ = userAgent.includes('Macintosh')
const isTouch_ = 'ontouchstart' in window
// text character positioning (for IE9 and now Chrome)
const supportsGoodTextCharPos_ = (function () {
const svgroot = document.createElementNS(NSSVG, 'svg');
const svgcontent = document.createElementNS(NSSVG, 'svg');
document.documentElement.append(svgroot);
svgcontent.setAttribute('x', 5);
svgroot.append(svgcontent);
const text = document.createElementNS(NSSVG, 'text');
text.textContent = 'a';
svgcontent.append(text);
const svgroot = document.createElementNS(NSSVG, 'svg')
const svgContent = document.createElementNS(NSSVG, 'svg')
document.documentElement.append(svgroot)
svgContent.setAttribute('x', 5)
svgroot.append(svgContent)
const text = document.createElementNS(NSSVG, 'text')
text.textContent = 'a'
svgContent.append(text)
try { // Chrome now fails here
const pos = text.getStartPositionOfChar(0).x;
return (pos === 0);
const pos = text.getStartPositionOfChar(0).x
return (pos === 0)
} catch (err) {
return false;
return false
} finally {
svgroot.remove();
svgroot.remove()
}
}());
}())
// Public API
@ -43,31 +43,31 @@ const supportsGoodTextCharPos_ = (function () {
* @function module:browser.isWebkit
* @returns {boolean}
*/
export const isWebkit = () => isWebkit_;
export const isWebkit = () => isWebkit_
/**
* @function module:browser.isGecko
* @returns {boolean}
*/
export const isGecko = () => isGecko_;
export const isGecko = () => isGecko_
/**
* @function module:browser.isChrome
* @returns {boolean}
*/
export const isChrome = () => isChrome_;
export const isChrome = () => isChrome_
/**
* @function module:browser.isMac
* @returns {boolean}
*/
export const isMac = () => isMac_;
export const isMac = () => isMac_
/**
* @function module:browser.isTouch
* @returns {boolean}
*/
export const isTouch = () => isTouch_;
export const isTouch = () => isTouch_
/**
* @function module:browser.supportsGoodTextCharPos
* @returns {boolean}
*/
export const supportsGoodTextCharPos = () => supportsGoodTextCharPos_;
export const supportsGoodTextCharPos = () => supportsGoodTextCharPos_

View File

@ -6,17 +6,17 @@
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
*/
const NSSVG = 'http://www.w3.org/2000/svg';
const NSSVG = 'http://www.w3.org/2000/svg'
const wAttrs = [ 'x', 'x1', 'cx', 'rx', 'width' ];
const hAttrs = [ 'y', 'y1', 'cy', 'ry', 'height' ];
const unitAttrs = [ 'r', 'radius', ...wAttrs, ...hAttrs ];
const wAttrs = ['x', 'x1', 'cx', 'rx', 'width']
const hAttrs = ['y', 'y1', 'cy', 'ry', 'height']
const unitAttrs = ['r', 'radius', ...wAttrs, ...hAttrs]
// Container of elements.
let elementContainer_;
let elementContainer_
// Stores mapping of unit type to user coordinates.
let typeMap_ = {};
let typeMap_ = {}
/**
* @interface module:units.ElementContainer
@ -63,20 +63,20 @@ let typeMap_ = {};
* @returns {void}
*/
export const init = function (elementContainer) {
elementContainer_ = elementContainer;
elementContainer_ = elementContainer
// Get correct em/ex values by creating a temporary SVG.
const svg = document.createElementNS(NSSVG, 'svg');
document.body.append(svg);
const rect = document.createElementNS(NSSVG, 'rect');
rect.setAttribute('width', '1em');
rect.setAttribute('height', '1ex');
rect.setAttribute('x', '1in');
svg.append(rect);
const bb = rect.getBBox();
svg.remove();
const svg = document.createElementNS(NSSVG, 'svg')
document.body.append(svg)
const rect = document.createElementNS(NSSVG, 'rect')
rect.setAttribute('width', '1em')
rect.setAttribute('height', '1ex')
rect.setAttribute('x', '1in')
svg.append(rect)
const bb = rect.getBBox()
svg.remove()
const inch = bb.x;
const inch = bb.x
typeMap_ = {
em: bb.width,
ex: bb.height,
@ -87,8 +87,8 @@ export const init = function (elementContainer) {
pc: inch / 6,
px: 1,
'%': 0
};
};
}
}
/**
* Group: Unit conversion functions.
@ -99,8 +99,8 @@ export const init = function (elementContainer) {
* @returns {module:units.TypeMap} The unit object with values for each unit
*/
export const getTypeMap = function () {
return typeMap_;
};
return typeMap_
}
/**
* @typedef {GenericArray} module:units.CompareNumbers
@ -119,15 +119,15 @@ export const getTypeMap = function () {
* with comma-separated floats
*/
export const shortFloat = function (val) {
const digits = elementContainer_.getRoundDigits();
const digits = elementContainer_.getRoundDigits()
if (!isNaN(val)) {
return Number(Number(val).toFixed(digits));
return Number(Number(val).toFixed(digits))
}
if (Array.isArray(val)) {
return shortFloat(val[0]) + ',' + shortFloat(val[1]);
return shortFloat(val[0]) + ',' + shortFloat(val[1])
}
return Number.parseFloat(val).toFixed(digits) - 0;
};
return Number.parseFloat(val).toFixed(digits) - 0
}
/**
* Converts the number to given unit or baseUnit.
@ -137,12 +137,12 @@ export const shortFloat = function (val) {
* @returns {Float}
*/
export const convertUnit = function (val, unit) {
unit = unit || elementContainer_.getBaseUnit();
unit = unit || elementContainer_.getBaseUnit()
// baseVal.convertToSpecifiedUnits(unitNumMap[unit]);
// const val = baseVal.valueInSpecifiedUnits;
// baseVal.convertToSpecifiedUnits(1);
return shortFloat(val / typeMap_[unit]);
};
return shortFloat(val / typeMap_[unit])
}
/**
* Sets an element's attribute based on the unit in its current value.
@ -185,19 +185,19 @@ export const setUnitAttr = function (elem, attr, val) {
// val += unit;
// }
// }
elem.setAttribute(attr, val);
};
elem.setAttribute(attr, val)
}
const attrsToConvert = {
line: [ 'x1', 'x2', 'y1', 'y2' ],
circle: [ 'cx', 'cy', 'r' ],
ellipse: [ 'cx', 'cy', 'rx', 'ry' ],
foreignObject: [ 'x', 'y', 'width', 'height' ],
rect: [ 'x', 'y', 'width', 'height' ],
image: [ 'x', 'y', 'width', 'height' ],
use: [ 'x', 'y', 'width', 'height' ],
text: [ 'x', 'y' ]
};
line: ['x1', 'x2', 'y1', 'y2'],
circle: ['cx', 'cy', 'r'],
ellipse: ['cx', 'cy', 'rx', 'ry'],
foreignObject: ['x', 'y', 'width', 'height'],
rect: ['x', 'y', 'width', 'height'],
image: ['x', 'y', 'width', 'height'],
use: ['x', 'y', 'width', 'height'],
text: ['x', 'y']
}
/**
* Converts all applicable attributes to the configured baseUnit.
@ -206,18 +206,18 @@ const attrsToConvert = {
* @returns {void}
*/
export const convertAttrs = function (element) {
const elName = element.tagName;
const unit = elementContainer_.getBaseUnit();
const attrs = attrsToConvert[elName];
if (!attrs) { return; }
const elName = element.tagName
const unit = elementContainer_.getBaseUnit()
const attrs = attrsToConvert[elName]
if (!attrs) { return }
attrs.forEach( (attr) => {
const cur = element.getAttribute(attr);
attrs.forEach((attr) => {
const cur = element.getAttribute(attr)
if (cur && !isNaN(cur)) {
element.setAttribute(attr, (cur / typeMap_[unit]) + unit);
element.setAttribute(attr, (cur / typeMap_[unit]) + unit)
}
});
};
})
}
/**
* Converts given values to numbers. Attributes must be supplied in
@ -230,26 +230,26 @@ export const convertAttrs = function (element) {
*/
export const convertToNum = function (attr, val) {
// Return a number if that's what it already is
if (!isNaN(val)) { return val - 0; }
if (!isNaN(val)) { return val - 0 }
if (val.substr(-1) === '%') {
// Deal with percentage, depends on attribute
const num = val.substr(0, val.length - 1) / 100;
const width = elementContainer_.getWidth();
const height = elementContainer_.getHeight();
const num = val.substr(0, val.length - 1) / 100
const width = elementContainer_.getWidth()
const height = elementContainer_.getHeight()
if (wAttrs.includes(attr)) {
return num * width;
return num * width
}
if (hAttrs.includes(attr)) {
return num * height;
return num * height
}
return num * Math.sqrt((width * width) + (height * height)) / Math.sqrt(2);
return num * Math.sqrt((width * width) + (height * height)) / Math.sqrt(2)
}
const unit = val.substr(-2);
const num = val.substr(0, val.length - 2);
const unit = val.substr(-2)
const num = val.substr(0, val.length - 2)
// Note that this multiplication turns the string into a number
return num * typeMap_[unit];
};
return num * typeMap_[unit]
}
/**
* Check if an attribute's value is in a valid format.
@ -259,33 +259,33 @@ export const convertToNum = function (attr, val) {
* @param {Element} selectedElement
* @returns {boolean} Whether the unit is valid
*/
export const isValidUnit = function (attr, val, selectedElement) {
export const isValidUnit = (attr, val, selectedElement) => {
if (unitAttrs.includes(attr)) {
// True if it's just a number
if (!isNaN(val)) {
return true;
return true
}
// Not a number, check if it has a valid unit
val = val.toLowerCase();
val = val.toLowerCase()
return Object.keys(typeMap_).some((unit) => {
const re = new RegExp('^-?[\\d\\.]+' + unit + '$');
return re.test(val);
});
const re = new RegExp('^-?[\\d\\.]+' + unit + '$')
return re.test(val)
})
}
if (attr === 'id') {
// if we're trying to change the id, make sure it's not already present in the doc
// and the id value is valid.
let result = false;
// because getElem() can throw an exception in the case of an invalid id
let result = false
// because getElement() can throw an exception in the case of an invalid id
// (according to https://www.w3.org/TR/xml-id/ IDs must be a NCName)
// we wrap it in an exception and only return true if the ID was valid and
// not already present
try {
const elem = elementContainer_.getElement(val);
result = (!elem || elem === selectedElement);
} catch (e) {/* empty fn */}
return result;
const elem = elementContainer_.getElement(val)
result = (!elem || elem === selectedElement)
} catch (e) { console.error(e) }
return result
}
return true;
};
return true
}

View File

@ -1,4 +1,4 @@
import { mergeDeep } from './components/jgraduate/Util.js';
import { mergeDeep } from './components/jgraduate/Util.js'
/**
* Escapes special characters in a regular expression.
@ -8,8 +8,8 @@ import { mergeDeep } from './components/jgraduate/Util.js';
*/
export const regexEscape = function (str) {
// Originally from: http://phpjs.org/functions
return String(str).replace(/[.\\+*?[^\]$(){}=!<>|:-]/g, '\\$&');
};
return String(str).replace(/[.\\+*?[^\]$(){}=!<>|:-]/g, '\\$&')
}
/**
* @class configObj
*/
@ -47,7 +47,7 @@ export default class ConfigObj {
// Only shows in UI as far as alert notices, but useful to remember, so keeping as pref
save_notice_done: false,
export_notice_done: false
};
}
/**
* @tutorial ConfigOptions
* @interface module:SVGEditor.Config
@ -134,7 +134,7 @@ export default class ConfigObj {
imgPath: './images',
// DOCUMENT PROPERTIES
// Change the following to a preference (already in the Document Properties dialog)?
dimensions: [ 640, 480 ],
dimensions: [640, 480],
// EDITOR OPTIONS
// Change the following to preferences (already in the Editor Options dialog)?
gridSnapping: false,
@ -158,13 +158,13 @@ export default class ConfigObj {
avoidClientSide: false, // Deprecated in favor of `avoidClientSideDownload`
avoidClientSideDownload: false,
avoidClientSideOpen: false
};
}
this.curPrefs = {};
this.curPrefs = {}
// 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.urldata = {};
this.urldata = {}
/**
* @name module:SVGEditor~defaultExtensions
* @type {string[]}
@ -182,7 +182,7 @@ export default class ConfigObj {
'ext-polystar',
'ext-storage',
'ext-opensave'
];
]
this.curConfig = {
// We do not put on defaultConfig to simplify object copying
// procedures (we obtain instead from defaultExtensions)
@ -203,61 +203,64 @@ export default class ConfigObj {
* @todo We might instead make as a user-facing preference.
*/
allowedOrigins: []
};
this.editor = editor;
}
this.editor = editor
}
/**
* @function setupCurPrefs
* @returns {void}
*/
setupCurPrefs () {
const curPrefs = { ...this.defaultPrefs, ...this.curPrefs }; // Now safe to merge with priority for curPrefs in the event any are already set
const curPrefs = { ...this.defaultPrefs, ...this.curPrefs } // Now safe to merge with priority for curPrefs in the event any are already set
// Export updated prefs
this.curPrefs = curPrefs;
this.curPrefs = curPrefs
}
/**
* Sets up current config based on defaults.
* @returns {void}
*/
setupCurConfig () {
const curConfig = { ...this.defaultConfig, ...this.curConfig }; // Now safe to merge with priority for curConfig in the event any are already set
const curConfig = { ...this.defaultConfig, ...this.curConfig } // Now safe to merge with priority for curConfig in the event any are already set
// Now deal with extensions and other array config
if (!curConfig.noDefaultExtensions) {
curConfig.extensions = [ ...this.defaultExtensions ];
curConfig.extensions = [...this.defaultExtensions]
}
// Export updated config
this.curConfig = curConfig;
this.curConfig = curConfig
}
/**
* @function loadFromURL Load config/data from URL if given
* @returns {void}
*/
loadFromURL () {
const self = this;
const { search, searchParams } = new URL(location);
const self = this
const { search, searchParams } = new URL(location)
if (search) {
this.urldata = {};
const entries = searchParams.entries();
for(const entry of entries) {
this.urldata[entry[0]] = entry[1];
this.urldata = {}
const entries = searchParams.entries()
for (const entry of entries) {
this.urldata[entry[0]] = entry[1]
}
[ 'initStroke', 'initFill' ].forEach((prop) => {
['initStroke', 'initFill'].forEach((prop) => {
if (searchParams.has(`${prop}[color]`)) {
// Restore back to original non-deparamed value to avoid color
// strings being converted to numbers
if(this.urldata[prop] === undefined) { this.urldata[prop] = {}; }
this.urldata[prop].color = searchParams.get(`${prop}[color]`);
if (this.urldata[prop] === undefined) { this.urldata[prop] = {} }
this.urldata[prop].color = searchParams.get(`${prop}[color]`)
}
});
})
if (searchParams.has('bkgd_color')) {
this.urldata.bkgd_color = '#' + searchParams.get('bkgd_color');
this.urldata.bkgd_color = '#' + searchParams.get('bkgd_color')
}
if (this.urldata.dimensions) {
this.urldata.dimensions = this.urldata.dimensions.split(',');
this.urldata.dimensions = this.urldata.dimensions.split(',')
}
if (this.urldata.extensions) {
@ -265,54 +268,55 @@ export default class ConfigObj {
// extensions via URL
this.urldata.extensions = (/[:/\\]/).test(this.urldata.extensions)
? ''
: this.urldata.extensions.split(',');
: this.urldata.extensions.split(',')
}
// Disallowing extension paths via URL for
// security reasons, even for same-domain
// ones given potential to interact in undesirable
// ways with other script resources
[ 'userExtensions', 'imgPath' ]
['userExtensions', 'imgPath']
.forEach(function (pathConfig) {
if (self.urldata[pathConfig]) {
delete self.urldata[pathConfig];
delete self.urldata[pathConfig]
}
});
})
// Note: `source` and `url` (as with `storagePrompt` later) are not
// set on config but are used below
this.setConfig(this.urldata, { overwrite: false });
this.setupCurConfig();
this.setConfig(this.urldata, { overwrite: false })
this.setupCurConfig()
if (!this.curConfig.preventURLContentLoading) {
let { source } = this.urldata;
let { source } = this.urldata
if (!source) { // urldata.source may have been null if it ended with '='
const src = searchParams.get('source');
const src = searchParams.get('source')
if (src && src.startsWith('data:')) {
source = src;
source = src
}
}
if (source) {
if (source.startsWith('data:')) {
this.editor.loadFromDataURI(source);
this.editor.loadFromDataURI(source)
} else {
this.editor.loadFromString(source);
this.editor.loadFromString(source)
}
return;
return
}
if (this.urldata.url) {
this.editor.loadFromURL(this.urldata.url);
return;
this.editor.loadFromURL(this.urldata.url)
return
}
}
if (!this.urldata.noStorageOnLoad || this.curConfig.forceStorage) {
this.loadContentAndPrefs();
this.loadContentAndPrefs()
}
} else {
this.setupCurConfig();
this.loadContentAndPrefs();
this.setupCurConfig()
this.loadContentAndPrefs()
}
}
/**
* Where permitted, sets canvas and/or `configObj.defaultPrefs` based on previous
* storage. This will override URL settings (for security reasons) but
@ -333,7 +337,7 @@ export default class ConfigObj {
!(/(?:^|;\s*)svgeditstore=(?:prefsAndContent|prefsOnly)/).test(document.cookie)
)
) {
return;
return
}
// LOAD CONTENT
@ -343,32 +347,32 @@ export default class ConfigObj {
(/(?:^|;\s*)svgeditstore=prefsAndContent/).test(document.cookie))
)
) {
const name = 'svgedit-' + this.curConfig.canvasName;
const cached = this.editor.storage.getItem(name);
const name = 'svgedit-' + this.curConfig.canvasName
const cached = this.editor.storage.getItem(name)
if (cached) {
this.editor.loadFromString(cached);
this.editor.loadFromString(cached)
}
}
// LOAD PREFS
Object.keys(this.defaultPrefs).forEach((key) => {
const storeKey = 'svg-edit-' + key;
const storeKey = 'svg-edit-' + key
if (this.editor.storage) {
const val = this.editor.storage.getItem(storeKey);
const val = this.editor.storage.getItem(storeKey)
if (val) {
this.defaultPrefs[key] = String(val); // Convert to string for FF (.value fails in Webkit)
this.defaultPrefs[key] = String(val) // Convert to string for FF (.value fails in Webkit)
}
} else if (window.widget) {
this.defaultPrefs[key] = window.widget.preferenceForKey(storeKey);
this.defaultPrefs[key] = window.widget.preferenceForKey(storeKey)
} else {
const result = document.cookie.match(
new RegExp('(?:^|;\\s*)' + regexEscape(
encodeURIComponent(storeKey)
) + '=([^;]+)')
);
this.defaultPrefs[key] = result ? decodeURIComponent(result[1]) : '';
)
this.defaultPrefs[key] = result ? decodeURIComponent(result[1]) : ''
}
});
})
}
/**
@ -402,61 +406,62 @@ export default class ConfigObj {
*/
const extendOrAdd = (cfgObj, key, val) => {
if (cfgObj[key] && typeof cfgObj[key] === 'object') {
cfgObj[key] = mergeDeep(cfgObj[key], val);
cfgObj[key] = mergeDeep(cfgObj[key], val)
} else {
cfgObj[key] = val;
cfgObj[key] = val
}
};
Object.entries(opts).forEach(([ key, val ]) => {
}
Object.entries(opts).forEach(([key, val]) => {
// Only allow prefs defined in configObj.defaultPrefs or...
if (this.defaultPrefs[key]) {
if (cfgCfg.overwrite === false && (
this.curConfig.preventAllURLConfig ||
this.curPrefs[key])
) {
return;
return
}
if (cfgCfg.allowInitialUserOverride === true) {
this.defaultPrefs[key] = val;
this.defaultPrefs[key] = val
} else {
this.pref(key, val);
this.pref(key, val)
}
} else if ([ 'extensions', 'userExtensions', 'allowedOrigins' ].includes(key)) {
} else if (['extensions', 'userExtensions', 'allowedOrigins'].includes(key)) {
if (cfgCfg.overwrite === false &&
(
this.curConfig.preventAllURLConfig ||
[ 'allowedOrigins' ].includes(key) ||
['allowedOrigins'].includes(key) ||
(key === 'extensions' && this.curConfig.lockExtensions)
)
) {
return;
return
}
this.curConfig[key] = this.curConfig[key].concat(val); // We will handle any dupes later
this.curConfig[key] = this.curConfig[key].concat(val) // We will handle any dupes later
// Only allow other configObj.curConfig if defined in configObj.defaultConfig
} else if ({}.hasOwnProperty.call(this.defaultConfig, key)) {
if (cfgCfg.overwrite === false && (
this.curConfig.preventAllURLConfig ||
{}.hasOwnProperty.call(this.curConfig, key)
)) {
return;
return
}
// Potentially overwriting of previously set config
if ({}.hasOwnProperty.call(this.curConfig, key)) {
if (cfgCfg.overwrite === false) {
return;
return
}
extendOrAdd(this.curConfig, key, val);
extendOrAdd(this.curConfig, key, val)
} else if (cfgCfg.allowInitialUserOverride === true) {
extendOrAdd(this.defaultConfig, key, val);
extendOrAdd(this.defaultConfig, key, val)
} else if (this.defaultConfig[key] && typeof this.defaultConfig[key] === 'object') {
this.curConfig[key] = Array.isArray(this.defaultConfig[key]) ? [] : {};
this.curConfig[key] = mergeDeep(this.curConfig[key], val);
this.curConfig[key] = Array.isArray(this.defaultConfig[key]) ? [] : {}
this.curConfig[key] = mergeDeep(this.curConfig[key], val)
} else {
this.curConfig[key] = val;
this.curConfig[key] = val
}
}
});
})
}
/**
* Store and retrieve preferences.
* @function pref
@ -475,17 +480,18 @@ export default class ConfigObj {
*/
pref (key, val, mayBeEmpty) {
if (mayBeEmpty || val) {
this.curPrefs[key] = val;
return undefined;
this.curPrefs[key] = val
return undefined
}
return (key in this.curPrefs) ? this.curPrefs[key] : this.defaultPrefs[key];
return (key in this.curPrefs) ? this.curPrefs[key] : this.defaultPrefs[key]
}
/**
* @function load load Config
* @returns {void}
*/
load () {
this.loadFromURL(this.editor);
this.setupCurPrefs(this.editor);
this.loadFromURL(this.editor)
this.setupCurPrefs(this.editor)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
/* globals seConfirm, seAlert */
import SvgCanvas from "../svgcanvas/svgcanvas.js";
import { convertUnit, isValidUnit } from '../common/units.js';
import { isChrome } from '../common/browser.js';
import SvgCanvas from '../svgcanvas/svgcanvas.js'
import { convertUnit, isValidUnit } from '../common/units.js'
import { isChrome } from '../common/browser.js'
const { $id } = SvgCanvas;
const homePage = 'https://github.com/SVG-Edit/svgedit';
const { $id } = SvgCanvas
const homePage = 'https://github.com/SVG-Edit/svgedit'
/**
*
@ -13,89 +13,91 @@ class MainMenu {
/**
* @param {PlainObject} editor svgedit handler
*/
constructor(editor) {
this.editor = editor;
constructor (editor) {
this.editor = editor
/**
* @type {Integer}
*/
this.exportWindowCt = 0;
this.exportWindowCt = 0
}
/**
* @fires module:svgcanvas.SvgCanvas#event:ext_onNewDocument
* @returns {void}
*/
async clickClear() {
const [ x, y ] = this.editor.configObj.curConfig.dimensions;
const ok = await seConfirm(this.editor.i18next.t('notification.QwantToClear'));
if (ok === "Cancel") {
return;
async clickClear () {
const [x, y] = this.editor.configObj.curConfig.dimensions
const ok = await seConfirm(this.editor.i18next.t('notification.QwantToClear'))
if (ok === 'Cancel') {
return
}
this.editor.leftPanel.clickSelect();
this.editor.svgCanvas.clear();
this.editor.svgCanvas.setResolution(x, y);
this.editor.updateCanvas(true);
this.editor.zoomImage();
this.editor.layersPanel.populateLayers();
this.editor.topPanel.updateContextPanel();
this.editor.svgCanvas.runExtensions("onNewDocument");
}
/**
*
* @returns {void}
*/
hideDocProperties() {
const $imgDialog = $id("se-img-prop");
$imgDialog.setAttribute("dialog", "close");
$imgDialog.setAttribute("save", this.editor.configObj.pref("img_save"));
this.editor.docprops = false;
this.editor.leftPanel.clickSelect()
this.editor.svgCanvas.clear()
this.editor.svgCanvas.setResolution(x, y)
this.editor.updateCanvas(true)
this.editor.zoomImage()
this.editor.layersPanel.populateLayers()
this.editor.topPanel.updateContextPanel()
this.editor.svgCanvas.runExtensions('onNewDocument')
}
/**
*
* @returns {void}
*/
hidePreferences() {
const $editDialog = $id("se-edit-prefs");
$editDialog.setAttribute("dialog", "close");
this.editor.configObj.preferences = false;
hideDocProperties () {
const $imgDialog = $id('se-img-prop')
$imgDialog.setAttribute('dialog', 'close')
$imgDialog.setAttribute('save', this.editor.configObj.pref('img_save'))
this.editor.docprops = false
}
/**
*
* @returns {void}
*/
hidePreferences () {
const $editDialog = $id('se-edit-prefs')
$editDialog.setAttribute('dialog', 'close')
this.editor.configObj.preferences = false
}
/**
* @param {Event} e
* @returns {boolean} Whether there were problems saving the document properties
*/
saveDocProperties(e) {
saveDocProperties (e) {
// set title
const { title, w, h, save } = e.detail;
const { title, w, h, save } = e.detail
// set document title
this.editor.svgCanvas.setDocumentTitle(title);
this.editor.svgCanvas.setDocumentTitle(title)
if (w !== "fit" && !isValidUnit("width", w)) {
seAlert(this.editor.i18next.t('notification.invalidAttrValGiven'));
return false;
if (w !== 'fit' && !isValidUnit('width', w)) {
seAlert(this.editor.i18next.t('notification.invalidAttrValGiven'))
return false
}
if (h !== "fit" && !isValidUnit("height", h)) {
seAlert(this.editor.i18next.t('notification.invalidAttrValGiven'));
return false;
if (h !== 'fit' && !isValidUnit('height', h)) {
seAlert(this.editor.i18next.t('notification.invalidAttrValGiven'))
return false
}
if (!this.editor.svgCanvas.setResolution(w, h)) {
seAlert(this.editor.i18next.t('notification.noContentToFitTo'));
return false;
seAlert(this.editor.i18next.t('notification.noContentToFitTo'))
return false
}
// Set image save option
this.editor.configObj.pref("img_save", save);
this.editor.updateCanvas();
this.hideDocProperties();
return true;
this.editor.configObj.pref('img_save', save)
this.editor.updateCanvas()
this.hideDocProperties()
return true
}
/**
* Save user preferences based on current values in the UI.
* @param {Event} e
* @function module:SVGthis.savePreferences
* @returns {Promise<void>}
*/
async savePreferences(e) {
async savePreferences (e) {
const {
lang,
bgcolor,
@ -105,57 +107,58 @@ class MainMenu {
gridcolor,
showrulers,
baseunit
} = e.detail;
} = e.detail
// Set background
this.editor.setBackground(bgcolor, bgurl);
this.editor.setBackground(bgcolor, bgurl)
// set language
if (lang && lang !== this.editor.configObj.pref("lang")) {
this.editor.configObj.pref("lang", lang);
seAlert('Changing the language needs reload');
if (lang && lang !== this.editor.configObj.pref('lang')) {
this.editor.configObj.pref('lang', lang)
seAlert('Changing the language needs reload')
}
// set grid setting
this.editor.configObj.curConfig.gridSnapping = gridsnappingon;
this.editor.configObj.curConfig.snappingStep = gridsnappingstep;
this.editor.configObj.curConfig.gridColor = gridcolor;
this.editor.configObj.curConfig.showRulers = showrulers;
this.editor.configObj.curConfig.gridSnapping = gridsnappingon
this.editor.configObj.curConfig.snappingStep = gridsnappingstep
this.editor.configObj.curConfig.gridColor = gridcolor
this.editor.configObj.curConfig.showRulers = showrulers
if (this.editor.configObj.curConfig.showRulers) {
this.editor.rulers.updateRulers();
this.editor.rulers.updateRulers()
}
this.editor.configObj.curConfig.baseUnit = baseunit;
this.editor.svgCanvas.setConfig(this.editor.configObj.curConfig);
this.editor.updateCanvas();
this.hidePreferences();
this.editor.configObj.curConfig.baseUnit = baseunit
this.editor.svgCanvas.setConfig(this.editor.configObj.curConfig)
this.editor.updateCanvas()
this.hidePreferences()
}
/**
*
* @param e
* @returns {Promise<void>} Resolves to `undefined`
*/
async clickExport(e) {
if (e?.detail?.trigger !== "ok" || e?.detail?.imgType === undefined) {
return;
async clickExport (e) {
if (e?.detail?.trigger !== 'ok' || e?.detail?.imgType === undefined) {
return
}
const imgType = e?.detail?.imgType;
const quality = e?.detail?.quality ? e?.detail?.quality / 100 : 1;
const imgType = e?.detail?.imgType
const quality = e?.detail?.quality ? e?.detail?.quality / 100 : 1
// Open placeholder window (prevents popup)
let exportWindowName;
let exportWindowName
/**
*
* @returns {void}
*/
const openExportWindow = () => {
const loadingImage = this.editor.i18next.t('notification.loadingImage');
if (this.editor.configObj.curConfig.exportWindowType === "new") {
this.editor.exportWindowCt++;
const loadingImage = this.editor.i18next.t('notification.loadingImage')
if (this.editor.configObj.curConfig.exportWindowType === 'new') {
this.editor.exportWindowCt++
}
this.editor.exportWindowName =
this.editor.configObj.curConfig.canvasName + this.editor.exportWindowCt;
let popHTML; let popURL;
this.editor.configObj.curConfig.canvasName + this.editor.exportWindowCt
let popHTML; let popURL
if (this.editor.loadingURL) {
popURL = this.editor.loadingURL;
popURL = this.editor.loadingURL
} else {
popHTML = `<!DOCTYPE html><html>
<head>
@ -163,35 +166,35 @@ class MainMenu {
<title>${loadingImage}</title>
</head>
<body><h1>${loadingImage}</h1></body>
<html>`;
if (typeof URL !== "undefined" && URL.createObjectURL) {
const blob = new Blob([ popHTML ], { type: "text/html" });
popURL = URL.createObjectURL(blob);
<html>`
if (typeof URL !== 'undefined' && URL.createObjectURL) {
const blob = new Blob([popHTML], { type: 'text/html' })
popURL = URL.createObjectURL(blob)
} else {
popURL = "data:text/html;base64;charset=utf-8," + popHTML;
popURL = 'data:text/html;base64;charset=utf-8,' + popHTML
}
this.editor.loadingURL = popURL;
this.editor.loadingURL = popURL
}
this.editor.exportWindow = window.open(
popURL,
this.editor.exportWindowName
);
};
const chrome = isChrome();
if (imgType === "PDF") {
)
}
const chrome = isChrome()
if (imgType === 'PDF') {
if (!this.editor.customExportPDF && !chrome) {
openExportWindow();
openExportWindow()
}
this.editor.svgCanvas.exportPDF(exportWindowName);
this.editor.svgCanvas.exportPDF(exportWindowName)
} else {
if (!this.editor.customExportImage) {
openExportWindow();
openExportWindow()
}
/* const results = */ await this.editor.svgCanvas.rasterExport(
imgType,
quality,
this.editor.exportWindowName
);
)
}
}
@ -199,129 +202,129 @@ class MainMenu {
*
* @returns {void}
*/
showDocProperties() {
showDocProperties () {
if (this.editor.docprops) {
return;
return
}
this.editor.docprops = true;
const $imgDialog = $id("se-img-prop");
this.editor.docprops = true
const $imgDialog = $id('se-img-prop')
// update resolution option with actual resolution
const resolution = this.editor.svgCanvas.getResolution();
if (this.editor.configObj.curConfig.baseUnit !== "px") {
const resolution = this.editor.svgCanvas.getResolution()
if (this.editor.configObj.curConfig.baseUnit !== 'px') {
resolution.w =
convertUnit(resolution.w) + this.editor.configObj.curConfig.baseUnit;
convertUnit(resolution.w) + this.editor.configObj.curConfig.baseUnit
resolution.h =
convertUnit(resolution.h) + this.editor.configObj.curConfig.baseUnit;
convertUnit(resolution.h) + this.editor.configObj.curConfig.baseUnit
}
$imgDialog.setAttribute("save", this.editor.configObj.pref("img_save"));
$imgDialog.setAttribute("width", resolution.w);
$imgDialog.setAttribute("height", resolution.h);
$imgDialog.setAttribute("title", this.editor.svgCanvas.getDocumentTitle());
$imgDialog.setAttribute("dialog", "open");
$imgDialog.setAttribute('save', this.editor.configObj.pref('img_save'))
$imgDialog.setAttribute('width', resolution.w)
$imgDialog.setAttribute('height', resolution.h)
$imgDialog.setAttribute('title', this.editor.svgCanvas.getDocumentTitle())
$imgDialog.setAttribute('dialog', 'open')
}
/**
*
* @returns {void}
*/
showPreferences() {
showPreferences () {
if (this.editor.configObj.preferences) {
return;
return
}
this.editor.configObj.preferences = true;
const $editDialog = $id("se-edit-prefs");
this.editor.configObj.preferences = true
const $editDialog = $id('se-edit-prefs')
// Update background color with current one
const canvasBg = this.editor.configObj.curPrefs.bkgd_color;
const url = this.editor.configObj.pref("bkgd_url");
const canvasBg = this.editor.configObj.curPrefs.bkgd_color
const url = this.editor.configObj.pref('bkgd_url')
if (url) {
$editDialog.setAttribute("bgurl", url);
$editDialog.setAttribute('bgurl', url)
}
$editDialog.setAttribute(
"gridsnappingon",
'gridsnappingon',
this.editor.configObj.curConfig.gridSnapping
);
)
$editDialog.setAttribute(
"gridsnappingstep",
'gridsnappingstep',
this.editor.configObj.curConfig.snappingStep
);
)
$editDialog.setAttribute(
"gridcolor",
'gridcolor',
this.editor.configObj.curConfig.gridColor
);
$editDialog.setAttribute("canvasbg", canvasBg);
$editDialog.setAttribute("dialog", "open");
)
$editDialog.setAttribute('canvasbg', canvasBg)
$editDialog.setAttribute('dialog', 'open')
}
/**
*
* @returns {void}
*/
openHomePage() {
window.open(homePage, "_blank");
openHomePage () {
window.open(homePage, '_blank')
}
/**
* @type {module}
*/
init() {
init () {
// add Top panel
const template = document.createElement("template");
const template = document.createElement('template')
template.innerHTML = `
<se-menu id="main_button" label="SVG-Edit" src="logo.svg" alt="logo">
<se-menu-item id="tool_export" label="tools.export_img" src="export.svg"></se-menu-item>
<se-menu-item id="tool_docprops" label="tools.docprops" shortcut="D" src="docprop.svg"></se-menu-item>
<se-menu-item id="tool_editor_prefs" label="config.editor_prefs" src="editPref.svg"></se-menu-item>
<se-menu-item id="tool_editor_homepage" label="tools.editor_homepage" src="logo.svg"></se-menu-item>
</se-menu>`;
this.editor.$svgEditor.append(template.content.cloneNode(true));
</se-menu>`
this.editor.$svgEditor.append(template.content.cloneNode(true))
// register action to main menu entries
/**
* Associate all button actions as well as non-button keyboard shortcuts.
*/
$id("tool_export").addEventListener("click", function() {
$id('tool_export').addEventListener('click', function () {
document
.getElementById("se-export-dialog")
.setAttribute("dialog", "open");
});
$id("se-export-dialog").addEventListener(
"change",
.getElementById('se-export-dialog')
.setAttribute('dialog', 'open')
})
$id('se-export-dialog').addEventListener(
'change',
this.clickExport.bind(this)
);
$id("tool_docprops").addEventListener(
"click",
)
$id('tool_docprops').addEventListener(
'click',
this.showDocProperties.bind(this)
);
$id("tool_editor_prefs").addEventListener(
"click",
)
$id('tool_editor_prefs').addEventListener(
'click',
this.showPreferences.bind(this)
);
$id("tool_editor_homepage").addEventListener(
"click",
)
$id('tool_editor_homepage').addEventListener(
'click',
this.openHomePage.bind(this)
);
$id("se-img-prop").addEventListener(
"change",
function(e) {
if (e.detail.dialog === "closed") {
this.hideDocProperties();
)
$id('se-img-prop').addEventListener(
'change',
function (e) {
if (e.detail.dialog === 'closed') {
this.hideDocProperties()
} else {
this.saveDocProperties(e);
this.saveDocProperties(e)
}
}.bind(this)
);
$id("se-edit-prefs").addEventListener(
"change",
function(e) {
if (e.detail.dialog === "closed") {
this.hidePreferences();
)
$id('se-edit-prefs').addEventListener(
'change',
function (e) {
if (e.detail.dialog === 'closed') {
this.hidePreferences()
} else {
this.savePreferences(e);
this.savePreferences(e)
}
}.bind(this)
);
)
}
}
export default MainMenu;
export default MainMenu

View File

@ -1,5 +1,6 @@
import { getTypeMap } from '../common/units.js';
import rulersTemplate from './templates/rulersTemplate.html';
import { getTypeMap } from '../common/units.js'
import rulersTemplate from './templates/rulersTemplate.html'
import SvgCanvas from '../svgcanvas/svgcanvas.js'
/**
*
*/
@ -9,41 +10,42 @@ class Rulers {
*/
constructor (editor) {
// Make [1,2,5] array
this.rulerIntervals = [];
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.rulerIntervals.push(i)
this.rulerIntervals.push(2 * i)
this.rulerIntervals.push(5 * i)
}
this.svgCanvas = editor.svgCanvas;
this.editor = editor;
this.svgCanvas = editor.svgCanvas
this.editor = editor
// add rulers component to the DOM
const template = document.createElement('template');
// eslint-disable-next-line no-unsanitized/property
template.innerHTML = rulersTemplate;
this.editor.$svgEditor.append(template.content.cloneNode(true));
const { $id } = this.svgCanvas;
this.rulerX = $id('ruler_x');
this.rulerY = $id('ruler_y');
this.rulerCorner = $id('ruler_corner');
const template = document.createElement('template')
template.innerHTML = rulersTemplate
this.editor.$svgEditor.append(template.content.cloneNode(true))
const { $id } = SvgCanvas
this.rulerX = $id('ruler_x')
this.rulerY = $id('ruler_y')
this.rulerCorner = $id('ruler_corner')
}
display (on) {
if (on) {
this.rulerX.style.removeProperty('display');
this.rulerY.style.removeProperty('display');
this.rulerCorner.style.removeProperty('display');
this.rulerX.style.removeProperty('display')
this.rulerY.style.removeProperty('display')
this.rulerCorner.style.removeProperty('display')
} else {
this.rulerX.style.display = 'none';
this.rulerY.style.display = 'none';
this.rulerCorner.style.display = 'none';
this.rulerX.style.display = 'none'
this.rulerY.style.display = 'none'
this.rulerCorner.style.display = 'none'
}
}
/**
* @type {Module}
*/
manageScroll () {
if (this.rulerX) this.rulerX.scrollLeft = this.editor.workarea.scrollLeft;
if (this.rulerY) this.rulerY.scrollTop = this.editor.workarea.scrollTop;
if (this.rulerX) this.rulerX.scrollLeft = this.editor.workarea.scrollLeft
if (this.rulerY) this.rulerY.scrollTop = this.editor.workarea.scrollTop
}
/**
@ -53,165 +55,165 @@ class Rulers {
* @returns {void}
*/
updateRulers (scanvas, zoom) {
if (!zoom) { zoom = this.svgCanvas.getZoom(); }
if (!scanvas) { scanvas = document.getElementById('svgcanvas'); }
if (!zoom) { zoom = this.svgCanvas.getZoom() }
if (!scanvas) { scanvas = document.getElementById('svgcanvas') }
let d; let i;
const limit = 30000;
const contentElem = this.svgCanvas.getContentElem();
const units = getTypeMap();
const unit = units[this.editor.configObj.curConfig.baseUnit]; // 1 = 1px
let d; let i
const limit = 30000
const contentElem = this.svgCanvas.getSvgContent()
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 { $id } = this.svgCanvas;
const $hcanvOrig = $id('ruler_' + dim).querySelector('canvas');
const isX = (d === 0)
const dim = isX ? 'x' : 'y'
const lentype = isX ? 'width' : 'height'
const contentDim = Number(contentElem.getAttribute(dim))
const { $id } = SvgCanvas
const $hcanvOrig = $id('ruler_' + dim).querySelector('canvas')
// Bit of a hack to fully clear the canvas in Safari & IE9
const $hcanv = $hcanvOrig.cloneNode(true);
$hcanvOrig.replaceWith($hcanv);
const $hcanv = $hcanvOrig.cloneNode(true)
$hcanvOrig.replaceWith($hcanv)
const hcanv = $hcanv;
const hcanv = $hcanv
// Set the canvas size to the width of the container
let rulerLen;
if(lentype === 'width'){
rulerLen = parseFloat(getComputedStyle(scanvas, null).width.replace("px", ""));
} else if(lentype === 'height'){
rulerLen = parseFloat(getComputedStyle(scanvas, null).height.replace("px", ""));
let rulerLen
if (lentype === 'width') {
rulerLen = parseFloat(getComputedStyle(scanvas, null).width.replace('px', ''))
} else if (lentype === 'height') {
rulerLen = parseFloat(getComputedStyle(scanvas, null).height.replace('px', ''))
}
const totalLen = rulerLen;
hcanv.parentNode.style[lentype] = totalLen + 'px';
let ctx = hcanv.getContext('2d');
let ctxArr; let num; let ctxArrNum;
const totalLen = rulerLen
hcanv.parentNode.style[lentype] = totalLen + 'px'
let ctx = hcanv.getContext('2d')
let ctxArr; let num; let ctxArrNum
ctx.fillStyle = 'rgb(200,0,0)';
ctx.fillRect(0, 0, hcanv.width, hcanv.height);
ctx.fillStyle = 'rgb(200,0,0)'
ctx.fillRect(0, 0, hcanv.width, hcanv.height)
// Remove any existing canvasses
const elements = Array.prototype.filter.call($hcanv.parentNode.children, function(child){
return child !== $hcanv;
});
Array.from(elements).forEach(function(element) {
element.remove();
});
const elements = Array.prototype.filter.call($hcanv.parentNode.children, function (child) {
return child !== $hcanv
})
Array.from(elements).forEach(function (element) {
element.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;
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');
hcanv[lentype] = limit
copy = hcanv.cloneNode(true)
hcanv.parentNode.append(copy)
ctxArr[i] = copy.getContext('2d')
}
copy[lentype] = rulerLen % limit;
copy[lentype] = rulerLen % limit
// set copy width to last
rulerLen = limit;
rulerLen = limit
}
hcanv[lentype] = rulerLen;
hcanv[lentype] = rulerLen
const uMulti = unit * zoom;
const uMulti = unit * zoom
// Calculate the main number interval
const rawM = 50 / uMulti;
let multi = 1;
const rawM = 50 / uMulti
let multi = 1
for (i = 0; i < this.rulerIntervals.length; i++) {
num = this.rulerIntervals[i];
multi = num;
num = this.rulerIntervals[i]
multi = num
if (rawM <= num) {
break;
break
}
}
const bigInt = multi * uMulti;
const bigInt = multi * uMulti
ctx.font = '9px sans-serif';
ctx.font = '9px sans-serif'
let rulerD = ((contentDim / uMulti) % multi) * uMulti;
let labelPos = rulerD - bigInt;
let rulerD = ((contentDim / uMulti) % multi) * uMulti
let labelPos = rulerD - bigInt
// draw big intervals
let ctxNum = 0;
let ctxNum = 0
while (rulerD < totalLen) {
labelPos += bigInt;
labelPos += bigInt
// const realD = rulerD - contentDim; // Currently unused
const curD = Math.round(rulerD) + 0.5;
const curD = Math.round(rulerD) + 0.5
if (isX) {
ctx.moveTo(curD, 15);
ctx.lineTo(curD, 0);
ctx.moveTo(curD, 15)
ctx.lineTo(curD, 0)
} else {
ctx.moveTo(15, curD);
ctx.lineTo(0, curD);
ctx.moveTo(15, curD)
ctx.lineTo(0, curD)
}
num = (labelPos - contentDim) / uMulti;
let label;
num = (labelPos - contentDim) / uMulti
let label
if (multi >= 1) {
label = Math.round(num);
label = Math.round(num)
} else {
const decs = String(multi).split('.')[1].length;
label = num.toFixed(decs);
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';
label = (label / 1000) + 'K'
}
if (isX) {
ctx.fillText(label, rulerD + 2, 8);
ctx.fillText(label, rulerD + 2, 8)
} else {
// draw label vertically
const str = String(label).split('');
const str = String(label).split('')
for (i = 0; i < str.length; i++) {
ctx.fillText(str[i], 1, (rulerD + 9) + i * 9);
ctx.fillText(str[i], 1, (rulerD + 9) + i * 9)
}
}
const part = bigInt / 10;
const part = bigInt / 10
// draw the small intervals
for (i = 1; i < 10; i++) {
let subD = Math.round(rulerD + part * i) + 0.5;
let subD = Math.round(rulerD + part * i) + 0.5
if (ctxArr && subD > rulerLen) {
ctxNum++;
ctx.stroke();
ctxNum++
ctx.stroke()
if (ctxNum >= ctxArrNum) {
i = 10;
rulerD = totalLen;
continue;
i = 10
rulerD = totalLen
continue
}
ctx = ctxArr[ctxNum];
rulerD -= limit;
subD = Math.round(rulerD + part * i) + 0.5;
ctx = ctxArr[ctxNum]
rulerD -= limit
subD = Math.round(rulerD + part * i) + 0.5
}
// odd lines are slighly longer
const lineNum = (i % 2) ? 12 : 10;
const lineNum = (i % 2) ? 12 : 10
if (isX) {
ctx.moveTo(subD, 15);
ctx.lineTo(subD, lineNum);
ctx.moveTo(subD, 15)
ctx.lineTo(subD, lineNum)
} else {
ctx.moveTo(15, subD);
ctx.lineTo(lineNum, subD);
ctx.moveTo(15, subD)
ctx.lineTo(lineNum, subD)
}
}
rulerD += bigInt;
rulerD += bigInt
}
ctx.strokeStyle = '#000';
ctx.stroke();
ctx.strokeStyle = '#000'
ctx.stroke()
}
}
}
export default Rulers;
export default Rulers

View File

@ -1,8 +1,8 @@
const supportsSvg = function () {
return Boolean(document.createElementNS && document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect);
};
return Boolean(document.createElementNS && document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect)
}
if (!supportsSvg()) {
window.location = './browser-not-supported.html';
window.location = './browser-not-supported.html'
}
export {};
export {}

View File

@ -1,4 +1,4 @@
import { jGraduate } from './jgraduate/jQuery.jGraduate.js';
import { jGraduate } from './jgraduate/jQuery.jGraduate.js'
/**
*
*/
@ -16,17 +16,17 @@ class PaintBox {
<defs><linearGradient id="gradbox_${PaintBox.ctr++}"/></defs>
</svg>`,
'text/xml'
);
)
let docElem = svgdocbox.documentElement;
docElem = document.importNode(docElem, true);
container.appendChild(docElem);
let docElem = svgdocbox.documentElement
docElem = document.importNode(docElem, true)
container.appendChild(docElem)
this.rect = docElem.firstElementChild;
this.defs = docElem.getElementsByTagName('defs')[0];
this.grad = this.defs.firstElementChild;
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.type = type
}
/**
@ -34,30 +34,31 @@ class PaintBox {
* @returns {void}
*/
setPaint (paint) {
this.paint = paint;
this.paint = paint
const ptype = paint.type;
const opac = paint.alpha / 100;
const ptype = paint.type
const opac = paint.alpha / 100
let fillAttr = 'none';
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 = paint[ptype];
this.defs.appendChild(this.grad);
const id = this.grad.id = 'gradbox_' + this.type;
fillAttr = 'url(#' + id + ')';
break;
}
case 'solidColor':
fillAttr = (paint[ptype] !== 'none') ? '#' + paint[ptype] : paint[ptype]
break
case 'linearGradient':
case 'radialGradient': {
this.grad.remove()
this.grad = paint[ptype]
this.defs.appendChild(this.grad)
const id = this.grad.id = 'gradbox_' + this.type
fillAttr = 'url(#' + id + ')'
break
}
}
this.rect.setAttribute('fill', fillAttr);
this.rect.setAttribute('opacity', opac);
this.rect.setAttribute('fill', fillAttr)
this.rect.setAttribute('opacity', opac)
}
/**
* @param {PlainObject} svgCanvas
* @param {string} color
@ -67,20 +68,20 @@ class PaintBox {
*/
static getPaint (svgCanvas, color, opac, type) {
// update the editor's fill paint
const opts = { alpha: opac };
const opts = { alpha: opac }
if (color.startsWith('url(#')) {
let refElem = svgCanvas.getRefElem(color);
refElem = (refElem) ? refElem.cloneNode(true) : document.querySelectorAll('#' + type + '_color defs *')[0];
let refElem = svgCanvas.getRefElem(color)
refElem = (refElem) ? refElem.cloneNode(true) : document.querySelectorAll('#' + type + '_color defs *')[0]
if (!refElem) {
console.error(`the color ${color} is referenced by an url that can't be identified - using 'none'`);
opts.solidColor = 'none';
console.error(`the color ${color} is referenced by an url that can't be identified - using 'none'`)
opts.solidColor = 'none'
} else {
opts[refElem.tagName] = refElem;
opts[refElem.tagName] = refElem
}
} else if (color.startsWith('#')) {
opts.solidColor = color.substr(1);
opts.solidColor = color.substr(1)
}
return new jGraduate.Paint(opts);
return new jGraduate.Paint(opts)
}
/**
@ -89,59 +90,59 @@ class PaintBox {
* @returns {any}
*/
update (svgcanvas, selectedElement) {
if (!selectedElement) { return null; }
if (!selectedElement) { return null }
const { type } = this;
const { type } = this
switch (selectedElement.tagName) {
case 'use':
case 'image':
case 'foreignObject':
case 'use':
case 'image':
case 'foreignObject':
// These elements don't have fill or stroke, so don't change
// the current value
return null;
case 'g':
case 'a': {
const childs = selectedElement.getElementsByTagName('*');
return null
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;
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) {
if (gPaint === null) {
// No common color, don't update anything
this._paintColor = null;
return null;
}
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;
}
this._paintColor = null
return null
}
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;
}
const defColor = type === 'fill' ? 'black' : 'none'
this._paintColor = selectedElement.getAttribute(type) || defColor
}
}
this._paintOpacity *= 100;
this._paintOpacity *= 100
const paint = PaintBox.getPaint(svgcanvas, this._paintColor, this._paintOpacity, type);
const paint = PaintBox.getPaint(svgcanvas, this._paintColor, this._paintOpacity, type)
// update the rect inside #fill_color/#stroke_color
this.setPaint(paint);
return (paint);
this.setPaint(paint)
return (paint)
}
}
PaintBox.ctr = 0;
PaintBox.ctr = 0
export default PaintBox;
export default PaintBox

View File

@ -1,15 +1,14 @@
import './seButton.js';
import './seFlyingButton.js';
import './seExplorerButton.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';
import './seSelect.js';
import './seText.js';
import './seButton.js'
import './seFlyingButton.js'
import './seExplorerButton.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'
import './seSelect.js'
import './seText.js'

View File

@ -8,8 +8,8 @@
* @returns {Float}
*/
function toFixedNumeric (value, precision) {
if (precision === undefined) precision = 0;
return Math.round(value * (10 ** precision)) / (10 ** precision);
if (precision === undefined) precision = 0
return Math.round(value * (10 ** precision)) / (10 ** precision)
}
/**
* Whether a value is `null` or `undefined`.
@ -17,8 +17,8 @@ function toFixedNumeric (value, precision) {
* @returns {boolean}
*/
const isNullish = (val) => {
return val === null || val === undefined;
};
return val === null || val === undefined
}
/**
* Controls for all the input elements for the typing in color values.
*/
@ -30,8 +30,8 @@ export default class ColorValuePicker {
* @param {Float} alphaPrecision
*/
constructor (picker, color, bindedHex, alphaPrecision) {
const that = this; // private properties and methods
const inputs = picker.querySelectorAll('td.Text input');
const that = this // private properties and methods
const inputs = picker.querySelectorAll('td.Text input')
// input box key down - use arrows to alter color
/**
*
@ -39,95 +39,95 @@ export default class ColorValuePicker {
* @returns {Event|false|void}
*/
function keyDown (e) {
if (e.target.value === '' && e.target !== hex && ((!isNullish(bindedHex) && e.target !== bindedHex) || isNullish(bindedHex))) return undefined;
if (!validateKey(e)) return e;
if (e.target.value === '' && e.target !== hex && ((!isNullish(bindedHex) && e.target !== bindedHex) || isNullish(bindedHex))) return undefined
if (!validateKey(e)) return e
switch (e.target) {
case red:
switch (e.keyCode) {
case 38:
red.value = setValueInRange.call(that, (red.value << 0) + 1, 0, 255);
color.val('r', red.value, e.target);
return false;
case 40:
red.value = setValueInRange.call(that, (red.value << 0) - 1, 0, 255);
color.val('r', red.value, e.target);
return false;
}
break;
case green:
switch (e.keyCode) {
case 38:
green.value = setValueInRange.call(that, (green.value << 0) + 1, 0, 255);
color.val('g', green.value, e.target);
return false;
case 40:
green.value = setValueInRange.call(that, (green.value << 0) - 1, 0, 255);
color.val('g', green.value, e.target);
return false;
}
break;
case blue:
switch (e.keyCode) {
case 38:
blue.value = setValueInRange.call(that, (blue.value << 0) + 1, 0, 255);
color.val('b', blue.value, e.target);
return false;
case 40:
blue.value = setValueInRange.call(that, (blue.value << 0) - 1, 0, 255);
color.val('b', blue.value, e.target);
return false;
}
break;
case alpha:
switch (e.keyCode) {
case 38:
alpha.value = setValueInRange.call(that, Number.parseFloat(alpha.value) + 1, 0, 100);
color.val('a', toFixedNumeric((alpha.value * 255) / 100, alphaPrecision), e.target);
return false;
case 40:
alpha.value = setValueInRange.call(that, Number.parseFloat(alpha.value) - 1, 0, 100);
color.val('a', toFixedNumeric((alpha.value * 255) / 100, alphaPrecision), e.target);
return false;
}
break;
case hue:
switch (e.keyCode) {
case 38:
hue.value = setValueInRange.call(that, (hue.value << 0) + 1, 0, 360);
color.val('h', hue.value, e.target);
return false;
case 40:
hue.value =setValueInRange.call(that, (hue.value << 0) - 1, 0, 360);
color.val('h', hue.value, e.target);
return false;
}
break;
case saturation:
switch (e.keyCode) {
case 38:
saturation.value = setValueInRange.call(that, (saturation.value << 0) + 1, 0, 100);
color.val('s', saturation.value, e.target);
return false;
case 40:
saturation.value = setValueInRange.call(that, (saturation.value << 0) - 1, 0, 100);
color.val('s', saturation.value, e.target);
return false;
}
break;
case value:
switch (e.keyCode) {
case 38:
value.value = setValueInRange.call(that, (value.value << 0) + 1, 0, 100);
color.val('v', value.value, e.target);
return false;
case 40:
value.value = setValueInRange.call(that, (value.value << 0) - 1, 0, 100);
color.val('v', value.value, e.target);
return false;
}
break;
case red:
switch (e.keyCode) {
case 38:
red.value = setValueInRange.call(that, (red.value << 0) + 1, 0, 255)
color.val('r', red.value, e.target)
return false
case 40:
red.value = setValueInRange.call(that, (red.value << 0) - 1, 0, 255)
color.val('r', red.value, e.target)
return false
}
break
case green:
switch (e.keyCode) {
case 38:
green.value = setValueInRange.call(that, (green.value << 0) + 1, 0, 255)
color.val('g', green.value, e.target)
return false
case 40:
green.value = setValueInRange.call(that, (green.value << 0) - 1, 0, 255)
color.val('g', green.value, e.target)
return false
}
break
case blue:
switch (e.keyCode) {
case 38:
blue.value = setValueInRange.call(that, (blue.value << 0) + 1, 0, 255)
color.val('b', blue.value, e.target)
return false
case 40:
blue.value = setValueInRange.call(that, (blue.value << 0) - 1, 0, 255)
color.val('b', blue.value, e.target)
return false
}
break
case alpha:
switch (e.keyCode) {
case 38:
alpha.value = setValueInRange.call(that, Number.parseFloat(alpha.value) + 1, 0, 100)
color.val('a', toFixedNumeric((alpha.value * 255) / 100, alphaPrecision), e.target)
return false
case 40:
alpha.value = setValueInRange.call(that, Number.parseFloat(alpha.value) - 1, 0, 100)
color.val('a', toFixedNumeric((alpha.value * 255) / 100, alphaPrecision), e.target)
return false
}
break
case hue:
switch (e.keyCode) {
case 38:
hue.value = setValueInRange.call(that, (hue.value << 0) + 1, 0, 360)
color.val('h', hue.value, e.target)
return false
case 40:
hue.value = setValueInRange.call(that, (hue.value << 0) - 1, 0, 360)
color.val('h', hue.value, e.target)
return false
}
break
case saturation:
switch (e.keyCode) {
case 38:
saturation.value = setValueInRange.call(that, (saturation.value << 0) + 1, 0, 100)
color.val('s', saturation.value, e.target)
return false
case 40:
saturation.value = setValueInRange.call(that, (saturation.value << 0) - 1, 0, 100)
color.val('s', saturation.value, e.target)
return false
}
break
case value:
switch (e.keyCode) {
case 38:
value.value = setValueInRange.call(that, (value.value << 0) + 1, 0, 100)
color.val('v', value.value, e.target)
return false
case 40:
value.value = setValueInRange.call(that, (value.value << 0) - 1, 0, 100)
color.val('v', value.value, e.target)
return false
}
break
}
return undefined;
return undefined
}
// input box key up - validate value and set color
/**
@ -138,53 +138,53 @@ export default class ColorValuePicker {
function keyUp (e) {
if (e.target.value === '' && e.target !== hex &&
((!isNullish(bindedHex) && e.target !== bindedHex) ||
isNullish(bindedHex))) return undefined;
if (!validateKey(e)) return e;
isNullish(bindedHex))) return undefined
if (!validateKey(e)) return e
switch (e.target) {
case red:
red.value = setValueInRange.call(that, red.value, 0, 255);
color.val('r', red.value, e.target);
break;
case green:
green.value = setValueInRange.call(that, green.value, 0, 255);
color.val('g', green.value, e.target);
break;
case blue:
blue.value = setValueInRange.call(that, blue.value, 0, 255);
color.val('b', blue.value, e.target);
break;
case alpha:
alpha.value = setValueInRange.call(that, alpha.value, 0, 100);
color.val('a', toFixedNumeric((alpha.value * 255) / 100, alphaPrecision), e.target);
break;
case hue:
hue.value = setValueInRange.call(that, hue.value, 0, 360);
color.val('h', hue.value, e.target);
break;
case saturation:
saturation.value = setValueInRange.call(that, saturation.value, 0, 100);
color.val('s', saturation.value, e.target);
break;
case value:
value.value = setValueInRange.call(that, value.value, 0, 100);
color.val('v', value.value, e.target);
break;
case hex:
hex.value = hex.value.replace(/[^a-fA-F\d]/g, '').toLowerCase().substring(0, 6);
bindedHex && bindedHex.val(hex.value);
color.val('hex', hex.value !== '' ? hex.value : null, e.target);
break;
case bindedHex:
bindedHex.value = bindedHex.value.replace(/[^a-fA-F\d]/g, '').toLowerCase().substring(0, 6);
hex.val(bindedHex.value);
color.val('hex', bindedHex.value !== '' ? bindedHex.value : null, e.target);
break;
case ahex:
ahex.value = ahex.value.replace(/[^a-fA-F\d]/g, '').toLowerCase().substring(0, 2);
color.val('a', !isNullish(ahex.value) ? Number.parseInt(ahex.value, 16) : null, e.target);
break;
case red:
red.value = setValueInRange.call(that, red.value, 0, 255)
color.val('r', red.value, e.target)
break
case green:
green.value = setValueInRange.call(that, green.value, 0, 255)
color.val('g', green.value, e.target)
break
case blue:
blue.value = setValueInRange.call(that, blue.value, 0, 255)
color.val('b', blue.value, e.target)
break
case alpha:
alpha.value = setValueInRange.call(that, alpha.value, 0, 100)
color.val('a', toFixedNumeric((alpha.value * 255) / 100, alphaPrecision), e.target)
break
case hue:
hue.value = setValueInRange.call(that, hue.value, 0, 360)
color.val('h', hue.value, e.target)
break
case saturation:
saturation.value = setValueInRange.call(that, saturation.value, 0, 100)
color.val('s', saturation.value, e.target)
break
case value:
value.value = setValueInRange.call(that, value.value, 0, 100)
color.val('v', value.value, e.target)
break
case hex:
hex.value = hex.value.replace(/[^a-fA-F\d]/g, '').toLowerCase().substring(0, 6)
bindedHex && bindedHex.val(hex.value)
color.val('hex', hex.value !== '' ? hex.value : null, e.target)
break
case bindedHex:
bindedHex.value = bindedHex.value.replace(/[^a-fA-F\d]/g, '').toLowerCase().substring(0, 6)
hex.val(bindedHex.value)
color.val('hex', bindedHex.value !== '' ? bindedHex.value : null, e.target)
break
case ahex:
ahex.value = ahex.value.replace(/[^a-fA-F\d]/g, '').toLowerCase().substring(0, 2)
color.val('a', !isNullish(ahex.value) ? Number.parseInt(ahex.value, 16) : null, e.target)
break
}
return undefined;
return undefined
}
// input box blur - reset to original if value empty
/**
@ -194,44 +194,44 @@ export default class ColorValuePicker {
function blur (e) {
if (!isNullish(color.value)) {
switch (e.target) {
case red:
color.value = 'r';
red.value = color.value;
break;
case green:
color.value = 'g';
green.value = color.value;
break;
case blue:
color.value = 'b';
blue.value = color.value;
break;
case alpha:
color.value = 'a';
alpha.value = toFixedNumeric((color.value * 100) / 255, alphaPrecision);
break;
case hue:
color.value = 'h';
hue.value = color.value;
break;
case saturation:
color.value = 's';
saturation.value = color.value;
break;
case value:
color.value = 'v';
value.value = color.value;
break;
case hex:
case bindedHex:
color.value = 'hex';
hex.value = color.value;
bindedHex.value = color.value;
break;
case ahex:
color.value = 'ahex';
ahex.value = color.value.substring(6);
break;
case red:
color.value = 'r'
red.value = color.value
break
case green:
color.value = 'g'
green.value = color.value
break
case blue:
color.value = 'b'
blue.value = color.value
break
case alpha:
color.value = 'a'
alpha.value = toFixedNumeric((color.value * 100) / 255, alphaPrecision)
break
case hue:
color.value = 'h'
hue.value = color.value
break
case saturation:
color.value = 's'
saturation.value = color.value
break
case value:
color.value = 'v'
value.value = color.value
break
case hex:
case bindedHex:
color.value = 'hex'
hex.value = color.value
bindedHex.value = color.value
break
case ahex:
color.value = 'ahex'
ahex.value = color.value.substring(6)
break
}
}
}
@ -241,17 +241,17 @@ export default class ColorValuePicker {
*/
function validateKey (e) {
switch (e.keyCode) {
case 9:
case 16:
case 29:
case 37:
case 39:
return false;
case 'c'.charCodeAt():
case 'v'.charCodeAt():
if (e.ctrlKey) return false;
case 9:
case 16:
case 29:
case 37:
case 39:
return false
case 'c'.charCodeAt():
case 'v'.charCodeAt():
if (e.ctrlKey) return false
}
return true;
return true
}
/**
@ -262,10 +262,10 @@ export default class ColorValuePicker {
* @returns {Float|string} Returns a number or numeric string
*/
function setValueInRange (value, min, max) {
if (value === '' || isNaN(value)) return min;
if (value > max) return max;
if (value < min) return min;
return value;
if (value === '' || isNaN(value)) return min
if (value > max) return max
if (value < min) return min
return value
}
/**
* @param {external:jQuery} ui
@ -273,117 +273,117 @@ export default class ColorValuePicker {
* @returns {void}
*/
function colorChanged (ui, context) {
const all = ui.val('all');
if (context !== red) red.value = (!isNullish(all) ? all.r : '');
if (context !== green) green.value = (!isNullish(all) ? all.g : '');
if (context !== blue) blue.value = (!isNullish(all) ? all.b : '');
if (alpha && context !== alpha) alpha.value = (!isNullish(all) ? toFixedNumeric((all.a * 100) / 255, alphaPrecision) : '');
if (context !== hue) hue.value = (!isNullish(all) ? all.h : '');
if (context !== saturation) saturation.value = (!isNullish(all) ? all.s : '');
if (context !== value) value.value = (!isNullish(all) ? all.v : '');
if (context !== hex && ((bindedHex && context !== bindedHex) || !bindedHex)) hex.value = (!isNullish(all) ? all.hex : '');
if (bindedHex && context !== bindedHex && context !== hex) bindedHex.value = (!isNullish(all) ? all.hex : '');
if (ahex && context !== ahex) ahex.value = (!isNullish(all) ? all.ahex.substring(6) : '');
const all = ui.val('all')
if (context !== red) red.value = (!isNullish(all) ? all.r : '')
if (context !== green) green.value = (!isNullish(all) ? all.g : '')
if (context !== blue) blue.value = (!isNullish(all) ? all.b : '')
if (alpha && context !== alpha) alpha.value = (!isNullish(all) ? toFixedNumeric((all.a * 100) / 255, alphaPrecision) : '')
if (context !== hue) hue.value = (!isNullish(all) ? all.h : '')
if (context !== saturation) saturation.value = (!isNullish(all) ? all.s : '')
if (context !== value) value.value = (!isNullish(all) ? all.v : '')
if (context !== hex && ((bindedHex && context !== bindedHex) || !bindedHex)) hex.value = (!isNullish(all) ? all.hex : '')
if (bindedHex && context !== bindedHex && context !== hex) bindedHex.value = (!isNullish(all) ? all.hex : '')
if (ahex && context !== ahex) ahex.value = (!isNullish(all) ? all.ahex.substring(6) : '')
}
/**
* Unbind all events and null objects.
* @returns {void}
*/
function destroy () {
red.removeEventListener('keyup', keyUp);
green.removeEventListener('keyup', keyUp);
blue.removeEventListener('keyup', keyUp);
hue.removeEventListener('keyup', keyUp);
saturation.removeEventListener('keyup', keyUp);
value.removeEventListener('keyup', keyUp);
hex.removeEventListener('keyup', keyUp);
red.removeEventListener('keyup', keyUp)
green.removeEventListener('keyup', keyUp)
blue.removeEventListener('keyup', keyUp)
hue.removeEventListener('keyup', keyUp)
saturation.removeEventListener('keyup', keyUp)
value.removeEventListener('keyup', keyUp)
hex.removeEventListener('keyup', keyUp)
red.removeEventListener('blur', blur);
green.removeEventListener('blur', blur);
blue.removeEventListener('blur', blur);
hue.removeEventListener('blur', blur);
saturation.removeEventListener('blur', blur);
value.removeEventListener('blur', blur);
hex.removeEventListener('blur', blur);
red.removeEventListener('blur', blur)
green.removeEventListener('blur', blur)
blue.removeEventListener('blur', blur)
hue.removeEventListener('blur', blur)
saturation.removeEventListener('blur', blur)
value.removeEventListener('blur', blur)
hex.removeEventListener('blur', blur)
red.removeEventListener('keydown', keyDown);
green.removeEventListener('keydown', keyDown);
blue.removeEventListener('keydown', keyDown);
hue.removeEventListener('keydown', keyDown);
saturation.removeEventListener('keydown', keyDown);
value.removeEventListener('keydown', keyDown);
red.removeEventListener('keydown', keyDown)
green.removeEventListener('keydown', keyDown)
blue.removeEventListener('keydown', keyDown)
hue.removeEventListener('keydown', keyDown)
saturation.removeEventListener('keydown', keyDown)
value.removeEventListener('keydown', keyDown)
if (alpha !== null) {
alpha.removeEventListener('keyup', keyUp);
alpha.removeEventListener('blur', blur);
alpha.removeEventListener('keydown', keyDown);
alpha.removeEventListener('keyup', keyUp)
alpha.removeEventListener('blur', blur)
alpha.removeEventListener('keydown', keyDown)
}
if (ahex !== null) {
ahex.removeEventListener('keyup', keyUp);
ahex.removeEventListener('blur', blur);
ahex.removeEventListener('keyup', keyUp)
ahex.removeEventListener('blur', blur)
}
if (bindedHex !== null) {
bindedHex.removeEventListener('keyup', keyUp);
bindedHex.removeEventListener('blur', blur);
bindedHex.removeEventListener('keyup', keyUp)
bindedHex.removeEventListener('blur', blur)
}
color.unbind(colorChanged);
red = null;
green = null;
blue = null;
alpha = null;
hue = null;
saturation = null;
value = null;
hex = null;
ahex = null;
color.unbind(colorChanged)
red = null
green = null
blue = null
alpha = null
hue = null
saturation = null
value = null
hex = null
ahex = null
}
let
red = inputs[3];
let green = inputs[4];
let blue = inputs[5];
let alpha = inputs.length > 7 ? inputs[6] : null;
let hue = inputs[0];
let saturation = inputs[1];
let value = inputs[2];
let hex = inputs[(inputs.length > 7) ? 7 : 6];
let ahex = inputs.length > 7 ? inputs[8] : null;
Object.assign(that, { destroy });
red.addEventListener('keyup', keyUp);
green.addEventListener('keyup', keyUp);
blue.addEventListener('keyup', keyUp);
hue.addEventListener('keyup', keyUp);
saturation.addEventListener('keyup', keyUp);
value.addEventListener('keyup', keyUp);
hex.addEventListener('keyup', keyUp);
red = inputs[3]
let green = inputs[4]
let blue = inputs[5]
let alpha = inputs.length > 7 ? inputs[6] : null
let hue = inputs[0]
let saturation = inputs[1]
let value = inputs[2]
let hex = inputs[(inputs.length > 7) ? 7 : 6]
let ahex = inputs.length > 7 ? inputs[8] : null
Object.assign(that, { destroy })
red.addEventListener('keyup', keyUp)
green.addEventListener('keyup', keyUp)
blue.addEventListener('keyup', keyUp)
hue.addEventListener('keyup', keyUp)
saturation.addEventListener('keyup', keyUp)
value.addEventListener('keyup', keyUp)
hex.addEventListener('keyup', keyUp)
red.addEventListener('blur', blur);
green.addEventListener('blur', blur);
blue.addEventListener('blur', blur);
hue.addEventListener('blur', blur);
saturation.addEventListener('blur', blur);
value.addEventListener('blur', blur);
hex.addEventListener('blur', blur);
red.addEventListener('blur', blur)
green.addEventListener('blur', blur)
blue.addEventListener('blur', blur)
hue.addEventListener('blur', blur)
saturation.addEventListener('blur', blur)
value.addEventListener('blur', blur)
hex.addEventListener('blur', blur)
red.addEventListener('keydown', keyDown);
green.addEventListener('keydown', keyDown);
blue.addEventListener('keydown', keyDown);
hue.addEventListener('keydown', keyDown);
saturation.addEventListener('keydown', keyDown);
value.addEventListener('keydown', keyDown);
red.addEventListener('keydown', keyDown)
green.addEventListener('keydown', keyDown)
blue.addEventListener('keydown', keyDown)
hue.addEventListener('keydown', keyDown)
saturation.addEventListener('keydown', keyDown)
value.addEventListener('keydown', keyDown)
if (alpha !== null) {
alpha.addEventListener('keyup', keyUp);
alpha.addEventListener('blur', blur);
alpha.addEventListener('keydown', keyDown);
alpha.addEventListener('keyup', keyUp)
alpha.addEventListener('blur', blur)
alpha.addEventListener('keydown', keyDown)
}
if (ahex !== null) {
ahex.addEventListener('keyup', keyUp);
ahex.addEventListener('blur', blur);
ahex.addEventListener('keyup', keyUp)
ahex.addEventListener('blur', blur)
}
if (bindedHex !== null) {
bindedHex.addEventListener('keyup', keyUp);
bindedHex.addEventListener('blur', blur);
bindedHex.addEventListener('keyup', keyUp)
bindedHex.addEventListener('blur', blur)
}
color.bind(colorChanged);
color.bind(colorChanged)
}
}

View File

@ -1,12 +1,12 @@
import { findPos } from './Util.js';
import { findPos } from './Util.js'
/**
* Whether a value is `null` or `undefined`.
* @param {any} val
* @returns {boolean}
*/
const isNullish = (val) => {
return val === null || val === undefined;
};
return val === null || val === undefined
}
/**
* Encapsulate slider functionality for the ColorMap and ColorBar -
* could be useful to use a jQuery UI draggable for this with certain extensions.
@ -18,7 +18,7 @@ export default class Slider {
* @param {module:jPicker.SliderOptions} options
*/
constructor (bar, options) {
const that = this;
const that = this
/**
* Fire events on the supplied `context`
* @param {module:jPicker.JPickerInit} context
@ -26,8 +26,8 @@ export default class Slider {
*/
function fireChangeEvents (context) {
changeEvents.forEach((changeEvent) => {
changeEvent.call(that, that, context);
});
changeEvent.call(that, that, context)
})
}
/**
@ -36,17 +36,17 @@ export default class Slider {
* @returns {void}
*/
function mouseDown (e) {
const off = findPos(bar);
offset = { l: off.left | 0, t: off.top | 0 };
clearTimeout(timeout);
const off = findPos(bar)
offset = { l: off.left | 0, t: off.top | 0 }
clearTimeout(timeout)
// using setTimeout for visual updates - once the style is updated the browser will re-render internally allowing the next Javascript to run
timeout = setTimeout(function () {
setValuesFromMousePosition.call(that, e);
}, 0);
setValuesFromMousePosition.call(that, e)
}, 0)
// Bind mousemove and mouseup event to the document so it responds when dragged of of the bar - we will unbind these when on mouseup to save processing
document.addEventListener('mousemove', mouseMove);
document.addEventListener('mouseup', mouseUp);
e.preventDefault(); // don't try to select anything or drag the image to the desktop
document.addEventListener('mousemove', mouseMove)
document.addEventListener('mouseup', mouseUp)
e.preventDefault() // don't try to select anything or drag the image to the desktop
}
/**
* Set the values as the mouse moves.
@ -54,13 +54,13 @@ export default class Slider {
* @returns {false}
*/
function mouseMove (e) {
clearTimeout(timeout);
clearTimeout(timeout)
timeout = setTimeout(function () {
setValuesFromMousePosition.call(that, e);
}, 0);
e.stopPropagation();
e.preventDefault();
return false;
setValuesFromMousePosition.call(that, e)
}, 0)
e.stopPropagation()
e.preventDefault()
return false
}
/**
* Unbind the document events - they aren't needed when not dragging.
@ -68,11 +68,11 @@ export default class Slider {
* @returns {false}
*/
function mouseUp (e) {
document.removeEventListener('mousemove', mouseMove);
document.removeEventListener('mouseup', mouseUp);
e.stopPropagation();
e.preventDefault();
return false;
document.removeEventListener('mousemove', mouseMove)
document.removeEventListener('mouseup', mouseUp)
e.stopPropagation()
e.preventDefault()
return false
}
/**
@ -81,19 +81,19 @@ export default class Slider {
* @returns {void}
*/
function setValuesFromMousePosition (e) {
const barW = bar.w; // local copies for YUI compressor
const barH = bar.h;
let locX = e.pageX - offset.l;
let locY = e.pageY - offset.t;
const barW = bar.w // local copies for YUI compressor
const barH = bar.h
let locX = e.pageX - offset.l
let locY = e.pageY - offset.t
// keep the arrow within the bounds of the bar
if (locX < 0) locX = 0;
else if (locX > barW) locX = barW;
if (locY < 0) locY = 0;
else if (locY > barH) locY = barH;
if (locX < 0) locX = 0
else if (locX > barW) locX = barW
if (locY < 0) locY = 0
else if (locY > barH) locY = barH
val.call(that, 'xy', {
x: ((locX / barW) * rangeX) + minX,
y: ((locY / barH) * rangeY) + minY
});
})
}
/**
*
@ -101,33 +101,33 @@ export default class Slider {
*/
function draw () {
const
barW = bar.w;
const barH = bar.h;
const arrowW = arrow.w;
const arrowH = arrow.h;
let arrowOffsetX = 0;
let arrowOffsetY = 0;
barW = bar.w
const barH = bar.h
const arrowW = arrow.w
const arrowH = arrow.h
let arrowOffsetX = 0
let arrowOffsetY = 0
setTimeout(function () {
if (rangeX > 0) { // range is greater than zero
// constrain to bounds
if (x === maxX) arrowOffsetX = barW;
else arrowOffsetX = ((x / rangeX) * barW) | 0;
if (x === maxX) arrowOffsetX = barW
else arrowOffsetX = ((x / rangeX) * barW) | 0
}
if (rangeY > 0) { // range is greater than zero
// constrain to bounds
if (y === maxY) arrowOffsetY = barH;
else arrowOffsetY = ((y / rangeY) * barH) | 0;
if (y === maxY) arrowOffsetY = barH
else arrowOffsetY = ((y / rangeY) * barH) | 0
}
// if arrow width is greater than bar width, center arrow and prevent horizontal dragging
if (arrowW >= barW) arrowOffsetX = (barW >> 1) - (arrowW >> 1); // number >> 1 - superfast bitwise divide by two and truncate (move bits over one bit discarding lowest)
else arrowOffsetX -= arrowW >> 1;
if (arrowW >= barW) arrowOffsetX = (barW >> 1) - (arrowW >> 1) // number >> 1 - superfast bitwise divide by two and truncate (move bits over one bit discarding lowest)
else arrowOffsetX -= arrowW >> 1
// if arrow height is greater than bar height, center arrow and prevent vertical dragging
if (arrowH >= barH) arrowOffsetY = (barH >> 1) - (arrowH >> 1);
else arrowOffsetY -= arrowH >> 1;
if (arrowH >= barH) arrowOffsetY = (barH >> 1) - (arrowH >> 1)
else arrowOffsetY -= arrowH >> 1
// set the arrow position based on these offsets
arrow.style.left = arrowOffsetX + 'px';
arrow.style.top = arrowOffsetY + 'px';
});
arrow.style.left = arrowOffsetX + 'px'
arrow.style.top = arrowOffsetY + 'px'
})
}
/**
@ -138,52 +138,52 @@ export default class Slider {
* @returns {module:math.XYObject|Float|void}
*/
function val (name, value, context) {
const set = value !== undefined;
const set = value !== undefined
if (!set) {
if (isNullish(name)) name = 'xy';
if (isNullish(name)) name = 'xy'
switch (name.toLowerCase()) {
case 'x': return x;
case 'y': return y;
case 'xy':
default: return { x, y };
case 'x': return x
case 'y': return y
case 'xy':
default: return { x, y }
}
}
if (!isNullish(context) && context === that) return undefined;
let changed = false;
if (!isNullish(context) && context === that) return undefined
let changed = false
let newX; let newY;
if (isNullish(name)) name = 'xy';
let newX; let newY
if (isNullish(name)) name = 'xy'
switch (name.toLowerCase()) {
case 'x':
newX = (value && ((value.x && value.x | 0) || value | 0)) || 0;
break;
case 'y':
newY = (value && ((value.y && value.y | 0) || value | 0)) || 0;
break;
case 'xy':
default:
newX = (value && value.x && value.x | 0) || 0;
newY = (value && value.y && value.y | 0) || 0;
break;
case 'x':
newX = (value && ((value.x && value.x | 0) || value | 0)) || 0
break
case 'y':
newY = (value && ((value.y && value.y | 0) || value | 0)) || 0
break
case 'xy':
default:
newX = (value && value.x && value.x | 0) || 0
newY = (value && value.y && value.y | 0) || 0
break
}
if (!isNullish(newX)) {
if (newX < minX) newX = minX;
else if (newX > maxX) newX = maxX;
if (newX < minX) newX = minX
else if (newX > maxX) newX = maxX
if (x !== newX) {
x = newX;
changed = true;
x = newX
changed = true
}
}
if (!isNullish(newY)) {
if (newY < minY) newY = minY;
else if (newY > maxY) newY = maxY;
if (newY < minY) newY = minY
else if (newY > maxY) newY = maxY
if (y !== newY) {
y = newY;
changed = true;
y = newY
changed = true
}
}
changed && fireChangeEvents.call(that, context || that);
return undefined;
changed && fireChangeEvents.call(that, context || that)
return undefined
}
/**
@ -209,89 +209,89 @@ export default class Slider {
* @returns {module:jPicker.MinMaxRangeXY|module:jPicker.MinMaxRangeX|module:jPicker.MinMaxRangeY|void}
*/
function range (name, value) {
const set = value !== undefined;
const set = value !== undefined
if (!set) {
if (isNullish(name)) name = 'all';
if (isNullish(name)) name = 'all'
switch (name.toLowerCase()) {
case 'minx': return minX;
case 'maxx': return maxX;
case 'rangex': return { minX, maxX, rangeX };
case 'miny': return minY;
case 'maxy': return maxY;
case 'rangey': return { minY, maxY, rangeY };
case 'all':
default: return { minX, maxX, rangeX, minY, maxY, rangeY };
case 'minx': return minX
case 'maxx': return maxX
case 'rangex': return { minX, maxX, rangeX }
case 'miny': return minY
case 'maxy': return maxY
case 'rangey': return { minY, maxY, rangeY }
case 'all':
default: return { minX, maxX, rangeX, minY, maxY, rangeY }
}
}
let // changed = false,
newMinX;
let newMaxX;
let newMinY;
let newMaxY;
if (isNullish(name)) name = 'all';
newMinX
let newMaxX
let newMinY
let newMaxY
if (isNullish(name)) name = 'all'
switch (name.toLowerCase()) {
case 'minx':
newMinX = (value && ((value.minX && value.minX | 0) || value | 0)) || 0;
break;
case 'maxx':
newMaxX = (value && ((value.maxX && value.maxX | 0) || value | 0)) || 0;
break;
case 'rangex':
newMinX = (value && value.minX && value.minX | 0) || 0;
newMaxX = (value && value.maxX && value.maxX | 0) || 0;
break;
case 'miny':
newMinY = (value && ((value.minY && value.minY | 0) || value | 0)) || 0;
break;
case 'maxy':
newMaxY = (value && ((value.maxY && value.maxY | 0) || value | 0)) || 0;
break;
case 'rangey':
newMinY = (value && value.minY && value.minY | 0) || 0;
newMaxY = (value && value.maxY && value.maxY | 0) || 0;
break;
case 'all':
default:
newMinX = (value && value.minX && value.minX | 0) || 0;
newMaxX = (value && value.maxX && value.maxX | 0) || 0;
newMinY = (value && value.minY && value.minY | 0) || 0;
newMaxY = (value && value.maxY && value.maxY | 0) || 0;
break;
case 'minx':
newMinX = (value && ((value.minX && value.minX | 0) || value | 0)) || 0
break
case 'maxx':
newMaxX = (value && ((value.maxX && value.maxX | 0) || value | 0)) || 0
break
case 'rangex':
newMinX = (value && value.minX && value.minX | 0) || 0
newMaxX = (value && value.maxX && value.maxX | 0) || 0
break
case 'miny':
newMinY = (value && ((value.minY && value.minY | 0) || value | 0)) || 0
break
case 'maxy':
newMaxY = (value && ((value.maxY && value.maxY | 0) || value | 0)) || 0
break
case 'rangey':
newMinY = (value && value.minY && value.minY | 0) || 0
newMaxY = (value && value.maxY && value.maxY | 0) || 0
break
case 'all':
default:
newMinX = (value && value.minX && value.minX | 0) || 0
newMaxX = (value && value.maxX && value.maxX | 0) || 0
newMinY = (value && value.minY && value.minY | 0) || 0
newMaxY = (value && value.maxY && value.maxY | 0) || 0
break
}
if (!isNullish(newMinX) && minX !== newMinX) {
minX = newMinX;
rangeX = maxX - minX;
minX = newMinX
rangeX = maxX - minX
}
if (!isNullish(newMaxX) && maxX !== newMaxX) {
maxX = newMaxX;
rangeX = maxX - minX;
maxX = newMaxX
rangeX = maxX - minX
}
if (!isNullish(newMinY) && minY !== newMinY) {
minY = newMinY;
rangeY = maxY - minY;
minY = newMinY
rangeY = maxY - minY
}
if (!isNullish(newMaxY) && maxY !== newMaxY) {
maxY = newMaxY;
rangeY = maxY - minY;
maxY = newMaxY
rangeY = maxY - minY
}
return undefined;
return undefined
}
/**
* @param {GenericCallback} callback
* @returns {void}
*/
function bind (callback) {
if (typeof callback === 'function') changeEvents.push(callback);
if (typeof callback === 'function') changeEvents.push(callback)
}
/**
* @param {GenericCallback} callback
* @returns {void}
*/
function unbind (callback) {
if (typeof callback !== 'function') return;
let i;
while ((i = changeEvents.includes(callback))) changeEvents.splice(i, 1);
if (typeof callback !== 'function') return
let i
while ((i = changeEvents.includes(callback))) changeEvents.splice(i, 1)
}
/**
*
@ -299,39 +299,39 @@ export default class Slider {
*/
function destroy () {
// unbind all possible events and null objects
document.removeEventListener('mousemove', mouseMove);
document.removeEventListener('mouseup', mouseUp);
bar.removeEventListener('mousedown', mouseDown);
bar = null;
arrow = null;
changeEvents = null;
document.removeEventListener('mousemove', mouseMove)
document.removeEventListener('mouseup', mouseUp)
bar.removeEventListener('mousedown', mouseDown)
bar = null
arrow = null
changeEvents = null
}
let offset;
let timeout;
let x = 0;
let y = 0;
let minX = 0;
let maxX = 100;
let rangeX = 100;
let minY = 0;
let maxY = 100;
let rangeY = 100;
let arrow = bar.querySelector('img'); // the arrow image to drag
let changeEvents = [];
let offset
let timeout
let x = 0
let y = 0
let minX = 0
let maxX = 100
let rangeX = 100
let minY = 0
let maxY = 100
let rangeY = 100
let arrow = bar.querySelector('img') // the arrow image to drag
let changeEvents = []
Object.assign(that, {
val,
range,
bind,
unbind,
destroy
});
})
// initialize this control
arrow.src = options.arrow && options.arrow.image;
arrow.w = (options.arrow && options.arrow.width) || parseFloat(getComputedStyle(arrow, null).width.replace("px", ""));
arrow.h = (options.arrow && options.arrow.height) || parseFloat(getComputedStyle(arrow, null).height.replace("px", ""));
bar.w = (options.map && options.map.width) || parseFloat(getComputedStyle(bar, null).width.replace("px", ""));
bar.h = (options.map && options.map.height) || parseFloat(getComputedStyle(bar, null).height.replace("px", ""));
bar.addEventListener('mousedown', mouseDown);
bind.call(that, draw);
arrow.src = options.arrow && options.arrow.image
arrow.w = (options.arrow && options.arrow.width) || parseFloat(getComputedStyle(arrow, null).width.replace('px', ''))
arrow.h = (options.arrow && options.arrow.height) || parseFloat(getComputedStyle(arrow, null).height.replace('px', ''))
bar.w = (options.map && options.map.width) || parseFloat(getComputedStyle(bar, null).width.replace('px', ''))
bar.h = (options.map && options.map.height) || parseFloat(getComputedStyle(bar, null).height.replace('px', ''))
bar.addEventListener('mousedown', mouseDown)
bind.call(that, draw)
}
}

View File

@ -2,39 +2,36 @@
* @param {any} obj
* @returns {any}
*/
export function findPos(obj) {
let curleft = 0;
let curtop = 0;
export function findPos (obj) {
let curleft = 0
let curtop = 0
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
curleft += obj.offsetLeft
curtop += obj.offsetTop
// eslint-disable-next-line no-cond-assign
} while (obj = obj.offsetParent);
return { left: curleft, top: curtop };
} while (obj = obj.offsetParent)
return { left: curleft, top: curtop }
}
return { left: curleft, top: curtop };
return { left: curleft, top: curtop }
}
export function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
export function isObject (item) {
return (item && typeof item === 'object' && !Array.isArray(item))
}
export function mergeDeep(target, source) {
const output = Object.assign({}, target);
export function mergeDeep (target, source) {
const output = Object.assign({}, target)
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach((key) => {
if (isObject(source[key])) {
if (!(key in target))
Object.assign(output, { [key]: source[key] });
else
output[key] = mergeDeep(target[key], source[key]);
if (!(key in target)) { Object.assign(output, { [key]: source[key] }) } else { output[key] = mergeDeep(target[key], source[key]) }
} else {
Object.assign(output, { [key]: source[key] });
Object.assign(output, { [key]: source[key] })
}
});
})
}
return output;
return output
}
/**
@ -43,17 +40,17 @@ export function mergeDeep(target, source) {
* @param {String} selector Selector to match against (class, ID, data attribute, or tag)
* @return {Boolean|Element} Returns null if not match found
*/
export function getClosest(elem, selector) {
const firstChar = selector.charAt(0);
const supports = 'classList' in document.documentElement;
let attribute; let value;
export function getClosest (elem, selector) {
const firstChar = selector.charAt(0)
const supports = 'classList' in document.documentElement
let attribute; let value
// If selector is a data attribute, split attribute from value
if (firstChar === '[') {
selector = selector.substr(1, selector.length - 2);
attribute = selector.split('=');
selector = selector.substr(1, selector.length - 2)
attribute = selector.split('=')
if (attribute.length > 1) {
value = true;
attribute[1] = attribute[1].replace(/"/g, '').replace(/'/g, '');
value = true
attribute[1] = attribute[1].replace(/"/g, '').replace(/'/g, '')
}
}
// Get closest match
@ -62,18 +59,18 @@ export function getClosest(elem, selector) {
if (firstChar === '.') {
if (supports) {
if (elem.classList.contains(selector.substr(1))) {
return elem;
return elem
}
} else {
if (new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)').test(elem.className)) {
return elem;
return elem
}
}
}
// If selector is an ID
if (firstChar === '#') {
if (elem.id === selector.substr(1)) {
return elem;
return elem
}
}
// If selector is a data attribute
@ -81,19 +78,19 @@ export function getClosest(elem, selector) {
if (elem.hasAttribute(attribute[0])) {
if (value) {
if (elem.getAttribute(attribute[0]) === attribute[1]) {
return elem;
return elem
}
} else {
return elem;
return elem
}
}
}
// If selector is a tag
if (elem.tagName.toLowerCase() === selector) {
return elem;
return elem
}
}
return null;
return null
}
/**
@ -102,100 +99,100 @@ export function getClosest(elem, selector) {
* @param {String} selector The class, id, data attribute, or tag to look for
* @return {Array} Null if no match
*/
export function getParents(elem, selector) {
const parents = [];
const firstChar = selector?.charAt(0);
export function getParents (elem, selector) {
const parents = []
const firstChar = selector?.charAt(0)
// Get matches
for ( ; elem && elem !== document; elem = elem.parentNode ) {
if ( selector ) {
for (; elem && elem !== document; elem = elem.parentNode) {
if (selector) {
// If selector is a class
if ( firstChar === '.' ) {
if ( elem.classList.contains( selector.substr(1) ) ) {
parents.push( elem );
if (firstChar === '.') {
if (elem.classList.contains(selector.substr(1))) {
parents.push(elem)
}
}
// If selector is an ID
if ( firstChar === '#' ) {
if ( elem.id === selector.substr(1) ) {
parents.push( elem );
if (firstChar === '#') {
if (elem.id === selector.substr(1)) {
parents.push(elem)
}
}
// If selector is a data attribute
if ( firstChar === '[' ) {
if ( elem.hasAttribute( selector.substr(1, selector.length - 1) ) ) {
parents.push( elem );
if (firstChar === '[') {
if (elem.hasAttribute(selector.substr(1, selector.length - 1))) {
parents.push(elem)
}
}
// If selector is a tag
if ( elem.tagName.toLowerCase() === selector ) {
parents.push( elem );
if (elem.tagName.toLowerCase() === selector) {
parents.push(elem)
}
} else {
parents.push( elem );
parents.push(elem)
}
}
// Return parents if any exist
return parents.length? parents : null;
return parents.length ? parents : null
}
export function getParentsUntil(elem, parent, selector) {
const parents = [];
const parentType = parent?.charAt(0);
const selectorType = selector?.selector.charAt(0);
export function getParentsUntil (elem, parent, selector) {
const parents = []
const parentType = parent?.charAt(0)
const selectorType = selector?.selector.charAt(0)
// Get matches
for ( ; elem && elem !== document; elem = elem.parentNode ) {
for (; elem && elem !== document; elem = elem.parentNode) {
// Check if parent has been reached
if ( parent ) {
if (parent) {
// If parent is a class
if ( parentType === '.' ) {
if ( elem.classList.contains( parent.substr(1) ) ) {
break;
if (parentType === '.') {
if (elem.classList.contains(parent.substr(1))) {
break
}
}
// If parent is an ID
if ( parentType === '#' ) {
if ( elem.id === parent.substr(1) ) {
break;
if (parentType === '#') {
if (elem.id === parent.substr(1)) {
break
}
}
// If parent is a data attribute
if ( parentType === '[' ) {
if ( elem.hasAttribute( parent.substr(1, parent.length - 1) ) ) {
break;
if (parentType === '[') {
if (elem.hasAttribute(parent.substr(1, parent.length - 1))) {
break
}
}
// If parent is a tag
if ( elem.tagName.toLowerCase() === parent ) {
break;
if (elem.tagName.toLowerCase() === parent) {
break
}
}
if ( selector ) {
if (selector) {
// If selector is a class
if ( selectorType === '.' ) {
if ( elem.classList.contains( selector.substr(1) ) ) {
parents.push( elem );
if (selectorType === '.') {
if (elem.classList.contains(selector.substr(1))) {
parents.push(elem)
}
}
// If selector is an ID
if ( selectorType === '#' ) {
if ( elem.id === selector.substr(1) ) {
parents.push( elem );
if (selectorType === '#') {
if (elem.id === selector.substr(1)) {
parents.push(elem)
}
}
// If selector is a data attribute
if ( selectorType === '[' ) {
if ( elem.hasAttribute( selector.substr(1, selector.length - 1) ) ) {
parents.push( elem );
if (selectorType === '[') {
if (elem.hasAttribute(selector.substr(1, selector.length - 1))) {
parents.push(elem)
}
}
// If selector is a tag
if ( elem.tagName.toLowerCase() === selector ) {
parents.push( elem );
if (elem.tagName.toLowerCase() === selector) {
parents.push(elem)
}
} else {
parents.push( elem );
parents.push(elem)
}
}
// Return parents if any exist
return parents.length? parents : null;
}
return parents.length ? parents : null
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,83 +6,83 @@ export default class Paint {
* @param {module:jGraduate.jGraduatePaintOptions} [opt]
*/
constructor (opt) {
const options = opt || {};
this.alpha = isNaN(options.alpha) ? 100 : options.alpha;
const options = opt || {}
this.alpha = isNaN(options.alpha) ? 100 : options.alpha
// copy paint object
if (options.copy) {
/**
* @name module:jGraduate~Paint#type
* @type {"none"|"solidColor"|"linearGradient"|"radialGradient"}
*/
this.type = options.copy.type;
this.type = options.copy.type
/**
* Represents opacity (0-100).
* @name module:jGraduate~Paint#alpha
* @type {Float}
*/
this.alpha = options.copy.alpha;
this.alpha = options.copy.alpha
/**
* Represents #RRGGBB hex of color.
* @name module:jGraduate~Paint#solidColor
* @type {string}
*/
this.solidColor = null;
this.solidColor = null
/**
* @name module:jGraduate~Paint#linearGradient
* @type {SVGLinearGradientElement}
*/
this.linearGradient = null;
this.linearGradient = null
/**
* @name module:jGraduate~Paint#radialGradient
* @type {SVGRadialGradientElement}
*/
this.radialGradient = null;
this.radialGradient = null
switch (this.type) {
case 'none':
break;
case 'solidColor':
this.solidColor = options.copy.solidColor;
break;
case 'linearGradient':
this.linearGradient = options.copy.linearGradient.cloneNode(true);
break;
case 'radialGradient':
this.radialGradient = options.copy.radialGradient.cloneNode(true);
break;
case 'none':
break
case 'solidColor':
this.solidColor = options.copy.solidColor
break
case 'linearGradient':
this.linearGradient = options.copy.linearGradient.cloneNode(true)
break
case 'radialGradient':
this.radialGradient = options.copy.radialGradient.cloneNode(true)
break
}
// create linear gradient paint
} else if (options.linearGradient) {
this.type = 'linearGradient';
this.solidColor = null;
this.radialGradient = null;
if(options.linearGradient.hasAttribute('xlink:href')) {
const xhref = document.getElementById(options.linearGradient.getAttribute('xlink:href').substr(1));
this.linearGradient = xhref.cloneNode(true);
this.type = 'linearGradient'
this.solidColor = null
this.radialGradient = null
if (options.linearGradient.hasAttribute('xlink:href')) {
const xhref = document.getElementById(options.linearGradient.getAttribute('xlink:href').substr(1))
this.linearGradient = xhref.cloneNode(true)
} else {
this.linearGradient = options.linearGradient.cloneNode(true);
this.linearGradient = options.linearGradient.cloneNode(true)
}
// create linear gradient paint
} else if (options.radialGradient) {
this.type = 'radialGradient';
this.solidColor = null;
this.linearGradient = null;
if(options.radialGradient.hasAttribute('xlink:href')) {
const xhref = document.getElementById(options.radialGradient.getAttribute('xlink:href').substr(1));
this.radialGradient = xhref.cloneNode(true);
this.type = 'radialGradient'
this.solidColor = null
this.linearGradient = null
if (options.radialGradient.hasAttribute('xlink:href')) {
const xhref = document.getElementById(options.radialGradient.getAttribute('xlink:href').substr(1))
this.radialGradient = xhref.cloneNode(true)
} else {
this.radialGradient = options.radialGradient.cloneNode(true);
this.radialGradient = options.radialGradient.cloneNode(true)
}
// create solid color paint
} else if (options.solidColor) {
this.type = 'solidColor';
this.solidColor = options.solidColor;
this.type = 'solidColor'
this.solidColor = options.solidColor
// create empty paint
} else {
this.type = 'none';
this.solidColor = null;
this.linearGradient = null;
this.radialGradient = null;
this.type = 'none'
this.solidColor = null
this.linearGradient = null
this.radialGradient = null
}
}
}
}

View File

@ -1,6 +1,6 @@
/* globals svgEditor */
import { t } from '../locale.js';
const template = document.createElement('template');
import { t } from '../locale.js'
const template = document.createElement('template')
template.innerHTML = `
<style>
:host(:hover) :not(.disabled)
@ -39,7 +39,7 @@ template.innerHTML = `
<div title="title">
<img alt="icon">
</div>
`;
`
/**
* @class ToolButton
*/
@ -48,22 +48,24 @@ export class ToolButton extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
// locate the component
this.$div = this._shadowRoot.querySelector('div');
this.$img = this._shadowRoot.querySelector('img');
this.imgPath = svgEditor.configObj.curConfig.imgPath;
this.$div = this._shadowRoot.querySelector('div')
this.$img = this._shadowRoot.querySelector('img')
this.imgPath = svgEditor.configObj.curConfig.imgPath
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'title', 'src', 'pressed', 'disabled', 'size', 'style' ];
return ['title', 'src', 'pressed', 'disabled', 'size', 'style']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -72,56 +74,57 @@ export class ToolButton extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
if (oldValue === newValue) return
switch (name) {
case 'title':
{
const shortcut = this.getAttribute('shortcut');
this.$div.setAttribute('title', `${t(newValue)} ${shortcut ? `[${t(shortcut)}]` : ''}`);
}
break;
case 'style':
this.$div.style = newValue;
break;
case 'src':
if (newValue.indexOf("data:") !== -1) {
this.$img.setAttribute('src', newValue);
} else {
this.$img.setAttribute('src', this.imgPath + "/" + newValue);
}
break;
case 'pressed':
if (newValue === null) {
this.$div.classList.remove('pressed');
} else {
this.$div.classList.add('pressed');
}
break;
case 'size':
if (newValue === 'small') {
this.$div.classList.add('small');
} else {
this.$div.classList.remove('small');
}
break;
case 'disabled':
if (newValue) {
this.$div.classList.add('disabled');
} else {
this.$div.classList.remove('disabled');
}
break;
default:
console.error(`unknown attribute: ${name}`);
break;
case 'title':
{
const shortcut = this.getAttribute('shortcut')
this.$div.setAttribute('title', `${t(newValue)} ${shortcut ? `[${t(shortcut)}]` : ''}`)
}
break
case 'style':
this.$div.style = newValue
break
case 'src':
if (newValue.indexOf('data:') !== -1) {
this.$img.setAttribute('src', newValue)
} else {
this.$img.setAttribute('src', this.imgPath + '/' + newValue)
}
break
case 'pressed':
if (newValue === null) {
this.$div.classList.remove('pressed')
} else {
this.$div.classList.add('pressed')
}
break
case 'size':
if (newValue === 'small') {
this.$div.classList.add('small')
} else {
this.$div.classList.remove('small')
}
break
case 'disabled':
if (newValue) {
this.$div.classList.add('disabled')
} else {
this.$div.classList.remove('disabled')
}
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get title () {
return this.getAttribute('title');
return this.getAttribute('title')
}
/**
@ -129,14 +132,15 @@ export class ToolButton extends HTMLElement {
* @returns {void}
*/
set title (value) {
this.setAttribute('title', value);
this.setAttribute('title', value)
}
/**
* @function get
* @returns {any}
*/
get pressed () {
return this.hasAttribute('pressed');
return this.hasAttribute('pressed')
}
/**
@ -146,17 +150,18 @@ export class ToolButton extends HTMLElement {
set pressed (value) {
// boolean value => existence = true
if (value) {
this.setAttribute('pressed', 'true');
this.setAttribute('pressed', 'true')
} else {
this.removeAttribute('pressed');
this.removeAttribute('pressed')
}
}
/**
* @function get
* @returns {any}
*/
get disabled () {
return this.hasAttribute('disabled');
return this.hasAttribute('disabled')
}
/**
@ -166,17 +171,18 @@ export class ToolButton extends HTMLElement {
set disabled (value) {
// boolean value => existence = true
if (value) {
this.setAttribute('disabled', 'true');
this.setAttribute('disabled', 'true')
} else {
this.removeAttribute('disabled');
this.removeAttribute('disabled')
}
}
/**
* @function get
* @returns {any}
*/
get src () {
return this.getAttribute('src');
return this.getAttribute('src')
}
/**
@ -184,7 +190,7 @@ export class ToolButton extends HTMLElement {
* @returns {void}
*/
set src (value) {
this.setAttribute('src', value);
this.setAttribute('src', value)
}
/**
@ -192,7 +198,7 @@ export class ToolButton extends HTMLElement {
* @returns {any}
*/
get size () {
return this.getAttribute('size');
return this.getAttribute('size')
}
/**
@ -200,7 +206,7 @@ export class ToolButton extends HTMLElement {
* @returns {void}
*/
set size (value) {
this.setAttribute('size', value);
this.setAttribute('size', value)
}
/**
@ -209,22 +215,22 @@ export class ToolButton extends HTMLElement {
*/
connectedCallback () {
// capture shortcuts
const shortcut = this.getAttribute('shortcut');
const shortcut = this.getAttribute('shortcut')
if (shortcut) {
// register the keydown event
document.addEventListener('keydown', (e) => {
// only track keyboard shortcuts for the body containing the SVG-Editor
if (e.target.nodeName !== 'BODY') return;
if (e.target.nodeName !== 'BODY') return
// normalize key
const key = `${(e.metaKey) ? 'meta+' : ''}${(e.ctrlKey) ? 'ctrl+' : ''}${e.key.toUpperCase()}`;
if (shortcut !== key) return;
const key = `${(e.metaKey) ? 'meta+' : ''}${(e.ctrlKey) ? 'ctrl+' : ''}${e.key.toUpperCase()}`
if (shortcut !== key) return
// launch the click event
this.click();
e.preventDefault();
});
this.click()
e.preventDefault()
})
}
}
}
// Register
customElements.define('se-button', ToolButton);
customElements.define('se-button', ToolButton)

View File

@ -1,9 +1,9 @@
/* globals svgEditor */
import { jGraduate, jGraduateMethod } from './jgraduate/jQuery.jGraduate.js';
import PaintBox from './PaintBox.js';
import { t } from '../locale.js';
import { jGraduate, jGraduateMethod } from './jgraduate/jQuery.jGraduate.js'
import PaintBox from './PaintBox.js'
import { t } from '../locale.js'
const template = document.createElement('template');
const template = document.createElement('template')
template.innerHTML = `
<style>
.jPicker .Icon {
@ -645,7 +645,7 @@ div.jGraduate_Slider img {
</div>
<!-- hidden div -->
<div id="color_picker"></div>
`;
`
/**
* @class SeColorPicker
*/
@ -654,35 +654,38 @@ export class SeColorPicker extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this.$logo = this._shadowRoot.getElementById('logo');
this.$label = this._shadowRoot.getElementById('label');
this.$block = this._shadowRoot.getElementById('block');
this.paintBox = null;
this.i18next = null;
this.$picker = this._shadowRoot.getElementById('picker');
this.$color_picker = this._shadowRoot.getElementById('color_picker');
this.imgPath = svgEditor.configObj.curConfig.imgPath;
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
this.$logo = this._shadowRoot.getElementById('logo')
this.$label = this._shadowRoot.getElementById('label')
this.$block = this._shadowRoot.getElementById('block')
this.paintBox = null
this.i18next = null
this.$picker = this._shadowRoot.getElementById('picker')
this.$color_picker = this._shadowRoot.getElementById('color_picker')
this.imgPath = svgEditor.configObj.curConfig.imgPath
}
/**
* @function init
* @param {any} name
* @returns {void}
*/
init (i18next) {
this.i18next = i18next;
this.setAttribute('config-change_xxx_color', t('config.change_xxx_color'));
this.i18next = i18next
this.setAttribute('config-change_xxx_color', t('config.change_xxx_color'))
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'label', 'src', 'type', 'config-change_xxx_color' ];
return ['label', 'src', 'type', 'config-change_xxx_color']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -691,31 +694,32 @@ export class SeColorPicker extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
if (oldValue === newValue) return
switch (name) {
case 'src':
this.$logo.setAttribute('src', this.imgPath + '/' + newValue);
break;
case 'label':
this.setAttribute('title', t(newValue));
break;
case 'type':
this.$label.setAttribute('title', 'config.pick_paint_opavity');
break;
case 'config-change_xxx_color':
this.$label.setAttribute('title', newValue);
break;
default:
console.error(`unknown attribute: ${name}`);
break;
case 'src':
this.$logo.setAttribute('src', this.imgPath + '/' + newValue)
break
case 'label':
this.setAttribute('title', t(newValue))
break
case 'type':
this.$label.setAttribute('title', 'config.pick_paint_opavity')
break
case 'config-change_xxx_color':
this.$label.setAttribute('title', newValue)
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get label () {
return this.$label.getAttribute('title');
return this.$label.getAttribute('title')
}
/**
@ -723,14 +727,15 @@ export class SeColorPicker extends HTMLElement {
* @returns {void}
*/
set label (value) {
this.setAttribute('label', value);
this.setAttribute('label', value)
}
/**
* @function get
* @returns {any}
*/
get type () {
return this.getAttribute('type');
return this.getAttribute('type')
}
/**
@ -738,14 +743,15 @@ export class SeColorPicker extends HTMLElement {
* @returns {void}
*/
set type (value) {
this.setAttribute('type', value);
this.setAttribute('type', value)
}
/**
* @function get
* @returns {any}
*/
get src () {
return this.getAttribute('src');
return this.getAttribute('src')
}
/**
@ -753,7 +759,7 @@ export class SeColorPicker extends HTMLElement {
* @returns {void}
*/
set src (value) {
this.setAttribute('src', value);
this.setAttribute('src', value)
}
/**
@ -763,20 +769,23 @@ export class SeColorPicker extends HTMLElement {
* @returns {void}
*/
update (svgCanvas, selectedElement, apply) {
const paint = this.paintBox.update(svgCanvas, selectedElement);
const paint = this.paintBox.update(svgCanvas, selectedElement)
if (paint && apply) {
const changeEvent = new CustomEvent('change', { detail: {
paint
} });
this.dispatchEvent(changeEvent);
const changeEvent = new CustomEvent('change', {
detail: {
paint
}
})
this.dispatchEvent(changeEvent)
}
}
/**
* @param {PlainObject} paint
* @returns {void}
*/
setPaint (paint) {
this.paintBox.setPaint(paint);
this.paintBox.setPaint(paint)
}
/**
@ -784,9 +793,9 @@ export class SeColorPicker extends HTMLElement {
* @returns {void}
*/
connectedCallback () {
this.paintBox = new PaintBox(this.$block, this.type);
this.paintBox = new PaintBox(this.$block, this.type)
this.$picker.addEventListener('click', () => {
let { paint } = this.paintBox;
let { paint } = this.paintBox
jGraduateMethod(
this.$color_picker,
{
@ -796,22 +805,24 @@ export class SeColorPicker extends HTMLElement {
newstop: 'inverse'
},
(p) => {
paint = new jGraduate.Paint(p);
this.setPaint(paint);
const changeEvent = new CustomEvent('change', { detail: {
paint
} });
this.dispatchEvent(changeEvent);
this.$color_picker.style.display = 'none';
paint = new jGraduate.Paint(p)
this.setPaint(paint)
const changeEvent = new CustomEvent('change', {
detail: {
paint
}
})
this.dispatchEvent(changeEvent)
this.$color_picker.style.display = 'none'
},
() => {
this.$color_picker.style.display = 'none';
this.$color_picker.style.display = 'none'
},
this.i18next
);
});
)
})
}
}
// Register
customElements.define('se-colorpicker', SeColorPicker);
customElements.define('se-colorpicker', SeColorPicker)

View File

@ -1,8 +1,8 @@
import ListComboBox from 'elix/define/ListComboBox.js';
import { defaultState } from 'elix/src/base/internal.js';
import { templateFrom, fragmentFrom } from 'elix/src/core/htmlLiterals.js';
import { internal } from 'elix';
import NumberSpinBox from '../dialogs/se-elix/define/NumberSpinBox.js';
import ListComboBox from 'elix/define/ListComboBox.js'
import { defaultState } from 'elix/src/base/internal.js'
import { templateFrom, fragmentFrom } from 'elix/src/core/htmlLiterals.js'
import { internal } from 'elix'
import NumberSpinBox from '../dialogs/se-elix/define/NumberSpinBox.js'
/**
* @class Dropdown
@ -15,21 +15,22 @@ class Dropdown extends ListComboBox {
get [defaultState] () {
return Object.assign(super[defaultState], {
inputPartType: NumberSpinBox,
src: "logo.svg",
src: 'logo.svg',
inputsize: '100%'
});
})
}
/**
* @function get
* @returns {PlainObject}
*/
get [internal.template] () {
const result = super[internal.template];
const source = result.content.getElementById('source');
const result = super[internal.template]
const source = result.content.getElementById('source')
// add a icon before our dropdown
source.prepend(fragmentFrom.html`
<img src="dropdown.svg" alt="icon" width="18" height="18"></img>
`.cloneNode(true));
`.cloneNode(true))
// change the style so it fits in our toolbar
result.content.append(
templateFrom.html`
@ -48,16 +49,18 @@ class Dropdown extends ListComboBox {
}
</style>
`.content
);
return result;
)
return result
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'title', 'src', 'inputsize', 'value' ];
return ['title', 'src', 'inputsize', 'value']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -66,97 +69,104 @@ class Dropdown extends ListComboBox {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
if (oldValue === newValue) return
switch (name) {
case 'title':
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;
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);
super[internal.render](changed)
if (this[internal.firstRender]) {
this.$img = this.shadowRoot.querySelector('img');
this.$input = this.shadowRoot.getElementById('input');
this.$img = this.shadowRoot.querySelector('img')
this.$input = this.shadowRoot.getElementById('input')
}
if (changed.src) {
this.$img.setAttribute('src', this[internal.state].src);
this.$img.setAttribute('src', this[internal.state].src)
}
if (changed.inputsize) {
this.$input.shadowRoot.querySelector('[part~="input"]').style.width = this[internal.state].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');
e.preventDefault()
const value = e.detail?.closeResult?.getAttribute('value')
if (value) {
const closeEvent = new CustomEvent('change', { detail: { value } });
this.dispatchEvent(closeEvent);
const closeEvent = new CustomEvent('change', { detail: { value } })
this.dispatchEvent(closeEvent)
}
});
})
}
}
/**
* @function src
* @returns {string} src
*/
get src () {
return this[internal.state].src;
return this[internal.state].src
}
/**
* @function src
* @returns {void}
*/
set src (src) {
this[internal.setState]({ src });
this[internal.setState]({ src })
}
/**
* @function inputsize
* @returns {string} src
*/
get inputsize () {
return this[internal.state].inputsize;
return this[internal.state].inputsize
}
/**
* @function src
* @returns {void}
*/
set inputsize (inputsize) {
this[internal.setState]({ inputsize });
this[internal.setState]({ inputsize })
}
/**
* @function value
* @returns {string} src
*/
get value () {
return this[internal.state].value;
return this[internal.state].value
}
/**
* @function value
* @returns {void}
*/
set value (value) {
this[internal.setState]({ value });
this[internal.setState]({ value })
}
}
// Register
customElements.define('se-dropdown', Dropdown);
customElements.define('se-dropdown', Dropdown)
/*
{TODO

View File

@ -1,6 +1,5 @@
/* globals svgEditor */
/* eslint-disable no-unsanitized/property */
const template = document.createElement('template');
const template = document.createElement('template')
template.innerHTML = `
<style>
:host {
@ -98,7 +97,7 @@ template.innerHTML = `
</div>
</div>
`;
`
/**
* @class ExplorerButton
*/
@ -107,28 +106,30 @@ export class ExplorerButton extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
// locate the component
this.$button = this._shadowRoot.querySelector('.menu-button');
this.$overall = this._shadowRoot.querySelector('.overall');
this.$img = this._shadowRoot.querySelector('.menu-button img');
this.$menu = this._shadowRoot.querySelector('.menu');
this.$handle = this._shadowRoot.querySelector('.handle');
this.$lib = this._shadowRoot.querySelector('.image-lib');
this.files = [];
this.request = new XMLHttpRequest();
this.imgPath = svgEditor.configObj.curConfig.imgPath;
this.$button = this._shadowRoot.querySelector('.menu-button')
this.$overall = this._shadowRoot.querySelector('.overall')
this.$img = this._shadowRoot.querySelector('.menu-button img')
this.$menu = this._shadowRoot.querySelector('.menu')
this.$handle = this._shadowRoot.querySelector('.handle')
this.$lib = this._shadowRoot.querySelector('.image-lib')
this.files = []
this.request = new XMLHttpRequest()
this.imgPath = svgEditor.configObj.curConfig.imgPath
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'title', 'pressed', 'disabled', 'lib', 'src' ];
return ['title', 'pressed', 'disabled', 'lib', 'src']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -137,55 +138,56 @@ export class ExplorerButton extends HTMLElement {
* @returns {void}
*/
async attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
if (oldValue === newValue) return
switch (name) {
case 'title':
{
const shortcut = this.getAttribute('shortcut');
this.$button.setAttribute('title', `${newValue} [${shortcut}]`);
}
break;
case 'pressed':
if (newValue) {
this.$overall.classList.add('pressed');
} else {
this.$overall.classList.remove('pressed');
}
break;
case 'disabled':
if (newValue) {
this.$overall.classList.add('disabled');
} else {
this.$overall.classList.remove('disabled');
}
break;
case 'lib':
try {
const response = await fetch(`${newValue}index.json`);
const json = await response.json();
const { lib } = json;
this.$menu.innerHTML = lib.map((menu, i) => (
case 'title':
{
const shortcut = this.getAttribute('shortcut')
this.$button.setAttribute('title', `${newValue} [${shortcut}]`)
}
break
case 'pressed':
if (newValue) {
this.$overall.classList.add('pressed')
} else {
this.$overall.classList.remove('pressed')
}
break
case 'disabled':
if (newValue) {
this.$overall.classList.add('disabled')
} else {
this.$overall.classList.remove('disabled')
}
break
case 'lib':
try {
const response = await fetch(`${newValue}index.json`)
const json = await response.json()
const { lib } = json
this.$menu.innerHTML = lib.map((menu, i) => (
`<div data-menu="${menu}" class="menu-item ${(i === 0) ? 'pressed' : ''} ">${menu}</div>`
)).join('');
await this.updateLib(lib[0]);
} catch (error) {
console.error(error);
}
break;
case 'src':
this.$img.setAttribute('src', this.imgPath + '/' + newValue);
break;
default:
console.error(`unknown attribute: ${name}`);
break;
)).join('')
await this.updateLib(lib[0])
} catch (error) {
console.error(error)
}
break
case 'src':
this.$img.setAttribute('src', this.imgPath + '/' + newValue)
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get title () {
return this.getAttribute('title');
return this.getAttribute('title')
}
/**
@ -193,14 +195,15 @@ export class ExplorerButton extends HTMLElement {
* @returns {void}
*/
set title (value) {
this.setAttribute('title', value);
this.setAttribute('title', value)
}
/**
* @function get
* @returns {any}
*/
get pressed () {
return this.hasAttribute('pressed');
return this.hasAttribute('pressed')
}
/**
@ -210,17 +213,18 @@ export class ExplorerButton extends HTMLElement {
set pressed (value) {
// boolean value => existence = true
if (value) {
this.setAttribute('pressed', 'true');
this.setAttribute('pressed', 'true')
} else {
this.removeAttribute('pressed', '');
this.removeAttribute('pressed', '')
}
}
/**
* @function get
* @returns {any}
*/
get disabled () {
return this.hasAttribute('disabled');
return this.hasAttribute('disabled')
}
/**
@ -230,11 +234,12 @@ export class ExplorerButton extends HTMLElement {
set disabled (value) {
// boolean value => existence = true
if (value) {
this.setAttribute('disabled', 'true');
this.setAttribute('disabled', 'true')
} else {
this.removeAttribute('disabled', '');
this.removeAttribute('disabled', '')
}
}
/**
* @function connectedCallback
* @returns {void}
@ -242,73 +247,74 @@ export class ExplorerButton extends HTMLElement {
connectedCallback () {
// capture click event on the button to manage the logic
const onClickHandler = (ev) => {
ev.stopPropagation();
ev.stopPropagation()
switch (ev.target.nodeName) {
case 'SE-EXPLORERBUTTON':
this.$menu.classList.add('open');
this.$lib.classList.add('open-lib');
break;
case 'SE-BUTTON':
case 'SE-EXPLORERBUTTON':
this.$menu.classList.add('open')
this.$lib.classList.add('open-lib')
break
case 'SE-BUTTON':
// change to the current action
this.currentAction = ev.target;
this.$img.setAttribute('src', this.currentAction.getAttribute('src'));
this.dataset.draw = this.data[this.currentAction.dataset.shape];
this._shadowRoot.querySelectorAll('.image-lib [pressed]').forEach((b) => { b.pressed = false; });
this.currentAction.setAttribute('pressed', 'pressed');
// and close the menu
this.$menu.classList.remove('open');
this.$lib.classList.remove('open-lib');
break;
case 'DIV':
if (ev.target.classList[0] === 'handle') {
this.currentAction = ev.target
this.$img.setAttribute('src', this.currentAction.getAttribute('src'))
this.dataset.draw = this.data[this.currentAction.dataset.shape]
this._shadowRoot.querySelectorAll('.image-lib [pressed]').forEach((b) => { b.pressed = false })
this.currentAction.setAttribute('pressed', 'pressed')
// and close the menu
this.$menu.classList.remove('open')
this.$lib.classList.remove('open-lib')
break
case 'DIV':
if (ev.target.classList[0] === 'handle') {
// this is a click on the handle so let's open/close the menu.
this.$menu.classList.toggle('open');
this.$lib.classList.toggle('open-lib');
} else {
this._shadowRoot.querySelectorAll('.menu > .pressed').forEach((b) => { b.classList.remove('pressed'); });
ev.target.classList.add('pressed');
this.updateLib(ev.target.dataset.menu);
}
break;
default:
console.error('unknown nodeName for:', ev.target, ev.target.className);
this.$menu.classList.toggle('open')
this.$lib.classList.toggle('open-lib')
} else {
this._shadowRoot.querySelectorAll('.menu > .pressed').forEach((b) => { b.classList.remove('pressed') })
ev.target.classList.add('pressed')
this.updateLib(ev.target.dataset.menu)
}
break
default:
console.error('unknown nodeName for:', ev.target, ev.target.className)
}
};
}
// capture event from slots
this.addEventListener('click', onClickHandler);
this.$menu.addEventListener('click', onClickHandler);
this.$lib.addEventListener('click', onClickHandler);
this.$handle.addEventListener('click', onClickHandler);
this.addEventListener('click', onClickHandler)
this.$menu.addEventListener('click', onClickHandler)
this.$lib.addEventListener('click', onClickHandler)
this.$handle.addEventListener('click', onClickHandler)
}
/**
* @function updateLib
* @param {string} lib
* @returns {void}
*/
async updateLib (lib) {
const libDir = this.getAttribute('lib');
const libDir = this.getAttribute('lib')
try {
// initialize buttons for all shapes defined for this library
const response = await fetch(`${libDir}${lib}.json`);
const json = await response.json();
this.data = json.data;
const size = json.size ?? 300;
const fill = json.fill ? '#333' : 'none';
const off = size * 0.05;
const vb = [ -off, -off, size + off * 2, size + off * 2 ].join(' ');
const stroke = json.fill ? 0 : (size / 30);
this.$lib.innerHTML = Object.entries(this.data).map(([ key, path ]) => {
const response = await fetch(`${libDir}${lib}.json`)
const json = await response.json()
this.data = json.data
const size = json.size ?? 300
const fill = json.fill ? '#333' : 'none'
const off = size * 0.05
const vb = [-off, -off, size + off * 2, size + off * 2].join(' ')
const stroke = json.fill ? 0 : (size / 30)
this.$lib.innerHTML = Object.entries(this.data).map(([key, path]) => {
const encoded = btoa(`
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<svg viewBox="${vb}"><path fill="${fill}" stroke="#f8bb00" stroke-width="${stroke}" d="${path}"></path></svg>
</svg>`);
return `<se-button data-shape="${key}"src="data:image/svg+xml;base64,${encoded}"></se-button>`;
}).join('');
</svg>`)
return `<se-button data-shape="${key}"src="data:image/svg+xml;base64,${encoded}"></se-button>`
}).join('')
} catch (error) {
console.error(`could not read file:${libDir}${lib}.json`, error);
console.error(`could not read file:${libDir}${lib}.json`, error)
}
}
}
// Register
customElements.define('se-explorerbutton', ExplorerButton);
customElements.define('se-explorerbutton', ExplorerButton)

View File

@ -1,6 +1,6 @@
/* globals svgEditor */
import { t } from '../locale.js';
const template = document.createElement('template');
import { t } from '../locale.js'
const template = document.createElement('template')
template.innerHTML = `
<style>
:host {
@ -79,7 +79,7 @@ template.innerHTML = `
</div>
`;
`
/**
* @class FlyingButton
*/
@ -88,28 +88,30 @@ export class FlyingButton extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
// locate the component
this.$button = this._shadowRoot.querySelector('.menu-button');
this.$handle = this._shadowRoot.querySelector('.handle');
this.$overall = this._shadowRoot.querySelector('.overall');
this.$img = this._shadowRoot.querySelector('img');
this.$menu = this._shadowRoot.querySelector('.menu');
this.$button = this._shadowRoot.querySelector('.menu-button')
this.$handle = this._shadowRoot.querySelector('.handle')
this.$overall = this._shadowRoot.querySelector('.overall')
this.$img = this._shadowRoot.querySelector('img')
this.$menu = this._shadowRoot.querySelector('.menu')
// the last element of the div is the slot
// we retrieve all elements added in the slot (i.e. se-buttons)
this.$elements = this.$menu.lastElementChild.assignedElements();
this.imgPath = svgEditor.configObj.curConfig.imgPath;
this.$elements = this.$menu.lastElementChild.assignedElements()
this.imgPath = svgEditor.configObj.curConfig.imgPath
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'title', 'pressed', 'disabled', 'opened' ];
return ['title', 'pressed', 'disabled', 'opened']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -118,46 +120,47 @@ export class FlyingButton extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
if (oldValue === newValue) return
switch (name) {
case 'title':
{
const shortcut = this.getAttribute('shortcut');
this.$button.setAttribute('title', `${t(newValue)} ${shortcut ? `[${t(shortcut)}]` : ''}`);
}
break;
case 'pressed':
if (newValue) {
this.$overall.classList.add('pressed');
} else {
this.$overall.classList.remove('pressed');
}
break;
case 'opened':
if (newValue) {
this.$menu.classList.add('open');
} else {
this.$menu.classList.remove('open');
}
break;
case 'disabled':
if (newValue) {
this.$overall.classList.add('disabled');
} else {
this.$overall.classList.remove('disabled');
}
break;
default:
console.error(`unknown attribute: ${name}`);
break;
case 'title':
{
const shortcut = this.getAttribute('shortcut')
this.$button.setAttribute('title', `${t(newValue)} ${shortcut ? `[${t(shortcut)}]` : ''}`)
}
break
case 'pressed':
if (newValue) {
this.$overall.classList.add('pressed')
} else {
this.$overall.classList.remove('pressed')
}
break
case 'opened':
if (newValue) {
this.$menu.classList.add('open')
} else {
this.$menu.classList.remove('open')
}
break
case 'disabled':
if (newValue) {
this.$overall.classList.add('disabled')
} else {
this.$overall.classList.remove('disabled')
}
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get title () {
return this.getAttribute('title');
return this.getAttribute('title')
}
/**
@ -165,14 +168,15 @@ export class FlyingButton extends HTMLElement {
* @returns {void}
*/
set title (value) {
this.setAttribute('title', value);
this.setAttribute('title', value)
}
/**
* @function get
* @returns {any}
*/
get pressed () {
return this.hasAttribute('pressed');
return this.hasAttribute('pressed')
}
/**
@ -182,19 +186,20 @@ export class FlyingButton extends HTMLElement {
set pressed (value) {
// boolean value => existence = true
if (value) {
this.setAttribute('pressed', 'true');
this.setAttribute('pressed', 'true')
} else {
this.removeAttribute('pressed', '');
this.removeAttribute('pressed', '')
// close also the menu if open
this.removeAttribute('opened');
this.removeAttribute('opened')
}
}
/**
* @function get
* @returns {any}
*/
get opened () {
return this.hasAttribute('opened');
return this.hasAttribute('opened')
}
/**
@ -204,17 +209,18 @@ export class FlyingButton extends HTMLElement {
set opened (value) {
// boolean value => existence = true
if (value) {
this.setAttribute('opened', 'opened');
this.setAttribute('opened', 'opened')
} else {
this.removeAttribute('opened');
this.removeAttribute('opened')
}
}
/**
* @function get
* @returns {any}
*/
get disabled () {
return this.hasAttribute('disabled');
return this.hasAttribute('disabled')
}
/**
@ -224,59 +230,60 @@ export class FlyingButton extends HTMLElement {
set disabled (value) {
// boolean value => existence = true
if (value) {
this.setAttribute('disabled', 'true');
this.setAttribute('disabled', 'true')
} else {
this.removeAttribute('disabled', '');
this.removeAttribute('disabled', '')
}
}
/**
* @function connectedCallback
* @returns {void}
*/
connectedCallback () {
this.activeSlot = this.shadowRoot.querySelector('slot').assignedElements()[0];
this.$img.setAttribute('src', this.imgPath + '/' + this.activeSlot.getAttribute('src'));
this.activeSlot = this.shadowRoot.querySelector('slot').assignedElements()[0]
this.$img.setAttribute('src', this.imgPath + '/' + this.activeSlot.getAttribute('src'))
// capture click event on the button to manage the logic
const onClickHandler = (ev) => {
ev.stopPropagation();
ev.stopPropagation()
switch (ev.target.nodeName) {
case 'SE-FLYINGBUTTON':
if (this.pressed) {
this.setAttribute('opened', 'opened');
} else {
case 'SE-FLYINGBUTTON':
if (this.pressed) {
this.setAttribute('opened', 'opened')
} else {
// launch current action
this.activeSlot.click();
this.setAttribute('pressed', 'pressed');
}
break;
case 'SE-BUTTON':
this.activeSlot.click()
this.setAttribute('pressed', 'pressed')
}
break
case 'SE-BUTTON':
// change to the current action
this.$img.setAttribute('src', this.imgPath + '/' + ev.target.getAttribute('src'));
this.activeSlot = ev.target;
this.setAttribute('pressed', 'pressed');
// and close the menu
this.$menu.classList.remove('open');
break;
case 'DIV':
this.$img.setAttribute('src', this.imgPath + '/' + ev.target.getAttribute('src'))
this.activeSlot = ev.target
this.setAttribute('pressed', 'pressed')
// and close the menu
this.$menu.classList.remove('open')
break
case 'DIV':
// this is a click on the handle so let's open/close the menu.
if (this.opened) {
this.removeAttribute('opened');
} else {
this.setAttribute('opened', 'opened');
// In case menu scroll on top or bottom position based popup position set
const rect = this.getBoundingClientRect();
this.$menu.style.top = rect.top + "px";
}
break;
default:
console.error('unkonw nodeName for:', ev.target, ev.target.className);
if (this.opened) {
this.removeAttribute('opened')
} else {
this.setAttribute('opened', 'opened')
// In case menu scroll on top or bottom position based popup position set
const rect = this.getBoundingClientRect()
this.$menu.style.top = rect.top + 'px'
}
break
default:
console.error('unkonw nodeName for:', ev.target, ev.target.className)
}
};
}
// capture event from slots
this.addEventListener('click', onClickHandler);
this.$handle.addEventListener('click', onClickHandler);
this.addEventListener('click', onClickHandler)
this.$handle.addEventListener('click', onClickHandler)
}
}
// Register
customElements.define('se-flyingbutton', FlyingButton);
customElements.define('se-flyingbutton', FlyingButton)

View File

@ -1,7 +1,7 @@
import 'elix/define/Input.js';
import { t } from '../locale.js';
import 'elix/define/Input.js'
import { t } from '../locale.js'
const template = document.createElement('template');
const template = document.createElement('template')
template.innerHTML = `
<style>
div {
@ -32,7 +32,7 @@ template.innerHTML = `
<span id="label">label</span>
<elix-input></elix-input>
</div>
`;
`
/**
* @class SEInput
@ -42,24 +42,26 @@ export class SEInput extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
// locate the component
this.$div = this._shadowRoot.querySelector('div');
this.$img = this._shadowRoot.querySelector('img');
this.$label = this.shadowRoot.getElementById('label');
this.$event = new CustomEvent('change');
this.$input = this._shadowRoot.querySelector('elix-input');
this.$div = this._shadowRoot.querySelector('div')
this.$img = this._shadowRoot.querySelector('img')
this.$label = this.shadowRoot.getElementById('label')
this.$event = new CustomEvent('change')
this.$input = this._shadowRoot.querySelector('elix-input')
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'value', 'label', 'src', 'size', 'title' ];
return ['value', 'label', 'src', 'size', 'title']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -68,36 +70,37 @@ export class SEInput extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
if (oldValue === newValue) return
switch (name) {
case 'title':
this.$div.setAttribute('title', `${t(newValue)}`);
break;
case 'src':
this.$img.setAttribute('src', newValue);
this.$label.remove();
break;
case 'size':
this.$input.setAttribute('size', newValue);
break;
case 'label':
this.$label.textContent = t(newValue);
this.$img.remove();
break;
case 'value':
this.$input.value = newValue;
break;
default:
console.error(`unknown attribute: ${name}`);
break;
case 'title':
this.$div.setAttribute('title', `${t(newValue)}`)
break
case 'src':
this.$img.setAttribute('src', newValue)
this.$label.remove()
break
case 'size':
this.$input.setAttribute('size', newValue)
break
case 'label':
this.$label.textContent = t(newValue)
this.$img.remove()
break
case 'value':
this.$input.value = newValue
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get title () {
return this.getAttribute('title');
return this.getAttribute('title')
}
/**
@ -105,14 +108,15 @@ export class SEInput extends HTMLElement {
* @returns {void}
*/
set title (value) {
this.setAttribute('title', value);
this.setAttribute('title', value)
}
/**
* @function get
* @returns {any}
*/
get label () {
return this.getAttribute('label');
return this.getAttribute('label')
}
/**
@ -120,14 +124,15 @@ export class SEInput extends HTMLElement {
* @returns {void}
*/
set label (value) {
this.setAttribute('label', value);
this.setAttribute('label', value)
}
/**
* @function get
* @returns {any}
*/
get value () {
return this.$input.value;
return this.$input.value
}
/**
@ -135,14 +140,15 @@ export class SEInput extends HTMLElement {
* @returns {void}
*/
set value (value) {
this.$input.value = value;
this.$input.value = value
}
/**
* @function get
* @returns {any}
*/
get src () {
return this.getAttribute('src');
return this.getAttribute('src')
}
/**
@ -150,7 +156,7 @@ export class SEInput extends HTMLElement {
* @returns {void}
*/
set src (value) {
this.setAttribute('src', value);
this.setAttribute('src', value)
}
/**
@ -158,7 +164,7 @@ export class SEInput extends HTMLElement {
* @returns {any}
*/
get size () {
return this.getAttribute('size');
return this.getAttribute('size')
}
/**
@ -166,7 +172,7 @@ export class SEInput extends HTMLElement {
* @returns {void}
*/
set size (value) {
this.setAttribute('size', value);
this.setAttribute('size', value)
}
/**
@ -175,16 +181,16 @@ export class SEInput extends HTMLElement {
*/
connectedCallback () {
this.$input.addEventListener('change', (e) => {
e.preventDefault();
this.value = e.target.value;
this.dispatchEvent(this.$event);
});
e.preventDefault()
this.value = e.target.value
this.dispatchEvent(this.$event)
})
this.$input.addEventListener('keyup', (e) => {
e.preventDefault();
this.value = e.target.value;
this.dispatchEvent(this.$event);
});
e.preventDefault()
this.value = e.target.value
this.dispatchEvent(this.$event)
})
}
}
// Register
customElements.define('se-input', SEInput);
customElements.define('se-input', SEInput)

View File

@ -1,8 +1,8 @@
/* globals svgEditor */
import 'elix/define/DropdownList.js';
import { t } from '../locale.js';
import 'elix/define/DropdownList.js'
import { t } from '../locale.js'
const template = document.createElement('template');
const template = document.createElement('template')
template.innerHTML = `
<style>
elix-dropdown-list {
@ -30,7 +30,7 @@ elix-dropdown-list::part(popup-toggle) {
<slot></slot>
</elix-dropdown-list>
`;
`
/**
* @class SeList
*/
@ -39,22 +39,23 @@ export class SeList extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this.$dropdown = this._shadowRoot.querySelector('elix-dropdown-list');
this.$label = this._shadowRoot.querySelector('label');
this.$selection = this.$dropdown.shadowRoot.querySelector('#value');
this.items = this.querySelectorAll("se-list-item");
this.imgPath = svgEditor.configObj.curConfig.imgPath;
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
this.$dropdown = this._shadowRoot.querySelector('elix-dropdown-list')
this.$label = this._shadowRoot.querySelector('label')
this.$selection = this.$dropdown.shadowRoot.querySelector('#value')
this.items = this.querySelectorAll('se-list-item')
this.imgPath = svgEditor.configObj.curConfig.imgPath
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'label', 'width', 'height', 'title', 'value' ];
return ['label', 'width', 'height', 'title', 'value']
}
/**
@ -65,51 +66,51 @@ export class SeList extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
const currentObj = this;
if (oldValue === newValue) return;
const currentObj = this
if (oldValue === newValue) return
switch (name) {
case 'title':
this.$dropdown.setAttribute('title', t(newValue));
break;
case 'label':
this.$label.textContent = t(newValue);
break;
case 'height':
this.$dropdown.style.height = newValue;
break;
case 'width':
this.$dropdown.style.width = newValue;
break;
case 'value':
Array.from(this.items).forEach(function (element) {
if(element.getAttribute("value") === newValue) {
if (element.hasAttribute("src")) {
case 'title':
this.$dropdown.setAttribute('title', t(newValue))
break
case 'label':
this.$label.textContent = t(newValue)
break
case 'height':
this.$dropdown.style.height = newValue
break
case 'width':
this.$dropdown.style.width = newValue
break
case 'value':
Array.from(this.items).forEach(function (element) {
if (element.getAttribute('value') === newValue) {
if (element.hasAttribute('src')) {
// empty current selection children
while(currentObj.$selection.firstChild)
currentObj.$selection.removeChild(currentObj.$selection.firstChild);
// replace selection child with image of new value
const img = document.createElement('img');
img.src = currentObj.imgPath + '/' + element.getAttribute("src");
img.style.height = element.getAttribute("img-height");
img.setAttribute('title', t(element.getAttribute("title")));
currentObj.$selection.append(img);
} else {
currentObj.$selection.textContent = t(element.getAttribute('option'));
while (currentObj.$selection.firstChild) { currentObj.$selection.removeChild(currentObj.$selection.firstChild) }
// replace selection child with image of new value
const img = document.createElement('img')
img.src = currentObj.imgPath + '/' + element.getAttribute('src')
img.style.height = element.getAttribute('img-height')
img.setAttribute('title', t(element.getAttribute('title')))
currentObj.$selection.append(img)
} else {
currentObj.$selection.textContent = t(element.getAttribute('option'))
}
}
}
});
break;
default:
console.error(`unknown attribute: ${name}`);
break;
})
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get title () {
return this.getAttribute('title');
return this.getAttribute('title')
}
/**
@ -117,14 +118,15 @@ export class SeList extends HTMLElement {
* @returns {void}
*/
set title (value) {
this.setAttribute('title', value);
this.setAttribute('title', value)
}
/**
* @function get
* @returns {any}
*/
get label () {
return this.getAttribute('label');
return this.getAttribute('label')
}
/**
@ -132,14 +134,15 @@ export class SeList extends HTMLElement {
* @returns {void}
*/
set label (value) {
this.setAttribute('label', value);
this.setAttribute('label', value)
}
/**
* @function get
* @returns {any}
*/
get width () {
return this.getAttribute('width');
return this.getAttribute('width')
}
/**
@ -147,14 +150,15 @@ export class SeList extends HTMLElement {
* @returns {void}
*/
set width (value) {
this.setAttribute('width', value);
this.setAttribute('width', value)
}
/**
* @function get
* @returns {any}
*/
get height () {
return this.getAttribute('height');
return this.getAttribute('height')
}
/**
@ -162,31 +166,32 @@ export class SeList extends HTMLElement {
* @returns {void}
*/
set height (value) {
this.setAttribute('height', value);
this.setAttribute('height', value)
}
/**
* @function connectedCallback
* @returns {void}
*/
connectedCallback () {
const currentObj = this;
const currentObj = this
this.$dropdown.addEventListener('selectedindexchange', (e) => {
if (e?.detail?.selectedIndex !== undefined) {
const value = this.$dropdown.selectedItem.getAttribute('value');
const closeEvent = new CustomEvent('change', { detail: { value } });
currentObj.dispatchEvent(closeEvent);
currentObj.value = value;
currentObj.setAttribute("value", value);
const value = this.$dropdown.selectedItem.getAttribute('value')
const closeEvent = new CustomEvent('change', { detail: { value } })
currentObj.dispatchEvent(closeEvent)
currentObj.value = value
currentObj.setAttribute('value', value)
}
});
})
this.$dropdown.addEventListener('close', (_e) => {
/** with Chrome, selectedindexchange does not fire consistently
* unless you forec change in this close event
*/
this.$dropdown.selectedIndex = this.$dropdown.currentIndex;
});
this.$dropdown.selectedIndex = this.$dropdown.currentIndex
})
}
}
// Register
customElements.define('se-list', SeList);
customElements.define('se-list', SeList)

View File

@ -1,8 +1,8 @@
/* globals svgEditor */
import 'elix/define/Option.js';
import { t } from '../locale.js';
import 'elix/define/Option.js'
import { t } from '../locale.js'
const template = document.createElement('template');
const template = document.createElement('template')
template.innerHTML = `
<style>
elix-option{
@ -17,7 +17,7 @@ template.innerHTML = `
<img alt="icon" />
<slot></slot>
</elix-option>
`;
`
/**
* @class SeMenu
*/
@ -26,23 +26,24 @@ export class SeListItem extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this.$menuitem = this._shadowRoot.querySelector('elix-option');
this.$svg = this.$menuitem.shadowRoot.querySelector('#checkmark');
this.$svg.setAttribute('style', 'display: none;');
this.$img = this._shadowRoot.querySelector('img');
this.$img.setAttribute('style', 'display: none;');
this.imgPath = svgEditor.configObj.curConfig.imgPath;
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
this.$menuitem = this._shadowRoot.querySelector('elix-option')
this.$svg = this.$menuitem.shadowRoot.querySelector('#checkmark')
this.$svg.setAttribute('style', 'display: none;')
this.$img = this._shadowRoot.querySelector('img')
this.$img.setAttribute('style', 'display: none;')
this.imgPath = svgEditor.configObj.curConfig.imgPath
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'option', 'src', 'title', 'img-height' ];
return ['option', 'src', 'title', 'img-height']
}
/**
@ -53,33 +54,34 @@ export class SeListItem extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
if (oldValue === newValue) return
switch (name) {
case 'option':
this.$menuitem.setAttribute('option', newValue);
this.$menuitem.textContent = t(newValue);
break;
case 'src':
this.$img.setAttribute('style', 'display: block;');
this.$img.setAttribute('src', this.imgPath + '/' + newValue);
break;
case 'title':
this.$img.setAttribute('title', t(newValue));
break;
case 'img-height':
this.$img.setAttribute('height', newValue);
break;
default:
console.error(`unknown attribute: ${name}`);
break;
case 'option':
this.$menuitem.setAttribute('option', newValue)
this.$menuitem.textContent = t(newValue)
break
case 'src':
this.$img.setAttribute('style', 'display: block;')
this.$img.setAttribute('src', this.imgPath + '/' + newValue)
break
case 'title':
this.$img.setAttribute('title', t(newValue))
break
case 'img-height':
this.$img.setAttribute('height', newValue)
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get option () {
return this.getAttribute('option');
return this.getAttribute('option')
}
/**
@ -87,14 +89,15 @@ export class SeListItem extends HTMLElement {
* @returns {void}
*/
set option (value) {
this.setAttribute('option', value);
this.setAttribute('option', value)
}
/**
* @function get
* @returns {any}
*/
get title () {
return this.getAttribute('title');
return this.getAttribute('title')
}
/**
@ -102,14 +105,15 @@ export class SeListItem extends HTMLElement {
* @returns {void}
*/
set title (value) {
this.setAttribute('title', value);
this.setAttribute('title', value)
}
/**
* @function get
* @returns {any}
*/
get imgHeight () {
return this.getAttribute('img-height');
return this.getAttribute('img-height')
}
/**
@ -117,14 +121,15 @@ export class SeListItem extends HTMLElement {
* @returns {void}
*/
set imgHeight (value) {
this.setAttribute('img-height', value);
this.setAttribute('img-height', value)
}
/**
* @function get
* @returns {any}
*/
get src () {
return this.getAttribute('src');
return this.getAttribute('src')
}
/**
@ -132,9 +137,9 @@ export class SeListItem extends HTMLElement {
* @returns {void}
*/
set src (value) {
this.setAttribute('src', value);
this.setAttribute('src', value)
}
}
// Register
customElements.define('se-list-item', SeListItem);
customElements.define('se-list-item', SeListItem)

View File

@ -1,8 +1,8 @@
/* globals svgEditor */
import 'elix/define/MenuItem.js';
import './sePlainMenuButton.js';
import 'elix/define/MenuItem.js'
import './sePlainMenuButton.js'
const template = document.createElement('template');
const template = document.createElement('template')
template.innerHTML = `
<style>
:host {
@ -29,7 +29,7 @@ template.innerHTML = `
<slot></slot>
</elix-menu-button>
`;
`
/**
* @class SeMenu
*/
@ -38,20 +38,21 @@ export class SeMenu extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this.$menu = this._shadowRoot.querySelector('elix-menu-button');
this.$label = this.$menu.shadowRoot.querySelector('#popupToggle').shadowRoot;
this.imgPath = svgEditor.configObj.curConfig.imgPath;
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
this.$menu = this._shadowRoot.querySelector('elix-menu-button')
this.$label = this.$menu.shadowRoot.querySelector('#popupToggle').shadowRoot
this.imgPath = svgEditor.configObj.curConfig.imgPath
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'label', 'src' ];
return ['label', 'src']
}
/**
@ -62,29 +63,30 @@ export class SeMenu extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
const image = new Image();
if (oldValue === newValue) return;
const image = new Image()
if (oldValue === newValue) return
switch (name) {
case 'src':
image.src = this.imgPath + '/' + newValue;
image.width = 24;
image.height = 24;
this.$label.prepend(image);
break;
case 'label':
this.$label.prepend(newValue);
break;
default:
console.error(`unknown attribute: ${name}`);
break;
case 'src':
image.src = this.imgPath + '/' + newValue
image.width = 24
image.height = 24
this.$label.prepend(image)
break
case 'label':
this.$label.prepend(newValue)
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get label () {
return this.getAttribute('label');
return this.getAttribute('label')
}
/**
@ -92,14 +94,15 @@ export class SeMenu extends HTMLElement {
* @returns {void}
*/
set label (value) {
this.setAttribute('label', value);
this.setAttribute('label', value)
}
/**
* @function get
* @returns {any}
*/
get src () {
return this.getAttribute('src');
return this.getAttribute('src')
}
/**
@ -107,7 +110,7 @@ export class SeMenu extends HTMLElement {
* @returns {void}
*/
set src (value) {
this.setAttribute('src', value);
this.setAttribute('src', value)
}
/**
* @function connectedCallback
@ -125,4 +128,4 @@ export class SeMenu extends HTMLElement {
}
// Register
customElements.define('se-menu', SeMenu);
customElements.define('se-menu', SeMenu)

View File

@ -1,8 +1,8 @@
/* globals svgEditor */
import 'elix/define/Menu.js';
import 'elix/define/MenuItem.js';
import { t } from '../locale.js';
const template = document.createElement('template');
import 'elix/define/Menu.js'
import 'elix/define/MenuItem.js'
import { t } from '../locale.js'
const template = document.createElement('template')
template.innerHTML = `
<style>
</style>
@ -12,7 +12,7 @@ template.innerHTML = `
<span style="margin-left: 7px;"></span>
</div>
</elix-menu-item>
`;
`
/**
* @class SeMenuItem
*/
@ -21,24 +21,26 @@ export class SeMenuItem extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this.$img = this._shadowRoot.querySelector('img');
this.$label = this._shadowRoot.querySelector('span');
this.$menuitem = this._shadowRoot.querySelector('elix-menu-item');
this.$svg = this.$menuitem.shadowRoot.querySelector('#checkmark');
this.$svg.setAttribute('style', 'display: none;');
this.imgPath = svgEditor.configObj.curConfig.imgPath;
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
this.$img = this._shadowRoot.querySelector('img')
this.$label = this._shadowRoot.querySelector('span')
this.$menuitem = this._shadowRoot.querySelector('elix-menu-item')
this.$svg = this.$menuitem.shadowRoot.querySelector('#checkmark')
this.$svg.setAttribute('style', 'display: none;')
this.imgPath = svgEditor.configObj.curConfig.imgPath
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'label', 'src' ];
return ['label', 'src']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -47,28 +49,29 @@ export class SeMenuItem extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
let shortcut = '';
if (oldValue === newValue) return;
let shortcut = ''
if (oldValue === newValue) return
switch (name) {
case 'src':
this.$img.style.display = 'inline-block';
this.$img.setAttribute('src', this.imgPath + '/' + newValue);
break;
case 'label':
shortcut = this.getAttribute('shortcut');
this.$label.textContent = `${t(newValue)} ${shortcut ? `(${shortcut})` : ''}`;
break;
default:
console.error(`unknown attribute: ${name}`);
break;
case 'src':
this.$img.style.display = 'inline-block'
this.$img.setAttribute('src', this.imgPath + '/' + newValue)
break
case 'label':
shortcut = this.getAttribute('shortcut')
this.$label.textContent = `${t(newValue)} ${shortcut ? `(${shortcut})` : ''}`
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get label () {
return this.getAttribute('label');
return this.getAttribute('label')
}
/**
@ -76,14 +79,15 @@ export class SeMenuItem extends HTMLElement {
* @returns {void}
*/
set label (value) {
this.setAttribute('label', value);
this.setAttribute('label', value)
}
/**
* @function get
* @returns {any}
*/
get src () {
return this.getAttribute('src');
return this.getAttribute('src')
}
/**
@ -91,7 +95,7 @@ export class SeMenuItem extends HTMLElement {
* @returns {void}
*/
set src (value) {
this.setAttribute('src', value);
this.setAttribute('src', value)
}
/**
@ -100,24 +104,24 @@ export class SeMenuItem extends HTMLElement {
*/
connectedCallback () {
// capture shortcuts
const shortcut = this.getAttribute('shortcut');
const shortcut = this.getAttribute('shortcut')
if (shortcut) {
// register the keydown event
document.addEventListener('keydown', (e) => {
// only track keyboard shortcuts for the body containing the SVG-Editor
if (e.target.nodeName !== 'BODY') return;
if (e.target.nodeName !== 'BODY') return
// normalize key
const key = `${(e.metaKey) ? 'meta+' : ''}${(e.ctrlKey) ? 'ctrl+' : ''}${e.key.toUpperCase()}`;
if (shortcut !== key) return;
const key = `${(e.metaKey) ? 'meta+' : ''}${(e.ctrlKey) ? 'ctrl+' : ''}${e.key.toUpperCase()}`
if (shortcut !== key) return
// launch the click event
if (this.id) {
document.getElementById(this.id).click();
document.getElementById(this.id).click()
}
e.preventDefault();
});
e.preventDefault()
})
}
}
}
// Register
customElements.define('se-menu-item', SeMenuItem);
customElements.define('se-menu-item', SeMenuItem)

View File

@ -11,9 +11,9 @@ const palette = [
'#ffaaaa', '#ffd4aa', '#ffffaa', '#d4ffaa',
'#aaffaa', '#aaffd4', '#aaffff', '#aad4ff',
'#aaaaff', '#d4aaff', '#ffaaff', '#ffaad4'
];
]
const template = document.createElement('template');
const template = document.createElement('template')
template.innerHTML = `
<style>
.square {
@ -50,7 +50,7 @@ template.innerHTML = `
<div id="js-se-palette">
</div>
</div>
`;
`
/**
* @class SEPalette
@ -60,38 +60,38 @@ export class SEPalette extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this.$strip = this._shadowRoot.querySelector('#js-se-palette');
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
this.$strip = this._shadowRoot.querySelector('#js-se-palette')
palette.forEach((rgb) => {
const newDiv = document.createElement('div');
newDiv.classList.add('square');
if(rgb === 'none') {
const img = document.createElement('img');
img.src = `data:image/svg+xml;charset=utf-8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgY2xhc3M9InN2Z19pY29uIj48c3ZnIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgICA8bGluZSBmaWxsPSJub25lIiBzdHJva2U9IiNkNDAwMDAiIGlkPSJzdmdfOTAiIHkyPSIyNCIgeDI9IjI0IiB5MT0iMCIgeDE9IjAiLz4KICAgIDxsaW5lIGlkPSJzdmdfOTIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2Q0MDAwMCIgeTI9IjI0IiB4Mj0iMCIgeTE9IjAiIHgxPSIyNCIvPgogIDwvc3ZnPjwvc3ZnPg==`;
img.style.width = "15px";
img.style.height = "15px";
newDiv.append(img);
const newDiv = document.createElement('div')
newDiv.classList.add('square')
if (rgb === 'none') {
const img = document.createElement('img')
img.src = 'data:image/svg+xml;charset=utf-8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgY2xhc3M9InN2Z19pY29uIj48c3ZnIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgICA8bGluZSBmaWxsPSJub25lIiBzdHJva2U9IiNkNDAwMDAiIGlkPSJzdmdfOTAiIHkyPSIyNCIgeDI9IjI0IiB5MT0iMCIgeDE9IjAiLz4KICAgIDxsaW5lIGlkPSJzdmdfOTIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2Q0MDAwMCIgeTI9IjI0IiB4Mj0iMCIgeTE9IjAiIHgxPSIyNCIvPgogIDwvc3ZnPjwvc3ZnPg=='
img.style.width = '15px'
img.style.height = '15px'
newDiv.append(img)
} else {
newDiv.style.backgroundColor = rgb;
newDiv.style.backgroundColor = rgb
}
newDiv.dataset.rgb = rgb;
newDiv.dataset.rgb = rgb
newDiv.addEventListener('click', (evt) => {
evt.preventDefault();
evt.preventDefault()
// shift key or right click for stroke
const picker = evt.shiftKey || evt.button === 2 ? 'stroke' : 'fill';
let color = newDiv.dataset.rgb;
const picker = evt.shiftKey || evt.button === 2 ? 'stroke' : 'fill'
let color = newDiv.dataset.rgb
// Webkit-based browsers returned 'initial' here for no stroke
if (color === 'none' || color === 'transparent' || color === 'initial') {
color = 'none';
color = 'none'
}
const paletteEvent = new CustomEvent('change', { detail: { picker, color }, bubbles: false });
this.dispatchEvent(paletteEvent);
});
this.$strip.append(newDiv);
});
const paletteEvent = new CustomEvent('change', { detail: { picker, color }, bubbles: false })
this.dispatchEvent(paletteEvent)
})
this.$strip.append(newDiv)
})
}
/**
@ -100,15 +100,17 @@ export class SEPalette extends HTMLElement {
* @returns {void}
*/
init (i18next) {
this.setAttribute('ui-palette_info', i18next.t('ui.palette_info'));
this.setAttribute('ui-palette_info', i18next.t('ui.palette_info'))
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'ui-palette_info' ];
return ['ui-palette_info']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -117,12 +119,13 @@ export class SEPalette extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
let node;
let node
if (name === 'ui-palette_info') {
node = this._shadowRoot.querySelector('#palette_holder');
node.setAttribute('title', newValue);
node = this._shadowRoot.querySelector('#palette_holder')
node.setAttribute('title', newValue)
}
}
/**
* @function connectedCallback
* @returns {void}
@ -132,4 +135,4 @@ export class SEPalette extends HTMLElement {
}
// Register
customElements.define('se-palette', SEPalette);
customElements.define('se-palette', SEPalette)

View File

@ -1,6 +1,6 @@
import { template } from 'elix/src/base/internal.js';
import { fragmentFrom } from 'elix/src/core/htmlLiterals.js';
import PlainButton from 'elix/src/plain/PlainButton.js';
import { template } from 'elix/src/base/internal.js'
import { fragmentFrom } from 'elix/src/core/htmlLiterals.js'
import PlainButton from 'elix/src/plain/PlainButton.js'
/**
* @class SePlainBorderButton
@ -13,7 +13,7 @@ class SePlainBorderButton extends PlainButton {
* @returns {PlainObject}
*/
get [template] () {
const result = super[template];
const result = super[template]
result.content.append(
fragmentFrom.html`
<style>
@ -23,9 +23,9 @@ class SePlainBorderButton extends PlainButton {
}
</style>
`
);
return result;
)
return result
}
}
export default SePlainBorderButton;
export default SePlainBorderButton

View File

@ -1,6 +1,6 @@
import PlainMenuButton from 'elix/src/plain/PlainMenuButton.js';
import { defaultState } from 'elix/src/base/internal.js';
import sePlainBorderButton from './sePlainBorderButton.js';
import PlainMenuButton from 'elix/src/plain/PlainMenuButton.js'
import { defaultState } from 'elix/src/base/internal.js'
import sePlainBorderButton from './sePlainBorderButton.js'
/**
* @class ElixMenuButton
@ -13,8 +13,8 @@ export default class ElixMenuButton extends PlainMenuButton {
get [defaultState] () {
return Object.assign(super[defaultState], {
sourcePartType: sePlainBorderButton
});
})
}
}
customElements.define('elix-menu-button', ElixMenuButton);
customElements.define('elix-menu-button', ElixMenuButton)

View File

@ -1,5 +1,5 @@
import { t } from '../locale.js';
const template = document.createElement('template');
import { t } from '../locale.js'
const template = document.createElement('template')
template.innerHTML = `
<style>
select {
@ -21,7 +21,7 @@ label {
<select>
</select>
`;
`
/**
* @class SeList
*/
@ -30,19 +30,20 @@ export class SeSelect extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this.$select = this._shadowRoot.querySelector('select');
this.$label = this._shadowRoot.querySelector('label');
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
this.$select = this._shadowRoot.querySelector('select')
this.$label = this._shadowRoot.querySelector('label')
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'label', 'width', 'height', 'options', 'values', 'title', 'disabled' ];
return ['label', 'width', 'height', 'options', 'values', 'title', 'disabled']
}
/**
@ -53,64 +54,63 @@ export class SeSelect extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
let options;
if (oldValue === newValue) return;
let options
if (oldValue === newValue) return
switch (name) {
case 'label':
this.$label.textContent = t(newValue);
break;
case 'title':
this.$select.setAttribute("title", t(newValue));
break;
case 'disabled':
if(newValue === null) {
this.$select.removeAttribute("disabled");
} else {
this.$select.setAttribute("disabled", newValue);
}
break;
case 'height':
this.$select.style.height = newValue;
break;
case 'width':
this.$select.style.width = newValue;
break;
case 'options':
if(newValue === "") {
while(this.$select.firstChild)
this.$select.removeChild(this.$select.firstChild);
} else {
options = newValue.split(',');
options.forEach((option) => {
const optionNode = document.createElement("OPTION");
const text = document.createTextNode(t(option));
optionNode.appendChild(text);
this.$select.appendChild(optionNode);
});
}
break;
case 'values':
if(newValue === "") {
while(this.$select.firstChild)
this.$select.removeChild(this.$select.firstChild);
} else {
options = newValue.split('::');
options.forEach((option, index) => {
this.$select.children[index].setAttribute('value', option);
});
}
break;
default:
console.error(`unknown attribute: ${name}`);
break;
case 'label':
this.$label.textContent = t(newValue)
break
case 'title':
this.$select.setAttribute('title', t(newValue))
break
case 'disabled':
if (newValue === null) {
this.$select.removeAttribute('disabled')
} else {
this.$select.setAttribute('disabled', newValue)
}
break
case 'height':
this.$select.style.height = newValue
break
case 'width':
this.$select.style.width = newValue
break
case 'options':
if (newValue === '') {
while (this.$select.firstChild) { this.$select.removeChild(this.$select.firstChild) }
} else {
options = newValue.split(',')
options.forEach((option) => {
const optionNode = document.createElement('OPTION')
const text = document.createTextNode(t(option))
optionNode.appendChild(text)
this.$select.appendChild(optionNode)
})
}
break
case 'values':
if (newValue === '') {
while (this.$select.firstChild) { this.$select.removeChild(this.$select.firstChild) }
} else {
options = newValue.split('::')
options.forEach((option, index) => {
this.$select.children[index].setAttribute('value', option)
})
}
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get label () {
return this.getAttribute('label');
return this.getAttribute('label')
}
/**
@ -118,14 +118,15 @@ export class SeSelect extends HTMLElement {
* @returns {void}
*/
set label (value) {
this.setAttribute('label', value);
this.setAttribute('label', value)
}
/**
* @function get
* @returns {any}
*/
get width () {
return this.getAttribute('width');
return this.getAttribute('width')
}
/**
@ -133,14 +134,15 @@ export class SeSelect extends HTMLElement {
* @returns {void}
*/
set width (value) {
this.setAttribute('width', value);
this.setAttribute('width', value)
}
/**
* @function get
* @returns {any}
*/
get height () {
return this.getAttribute('height');
return this.getAttribute('height')
}
/**
@ -148,14 +150,15 @@ export class SeSelect extends HTMLElement {
* @returns {void}
*/
set height (value) {
this.setAttribute('height', value);
this.setAttribute('height', value)
}
/**
* @function get
* @returns {any}
*/
get value () {
return this.$select.value;
return this.$select.value
}
/**
@ -163,14 +166,15 @@ export class SeSelect extends HTMLElement {
* @returns {void}
*/
set value (value) {
this.$select.value = value;
this.$select.value = value
}
/**
* @function get
* @returns {any}
*/
get disabled () {
return this.$select.getAttribute('disabled');
return this.$select.getAttribute('disabled')
}
/**
@ -178,22 +182,23 @@ export class SeSelect extends HTMLElement {
* @returns {void}
*/
set disabled (value) {
this.$select.setAttribute('disabled', value);
this.$select.setAttribute('disabled', value)
}
/**
* @function connectedCallback
* @returns {void}
*/
connectedCallback () {
const currentObj = this;
const currentObj = this
this.$select.addEventListener('change', () => {
const value = this.$select.value;
const closeEvent = new CustomEvent('change', { detail: { value } });
currentObj.dispatchEvent(closeEvent);
currentObj.value = value;
});
const value = this.$select.value
const closeEvent = new CustomEvent('change', { detail: { value } })
currentObj.dispatchEvent(closeEvent)
currentObj.value = value
})
}
}
// Register
customElements.define('se-select', SeSelect);
customElements.define('se-select', SeSelect)

View File

@ -1,8 +1,8 @@
/* globals svgEditor */
import '../dialogs/se-elix/define/NumberSpinBox.js';
import { t } from '../locale.js';
import '../dialogs/se-elix/define/NumberSpinBox.js'
import { t } from '../locale.js'
const template = document.createElement('template');
const template = document.createElement('template')
template.innerHTML = `
<style>
div {
@ -49,7 +49,7 @@ template.innerHTML = `
<span id="label">label</span>
<elix-number-spin-box min="1" step="1"></elix-number-spin-box>
</div>
`;
`
/**
* @class SESpinInput
@ -59,25 +59,27 @@ export class SESpinInput extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
// locate the component
this.$div = this._shadowRoot.querySelector('div');
this.$img = this._shadowRoot.querySelector('img');
this.$label = this.shadowRoot.getElementById('label');
this.$event = new CustomEvent('change');
this.$input = this._shadowRoot.querySelector('elix-number-spin-box');
this.imgPath = svgEditor.configObj.curConfig.imgPath;
this.$div = this._shadowRoot.querySelector('div')
this.$img = this._shadowRoot.querySelector('img')
this.$label = this.shadowRoot.getElementById('label')
this.$event = new CustomEvent('change')
this.$input = this._shadowRoot.querySelector('elix-number-spin-box')
this.imgPath = svgEditor.configObj.curConfig.imgPath
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'value', 'label', 'src', 'size', 'min', 'max', 'step', 'title' ];
return ['value', 'label', 'src', 'size', 'min', 'max', 'step', 'title']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -86,52 +88,53 @@ export class SESpinInput extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
if (oldValue === newValue) return
switch (name) {
case 'title':
{
const shortcut = this.getAttribute('shortcut');
this.$div.setAttribute('title', `${t(newValue)} ${shortcut ? `[${t(shortcut)}]` : ''}`);
}
break;
case 'src':
this.$img.setAttribute('src', this.imgPath + '/' + newValue);
this.$label.remove();
this.$div.classList.add('imginside');
break;
case 'size':
case 'title':
{
const shortcut = this.getAttribute('shortcut')
this.$div.setAttribute('title', `${t(newValue)} ${shortcut ? `[${t(shortcut)}]` : ''}`)
}
break
case 'src':
this.$img.setAttribute('src', this.imgPath + '/' + newValue)
this.$label.remove()
this.$div.classList.add('imginside')
break
case 'size':
// access to the underlying input box
this.$input.shadowRoot.getElementById('input').size = newValue;
// below seems mandatory to override the default width style that takes precedence on size
this.$input.shadowRoot.getElementById('input').style.width = 'unset';
break;
case 'step':
this.$input.setAttribute('step', newValue);
break;
case 'min':
this.$input.setAttribute('min', newValue);
break;
case 'max':
this.$input.setAttribute('max', newValue);
break;
case 'label':
this.$label.textContent = t(newValue);
this.$img.remove();
break;
case 'value':
this.$input.value = newValue;
break;
default:
console.error(`unknown attribute: ${name}`);
break;
this.$input.shadowRoot.getElementById('input').size = newValue
// below seems mandatory to override the default width style that takes precedence on size
this.$input.shadowRoot.getElementById('input').style.width = 'unset'
break
case 'step':
this.$input.setAttribute('step', newValue)
break
case 'min':
this.$input.setAttribute('min', newValue)
break
case 'max':
this.$input.setAttribute('max', newValue)
break
case 'label':
this.$label.textContent = t(newValue)
this.$img.remove()
break
case 'value':
this.$input.value = newValue
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get title () {
return this.getAttribute('title');
return this.getAttribute('title')
}
/**
@ -139,14 +142,15 @@ export class SESpinInput extends HTMLElement {
* @returns {void}
*/
set title (value) {
this.setAttribute('title', value);
this.setAttribute('title', value)
}
/**
* @function get
* @returns {any}
*/
get label () {
return this.getAttribute('label');
return this.getAttribute('label')
}
/**
@ -154,14 +158,15 @@ export class SESpinInput extends HTMLElement {
* @returns {void}
*/
set label (value) {
this.setAttribute('label', value);
this.setAttribute('label', value)
}
/**
* @function get
* @returns {any}
*/
get value () {
return this.$input.value;
return this.$input.value
}
/**
@ -169,14 +174,15 @@ export class SESpinInput extends HTMLElement {
* @returns {void}
*/
set value (value) {
this.$input.value = value;
this.$input.value = value
}
/**
* @function get
* @returns {any}
*/
get src () {
return this.getAttribute('src');
return this.getAttribute('src')
}
/**
@ -184,7 +190,7 @@ export class SESpinInput extends HTMLElement {
* @returns {void}
*/
set src (value) {
this.setAttribute('src', value);
this.setAttribute('src', value)
}
/**
@ -192,7 +198,7 @@ export class SESpinInput extends HTMLElement {
* @returns {any}
*/
get size () {
return this.getAttribute('size');
return this.getAttribute('size')
}
/**
@ -200,7 +206,7 @@ export class SESpinInput extends HTMLElement {
* @returns {void}
*/
set size (value) {
this.setAttribute('size', value);
this.setAttribute('size', value)
}
/**
@ -208,31 +214,31 @@ export class SESpinInput extends HTMLElement {
* @returns {void}
*/
connectedCallback () {
const shadow = this.$input.shadowRoot;
const childNodes = Array.from(shadow.childNodes);
childNodes.forEach( (childNode) => {
if(childNode?.id === "input") {
const shadow = this.$input.shadowRoot
const childNodes = Array.from(shadow.childNodes)
childNodes.forEach((childNode) => {
if (childNode?.id === 'input') {
childNode.addEventListener('keyup', (e) => {
e.preventDefault();
e.preventDefault()
if (!isNaN(e.target.value)) {
this.value = e.target.value;
this.dispatchEvent(this.$event);
this.value = e.target.value
this.dispatchEvent(this.$event)
}
});
})
}
});
})
this.$input.addEventListener('change', (e) => {
e.preventDefault();
this.value = e.target.value;
this.dispatchEvent(this.$event);
});
e.preventDefault()
this.value = e.target.value
this.dispatchEvent(this.$event)
})
this.$input.addEventListener('click', (e) => {
e.preventDefault();
this.value = e.target.value;
this.dispatchEvent(this.$event);
});
e.preventDefault()
this.value = e.target.value
this.dispatchEvent(this.$event)
})
}
}
// Register
customElements.define('se-spin-input', SESpinInput);
customElements.define('se-spin-input', SESpinInput)

View File

@ -1,5 +1,5 @@
import { t } from '../locale.js';
const template = document.createElement('template');
import { t } from '../locale.js'
const template = document.createElement('template')
template.innerHTML = `
<style>
#layersLabel {
@ -9,7 +9,7 @@ template.innerHTML = `
}
</style>
<div></div>
`;
`
/**
* @class SeText
*/
@ -18,20 +18,22 @@ export class SeText extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
// locate the component
this.$div = this._shadowRoot.querySelector('div');
this.$div = this._shadowRoot.querySelector('div')
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'text', 'value', 'style', 'title', 'id' ];
return ['text', 'value', 'style', 'title', 'id']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -40,35 +42,36 @@ export class SeText extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue) return;
if (oldValue === newValue) return
switch (name) {
case 'text':
this.$div.textContent = t(newValue);
break;
case 'title':
this.$div.setAttribute("title", t(newValue));
break;
case 'style':
this.$div.style = newValue;
break;
case 'id':
this.$div.id = newValue;
break;
case 'value':
this.$div.value = newValue;
//this.$div.setAttribute("value", newValue);
break;
default:
console.error(`unknown attribute: ${name}`);
break;
case 'text':
this.$div.textContent = t(newValue)
break
case 'title':
this.$div.setAttribute('title', t(newValue))
break
case 'style':
this.$div.style = newValue
break
case 'id':
this.$div.id = newValue
break
case 'value':
this.$div.value = newValue
// this.$div.setAttribute("value", newValue);
break
default:
console.error(`unknown attribute: ${name}`)
break
}
}
/**
* @function get
* @returns {any}
*/
get text () {
return this.$div.textContent;
return this.$div.textContent
}
/**
@ -76,14 +79,15 @@ export class SeText extends HTMLElement {
* @returns {void}
*/
set text (value) {
this.$div.setAttribute("title", t(value));
this.$div.setAttribute('title', t(value))
}
/**
* @function get
* @returns {any}
*/
get value () {
return this.value;
return this.value
}
/**
@ -91,14 +95,15 @@ export class SeText extends HTMLElement {
* @returns {void}
*/
set value (value) {
this.value = value;
this.value = value
}
/**
* @function get
* @returns {any}
*/
get title () {
return this.getAttribute('title');
return this.getAttribute('title')
}
/**
@ -106,8 +111,9 @@ export class SeText extends HTMLElement {
* @returns {void}
*/
set title (value) {
this.setAttribute('title', value);
this.setAttribute('title', value)
}
/**
* @function connectedCallback
* @returns {void}
@ -118,4 +124,4 @@ export class SeText extends HTMLElement {
}
// Register
customElements.define('se-text', SeText);
customElements.define('se-text', SeText)

View File

@ -1,8 +1,8 @@
/* globals svgEditor */
import ListComboBox from 'elix/define/ListComboBox.js';
import * as internal from 'elix/src/base/internal.js';
import { templateFrom, fragmentFrom } from 'elix/src/core/htmlLiterals.js';
import NumberSpinBox from '../dialogs/se-elix/define/NumberSpinBox.js';
import ListComboBox from 'elix/define/ListComboBox.js'
import * as internal from 'elix/src/base/internal.js'
import { templateFrom, fragmentFrom } from 'elix/src/core/htmlLiterals.js'
import NumberSpinBox from '../dialogs/se-elix/define/NumberSpinBox.js'
/**
* @class Dropdown
@ -17,20 +17,21 @@ class Zoom extends ListComboBox {
inputPartType: NumberSpinBox,
src: 'logo.svg',
inputsize: '100%'
});
})
}
/**
* @function get
* @returns {PlainObject}
*/
get [internal.template] () {
const result = super[internal.template];
const source = result.content.getElementById('source');
const result = super[internal.template]
const source = result.content.getElementById('source')
// add a icon before our dropdown
source.prepend(fragmentFrom.html`
<img src="zoom" alt="icon" width="18" height="18">
</img>
`.cloneNode(true));
`.cloneNode(true))
// change the style so it fits in our toolbar
result.content.append(
templateFrom.html`
@ -57,16 +58,18 @@ class Zoom extends ListComboBox {
</style>
`.content
);
return result;
)
return result
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'title', 'src', 'inputsize', 'value' ];
return ['title', 'src', 'inputsize', 'value']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -75,97 +78,104 @@ class Zoom extends ListComboBox {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
if (oldValue === newValue && name !== "src") return;
if (oldValue === newValue && name !== 'src') return
switch (name) {
case 'title':
case 'title':
// this.$span.setAttribute('title', `${newValue} ${shortcut ? `[${shortcut}]` : ''}`);
break;
case 'src':
{
const { imgPath } = svgEditor.configObj.curConfig;
this.src = imgPath + '/' + newValue;
}
break;
case 'inputsize':
this.inputsize = newValue;
break;
default:
super.attributeChangedCallback(name, oldValue, newValue);
break;
break
case 'src':
{
const { imgPath } = svgEditor.configObj.curConfig
this.src = imgPath + '/' + 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);
super[internal.render](changed)
if (this[internal.firstRender]) {
this.$img = this.shadowRoot.querySelector('img');
this.$input = this.shadowRoot.getElementById('input');
this.$img = this.shadowRoot.querySelector('img')
this.$input = this.shadowRoot.getElementById('input')
}
if (changed.src) {
this.$img.setAttribute('src', this[internal.state].src);
this.$img.setAttribute('src', this[internal.state].src)
}
if (changed.inputsize) {
this.$input.shadowRoot.querySelector('[part~="input"]').style.width = this[internal.state].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');
e.preventDefault()
const value = e.detail?.closeResult?.getAttribute('value')
if (value) {
const closeEvent = new CustomEvent('change', { detail: { value } });
this.dispatchEvent(closeEvent);
const closeEvent = new CustomEvent('change', { detail: { value } })
this.dispatchEvent(closeEvent)
}
});
})
}
}
/**
* @function src
* @returns {string} src
*/
get src () {
return this[internal.state].src;
return this[internal.state].src
}
/**
* @function src
* @returns {void}
*/
set src (src) {
this[internal.setState]({ src });
this[internal.setState]({ src })
}
/**
* @function inputsize
* @returns {string} src
*/
get inputsize () {
return this[internal.state].inputsize;
return this[internal.state].inputsize
}
/**
* @function src
* @returns {void}
*/
set inputsize (inputsize) {
this[internal.setState]({ inputsize });
this[internal.setState]({ inputsize })
}
/**
* @function value
* @returns {string} src
*/
get value () {
return this[internal.state].value;
return this[internal.state].value
}
/**
* @function value
* @returns {void}
*/
set value (value) {
this[internal.setState]({ value });
this[internal.setState]({ value })
}
}
// Register
customElements.define('se-zoom', Zoom);
customElements.define('se-zoom', Zoom)

View File

@ -5,7 +5,7 @@
* @author Adam Bender
*/
let contextMenuExtensions = {};
let contextMenuExtensions = {}
/**
* Signature depends on what the user adds; in the case of our uses with
@ -27,8 +27,8 @@ let contextMenuExtensions = {};
* @returns {boolean}
*/
const menuItemIsValid = function (menuItem) {
return menuItem && menuItem.id && menuItem.label && menuItem.action && typeof menuItem.action === 'function';
};
return menuItem && menuItem.id && menuItem.label && menuItem.action && typeof menuItem.action === 'function'
}
/**
* @function module:contextmenu.add
@ -42,15 +42,15 @@ export const add = function (menuItem) {
throw new TypeError(
'Menu items must be defined and have at least properties: ' +
'id, label, action, where action must be a function'
);
)
}
if (menuItem.id in contextMenuExtensions) {
throw new Error('Cannot add extension "' + menuItem.id + '", an extension by that name already exists"');
throw new Error('Cannot add extension "' + menuItem.id + '", an extension by that name already exists"')
}
// Register menuItem action, see below for deferred menu dom injection
contextMenuExtensions[menuItem.id] = menuItem;
contextMenuExtensions[menuItem.id] = menuItem
// TODO: Need to consider how to handle custom enable/disable behavior
};
}
/**
* @function module:contextmenu.hasCustomHandler
@ -58,8 +58,8 @@ export const add = function (menuItem) {
* @returns {boolean}
*/
export const hasCustomHandler = function (handlerKey) {
return Boolean(contextMenuExtensions[handlerKey]);
};
return Boolean(contextMenuExtensions[handlerKey])
}
/**
* @function module:contextmenu.getCustomHandler
@ -67,8 +67,8 @@ export const hasCustomHandler = function (handlerKey) {
* @returns {module:contextmenu.MenuItemAction}
*/
export const getCustomHandler = function (handlerKey) {
return contextMenuExtensions[handlerKey].action;
};
return contextMenuExtensions[handlerKey].action
}
/**
* @param {module:contextmenu.MenuItem} menuItem
@ -78,12 +78,12 @@ const injectExtendedContextMenuItemIntoDom = function (menuItem) {
if (!Object.keys(contextMenuExtensions).length) {
// all menuItems appear at the bottom of the menu in their own container.
// if this is the first extension menu we need to add the separator.
document.getElementById('cmenu_canvas').appendChild(`<li class='separator'>`);
document.getElementById('cmenu_canvas').appendChild('<li class=\'separator\'>')
}
const shortcut = menuItem.shortcut || '';
const shortcut = menuItem.shortcut || ''
document.getElementById('cmenu_canvas').appendChild(`
<li class='disabled'><a href='#${menuItem.id}'>${menuItem.label}<span class='shortcut'>${shortcut}</span></a></li>`);
};
<li class='disabled'><a href='#${menuItem.id}'>${menuItem.label}<span class='shortcut'>${shortcut}</span></a></li>`)
}
/**
* @function module:contextmenu.injectExtendedContextMenuItemsIntoDom
@ -91,11 +91,11 @@ const injectExtendedContextMenuItemIntoDom = function (menuItem) {
*/
export const injectExtendedContextMenuItemsIntoDom = function () {
Object.values(contextMenuExtensions).forEach((menuItem) => {
injectExtendedContextMenuItemIntoDom(menuItem);
});
};
injectExtendedContextMenuItemIntoDom(menuItem)
})
}
/**
* @function module:contextmenu.resetCustomMenus
* @returns {void}
*/
export const resetCustomMenus = function () { contextMenuExtensions = {}; };
export const resetCustomMenus = function () { contextMenuExtensions = {} }

View File

@ -1,6 +1,6 @@
import PlainAlertDialog from 'elix/src/plain/PlainAlertDialog.js';
import { template } from 'elix/src/base/internal.js';
import { fragmentFrom } from 'elix/src/core/htmlLiterals.js';
import PlainAlertDialog from 'elix/src/plain/PlainAlertDialog.js'
import { template } from 'elix/src/base/internal.js'
import { fragmentFrom } from 'elix/src/core/htmlLiterals.js'
/**
* @class SePlainAlertDialog
@ -11,9 +11,9 @@ export default class SePlainAlertDialog extends PlainAlertDialog {
* @returns {PlainObject}
*/
get [template] () {
const result = super[template];
const result = super[template]
// Replace the default slot with a new default slot and a button container.
const defaultSlot = result.content.querySelector('#frameContent');
const defaultSlot = result.content.querySelector('#frameContent')
if (defaultSlot) {
defaultSlot.replaceWith(fragmentFrom.html`
<div id="alertDialogContent">
@ -22,7 +22,7 @@ export default class SePlainAlertDialog extends PlainAlertDialog {
</div>
<div id="choiceButtonContainer" part="choice-button-container"></div>
</div>
`);
`)
}
result.content.append(
fragmentFrom.html`
@ -60,9 +60,9 @@ export default class SePlainAlertDialog extends PlainAlertDialog {
}
</style>
`
);
return result;
)
return result
}
}
customElements.define('se-elix-alert-dialog', SePlainAlertDialog);
customElements.define('se-elix-alert-dialog', SePlainAlertDialog)

View File

@ -1,7 +1,6 @@
import cMenuDialogHTML from './cmenuDialog.html';
const template = document.createElement('template');
// eslint-disable-next-line no-unsanitized/property
template.innerHTML = cMenuDialogHTML;
import cMenuDialogHTML from './cmenuDialog.html'
const template = document.createElement('template')
template.innerHTML = cMenuDialogHTML
/**
* @class SeCMenuDialog
*/
@ -10,52 +9,55 @@ export class SeCMenuDialog extends HTMLElement {
* @function constructor
*/
constructor () {
super();
super()
// create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.append(template.content.cloneNode(true));
this._workarea = document.getElementById('workarea');
this.$dialog = this._shadowRoot.querySelector('#cmenu_canvas');
this.$copyLink = this._shadowRoot.querySelector('#se-copy');
this.$cutLink = this._shadowRoot.querySelector('#se-cut');
this.$pasteLink = this._shadowRoot.querySelector('#se-paste');
this.$pasteInPlaceLink = this._shadowRoot.querySelector('#se-paste-in-place');
this.$deleteLink = this._shadowRoot.querySelector('#se-delete');
this.$groupLink = this._shadowRoot.querySelector('#se-group');
this.$ungroupLink = this._shadowRoot.querySelector('#se-ungroup');
this.$moveFrontLink = this._shadowRoot.querySelector('#se-move-front');
this.$moveUpLink = this._shadowRoot.querySelector('#se-move-up');
this.$moveDownLink = this._shadowRoot.querySelector('#se-move-down');
this.$moveBackLink = this._shadowRoot.querySelector('#se-move-back');
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true))
this._workarea = document.getElementById('workarea')
this.$dialog = this._shadowRoot.querySelector('#cmenu_canvas')
this.$copyLink = this._shadowRoot.querySelector('#se-copy')
this.$cutLink = this._shadowRoot.querySelector('#se-cut')
this.$pasteLink = this._shadowRoot.querySelector('#se-paste')
this.$pasteInPlaceLink = this._shadowRoot.querySelector('#se-paste-in-place')
this.$deleteLink = this._shadowRoot.querySelector('#se-delete')
this.$groupLink = this._shadowRoot.querySelector('#se-group')
this.$ungroupLink = this._shadowRoot.querySelector('#se-ungroup')
this.$moveFrontLink = this._shadowRoot.querySelector('#se-move-front')
this.$moveUpLink = this._shadowRoot.querySelector('#se-move-up')
this.$moveDownLink = this._shadowRoot.querySelector('#se-move-down')
this.$moveBackLink = this._shadowRoot.querySelector('#se-move-back')
}
/**
* @function init
* @param {any} name
* @returns {void}
*/
init (i18next) {
this.setAttribute('tools-cut', i18next.t('tools.cut'));
this.setAttribute('tools-copy', i18next.t('tools.copy'));
this.setAttribute('tools-paste', i18next.t('tools.paste'));
this.setAttribute('tools-paste_in_place', i18next.t('tools.paste_in_place'));
this.setAttribute('tools-delete', i18next.t('tools.delete'));
this.setAttribute('tools-group', i18next.t('tools.group'));
this.setAttribute('tools-ungroup', i18next.t('tools.ungroup'));
this.setAttribute('tools-move_front', i18next.t('tools.move_front'));
this.setAttribute('tools-move_up', i18next.t('tools.move_up'));
this.setAttribute('tools-move_down', i18next.t('tools.move_down'));
this.setAttribute('tools-move_back', i18next.t('tools.move_back'));
this.setAttribute('tools-cut', i18next.t('tools.cut'))
this.setAttribute('tools-copy', i18next.t('tools.copy'))
this.setAttribute('tools-paste', i18next.t('tools.paste'))
this.setAttribute('tools-paste_in_place', i18next.t('tools.paste_in_place'))
this.setAttribute('tools-delete', i18next.t('tools.delete'))
this.setAttribute('tools-group', i18next.t('tools.group'))
this.setAttribute('tools-ungroup', i18next.t('tools.ungroup'))
this.setAttribute('tools-move_front', i18next.t('tools.move_front'))
this.setAttribute('tools-move_up', i18next.t('tools.move_up'))
this.setAttribute('tools-move_down', i18next.t('tools.move_down'))
this.setAttribute('tools-move_back', i18next.t('tools.move_back'))
}
/**
* @function observedAttributes
* @returns {any} observed
*/
static get observedAttributes () {
return [ 'disableallmenu', 'enablemenuitems', 'disablemenuitems', 'tools-cut',
return ['disableallmenu', 'enablemenuitems', 'disablemenuitems', 'tools-cut',
'tools-copy', 'tools-paste', 'tools-paste_in_place', 'tools-delete', 'tools-group',
'tools-ungroup', 'tools-move_front', 'tools-move_up', 'tools-move_down',
'tools-move_back' ];
'tools-move_back']
}
/**
* @function attributeChangedCallback
* @param {string} name
@ -64,85 +66,86 @@ export class SeCMenuDialog extends HTMLElement {
* @returns {void}
*/
attributeChangedCallback (name, oldValue, newValue) {
let eles = [];
let textnode;
const sdowRoot = this._shadowRoot;
let eles = []
let textnode
const sdowRoot = this._shadowRoot
switch (name) {
case 'disableallmenu':
if (newValue === 'true') {
const elesli = sdowRoot.querySelectorAll('li');
elesli.forEach(function (eleli) {
eleli.classList.add('disabled');
});
}
break;
case 'enablemenuitems':
eles = newValue.split(',');
eles.forEach(function (ele) {
const selEle = sdowRoot.querySelector('a[href*="' + ele + '"]');
selEle.parentElement.classList.remove('disabled');
});
break;
case 'disablemenuitems':
eles = newValue.split(',');
eles.forEach(function (ele) {
const selEle = sdowRoot.querySelector('a[href*="' + ele + '"]');
selEle.parentElement.classList.add('disabled');
});
break;
case 'tools-cut':
textnode = document.createTextNode(newValue);
this.$cutLink.prepend(textnode);
break;
case 'tools-copy':
textnode = document.createTextNode(newValue);
this.$copyLink.prepend(textnode);
break;
case 'tools-paste':
this.$pasteLink.textContent = newValue;
break;
case 'tools-paste_in_place':
this.$pasteInPlaceLink.textContent = newValue;
break;
case 'tools-delete':
textnode = document.createTextNode(newValue);
this.$deleteLink.prepend(textnode);
break;
case 'tools-group':
textnode = document.createTextNode(newValue);
this.$groupLink.prepend(textnode);
break;
case 'tools-ungroup':
textnode = document.createTextNode(newValue);
this.$ungroupLink.prepend(textnode);
break;
case 'tools-move_front':
textnode = document.createTextNode(newValue);
this.$moveFrontLink.prepend(textnode);
break;
case 'tools-move_up':
textnode = document.createTextNode(newValue);
this.$moveUpLink.prepend(textnode);
break;
case 'tools-move_down':
textnode = document.createTextNode(newValue);
this.$moveDownLink.prepend(textnode);
break;
case 'tools-move_back':
textnode = document.createTextNode(newValue);
this.$moveBackLink.prepend(textnode);
break;
default:
case 'disableallmenu':
if (newValue === 'true') {
const elesli = sdowRoot.querySelectorAll('li')
elesli.forEach(function (eleli) {
eleli.classList.add('disabled')
})
}
break
case 'enablemenuitems':
eles = newValue.split(',')
eles.forEach(function (ele) {
const selEle = sdowRoot.querySelector('a[href*="' + ele + '"]')
selEle.parentElement.classList.remove('disabled')
})
break
case 'disablemenuitems':
eles = newValue.split(',')
eles.forEach(function (ele) {
const selEle = sdowRoot.querySelector('a[href*="' + ele + '"]')
selEle.parentElement.classList.add('disabled')
})
break
case 'tools-cut':
textnode = document.createTextNode(newValue)
this.$cutLink.prepend(textnode)
break
case 'tools-copy':
textnode = document.createTextNode(newValue)
this.$copyLink.prepend(textnode)
break
case 'tools-paste':
this.$pasteLink.textContent = newValue
break
case 'tools-paste_in_place':
this.$pasteInPlaceLink.textContent = newValue
break
case 'tools-delete':
textnode = document.createTextNode(newValue)
this.$deleteLink.prepend(textnode)
break
case 'tools-group':
textnode = document.createTextNode(newValue)
this.$groupLink.prepend(textnode)
break
case 'tools-ungroup':
textnode = document.createTextNode(newValue)
this.$ungroupLink.prepend(textnode)
break
case 'tools-move_front':
textnode = document.createTextNode(newValue)
this.$moveFrontLink.prepend(textnode)
break
case 'tools-move_up':
textnode = document.createTextNode(newValue)
this.$moveUpLink.prepend(textnode)
break
case 'tools-move_down':
textnode = document.createTextNode(newValue)
this.$moveDownLink.prepend(textnode)
break
case 'tools-move_back':
textnode = document.createTextNode(newValue)
this.$moveBackLink.prepend(textnode)
break
default:
// super.attributeChangedCallback(name, oldValue, newValue);
break;
break
}
}
/**
* @function get
* @returns {any}
*/
get disableallmenu () {
return this.getAttribute('disableallmenu');
return this.getAttribute('disableallmenu')
}
/**
@ -150,14 +153,15 @@ export class SeCMenuDialog extends HTMLElement {
* @returns {void}
*/
set disableallmenu (value) {
this.setAttribute('disableallmenu', value);
this.setAttribute('disableallmenu', value)
}
/**
* @function get
* @returns {any}
*/
get enablemenuitems () {
return this.getAttribute('enablemenuitems');
return this.getAttribute('enablemenuitems')
}
/**
@ -165,14 +169,15 @@ export class SeCMenuDialog extends HTMLElement {
* @returns {void}
*/
set enablemenuitems (value) {
this.setAttribute('enablemenuitems', value);
this.setAttribute('enablemenuitems', value)
}
/**
* @function get
* @returns {any}
*/
get disablemenuitems () {
return this.getAttribute('disablemenuitems');
return this.getAttribute('disablemenuitems')
}
/**
@ -180,59 +185,62 @@ export class SeCMenuDialog extends HTMLElement {
* @returns {void}
*/
set disablemenuitems (value) {
this.setAttribute('disablemenuitems', value);
this.setAttribute('disablemenuitems', value)
}
/**
* @function connectedCallback
* @returns {void}
*/
connectedCallback () {
const current = this;
const current = this
const onMenuOpenHandler = (e) => {
e.preventDefault();
e.preventDefault()
// Detect mouse position
let x = e.pageX;
let y = e.pageY;
let x = e.pageX
let y = e.pageY
const xOff = screen.width - 250; // menu width
const yOff = screen.height - (276 + 150); // menu height + bottom panel height and scroll bar
const xOff = screen.width - 250 // menu width
const yOff = screen.height - (276 + 150) // menu height + bottom panel height and scroll bar
if (x > xOff) {
x = xOff;
x = xOff
}
if (y > yOff) {
y = yOff;
y = yOff
}
current.$dialog.style.top = y + 'px';
current.$dialog.style.left = x + 'px';
current.$dialog.style.display = 'block';
};
current.$dialog.style.top = y + 'px'
current.$dialog.style.left = x + 'px'
current.$dialog.style.display = 'block'
}
const onMenuCloseHandler = (e) => {
if (e.button !== 2) {
current.$dialog.style.display = 'none';
current.$dialog.style.display = 'none'
}
};
}
const onMenuClickHandler = (e, action) => {
const triggerEvent = new CustomEvent('change', { detail: {
trigger: action
} });
this.dispatchEvent(triggerEvent);
};
this._workarea.addEventListener('contextmenu', onMenuOpenHandler);
this._workarea.addEventListener('mousedown', onMenuCloseHandler);
this.$cutLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'cut'));
this.$copyLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'copy'));
this.$pasteLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'paste'));
this.$pasteInPlaceLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'paste_in_place'));
this.$deleteLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'delete'));
this.$groupLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'group'));
this.$ungroupLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'ungroup'));
this.$moveFrontLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_front'));
this.$moveUpLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_up'));
this.$moveDownLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_down'));
this.$moveBackLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_back'));
const triggerEvent = new CustomEvent('change', {
detail: {
trigger: action
}
})
this.dispatchEvent(triggerEvent)
}
this._workarea.addEventListener('contextmenu', onMenuOpenHandler)
this._workarea.addEventListener('mousedown', onMenuCloseHandler)
this.$cutLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'cut'))
this.$copyLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'copy'))
this.$pasteLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'paste'))
this.$pasteInPlaceLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'paste_in_place'))
this.$deleteLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'delete'))
this.$groupLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'group'))
this.$ungroupLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'ungroup'))
this.$moveFrontLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_front'))
this.$moveUpLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_up'))
this.$moveDownLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_down'))
this.$moveBackLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_back'))
}
}
// Register
customElements.define('se-cmenu_canvas-dialog', SeCMenuDialog);
customElements.define('se-cmenu_canvas-dialog', SeCMenuDialog)

Some files were not shown because too many files have changed in this diff Show More