From 7c470e99099e391c553f250f61fc7aade7a6b28f Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 7 Nov 2018 14:51:50 +0800 Subject: [PATCH] - Linting (ESLint): Stricter rules (or switch to warning) - Breaking internal API change: `updateGripCursor` moved to be class method of Selector rather than instance method - Breaking internal API change: `subpathIsClosed` moved to be class method of `Path` rather than instance method - Refactoring: Reuse utilities base64 encoder for SVG icons plugin - Docs (JSDoc): Fix return of the `mouseUp` (can also be an object) and `mouseDown` (may also be a boolean) of `pathActions`; other JSDoc additions/improvements --- .eslintrc | 314 ++++++++- editor/browser.js | 8 +- editor/canvg/canvg.js | 257 +++---- editor/canvg/rgbcolor.js | 9 +- editor/contextmenu.js | 6 +- editor/contextmenu/jQuery.contextMenu.js | 11 +- editor/coords.js | 16 +- editor/draw.js | 8 +- editor/embedapi.js | 40 +- editor/extensions/ext-connector.js | 20 +- editor/extensions/ext-overview_window.js | 2 +- editor/extensions/ext-webappfind.js | 25 +- editor/extensions/imagelib/openclipart.js | 17 +- .../dynamic-import-polyfill/importModule.js | 44 +- editor/history.js | 10 +- editor/historyrecording.js | 7 +- editor/jQuery.attr.js | 6 +- editor/jgraduate/jQuery.jGraduate.js | 5 +- editor/jgraduate/jQuery.jPicker.js | 667 ++++++++++-------- editor/jspdf/jspdf.plugin.svgToPdf.js | 38 +- editor/layer.js | 6 +- editor/locale/lang.af.js | 2 +- editor/locale/lang.ar.js | 2 +- editor/locale/lang.az.js | 2 +- editor/locale/lang.be.js | 2 +- editor/locale/lang.bg.js | 2 +- editor/locale/lang.ca.js | 2 +- editor/locale/lang.cs.js | 2 +- editor/locale/lang.cy.js | 2 +- editor/locale/lang.da.js | 2 +- editor/locale/lang.de.js | 2 +- editor/locale/lang.el.js | 2 +- editor/locale/lang.en.js | 2 +- editor/locale/lang.es.js | 2 +- editor/locale/lang.et.js | 2 +- editor/locale/lang.fa.js | 2 +- editor/locale/lang.fi.js | 2 +- editor/locale/lang.fr.js | 2 +- editor/locale/lang.fy.js | 2 +- editor/locale/lang.ga.js | 2 +- editor/locale/lang.gl.js | 2 +- editor/locale/lang.he.js | 2 +- editor/locale/lang.hi.js | 2 +- editor/locale/lang.hr.js | 2 +- editor/locale/lang.hu.js | 2 +- editor/locale/lang.hy.js | 2 +- editor/locale/lang.id.js | 2 +- editor/locale/lang.is.js | 2 +- editor/locale/lang.it.js | 2 +- editor/locale/lang.ja.js | 2 +- editor/locale/lang.ko.js | 2 +- editor/locale/lang.lt.js | 2 +- editor/locale/lang.lv.js | 2 +- editor/locale/lang.mk.js | 2 +- editor/locale/lang.ms.js | 2 +- editor/locale/lang.mt.js | 2 +- editor/locale/lang.nl.js | 2 +- editor/locale/lang.no.js | 2 +- editor/locale/lang.pl.js | 2 +- editor/locale/lang.pt-BR.js | 2 +- editor/locale/lang.pt-PT.js | 2 +- editor/locale/lang.ro.js | 2 +- editor/locale/lang.ru.js | 2 +- editor/locale/lang.sk.js | 2 +- editor/locale/lang.sl.js | 2 +- editor/locale/lang.sq.js | 2 +- editor/locale/lang.sr.js | 2 +- editor/locale/lang.sv.js | 2 +- editor/locale/lang.sw.js | 2 +- editor/locale/lang.test.js | 2 +- editor/locale/lang.th.js | 2 +- editor/locale/lang.tl.js | 2 +- editor/locale/lang.tr.js | 2 +- editor/locale/lang.uk.js | 2 +- editor/locale/lang.vi.js | 2 +- editor/locale/lang.yi.js | 2 +- editor/locale/lang.zh-CN.js | 2 +- editor/locale/lang.zh-HK.js | 2 +- editor/locale/lang.zh-TW.js | 2 +- editor/locale/locale.js | 2 +- editor/math.js | 7 +- editor/path.js | 172 +++-- editor/recalculate.js | 19 +- editor/redirect-on-lacking-support.js | 1 + editor/sanitize.js | 5 +- editor/select.js | 60 +- editor/spinbtn/jQuery.SpinButton.js | 2 +- editor/svg-editor.js | 270 +++---- editor/svgcanvas.js | 235 +++--- editor/svgicons/jQuery.svgIcons.js | 105 +-- editor/svgpathseg.js | 10 +- editor/svgtransformlist.js | 29 +- editor/touch.js | 7 +- editor/typedefs.js | 9 + editor/units.js | 8 +- editor/utilities.js | 161 +++-- editor/xdomain-svgedit-config-es.js | 1 + firefox-extension/content/svg-edit-overlay.js | 4 + firefox-extension/handlers.js | 17 +- jsdoc-check-overly-generic-types.js | 27 +- opera-widget/handlers.js | 8 +- package-lock.json | 5 +- package.json | 6 +- rollup-config.config.js | 1 + rollup.config.js | 50 +- screencasts/svgopen2010/script.js | 72 +- test/contextmenu_test.js | 10 +- test/coords_test.js | 11 +- test/draw_test.js | 58 +- test/history_test.js | 116 +-- test/jQuery.attr_test.js | 4 + test/math_test.js | 6 +- test/qunit/qunit-assert-almostEquals.js | 11 + test/qunit/qunit-assert-close.js | 6 +- ...qunit-assert-expectOutOfBoundsException.js | 13 +- test/recalculate_test.js | 21 +- test/select_test.js | 26 +- test/sinon/sinon-qunit.js | 18 +- test/svgtransformlist_test.js | 20 +- test/test1.js | 18 +- test/ui-tests/accessibility.js | 4 +- test/ui-tests/ui.js | 4 +- test/units_test.js | 10 +- test/utilities_bbox_test.js | 52 +- test/utilities_performance_test.js | 60 +- test/utilities_test.js | 61 +- 126 files changed, 2081 insertions(+), 1373 deletions(-) diff --git a/.eslintrc b/.eslintrc index df9d7d57..fd6d4a88 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,7 +6,7 @@ "parserOptions": { "sourceType": "module" }, - "plugins": ["compat", "qunit", "testcafe", "jsdoc", "markdown"], + "plugins": ["compat", "qunit", "testcafe", "jsdoc", "markdown", "import", "node", "promise"], "env": { "node": false, "browser": true @@ -26,24 +26,296 @@ "allowAugmentsExtendsWithoutParam": true } }, - "overrides": [{ - "files": ["**/*.md"], - "rules": { - "no-undef": ["off"], - "no-unused-vars": ["warn"], - "padded-blocks": ["off"] + "overrides": [ + { + "files": ["editor/locale/lang.*.js"], + "rules": { + "import/no-anonymous-default-export": ["off"] + } + }, + { + "files": ["editor/extensions/ext-locale/**"], + "rules": { + "import/no-anonymous-default-export": ["off"] + } + }, + { + "files": ["editor/extensions/**/ext-*.js"], + "rules": { + "consistent-this": ["error", "svgEditor"], + "import/no-anonymous-default-export": ["off"] + } + }, + { + "files": [ + "editor/svgpathseg.js", "editor/touch.js", "editor/typedefs.js", + "editor/redirect-on-no-module-support.js", + "test/all_tests.js", "screencasts/svgopen2010/script.js", + "opera-widget/handlers.js", + "firefox-extension/handlers.js", + "firefox-extension/content/svg-edit-overlay.js" + ], + "rules": { + "import/unambiguous": ["off"] + } + }, + { + "files": ["**/*.md"], + "rules": { + "no-undef": ["off"], + "no-unused-vars": ["warn"], + "padded-blocks": ["off"], + "import/unambiguous": ["off"] + } + }, + { + "files": ["test/**"], + "rules": { + "no-console": ["off"] + } + }, + { + "files": [ + "docs/jsdoc-config.js", "build-html.js", "jsdoc-check-overly-generic-types.js", + "rollup.config.js", "rollup-config.config.js" + ], + "rules": { + "node/no-extraneous-import": ["error"], + "node/no-extraneous-require": ["error"], + "node/no-missing-import": ["error"], + "node/no-missing-require": ["error"], + "node/no-unpublished-bin": ["error"], + "node/no-unpublished-import": ["error"], + "node/no-unpublished-require": ["error"], + "node/no-unsupported-features/es-builtins": ["error"], + "node/no-unsupported-features/es-syntax": ["error"], + "node/no-unsupported-features/node-builtins": ["error"], + "node/process-exit-as-throw": ["error"], + "node/shebang": ["error"], + + "node/exports-style": ["error", "module.exports"], + "node/prefer-global/buffer": ["error", "always"], + "node/prefer-global/console": ["error", "always"], + "node/prefer-global/process": ["error", "always"], + "node/prefer-global/text-decoder": ["error", "always"], + "node/prefer-global/text-encoder": ["error", "always"], + "node/prefer-global/url-search-params": ["error", "always"], + "node/prefer-global/url": ["error", "always"] + } + }, + { + "files": ["rollup.config.js", "rollup-config.config.js"], + "rules": { + "node/no-unsupported-features/es-syntax": "off", + "node/no-unpublished-import": "off" + } + }, + { + "files": ["jsdoc-check-overly-generic-types.js", "build-html.js"], + "rules": { + "import/unambiguous": "off", + "import/no-commonjs": "off" + } } - }], + ], "rules": { - "semi": [2, "always"], + "array-bracket-newline": ["error", "consistent"], + "array-bracket-spacing": ["error"], + "array-callback-return": ["error"], + "array-element-newline": ["off"], + "arrow-body-style": ["off"], + "arrow-parens": ["error"], + "block-scoped-var": ["error"], + "callback-return": ["error"], + "class-methods-use-this": ["warn"], + "computed-property-spacing": ["error"], + "consistent-return": ["error"], + "consistent-this": ["warn"], + "dot-notation": ["error"], + "for-direction": ["error"], + "func-name-matching": ["error"], + "func-names": ["off"], + "func-style": ["off"], + "function-paren-newline": ["error", "consistent"], + "getter-return": ["error"], + "global-require": ["error"], + "guard-for-in": ["error"], + "id-blacklist": ["off"], + "id-length": ["off"], + "id-match": ["off"], + "implicit-arrow-linebreak": ["error"], + "init-declarations": ["off"], + "jsx-quotes": ["error"], + "line-comment-position": ["off"], + "linebreak-style": ["error"], + "lines-around-comment": ["off"], + "lines-between-class-members": ["off"], + "max-classes-per-file": ["off"], + "max-depth": ["off"], + "max-lines-per-function": ["off"], + "max-lines": ["off"], + "max-nested-callbacks": ["error"], + "max-params": ["off"], + "max-statements-per-line": ["off"], + "max-statements": ["off"], + "multiline-comment-style": ["off"], + "multiline-ternary": ["error", "always-multiline"], + "newline-after-var": ["off"], + "newline-before-return": ["off"], + "newline-per-chained-call": ["off"], + "no-alert": ["warn"], + "no-async-promise-executor": ["error"], + "no-await-in-loop": ["error"], + "no-bitwise": ["error"], + "no-buffer-constructor": ["error"], + "no-case-declarations": ["error"], + "no-confusing-arrow": ["error"], + "no-console": ["warn"], + "no-continue": ["off"], + "no-div-regex": ["error"], + "no-duplicate-imports": ["error"], + "no-else-return": ["error"], + "no-empty-function": ["warn"], + "no-empty": ["error", {"allowEmptyCatch": true}], + "no-eq-null": ["error"], + "no-extra-label": ["error"], + "no-extra-semi": ["error"], + "no-implicit-coercion": ["error"], + "no-implicit-globals": ["error"], + "no-inline-comments": ["off"], + "no-invalid-this": ["off"], + "no-lonely-if": ["error"], + "no-loop-func": ["error"], + "no-misleading-character-class": ["error"], + "no-mixed-requires": ["error", {"grouping": true, "allowCall": true}], + "no-multi-assign": ["off"], + "no-negated-condition": ["off"], + "no-nested-ternary": ["off"], + "no-param-reassign": ["off"], + "no-plusplus": ["off"], + "no-process-env": ["error"], + "no-process-exit": ["error"], + "no-prototype-builtins": ["error"], + "no-restricted-globals": ["error", { + "name": "event", + "message": "Use local event parameter instead (preferably as \"e\" or \"ev\")." + }, { + "name": "fdescribe", + "message": "Do not commit fdescribe. Use describe instead." + }], + "no-restricted-imports": ["off"], + "no-restricted-modules": ["off"], + "no-restricted-properties": ["error", { + "property": "__defineGetter__", + "message": "Please use `Object.defineProperty` instead." + }], + "no-restricted-syntax": ["off"], + "no-script-url": ["error"], + "no-shadow": ["error", {"builtinGlobals": true, "hoist": "functions", "allow": ["parent", "top", "open", "name", "closed", "start"]}], + "no-spaced-func": ["error"], + "no-sync": ["error"], + "no-ternary": ["off"], + "no-undefined": ["off"], + "no-underscore-dangle": ["off"], + "no-unused-labels": ["error"], + "no-useless-concat": ["off"], + "no-var": ["error"], + "no-void": ["error"], + "nonblock-statement-body-position": ["error"], + "object-curly-newline": ["off"], + "object-shorthand": ["error", "always", {"avoidExplicitReturnArrows": true}], + "one-var-declaration-per-line": ["off"], + "operator-assignment": ["error"], + "padding-line-between-statements": ["off"], + "prefer-arrow-callback": ["off"], + "prefer-const": ["error"], + "prefer-destructuring": ["error", {"object": true}], + "prefer-numeric-literals": ["warn"], + "prefer-object-spread": ["error"], + "prefer-rest-params": ["error"], + "prefer-spread": ["error"], + "prefer-template": ["off"], + "quote-props": ["error", "as-needed"], + "radix": ["error", "as-needed"], + "require-atomic-updates": ["error"], + "require-await": ["error"], + "require-jsdoc": ["warn"], + "require-yield": ["error"], + "semi-style": ["error"], + "sort-imports": ["off"], + "sort-keys": ["off"], + "sort-vars": ["off"], + "strict": ["error"], + "switch-colon-spacing": ["error"], + "vars-on-top": ["warn"], + "wrap-regex": ["error"], + + "semi": ["error", "always"], "indent": ["error", 2, {"outerIIFEBody": 0}], - "object-property-newline": 0, - "one-var": 0, - "no-var": 2, - "prefer-const": 2, - "no-extra-semi": 2, - "quote-props": [2, "as-needed"], + "object-property-newline": ["off"], + "one-var": ["off"], "object-curly-spacing": ["error", "never"], + + "promise/catch-or-return": "error", + "promise/no-return-wrap": "error", + "promise/param-names": "error", + "promise/always-return": "error", + "promise/no-native": "off", + "promise/no-nesting": "warn", + "promise/no-promise-in-callback": "warn", + "promise/no-callback-in-promise": "warn", + "promise/avoid-new": "warn", + "promise/no-new-statics": "error", + "promise/no-return-in-finally": "warn", + "promise/valid-params": "warn", + "promise/prefer-await-to-then": "error", + "promise/prefer-await-to-callbacks": "warn", + + "import/no-unresolved": "error", + "import/named": "error", + "import/default": "error", + "import/namespace": "error", + "import/no-restricted-paths": "off", + "import/no-absolute-path": "error", + "import/no-dynamic-require": "error", + "import/no-internal-modules": "off", + "import/no-webpack-loader-syntax": "error", + "import/no-self-import": "error", + "import/no-cycle": "off", + "import/no-useless-path-segments": "error", + "import/no-relative-parent-imports": "off", + "import/export": "error", + "import/no-named-as-default": "error", + "import/no-named-as-default-member": "error", + "import/no-deprecated": "error", + "import/no-extraneous-dependencies": "error", + "import/no-mutable-exports": "error", + "import/unambiguous": "warn", + "import/no-commonjs": "error", + "import/no-amd": "error", + "import/no-nodejs-modules": "off", + "import/first": "error", + "import/exports-last": "off", + "import/no-duplicates": "error", + "import/no-namespace": "off", + "import/extensions": ["error", "always", {"ignorePackages": true}], + "import/order": ["error", {"groups": [ + "builtin", + "external", + "internal", + ["parent", "sibling", "index"] + ]}], + "import/newline-after-import": "error", + "import/prefer-default-export": "off", + "import/max-dependencies": "off", + "import/no-unassigned-import": "off", + "import/no-named-default": "error", + "import/no-default-export": "off", + "import/no-named-export": "off", + "import/no-anonymous-default-export": "error", + "import/group-exports": "off", + "import/dynamic-import-chunkname": "off", + "jsdoc/check-param-names": 1, "jsdoc/check-tag-names": 1, "jsdoc/check-types": 1, @@ -61,7 +333,7 @@ "jsdoc/no-undefined-types": ["off"], "jsdoc/valid-types": ["error"], - "valid-jsdoc": ["off", { + "valid-jsdoc": ["error", { "prefer": { "arg": "param", "argument": "param", @@ -81,6 +353,16 @@ "matchDescription": "^([A-Z][\\s\\S]*[.`?!])?$", "requireParamDescription": false, "requireReturnDescription": false + }], + "no-warning-comments": ["off"], + "default-case": ["off"], + "complexity": ["off"], + "require-unicode-regexp": ["off"], + "capitalized-comments": ["off"], + "no-magic-numbers": ["off"], + "max-len": ["off", { + "ignoreUrls": true, + "ignoreRegExpLiterals": true }] } } diff --git a/editor/browser.js b/editor/browser.js index b21e1bd5..747da58b 100644 --- a/editor/browser.js +++ b/editor/browser.js @@ -16,7 +16,7 @@ import {NS} from './namespaces.js'; const $ = jQuery; const supportsSVG_ = (function () { -return !!document.createElementNS && !!document.createElementNS(NS.SVG, 'svg').createSVGRect; +return Boolean(document.createElementNS && document.createElementNS(NS.SVG, 'svg').createSVGRect); }()); /** @@ -29,7 +29,7 @@ const {userAgent} = navigator; const svg = document.createElementNS(NS.SVG, 'svg'); // Note: Browser sniffing should only be used if no other detection method is possible -const isOpera_ = !!window.opera; +const isOpera_ = Boolean(window.opera); const isWebkit_ = userAgent.includes('AppleWebKit'); const isGecko_ = userAgent.includes('Gecko/'); const isIE_ = userAgent.includes('MSIE'); @@ -39,11 +39,11 @@ const isMac_ = userAgent.includes('Macintosh'); const isTouch_ = 'ontouchstart' in window; const supportsSelectors_ = (function () { -return !!svg.querySelector; +return Boolean(svg.querySelector); }()); const supportsXpath_ = (function () { -return !!document.evaluate; +return Boolean(document.evaluate); }()); // segList functions (for FF1.5 and 2.0) diff --git a/editor/canvg/canvg.js b/editor/canvg/canvg.js index f5a09937..5bf8039b 100644 --- a/editor/canvg/canvg.js +++ b/editor/canvg/canvg.js @@ -11,6 +11,15 @@ import RGBColor from './rgbcolor.js'; import {canvasRGBA} from '../external/stackblur-canvas/dist/stackblur-es.js'; +/** + * Whether a value is `null` or `undefined`. + * @param {Any} val + * @returns {boolean} + */ +const isNullish = (val) => { + return val === null || val === undefined; +}; + let canvasRGBA_ = canvasRGBA; /** @@ -63,7 +72,7 @@ export const setStackBlurCanvasRGBA = (cb) => { */ export const canvg = function (target, s, opts) { // no parameters - if (target == null && s == null && opts == null) { + if (isNullish(target) && isNullish(s) && isNullish(opts)) { const svgTags = document.querySelectorAll('svg'); return Promise.all([...svgTags].map((svgTag) => { const c = document.createElement('canvas'); @@ -82,7 +91,7 @@ export const canvg = function (target, s, opts) { } // store class on canvas - if (target.svg != null) target.svg.stop(); + if (!isNullish(target.svg)) target.svg.stop(); const svg = build(opts || {}); // on i.e. 8 for flash canvas, we can't assign the property so check for it if (!(target.childNodes.length === 1 && target.childNodes[0].nodeName === 'OBJECT')) { @@ -113,9 +122,9 @@ function build (opts) { svg.FRAMERATE = 30; svg.MAX_VIRTUAL_PIXELS = 30000; - svg.log = function (msg) {}; + svg.log = function (msg) { /* */ }; if (svg.opts.log === true && typeof console !== 'undefined') { - svg.log = function (msg) { console.log(msg); }; + svg.log = function (msg) { console.log(msg); }; // eslint-disable-line no-console } // globals @@ -139,7 +148,7 @@ function build (opts) { width () { return this.Current().width; }, height () { return this.Current().height; }, ComputeSize (d) { - if (d != null && typeof d === 'number') return d; + if (!isNullish(d) && typeof d === 'number') return d; if (d === 'x') return this.width(); if (d === 'y') return this.height(); return Math.sqrt( @@ -190,13 +199,12 @@ function build (opts) { if (window.DOMParser) { const parser = new DOMParser(); return parser.parseFromString(xml, 'text/xml'); - } else { - xml = xml.replace(/]*>/, ''); - const xmlDoc = new window.ActiveXObject('Microsoft.XMLDOM'); - xmlDoc.async = 'false'; - xmlDoc.loadXML(xml); - return xmlDoc; } + xml = xml.replace(/]*>/, ''); + const xmlDoc = new window.ActiveXObject('Microsoft.XMLDOM'); + xmlDoc.async = 'false'; + xmlDoc.loadXML(xml); + return xmlDoc; }; // text extensions @@ -226,7 +234,7 @@ function build (opts) { } hasValue () { - return (this.value != null && this.value !== ''); + return (!isNullish(this.value) && this.value !== ''); } // return the numerical value of the property @@ -234,8 +242,8 @@ function build (opts) { if (!this.hasValue()) return 0; let n = parseFloat(this.value); - if ((this.value + '').match(/%$/)) { - n = n / 100.0; + if (String(this.value).match(/%$/)) { + n /= 100.0; } return n; } @@ -254,7 +262,7 @@ function build (opts) { // augment the current color value with the opacity addOpacity (opacityProp) { let newValue = this.value; - if (opacityProp.value != null && opacityProp.value !== '' && typeof this.value === 'string') { // can only add opacity to colors, not patterns + if (!isNullish(opacityProp.value) && opacityProp.value !== '' && typeof this.value === 'string') { // can only add opacity to colors, not patterns const color = new RGBColor(this.value); if (color.ok) { newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacityProp.numValue() + ')'; @@ -280,12 +288,12 @@ function build (opts) { let def = this.getDefinition(); // gradient - if (def != null && def.createGradient) { + if (!isNullish(def) && def.createGradient) { return def.createGradient(svg.ctx, e, opacityProp); } // pattern - if (def != null && def.createPattern) { + if (!isNullish(def) && def.createPattern) { if (def.getHrefAttribute().hasValue()) { const pt = def.attribute('patternTransform'); def = def.getHrefAttribute().getDefinition(); @@ -312,14 +320,13 @@ function build (opts) { } getUnits () { - const s = this.value + ''; - return s.replace(/[0-9.-]/g, ''); + return String(this.value).replace(/[0-9.-]/g, ''); } // get the length as pixels toPixels (viewPort, processPercent) { if (!this.hasValue()) return 0; - const s = this.value + ''; + const s = String(this.value); if (s.match(/em$/)) return this.numValue() * this.getEM(viewPort); if (s.match(/ex$/)) return this.numValue() * this.getEM(viewPort) / 2.0; if (s.match(/px$/)) return this.numValue(); @@ -338,7 +345,7 @@ function build (opts) { // get the time as milliseconds toMilliseconds () { if (!this.hasValue()) return 0; - const s = this.value + ''; + const s = String(this.value); if (s.match(/s$/)) return this.numValue() * 1000; if (s.match(/ms$/)) return this.numValue(); return this.numValue(); @@ -348,7 +355,7 @@ function build (opts) { // get the angle as radians toRadians () { if (!this.hasValue()) return 0; - const s = this.value + ''; + const s = String(this.value); if (s.match(/deg$/)) return this.numValue() * (Math.PI / 180.0); if (s.match(/grad$/)) return this.numValue() * (Math.PI / 200.0); if (s.match(/rad$/)) return this.numValue(); @@ -368,7 +375,7 @@ function build (opts) { Weights: 'normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900|inherit', CreateFont (fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) { - const f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font); + const f = !isNullish(inherit) ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font); return { fontFamily: fontFamily || f.fontFamily, fontSize: fontSize || f.fontSize, @@ -474,7 +481,7 @@ function build (opts) { height () { return this.y2 - this.y1; } addPoint (x, y) { - if (x != null) { + if (!isNullish(x)) { if (isNaN(this.x1) || isNaN(this.x2)) { this.x1 = x; this.x2 = x; @@ -483,7 +490,7 @@ function build (opts) { if (x > this.x2) this.x2 = x; } - if (y != null) { + if (!isNullish(y)) { if (isNaN(this.y1) || isNaN(this.y2)) { this.y1 = y; this.y2 = y; @@ -710,7 +717,7 @@ function build (opts) { else if (meetOrSlice === 'slice') ctx.scale(scaleMax, scaleMax); // translate - ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY); + ctx.translate(isNullish(minX) ? 0 : -minX, isNullish(minY) ? 0 : -minY); }; // elements @@ -724,7 +731,7 @@ function build (opts) { this.attributes = {}; this.styles = {}; this.children = []; - if (node != null && node.nodeType === 1) { // ELEMENT_NODE + if (!isNullish(node) && node.nodeType === 1) { // ELEMENT_NODE // add children [...node.childNodes].forEach((childNode) => { if (childNode.nodeType === 1) { @@ -749,7 +756,7 @@ function build (opts) { }); // add tag styles let styles = svg.Styles[node.nodeName]; - if (styles != null) { + if (!isNullish(styles)) { for (const name in styles) { this.styles[name] = styles[name]; } @@ -760,13 +767,13 @@ function build (opts) { const classes = svg.compressSpaces(this.attribute('class').value).split(' '); classes.forEach((clss) => { styles = svg.Styles['.' + clss]; - if (styles != null) { + if (!isNullish(styles)) { for (const name in styles) { this.styles[name] = styles[name]; } } styles = svg.Styles[node.nodeName + '.' + clss]; - if (styles != null) { + if (!isNullish(styles)) { for (const name in styles) { this.styles[name] = styles[name]; } @@ -777,7 +784,7 @@ function build (opts) { // add id styles if (this.attribute('id').hasValue()) { const styles = svg.Styles['#' + this.attribute('id').value]; - if (styles != null) { + if (!isNullish(styles)) { for (const name in styles) { this.styles[name] = styles[name]; } @@ -799,7 +806,7 @@ function build (opts) { // add id if (this.attribute('id').hasValue()) { - if (svg.Definitions[this.attribute('id').value] == null) { + if (isNullish(svg.Definitions[this.attribute('id').value])) { svg.Definitions[this.attribute('id').value] = this; } } @@ -809,7 +816,7 @@ function build (opts) { // get or create attribute attribute (name, createIfNotExists) { let a = this.attributes[name]; - if (a != null) return a; + if (!isNullish(a)) return a; if (createIfNotExists === true) { a = new svg.Property(name, ''); this.attributes[name] = a; } return a || svg.EmptyProperty; @@ -827,19 +834,19 @@ function build (opts) { // get or create style, crawls up node tree style (name, createIfNotExists, skipAncestors) { let s = this.styles[name]; - if (s != null) return s; + if (!isNullish(s)) return s; const a = this.attribute(name); - if (a != null && a.hasValue()) { + if (!isNullish(a) && a.hasValue()) { this.styles[name] = a; // move up to me to cache return a; } if (skipAncestors !== true) { const p = this.parent; - if (p != null) { + if (!isNullish(p)) { const ps = p.style(name); - if (ps != null && ps.hasValue()) { + if (!isNullish(ps) && ps.hasValue()) { return ps; } } @@ -860,10 +867,10 @@ function build (opts) { ctx.save(); if (this.attribute('mask').hasValue()) { // mask const mask = this.attribute('mask').getDefinition(); - if (mask != null) mask.apply(ctx, this); + if (!isNullish(mask)) mask.apply(ctx, this); } else if (this.style('filter').hasValue()) { // filter const filter = this.style('filter').getDefinition(); - if (filter != null) filter.apply(ctx, this); + if (!isNullish(filter)) filter.apply(ctx, this); } else { this.setContext(ctx); this.renderChildren(ctx); @@ -903,7 +910,7 @@ function build (opts) { // fill if (this.style('fill').isUrlDefinition()) { const fs = this.style('fill').getFillStyleDefinition(this, this.style('fill-opacity')); - if (fs != null) ctx.fillStyle = fs; + if (!isNullish(fs)) ctx.fillStyle = fs; } else if (this.style('fill').hasValue()) { const fillStyle = this.style('fill'); if (fillStyle.value === 'currentColor') fillStyle.value = this.style('color').value; @@ -918,7 +925,7 @@ function build (opts) { // stroke if (this.style('stroke').isUrlDefinition()) { const fs = this.style('stroke').getFillStyleDefinition(this, this.style('stroke-opacity')); - if (fs != null) ctx.strokeStyle = fs; + if (!isNullish(fs)) ctx.strokeStyle = fs; } else if (this.style('stroke').hasValue()) { const strokeStyle = this.style('stroke'); if (strokeStyle.value === 'currentColor') strokeStyle.value = this.style('color').value; @@ -963,7 +970,8 @@ function build (opts) { this.style('font-variant').value, this.style('font-weight').value, this.style('font-size').hasValue() ? this.style('font-size').toPixels() + 'px' : '', - this.style('font-family').value).toString(); + this.style('font-family').value + ).toString(); } // transform @@ -975,7 +983,7 @@ function build (opts) { // clip if (this.style('clip-path', false, true).hasValue()) { const clip = this.style('clip-path', false, true).getDefinition(); - if (clip != null) clip.apply(ctx); + if (!isNullish(clip)) clip.apply(ctx); } // opacity @@ -987,7 +995,7 @@ function build (opts) { svg.Element.PathElementBase = class extends svg.Element.RenderedElementBase { path (ctx) { - if (ctx != null) ctx.beginPath(); + if (!isNullish(ctx)) ctx.beginPath(); return new svg.BoundingBox(); } @@ -1004,7 +1012,7 @@ function build (opts) { if (ctx.strokeStyle !== '') ctx.stroke(); const markers = this.getMarkers(); - if (markers != null) { + if (!isNullish(markers)) { if (this.style('marker-start').isUrlDefinition()) { const marker = this.style('marker-start').getDefinition(); marker.render(ctx, markers[0][0], markers[0][1]); @@ -1123,7 +1131,7 @@ function build (opts) { if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry; rx = Math.min(rx, width / 2.0); ry = Math.min(ry, height / 2.0); - if (ctx != null) { + if (!isNullish(ctx)) { ctx.beginPath(); ctx.moveTo(x + rx, y); ctx.lineTo(x + width - rx, y); @@ -1148,7 +1156,7 @@ function build (opts) { const cy = this.attribute('cy').toPixels('y'); const r = this.attribute('r').toPixels(); - if (ctx != null) { + if (!isNullish(ctx)) { ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2, true); ctx.closePath(); @@ -1167,7 +1175,7 @@ function build (opts) { const cx = this.attribute('cx').toPixels('x'); const cy = this.attribute('cy').toPixels('y'); - if (ctx != null) { + if (!isNullish(ctx)) { ctx.beginPath(); ctx.moveTo(cx, cy - ry); ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy); @@ -1186,13 +1194,14 @@ function build (opts) { getPoints () { return [ new svg.Point(this.attribute('x1').toPixels('x'), this.attribute('y1').toPixels('y')), - new svg.Point(this.attribute('x2').toPixels('x'), this.attribute('y2').toPixels('y'))]; + new svg.Point(this.attribute('x2').toPixels('x'), this.attribute('y2').toPixels('y')) + ]; } path (ctx) { const points = this.getPoints(); - if (ctx != null) { + if (!isNullish(ctx)) { ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); ctx.lineTo(points[1].x, points[1].y); @@ -1218,14 +1227,14 @@ function build (opts) { path (ctx) { const {x, y} = this.points[0]; const bb = new svg.BoundingBox(x, y); - if (ctx != null) { + if (!isNullish(ctx)) { ctx.beginPath(); ctx.moveTo(x, y); } for (let i = 1; i < this.points.length; i++) { const {x, y} = this.points[i]; bb.addPoint(x, y); - if (ctx != null) ctx.lineTo(x, y); + if (!isNullish(ctx)) ctx.lineTo(x, y); } return bb; } @@ -1244,7 +1253,7 @@ function build (opts) { svg.Element.polygon = class extends svg.Element.polyline { path (ctx) { const bb = super.path(ctx); - if (ctx != null) { + if (!isNullish(ctx)) { ctx.lineTo(this.points[0].x, this.points[0].y); ctx.closePath(); } @@ -1289,7 +1298,7 @@ function build (opts) { isCommandOrEnd () { if (this.isEnd()) return true; - return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null; + return !isNullish(this.tokens[this.i + 1].match(/^[A-Za-z]$/)); }, isRelativeCommand () { @@ -1363,10 +1372,10 @@ function build (opts) { addMarker (p, from, priorTo) { // if the last angle isn't filled in because we didn't have this point yet ... - if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length - 1] == null) { + if (!isNullish(priorTo) && this.angles.length > 0 && isNullish(this.angles[this.angles.length - 1])) { this.angles[this.angles.length - 1] = this.points[this.points.length - 1].angleTo(priorTo); } - this.addMarkerAngle(p, from == null ? null : from.angleTo(p)); + this.addMarkerAngle(p, isNullish(from) ? null : from.angleTo(p)); }, addMarkerAngle (p, a) { @@ -1377,9 +1386,9 @@ function build (opts) { getMarkerPoints () { return this.points; }, getMarkerAngles () { for (let i = 0; i < this.angles.length; i++) { - if (this.angles[i] == null) { + if (isNullish(this.angles[i])) { for (let j = i + 1; j < this.angles.length; j++) { - if (this.angles[j] != null) { + if (!isNullish(this.angles[j])) { this.angles[i] = this.angles[j]; break; } @@ -1396,7 +1405,7 @@ function build (opts) { pp.reset(); const bb = new svg.BoundingBox(); - if (ctx != null) ctx.beginPath(); + if (!isNullish(ctx)) ctx.beginPath(); while (!pp.isEnd()) { pp.nextCommand(); switch (pp.command) { @@ -1405,13 +1414,13 @@ function build (opts) { const p = pp.getAsCurrentPoint(); pp.addMarker(p); bb.addPoint(p.x, p.y); - if (ctx != null) ctx.moveTo(p.x, p.y); + if (!isNullish(ctx)) ctx.moveTo(p.x, p.y); pp.start = pp.current; while (!pp.isCommandOrEnd()) { const p = pp.getAsCurrentPoint(); pp.addMarker(p, pp.start); bb.addPoint(p.x, p.y); - if (ctx != null) ctx.lineTo(p.x, p.y); + if (!isNullish(ctx)) ctx.lineTo(p.x, p.y); } break; case 'L': @@ -1421,7 +1430,7 @@ function build (opts) { const p = pp.getAsCurrentPoint(); pp.addMarker(p, c); bb.addPoint(p.x, p.y); - if (ctx != null) ctx.lineTo(p.x, p.y); + if (!isNullish(ctx)) ctx.lineTo(p.x, p.y); } break; case 'H': @@ -1431,7 +1440,7 @@ function build (opts) { pp.addMarker(newP, pp.current); pp.current = newP; bb.addPoint(pp.current.x, pp.current.y); - if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y); + if (!isNullish(ctx)) ctx.lineTo(pp.current.x, pp.current.y); } break; case 'V': @@ -1441,7 +1450,7 @@ function build (opts) { pp.addMarker(newP, pp.current); pp.current = newP; bb.addPoint(pp.current.x, pp.current.y); - if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y); + if (!isNullish(ctx)) ctx.lineTo(pp.current.x, pp.current.y); } break; case 'C': @@ -1453,7 +1462,7 @@ function build (opts) { const cp = pp.getAsCurrentPoint(); pp.addMarker(cp, cntrl, p1); bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); - if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); + if (!isNullish(ctx)) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); } break; case 'S': @@ -1465,7 +1474,7 @@ function build (opts) { const cp = pp.getAsCurrentPoint(); pp.addMarker(cp, cntrl, p1); bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); - if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); + if (!isNullish(ctx)) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); } break; case 'Q': @@ -1476,7 +1485,7 @@ function build (opts) { const cp = pp.getAsCurrentPoint(); pp.addMarker(cp, cntrl, cntrl); bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y); - if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y); + if (!isNullish(ctx)) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y); } break; case 'T': @@ -1488,7 +1497,7 @@ function build (opts) { const cp = pp.getAsCurrentPoint(); pp.addMarker(cp, cntrl, cntrl); bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y); - if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y); + if (!isNullish(ctx)) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y); } break; case 'A': @@ -1560,7 +1569,7 @@ function build (opts) { pp.addMarkerAngle(cp, ah - dir * Math.PI); bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better - if (ctx != null) { + if (!isNullish(ctx)) { const r = rx > ry ? rx : ry; const sx = rx > ry ? 1 : rx / ry; const sy = rx > ry ? ry / rx : 1; @@ -1577,7 +1586,7 @@ function build (opts) { break; case 'Z': case 'z': - if (ctx != null) ctx.closePath(); + if (!isNullish(ctx)) ctx.closePath(); pp.current = pp.start; } } @@ -1604,10 +1613,10 @@ function build (opts) { // render me using a temporary svg element const tempSvg = new svg.Element.svg(); - tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value); - tempSvg.attributes['width'] = new svg.Property('width', width + 'px'); - tempSvg.attributes['height'] = new svg.Property('height', height + 'px'); - tempSvg.attributes['transform'] = new svg.Property('transform', this.attribute('patternTransform').value); + tempSvg.attributes.viewBox = new svg.Property('viewBox', this.attribute('viewBox').value); + tempSvg.attributes.width = new svg.Property('width', width + 'px'); + tempSvg.attributes.height = new svg.Property('height', height + 'px'); + tempSvg.attributes.transform = new svg.Property('transform', this.attribute('patternTransform').value); tempSvg.children = this.children; const c = document.createElement('canvas'); @@ -1641,13 +1650,13 @@ function build (opts) { // render me using a temporary svg element const tempSvg = new svg.Element.svg(); - tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value); - tempSvg.attributes['refX'] = new svg.Property('refX', this.attribute('refX').value); - tempSvg.attributes['refY'] = new svg.Property('refY', this.attribute('refY').value); - tempSvg.attributes['width'] = new svg.Property('width', this.attribute('markerWidth').value); - tempSvg.attributes['height'] = new svg.Property('height', this.attribute('markerHeight').value); - tempSvg.attributes['fill'] = new svg.Property('fill', this.attribute('fill').valueOrDefault('black')); - tempSvg.attributes['stroke'] = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none')); + tempSvg.attributes.viewBox = new svg.Property('viewBox', this.attribute('viewBox').value); + tempSvg.attributes.refX = new svg.Property('refX', this.attribute('refX').value); + tempSvg.attributes.refY = new svg.Property('refY', this.attribute('refY').value); + tempSvg.attributes.width = new svg.Property('width', this.attribute('markerWidth').value); + tempSvg.attributes.height = new svg.Property('height', this.attribute('markerHeight').value); + tempSvg.attributes.fill = new svg.Property('fill', this.attribute('fill').valueOrDefault('black')); + tempSvg.attributes.stroke = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none')); tempSvg.children = this.children; tempSvg.render(ctx); @@ -1698,7 +1707,7 @@ function build (opts) { }; const g = this.getGradient(ctx, element); - if (g == null) return addParentOpacity(stopsContainer.stops[stopsContainer.stops.length - 1].color); + if (isNullish(g)) return addParentOpacity(stopsContainer.stops[stopsContainer.stops.length - 1].color); stopsContainer.stops.forEach(({offset, color}) => { g.addColorStop(offset, addParentOpacity(color)); }); @@ -1708,20 +1717,20 @@ function build (opts) { const rootView = svg.ViewPort.viewPorts[0]; const rect = new svg.Element.rect(); - rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS / 3.0); - rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS / 3.0); - rect.attributes['width'] = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS); - rect.attributes['height'] = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS); + rect.attributes.x = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS / 3.0); + rect.attributes.y = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS / 3.0); + rect.attributes.width = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS); + rect.attributes.height = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS); const group = new svg.Element.g(); - group.attributes['transform'] = new svg.Property('transform', this.attribute('gradientTransform').value); + group.attributes.transform = new svg.Property('transform', this.attribute('gradientTransform').value); group.children = [rect]; const tempSvg = new svg.Element.svg(); - tempSvg.attributes['x'] = new svg.Property('x', 0); - tempSvg.attributes['y'] = new svg.Property('y', 0); - tempSvg.attributes['width'] = new svg.Property('width', rootView.width); - tempSvg.attributes['height'] = new svg.Property('height', rootView.height); + tempSvg.attributes.x = new svg.Property('x', 0); + tempSvg.attributes.y = new svg.Property('y', 0); + tempSvg.attributes.width = new svg.Property('width', rootView.width); + tempSvg.attributes.height = new svg.Property('height', rootView.height); tempSvg.children = [group]; const c = document.createElement('canvas'); @@ -1867,7 +1876,7 @@ function build (opts) { update (delta) { // set initial value - if (this.initialValue == null) { + if (isNullish(this.initialValue)) { this.initialValue = this.getProperty().value; this.initialUnits = this.getProperty().getUnits(); } @@ -1949,7 +1958,7 @@ function build (opts) { const r = from.r + (to.r - from.r) * p.progress; const g = from.g + (to.g - from.g) * p.progress; const b = from.b + (to.b - from.b) * p.progress; - return 'rgb(' + parseInt(r, 10) + ',' + parseInt(g, 10) + ',' + parseInt(b, 10) + ')'; + return 'rgb(' + parseInt(r) + ',' + parseInt(g) + ',' + parseInt(b) + ')'; } return this.attribute('from').value; } @@ -2048,8 +2057,8 @@ function build (opts) { super.setContext(ctx); let textBaseline = this.style('dominant-baseline').toTextBaseline(); - if (textBaseline == null) textBaseline = this.style('alignment-baseline').toTextBaseline(); - if (textBaseline != null) ctx.textBaseline = textBaseline; + if (isNullish(textBaseline)) textBaseline = this.style('alignment-baseline').toTextBaseline(); + if (!isNullish(textBaseline)) ctx.textBaseline = textBaseline; } getBoundingBox () { @@ -2124,18 +2133,18 @@ function build (opts) { if (i > 0 && text[i - 1] !== ' ' && (i === text.length - 1 || text[i + 1] === ' ')) arabicForm = 'initial'; if (typeof font.glyphs[c] !== 'undefined') { glyph = font.glyphs[c][arabicForm]; - if (glyph == null && font.glyphs[c].type === 'glyph') glyph = font.glyphs[c]; + if (isNullish(glyph) && font.glyphs[c].type === 'glyph') glyph = font.glyphs[c]; } } else { glyph = font.glyphs[c]; } - if (glyph == null) glyph = font.missingGlyph; + if (isNullish(glyph)) glyph = font.missingGlyph; return glyph; } renderChildren (ctx) { const customFont = this.parent.style('font-family').getDefinition(); - if (customFont != null) { + if (!isNullish(customFont)) { const fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize); const fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle); let text = this.getText(); @@ -2182,7 +2191,7 @@ function build (opts) { measureText (ctx) { const customFont = this.parent.style('font-family').getDefinition(); - if (customFont != null) { + if (!isNullish(customFont)) { const fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize); let measure = 0; let text = this.getText(); @@ -2225,7 +2234,8 @@ function build (opts) { svg.Element.tref = class extends svg.Element.TextElementBase { getText () { const element = this.getHrefAttribute().getDefinition(); - if (element != null) return element.children[0].getText(); + if (!isNullish(element)) return element.children[0].getText(); + return undefined; } }; @@ -2290,13 +2300,12 @@ function build (opts) { if (svg.opts.useCORS === true) { this.img.crossOrigin = 'Anonymous'; } - const self = this; - this.img.onload = function () { - self.loaded = true; + this.img.onload = () => { + this.loaded = true; }; - this.img.onerror = function () { + this.img.onerror = () => { svg.log('ERROR: image "' + href + '" not found'); - self.loaded = true; + this.loaded = true; }; this.img.src = href; } else { @@ -2387,14 +2396,14 @@ function build (opts) { const prop = cssProp.indexOf(':'); const name = cssProp.substr(0, prop); const value = cssProp.substr(prop + 1, cssProp.length - prop); - if (name != null && value != null) { + if (!isNullish(name) && !isNullish(value)) { props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value)); } }); svg.Styles[cssClass] = props; if (cssClass === '@font-face') { const fontFamily = props['font-family'].value.replace(/"/g, ''); - const srcs = props['src'].value.split(','); + const srcs = props.src.value.split(','); srcs.forEach((src) => { if (src.includes('format("svg")')) { const urlStart = src.indexOf('url'); @@ -2433,31 +2442,31 @@ function build (opts) { path (ctx) { const {_el: element} = this; - if (element != null) element.path(ctx); + if (!isNullish(element)) element.path(ctx); } getBoundingBox () { const {_el: element} = this; - if (element != null) return element.getBoundingBox(); + if (!isNullish(element)) return element.getBoundingBox(); } renderChildren (ctx) { const {_el: element} = this; - if (element != null) { + if (!isNullish(element)) { let tempSvg = element; if (element.type === 'symbol') { // render me using a temporary svg element in symbol cases (https://www.w3.org/TR/SVG/struct.html#UseElement) tempSvg = new svg.Element.svg(); tempSvg.type = 'svg'; - tempSvg.attributes['viewBox'] = new svg.Property('viewBox', element.attribute('viewBox').value); - tempSvg.attributes['preserveAspectRatio'] = new svg.Property('preserveAspectRatio', element.attribute('preserveAspectRatio').value); - tempSvg.attributes['overflow'] = new svg.Property('overflow', element.attribute('overflow').value); + tempSvg.attributes.viewBox = new svg.Property('viewBox', element.attribute('viewBox').value); + tempSvg.attributes.preserveAspectRatio = new svg.Property('preserveAspectRatio', element.attribute('preserveAspectRatio').value); + tempSvg.attributes.overflow = new svg.Property('overflow', element.attribute('overflow').value); tempSvg.children = element.children; } if (tempSvg.type === 'svg') { // if symbol or svg, inherit width/height from me - if (this.attribute('width').hasValue()) tempSvg.attributes['width'] = new svg.Property('width', this.attribute('width').value); - if (this.attribute('height').hasValue()) tempSvg.attributes['height'] = new svg.Property('height', this.attribute('height').value); + if (this.attribute('width').hasValue()) tempSvg.attributes.width = new svg.Property('width', this.attribute('width').value); + if (this.attribute('height').hasValue()) tempSvg.attributes.height = new svg.Property('height', this.attribute('height').value); } const oldParent = tempSvg.parent; tempSvg.parent = null; @@ -2762,14 +2771,14 @@ function build (opts) { // bind mouse if (svg.opts.ignoreMouse !== true) { ctx.canvas.onclick = function (e) { - const args = e != null + const args = !isNullish(e) ? [e.clientX, e.clientY] : [event.clientX, event.clientY]; const {x, y} = mapXY(new svg.Point(...args)); svg.Mouse.onclick(x, y); }; ctx.canvas.onmousemove = function (e) { - const args = e != null + const args = !isNullish(e) ? [e.clientX, e.clientY] : [event.clientX, event.clientY]; const {x, y} = mapXY(new svg.Point(...args)); @@ -2812,17 +2821,17 @@ function build (opts) { } svg.ViewPort.SetCurrent(cWidth, cHeight); - if (svg.opts.offsetX != null) { + if (!isNullish(svg.opts.offsetX)) { e.attribute('x', true).value = svg.opts.offsetX; } - if (svg.opts.offsetY != null) { + if (!isNullish(svg.opts.offsetY)) { e.attribute('y', true).value = svg.opts.offsetY; } - if (svg.opts.scaleWidth != null || svg.opts.scaleHeight != null) { + if (!isNullish(svg.opts.scaleWidth) || !isNullish(svg.opts.scaleHeight)) { const viewBox = svg.ToNumberArray(e.attribute('viewBox').value); let xRatio = null, yRatio = null; - if (svg.opts.scaleWidth != null) { + if (!isNullish(svg.opts.scaleWidth)) { if (e.attribute('width').hasValue()) { xRatio = e.attribute('width').toPixels('x') / svg.opts.scaleWidth; } else if (!isNaN(viewBox[2])) { @@ -2830,7 +2839,7 @@ function build (opts) { } } - if (svg.opts.scaleHeight != null) { + if (!isNullish(svg.opts.scaleHeight)) { if (e.attribute('height').hasValue()) { yRatio = e.attribute('height').toPixels('y') / svg.opts.scaleHeight; } else if (!isNaN(viewBox[3])) { @@ -2838,8 +2847,8 @@ function build (opts) { } } - if (xRatio == null) { xRatio = yRatio; } - if (yRatio == null) { yRatio = xRatio; } + if (isNullish(xRatio)) { xRatio = yRatio; } + if (isNullish(yRatio)) { yRatio = xRatio; } e.attribute('width', true).value = svg.opts.scaleWidth; e.attribute('height', true).value = svg.opts.scaleHeight; diff --git a/editor/canvg/rgbcolor.js b/editor/canvg/rgbcolor.js index b3762975..aadcb1c5 100644 --- a/editor/canvg/rgbcolor.js +++ b/editor/canvg/rgbcolor.js @@ -158,9 +158,9 @@ const colorDefs = [ example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'], process (bits) { return [ - parseInt(bits[1], 10), - parseInt(bits[2], 10), - parseInt(bits[3], 10) + parseInt(bits[1]), + parseInt(bits[2]), + parseInt(bits[3]) ]; } }, @@ -279,8 +279,7 @@ export default class RGBColor { margin: 3px; border: 1px solid black; background: ${listColor.toHex()}; -color: ${listColor.toHex()};` - ; +color: ${listColor.toHex()};`; exampleDiv.append('test'); const listItemValue = ` ${examples[i]} -> ${listColor.toRGB()} -> ${listColor.toHex()}`; listItem.append(exampleDiv, listItemValue); diff --git a/editor/contextmenu.js b/editor/contextmenu.js index f039a49a..e91232d9 100644 --- a/editor/contextmenu.js +++ b/editor/contextmenu.js @@ -93,9 +93,9 @@ const injectExtendedContextMenuItemIntoDom = function (menuItem) { * @returns {undefined} */ export const injectExtendedContextMenuItemsIntoDom = function () { - for (const menuItem in contextMenuExtensions) { - injectExtendedContextMenuItemIntoDom(contextMenuExtensions[menuItem]); - } + Object.values(contextMenuExtensions).forEach((menuItem) => { + injectExtendedContextMenuItemIntoDom(menuItem); + }); }; /** * @function module:contextmenu.resetCustomMenus diff --git a/editor/contextmenu/jQuery.contextMenu.js b/editor/contextmenu/jQuery.contextMenu.js index 1715d3f0..9468115a 100755 --- a/editor/contextmenu/jQuery.contextMenu.js +++ b/editor/contextmenu/jQuery.contextMenu.js @@ -74,8 +74,7 @@ function jQueryContextMenu ($) { // Add contextMenu class menu.addClass('contextMenu'); // Simulate a true right click - $(this).bind('mousedown', function (e) { - const evt = e; + $(this).bind('mousedown', function (evt) { $(this).mouseup(function (e) { const srcElement = $(this); srcElement.unbind('mouseup'); @@ -109,8 +108,8 @@ function jQueryContextMenu ($) { }); // Keyboard - doc.keypress(function (e) { - switch (e.keyCode) { + doc.keypress(function (ev) { + switch (ev.keyCode) { case 38: // up if (!menu.find('LI.hover').length) { menu.find('LI:last').addClass('hover'); @@ -143,7 +142,9 @@ function jQueryContextMenu ($) { $('.contextMenu').hide(); // Callback if (callback) { - callback($(this).attr('href').substr(1), $(srcElement), {x: x - offset.left, y: y - offset.top, docX: x, docY: y}); + callback($(this).attr('href').substr(1), $(srcElement), { + x: x - offset.left, y: y - offset.top, docX: x, docY: y + }); } return false; }); diff --git a/editor/coords.js b/editor/coords.js index 1fbe6f02..19965d88 100644 --- a/editor/coords.js +++ b/editor/coords.js @@ -17,8 +17,10 @@ import {getTransformList} from './svgtransformlist.js'; const $ = jQuery; // this is how we map paths to our preferred relative segment types -const pathMap = [0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a', - 'H', 'h', 'V', 'v', 'S', 's', 'T', 't']; +const pathMap = [ + 0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a', + 'H', 'h', 'V', 'v', 'S', 's', 'T', 't' +]; /** * @interface module:coords.EditorContext @@ -59,9 +61,9 @@ export const remapElement = function (selected, changes, m) { doSnapping = editorContext_.getGridSnapping() && selected.parentNode.parentNode.localName === 'svg', finishUp = function () { if (doSnapping) { - for (const o in changes) { - changes[o] = snapToGrid(changes[o]); - } + Object.entries(changes).forEach(([o, value]) => { + changes[o] = snapToGrid(value); + }); } assignAttributes(selected, changes, 1000, true); }, @@ -297,8 +299,8 @@ export const remapElement = function (selected, changes, m) { break; case 11: // relative elliptical arc (a) case 10: // absolute elliptical arc (A) - dstr += seg.r1 + ',' + seg.r2 + ' ' + seg.angle + ' ' + (+seg.largeArcFlag) + - ' ' + (+seg.sweepFlag) + ' ' + seg.x + ',' + seg.y + ' '; + dstr += seg.r1 + ',' + seg.r2 + ' ' + seg.angle + ' ' + Number(seg.largeArcFlag) + + ' ' + Number(seg.sweepFlag) + ' ' + seg.x + ',' + seg.y + ' '; break; case 17: // relative smooth cubic (s) case 16: // absolute smooth cubic (S) diff --git a/editor/draw.js b/editor/draw.js index 0e66edc6..bf2ceab3 100644 --- a/editor/draw.js +++ b/editor/draw.js @@ -139,7 +139,7 @@ export class Drawing { const n = this.svgElem_.getAttributeNS(NS.SE, 'nonce'); // If already set in the DOM, use the nonce throughout the document // else, if randomizeIds(true) has been called, create and set the nonce. - if (!!n && randIds !== RandomizeModes.NEVER_RANDOMIZE) { + if (n && randIds !== RandomizeModes.NEVER_RANDOMIZE) { this.nonce_ = n; } else if (randIds === RandomizeModes.ALWAYS_RANDOMIZE) { this.setNonce(Math.floor(Math.random() * 100001)); @@ -253,7 +253,7 @@ export class Drawing { return false; } // extract the obj_num of this id - const num = parseInt(id.substr(front.length), 10); + const num = parseInt(id.substr(front.length)); // if we didn't get a positive number or we already released this number // then return false. @@ -669,8 +669,8 @@ export class Drawing { * @returns {Element} */ copyElem (el) { - const self = this; - const getNextIdClosure = function () { return self.getNextId(); }; + const that = this; + const getNextIdClosure = function () { return that.getNextId(); }; return utilCopyElem(el, getNextIdClosure); } } diff --git a/editor/embedapi.js b/editor/embedapi.js index c8f2bc43..310a9cbd 100644 --- a/editor/embedapi.js +++ b/editor/embedapi.js @@ -22,11 +22,11 @@ let cbid = 0; */ function getCallbackSetter (funcName) { return function (...args) { - const t = this, // New callback - cbid = t.send(funcName, args, function () {}); // The callback (currently it's nothing, but will be set later) + const that = this, // New callback + callbackID = this.send(funcName, args, function () { /* */ }); // The callback (currently it's nothing, but will be set later) return function (newCallback) { - t.callbacks[cbid] = newCallback; // Set callback + that.callbacks[callbackID] = newCallback; // Set callback }; }; } @@ -39,14 +39,14 @@ function getCallbackSetter (funcName) { * @param {JSON} data * @returns {undefined} */ -function addCallback (t, {result, error, id: cbid}) { - if (typeof cbid === 'number' && t.callbacks[cbid]) { +function addCallback (t, {result, error, id: callbackID}) { + if (typeof callbackID === 'number' && t.callbacks[callbackID]) { // These should be safe both because we check `cbid` is numeric and // because the calls are from trusted origins if (result) { - t.callbacks[cbid](result); // lgtm [js/remote-property-injection] + t.callbacks[callbackID](result); // lgtm [js/remote-property-injection] } else { - t.callbacks[cbid](error, 'error'); // lgtm [js/remote-property-injection] + t.callbacks[callbackID](error, 'error'); // lgtm [js/remote-property-injection] } } } @@ -67,7 +67,7 @@ function messageListener (e) { e.source !== this.frame.contentWindow || (!allowedOrigins.includes('*') && !allowedOrigins.includes(e.origin)) ) { - console.log(`The origin ${e.origin} was not whitelisted as an origin from which responses may be received by this ${window.origin} script.`); + console.log(`The origin ${e.origin} was not whitelisted as an origin from which responses may be received by this ${window.origin} script.`); // eslint-disable-line no-console return; } addCallback(this, data); @@ -136,7 +136,7 @@ class EmbeddedSVGEdit { * If supplied, it should probably be the same as svgEditor's allowedOrigins */ constructor (frame, allowedOrigins) { - const t = this; + const that = this; this.allowedOrigins = allowedOrigins || []; // Initialize communication this.frame = frame; @@ -326,7 +326,7 @@ class EmbeddedSVGEdit { const keyboardEvent = new KeyboardEvent(e.type, { key, keyCode, charCode, which }); - t.frame.contentDocument.dispatchEvent(keyboardEvent); + that.frame.contentDocument.dispatchEvent(keyboardEvent); } }); } @@ -338,11 +338,11 @@ class EmbeddedSVGEdit { * @returns {Integer} */ send (name, args, callback) { - const t = this; + const that = this; cbid++; this.callbacks[cbid] = callback; - setTimeout((function (cbid) { + setTimeout((function (callbackID) { return function () { // Delay for the callback to be set in case its synchronous /* * Todo: Handle non-JSON arguments and return values (undefined, @@ -354,8 +354,8 @@ class EmbeddedSVGEdit { // We accept and post strings for the sake of IE9 support let sameOriginWithGlobal = false; try { - sameOriginWithGlobal = window.location.origin === t.frame.contentWindow.location.origin && - t.frame.contentWindow.svgEditor.canvas; + sameOriginWithGlobal = window.location.origin === that.frame.contentWindow.location.origin && + that.frame.contentWindow.svgEditor.canvas; } catch (err) {} if (sameOriginWithGlobal) { @@ -365,17 +365,17 @@ class EmbeddedSVGEdit { // of the current JSON-based communication API (e.g., not passing // callbacks). We might be able to address these shortcomings; see // the todo elsewhere in this file. - const message = {id: cbid}, - {svgEditor: {canvas: svgCanvas}} = t.frame.contentWindow; + const message = {id: callbackID}, + {svgEditor: {canvas: svgCanvas}} = that.frame.contentWindow; try { - message.result = svgCanvas[name].apply(svgCanvas, args); + message.result = svgCanvas[name](...args); } catch (err) { message.error = err.message; } - addCallback(t, message); + addCallback(that, message); } else { // Requires the ext-xdomain-messaging.js extension - t.frame.contentWindow.postMessage(JSON.stringify({ - namespace: 'svgCanvas', id: cbid, name, args + that.frame.contentWindow.postMessage(JSON.stringify({ + namespace: 'svgCanvas', id: callbackID, name, args }), '*'); } }; diff --git a/editor/extensions/ext-connector.js b/editor/extensions/ext-connector.js index 39de3fc2..f41152ae 100644 --- a/editor/extensions/ext-connector.js +++ b/editor/extensions/ext-connector.js @@ -67,7 +67,7 @@ export default { } function getOffset (side, line) { - const giveOffset = !!line.getAttribute('marker-' + side); + const giveOffset = line.getAttribute('marker-' + side); // const giveOffset = $(line).data(side+'_off'); // TODO: Make this number (5) be based on marker width/height @@ -170,7 +170,7 @@ export default { ['start', 'end'].forEach(function (pos, i) { const key = 'c_' + pos; let part = elData(this, key); - if (part == null) { + if (part === null || part === undefined) { // Does this ever return nullish values? part = document.getElementById( this.attributes['se:connector'].value.split(' ')[i] ); @@ -178,7 +178,7 @@ export default { elData(this, pos + '_bb', svgCanvas.getStrokedBBox([part])); } else part = document.getElementById(part); parts.push(part); - }.bind(this)); + }, this); for (let i = 0; i < 2; i++) { const cElem = parts[i]; @@ -262,15 +262,15 @@ export default { (function () { const gse = svgCanvas.groupSelectedElements; - svgCanvas.groupSelectedElements = function () { + svgCanvas.groupSelectedElements = function (...args) { svgCanvas.removeFromSelection($(connSel).toArray()); - return gse.apply(this, arguments); + return gse.apply(this, args); }; const mse = svgCanvas.moveSelectedElements; - svgCanvas.moveSelectedElements = function () { - const cmd = mse.apply(this, arguments); + svgCanvas.moveSelectedElements = function (...args) { + const cmd = mse.apply(this, args); updateConnectors(); return cmd; }; @@ -331,7 +331,7 @@ export default { buttons: strings.buttons.map((button, i) => { return Object.assign(buttons[i], button); }), - async addLangData ({lang, importLocale}) { + /* async */ addLangData ({lang, importLocale}) { return { data: strings.langList }; @@ -548,8 +548,8 @@ export default { const end = elem.getAttribute('marker-end'); curLine = elem; $(elem) - .data('start_off', !!start) - .data('end_off', !!end); + .data('start_off', Boolean(start)) + .data('end_off', Boolean(end)); if (elem.tagName === 'line' && mid) { // Convert to polyline to accept mid-arrow diff --git a/editor/extensions/ext-overview_window.js b/editor/extensions/ext-overview_window.js index 13a50e13..e90617f4 100644 --- a/editor/extensions/ext-overview_window.js +++ b/editor/extensions/ext-overview_window.js @@ -16,7 +16,7 @@ export default { // https://code.google.com/p/chromium/issues/detail?id=565120. if (isChrome()) { const verIndex = navigator.userAgent.indexOf('Chrome/') + 7; - const chromeVersion = parseInt(navigator.userAgent.substring(verIndex), 10); + const chromeVersion = parseInt(navigator.userAgent.substring(verIndex)); if (chromeVersion < 49) { return; } diff --git a/editor/extensions/ext-webappfind.js b/editor/extensions/ext-webappfind.js index 21288afc..e478caec 100644 --- a/editor/extensions/ext-webappfind.js +++ b/editor/extensions/ext-webappfind.js @@ -24,7 +24,7 @@ export default { * @throws {Error} Unexpected event type * @returns {undefined} */ - (win, {data, origin}) => { + (win, {data, origin}) => { // eslint-disable-line no-shadow // console.log('data, origin', data, origin); let type, content; try { @@ -78,17 +78,18 @@ export default { if (!pathID) { // Not ready yet as haven't received first payload return; } - window.postMessage({ - webappfind: { - type: saveMessage, - pathID, - content: svgEditor.canvas.getSvgString() - } - }, window.location.origin === 'null' - // Avoid "null" string error for `file:` protocol (even - // though file protocol not currently supported by add-on) - ? '*' - : window.location.origin + window.postMessage( + { + webappfind: { + type: saveMessage, + pathID, + content: svgEditor.canvas.getSvgString() + } + }, window.location.origin === 'null' + // Avoid "null" string error for `file:` protocol (even + // though file protocol not currently supported by add-on) + ? '*' + : window.location.origin ); } } diff --git a/editor/extensions/imagelib/openclipart.js b/editor/extensions/imagelib/openclipart.js index 45de98aa..6772d0e6 100644 --- a/editor/extensions/imagelib/openclipart.js +++ b/editor/extensions/imagelib/openclipart.js @@ -1,12 +1,21 @@ import {jml, body, nbsp} from '../../external/jamilih/jml-es.js'; - import $ from '../../external/query-result/esm/index.js'; import {manipulation} from '../../external/qr-manipulation/dist/index-es.js'; + manipulation($, jml); const baseAPIURL = 'https://openclipart.org/search/json/'; +/** + * Shows results after query submission. + * @param {string} url + * @returns {undefined} + */ async function processResults (url) { + /** + * @param {string} query + * @returns {external:JamilihArray} + */ function queryLink (query) { return ['a', { href: 'javascript: void(0);', @@ -22,7 +31,7 @@ async function processResults (url) { const r = await fetch(url); const json = await r.json(); - console.log('json', json); + // console.log('json', json); if (!json || json.msg !== 'success') { alert('There was a problem downloading the results'); @@ -74,7 +83,7 @@ async function processResults (url) { async click (e) { e.preventDefault(); const {value: svgURL, id} = this.dataset; - console.log('this', id, svgURL); + // console.log('this', id, svgURL); const post = (message) => { // Todo: Make origin customizable as set by opening window // Todo: If dropping IE9, avoid stringifying @@ -90,7 +99,7 @@ async function processResults (url) { }); const result = await fetch(svgURL); const svg = await result.text(); - console.log('h', svgURL, svg); + // console.log('url and svg', svgURL, svg); post({ href: svgURL, data: svg diff --git a/editor/external/dynamic-import-polyfill/importModule.js b/editor/external/dynamic-import-polyfill/importModule.js index ed9a4826..5f6545a3 100644 --- a/editor/external/dynamic-import-polyfill/importModule.js +++ b/editor/external/dynamic-import-polyfill/importModule.js @@ -5,12 +5,23 @@ * @module importModule */ +/** + * Converts a possible relative URL into an absolute one. + * @param {string} url + * @returns {string} + */ function toAbsoluteURL (url) { const a = document.createElement('a'); a.setAttribute('href', url); // return a.cloneNode(false).href; // -> "http://example.com/hoge.html" } +/** + * Add any of the whitelisted attributes to the script tag. + * @param {HTMLScriptElement} script + * @param {PlainObject.} atts + * @returns {undefined} + */ function addScriptAtts (script, atts) { ['id', 'class', 'type'].forEach((prop) => { if (prop in atts) { @@ -27,18 +38,19 @@ function addScriptAtts (script, atts) { */ /** * @function module:importModule.importSetGlobalDefault -* @param {string} url +* @param {string|string[]} url * @param {module:importModule.ImportConfig} config -* @returns {*} The return depends on the export of the targeted module. +* @returns {Promise} The value to which it resolves depends on the export of the targeted module. */ -export async function importSetGlobalDefault (url, config) { +export function importSetGlobalDefault (url, config) { return importSetGlobal(url, {...config, returnDefault: true}); } /** * @function module:importModule.importSetGlobal -* @param {string} url +* @param {string|string[]} url * @param {module:importModule.ImportConfig} config -* @returns {ArbitraryModule|*} The return depends on the export of the targeted module. +* @returns {Promise} The promise resolves to either an `ArbitraryModule` or +* any other value depends on the export of the targeted module. */ export async function importSetGlobal (url, {global, returnDefault}) { // Todo: Replace calls to this function with `import()` when supported @@ -51,14 +63,21 @@ export async function importSetGlobal (url, {global, returnDefault}) { await importScript(url); return window[global]; } -// Addition by Brett +/** + * + * @author Brett Zamir (other items are from `dynamic-import-polyfill`) + * @param {string|string[]} url + * @param {Object} [atts={}] + * @returns {Promise} Resolves to `undefined` or rejects with an `Error` upon a + * script loading error + */ export function importScript (url, atts = {}) { if (Array.isArray(url)) { return Promise.all(url.map((u) => { return importScript(u, atts); })); } - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { // eslint-disable-line promise/avoid-new const script = document.createElement('script'); const destructor = () => { script.onerror = null; @@ -82,13 +101,22 @@ export function importScript (url, atts = {}) { }); } +/** + * + * @param {string|string[]} url + * @param {Object} [atts={}] + * @param {PlainObject} opts + * @param {boolean} [opts.returnDefault=false} = {}] + * @returns {Promise} Resolves to value of loading module or rejects with + * `Error` upon a script loading error. + */ export function importModule (url, atts = {}, {returnDefault = false} = {}) { if (Array.isArray(url)) { return Promise.all(url.map((u) => { return importModule(u, atts); })); } - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { // eslint-disable-line promise/avoid-new const vector = '$importModule$' + Math.random().toString(32).slice(2); const script = document.createElement('script'); const destructor = () => { diff --git a/editor/history.js b/editor/history.js index fa40ce49..c53fb227 100644 --- a/editor/history.js +++ b/editor/history.js @@ -5,7 +5,7 @@ * @copyright 2010 Jeff Schiller */ -import {getHref, setHref, getRotationAngle} from './utilities.js'; +import {getHref, setHref, getRotationAngle, isNullish} from './utilities.js'; import {removeElementFromListMap} from './svgtransformlist.js'; /** @@ -285,9 +285,9 @@ export class RemoveElementCommand extends Command { } removeElementFromListMap(this.elem); - if (this.nextSibling == null) { + if (isNullish(this.nextSibling)) { if (window.console) { - console.log('Error: reference element was lost'); + console.log('Error: reference element was lost'); // eslint-disable-line no-console } } this.parent.insertBefore(this.elem, this.nextSibling); // Don't use `before` or `prepend` as `this.nextSibling` may be `null` @@ -670,7 +670,7 @@ export class UndoManager { const oldValues = new Array(i), elements = new Array(i); while (i--) { const elem = elems[i]; - if (elem == null) { continue; } + if (isNullish(elem)) { continue; } elements[i] = elem; oldValues[i] = elem.getAttribute(attrName); } @@ -695,7 +695,7 @@ export class UndoManager { let i = changeset.elements.length; while (i--) { const elem = changeset.elements[i]; - if (elem == null) { continue; } + if (isNullish(elem)) { continue; } const changes = {}; changes[attrName] = changeset.oldValues[i]; if (changes[attrName] !== elem.getAttribute(attrName)) { diff --git a/editor/historyrecording.js b/editor/historyrecording.js index e7e40feb..49437dde 100644 --- a/editor/historyrecording.js +++ b/editor/historyrecording.js @@ -77,8 +77,8 @@ class HistoryRecordingService { if (this.currentBatchCommand_) { const batchCommand = this.currentBatchCommand_; this.batchCommandStack_.pop(); - const {length} = this.batchCommandStack_; - this.currentBatchCommand_ = length ? this.batchCommandStack_[length - 1] : null; + const {length: len} = this.batchCommandStack_; + this.currentBatchCommand_ = len ? this.batchCommandStack_[len - 1] : null; this.addCommand_(batchCommand); } return this; @@ -141,7 +141,7 @@ class HistoryRecordingService { * Private function to add a command to the history or current batch command. * @private * @param {Command} cmd - * @returns {module:history.HistoryRecordingService} + * @returns {module:history.HistoryRecordingService|undefined} */ addCommand_ (cmd) { if (!this.undoManager_) { return this; } @@ -150,6 +150,7 @@ class HistoryRecordingService { } else { this.undoManager_.addCommandToHistory(cmd); } + return undefined; } } /** diff --git a/editor/jQuery.attr.js b/editor/jQuery.attr.js index cf6e132e..5dc2f302 100644 --- a/editor/jQuery.attr.js +++ b/editor/jQuery.attr.js @@ -18,7 +18,7 @@ * @param {external:jQuery} $ The jQuery object to which to add the plug-in * @returns {external:jQuery} */ -export default function ($) { +export default function jQueryPluginSVG ($) { const proxied = $.fn.attr, svgns = 'http://www.w3.org/2000/svg'; /** @@ -32,7 +32,7 @@ export default function ($) { */ $.fn.attr = function (key, value) { const len = this.length; - if (!len) { return proxied.apply(this, arguments); } + if (!len) { return proxied.call(this, key, value); } for (let i = 0; i < len; ++i) { const elem = this[i]; // set/get SVG attribute @@ -70,7 +70,7 @@ export default function ($) { return attr; } } else { - return proxied.apply(this, arguments); + return proxied.call(this, key, value); } } return this; diff --git a/editor/jgraduate/jQuery.jGraduate.js b/editor/jgraduate/jQuery.jGraduate.js index 47ab1289..fa576aac 100644 --- a/editor/jgraduate/jQuery.jGraduate.js +++ b/editor/jgraduate/jQuery.jGraduate.js @@ -407,7 +407,8 @@ export default function ($) { '
' + '' + '' + - '
'); + '' + ); // -------------- // Set up all the SVG elements (the gradient, stops and rectangle) @@ -1234,7 +1235,7 @@ export default function ($) { const sm = spreadMethodOpt.val(); curGradient.setAttribute('spreadMethod', sm); } - showFocus = type === 'rg' && curGradient.getAttribute('fx') != null && !(cx === fx && cy === fy); + showFocus = type === 'rg' && curGradient.getAttribute('fx') !== null && !(cx === fx && cy === fy); $('#' + id + '_jGraduate_focusCoord').toggle(showFocus); if (showFocus) { $('#' + id + '_jGraduate_match_ctr')[0].checked = false; diff --git a/editor/jgraduate/jQuery.jPicker.js b/editor/jgraduate/jQuery.jPicker.js index 2377391b..527b4378 100755 --- a/editor/jgraduate/jQuery.jPicker.js +++ b/editor/jgraduate/jQuery.jPicker.js @@ -29,6 +29,15 @@ function toFixedNumeric (value, precision) { return Math.round(value * Math.pow(10, precision)) / Math.pow(10, precision); } +/** + * Whether a value is `null` or `undefined`. + * @param {Any} val + * @returns {boolean} + */ +const isNullish = (val) => { + return val === null || val === undefined; +}; + /** * @function module:jPicker.jPicker * @param {external:jQuery} $ The jQuery object to wrap (with {@link external:jQuery.loadingStylesheets}, {@link external:jQuery.fn.$.fn.jPicker}, {@link external:jQuery.fn.$.fn.jPicker.defaults}) @@ -67,36 +76,54 @@ const jPicker = function ($) { */ class Slider { constructor (bar, options) { - const $this = this; + const that = this; + /** + * Fire events on the supplied `context` + * @param {module:jPicker.JPickerInit} context + * @returns {undefined} + */ function fireChangeEvents (context) { - for (let i = 0; i < changeEvents.length; i++) { - changeEvents[i].call($this, $this, context); - } + changeEvents.forEach((changeEvent) => { + changeEvent.call(that, that, context); + }); } - // bind the mousedown to the bar not the arrow for quick snapping to the clicked location + + /** + * Bind the mousedown to the bar not the arrow for quick snapping to the clicked location. + * @param {external:jQuery.Event} e + * @returns {undefined} + */ function mouseDown (e) { const off = bar.offset(); - offset = {l: off.left | 0, t: off.top | 0}; + offset = {l: off.left | 0, t: off.top | 0}; // eslint-disable-line no-bitwise 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($this, e); + 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).bind('mousemove', mouseMove).bind('mouseup', mouseUp); e.preventDefault(); // don't try to select anything or drag the image to the desktop } - // set the values as the mouse moves + /** + * Set the values as the mouse moves. + * @param {external:jQuery.Event} e + * @returns {false} + */ function mouseMove (e) { clearTimeout(timeout); timeout = setTimeout(function () { - setValuesFromMousePosition.call($this, e); + setValuesFromMousePosition.call(that, e); }, 0); e.stopPropagation(); e.preventDefault(); return false; } - // unbind the document events - they aren't needed when not dragging + /** + * Unbind the document events - they aren't needed when not dragging. + * @param {external:jQuery.Event} e + * @returns {false} + */ function mouseUp (e) { $(document).unbind('mouseup', mouseUp).unbind('mousemove', mouseMove); e.stopPropagation(); @@ -114,7 +141,7 @@ const jPicker = function ($) { else if (locX > barW) locX = barW; if (locY < 0) locY = 0; else if (locY > barH) locY = barH; - val.call($this, 'xy', {x: ((locX / barW) * rangeX) + minX, y: ((locY / barH) * rangeY) + minY}); + val.call(that, 'xy', {x: ((locX / barW) * rangeX) + minX, y: ((locY / barH) * rangeY) + minY}); } function draw () { const @@ -125,6 +152,7 @@ const jPicker = function ($) { let arrowOffsetX = 0, arrowOffsetY = 0; setTimeout(function () { + /* eslint-disable no-bitwise */ if (rangeX > 0) { // range is greater than zero // constrain to bounds if (x === maxX) arrowOffsetX = barW; @@ -143,12 +171,13 @@ const jPicker = function ($) { else arrowOffsetY -= arrowH >> 1; // set the arrow position based on these offsets arrow.css({left: arrowOffsetX + 'px', top: arrowOffsetY + 'px'}); - }, 0); + /* eslint no-bitwise: ["error"] */ + }); } function val (name, value, context) { const set = value !== undefined; if (!set) { - if (name === undefined || name == null) name = 'xy'; + if (isNullish(name)) name = 'xy'; switch (name.toLowerCase()) { case 'x': return x; case 'y': return y; @@ -156,11 +185,11 @@ const jPicker = function ($) { default: return {x, y}; } } - if (context != null && context === $this) return; + if (!isNullish(context) && context === that) return; let changed = false; let newX, newY; - if (name == null) name = 'xy'; + if (isNullish(name)) name = 'xy'; switch (name.toLowerCase()) { case 'x': newX = (value && ((value.x && value.x | 0) || value | 0)) || 0; @@ -174,7 +203,7 @@ const jPicker = function ($) { newY = (value && value.y && value.y | 0) || 0; break; } - if (newX != null) { + if (!isNullish(newX)) { if (newX < minX) newX = minX; else if (newX > maxX) newX = maxX; if (x !== newX) { @@ -182,7 +211,7 @@ const jPicker = function ($) { changed = true; } } - if (newY != null) { + if (!isNullish(newY)) { if (newY < minY) newY = minY; else if (newY > maxY) newY = maxY; if (y !== newY) { @@ -190,12 +219,12 @@ const jPicker = function ($) { changed = true; } } - changed && fireChangeEvents.call($this, context || $this); + changed && fireChangeEvents.call(that, context || that); } function range (name, value) { const set = value !== undefined; if (!set) { - if (name === undefined || name == null) name = 'all'; + if (isNullish(name)) name = 'all'; switch (name.toLowerCase()) { case 'minx': return minX; case 'maxx': return maxX; @@ -212,7 +241,8 @@ const jPicker = function ($) { newMaxX, newMinY, newMaxY; - if (name == null) name = 'all'; + if (isNullish(name)) name = 'all'; + /* eslint-disable no-bitwise */ switch (name.toLowerCase()) { case 'minx': newMinX = (value && ((value.minX && value.minX | 0) || value | 0)) || 0; @@ -242,19 +272,21 @@ const jPicker = function ($) { newMaxY = (value && value.maxY && value.maxY | 0) || 0; break; } - if (newMinX != null && minX !== newMinX) { + + /* eslint no-bitwise: ["error"] */ + if (!isNullish(newMinX) && minX !== newMinX) { minX = newMinX; rangeX = maxX - minX; } - if (newMaxX != null && maxX !== newMaxX) { + if (!isNullish(newMaxX) && maxX !== newMaxX) { maxX = newMaxX; rangeX = maxX - minX; } - if (newMinY != null && minY !== newMinY) { + if (!isNullish(newMinY) && minY !== newMinY) { minY = newMinY; rangeY = maxY - minY; } - if (newMaxY != null && maxY !== newMaxY) { + if (!isNullish(newMaxY) && maxY !== newMaxY) { maxY = newMaxY; rangeY = maxY - minY; } @@ -288,7 +320,11 @@ const jPicker = function ($) { arrow = bar.find('img:first'), // the arrow image to drag changeEvents = []; - $.extend(true, $this, // public properties, methods, and event bindings - these we need to access from other controls + $.extend( + true, + // public properties, methods, and event bindings - these we need + // to access from other controls + that, { val, range, @@ -305,26 +341,26 @@ const jPicker = function ($) { bar.h = (options.map && options.map.height) || bar.height(); // bind mousedown event bar.bind('mousedown', mouseDown); - bind.call($this, draw); + bind.call(that, draw); } } // controls for all the input elements for the typing in color values function ColorValuePicker (picker, color, bindedHex, alphaPrecision) { - const $this = this; // private properties and methods + const that = this; // private properties and methods const inputs = picker.find('td.Text input'); // input box key down - use arrows to alter color function keyDown (e) { - if (e.target.value === '' && e.target !== hex.get(0) && ((bindedHex != null && e.target !== bindedHex.get(0)) || bindedHex == null)) return; + if (e.target.value === '' && e.target !== hex.get(0) && ((!isNullish(bindedHex) && e.target !== bindedHex.get(0)) || isNullish(bindedHex))) return; if (!validateKey(e)) return e; switch (e.target) { case red.get(0): switch (e.keyCode) { case 38: - red.val(setValueInRange.call($this, (red.val() << 0) + 1, 0, 255)); + red.val(setValueInRange.call(that, (red.val() << 0) + 1, 0, 255)); color.val('r', red.val(), e.target); return false; case 40: - red.val(setValueInRange.call($this, (red.val() << 0) - 1, 0, 255)); + red.val(setValueInRange.call(that, (red.val() << 0) - 1, 0, 255)); color.val('r', red.val(), e.target); return false; } @@ -332,11 +368,11 @@ const jPicker = function ($) { case green.get(0): switch (e.keyCode) { case 38: - green.val(setValueInRange.call($this, (green.val() << 0) + 1, 0, 255)); + green.val(setValueInRange.call(that, (green.val() << 0) + 1, 0, 255)); color.val('g', green.val(), e.target); return false; case 40: - green.val(setValueInRange.call($this, (green.val() << 0) - 1, 0, 255)); + green.val(setValueInRange.call(that, (green.val() << 0) - 1, 0, 255)); color.val('g', green.val(), e.target); return false; } @@ -344,11 +380,11 @@ const jPicker = function ($) { case blue.get(0): switch (e.keyCode) { case 38: - blue.val(setValueInRange.call($this, (blue.val() << 0) + 1, 0, 255)); + blue.val(setValueInRange.call(that, (blue.val() << 0) + 1, 0, 255)); color.val('b', blue.val(), e.target); return false; case 40: - blue.val(setValueInRange.call($this, (blue.val() << 0) - 1, 0, 255)); + blue.val(setValueInRange.call(that, (blue.val() << 0) - 1, 0, 255)); color.val('b', blue.val(), e.target); return false; } @@ -356,11 +392,11 @@ const jPicker = function ($) { case alpha && alpha.get(0): switch (e.keyCode) { case 38: - alpha.val(setValueInRange.call($this, parseFloat(alpha.val()) + 1, 0, 100)); + alpha.val(setValueInRange.call(that, parseFloat(alpha.val()) + 1, 0, 100)); color.val('a', toFixedNumeric((alpha.val() * 255) / 100, alphaPrecision), e.target); return false; case 40: - alpha.val(setValueInRange.call($this, parseFloat(alpha.val()) - 1, 0, 100)); + alpha.val(setValueInRange.call(that, parseFloat(alpha.val()) - 1, 0, 100)); color.val('a', toFixedNumeric((alpha.val() * 255) / 100, alphaPrecision), e.target); return false; } @@ -368,11 +404,11 @@ const jPicker = function ($) { case hue.get(0): switch (e.keyCode) { case 38: - hue.val(setValueInRange.call($this, (hue.val() << 0) + 1, 0, 360)); + hue.val(setValueInRange.call(that, (hue.val() << 0) + 1, 0, 360)); color.val('h', hue.val(), e.target); return false; case 40: - hue.val(setValueInRange.call($this, (hue.val() << 0) - 1, 0, 360)); + hue.val(setValueInRange.call(that, (hue.val() << 0) - 1, 0, 360)); color.val('h', hue.val(), e.target); return false; } @@ -380,11 +416,11 @@ const jPicker = function ($) { case saturation.get(0): switch (e.keyCode) { case 38: - saturation.val(setValueInRange.call($this, (saturation.val() << 0) + 1, 0, 100)); + saturation.val(setValueInRange.call(that, (saturation.val() << 0) + 1, 0, 100)); color.val('s', saturation.val(), e.target); return false; case 40: - saturation.val(setValueInRange.call($this, (saturation.val() << 0) - 1, 0, 100)); + saturation.val(setValueInRange.call(that, (saturation.val() << 0) - 1, 0, 100)); color.val('s', saturation.val(), e.target); return false; } @@ -392,11 +428,11 @@ const jPicker = function ($) { case value.get(0): switch (e.keyCode) { case 38: - value.val(setValueInRange.call($this, (value.val() << 0) + 1, 0, 100)); + value.val(setValueInRange.call(that, (value.val() << 0) + 1, 0, 100)); color.val('v', value.val(), e.target); return false; case 40: - value.val(setValueInRange.call($this, (value.val() << 0) - 1, 0, 100)); + value.val(setValueInRange.call(that, (value.val() << 0) - 1, 0, 100)); color.val('v', value.val(), e.target); return false; } @@ -406,36 +442,36 @@ const jPicker = function ($) { // input box key up - validate value and set color function keyUp (e) { if (e.target.value === '' && e.target !== hex.get(0) && - ((bindedHex != null && e.target !== bindedHex.get(0)) || - bindedHex == null)) return; + ((!isNullish(bindedHex) && e.target !== bindedHex.get(0)) || + isNullish(bindedHex))) return; if (!validateKey(e)) return e; switch (e.target) { case red.get(0): - red.val(setValueInRange.call($this, red.val(), 0, 255)); + red.val(setValueInRange.call(that, red.val(), 0, 255)); color.val('r', red.val(), e.target); break; case green.get(0): - green.val(setValueInRange.call($this, green.val(), 0, 255)); + green.val(setValueInRange.call(that, green.val(), 0, 255)); color.val('g', green.val(), e.target); break; case blue.get(0): - blue.val(setValueInRange.call($this, blue.val(), 0, 255)); + blue.val(setValueInRange.call(that, blue.val(), 0, 255)); color.val('b', blue.val(), e.target); break; case alpha && alpha.get(0): - alpha.val(setValueInRange.call($this, alpha.val(), 0, 100)); + alpha.val(setValueInRange.call(that, alpha.val(), 0, 100)); color.val('a', toFixedNumeric((alpha.val() * 255) / 100, alphaPrecision), e.target); break; case hue.get(0): - hue.val(setValueInRange.call($this, hue.val(), 0, 360)); + hue.val(setValueInRange.call(that, hue.val(), 0, 360)); color.val('h', hue.val(), e.target); break; case saturation.get(0): - saturation.val(setValueInRange.call($this, saturation.val(), 0, 100)); + saturation.val(setValueInRange.call(that, saturation.val(), 0, 100)); color.val('s', saturation.val(), e.target); break; case value.get(0): - value.val(setValueInRange.call($this, value.val(), 0, 100)); + value.val(setValueInRange.call(that, value.val(), 0, 100)); color.val('v', value.val(), e.target); break; case hex.get(0): @@ -450,13 +486,13 @@ const jPicker = function ($) { break; case ahex && ahex.get(0): ahex.val(ahex.val().replace(/[^a-fA-F0-9]/g, '').toLowerCase().substring(0, 2)); - color.val('a', ahex.val() != null ? parseInt(ahex.val(), 16) : null, e.target); + color.val('a', !isNullish(ahex.val()) ? parseInt(ahex.val(), 16) : null, e.target); break; } } // input box blur - reset to original if value empty function blur (e) { - if (color.val() != null) { + if (!isNullish(color.val())) { switch (e.target) { case red.get(0): red.val(color.val('r')); break; case green.get(0): green.val(color.val('g')); break; @@ -497,16 +533,16 @@ const jPicker = function ($) { } function colorChanged (ui, context) { const all = ui.val('all'); - if (context !== red.get(0)) red.val(all != null ? all.r : ''); - if (context !== green.get(0)) green.val(all != null ? all.g : ''); - if (context !== blue.get(0)) blue.val(all != null ? all.b : ''); - if (alpha && context !== alpha.get(0)) alpha.val(all != null ? toFixedNumeric((all.a * 100) / 255, alphaPrecision) : ''); - if (context !== hue.get(0)) hue.val(all != null ? all.h : ''); - if (context !== saturation.get(0)) saturation.val(all != null ? all.s : ''); - if (context !== value.get(0)) value.val(all != null ? all.v : ''); - if (context !== hex.get(0) && ((bindedHex && context !== bindedHex.get(0)) || !bindedHex)) hex.val(all != null ? all.hex : ''); - if (bindedHex && context !== bindedHex.get(0) && context !== hex.get(0)) bindedHex.val(all != null ? all.hex : ''); - if (ahex && context !== ahex.get(0)) ahex.val(all != null ? all.ahex.substring(6) : ''); + if (context !== red.get(0)) red.val(!isNullish(all) ? all.r : ''); + if (context !== green.get(0)) green.val(!isNullish(all) ? all.g : ''); + if (context !== blue.get(0)) blue.val(!isNullish(all) ? all.b : ''); + if (alpha && context !== alpha.get(0)) alpha.val(!isNullish(all) ? toFixedNumeric((all.a * 100) / 255, alphaPrecision) : ''); + if (context !== hue.get(0)) hue.val(!isNullish(all) ? all.h : ''); + if (context !== saturation.get(0)) saturation.val(!isNullish(all) ? all.s : ''); + if (context !== value.get(0)) value.val(!isNullish(all) ? all.v : ''); + if (context !== hex.get(0) && ((bindedHex && context !== bindedHex.get(0)) || !bindedHex)) hex.val(!isNullish(all) ? all.hex : ''); + if (bindedHex && context !== bindedHex.get(0) && context !== hex.get(0)) bindedHex.val(!isNullish(all) ? all.hex : ''); + if (ahex && context !== ahex.get(0)) ahex.val(!isNullish(all) ? all.ahex.substring(6) : ''); } function destroy () { // unbind all events and null objects @@ -533,7 +569,7 @@ const jPicker = function ($) { value = inputs.eq(2), hex = inputs.eq(inputs.length > 7 ? 7 : 6), ahex = inputs.length > 7 ? inputs.eq(8) : null; - $.extend(true, $this, { + $.extend(true, that, { // public properties and methods destroy }); @@ -544,15 +580,15 @@ const jPicker = function ($) { /** * @typedef {PlainObject} module:jPicker.JPickerInit - * @property {Integer} a - * @property {Integer} b - * @property {Integer} g - * @property {Integer} h - * @property {Integer} r - * @property {Integer} s - * @property {Integer} v - * @property {string} hex - * @property {string} ahex + * @property {Integer} [a] + * @property {Integer} [b] + * @property {Integer} [g] + * @property {Integer} [h] + * @property {Integer} [r] + * @property {Integer} [s] + * @property {Integer} [v] + * @property {string} [hex] + * @property {string} [ahex] */ /** @@ -577,21 +613,21 @@ const jPicker = function ($) { * @param {module:jPicker.JPickerInit} init */ Color: function (init) { - const $this = this; + const that = this; function fireChangeEvents (context) { - for (let i = 0; i < changeEvents.length; i++) changeEvents[i].call($this, $this, context); + for (let i = 0; i < changeEvents.length; i++) changeEvents[i].call(that, that, context); } function val (name, value, context) { // Kind of ugly const set = Boolean(value); if (set && value.ahex === '') value.ahex = '00000000'; if (!set) { - if (name === undefined || name == null || name === '') name = 'all'; - if (r == null) return null; + if (isNullish(name) || name === '') name = 'all'; + if (isNullish(r)) return null; switch (name.toLowerCase()) { case 'ahex': return ColorMethods.rgbaToHex({r, g, b, a}); case 'hex': return val('ahex').substring(0, 6); - case 'all': return {r, g, b, a, h, s, v, hex: val.call($this, 'hex'), ahex: val.call($this, 'ahex')}; + case 'all': return {r, g, b, a, h, s, v, hex: val.call(that, 'hex'), ahex: val.call(that, 'ahex')}; default: let ret = {}; const nameLength = name.length; @@ -628,55 +664,55 @@ const jPicker = function ($) { } } return typeof ret === 'object' && !Object.keys(ret).length - ? val.call($this, 'all') + ? val.call(that, 'all') : ret; } } - if (context != null && context === $this) return; - if (name == null) name = ''; + if (!isNullish(context) && context === that) return; + if (isNullish(name)) name = ''; let changed = false; - if (value == null) { - if (r != null) { + if (isNullish(value)) { + if (!isNullish(r)) { r = null; changed = true; } - if (g != null) { + if (!isNullish(g)) { g = null; changed = true; } - if (b != null) { + if (!isNullish(b)) { b = null; changed = true; } - if (a != null) { + if (!isNullish(a)) { a = null; changed = true; } - if (h != null) { + if (!isNullish(h)) { h = null; changed = true; } - if (s != null) { + if (!isNullish(s)) { s = null; changed = true; } - if (v != null) { + if (!isNullish(v)) { v = null; changed = true; } - changed && fireChangeEvents.call($this, context || $this); + changed && fireChangeEvents.call(that, context || that); return; } switch (name.toLowerCase()) { case 'ahex': - case 'hex': + case 'hex': { const ret = ColorMethods.hexToRgba((value && (value.ahex || value.hex)) || value || 'none'); - val.call($this, 'rgba', {r: ret.r, g: ret.g, b: ret.b, a: name === 'ahex' ? ret.a : a != null ? a : 255}, context); + val.call(that, 'rgba', {r: ret.r, g: ret.g, b: ret.b, a: name === 'ahex' ? ret.a : !isNullish(a) ? a : 255}, context); break; - default: - if (value && (value.ahex != null || value.hex != null)) { - val.call($this, 'ahex', value.ahex || value.hex || '00000000', context); + } default: { + if (value && (!isNullish(value.ahex) || !isNullish(value.hex))) { + val.call(that, 'ahex', value.ahex || value.hex || '00000000', context); return; } const newV = {}; @@ -724,7 +760,7 @@ const jPicker = function ($) { } break; case 'a': - newV.a = value && value.a != null ? value.a | 0 : value | 0; + newV.a = value && !isNullish(value.a) ? value.a | 0 : value | 0; if (newV.a < 0) newV.a = 0; else if (newV.a > 255) newV.a = 255; if (a !== newV.a) { @@ -746,7 +782,7 @@ const jPicker = function ($) { case 's': if (rgb) continue; hsv = true; - newV.s = value.s != null ? value.s | 0 : value | 0; + newV.s = !isNullish(value.s) ? value.s | 0 : value | 0; if (newV.s < 0) newV.s = 0; else if (newV.s > 100) newV.s = 100; if (s !== newV.s) { @@ -757,7 +793,7 @@ const jPicker = function ($) { case 'v': if (rgb) continue; hsv = true; - newV.v = value.v != null ? value.v | 0 : value | 0; + newV.v = !isNullish(value.v) ? value.v | 0 : value | 0; if (newV.v < 0) newV.v = 0; else if (newV.v > 100) newV.v = 100; if (v !== newV.v) { @@ -776,16 +812,17 @@ const jPicker = function ($) { ({h, s, v} = ret); } else if (hsv) { h = h || 0; - s = s != null ? s : 100; - v = v != null ? v : 100; + s = !isNullish(s) ? s : 100; + v = !isNullish(v) ? v : 100; const ret = ColorMethods.hsvToRgb({h, s, v}); ({r, g, b} = ret); } - a = a != null ? a : 255; - fireChangeEvents.call($this, context || $this); + a = !isNullish(a) ? a : 255; + fireChangeEvents.call(that, context || that); } break; } + } } function bind (callback) { if (typeof callback === 'function') changeEvents.push(callback); @@ -802,7 +839,7 @@ const jPicker = function ($) { } let r, g, b, a, h, s, v, changeEvents = []; - $.extend(true, $this, { + $.extend(true, that, { // public properties and methods val, bind, @@ -810,19 +847,19 @@ const jPicker = function ($) { destroy }); if (init) { - if (init.ahex != null) { + if (!isNullish(init.ahex)) { val('ahex', init); - } else if (init.hex != null) { + } else if (!isNullish(init.hex)) { val( - (init.a != null ? 'a' : '') + 'hex', - init.a != null + (!isNullish(init.a) ? 'a' : '') + 'hex', + !isNullish(init.a) ? {ahex: init.hex + ColorMethods.intToHex(init.a)} : init ); - } else if (init.r != null && init.g != null && init.b != null) { - val('rgb' + (init.a != null ? 'a' : ''), init); - } else if (init.h != null && init.s != null && init.v != null) { - val('hsv' + (init.a != null ? 'a' : ''), init); + } else if (!isNullish(init.r) && !isNullish(init.g) && !isNullish(init.b)) { + val('rgb' + (!isNullish(init.a) ? 'a' : ''), init); + } else if (!isNullish(init.h) && !isNullish(init.s) && !isNullish(init.v)) { + val('hsv' + (!isNullish(init.a) ? 'a' : ''), init); } } }, @@ -958,8 +995,8 @@ const jPicker = function ($) { } else { if (h === 360) h = 0; h /= 60; - s = s / 100; - v = v / 100; + s /= 100; + v /= 100; const i = h | 0, f = h - i, p = v * (1 - s), @@ -1017,30 +1054,33 @@ const jPicker = function ($) { * @namespace * @memberof external:jQuery.fn * @param {external:jQuery.fn.jPickerOptions} options + * @param {function} [commitCallback] + * @param {function} [liveCallback] + * @param {function} [cancelCallback] * @returns {external:jQuery} */ - $.fn.jPicker = function (options) { - const $arguments = arguments; + $.fn.jPicker = function (options, commitCallback, liveCallback, cancelCallback) { return this.each(function () { - const $this = this, settings = $.extend(true, {}, $.fn.jPicker.defaults, options); // local copies for YUI compressor - if ($($this).get(0).nodeName.toLowerCase() === 'input') { // Add color picker icon if binding to an input element and bind the events to the input + const that = this, + settings = $.extend(true, {}, $.fn.jPicker.defaults, options); // local copies for YUI compressor + if ($(that).get(0).nodeName.toLowerCase() === 'input') { // Add color picker icon if binding to an input element and bind the events to the input $.extend(true, settings, { window: { bindToInput: true, expandable: true, - input: $($this) + input: $(that) } }); - if ($($this).val() === '') { + if ($(that).val() === '') { settings.color.active = new Color({hex: null}); settings.color.current = new Color({hex: null}); - } else if (ColorMethods.validateHex($($this).val())) { - settings.color.active = new Color({hex: $($this).val(), a: settings.color.active.val('a')}); - settings.color.current = new Color({hex: $($this).val(), a: settings.color.active.val('a')}); + } else if (ColorMethods.validateHex($(that).val())) { + settings.color.active = new Color({hex: $(that).val(), a: settings.color.active.val('a')}); + settings.color.current = new Color({hex: $(that).val(), a: settings.color.active.val('a')}); } } if (settings.window.expandable) { - $($this).after('    '); + $(that).after('    '); } else { settings.window.liveUpdate = false; // Basic control binding for inline use - You will need to override the liveCallback or commitCallback function to retrieve results } @@ -1061,60 +1101,60 @@ const jPicker = function ($) { switch (colorMode) { case 'h': setTimeout(function () { - setBG.call($this, colorMapDiv, 'transparent'); - setImgLoc.call($this, colorMapL1, 0); - setAlpha.call($this, colorMapL1, 100); - setImgLoc.call($this, colorMapL2, 260); - setAlpha.call($this, colorMapL2, 100); - setBG.call($this, colorBarDiv, 'transparent'); - setImgLoc.call($this, colorBarL1, 0); - setAlpha.call($this, colorBarL1, 100); - setImgLoc.call($this, colorBarL2, 260); - setAlpha.call($this, colorBarL2, 100); - setImgLoc.call($this, colorBarL3, 260); - setAlpha.call($this, colorBarL3, 100); - setImgLoc.call($this, colorBarL4, 260); - setAlpha.call($this, colorBarL4, 100); - setImgLoc.call($this, colorBarL6, 260); - setAlpha.call($this, colorBarL6, 100); + setBG.call(that, colorMapDiv, 'transparent'); + setImgLoc.call(that, colorMapL1, 0); + setAlpha.call(that, colorMapL1, 100); + setImgLoc.call(that, colorMapL2, 260); + setAlpha.call(that, colorMapL2, 100); + setBG.call(that, colorBarDiv, 'transparent'); + setImgLoc.call(that, colorBarL1, 0); + setAlpha.call(that, colorBarL1, 100); + setImgLoc.call(that, colorBarL2, 260); + setAlpha.call(that, colorBarL2, 100); + setImgLoc.call(that, colorBarL3, 260); + setAlpha.call(that, colorBarL3, 100); + setImgLoc.call(that, colorBarL4, 260); + setAlpha.call(that, colorBarL4, 100); + setImgLoc.call(that, colorBarL6, 260); + setAlpha.call(that, colorBarL6, 100); }, 0); colorMap.range('all', {minX: 0, maxX: 100, minY: 0, maxY: 100}); colorBar.range('rangeY', {minY: 0, maxY: 360}); - if (active.val('ahex') == null) break; + if (isNullish(active.val('ahex'))) break; colorMap.val('xy', {x: active.val('s'), y: 100 - active.val('v')}, colorMap); colorBar.val('y', 360 - active.val('h'), colorBar); break; case 's': setTimeout(function () { - setBG.call($this, colorMapDiv, 'transparent'); - setImgLoc.call($this, colorMapL1, -260); - setImgLoc.call($this, colorMapL2, -520); - setImgLoc.call($this, colorBarL1, -260); - setImgLoc.call($this, colorBarL2, -520); - setImgLoc.call($this, colorBarL6, 260); - setAlpha.call($this, colorBarL6, 100); + setBG.call(that, colorMapDiv, 'transparent'); + setImgLoc.call(that, colorMapL1, -260); + setImgLoc.call(that, colorMapL2, -520); + setImgLoc.call(that, colorBarL1, -260); + setImgLoc.call(that, colorBarL2, -520); + setImgLoc.call(that, colorBarL6, 260); + setAlpha.call(that, colorBarL6, 100); }, 0); colorMap.range('all', {minX: 0, maxX: 360, minY: 0, maxY: 100}); colorBar.range('rangeY', {minY: 0, maxY: 100}); - if (active.val('ahex') == null) break; + if (isNullish(active.val('ahex'))) break; colorMap.val('xy', {x: active.val('h'), y: 100 - active.val('v')}, colorMap); colorBar.val('y', 100 - active.val('s'), colorBar); break; case 'v': setTimeout(function () { - setBG.call($this, colorMapDiv, '000000'); - setImgLoc.call($this, colorMapL1, -780); - setImgLoc.call($this, colorMapL2, 260); - setBG.call($this, colorBarDiv, hex); - setImgLoc.call($this, colorBarL1, -520); - setImgLoc.call($this, colorBarL2, 260); - setAlpha.call($this, colorBarL2, 100); - setImgLoc.call($this, colorBarL6, 260); - setAlpha.call($this, colorBarL6, 100); + setBG.call(that, colorMapDiv, '000000'); + setImgLoc.call(that, colorMapL1, -780); + setImgLoc.call(that, colorMapL2, 260); + setBG.call(that, colorBarDiv, hex); + setImgLoc.call(that, colorBarL1, -520); + setImgLoc.call(that, colorBarL2, 260); + setAlpha.call(that, colorBarL2, 100); + setImgLoc.call(that, colorBarL6, 260); + setAlpha.call(that, colorBarL6, 100); }, 0); colorMap.range('all', {minX: 0, maxX: 360, minY: 0, maxY: 100}); colorBar.range('rangeY', {minY: 0, maxY: 100}); - if (active.val('ahex') == null) break; + if (isNullish(active.val('ahex'))) break; colorMap.val('xy', {x: active.val('h'), y: 100 - active.val('s')}, colorMap); colorBar.val('y', 100 - active.val('v'), colorBar); break; @@ -1123,7 +1163,7 @@ const jPicker = function ($) { rgbBar = -780; colorMap.range('all', {minX: 0, maxX: 255, minY: 0, maxY: 255}); colorBar.range('rangeY', {minY: 0, maxY: 255}); - if (active.val('ahex') == null) break; + if (isNullish(active.val('ahex'))) break; colorMap.val('xy', {x: active.val('b'), y: 255 - active.val('g')}, colorMap); colorBar.val('y', 255 - active.val('r'), colorBar); break; @@ -1132,7 +1172,7 @@ const jPicker = function ($) { rgbBar = -1820; colorMap.range('all', {minX: 0, maxX: 255, minY: 0, maxY: 255}); colorBar.range('rangeY', {minY: 0, maxY: 255}); - if (active.val('ahex') == null) break; + if (isNullish(active.val('ahex'))) break; colorMap.val('xy', {x: active.val('b'), y: 255 - active.val('r')}, colorMap); colorBar.val('y', 255 - active.val('g'), colorBar); break; @@ -1141,24 +1181,24 @@ const jPicker = function ($) { rgbBar = -2860; colorMap.range('all', {minX: 0, maxX: 255, minY: 0, maxY: 255}); colorBar.range('rangeY', {minY: 0, maxY: 255}); - if (active.val('ahex') == null) break; + if (isNullish(active.val('ahex'))) break; colorMap.val('xy', {x: active.val('r'), y: 255 - active.val('g')}, colorMap); colorBar.val('y', 255 - active.val('b'), colorBar); break; case 'a': setTimeout(function () { - setBG.call($this, colorMapDiv, 'transparent'); - setImgLoc.call($this, colorMapL1, -260); - setImgLoc.call($this, colorMapL2, -520); - setImgLoc.call($this, colorBarL1, 260); - setImgLoc.call($this, colorBarL2, 260); - setAlpha.call($this, colorBarL2, 100); - setImgLoc.call($this, colorBarL6, 0); - setAlpha.call($this, colorBarL6, 100); + setBG.call(that, colorMapDiv, 'transparent'); + setImgLoc.call(that, colorMapL1, -260); + setImgLoc.call(that, colorMapL2, -520); + setImgLoc.call(that, colorBarL1, 260); + setImgLoc.call(that, colorBarL2, 260); + setAlpha.call(that, colorBarL2, 100); + setImgLoc.call(that, colorBarL6, 0); + setAlpha.call(that, colorBarL6, 100); }, 0); colorMap.range('all', {minX: 0, maxX: 360, minY: 0, maxY: 100}); colorBar.range('rangeY', {minY: 0, maxY: 255}); - if (active.val('ahex') == null) break; + if (isNullish(active.val('ahex'))) break; colorMap.val('xy', {x: active.val('h'), y: 100 - active.val('v')}, colorMap); colorBar.val('y', 255 - active.val('a'), colorBar); break; @@ -1172,49 +1212,49 @@ const jPicker = function ($) { case 'v': case 'a': setTimeout(function () { - setAlpha.call($this, colorMapL1, 100); - setAlpha.call($this, colorBarL1, 100); - setImgLoc.call($this, colorBarL3, 260); - setAlpha.call($this, colorBarL3, 100); - setImgLoc.call($this, colorBarL4, 260); - setAlpha.call($this, colorBarL4, 100); + setAlpha.call(that, colorMapL1, 100); + setAlpha.call(that, colorBarL1, 100); + setImgLoc.call(that, colorBarL3, 260); + setAlpha.call(that, colorBarL3, 100); + setImgLoc.call(that, colorBarL4, 260); + setAlpha.call(that, colorBarL4, 100); }, 0); break; case 'r': case 'g': case 'b': setTimeout(function () { - setBG.call($this, colorMapDiv, 'transparent'); - setBG.call($this, colorBarDiv, 'transparent'); - setAlpha.call($this, colorBarL1, 100); - setAlpha.call($this, colorMapL1, 100); - setImgLoc.call($this, colorMapL1, rgbMap); - setImgLoc.call($this, colorMapL2, rgbMap - 260); - setImgLoc.call($this, colorBarL1, rgbBar - 780); - setImgLoc.call($this, colorBarL2, rgbBar - 520); - setImgLoc.call($this, colorBarL3, rgbBar); - setImgLoc.call($this, colorBarL4, rgbBar - 260); - setImgLoc.call($this, colorBarL6, 260); - setAlpha.call($this, colorBarL6, 100); + setBG.call(that, colorMapDiv, 'transparent'); + setBG.call(that, colorBarDiv, 'transparent'); + setAlpha.call(that, colorBarL1, 100); + setAlpha.call(that, colorMapL1, 100); + setImgLoc.call(that, colorMapL1, rgbMap); + setImgLoc.call(that, colorMapL2, rgbMap - 260); + setImgLoc.call(that, colorBarL1, rgbBar - 780); + setImgLoc.call(that, colorBarL2, rgbBar - 520); + setImgLoc.call(that, colorBarL3, rgbBar); + setImgLoc.call(that, colorBarL4, rgbBar - 260); + setImgLoc.call(that, colorBarL6, 260); + setAlpha.call(that, colorBarL6, 100); }, 0); break; } - if (active.val('ahex') == null) return; - activeColorChanged.call($this, active); + if (isNullish(active.val('ahex'))) return; + activeColorChanged.call(that, active); } // Update color when user changes text values function activeColorChanged (ui, context) { - if (context == null || (context !== colorBar && context !== colorMap)) positionMapAndBarArrows.call($this, ui, context); + if (isNullish(context) || (context !== colorBar && context !== colorMap)) positionMapAndBarArrows.call(that, ui, context); setTimeout(function () { - updatePreview.call($this, ui); - updateMapVisuals.call($this, ui); - updateBarVisuals.call($this, ui); + updatePreview.call(that, ui); + updateMapVisuals.call(that, ui); + updateBarVisuals.call(that, ui); }, 0); } // user has dragged the ColorMap pointer function mapValueChanged (ui, context) { const {active} = color; - if (context !== colorMap && active.val() == null) return; + if (context !== colorMap && isNullish(active.val())) return; const xy = ui.val('all'); switch (settings.color.mode) { case 'h': @@ -1241,7 +1281,7 @@ const jPicker = function ($) { // user has dragged the ColorBar slider function colorBarValueChanged (ui, context) { const {active} = color; - if (context !== colorBar && active.val() == null) return; + if (context !== colorBar && isNullish(active.val())) return; switch (settings.color.mode) { case 'h': active.val('h', {h: 360 - ui.val('y')}, context); @@ -1270,47 +1310,50 @@ const jPicker = function ($) { function positionMapAndBarArrows (ui, context) { if (context !== colorMap) { switch (settings.color.mode) { - case 'h': + case 'h': { const sv = ui.val('sv'); - colorMap.val('xy', {x: sv != null ? sv.s : 100, y: 100 - (sv != null ? sv.v : 100)}, context); + colorMap.val('xy', {x: !isNullish(sv) ? sv.s : 100, y: 100 - (!isNullish(sv) ? sv.v : 100)}, context); break; - case 's': - case 'a': + } case 's': { // eslint-disable-line no-empty + } + // Fall through + case 'a': { const hv = ui.val('hv'); - colorMap.val('xy', {x: (hv && hv.h) || 0, y: 100 - (hv != null ? hv.v : 100)}, context); + colorMap.val('xy', {x: (hv && hv.h) || 0, y: 100 - (!isNullish(hv) ? hv.v : 100)}, context); break; - case 'v': + } case 'v': { const hs = ui.val('hs'); - colorMap.val('xy', {x: (hs && hs.h) || 0, y: 100 - (hs != null ? hs.s : 100)}, context); + colorMap.val('xy', {x: (hs && hs.h) || 0, y: 100 - (!isNullish(hs) ? hs.s : 100)}, context); break; - case 'r': + } case 'r': { const bg = ui.val('bg'); colorMap.val('xy', {x: (bg && bg.b) || 0, y: 255 - ((bg && bg.g) || 0)}, context); break; - case 'g': + } case 'g': { const br = ui.val('br'); colorMap.val('xy', {x: (br && br.b) || 0, y: 255 - ((br && br.r) || 0)}, context); break; - case 'b': + } case 'b': { const rg = ui.val('rg'); colorMap.val('xy', {x: (rg && rg.r) || 0, y: 255 - ((rg && rg.g) || 0)}, context); break; } + } } if (context !== colorBar) { switch (settings.color.mode) { case 'h': colorBar.val('y', 360 - (ui.val('h') || 0), context); break; - case 's': + case 's': { const s = ui.val('s'); - colorBar.val('y', 100 - (s != null ? s : 100), context); + colorBar.val('y', 100 - (!isNullish(s) ? s : 100), context); break; - case 'v': + } case 'v': { const v = ui.val('v'); - colorBar.val('y', 100 - (v != null ? v : 100), context); + colorBar.val('y', 100 - (!isNullish(v) ? v : 100), context); break; - case 'r': + } case 'r': colorBar.val('y', 255 - (ui.val('r') || 0), context); break; case 'g': @@ -1319,69 +1362,70 @@ const jPicker = function ($) { case 'b': colorBar.val('y', 255 - (ui.val('b') || 0), context); break; - case 'a': + case 'a': { const a = ui.val('a'); - colorBar.val('y', 255 - (a != null ? a : 255), context); + colorBar.val('y', 255 - (!isNullish(a) ? a : 255), context); break; } + } } } function updatePreview (ui) { try { const all = ui.val('all'); activePreview.css({backgroundColor: (all && '#' + all.hex) || 'transparent'}); - setAlpha.call($this, activePreview, (all && toFixedNumeric((all.a * 100) / 255, 4)) || 0); + setAlpha.call(that, activePreview, (all && toFixedNumeric((all.a * 100) / 255, 4)) || 0); } catch (e) { } } function updateMapVisuals (ui) { switch (settings.color.mode) { case 'h': - setBG.call($this, colorMapDiv, new Color({h: ui.val('h') || 0, s: 100, v: 100}).val('hex')); + setBG.call(that, colorMapDiv, new Color({h: ui.val('h') || 0, s: 100, v: 100}).val('hex')); break; case 's': - case 'a': + case 'a': { const s = ui.val('s'); - setAlpha.call($this, colorMapL2, 100 - (s != null ? s : 100)); + setAlpha.call(that, colorMapL2, 100 - (!isNullish(s) ? s : 100)); break; - case 'v': + } case 'v': { const v = ui.val('v'); - setAlpha.call($this, colorMapL1, v != null ? v : 100); + setAlpha.call(that, colorMapL1, !isNullish(v) ? v : 100); break; - case 'r': - setAlpha.call($this, colorMapL2, toFixedNumeric((ui.val('r') || 0) / 255 * 100, 4)); + } case 'r': + setAlpha.call(that, colorMapL2, toFixedNumeric((ui.val('r') || 0) / 255 * 100, 4)); break; case 'g': - setAlpha.call($this, colorMapL2, toFixedNumeric((ui.val('g') || 0) / 255 * 100, 4)); + setAlpha.call(that, colorMapL2, toFixedNumeric((ui.val('g') || 0) / 255 * 100, 4)); break; case 'b': - setAlpha.call($this, colorMapL2, toFixedNumeric((ui.val('b') || 0) / 255 * 100)); + setAlpha.call(that, colorMapL2, toFixedNumeric((ui.val('b') || 0) / 255 * 100)); break; } const a = ui.val('a'); - setAlpha.call($this, colorMapL3, toFixedNumeric(((255 - (a || 0)) * 100) / 255, 4)); + setAlpha.call(that, colorMapL3, toFixedNumeric(((255 - (a || 0)) * 100) / 255, 4)); } function updateBarVisuals (ui) { switch (settings.color.mode) { - case 'h': + case 'h': { const a = ui.val('a'); - setAlpha.call($this, colorBarL5, toFixedNumeric(((255 - (a || 0)) * 100) / 255, 4)); + setAlpha.call(that, colorBarL5, toFixedNumeric(((255 - (a || 0)) * 100) / 255, 4)); break; - case 's': + } case 's': { const hva = ui.val('hva'), - saturatedColor = new Color({h: (hva && hva.h) || 0, s: 100, v: hva != null ? hva.v : 100}); - setBG.call($this, colorBarDiv, saturatedColor.val('hex')); - setAlpha.call($this, colorBarL2, 100 - (hva != null ? hva.v : 100)); - setAlpha.call($this, colorBarL5, toFixedNumeric(((255 - ((hva && hva.a) || 0)) * 100) / 255, 4)); + saturatedColor = new Color({h: (hva && hva.h) || 0, s: 100, v: !isNullish(hva) ? hva.v : 100}); + setBG.call(that, colorBarDiv, saturatedColor.val('hex')); + setAlpha.call(that, colorBarL2, 100 - (!isNullish(hva) ? hva.v : 100)); + setAlpha.call(that, colorBarL5, toFixedNumeric(((255 - ((hva && hva.a) || 0)) * 100) / 255, 4)); break; - case 'v': + } case 'v': { const hsa = ui.val('hsa'), - valueColor = new Color({h: (hsa && hsa.h) || 0, s: hsa != null ? hsa.s : 100, v: 100}); - setBG.call($this, colorBarDiv, valueColor.val('hex')); - setAlpha.call($this, colorBarL5, toFixedNumeric(((255 - ((hsa && hsa.a) || 0)) * 100) / 255, 4)); + valueColor = new Color({h: (hsa && hsa.h) || 0, s: !isNullish(hsa) ? hsa.s : 100, v: 100}); + setBG.call(that, colorBarDiv, valueColor.val('hex')); + setAlpha.call(that, colorBarL5, toFixedNumeric(((255 - ((hsa && hsa.a) || 0)) * 100) / 255, 4)); break; - case 'r': + } case 'r': case 'g': - case 'b': + case 'b': { const rgba = ui.val('rgba'); let hValue = 0, vValue = 0; if (settings.color.mode === 'r') { @@ -1395,16 +1439,16 @@ const jPicker = function ($) { vValue = (rgba && rgba.g) || 0; } const middle = vValue > hValue ? hValue : vValue; - setAlpha.call($this, colorBarL2, hValue > vValue ? toFixedNumeric(((hValue - vValue) / (255 - vValue)) * 100, 4) : 0); - setAlpha.call($this, colorBarL3, vValue > hValue ? toFixedNumeric(((vValue - hValue) / (255 - hValue)) * 100, 4) : 0); - setAlpha.call($this, colorBarL4, toFixedNumeric((middle / 255) * 100, 4)); - setAlpha.call($this, colorBarL5, toFixedNumeric(((255 - ((rgba && rgba.a) || 0)) * 100) / 255, 4)); + setAlpha.call(that, colorBarL2, hValue > vValue ? toFixedNumeric(((hValue - vValue) / (255 - vValue)) * 100, 4) : 0); + setAlpha.call(that, colorBarL3, vValue > hValue ? toFixedNumeric(((vValue - hValue) / (255 - hValue)) * 100, 4) : 0); + setAlpha.call(that, colorBarL4, toFixedNumeric((middle / 255) * 100, 4)); + setAlpha.call(that, colorBarL5, toFixedNumeric(((255 - ((rgba && rgba.a) || 0)) * 100) / 255, 4)); break; - case 'a': { + } case 'a': { const a = ui.val('a'); - setBG.call($this, colorBarDiv, ui.val('hex') || '000000'); - setAlpha.call($this, colorBarL5, a != null ? 0 : 100); - setAlpha.call($this, colorBarL6, a != null ? 100 : 0); + setBG.call(that, colorBarDiv, ui.val('hex') || '000000'); + setAlpha.call(that, colorBarL5, !isNullish(a) ? 0 : 100); + setAlpha.call(that, colorBarL6, !isNullish(a) ? 100 : 0); break; } } @@ -1426,7 +1470,7 @@ const jPicker = function ($) { if (alpha > 0 && alpha < 100) { if (isLessThanIE7) { const src = obj.attr('pngSrc'); - if (src != null && ( + if (!isNullish(src) && ( src.includes('AlphaBar.png') || src.includes('Bars.png') || src.includes('Maps.png') )) { obj.css({filter: 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + src + '\', sizingMethod=\'scale\') progid:DXImageTransform.Microsoft.Alpha(opacity=' + alpha + ')'}); @@ -1435,7 +1479,7 @@ const jPicker = function ($) { } else if (alpha === 0 || alpha === 100) { if (isLessThanIE7) { const src = obj.attr('pngSrc'); - if (src != null && ( + if (!isNullish(src) && ( src.includes('AlphaBar.png') || src.includes('Bars.png') || src.includes('Maps.png') )) { obj.css({filter: 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + src + '\', sizingMethod=\'scale\')'}); @@ -1453,38 +1497,38 @@ const jPicker = function ($) { } function radioClicked (e) { $(this).parents('tbody:first').find('input:radio[value!="' + e.target.value + '"]').removeAttr('checked'); - setColorMode.call($this, e.target.value); + setColorMode.call(that, e.target.value); } function currentClicked () { - revertColor.call($this); + revertColor.call(that); } function cancelClicked () { - revertColor.call($this); - settings.window.expandable && hide.call($this); - typeof cancelCallback === 'function' && cancelCallback.call($this, color.active, cancelButton); + revertColor.call(that); + settings.window.expandable && hide.call(that); + typeof cancelCallback === 'function' && cancelCallback.call(that, color.active, cancelButton); } function okClicked () { - commitColor.call($this); - settings.window.expandable && hide.call($this); - typeof commitCallback === 'function' && commitCallback.call($this, color.active, okButton); + commitColor.call(that); + settings.window.expandable && hide.call(that); + typeof commitCallback === 'function' && commitCallback.call(that, color.active, okButton); } function iconImageClicked () { - show.call($this); + show.call(that); } function currentColorChanged (ui, context) { const hex = ui.val('hex'); currentPreview.css({backgroundColor: (hex && '#' + hex) || 'transparent'}); - setAlpha.call($this, currentPreview, toFixedNumeric(((ui.val('a') || 0) * 100) / 255, 4)); + setAlpha.call(that, currentPreview, toFixedNumeric(((ui.val('a') || 0) * 100) / 255, 4)); } function expandableColorChanged (ui, context) { const hex = ui.val('hex'); const va = ui.val('va'); iconColor.css({backgroundColor: (hex && '#' + hex) || 'transparent'}); - setAlpha.call($this, iconAlpha, toFixedNumeric(((255 - ((va && va.a) || 0)) * 100) / 255, 4)); + setAlpha.call(that, iconAlpha, toFixedNumeric(((255 - ((va && va.a) || 0)) * 100) / 255, 4)); if (settings.window.bindToInput && settings.window.updateInputColor) { settings.window.input.css({ backgroundColor: (hex && '#' + hex) || 'transparent', - color: va == null || va.v > 75 ? '#000000' : '#ffffff' + color: isNullish(va) || va.v > 75 ? '#000000' : '#ffffff' }); } } @@ -1564,11 +1608,11 @@ const jPicker = function ($) { } function initialize () { const win = settings.window, - popup = win.expandable ? $($this).next().find('.Container:first') : null; - container = win.expandable ? $('
') : $($this); + popup = win.expandable ? $(that).next().find('.Container:first') : null; + container = win.expandable ? $('
') : $(that); container.addClass('jPicker Container'); if (win.expandable) container.hide(); - container.get(0).onselectstart = function (event) { if (event.target.nodeName.toLowerCase() !== 'input') return false; }; + container.get(0).onselectstart = function (e) { if (e.target.nodeName.toLowerCase() !== 'input') return false; }; // inject html source code - we are using a single table for this control - I know tables are considered bad, but it takes care of equal height columns and // this control really is tabular data, so I believe it is the right move const all = color.active.val('all'); @@ -1585,34 +1629,34 @@ const jPicker = function ($) { -  ° +  ° -  % +  %

-  %

+  %

- + - + - + ${win.alphaSupport ? `` : ' '} - ${win.alphaSupport ? ` %` : ' '} + ${win.alphaSupport ? ` %` : ' '} - ${win.alphaSupport ? `` : ' '} + ${win.alphaSupport ? `` : ' '} `; if (win.expandable) { @@ -1626,7 +1670,8 @@ const jPicker = function ($) { function () { $(document.body).children('div.jPicker.Container').css({zIndex: 10}); container.css({zIndex: 20}); - }); + } + ); container.css( // positions must be set and display set to absolute before source code injection or IE will size the container to fit the window { left: @@ -1647,9 +1692,10 @@ const jPicker = function ($) { : win.position.y === 'bottom' ? (popup.offset().top + 25) + 'px' : (popup.offset().top + parseInt(win.position.y)) + 'px' - }); + } + ); } else { - container = $($this); + container = $(that); container.html(controlHtml); } // initialize the objects to the source code just injected @@ -1668,7 +1714,8 @@ const jPicker = function ($) { colorBarL5 = BarMaps.filter('.Map5:first'); colorBarL6 = BarMaps.filter('.Map6:first'); // create color pickers and maps - colorMap = new Slider(colorMapDiv, + colorMap = new Slider( + colorMapDiv, { map: { width: images.colorMap.width, @@ -1682,7 +1729,8 @@ const jPicker = function ($) { } ); colorMap.bind(mapValueChanged); - colorBar = new Slider(colorBarDiv, + colorBar = new Slider( + colorBarDiv, { map: { width: images.colorBar.width, @@ -1701,26 +1749,26 @@ const jPicker = function ($) { color.active, win.expandable && win.bindToInput ? win.input : null, win.alphaPrecision ); - const hex = all != null ? all.hex : null, + const hex = !isNullish(all) ? all.hex : null, preview = tbody.find('.Preview'), button = tbody.find('.Button'); activePreview = preview.find('.Active:first').css({backgroundColor: (hex && '#' + hex) || 'transparent'}); currentPreview = preview.find('.Current:first').css({backgroundColor: (hex && '#' + hex) || 'transparent'}).bind('click', currentClicked); - setAlpha.call($this, currentPreview, toFixedNumeric((color.current.val('a') * 100) / 255, 4)); + setAlpha.call(that, currentPreview, toFixedNumeric((color.current.val('a') * 100) / 255, 4)); okButton = button.find('.Ok:first').bind('click', okClicked); cancelButton = button.find('.Cancel:first').bind('click', cancelClicked); grid = button.find('.Grid:first'); setTimeout(function () { - setImg.call($this, colorMapL1, images.clientPath + 'Maps.png'); - setImg.call($this, colorMapL2, images.clientPath + 'Maps.png'); - setImg.call($this, colorMapL3, images.clientPath + 'map-opacity.png'); - setImg.call($this, colorBarL1, images.clientPath + 'Bars.png'); - setImg.call($this, colorBarL2, images.clientPath + 'Bars.png'); - setImg.call($this, colorBarL3, images.clientPath + 'Bars.png'); - setImg.call($this, colorBarL4, images.clientPath + 'Bars.png'); - setImg.call($this, colorBarL5, images.clientPath + 'bar-opacity.png'); - setImg.call($this, colorBarL6, images.clientPath + 'AlphaBar.png'); - setImg.call($this, preview.find('div:first'), images.clientPath + 'preview-opacity.png'); + setImg.call(that, colorMapL1, images.clientPath + 'Maps.png'); + setImg.call(that, colorMapL2, images.clientPath + 'Maps.png'); + setImg.call(that, colorMapL3, images.clientPath + 'map-opacity.png'); + setImg.call(that, colorBarL1, images.clientPath + 'Bars.png'); + setImg.call(that, colorBarL2, images.clientPath + 'Bars.png'); + setImg.call(that, colorBarL3, images.clientPath + 'Bars.png'); + setImg.call(that, colorBarL4, images.clientPath + 'Bars.png'); + setImg.call(that, colorBarL5, images.clientPath + 'bar-opacity.png'); + setImg.call(that, colorBarL6, images.clientPath + 'AlphaBar.png'); + setImg.call(that, preview.find('div:first'), images.clientPath + 'preview-opacity.png'); }, 0); tbody.find('td.Radio input').bind('click', radioClicked); // initialize quick list @@ -1736,33 +1784,33 @@ const jPicker = function ($) { if (!ahex) ahex = '00000000'; html += ' '; } - setImg.call($this, grid, images.clientPath + 'bar-opacity.png'); + setImg.call(that, grid, images.clientPath + 'bar-opacity.png'); grid.html(html); grid.find('.QuickColor').click(quickPickClicked); } - setColorMode.call($this, settings.color.mode); + setColorMode.call(that, settings.color.mode); color.active.bind(activeColorChanged); typeof liveCallback === 'function' && color.active.bind(liveCallback); color.current.bind(currentColorChanged); // bind to input if (win.expandable) { - $this.icon = popup.parents('.Icon:first'); - iconColor = $this.icon.find('.Color:first').css({backgroundColor: (hex && '#' + hex) || 'transparent'}); - iconAlpha = $this.icon.find('.Alpha:first'); - setImg.call($this, iconAlpha, images.clientPath + 'bar-opacity.png'); - setAlpha.call($this, iconAlpha, toFixedNumeric(((255 - (all != null ? all.a : 0)) * 100) / 255, 4)); - iconImage = $this.icon.find('.Image:first').css({ + that.icon = popup.parents('.Icon:first'); + iconColor = that.icon.find('.Color:first').css({backgroundColor: (hex && '#' + hex) || 'transparent'}); + iconAlpha = that.icon.find('.Alpha:first'); + setImg.call(that, iconAlpha, images.clientPath + 'bar-opacity.png'); + setAlpha.call(that, iconAlpha, toFixedNumeric(((255 - (!isNullish(all) ? all.a : 0)) * 100) / 255, 4)); + iconImage = that.icon.find('.Image:first').css({ backgroundImage: 'url(\'' + images.clientPath + images.picker.file + '\')' }).bind('click', iconImageClicked); if (win.bindToInput && win.updateInputColor) { win.input.css({ backgroundColor: (hex && '#' + hex) || 'transparent', - color: all == null || all.v > 75 ? '#000000' : '#ffffff' + color: isNullish(all) || all.v > 75 ? '#000000' : '#ffffff' }); } moveBar = tbody.find('.Move:first').bind('mousedown', moveBarMouseDown); color.active.bind(expandableColorChanged); - } else show.call($this); + } else show.call(that); } function destroy () { container.find('td.Radio input').unbind('click', radioClicked); @@ -1772,7 +1820,7 @@ const jPicker = function ($) { if (settings.window.expandable) { iconImage.unbind('click', iconImageClicked); moveBar.unbind('mousedown', moveBarMouseDown); - $this.icon = null; + that.icon = null; } container.find('.QuickColor').unbind('click', quickPickClicked); colorMapDiv = null; @@ -1802,7 +1850,7 @@ const jPicker = function ($) { liveCallback = null; container.html(''); for (let i = 0; i < List.length; i++) { - if (List[i] === $this) { + if (List[i] === that) { List.splice(i, 1); } } @@ -1831,6 +1879,16 @@ const jPicker = function ($) { quickList: settings.color.quickList }; + if (typeof commitCallback !== 'function') { + commitCallback = null; + } + if (typeof liveCallback !== 'function') { + liveCallback = null; + } + if (typeof cancelCallback !== 'function') { + cancelCallback = null; + } + let elementStartX = null, // Used to record the starting css positions for dragging the control elementStartY = null, pageStartX = null, // Used to record the mousedown coordinates for dragging the control @@ -1858,12 +1916,9 @@ const jPicker = function ($) { iconColor = null, // iconColor for popup icon iconAlpha = null, // iconAlpha for popup icon iconImage = null, // iconImage popup icon - moveBar = null, // drag bar - commitCallback = typeof $arguments[1] === 'function' ? $arguments[1] : null, - liveCallback = typeof $arguments[2] === 'function' ? $arguments[2] : null, - cancelCallback = typeof $arguments[3] === 'function' ? $arguments[3] : null; + moveBar = null; // drag bar - $.extend(true, $this, { + $.extend(true, that, { // public properties, methods, and callbacks commitCallback, // commitCallback function can be overridden to return the selected color to a method you specify when the user clicks "OK" liveCallback, // liveCallback function can be overridden to return the selected color to a method you specify in live mode (continuous update) @@ -1873,9 +1928,9 @@ const jPicker = function ($) { hide, destroy // destroys this control entirely, removing all events and objects, and removing itself from the List }); - List.push($this); + List.push(that); setTimeout(function () { - initialize.call($this); + initialize.call(that); }, 0); }); }; diff --git a/editor/jspdf/jspdf.plugin.svgToPdf.js b/editor/jspdf/jspdf.plugin.svgToPdf.js index c608a77f..41c07fe3 100644 --- a/editor/jspdf/jspdf.plugin.svgToPdf.js +++ b/editor/jspdf/jspdf.plugin.svgToPdf.js @@ -106,7 +106,7 @@ const svgElementToPdf = function (element, pdf, options) { pdf.setFillColor(fillRGB.r, fillRGB.g, fillRGB.b); } if (attributeIsNotEmpty(node, 'stroke-width')) { - pdf.setLineWidth(k * parseInt(node.getAttribute('stroke-width'), 10)); + pdf.setLineWidth(k * parseInt(node.getAttribute('stroke-width'))); } const strokeColor = node.getAttribute('stroke'); if (attributeIsNotEmpty(strokeColor)) { @@ -134,38 +134,38 @@ const svgElementToPdf = function (element, pdf, options) { break; case 'line': pdf.line( - k * parseInt(node.getAttribute('x1'), 10), - k * parseInt(node.getAttribute('y1'), 10), - k * parseInt(node.getAttribute('x2'), 10), - k * parseInt(node.getAttribute('y2'), 10) + k * parseInt(node.getAttribute('x1')), + k * parseInt(node.getAttribute('y1')), + k * parseInt(node.getAttribute('x2')), + k * parseInt(node.getAttribute('y2')) ); removeAttributes(node, pdfSvgAttr.line); break; case 'rect': pdf.rect( - k * parseInt(node.getAttribute('x'), 10), - k * parseInt(node.getAttribute('y'), 10), - k * parseInt(node.getAttribute('width'), 10), - k * parseInt(node.getAttribute('height'), 10), + k * parseInt(node.getAttribute('x')), + k * parseInt(node.getAttribute('y')), + k * parseInt(node.getAttribute('width')), + k * parseInt(node.getAttribute('height')), colorMode ); removeAttributes(node, pdfSvgAttr.rect); break; case 'ellipse': pdf.ellipse( - k * parseInt(node.getAttribute('cx'), 10), - k * parseInt(node.getAttribute('cy'), 10), - k * parseInt(node.getAttribute('rx'), 10), - k * parseInt(node.getAttribute('ry'), 10), + k * parseInt(node.getAttribute('cx')), + k * parseInt(node.getAttribute('cy')), + k * parseInt(node.getAttribute('rx')), + k * parseInt(node.getAttribute('ry')), colorMode ); removeAttributes(node, pdfSvgAttr.ellipse); break; case 'circle': pdf.circle( - k * parseInt(node.getAttribute('cx'), 10), - k * parseInt(node.getAttribute('cy'), 10), - k * parseInt(node.getAttribute('r'), 10), + k * parseInt(node.getAttribute('cx')), + k * parseInt(node.getAttribute('cy')), + k * parseInt(node.getAttribute('r')), colorMode ); removeAttributes(node, pdfSvgAttr.circle); @@ -216,7 +216,7 @@ const svgElementToPdf = function (element, pdf, options) { } pdf.setFontType(fontType); const pdfFontSize = node.hasAttribute('font-size') - ? parseInt(node.getAttribute('font-size'), 10) + ? parseInt(node.getAttribute('font-size')) : 16; const getWidth = (node) => { @@ -247,8 +247,8 @@ const svgElementToPdf = function (element, pdf, options) { case 'start': break; case 'default': node.setAttribute('text-anchor', 'start'); break; } - x = parseInt(node.getAttribute('x'), 10) - xOffset; - y = parseInt(node.getAttribute('y'), 10); + x = parseInt(node.getAttribute('x')) - xOffset; + y = parseInt(node.getAttribute('y')); } // console.log('fontSize:', pdfFontSize, 'text:', node.textContent); pdf.setFontSize(pdfFontSize).text( diff --git a/editor/layer.js b/editor/layer.js index cc3ca743..37446815 100644 --- a/editor/layer.js +++ b/editor/layer.js @@ -8,7 +8,7 @@ */ import {NS} from './namespaces.js'; -import {toXml, walkTree} from './utilities.js'; +import {toXml, walkTree, isNullish} from './utilities.js'; const $ = jQuery; @@ -117,7 +117,7 @@ class Layer { */ getOpacity () { const opacity = this.group_.getAttribute('opacity'); - if (opacity === null || opacity === undefined) { + if (isNullish(opacity)) { return 1; } return parseFloat(opacity); @@ -212,7 +212,7 @@ Layer.CLASS_REGEX = new RegExp('(\\s|^)' + Layer.CLASS_NAME + '(\\s|$)'); */ function addLayerClass (elem) { const classes = elem.getAttribute('class'); - if (classes === null || classes === undefined || !classes.length) { + if (isNullish(classes) || !classes.length) { elem.setAttribute('class', Layer.CLASS_NAME); } else if (!Layer.CLASS_REGEX.test(classes)) { elem.setAttribute('class', classes + ' ' + Layer.CLASS_NAME); diff --git a/editor/locale/lang.af.js b/editor/locale/lang.af.js index dbabcc85..86c8ece9 100644 --- a/editor/locale/lang.af.js +++ b/editor/locale/lang.af.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.ar.js b/editor/locale/lang.ar.js index 67fd7126..6746c0bf 100644 --- a/editor/locale/lang.ar.js +++ b/editor/locale/lang.ar.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.az.js b/editor/locale/lang.az.js index 2def669a..f6455252 100644 --- a/editor/locale/lang.az.js +++ b/editor/locale/lang.az.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.be.js b/editor/locale/lang.be.js index 7d16a3b3..87d02fc1 100644 --- a/editor/locale/lang.be.js +++ b/editor/locale/lang.be.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.bg.js b/editor/locale/lang.bg.js index 7904f3eb..2fa3dc2a 100644 --- a/editor/locale/lang.bg.js +++ b/editor/locale/lang.bg.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.ca.js b/editor/locale/lang.ca.js index 00ef0653..e46a7edb 100644 --- a/editor/locale/lang.ca.js +++ b/editor/locale/lang.ca.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.cs.js b/editor/locale/lang.cs.js index 85575a6a..3e420ffe 100644 --- a/editor/locale/lang.cs.js +++ b/editor/locale/lang.cs.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.cy.js b/editor/locale/lang.cy.js index 3c37326a..683b8def 100644 --- a/editor/locale/lang.cy.js +++ b/editor/locale/lang.cy.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.da.js b/editor/locale/lang.da.js index 8ced17ca..f1699e5a 100644 --- a/editor/locale/lang.da.js +++ b/editor/locale/lang.da.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.de.js b/editor/locale/lang.de.js index 1ae14c0a..76bf6201 100644 --- a/editor/locale/lang.de.js +++ b/editor/locale/lang.de.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'Es sind nicht-gespeicherte Änderungen vorhanden.', enterNewLinkURL: 'Geben Sie die neue URL ein', errorLoadingSVG: 'Fehler: Kann SVG-Daten nicht laden', - URLloadFail: 'Kann von dieser URL nicht laden', + URLLoadFail: 'Kann von dieser URL nicht laden', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.el.js b/editor/locale/lang.el.js index a429dfd6..b7a72441 100644 --- a/editor/locale/lang.el.js +++ b/editor/locale/lang.el.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.en.js b/editor/locale/lang.en.js index 204c8488..3dc5458b 100644 --- a/editor/locale/lang.en.js +++ b/editor/locale/lang.en.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.es.js b/editor/locale/lang.es.js index a07ea660..98739708 100644 --- a/editor/locale/lang.es.js +++ b/editor/locale/lang.es.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.et.js b/editor/locale/lang.et.js index e5bd4c5b..c98d6810 100644 --- a/editor/locale/lang.et.js +++ b/editor/locale/lang.et.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.fa.js b/editor/locale/lang.fa.js index 912e1086..d6f241c3 100644 --- a/editor/locale/lang.fa.js +++ b/editor/locale/lang.fa.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.fi.js b/editor/locale/lang.fi.js index 4397616d..12af7ca4 100644 --- a/editor/locale/lang.fi.js +++ b/editor/locale/lang.fi.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: "Retrieving '%s' ...", popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.fr.js b/editor/locale/lang.fr.js index 92dbbfdc..e04073e8 100644 --- a/editor/locale/lang.fr.js +++ b/editor/locale/lang.fr.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'Il y a des changements non sauvegardés.', enterNewLinkURL: "Entrez la nouvelle URL de l'hyperlien", errorLoadingSVG: 'Erreur : Impossible de charger les données SVG', - URLloadFail: "Impossible de charger l'URL", + URLLoadFail: "Impossible de charger l'URL", retrieving: 'Récupération de « %s »…', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.fy.js b/editor/locale/lang.fy.js index bdcb0ff7..e75046dd 100644 --- a/editor/locale/lang.fy.js +++ b/editor/locale/lang.fy.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\'...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.ga.js b/editor/locale/lang.ga.js index 560c5c9c..298d8741 100644 --- a/editor/locale/lang.ga.js +++ b/editor/locale/lang.ga.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.gl.js b/editor/locale/lang.gl.js index 85d1be54..57524c92 100644 --- a/editor/locale/lang.gl.js +++ b/editor/locale/lang.gl.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.he.js b/editor/locale/lang.he.js index f9b08f97..91c1e0de 100755 --- a/editor/locale/lang.he.js +++ b/editor/locale/lang.he.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.hi.js b/editor/locale/lang.hi.js index e9558ac2..e5fe2343 100644 --- a/editor/locale/lang.hi.js +++ b/editor/locale/lang.hi.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.hr.js b/editor/locale/lang.hr.js index f6459678..40cebeb3 100644 --- a/editor/locale/lang.hr.js +++ b/editor/locale/lang.hr.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.hu.js b/editor/locale/lang.hu.js index 89d96c3c..7c1acb9c 100644 --- a/editor/locale/lang.hu.js +++ b/editor/locale/lang.hu.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.hy.js b/editor/locale/lang.hy.js index 1a5a9adc..16931a06 100644 --- a/editor/locale/lang.hy.js +++ b/editor/locale/lang.hy.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.id.js b/editor/locale/lang.id.js index 02e17e0e..4c6d418b 100644 --- a/editor/locale/lang.id.js +++ b/editor/locale/lang.id.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.is.js b/editor/locale/lang.is.js index af88bb90..56c79e91 100644 --- a/editor/locale/lang.is.js +++ b/editor/locale/lang.is.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.it.js b/editor/locale/lang.it.js index c6cbbf71..cce861c1 100644 --- a/editor/locale/lang.it.js +++ b/editor/locale/lang.it.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.ja.js b/editor/locale/lang.ja.js index 23d0df6a..cd139bee 100644 --- a/editor/locale/lang.ja.js +++ b/editor/locale/lang.ja.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.ko.js b/editor/locale/lang.ko.js index 48a407f4..e22c15ec 100644 --- a/editor/locale/lang.ko.js +++ b/editor/locale/lang.ko.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.lt.js b/editor/locale/lang.lt.js index 6c16e56a..a89d2341 100644 --- a/editor/locale/lang.lt.js +++ b/editor/locale/lang.lt.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.lv.js b/editor/locale/lang.lv.js index 19ee6080..db0ac8aa 100644 --- a/editor/locale/lang.lv.js +++ b/editor/locale/lang.lv.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.mk.js b/editor/locale/lang.mk.js index b3da97d9..cb09ff71 100644 --- a/editor/locale/lang.mk.js +++ b/editor/locale/lang.mk.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.ms.js b/editor/locale/lang.ms.js index 6352de6c..7af353fb 100644 --- a/editor/locale/lang.ms.js +++ b/editor/locale/lang.ms.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.mt.js b/editor/locale/lang.mt.js index a6ba800c..ca186c94 100644 --- a/editor/locale/lang.mt.js +++ b/editor/locale/lang.mt.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.nl.js b/editor/locale/lang.nl.js index c4d29123..40adf6af 100644 --- a/editor/locale/lang.nl.js +++ b/editor/locale/lang.nl.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.no.js b/editor/locale/lang.no.js index 78847aa0..c33c4731 100644 --- a/editor/locale/lang.no.js +++ b/editor/locale/lang.no.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.pl.js b/editor/locale/lang.pl.js index e77d9c21..755902a6 100644 --- a/editor/locale/lang.pl.js +++ b/editor/locale/lang.pl.js @@ -207,7 +207,7 @@ export default { unsavedChanges: 'Wykryto niezapisane zmiany.', enterNewLinkURL: 'Wpisz nowy adres URL hiperłącza', errorLoadingSVG: 'Błąd: Nie można załadować danych SVG', - URLloadFail: 'Nie można załadować z adresu URL', + URLLoadFail: 'Nie można załadować z adresu URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.pt-BR.js b/editor/locale/lang.pt-BR.js index 29dd84ef..265f7cab 100644 --- a/editor/locale/lang.pt-BR.js +++ b/editor/locale/lang.pt-BR.js @@ -205,7 +205,7 @@ export default { unsavedChanges: 'Existem alterações não salvas.', enterNewLinkURL: 'Insira novo URL do hyperlink', errorLoadingSVG: 'Erro: Impossível carregar dados SVG', - URLloadFail: 'Impossível carregar deste URL', + URLLoadFail: 'Impossível carregar deste URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.pt-PT.js b/editor/locale/lang.pt-PT.js index 8b187eaf..fe358ae2 100644 --- a/editor/locale/lang.pt-PT.js +++ b/editor/locale/lang.pt-PT.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.ro.js b/editor/locale/lang.ro.js index e7c82ad7..1e6c3175 100644 --- a/editor/locale/lang.ro.js +++ b/editor/locale/lang.ro.js @@ -205,7 +205,7 @@ export default { unsavedChanges: 'Sunt schimbări nesalvate.', enterNewLinkURL: 'IntroduAliniere în raport cu ...sceţi noul URL', errorLoadingSVG: 'Eroare: Nu se pot încărca datele SVG', - URLloadFail: 'Nu se poate încărca de la URL', + URLLoadFail: 'Nu se poate încărca de la URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.ru.js b/editor/locale/lang.ru.js index e5ae878a..1dff0cf1 100644 --- a/editor/locale/lang.ru.js +++ b/editor/locale/lang.ru.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.sk.js b/editor/locale/lang.sk.js index ee04cde2..833244f5 100644 --- a/editor/locale/lang.sk.js +++ b/editor/locale/lang.sk.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'Sú tu neuložené zmeny.', enterNewLinkURL: 'Zadajte nové URL odkazu (hyperlink)', errorLoadingSVG: 'Chyba: Nedajú sa načítať SVG data', - URLloadFail: 'Nemožno čítať z URL', + URLLoadFail: 'Nemožno čítať z URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.sl.js b/editor/locale/lang.sl.js index a72c3715..42cf787a 100644 --- a/editor/locale/lang.sl.js +++ b/editor/locale/lang.sl.js @@ -205,7 +205,7 @@ export default { unsavedChanges: 'Obstajajo neshranjene spremembe.', enterNewLinkURL: 'Vnesite novo URL povezavo', errorLoadingSVG: 'Napaka: Ne morem naložiti SVG podatkov', - URLloadFail: 'Ne morem naložiti z URL', + URLLoadFail: 'Ne morem naložiti z URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.sq.js b/editor/locale/lang.sq.js index 01782fde..d224ba2e 100644 --- a/editor/locale/lang.sq.js +++ b/editor/locale/lang.sq.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.sr.js b/editor/locale/lang.sr.js index a7d466fa..4c3b80e5 100644 --- a/editor/locale/lang.sr.js +++ b/editor/locale/lang.sr.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.sv.js b/editor/locale/lang.sv.js index fcf98153..7a618ca4 100644 --- a/editor/locale/lang.sv.js +++ b/editor/locale/lang.sv.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.sw.js b/editor/locale/lang.sw.js index e97c8e1c..6ff7f4db 100644 --- a/editor/locale/lang.sw.js +++ b/editor/locale/lang.sw.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.test.js b/editor/locale/lang.test.js index 92f0e0f2..c30e3484 100644 --- a/editor/locale/lang.test.js +++ b/editor/locale/lang.test.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.th.js b/editor/locale/lang.th.js index 876fdf64..7c3fff53 100644 --- a/editor/locale/lang.th.js +++ b/editor/locale/lang.th.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.tl.js b/editor/locale/lang.tl.js index 06963af4..b1af0649 100644 --- a/editor/locale/lang.tl.js +++ b/editor/locale/lang.tl.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.tr.js b/editor/locale/lang.tr.js index 309c25bf..e6dc64bd 100644 --- a/editor/locale/lang.tr.js +++ b/editor/locale/lang.tr.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.uk.js b/editor/locale/lang.uk.js index 79244a47..05bba17f 100644 --- a/editor/locale/lang.uk.js +++ b/editor/locale/lang.uk.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.vi.js b/editor/locale/lang.vi.js index 2a119caa..17ea015a 100644 --- a/editor/locale/lang.vi.js +++ b/editor/locale/lang.vi.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.yi.js b/editor/locale/lang.yi.js index 4e42e20b..87e83305 100644 --- a/editor/locale/lang.yi.js +++ b/editor/locale/lang.yi.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.zh-CN.js b/editor/locale/lang.zh-CN.js index caa03d08..bff08635 100644 --- a/editor/locale/lang.zh-CN.js +++ b/editor/locale/lang.zh-CN.js @@ -206,7 +206,7 @@ export default { unsavedChanges: '存在未保存的修改.', enterNewLinkURL: '输入新建链接的URL地址', errorLoadingSVG: '错误: 无法加载SVG数据', - URLloadFail: '无法从URL中加载', + URLLoadFail: '无法从URL中加载', retrieving: '检索 \'%s\'...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.zh-HK.js b/editor/locale/lang.zh-HK.js index e2e821f1..71d85844 100644 --- a/editor/locale/lang.zh-HK.js +++ b/editor/locale/lang.zh-HK.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/lang.zh-TW.js b/editor/locale/lang.zh-TW.js index 1e544bfa..98d3e30a 100644 --- a/editor/locale/lang.zh-TW.js +++ b/editor/locale/lang.zh-TW.js @@ -206,7 +206,7 @@ export default { unsavedChanges: 'There are unsaved changes.', enterNewLinkURL: 'Enter the new hyperlink URL', errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', + URLLoadFail: 'Unable to load from URL', retrieving: 'Retrieving \'%s\' ...', popupWindowBlocked: 'Popup window may be blocked by browser', exportNoBlur: 'Blurred elements will appear as un-blurred', diff --git a/editor/locale/locale.js b/editor/locale/locale.js index d523d5d0..e65cd074 100644 --- a/editor/locale/locale.js +++ b/editor/locale/locale.js @@ -223,7 +223,7 @@ export const readLang = async function (langData) { image_width: properties.image_width, layer_delete: layers.del, layer_down: layers.move_down, - layer_new: layers['new'], + layer_new: layers.new, layer_rename: layers.rename, layer_moreopts: common.more_opts, layer_up: layers.move_up, diff --git a/editor/math.js b/editor/math.js index f1c27c57..44c16029 100644 --- a/editor/math.js +++ b/editor/math.js @@ -21,6 +21,7 @@ import {NS} from './namespaces.js'; import {getTransformList} from './svgtransformlist.js'; +import {isNullish} from './utilities.js'; // Constants const NEAR_ZERO = 1e-14; @@ -151,14 +152,14 @@ export const transformBox = function (l, t, w, h, m) { * @returns {SVGTransform} A single matrix transform object */ export const transformListToTransform = function (tlist, min, max) { - if (tlist == null) { + if (isNullish(tlist)) { // Or should tlist = null have been prevented before this? return svg.createSVGTransformFromMatrix(svg.createSVGMatrix()); } min = min || 0; max = max || (tlist.numberOfItems - 1); - min = parseInt(min, 10); - max = parseInt(max, 10); + min = parseInt(min); + max = parseInt(max); if (min > max) { const temp = max; max = min; min = temp; } let m = svg.createSVGMatrix(); for (let i = min; i <= max; ++i) { diff --git a/editor/path.js b/editor/path.js index 8e6d102d..12912c06 100644 --- a/editor/path.js +++ b/editor/path.js @@ -18,7 +18,7 @@ import { } from './math.js'; import { assignAttributes, getElem, getRotationAngle, getBBox, - getRefElem, findDefs, snapToGrid, + getRefElem, findDefs, snapToGrid, isNullish, getBBox as utilsGetBBox } from './utilities.js'; import { @@ -77,7 +77,7 @@ export const setLinkControlPoints = function (lcp) { * @type {null|module:path.Path} * @memberof module:path */ -export let path = null; +export let path = null; // eslint-disable-line import/no-mutable-exports let editorContext_ = null; @@ -240,8 +240,10 @@ export const init = function (editorContext) { editorContext_ = editorContext; pathFuncs = [0, 'ClosePath']; - const pathFuncsStrs = ['Moveto', 'Lineto', 'CurvetoCubic', 'CurvetoQuadratic', 'Arc', - 'LinetoHorizontal', 'LinetoVertical', 'CurvetoCubicSmooth', 'CurvetoQuadraticSmooth']; + const pathFuncsStrs = [ + 'Moveto', 'Lineto', 'CurvetoCubic', 'CurvetoQuadratic', 'Arc', + 'LinetoHorizontal', 'LinetoVertical', 'CurvetoCubicSmooth', 'CurvetoQuadraticSmooth' + ]; $.each(pathFuncsStrs, function (i, s) { pathFuncs.push(s + 'Abs'); pathFuncs.push(s + 'Rel'); @@ -299,14 +301,14 @@ export const ptObjToArr = function (type, segItem) { * @returns {module:math.XYObject} */ export const getGripPt = function (seg, altPt) { - const {path} = seg; + const {path: pth} = seg; let out = { x: altPt ? altPt.x : seg.item.x, y: altPt ? altPt.y : seg.item.y }; - if (path.matrix) { - const pt = transformPoint(out.x, out.y, path.matrix); + if (pth.matrix) { + const pt = transformPoint(out.x, out.y, pth.matrix); out = pt; } @@ -320,17 +322,17 @@ export const getGripPt = function (seg, altPt) { /** * @function module:path.getPointFromGrip * @param {module:math.XYObject} pt -* @param {module:path.Path} path +* @param {module:path.Path} pth * @returns {module:math.XYObject} */ -export const getPointFromGrip = function (pt, path) { +export const getPointFromGrip = function (pt, pth) { const out = { x: pt.x, y: pt.y }; - if (path.matrix) { - pt = transformPoint(out.x, out.y, path.imatrix); + if (pth.matrix) { + pt = transformPoint(out.x, out.y, pth.imatrix); out.x = pt.x; out.y = pt.y; } @@ -400,8 +402,8 @@ export const addPointGrip = function (index, x, y) { export const getGripContainer = function () { let c = getElem('pathpointgrip_container'); if (!c) { - const parent = getElem('selectorParentGroup'); - c = parent.appendChild(document.createElementNS(NS.SVG, 'g')); + const parentElement = getElem('selectorParentGroup'); + c = parentElement.appendChild(document.createElementNS(NS.SVG, 'g')); c.id = 'pathpointgrip_container'; } return c; @@ -539,7 +541,7 @@ export const replacePathSeg = function (type, index, pts, elem) { const pth = elem || path.elem; const func = 'createSVGPathSeg' + pathFuncs[type]; - const seg = pth[func].apply(pth, pts); + const seg = pth[func](...pts); if (supportsPathReplaceItem()) { pth.pathSegList.replaceItem(seg, index); @@ -600,9 +602,9 @@ export const getSegSelector = function (seg, update) { const pts = ptObjToArr(seg.type, seg.item); // , true); for (let i = 0; i < pts.length; i += 2) { - const pt = getGripPt(seg, {x: pts[i], y: pts[i + 1]}); - pts[i] = pt.x; - pts[i + 1] = pt.y; + const point = getGripPt(seg, {x: pts[i], y: pts[i + 1]}); + pts[i] = point.x; + pts[i + 1] = point.y; } replacePathSeg(seg.type, 1, pts, segLine); @@ -691,7 +693,7 @@ export class Segment { */ showCtrlPts (y) { for (const i in this.ctrlpts) { - if (this.ctrlpts.hasOwnProperty(i)) { + if ({}.hasOwnProperty.call(this.ctrlpts, i)) { this.ctrlpts[i].setAttribute('display', y ? 'inline' : 'none'); } } @@ -777,7 +779,8 @@ export class Segment { const {item} = this; const curPts = this.ctrlpts - ? [item.x += dx, item.y += dy, + ? [ + item.x += dx, item.y += dy, item.x1, item.y1, item.x2 += dx, item.y2 += dy ] : [item.x += dx, item.y += dy]; @@ -786,16 +789,18 @@ export class Segment { if (this.next && this.next.ctrlpts) { const next = this.next.item; - const nextPts = [next.x, next.y, - next.x1 += dx, next.y1 += dy, next.x2, next.y2]; + const nextPts = [ + next.x, next.y, + next.x1 += dx, next.y1 += dy, next.x2, next.y2 + ]; replacePathSeg(this.next.type, this.next.index, nextPts); } if (this.mate) { // The last point of a closed subpath has a 'mate', // which is the 'M' segment of the subpath - const {item} = this.mate; - const pts = [item.x += dx, item.y += dy]; + const {item: itm} = this.mate; + const pts = [itm.x += dx, itm.y += dy]; replacePathSeg(this.mate.type, this.mate.index, pts); // Has no grip, so does not need 'updating'? } @@ -826,9 +831,11 @@ export class Segment { item['x' + anum] = pt.x + (pt.x - this.item['x' + num]); item['y' + anum] = pt.y + (pt.y - this.item['y' + num]); - const pts = [item.x, item.y, + const pts = [ + item.x, item.y, item.x1, item.y1, - item.x2, item.y2]; + item.x2, item.y2 + ]; replacePathSeg(seg.type, seg.index, pts); seg.update(true); @@ -845,8 +852,10 @@ export class Segment { item['x' + num] += dx; item['y' + num] += dy; - const pts = [item.x, item.y, - item.x1, item.y1, item.x2, item.y2]; + const pts = [ + item.x, item.y, + item.x1, item.y1, item.x2, item.y2 + ]; replacePathSeg(this.type, this.index, pts); this.update(true); @@ -883,7 +892,7 @@ export class Path { this.elem = elem; this.segs = []; this.selected_pts = []; - path = this; + path = this; // eslint-disable-line consistent-this this.init(); } @@ -941,7 +950,7 @@ export class Path { seg.next.prev = seg; seg.mate = segs[startI]; seg.addGrip(); - if (this.first_seg == null) { + if (isNullish(this.first_seg)) { this.first_seg = seg; } } else if (!nextSeg) { @@ -977,7 +986,7 @@ export class Path { * @callback module:path.PathEachSegCallback * @this module:path.Segment * @param {Integer} i The index of the seg being iterated - * @returns {boolean} Will stop execution of `eachSeg` if returns `false` + * @returns {boolean|undefined} Will stop execution of `eachSeg` if returns `false` */ /** * @param {module:path.PathEachSegCallback} fn @@ -1062,29 +1071,6 @@ export class Path { } } - /** - * @param {Integer} index - * @returns {boolean} - */ - subpathIsClosed (index) { - let closed = false; - // Check if subpath is already open - path.eachSeg(function (i) { - if (i <= index) { return true; } - if (this.type === 2) { - // Found M first, so open - return false; - } - if (this.type === 1) { - // Found Z first, so closed - closed = true; - return false; - } - }); - - return closed; - } - /** * @param {Integer} index * @returns {undefined} @@ -1235,7 +1221,7 @@ export class Path { */ selectPt (pt, ctrlNum) { this.clearSelection(); - if (pt == null) { + if (isNullish(pt)) { this.eachSeg(function (i) { // 'this' is the segment here. if (this.prev) { @@ -1312,11 +1298,35 @@ export class Path { grips[i] = seg.ptgrip; } - const closedSubpath = this.subpathIsClosed(this.selected_pts[0]); + const closedSubpath = Path.subpathIsClosed(this.selected_pts[0]); editorContext_.addPtsToSelection({grips, closedSubpath}); } } +/** +* @param {Integer} index +* @returns {boolean} +*/ +Path.subpathIsClosed = function (index) { + let clsd = false; + // Check if subpath is already open + path.eachSeg(function (i) { + if (i <= index) { return true; } + if (this.type === 2) { + // Found M first, so open + return false; + } + if (this.type === 1) { + // Found Z first, so closed + clsd = true; + return false; + } + return true; + }); + + return clsd; +}; + /** * @function module:path.getPath_ * @param {SVGPathElement} elem @@ -1408,7 +1418,7 @@ export const recalcRotatedPath = function () { const rvals = getRotVals(seg.x, seg.y), points = [rvals.x, rvals.y]; - if (seg.x1 != null && seg.x2 != null) { + if (!isNullish(seg.x1) && !isNullish(seg.x2)) { const cVals1 = getRotVals(seg.x1, seg.y1); const cVals2 = getRotVals(seg.x2, seg.y2); points.splice(points.length, 0, cVals1.x, cVals1.y, cVals2.x, cVals2.y); @@ -1492,26 +1502,28 @@ export const reorientGrads = function (elem, m) { * @name module:path.pathMap * @type {GenericArray} */ -const pathMap = [0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a', - 'H', 'h', 'V', 'v', 'S', 's', 'T', 't']; +const pathMap = [ + 0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a', + 'H', 'h', 'V', 'v', 'S', 's', 'T', 't' +]; /** * Convert a path to one with only absolute or relative values. * @todo move to pathActions.js * @function module:path.convertPath - * @param {SVGPathElement} path - the path to convert + * @param {SVGPathElement} pth - the path to convert * @param {boolean} toRel - true of convert to relative * @returns {string} */ -export const convertPath = function (path, toRel) { - const segList = path.pathSegList; - const len = segList.numberOfItems; +export const convertPath = function (pth, toRel) { + const {pathSegList} = pth; + const len = pathSegList.numberOfItems; let curx = 0, cury = 0; let d = ''; let lastM = null; for (let i = 0; i < len; ++i) { - const seg = segList.getItem(i); + const seg = pathSegList.getItem(i); // if these properties are not in the segment, set them to zero let x = seg.x || 0, y = seg.y || 0, @@ -1776,7 +1788,7 @@ export const pathActions = (function () { * @param {Element} mouseTarget * @param {Float} startX * @param {Float} startY - * @returns {undefined} + * @returns {boolean|undefined} */ mouseDown (evt, mouseTarget, startX, startY) { let id; @@ -1922,7 +1934,7 @@ export const pathActions = (function () { editorContext_.getMouseTarget(evt) )) { // Clicked outside canvas, so don't make point - console.log('Clicked outside canvas'); + // console.log('Clicked outside canvas'); return false; } @@ -1967,11 +1979,11 @@ export const pathActions = (function () { // keep = true; } - return; + return undefined; } // TODO: Make sure currentPath isn't null at this point - if (!path) { return; } + if (!path) { return undefined; } path.storeD(); @@ -1979,7 +1991,7 @@ export const pathActions = (function () { let curPt; if (id.substr(0, 14) === 'pathpointgrip_') { // Select this point - curPt = path.cur_pt = parseInt(id.substr(14), 10); + curPt = path.cur_pt = parseInt(id.substr(14)); path.dragging = [startX, startY]; const seg = path.segs[curPt]; @@ -2007,7 +2019,7 @@ export const pathActions = (function () { // Start selection box if (!path.dragging) { let rubberBox = editorContext_.getRubberBox(); - if (rubberBox == null) { + if (isNullish(rubberBox)) { rubberBox = editorContext_.setRubberBox( editorContext_.selectorManager.getRubberBandBox() ); @@ -2021,6 +2033,7 @@ export const pathActions = (function () { display: 'inline' }, 100); } + return undefined; }, /** * @param {Float} mouseX @@ -2127,7 +2140,7 @@ export const pathActions = (function () { } else { path.selected_pts = []; path.eachSeg(function (i) { - const seg = this; + const seg = this; // eslint-disable-line consistent-this if (!seg.next && !seg.prev) { return; } // const {item} = seg; @@ -2150,12 +2163,18 @@ export const pathActions = (function () { }); } }, + /** + * @typedef module:path.keepElement + * @type {PlainObject} + * @property {boolean} keep + * @property {Element} element + */ /** * @param {Event} evt * @param {Element} element * @param {Float} mouseX * @param {Float} mouseY - * @returns {undefined} + * @returns {module:path.keepElement|undefined} */ mouseUp (evt, element, mouseX, mouseY) { const drawnPath = editorContext_.getDrawnPath(); @@ -2203,6 +2222,7 @@ export const pathActions = (function () { pathActions.toSelectMode(evt.target); } hasMoved = false; + return undefined; }, /** * @param {Element} element @@ -2273,8 +2293,8 @@ export const pathActions = (function () { reorient () { const elem = editorContext_.getSelectedElements()[0]; if (!elem) { return; } - const angle = getRotationAngle(elem); - if (angle === 0) { return; } + const angl = getRotationAngle(elem); + if (angl === 0) { return; } const batchCmd = new BatchCommand('Reorient path'); const changes = { @@ -2321,7 +2341,7 @@ export const pathActions = (function () { * @returns {false|undefined} */ resetOrientation (pth) { - if (pth == null || pth.nodeName !== 'path') { return false; } + if (isNullish(pth) || pth.nodeName !== 'path') { return false; } const tlist = getTransformList(pth); const m = transformListToTransform(tlist).matrix; tlist.clear(); @@ -2357,6 +2377,7 @@ export const pathActions = (function () { } reorientGrads(pth, m); + return undefined; }, /** * @returns {undefined} @@ -2449,9 +2470,10 @@ export const pathActions = (function () { openPt = false; return false; } + return true; }); - if (openPt == null) { + if (isNullish(openPt)) { // Single path, so close last seg openPt = path.segs.length - 1; } diff --git a/editor/recalculate.js b/editor/recalculate.js index a4559bf0..52512e21 100644 --- a/editor/recalculate.js +++ b/editor/recalculate.js @@ -5,12 +5,12 @@ * @license MIT */ -import jqPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr` +import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr` import {NS} from './namespaces.js'; import {convertToNum} from './units.js'; import {isWebkit} from './browser.js'; import {getTransformList} from './svgtransformlist.js'; -import {getRotationAngle, getHref, getBBox, getRefElem} from './utilities.js'; +import {getRotationAngle, getHref, getBBox, getRefElem, isNullish} from './utilities.js'; import {BatchCommand, ChangeElementCommand} from './history.js'; import {remapElement} from './coords.js'; import { @@ -18,7 +18,7 @@ import { hasMatrixTransform } from './math.js'; -const $ = jqPluginSVG(jQuery); +const $ = jQueryPluginSVG(jQuery); let context_; @@ -75,7 +75,7 @@ export const updateClipPath = function (attr, tx, ty) { * @returns {Command} Undo command object with the resulting change */ export const recalculateDimensions = function (selected) { - if (selected == null) { return null; } + if (isNullish(selected)) { return null; } // Firefox Issue - 1081 if (selected.nodeName === 'svg' && navigator.userAgent.includes('Firefox/20')) { @@ -154,7 +154,8 @@ export const recalculateDimensions = function (selected) { const m = matrixMultiply( tlist.getItem(k - 2).matrix, - tlist.getItem(k - 1).matrix); + tlist.getItem(k - 1).matrix + ); mt.setMatrix(m); tlist.removeItem(k - 2); tlist.removeItem(k - 2); @@ -241,7 +242,7 @@ export const recalculateDimensions = function (selected) { // if we haven't created an initial array in polygon/polyline/path, then // make a copy of initial values and include the transform - if (initial == null) { + if (isNullish(initial)) { initial = $.extend(true, {}, changes); $.each(initial, function (attr, val) { initial[attr] = convertToNum(attr, val); @@ -795,15 +796,15 @@ export const recalculateDimensions = function (selected) { // translation required to re-center it // Therefore, [Tr] = [M_inv][Rnew_inv][Rold][M] } else if (operation === 3 && angle) { - const m = transformListToTransform(tlist).matrix; + const {matrix} = transformListToTransform(tlist); const roldt = svgroot.createSVGTransform(); roldt.setRotate(angle, oldcenter.x, oldcenter.y); const rold = roldt.matrix; const rnew = svgroot.createSVGTransform(); rnew.setRotate(angle, newcenter.x, newcenter.y); const rnewInv = rnew.matrix.inverse(); - const mInv = m.inverse(); - const extrat = matrixMultiply(mInv, rnewInv, rold, m); + const mInv = matrix.inverse(); + const extrat = matrixMultiply(mInv, rnewInv, rold, matrix); remapElement(selected, changes, extrat); if (angle) { diff --git a/editor/redirect-on-lacking-support.js b/editor/redirect-on-lacking-support.js index 0a4f981a..e967f094 100644 --- a/editor/redirect-on-lacking-support.js +++ b/editor/redirect-on-lacking-support.js @@ -1,4 +1,5 @@ import {supportsSvg} from './browser.js'; + if (!supportsSvg()) { window.location = 'browser-not-supported.html'; } diff --git a/editor/sanitize.js b/editor/sanitize.js index 891b663a..d866d779 100644 --- a/editor/sanitize.js +++ b/editor/sanitize.js @@ -143,7 +143,7 @@ export const sanitizeSvg = function (node) { const attrNsURI = attr.namespaceURI; // Check that an attribute with the correct localName in the correct namespace is on // our whitelist or is a namespace declaration for one of our allowed namespaces - if (!(allowedAttrsNS.hasOwnProperty(attrLocalName) && attrNsURI === allowedAttrsNS[attrLocalName] && attrNsURI !== NS.XMLNS) && + if (!({}.hasOwnProperty.call(allowedAttrsNS, attrLocalName) && attrNsURI === allowedAttrsNS[attrLocalName] && attrNsURI !== NS.XMLNS) && !(attrNsURI === NS.XMLNS && REVERSE_NS[attr.value])) { // TODO(codedread): Programmatically add the se: attributes to the NS-aware whitelist. // Bypassing the whitelist to allow se: prefixes. @@ -159,11 +159,12 @@ export const sanitizeSvg = function (node) { switch (attrName) { case 'transform': case 'gradientTransform': - case 'patternTransform': + case 'patternTransform': { const val = attr.value.replace(/(\d)-/g, '$1 -'); node.setAttribute(attrName, val); break; } + } } // For the style attribute, rewrite it in terms of XML presentational attributes diff --git a/editor/select.js b/editor/select.js index 27fe81bc..6014925f 100644 --- a/editor/select.js +++ b/editor/select.js @@ -8,7 +8,7 @@ */ import {isTouch, isWebkit} from './browser.js'; // , isOpera -import {getRotationAngle, getBBox, getStrokedBBox} from './utilities.js'; +import {getRotationAngle, getBBox, getStrokedBBox, isNullish} from './utilities.js'; import {transformListToTransform, transformBox, transformPoint} from './math.js'; import {getTransformList} from './svgtransformlist.js'; @@ -88,30 +88,6 @@ export class Selector { this.selectorGroup.setAttribute('display', 'inline'); } - /** - * Updates cursors for corner grips on rotation so arrows point the right way. - * @param {Float} angle - Current rotation angle in degrees - * @returns {undefined} - */ - updateGripCursors (angle) { - let dir; - const dirArr = []; - let steps = Math.round(angle / 45); - if (steps < 0) { steps += 8; } - for (dir in selectorManager_.selectorGrips) { - dirArr.push(dir); - } - while (steps > 0) { - dirArr.push(dirArr.shift()); - steps--; - } - let i = 0; - for (dir in selectorManager_.selectorGrips) { - selectorManager_.selectorGrips[dir].setAttribute('style', ('cursor:' + dirArr[i] + '-resize')); - i++; - } - } - /** * Show the resize grips of this selector. * @param {boolean} show - Indicates whether grips should be shown or not @@ -124,7 +100,7 @@ export class Selector { this.hasGrips = show; if (elem && show) { this.selectorGroup.append(selectorManager_.selectorGripsGroup); - this.updateGripCursors(getRotationAngle(elem)); + Selector.updateGripCursors(getRotationAngle(elem)); } } @@ -245,11 +221,10 @@ export class Selector { e: [nbax + nbaw, nbay + (nbah) / 2], s: [nbax + (nbaw) / 2, nbay + nbah] }; - for (const dir in this.gripCoords) { - const coords = this.gripCoords[dir]; + Object.entries(this.gripCoords).forEach(([dir, coords]) => { selectedGrips[dir].setAttribute('cx', coords[0]); selectedGrips[dir].setAttribute('cy', coords[1]); - } + }); // we want to go 20 pixels in the negative transformed y direction, ignoring scale mgr.rotateGripConnector.setAttribute('x1', nbax + (nbaw) / 2); @@ -262,6 +237,23 @@ export class Selector { // } } } +/** +* Updates cursors for corner grips on rotation so arrows point the right way. +* @param {Float} angle - Current rotation angle in degrees +* @returns {undefined} +*/ +Selector.updateGripCursors = function (angle) { + const dirArr = Object.keys(selectorManager_.selectorGrips); + let steps = Math.round(angle / 45); + if (steps < 0) { steps += 8; } + while (steps > 0) { + dirArr.push(dirArr.shift()); + steps--; + } + Object.values(selectorManager_.selectorGrips).forEach((gripElement, i) => { + gripElement.setAttribute('style', ('cursor:' + dirArr[i] + '-resize')); + }); +}; /** * Manage all selector objects (selection boxes). @@ -326,7 +318,7 @@ export class SelectorManager { this.rubberBandBox = null; // add the corner grips - for (const dir in this.selectorGrips) { + Object.keys(this.selectorGrips).forEach((dir) => { const grip = svgFactory_.createSVGElement({ element: 'circle', attr: { @@ -346,7 +338,7 @@ export class SelectorManager { $.data(grip, 'dir', dir); $.data(grip, 'type', 'resize'); this.selectorGrips[dir] = this.selectorGripsGroup.appendChild(grip); - } + }); // add rotator elems this.rotateGripConnector = this.selectorGripsGroup.appendChild( @@ -420,7 +412,7 @@ export class SelectorManager { * @returns {Selector} The selector based on the given element */ requestSelector (elem, bbox) { - if (elem == null) { return null; } + if (isNullish(elem)) { return null; } const N = this.selectors.length; // If we've already acquired one for this element, return it. @@ -450,12 +442,12 @@ export class SelectorManager { * @returns {undefined} */ releaseSelector (elem) { - if (elem == null) { return; } + if (isNullish(elem)) { return; } const N = this.selectors.length, sel = this.selectorMap[elem.id]; if (!sel.locked) { // TODO(codedread): Ensure this exists in this module. - console.log('WARNING! selector was released but was already unlocked'); + console.log('WARNING! selector was released but was already unlocked'); // eslint-disable-line no-console } for (let i = 0; i < N; ++i) { if (this.selectors[i] && this.selectors[i] === sel) { diff --git a/editor/spinbtn/jQuery.SpinButton.js b/editor/spinbtn/jQuery.SpinButton.js index be72d810..7f1e60e7 100644 --- a/editor/spinbtn/jQuery.SpinButton.js +++ b/editor/spinbtn/jQuery.SpinButton.js @@ -73,7 +73,7 @@ * @param {external:jQuery} $ The jQuery object to which to add the plug-in * @returns {external:jQuery} */ -export default function ($) { +export default function jQueryPluginSpinButton ($) { if (!$.loadingStylesheets) { $.loadingStylesheets = []; } diff --git a/editor/svg-editor.js b/editor/svg-editor.js index 09c2c981..bd001fe8 100644 --- a/editor/svg-editor.js +++ b/editor/svg-editor.js @@ -13,14 +13,14 @@ import {importSetGlobalDefault} from './external/dynamic-import-polyfill/importM import SvgCanvas from './svgcanvas.js'; import Layer from './layer.js'; -import jqPluginJSHotkeys from './js-hotkeys/jquery.hotkeys.min.js'; -import jqPluginBBQ from './jquerybbq/jquery.bbq.min.js'; -import jqPluginSVGIcons from './svgicons/jQuery.svgIcons.js'; -import jqPluginJGraduate from './jgraduate/jQuery.jGraduate.js'; -import jqPluginSpinBtn from './spinbtn/jQuery.SpinButton.js'; -import jqPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr` -import jqPluginContextMenu from './contextmenu/jQuery.contextMenu.js'; -import jqPluginJPicker from './jgraduate/jQuery.jPicker.js'; +import jQueryPluginJSHotkeys from './js-hotkeys/jquery.hotkeys.min.js'; +import jQueryPluginBBQ from './jquerybbq/jquery.bbq.min.js'; +import jQueryPluginSVGIcons from './svgicons/jQuery.svgIcons.js'; +import jQueryPluginJGraduate from './jgraduate/jQuery.jGraduate.js'; +import jQueryPluginSpinButton from './spinbtn/jQuery.SpinButton.js'; +import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr` +import jQueryPluginContextMenu from './contextmenu/jQuery.contextMenu.js'; +import jQueryPluginJPicker from './jgraduate/jQuery.jPicker.js'; import { readLang, putLocale, setStrings, @@ -46,9 +46,9 @@ import loadStylesheets from './external/load-stylesheets/index-es.js'; const editor = {}; const $ = [ - jqPluginJSHotkeys, jqPluginBBQ, jqPluginSVGIcons, jqPluginJGraduate, - jqPluginSpinBtn, jqPluginSVG, jqPluginContextMenu, jqPluginJPicker -].reduce(($, cb) => cb($), jQuery); + jQueryPluginJSHotkeys, jQueryPluginBBQ, jQueryPluginSVGIcons, jQueryPluginJGraduate, + jQueryPluginSpinButton, jQueryPluginSVG, jQueryPluginContextMenu, jQueryPluginJPicker +].reduce((jq, func) => func(jq), jQuery); /* if (!$.loadingStylesheets) { @@ -337,11 +337,16 @@ function getImportLocale ({defaultLang, defaultName}) { * @param {string} [localeInfo.lang=defaultLang] Defaults to `defaultLang` of {@link module:SVGEditor~getImportLocale} * @returns {Promise} Resolves to {@link module:locale.LocaleStrings} */ - return async function importLocale ({name = defaultName, lang = defaultLang} = {}) { - async function importLocale (lang) { - const url = `${curConfig.extPath}ext-locale/${name}/${lang}.js`; + return async function importLocaleDefaulting ({name = defaultName, lang = defaultLang} = {}) { + /** + * + * @param {string} language + * @returns {Promise} Resolves to {@link module:locale.LocaleStrings} + */ + function importLocale (language) { + const url = `${curConfig.extPath}ext-locale/${name}/${language}.js`; return importSetGlobalDefault(url, { - global: `svgEditorExtensionLocale_${name}_${lang}` + global: `svgEditorExtensionLocale_${name}_${language}` }); } try { @@ -360,7 +365,7 @@ function getImportLocale ({defaultLang, defaultName}) { * Store and retrieve preferences. * @param {string} key The preference name to be retrieved or set * @param {string} [val] The value. If the value supplied is missing or falsey, no change to the preference will be made. -* @returns {string} If val is missing or falsey, the value of the previously stored preference will be returned. +* @returns {string|undefined} If val is missing or falsey, the value of the previously stored preference will be returned. * @todo Can we change setting on the jQuery namespace (onto editor) to avoid conflicts? * @todo Review whether any remaining existing direct references to * getting `curPrefs` can be changed to use `$.pref()` getting to ensure @@ -377,7 +382,7 @@ $.pref = function (key, val) { * @implements {module:SVGEditor.Prefs} */ editor.curPrefs = curPrefs; // Update exported value - return; + return undefined; } return (key in curPrefs) ? curPrefs[key] : defaultPrefs[key]; }; @@ -428,22 +433,20 @@ editor.loadContentAndPrefs = function () { } // LOAD PREFS - for (const key in defaultPrefs) { - if (defaultPrefs.hasOwnProperty(key)) { // It's our own config, so we don't need to iterate up the prototype chain - const storeKey = 'svg-edit-' + key; - if (editor.storage) { - const val = editor.storage.getItem(storeKey); - if (val) { - defaultPrefs[key] = String(val); // Convert to string for FF (.value fails in Webkit) - } - } else if (window.widget) { - defaultPrefs[key] = window.widget.preferenceForKey(storeKey); - } else { - const result = document.cookie.match(new RegExp('(?:^|;\\s*)' + Utils.regexEscape(encodeURIComponent(storeKey)) + '=([^;]+)')); - defaultPrefs[key] = result ? decodeURIComponent(result[1]) : ''; + Object.keys(defaultPrefs).forEach((key) => { + const storeKey = 'svg-edit-' + key; + if (editor.storage) { + const val = editor.storage.getItem(storeKey); + if (val) { + defaultPrefs[key] = String(val); // Convert to string for FF (.value fails in Webkit) } + } else if (window.widget) { + defaultPrefs[key] = window.widget.preferenceForKey(storeKey); + } else { + const result = document.cookie.match(new RegExp('(?:^|;\\s*)' + Utils.regexEscape(encodeURIComponent(storeKey)) + '=([^;]+)')); + defaultPrefs[key] = result ? decodeURIComponent(result[1]) : ''; } - } + }); }; /** @@ -476,12 +479,12 @@ editor.setConfig = function (opts, cfgCfg) { } } $.each(opts, function (key, val) { - if (opts.hasOwnProperty(key)) { + if ({}.hasOwnProperty.call(opts, key)) { // Only allow prefs defined in defaultPrefs - if (defaultPrefs.hasOwnProperty(key)) { + if ({}.hasOwnProperty.call(defaultPrefs, key)) { if (cfgCfg.overwrite === false && ( curConfig.preventAllURLConfig || - curPrefs.hasOwnProperty(key) + {}.hasOwnProperty.call(curPrefs, key) )) { return; } @@ -502,30 +505,26 @@ editor.setConfig = function (opts, cfgCfg) { } curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later // Only allow other curConfig if defined in defaultConfig - } else if (defaultConfig.hasOwnProperty(key)) { + } else if ({}.hasOwnProperty.call(defaultConfig, key)) { if (cfgCfg.overwrite === false && ( curConfig.preventAllURLConfig || - curConfig.hasOwnProperty(key) + {}.hasOwnProperty.call(curConfig, key) )) { return; } // Potentially overwriting of previously set config - if (curConfig.hasOwnProperty(key)) { + if ({}.hasOwnProperty.call(curConfig, key)) { if (cfgCfg.overwrite === false) { return; } extendOrAdd(curConfig, key, val); + } else if (cfgCfg.allowInitialUserOverride === true) { + extendOrAdd(defaultConfig, key, val); + } else if (defaultConfig[key] && typeof defaultConfig[key] === 'object') { + curConfig[key] = {}; + $.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects } else { - if (cfgCfg.allowInitialUserOverride === true) { - extendOrAdd(defaultConfig, key, val); - } else { - if (defaultConfig[key] && typeof defaultConfig[key] === 'object') { - curConfig[key] = {}; - $.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects - } else { - curConfig[key] = val; - } - } + curConfig[key] = val; } } } @@ -781,7 +780,7 @@ editor.init = function () { curConfig.extensions.map(async (extname) => { const extName = extname.match(/^ext-(.+)\.js/); if (!extName) { // Ensure URL cannot specify some other unintended file in the extPath - return; + return undefined; } const url = curConfig.extPath + extname; // Todo: Replace this with `return import(url);` when @@ -803,12 +802,15 @@ editor.init = function () { const importLocale = getImportLocale({defaultLang: langParam, defaultName: name}); return editor.addExtension(name, (init && init.bind(editor)), importLocale); } catch (err) { - console.log(err); - console.error('Extension failed to load: ' + extname + '; ' + err); + // Todo: Add config to alert any errors + console.log(err); // eslint-disable-line no-console + console.error('Extension failed to load: ' + extname + '; ' + err); // eslint-disable-line no-console + return undefined; } }) ); - svgCanvas.bind('extensions_added', + svgCanvas.bind( + 'extensions_added', /** * @param {external:Window} win * @param {module:svgcanvas.SvgCanvas#event:extensions_added} data @@ -888,8 +890,8 @@ editor.init = function () { for (let i = 0; i < 4; i++) { const s = sides[i]; let cur = el.data('orig_margin-' + s); - if (cur == null) { - cur = parseInt(el.css('margin-' + s), 10); + if (Utils.isNullish(cur)) { + cur = parseInt(el.css('margin-' + s)); // Cache the original margin el.data('orig_margin-' + s, cur); } @@ -1336,8 +1338,14 @@ editor.init = function () { } }); - function getStylesheetPriority (stylesheet) { - switch (stylesheet) { + /** + * Since stylesheets may be added out of order, we indicate the desired order + * for defaults and others after them (in an indeterminate order). + * @param {string} stylesheetFile + * @returns {Integer|PositiveInfinity} + */ + function getStylesheetPriority (stylesheetFile) { + switch (stylesheetFile) { case 'jgraduate/css/jPicker.css': return 1; case 'jgraduate/css/jGraduate.css': @@ -1366,7 +1374,7 @@ editor.init = function () { stylesheets.splice(idx, 1, ...$.loadingStylesheets); } } - loadStylesheets(stylesheets, {acceptErrors: ({stylesheetURL, reject, resolve}) => { + loadStylesheets(stylesheets, {acceptErrors ({stylesheetURL, reject, resolve}) { if ($.loadingStylesheets.includes(stylesheetURL)) { reject(new Error(`Missing expected stylesheet: ${stylesheetURL}`)); return; @@ -1387,7 +1395,8 @@ editor.init = function () { document.getElementById('svgcanvas'), curConfig ); - const palette = [ // Todo: Make into configuration item? + const palette = [ + // Todo: Make into configuration item? '#000000', '#3f3f3f', '#7f7f7f', '#bfbfbf', '#ffffff', '#ff0000', '#ff7f00', '#ffff00', '#7fff00', '#00ff00', '#00ff7f', '#00ffff', '#007fff', @@ -1487,7 +1496,7 @@ editor.init = function () { if (checkbox.tooltip) { label.attr('title', checkbox.tooltip); } - chkbx.prop('checked', !!checkbox.checked); + chkbx.prop('checked', Boolean(checkbox.checked)); div.append($('
').append(label)); } $.each(opts || [], function (opt, val) { @@ -1649,7 +1658,7 @@ editor.init = function () { editingsource = true; origSource = svgCanvas.getSvgString(); - $('#save_output_btns').toggle(!!forSaving); + $('#save_output_btns').toggle(Boolean(forSaving)); $('#tool_source_back').toggle(!forSaving); $('#svg_source_textarea').val(origSource); $('#svg_source_editor').fadeIn(); @@ -1931,7 +1940,7 @@ editor.init = function () { // Create multiple canvases when necessary (due to browser limits) if (rulerLen >= limit) { - ctxArrNum = parseInt(rulerLen / limit, 10) + 1; + ctxArrNum = parseInt(rulerLen / limit) + 1; ctxArr = []; ctxArr[0] = ctx; let copy; @@ -2078,8 +2087,8 @@ editor.init = function () { const ratio = newCanX / oldCanX; - const scrollX = w / 2 - wOrig / 2; - const scrollY = h / 2 - hOrig / 2; + const scrollX = w / 2 - wOrig / 2; // eslint-disable-line no-shadow + const scrollY = h / 2 - hOrig / 2; // eslint-disable-line no-shadow if (!newCtr) { const oldDistX = oldCtr.x - oldCanX; @@ -2127,39 +2136,35 @@ editor.init = function () { * @returns {undefined} */ const updateToolButtonState = function () { - let index, button; const bNoFill = (svgCanvas.getColor('fill') === 'none'); const bNoStroke = (svgCanvas.getColor('stroke') === 'none'); const buttonsNeedingStroke = ['#tool_fhpath', '#tool_line']; const buttonsNeedingFillAndStroke = ['#tools_rect .tool_button', '#tools_ellipse .tool_button', '#tool_text', '#tool_path']; + if (bNoStroke) { - for (index in buttonsNeedingStroke) { - button = buttonsNeedingStroke[index]; - if ($(button).hasClass('tool_button_current')) { + buttonsNeedingStroke.forEach((btn) => { + if ($(btn).hasClass('tool_button_current')) { clickSelect(); } - $(button).addClass('disabled'); - } + $(btn).addClass('disabled'); + }); } else { - for (index in buttonsNeedingStroke) { - button = buttonsNeedingStroke[index]; - $(button).removeClass('disabled'); - } + buttonsNeedingStroke.forEach((btn) => { + $(btn).removeClass('disabled'); + }); } if (bNoStroke && bNoFill) { - for (index in buttonsNeedingFillAndStroke) { - button = buttonsNeedingFillAndStroke[index]; - if ($(button).hasClass('tool_button_current')) { + buttonsNeedingFillAndStroke.forEach((btn) => { + if ($(btn).hasClass('tool_button_current')) { clickSelect(); } - $(button).addClass('disabled'); - } + $(btn).addClass('disabled'); + }); } else { - for (index in buttonsNeedingFillAndStroke) { - button = buttonsNeedingFillAndStroke[index]; - $(button).removeClass('disabled'); - } + buttonsNeedingFillAndStroke.forEach((btn) => { + $(btn).removeClass('disabled'); + }); } svgCanvas.runExtensions('toolButtonStateUpdate', /** @type {module:svgcanvas.SvgCanvas#event:ext-toolButtonStateUpdate} */ { @@ -2186,14 +2191,14 @@ editor.init = function () { // This function also updates the opacity and id elements that are in the context panel const updateToolbar = function () { let i, len; - if (selectedElement != null) { + if (!Utils.isNullish(selectedElement)) { switch (selectedElement.tagName) { case 'use': case 'image': case 'foreignObject': break; case 'g': - case 'a': + case 'a': { // Look for common styles const childs = selectedElement.getElementsByTagName('*'); let gWidth = null; @@ -2213,7 +2218,7 @@ editor.init = function () { paintBox.stroke.update(true); break; - default: + } default: { paintBox.fill.update(true); paintBox.stroke.update(true); @@ -2232,10 +2237,11 @@ editor.init = function () { setStrokeOpt($('#linecap_' + attr)[0]); } } + } } // All elements including image and group have opacity - if (selectedElement != null) { + if (!Utils.isNullish(selectedElement)) { const opacPerc = (selectedElement.getAttribute('opacity') || 1.0) * 100; $('#group_opacity').val(opacPerc); $('#opac_slider').slider('option', 'value', opacPerc); @@ -2250,7 +2256,7 @@ editor.init = function () { const updateContextPanel = function () { let elem = selectedElement; // If element has just been deleted, consider it null - if (elem != null && !elem.parentNode) { elem = null; } + if (!Utils.isNullish(elem) && !elem.parentNode) { elem = null; } const currentLayerName = svgCanvas.getCurrentDrawing().getCurrentLayerName(); const currentMode = svgCanvas.getMode(); const unit = curConfig.baseUnit !== 'px' ? curConfig.baseUnit : null; @@ -2260,7 +2266,7 @@ editor.init = function () { $('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,' + '#ellipse_panel, #line_panel, #text_panel, #image_panel, #container_panel,' + ' #use_panel, #a_panel').hide(); - if (elem != null) { + if (!Utils.isNullish(elem)) { const elname = elem.nodeName; // If this is a link with no transform and one child, pretend // its child is selected @@ -2436,7 +2442,7 @@ editor.init = function () { } menuItems[(tagName === 'g' ? 'en' : 'dis') + 'ableContextMenuItems']('#ungroup'); menuItems[((tagName === 'g' || !multiselected) ? 'dis' : 'en') + 'ableContextMenuItems']('#group'); - // if (elem != null) + // if (!Utils.isNullish(elem)) } else if (multiselected) { $('#multiselected_panel').show(); menuItems @@ -2506,9 +2512,9 @@ editor.init = function () { } const isNode = mode === 'pathedit'; // if elems[1] is present, then we have more than one element - selectedElement = (elems.length === 1 || elems[1] == null ? elems[0] : null); - multiselected = (elems.length >= 2 && elems[1] != null); - if (selectedElement != null) { + selectedElement = (elems.length === 1 || Utils.isNullish(elems[1]) ? elems[0] : null); + multiselected = (elems.length >= 2 && !Utils.isNullish(elems[1])); + if (!Utils.isNullish(selectedElement)) { // unless we're already in always set the mode of the editor to select because // upon creation of a text element the editor is switched into // select mode and this event fires - we need our UI to be in sync @@ -2516,7 +2522,7 @@ editor.init = function () { if (!isNode) { updateToolbar(); } - } // if (elem != null) + } // if (!Utils.isNullish(elem)) // Deal with pathedit mode togglePathEditMode(isNode, elems); @@ -2545,7 +2551,7 @@ editor.init = function () { return; } - multiselected = (elems.length >= 2 && elems[1] != null); + multiselected = (elems.length >= 2 && !Utils.isNullish(elems[1])); // Only updating fields for single elements for now if (!multiselected) { switch (mode) { @@ -2601,7 +2607,7 @@ editor.init = function () { } // Update selectedElement if element is no longer part of the image. // This occurs for the text elements in Firefox - } else if (elem && selectedElement && selectedElement.parentNode == null) { + } else if (elem && selectedElement && Utils.isNullish(selectedElement.parentNode)) { // || elem && elem.tagName == "path" && !multiselected) { // This was added in r1430, but not sure why selectedElement = elem; } @@ -2820,7 +2826,7 @@ editor.init = function () { options = tool; } else { // If flyout is selected, allow shift key to iterate through subitems - i = parseInt(i, 10); + i = parseInt(i); // Use `allHolders` to include both extension `includeWith` and toolbarButtons options = allHolders[opts.parent][i + 1] || holders[opts.parent][0]; @@ -3048,7 +3054,7 @@ editor.init = function () { let html; // TODO: Allow support for other types, or adding to existing tool switch (tool.type) { - case 'tool_button': + case 'tool_button': { html = '
' + tool.id + '
'; const div = $(html).appendTo(panel); if (tool.events) { @@ -3057,7 +3063,7 @@ editor.init = function () { }); } break; - case 'select': + } case 'select': { html = '' + '').click(function () { - const f = this; + const f = this; // eslint-disable-line consistent-this editor.openPrep(function (ok) { if (!ok) { return; } svgCanvas.clear(); @@ -5976,7 +5986,7 @@ editor.loadFromURL = function (url, opts) { $.ajax({ url, dataType: 'text', - cache: !!cache, + cache: Boolean(cache), beforeSend () { $.process_cancel(uiStrings.notification.loadingImage); }, @@ -6051,7 +6061,7 @@ const messageQueue = []; * @fires module:svgcanvas.SvgCanvas#event:message * @returns {undefined} */ -const messageListener = ({data, origin}) => { +const messageListener = ({data, origin}) => { // eslint-disable-line no-shadow // console.log('data, origin, extensionsAdded', data, origin, extensionsAdded); const messageObj = {data, origin}; if (!extensionsAdded) { diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index bd5a7317..0ea2ba86 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -18,7 +18,7 @@ // Todo: Obtain/adapt latest jsPDF to utilize ES Module for `jsPDF`/avoid global import './svgpathseg.js'; -import jqPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr` +import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr` import * as draw from './draw.js'; import * as pathModule from './path.js'; @@ -32,9 +32,10 @@ import { getBBoxOfElementAsPath, convertToPath, toXml, encode64, decode64, dataURLToObjectURL, createObjectURL, getVisibleElements, dropXMLInteralSubset, - init as utilsInit, getBBox as utilsGetBBox, getStrokedBBoxDefaultVisible + init as utilsInit, getBBox as utilsGetBBox, getStrokedBBoxDefaultVisible, + isNullish } from './utilities.js'; -import * as history from './history.js'; +import * as hstry from './history.js'; import { transformPoint, matrixMultiply, hasMatrixTransform, transformListToTransform, getMatrix, snapToAngle, isIdentity, rectsIntersect, transformBox @@ -60,24 +61,25 @@ import { } from './recalculate.js'; import { getSelectorManager, + Selector, init as selectInit } from './select.js'; -const $ = jqPluginSVG(jQuery); +const $ = jQueryPluginSVG(jQuery); const { MoveElementCommand, InsertElementCommand, RemoveElementCommand, ChangeElementCommand, BatchCommand, UndoManager, HistoryEventTypes -} = history; +} = hstry; if (!window.console) { window.console = {}; - window.console.log = function (str) {}; - window.console.dir = function (str) {}; + window.console.log = function (str) { /* */ }; + window.console.dir = function (str) { /* */ }; } if (window.opera) { window.console.log = function (str) { window.opera.postError(str); }; - window.console.dir = function (str) {}; + window.console.dir = function (str) { /* */ }; } /** @@ -131,7 +133,7 @@ if (config) { // Array with width/height of canvas const {dimensions} = curConfig; -const canvas = this; +const canvas = this; // eslint-disable-line consistent-this // "document" element associated with the container (same as window.document using default svg-editor.js) // NOTE: This is not actually a SVG document, but an HTML document. @@ -396,7 +398,7 @@ const getSelectedElements = this.getSelectedElems = function () { return selectedElements; }; -const pathActions = pathModule.pathActions; +const {pathActions} = pathModule; /** * This should actually be an intersection as all interfaces should be met. @@ -503,8 +505,8 @@ const undoMgr = canvas.undoMgr = new UndoManager({ } if (cmdType === InsertElementCommand.type()) { if (isApply) { restoreRefElems(cmd.elem); } - } else { - if (!isApply) { restoreRefElems(cmd.elem); } + } else if (!isApply) { + restoreRefElems(cmd.elem); } if (cmd.elem.tagName === 'use') { setUseData(cmd.elem); @@ -562,7 +564,7 @@ const getCurrentZoom = this.getZoom = function () { return currentZoom; }; * @implements {module:path.EditorContext#round} */ const round = this.round = function (val) { - return parseInt(val * currentZoom, 10) / currentZoom; + return parseInt(val * currentZoom) / currentZoom; }; selectInit( @@ -607,12 +609,13 @@ const getId = canvas.getId = function () { * @implements {module:draw.DrawCanvasInit#call|module:path.EditorContext#call} * @param {"selected"|"changed"|"contextset"|"pointsAdded"|"extension_added"|"extensions_added"|"message"|"transition"|"zoomed"|"updateCanvas"|"zoomDone"|"saved"|"exported"|"exportedPDF"|"setnonce"|"unsetnonce"|"cleared"} ev - String with the event name * @param {module:svgcanvas.SvgCanvas#event:GenericCanvasEvent} arg - Argument to pass through to the callback function. -* @returns {undefined} +* @returns {module:svgcanvas.EventHandlerReturn|undefined} */ const call = function (ev, arg) { if (events[ev]) { return events[ev](window, arg); } + return undefined; }; /** @@ -624,7 +627,7 @@ const call = function (ev, arg) { */ const clearSelection = this.clearSelection = function (noCall) { selectedElements.forEach((elem) => { - if (elem == null) { + if (isNullish(elem)) { return; } selectorManager.releaseSelector(elem); @@ -646,7 +649,7 @@ const addToSelection = this.addToSelection = function (elemsToAdd, showGrips) { let j = 0; while (j < selectedElements.length) { - if (selectedElements[j] == null) { + if (isNullish(selectedElements[j])) { break; } ++j; @@ -692,15 +695,16 @@ const addToSelection = this.addToSelection = function (elemsToAdd, showGrips) { selectedElements.sort(function (a, b) { if (a && b && a.compareDocumentPosition) { - return 3 - (b.compareDocumentPosition(a) & 6); + return 3 - (b.compareDocumentPosition(a) & 6); // eslint-disable-line no-bitwise } - if (a == null) { + if (isNullish(a)) { return 1; } + return 0; }); // Make sure first elements are not null - while (selectedElements[0] == null) { + while (isNullish(selectedElements[0])) { selectedElements.shift(0); } }; @@ -717,7 +721,7 @@ const getOpacity = function () { * @implements {module:path.EditorContext#getMouseTarget} */ const getMouseTarget = this.getMouseTarget = function (evt) { - if (evt == null) { + if (isNullish(evt)) { return null; } let mouseTarget = evt.target; @@ -881,8 +885,7 @@ $(opacAni).attr({ const restoreRefElems = function (elem) { // Look for missing reference elements, restore any found const attrs = $(elem).attr(refAttrs); - for (const o in attrs) { - const val = attrs[o]; + Object.values(attrs).forEach((val) => { if (val && val.startsWith('url(')) { const id = getUrlFromAttr(val).substr(1); const ref = getElem(id); @@ -891,7 +894,7 @@ const restoreRefElems = function (elem) { delete removedElements[id]; } } - } + }); const childs = elem.getElementsByTagName('*'); @@ -1158,9 +1161,9 @@ this.addExtension = async function (name, extInitFunc, importLocale) { extensions[name] = extObj; return call('extension_added', extObj); - } else { - console.log('Cannot add extension "' + name + '", an extension by that name already exists.'); } + console.log('Cannot add extension "' + name + '", an extension by that name already exists.'); + return undefined; }; /** @@ -1176,7 +1179,7 @@ this.addExtension = async function (name, extInitFunc, importLocale) { * @returns {Element[]|NodeList} Bbox elements */ const getIntersectionList = this.getIntersectionList = function (rect) { - if (rubberBox == null) { return null; } + if (isNullish(rubberBox)) { return null; } const parent = currentGroup || getCurrentDrawing().getCurrentLayer(); @@ -1201,14 +1204,14 @@ const getIntersectionList = this.getIntersectionList = function (rect) { if (!isIE) { if (typeof svgroot.getIntersectionList === 'function') { // Offset the bbox of the rubber box by the offset of the svgcontent element. - rubberBBox.x += parseInt(svgcontent.getAttribute('x'), 10); - rubberBBox.y += parseInt(svgcontent.getAttribute('y'), 10); + rubberBBox.x += parseInt(svgcontent.getAttribute('x')); + rubberBBox.y += parseInt(svgcontent.getAttribute('y')); resultList = svgroot.getIntersectionList(rubberBBox, parent); } } - if (resultList == null || typeof resultList.item !== 'function') { + if (isNullish(resultList) || typeof resultList.item !== 'function') { resultList = []; if (!curBBoxes.length) { @@ -1385,11 +1388,18 @@ canvas.call = call; * @type {module:svgcanvas.SvgCanvas#event:selected|module:svgcanvas.SvgCanvas#event:changed|module:svgcanvas.SvgCanvas#event:contextset|module:svgcanvas.SvgCanvas#event:pointsAdded|module:svgcanvas.SvgCanvas#event:extension_added|module:svgcanvas.SvgCanvas#event:extensions_added|module:svgcanvas.SvgCanvas#event:message|module:svgcanvas.SvgCanvas#event:transition|module:svgcanvas.SvgCanvas#event:zoomed|module:svgcanvas.SvgCanvas#event:updateCanvas|module:svgcanvas.SvgCanvas#event:saved|module:svgcanvas.SvgCanvas#event:exported|module:svgcanvas.SvgCanvas#event:exportedPDF|module:svgcanvas.SvgCanvas#event:setnonce|module:svgcanvas.SvgCanvas#event:unsetnonce|undefined} */ +/** + * The promise return, if present, resolves to `undefined` + * (`extension_added`, `exported`, `saved`) + * @typedef {Promise|undefined} module:svgcanvas.EventHandlerReturn +*/ + /** * @callback module:svgcanvas.EventHandler * @param {external:Window} win * @param {module:svgcanvas.SvgCanvas#event:GenericCanvasEvent} arg * @listens module:svgcanvas.SvgCanvas#event:GenericCanvasEvent +* @returns {module:svgcanvas.EventHandlerReturn} */ /** @@ -1503,7 +1513,7 @@ this.setRotationAngle = function (val, preventUndo) { // } const selector = selectorManager.requestSelector(selectedElements[0]); selector.resize(); - selector.updateGripCursors(val); + Selector.updateGripCursors(val); }; /** @@ -1574,7 +1584,7 @@ const selectOnly = this.selectOnly = function (elems, showGrips) { * @returns {undefined} */ /* const removeFromSelection = */ this.removeFromSelection = function (elemsToRemove) { - if (selectedElements[0] == null) { return; } + if (isNullish(selectedElements[0])) { return; } if (!elemsToRemove.length) { return; } // find every element and remove it from our array copy @@ -1732,7 +1742,7 @@ const mouseDown = function (evt) { // if it is a selector grip, then it must be a single element selected, // set the mouseTarget to that and update the mode to rotate/resize - if (mouseTarget === selectorManager.selectorParentGroup && selectedElements[0] != null) { + if (mouseTarget === selectorManager.selectorParentGroup && !isNullish(selectedElements[0])) { const grip = evt.target; const griptype = elData(grip, 'type'); // rotating @@ -1774,7 +1784,7 @@ const mouseDown = function (evt) { // insert a dummy transform so if the element(s) are moved it will have // a transform to use for its translate for (i = 0; i < selectedElements.length; ++i) { - if (selectedElements[i] == null) { continue; } + if (isNullish(selectedElements[i])) { continue; } const slist = getTransformList(selectedElements[i]); if (slist.numberOfItems) { slist.insertItemBefore(svgroot.createSVGTransform(), 0); @@ -1786,7 +1796,7 @@ const mouseDown = function (evt) { } else if (!rightClick) { clearSelection(); currentMode = 'multiselect'; - if (rubberBox == null) { + if (isNullish(rubberBox)) { rubberBox = selectorManager.getRubberBandBox(); } rStartX *= currentZoom; @@ -1807,7 +1817,7 @@ const mouseDown = function (evt) { break; case 'zoom': started = true; - if (rubberBox == null) { + if (isNullish(rubberBox)) { rubberBox = selectorManager.getRubberBandBox(); } assignAttributes(rubberBox, { @@ -1818,7 +1828,7 @@ const mouseDown = function (evt) { display: 'inline' }, 100); break; - case 'resize': + case 'resize': { started = true; startX = x; startY = y; @@ -1876,7 +1886,7 @@ const mouseDown = function (evt) { } } break; - case 'fhellipse': + } case 'fhellipse': case 'fhrect': case 'fhpath': start.x = realX; @@ -1902,7 +1912,7 @@ const mouseDown = function (evt) { freehand.miny = realY; freehand.maxy = realY; break; - case 'image': + case 'image': { started = true; const newImage = addSVGElementFromJson({ element: 'image', @@ -1919,7 +1929,7 @@ const mouseDown = function (evt) { setHref(newImage, lastGoodImgUrl); preventClickDefault(newImage); break; - case 'square': + } case 'square': // FIXME: once we create the rect, we lose information that this was a square // (for resizing purposes this could be important) // Fallthrough @@ -2119,7 +2129,7 @@ const mouseMove = function (evt) { len = selectedElements.length; for (i = 0; i < len; ++i) { selected = selectedElements[i]; - if (selected == null) { break; } + if (isNullish(selected)) { break; } // if (i === 0) { // const box = utilsGetBBox(selected); // selectedBBoxes[i].x = box.x + dx; @@ -2411,9 +2421,8 @@ const mouseMove = function (evt) { start = {x: end.x, y: end.y}; break; // update path stretch line coordinates - } case 'path': { - } - // fall through +} case 'path': { // eslint-disable-line no-empty +} // fall through case 'pathedit': { x *= currentZoom; y *= currentZoom; @@ -2545,16 +2554,16 @@ const mouseUp = function (evt) { // intentionally fall-through to select here case 'resize': case 'multiselect': - if (rubberBox != null) { + if (!isNullish(rubberBox)) { rubberBox.setAttribute('display', 'none'); curBBoxes = []; } currentMode = 'select'; // Fallthrough case 'select': - if (selectedElements[0] != null) { + if (!isNullish(selectedElements[0])) { // if we only have one selected element - if (selectedElements[1] == null) { + if (isNullish(selectedElements[1])) { // set our current stroke/fill properties to the element's const selected = selectedElements[0]; switch (selected.tagName) { @@ -2589,7 +2598,7 @@ const mouseUp = function (evt) { if (realX !== rStartX || realY !== rStartY) { const len = selectedElements.length; for (let i = 0; i < len; ++i) { - if (selectedElements[i] == null) { break; } + if (isNullish(selectedElements[i])) { break; } if (!selectedElements[i].firstChild) { // Not needed for groups (incorrectly resizes elems), possibly not needed at all? selectorManager.requestSelector(selectedElements[i]).resize(); @@ -2598,7 +2607,7 @@ const mouseUp = function (evt) { // no change in position/size, so maybe we should move to pathedit } else { t = evt.target; - if (selectedElements[0].nodeName === 'path' && selectedElements[1] == null) { + if (selectedElements[0].nodeName === 'path' && isNullish(selectedElements[1])) { pathActions.select(selectedElements[0]); // if it was a path // else, if it was selected and this is a shift-click, remove it from selection @@ -2622,7 +2631,7 @@ const mouseUp = function (evt) { } return; case 'zoom': - if (rubberBox != null) { + if (!isNullish(rubberBox)) { rubberBox.setAttribute('display', 'none'); } const factor = evt.shiftKey ? 0.5 : 2; @@ -2774,7 +2783,7 @@ const mouseUp = function (evt) { } }); - if (!keep && element != null) { + if (!keep && !isNullish(element)) { getCurrentDrawing().releaseId(getId()); element.remove(); element = null; @@ -2799,7 +2808,7 @@ const mouseUp = function (evt) { canvas.setMode('select'); selectOnly([t], true); } - } else if (element != null) { + } else if (!isNullish(element)) { /** * @name module:svgcanvas.SvgCanvas#addedNew * @type {boolean} @@ -2936,8 +2945,8 @@ $(container).bind( // content offset from canvas in screen pixels const wOffset = workarea.offset(); - const wOffsetLeft = wOffset['left'] + rulerwidth; - const wOffsetTop = wOffset['top'] + rulerwidth; + const wOffsetLeft = wOffset.left + rulerwidth; + const wOffsetTop = wOffset.top + rulerwidth; const delta = (evt.wheelDelta) ? evt.wheelDelta : (evt.detail) ? -evt.detail : 0; if (!delta) { return; } @@ -3508,7 +3517,7 @@ const removeUnusedDefElems = this.removeUnusedDefElems = function () { */ this.svgCanvasToString = function () { // keep calling it until there are none to remove - while (removeUnusedDefElems() > 0) {} + while (removeUnusedDefElems() > 0) {} // eslint-disable-line no-empty pathActions.clear(true); @@ -3572,7 +3581,9 @@ this.svgToString = function (elem, indent) { const attrs = Array.from(elem.attributes); let i; const childs = elem.childNodes; - attrs.sort((a, b) => a.name > b.name ? -1 : 1); + attrs.sort((a, b) => { + return a.name > b.name ? -1 : 1; + }); for (i = 0; i < indent; i++) { out.push(' '); } out.push('<'); out.push(elem.nodeName); @@ -3693,14 +3704,14 @@ this.svgToString = function (elem, indent) { out.push('\n'); out.push(this.svgToString(childs.item(i), indent)); break; - case 3: // text node + case 3: { // text node const str = child.nodeValue.replace(/^\s+|\s+$/g, ''); if (str !== '') { bOneLine = true; out.push(String(toXml(str))); } break; - case 4: // cdata node + } case 4: // cdata node out.push('\n'); out.push(new Array(indent + 1).join(' ')); out.push(' innerw) { @@ -4864,12 +4875,13 @@ this.setConfig = function (opts) { /** * @function module:svgcanvas.SvgCanvas#getTitle -* @param {Element} elem -* @returns {string|undefined} the current group/SVG's title contents +* @param {Element} [elem] +* @returns {string|undefined} the current group/SVG's title contents or +* `undefined` if no element is passed nd there are no selected elements. */ this.getTitle = function (elem) { elem = elem || selectedElements[0]; - if (!elem) { return; } + if (!elem) { return undefined; } elem = $(elem).data('gsvg') || $(elem).data('symbol') || elem; const childs = elem.childNodes; for (let i = 0; i < childs.length; i++) { @@ -5088,24 +5100,29 @@ this.setBBoxZoom = function (val, editorW, editorH) { } switch (val) { - case 'selection': - if (!selectedElements[0]) { return; } - const selectedElems = $.map(selectedElements, function (n) { if (n) { return n; } }); + case 'selection': { + if (!selectedElements[0]) { return undefined; } + const selectedElems = $.map(selectedElements, function (n) { + if (n) { + return n; + } + return undefined; + }); bb = getStrokedBBoxDefaultVisible(selectedElems); break; - case 'canvas': + } case 'canvas': { const res = getResolution(); spacer = 0.95; bb = {width: res.w, height: res.h, x: 0, y: 0}; break; - case 'content': + } case 'content': bb = getStrokedBBoxDefaultVisible(); break; case 'layer': bb = getStrokedBBoxDefaultVisible(getVisibleElements(getCurrentDrawing().getCurrentLayer())); break; default: - return; + return undefined; } return calcZoom(bb); }; @@ -5262,14 +5279,12 @@ this.setColor = function (type, val, preventUndo) { if (elem) { if (elem.tagName === 'g') { walkTree(elem, addNonG); - } else { - if (type === 'fill') { - if (elem.tagName !== 'polyline' && elem.tagName !== 'line') { - elems.push(elem); - } - } else { + } else if (type === 'fill') { + if (elem.tagName !== 'polyline' && elem.tagName !== 'line') { elems.push(elem); } + } else { + elems.push(elem); } } } @@ -5651,14 +5666,12 @@ canvas.setBlurOffsets = function (filter, stdDev) { width: '200%', height: '200%' }, 100); - } else { - // Removing these attributes hides text in Chrome (see Issue 579) - if (!isWebkit()) { + // Removing these attributes hides text in Chrome (see Issue 579) + } else if (!isWebkit()) { filter.removeAttribute('x'); filter.removeAttribute('y'); filter.removeAttribute('width'); filter.removeAttribute('height'); - } } }; @@ -5739,8 +5752,8 @@ canvas.setBlur = function (val, complete) { this.getBold = function () { // should only have one element selected const selected = selectedElements[0]; - if (selected != null && selected.tagName === 'text' && - selectedElements[1] == null) { + if (!isNullish(selected) && selected.tagName === 'text' && + isNullish(selectedElements[1])) { return (selected.getAttribute('font-weight') === 'bold'); } return false; @@ -5754,8 +5767,8 @@ this.getBold = function () { */ this.setBold = function (b) { const selected = selectedElements[0]; - if (selected != null && selected.tagName === 'text' && - selectedElements[1] == null) { + if (!isNullish(selected) && selected.tagName === 'text' && + isNullish(selectedElements[1])) { changeSelectedAttribute('font-weight', b ? 'bold' : 'normal'); } if (!selectedElements[0].textContent) { @@ -5770,8 +5783,8 @@ this.setBold = function (b) { */ this.getItalic = function () { const selected = selectedElements[0]; - if (selected != null && selected.tagName === 'text' && - selectedElements[1] == null) { + if (!isNullish(selected) && selected.tagName === 'text' && + isNullish(selectedElements[1])) { return (selected.getAttribute('font-style') === 'italic'); } return false; @@ -5785,8 +5798,8 @@ this.getItalic = function () { */ this.setItalic = function (i) { const selected = selectedElements[0]; - if (selected != null && selected.tagName === 'text' && - selectedElements[1] == null) { + if (!isNullish(selected) && selected.tagName === 'text' && + isNullish(selectedElements[1])) { changeSelectedAttribute('font-style', i ? 'italic' : 'normal'); } if (!selectedElements[0].textContent) { @@ -5863,7 +5876,7 @@ this.setFontSize = function (val) { */ this.getText = function () { const selected = selectedElements[0]; - if (selected == null) { return ''; } + if (isNullish(selected)) { return ''; } return selected.textContent; }; @@ -5967,7 +5980,7 @@ this.setLinkURL = function (val) { */ this.setRectRadius = function (val) { const selected = selectedElements[0]; - if (selected != null && selected.tagName === 'rect') { + if (!isNullish(selected) && selected.tagName === 'rect') { const r = selected.getAttribute('rx'); if (r !== String(val)) { selected.setAttribute('rx', val); @@ -6023,12 +6036,12 @@ this.setSegType = function (newType) { * Otherwise the resulting path element is returned. */ this.convertToPath = function (elem, getBBox) { - if (elem == null) { + if (isNullish(elem)) { const elems = selectedElements; - $.each(elems, function (i, elem) { - if (elem) { canvas.convertToPath(elem); } + $.each(elems, function (i, el) { + if (el) { canvas.convertToPath(el); } }); - return; + return undefined; } if (getBBox) { return getBBoxOfElementAsPath(elem, addSVGElementFromJson, pathActions); @@ -6047,7 +6060,7 @@ this.convertToPath = function (elem, getBBox) { opacity: curShape.opacity, visibility: 'hidden' }; - return convertToPath(elem, attrs, addSVGElementFromJson, pathActions, clearSelection, addToSelection, history, addCommandToHistory); + return convertToPath(elem, attrs, addSVGElementFromJson, pathActions, clearSelection, addToSelection, hstry, addCommandToHistory); }; /** @@ -6066,11 +6079,11 @@ const changeSelectedAttributeNoUndo = function (attr, newValue, elems) { elems = elems || selectedElements; let i = elems.length; const noXYElems = ['g', 'polyline', 'path']; - const goodGAttrs = ['transform', 'opacity', 'filter']; + // const goodGAttrs = ['transform', 'opacity', 'filter']; while (i--) { let elem = elems[i]; - if (elem == null) { continue; } + if (isNullish(elem)) { continue; } // Set x,y vals on elements that don't have them if ((attr === 'x' || attr === 'y') && noXYElems.includes(elem.tagName)) { @@ -6082,10 +6095,10 @@ const changeSelectedAttributeNoUndo = function (attr, newValue, elems) { } // only allow the transform/opacity/filter attribute to change on elements, slightly hacky - // TODO: FIXME: This doesn't seem right. Where's the body of this if statement? - if (elem.tagName === 'g' && goodGAttrs.includes(attr)) {} + // TODO: FIXME: Missing statement body + // if (elem.tagName === 'g' && goodGAttrs.includes(attr)) {} let oldval = attr === '#text' ? elem.textContent : elem.getAttribute(attr); - if (oldval == null) { oldval = ''; } + if (isNullish(oldval)) { oldval = ''; } if (oldval !== String(newValue)) { if (attr === '#text') { // const oldW = utilsGetBBox(elem).width; @@ -6209,7 +6222,7 @@ this.deleteSelectedElements = function () { for (let i = 0; i < len; ++i) { const selected = selectedElements[i]; - if (selected == null) { break; } + if (isNullish(selected)) { break; } let parent = selected.parentNode; let t = selected; @@ -6406,7 +6419,7 @@ this.groupSelectedElements = function (type, urlArg) { let i = selectedElements.length; while (i--) { let elem = selectedElements[i]; - if (elem == null) { continue; } + if (isNullish(elem)) { continue; } if (elem.parentNode.tagName === 'a' && elem.parentNode.childNodes.length === 1) { elem = elem.parentNode; @@ -6690,7 +6703,7 @@ this.ungroupSelectedElement = function () { */ this.moveToTopSelectedElement = function () { const [selected] = selectedElements; - if (selected != null) { + if (!isNullish(selected)) { let t = selected; const oldParent = t.parentNode; const oldNextSibling = t.nextSibling; @@ -6713,7 +6726,7 @@ this.moveToTopSelectedElement = function () { */ this.moveToBottomSelectedElement = function () { const [selected] = selectedElements; - if (selected != null) { + if (!isNullish(selected)) { let t = selected; const oldParent = t.parentNode; const oldNextSibling = t.nextSibling; @@ -6759,9 +6772,9 @@ this.moveUpDownSelected = function (dir) { if (this === selected) { foundCur = true; } - return; + return true; } - closest = this; + closest = this; // eslint-disable-line consistent-this return false; }); if (!closest) { return; } @@ -6799,7 +6812,7 @@ this.moveSelectedElements = function (dx, dy, undoable) { let i = selectedElements.length; while (i--) { const selected = selectedElements[i]; - if (selected != null) { + if (!isNullish(selected)) { // if (i === 0) { // selectedBBoxes[0] = utilsGetBBox(selected); // } @@ -6867,7 +6880,7 @@ this.cloneSelectedElements = function (x, y) { selectedElements.sort(sortfunction); for (i = 0; i < len; ++i) { elem = selectedElements[i]; - if (elem == null) { break; } + if (isNullish(elem)) { break; } } // use slice to quickly get the subset of elements we need const copiedElements = selectedElements.slice(0, i); @@ -6905,7 +6918,7 @@ this.alignSelectedElements = function (type, relativeTo) { miny = Number.MAX_VALUE, maxy = Number.MIN_VALUE; let curwidth = Number.MIN_VALUE, curheight = Number.MIN_VALUE; for (let i = 0; i < len; ++i) { - if (selectedElements[i] == null) { break; } + if (isNullish(selectedElements[i])) { break; } const elem = selectedElements[i]; bboxes[i] = getStrokedBBoxDefaultVisible([elem]); @@ -6958,7 +6971,7 @@ this.alignSelectedElements = function (type, relativeTo) { const dx = new Array(len); const dy = new Array(len); for (let i = 0; i < len; ++i) { - if (selectedElements[i] == null) { break; } + if (isNullish(selectedElements[i])) { break; } // const elem = selectedElements[i]; const bbox = bboxes[i]; dx[i] = 0; @@ -7118,7 +7131,7 @@ this.cycleElement = function (next) { let elem = false; const allElems = getVisibleElements(currentGroup || getCurrentDrawing().getCurrentLayer()); if (!allElems.length) { return; } - if (curElem == null) { + if (isNullish(curElem)) { num = next ? allElems.length - 1 : 0; elem = allElems[num]; } else { diff --git a/editor/svgicons/jQuery.svgIcons.js b/editor/svgicons/jQuery.svgIcons.js index 2a4906e0..546cd602 100644 --- a/editor/svgicons/jQuery.svgIcons.js +++ b/editor/svgicons/jQuery.svgIcons.js @@ -1,3 +1,5 @@ +// Todo: Move to own module (and have it import a modular base64 encoder) +import {encode64} from '../utilities.js'; /** * SVG Icon Loader 2.0 * @@ -96,18 +98,22 @@ $(function() { * @param {external:jQuery} $ Its keys include all icon IDs and the values, the icon as a jQuery object * @returns {external:jQuery} The enhanced jQuery object */ -export default function ($) { +export default function jQueryPluginSVGIcons ($) { const svgIcons = {}; let fixIDs; + /** + * List of raster images with each + * key being the SVG icon ID to replace, and the value the image file name + * @typedef {PlainObject.} external:jQuery.svgIcons.Fallback + */ /** * @function external:jQuery.svgIcons * @param {string} file The location of a local SVG or SVGz file * @param {PlainObject} [opts] * @param {Float} [opts.w] The icon widths * @param {Float} [opts.h] The icon heights - * @param {PlainObject.} [opts.fallback] List of raster images with each - key being the SVG icon ID to replace, and the value the image file name + * @param {external:jQuery.svgIcons.Fallback} [opts.fallback] * @param {string} [opts.fallback_path] The path to use for all images listed under "fallback" * @param {boolean} [opts.replace] If set to `true`, HTML elements will be replaced by, @@ -132,8 +138,10 @@ export default function ($) { iconW = opts.w || 24, iconH = opts.h || 24; let elems, svgdoc, testImg, - iconsMade = false, dataLoaded = false, loadAttempts = 0; - const isOpera = !!window.opera, + iconsMade = false, + dataLoaded = false, + loadAttempts = 0; + const isOpera = Boolean(window.opera), // ua = navigator.userAgent, // isSafari = (ua.includes('Safari/') && !ua.includes('Chrome/')), dataPre = 'data:image/svg+xml;charset=utf-8;base64,'; @@ -169,24 +177,28 @@ export default function ($) { $(function () { useFallback(); }); - } else { - if (err.responseText) { - svgdoc = parser.parseFromString(err.responseText, 'text/xml'); + } else if (err.responseText) { + svgdoc = parser.parseFromString(err.responseText, 'text/xml'); - if (!svgdoc.childNodes.length) { - $(useFallback); - } - $(function () { - getIcons('ajax'); - }); - } else { + if (!svgdoc.childNodes.length) { $(useFallback); } + $(function () { + getIcons('ajax'); + }); + } else { + $(useFallback); } } }); } + /** + * + * @param {"ajax"|0|undefined} evt + * @param {boolean} [noWait] + * @returns {undefined} + */ function getIcons (evt, noWait) { if (evt !== 'ajax') { if (dataLoaded) return; @@ -231,6 +243,14 @@ export default function ($) { } } + /** + * + * @param {external:jQuery} target + * @param {external:jQuery} icon A wrapped `defs` or Image + * @param {string} id SVG icon ID + * @param {string} setID + * @returns {undefined} + */ function setIcon (target, icon, id, setID) { if (isOpera) icon.css('visibility', 'hidden'); if (opts.replace) { @@ -249,6 +269,11 @@ export default function ($) { } let holder; + /** + * @param {external:jQuery} icon A wrapped `defs` or Image + * @param {string} id SVG icon ID + * @returns {undefined} + */ function addIcon (icon, id) { if (opts.id_match === undefined || opts.id_match !== false) { setIcon(holder, icon, id, true); @@ -256,7 +281,13 @@ export default function ($) { svgIcons[id] = icon; } - function makeIcons (toImage, fallback) { + /** + * + * @param {boolean} [toImage] + * @param {external:jQuery.svgIcons.Fallback} [fallback] + * @returns {undefined} + */ + function makeIcons (toImage = false, fallback) { if (iconsMade) return; if (opts.no_img) toImage = false; @@ -359,13 +390,14 @@ export default function ($) { let idElems; if (isOpera) { idElems = defs.find('*').filter(function () { - return !!this.id; + return Boolean(this.id); }); } else { idElems = defs.find('[id]'); } - const allElems = svgEl[0].getElementsByTagName('*'), len = allElems.length; + const allElems = svgEl[0].getElementsByTagName('*'), + len = allElems.length; idElems.each(function (i) { const {id} = this; @@ -409,49 +441,20 @@ export default function ($) { return svgEl; }; + /** + * @returns {undefined} + */ function useFallback () { if (file.includes('.svgz')) { const regFile = file.replace('.svgz', '.svg'); if (window.console) { - console.log('.svgz failed, trying with .svg'); + console.log('.svgz failed, trying with .svg'); // eslint-disable-line no-console } $.svgIcons(regFile, opts); } else if (opts.fallback) { makeIcons(false, opts.fallback); } } - - function encode64 (input) { - // base64 strings are 4/3 larger than the original string - if (window.btoa) return window.btoa(input); - const _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - const output = new Array(Math.floor((input.length + 2) / 3) * 4); - - let i = 0, p = 0; - do { - const chr1 = input.charCodeAt(i++); - const chr2 = input.charCodeAt(i++); - const chr3 = input.charCodeAt(i++); - - const enc1 = chr1 >> 2; - const enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - - let enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - let enc4 = chr3 & 63; - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - - output[p++] = _keyStr.charAt(enc1); - output[p++] = _keyStr.charAt(enc2); - output[p++] = _keyStr.charAt(enc3); - output[p++] = _keyStr.charAt(enc4); - } while (i < input.length); - - return output.join(''); - } }; /** diff --git a/editor/svgpathseg.js b/editor/svgpathseg.js index e208fc39..0a26b6d3 100644 --- a/editor/svgpathseg.js +++ b/editor/svgpathseg.js @@ -16,6 +16,10 @@ * including the latest spec changes which were implemented in Firefox 43 and * Chrome 46. */ +/* eslint-disable no-shadow, class-methods-use-this */ +// Linting: We avoid `no-shadow` as ESLint thinks these are still available globals +// Linting: We avoid `class-methods-use-this` as this is a polyfill that must +// follow the conventions (() => { if (!('SVGPathSeg' in window)) { // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg @@ -591,7 +595,7 @@ if (!('SVGPathSegList' in window) || !('appendItem' in window.SVGPathSegList.pro return []; } - const owningPathSegList = this; + const owningPathSegList = this; // eslint-disable-line consistent-this class Builder { constructor () { @@ -883,10 +887,10 @@ if (!('SVGPathSegList' in window) || !('appendItem' in window.SVGPathSegList.pro } case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: { const points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; return new SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1); - } case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: + } case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: { const points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; return new SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1); - case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + } case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: return new SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber()); case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: return new SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); diff --git a/editor/svgtransformlist.js b/editor/svgtransformlist.js index ed11f714..1b99d0d3 100644 --- a/editor/svgtransformlist.js +++ b/editor/svgtransformlist.js @@ -12,7 +12,11 @@ import {supportsNativeTransformLists} from './browser.js'; const svgroot = document.createElementNS(NS.SVG, 'svg'); -// Helper function. +/** + * Helper function to convert `SVGTransform` to a string. + * @param {SVGTransform} xform + * @returns {string} + */ function transformToString (xform) { const m = xform.matrix; let text = ''; @@ -114,8 +118,9 @@ let listMap_ = {}; * These methods do not currently raise any exceptions. * These methods also do not check that transforms are being inserted. This is basically * implementing as much of SVGTransformList that we need to get the job done. +* @implements {module:SVGTransformList.SVGEditTransformList} */ -export class SVGTransformList { +export class SVGTransformList {// eslint-disable-line no-shadow /** * @param {Element} elem */ @@ -170,7 +175,7 @@ export class SVGTransformList { } else if (name === 'rotate' && values.length === 1) { values.push(0, 0); } - xform[fname].apply(xform, values); + xform[fname](...values); this._list.appendItem(xform); } } @@ -179,20 +184,15 @@ export class SVGTransformList { if (item) { // Check if this transform is already in a transformlist, and // remove it if so. - let found = false; - for (const id in listMap_) { - const tl = listMap_[id]; + Object.values(listMap_).some((tl) => { for (let i = 0, len = tl._xforms.length; i < len; ++i) { if (tl._xforms[i] === item) { - found = true; tl.removeItem(i); - break; + return true; } } - if (found) { - break; - } - } + return false; + }); } }; @@ -330,7 +330,7 @@ export const resetListMap = function () { * @param {Element} elem - a DOM Element * @returns {undefined} */ -export let removeElementFromListMap = function (elem) { +export let removeElementFromListMap = function (elem) { // eslint-disable-line import/no-mutable-exports if (elem.id && listMap_[elem.id]) { delete listMap_[elem.id]; } @@ -377,6 +377,7 @@ export const getTransformList = function (elem) { * @param {module:SVGTransformList.removeElementFromListMap} cb Passed a single argument `elem` * @returns {undefined} */ -export const changeRemoveElementFromListMap = function (cb) { + +export const changeRemoveElementFromListMap = function (cb) { // eslint-disable-line promise/prefer-await-to-callbacks removeElementFromListMap = cb; }; diff --git a/editor/touch.js b/editor/touch.js index f6996b67..256e665d 100644 --- a/editor/touch.js +++ b/editor/touch.js @@ -1,4 +1,9 @@ // http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/ +/** + * + * @param {Event} ev + * @returns {undefined} + */ function touchHandler (ev) { const {changedTouches} = ev, first = changedTouches[0]; @@ -11,7 +16,7 @@ function touchHandler (ev) { default: return; } - const {screenX, screenY, clientX, clientY} = first; + const {screenX, screenY, clientX, clientY} = first; // eslint-disable-line no-shadow const simulatedEvent = new MouseEvent(type, { // Event interface bubbles: true, diff --git a/editor/typedefs.js b/editor/typedefs.js index 09bdd4b3..7422b42e 100644 --- a/editor/typedefs.js +++ b/editor/typedefs.js @@ -50,3 +50,12 @@ /** * @external Window */ + +/** + * @external JamilihArray + * @type {GenericArray} + * @property {string} 0 Element name + * @property {PlainObject|JamilihArray} [1] Generally a map from an attribute name to attribute value, but also adds event handlers, etc. + * @property {JamilihArray} [2] Children + * @see {@link https://github.com/brettz9/jamilih/} + */ diff --git a/editor/units.js b/editor/units.js index 13a4e9e0..d5f83416 100644 --- a/editor/units.js +++ b/editor/units.js @@ -7,6 +7,7 @@ */ import {NS} from './namespaces.js'; +import {isNullish} from './utilities.js'; const wAttrs = ['x', 'x1', 'cx', 'rx', 'width']; const hAttrs = ['y', 'y1', 'cy', 'ry', 'height']; @@ -40,7 +41,7 @@ let typeMap_ = {}; */ /** * @function module:units.ElementContainer#getElement - * @returns {Element} An element in the container given an id + * @returns {?Element} An element in the container given an id */ /** * @function module:units.ElementContainer#getHeight @@ -134,8 +135,7 @@ export const getTypeMap = function () { export const shortFloat = function (val) { const digits = elementContainer_.getRoundDigits(); if (!isNaN(val)) { - // Note that + converts to Number - return +((+val).toFixed(digits)); + return Number(Number(val).toFixed(digits)); } if (Array.isArray(val)) { return shortFloat(val[0]) + ',' + shortFloat(val[1]); @@ -304,7 +304,7 @@ export const isValidUnit = function (attr, val, selectedElement) { // not already present try { const elem = elementContainer_.getElement(val); - result = (elem == null || elem === selectedElement); + result = (isNullish(elem) || elem === selectedElement); } catch (e) {} return result; } diff --git a/editor/utilities.js b/editor/utilities.js index 8a89a6df..4bfeaa58 100644 --- a/editor/utilities.js +++ b/editor/utilities.js @@ -8,7 +8,7 @@ */ import './svgpathseg.js'; -import jqPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr` +import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr` import {NS} from './namespaces.js'; import {getTransformList} from './svgtransformlist.js'; import {setUnitAttr, getTypeMap} from './units.js'; @@ -22,7 +22,7 @@ import { } from './browser.js'; // Constants -const $ = jqPluginSVG(jQuery); +const $ = jQueryPluginSVG(jQuery); // String used to encode base64. const KEYSTR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; @@ -123,7 +123,12 @@ export const dropXMLInteralSubset = (str) => { export const toXml = function (str) { // ' is ok in XML, but not HTML // > does not normally need escaping, though it can if within a CDATA expression (and preceded by "]]") - return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); // Note: `'` is XML only + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); // Note: `'` is XML only }; /** @@ -133,9 +138,9 @@ export const toXml = function (str) { * @param {string} str - The string to be converted * @returns {string} The converted string */ -export const fromXml = function (str) { +export function fromXml (str) { return $('

').html(str).text(); -}; +} // This code was written by Tyler Akins and has been placed in the // public domain. It would be nice if you left this header intact. @@ -150,30 +155,33 @@ export const fromXml = function (str) { * @param {string} input * @returns {string} Base64 output */ -export const encode64 = function (input) { +export function encode64 (input) { // base64 strings are 4/3 larger than the original string input = encodeUTF8(input); // convert non-ASCII characters // input = convertToXMLReferences(input); if (window.btoa) { return window.btoa(input); // Use native if available } - const output = []; - output.length = Math.floor((input.length + 2) / 3) * 4; + const output = new Array(Math.floor((input.length + 2) / 3) * 4); - let i = 0, p = 0; + let i = 0, + p = 0; do { const chr1 = input.charCodeAt(i++); const chr2 = input.charCodeAt(i++); const chr3 = input.charCodeAt(i++); + /* eslint-disable no-bitwise */ const enc1 = chr1 >> 2; const enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); let enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); let enc4 = chr3 & 63; + /* eslint-enable no-bitwise */ if (isNaN(chr2)) { - enc3 = enc4 = 64; + enc3 = 64; + enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } @@ -185,7 +193,7 @@ export const encode64 = function (input) { } while (i < input.length); return output.join(''); -}; +} /** * Converts a string from base64. @@ -193,7 +201,7 @@ export const encode64 = function (input) { * @param {string} input Base64-encoded input * @returns {string} Decoded output */ -export const decode64 = function (input) { +export function decode64 (input) { if (window.atob) { return decodeUTF8(window.atob(input)); } @@ -210,30 +218,32 @@ export const decode64 = function (input) { const enc3 = KEYSTR.indexOf(input.charAt(i++)); const enc4 = KEYSTR.indexOf(input.charAt(i++)); + /* eslint-disable no-bitwise */ const chr1 = (enc1 << 2) | (enc2 >> 4); const chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); const chr3 = ((enc3 & 3) << 6) | enc4; + /* eslint-enable no-bitwise */ output += String.fromCharCode(chr1); if (enc3 !== 64) { - output = output + String.fromCharCode(chr2); + output += String.fromCharCode(chr2); } if (enc4 !== 64) { - output = output + String.fromCharCode(chr3); + output += String.fromCharCode(chr3); } } while (i < input.length); return decodeUTF8(output); -}; +} /** * @function module:utilities.decodeUTF8 * @param {string} argString * @returns {string} */ -export const decodeUTF8 = function (argString) { +export function decodeUTF8 (argString) { return decodeURIComponent(escape(argString)); -}; +} // codedread:does not seem to work with webkit-based browsers on OSX // Brettz9: please test again as function upgraded /** @@ -255,7 +265,8 @@ export const dataURLToObjectURL = function (dataurl) { if (typeof Uint8Array === 'undefined' || typeof Blob === 'undefined' || typeof URL === 'undefined' || !URL.createObjectURL) { return ''; } - const arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], + const arr = dataurl.split(','), + mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]); let n = bstr.length; const u8arr = new Uint8Array(n); @@ -430,7 +441,7 @@ export const getUrlFromAttr = function (attrVal) { * @param {Element} elem * @returns {string} The given element's `xlink:href` value */ -export let getHref = function (elem) { +export let getHref = function (elem) { // eslint-disable-line import/no-mutable-exports return elem.getAttributeNS(NS.XLINK, 'href'); }; @@ -441,7 +452,7 @@ export let getHref = function (elem) { * @param {string} val * @returns {undefined} */ -export let setHref = function (elem, val) { +export let setHref = function (elem, val) { // eslint-disable-line import/no-mutable-exports elem.setAttributeNS(NS.XLINK, 'xlink:href', val); }; @@ -484,6 +495,15 @@ export const getPathBBox = function (path) { const start = seglist.getItem(0); let P0 = [start.x, start.y]; + const getCalc = function (j, P1, P2, P3) { + return function (t) { + return Math.pow(1 - t, 3) * P0[j] + + 3 * Math.pow(1 - t, 2) * t * P1[j] + + 3 * (1 - t) * Math.pow(t, 2) * P2[j] + + Math.pow(t, 3) * P3[j]; + }; + }; + for (let i = 0; i < tot; i++) { const seg = seglist.getItem(i); @@ -499,21 +519,14 @@ export const getPathBBox = function (path) { P3 = [seg.x, seg.y]; for (let j = 0; j < 2; j++) { - const calc = function (t) { - return Math.pow(1 - t, 3) * P0[j] + - 3 * Math.pow(1 - t, 2) * t * P1[j] + - 3 * (1 - t) * Math.pow(t, 2) * P2[j] + - Math.pow(t, 3) * P3[j]; - }; + const calc = getCalc(j, P1, P2, P3); const b = 6 * P0[j] - 12 * P1[j] + 6 * P2[j]; const a = -3 * P0[j] + 9 * P1[j] - 9 * P2[j] + 3 * P3[j]; const c = 3 * P1[j] - 3 * P0[j]; if (a === 0) { - if (b === 0) { - continue; - } + if (b === 0) { continue; } const t = -c / b; if (t > 0 && t < 1) { bounds[j].push(calc(t)); @@ -612,15 +625,15 @@ export const getBBox = function (elem) { selected.textContent = 'a'; // Some character needed for the selector to use. ret = selected.getBBox(); selected.textContent = ''; - } else { - if (selected.getBBox) { ret = selected.getBBox(); } + } else if (selected.getBBox) { + ret = selected.getBBox(); } break; case 'path': if (!supportsPathBBox()) { ret = getPathBBox(selected); - } else { - if (selected.getBBox) { ret = selected.getBBox(); } + } else if (selected.getBBox) { + ret = selected.getBBox(); } break; case 'g': @@ -719,12 +732,13 @@ export const getPathDFromElement = function (elem) { let d, a, rx, ry; switch (elem.tagName) { case 'ellipse': - case 'circle': + case 'circle': { a = $(elem).attr(['rx', 'ry', 'cx', 'cy']); const {cx, cy} = a; ({rx, ry} = a); if (elem.tagName === 'circle') { - rx = ry = $(elem).attr('r'); + ry = $(elem).attr('r'); + rx = ry; } d = getPathDFromSegments([ @@ -736,7 +750,7 @@ export const getPathDFromElement = function (elem) { ['Z', []] ]); break; - case 'path': + } case 'path': d = elem.getAttribute('d'); break; case 'line': @@ -749,11 +763,13 @@ export const getPathDFromElement = function (elem) { case 'polygon': d = 'M' + elem.getAttribute('points') + ' Z'; break; - case 'rect': + case 'rect': { const r = $(elem).attr(['rx', 'ry']); ({rx, ry} = r); const b = elem.getBBox(); - const {x, y} = b, w = b.width, h = b.height; + const {x, y} = b, + w = b.width, + h = b.height; num = 4 - num; // Why? Because! if (!rx && !ry) { @@ -781,7 +797,7 @@ export const getPathDFromElement = function (elem) { ]); } break; - default: + } default: break; } @@ -826,11 +842,11 @@ export const getBBoxOfElementAsPath = function (elem, addSVGElementFromJson, pat path.setAttribute('transform', eltrans); } - const parent = elem.parentNode; + const {parentNode} = elem; if (elem.nextSibling) { elem.before(path); } else { - parent.append(path); + parentNode.append(path); } const d = getPathDFromElement(elem); @@ -861,12 +877,12 @@ export const getBBoxOfElementAsPath = function (elem, addSVGElementFromJson, pat * @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions. * @param {module:draw.DrawCanvasInit#clearSelection|module:path.EditorContext#clearSelection} clearSelection - see [canvas.clearSelection]{@link module:svgcanvas.SvgCanvas#clearSelection} * @param {module:path.EditorContext#addToSelection} addToSelection - see [canvas.addToSelection]{@link module:svgcanvas.SvgCanvas#addToSelection} -* @param {module:history} history - see history module +* @param {module:history} hstry - see history module * @param {module:path.EditorContext#addCommandToHistory|module:draw.DrawCanvasInit#addCommandToHistory} addCommandToHistory - see [canvas.addCommandToHistory]{@link module:svgcanvas~addCommandToHistory} * @returns {SVGPathElement|null} The converted path element or null if the DOM element was not recognized. */ -export const convertToPath = function (elem, attrs, addSVGElementFromJson, pathActions, clearSelection, addToSelection, history, addCommandToHistory) { - const batchCmd = new history.BatchCommand('Convert element to Path'); +export const convertToPath = function (elem, attrs, addSVGElementFromJson, pathActions, clearSelection, addToSelection, hstry, addCommandToHistory) { + const batchCmd = new hstry.BatchCommand('Convert element to Path'); // Any attribute on the element not covered by the passed-in attributes attrs = $.extend({}, attrs, getExtraAttributesForConvertToPath(elem)); @@ -882,11 +898,11 @@ export const convertToPath = function (elem, attrs, addSVGElementFromJson, pathA } const {id} = elem; - const parent = elem.parentNode; + const {parentNode} = elem; if (elem.nextSibling) { elem.before(path); } else { - parent.append(path); + parentNode.append(path); } const d = getPathDFromElement(elem); @@ -939,14 +955,14 @@ export const convertToPath = function (elem, attrs, addSVGElementFromJson, pathA * getBBox then apply the angle and any transforms. * * @param {Float} angle - The rotation angle in degrees -* @param {boolean} hasMatrixTransform - True if there is a matrix transform +* @param {boolean} hasAMatrixTransform - True if there is a matrix transform * @returns {boolean} True if the bbox can be optimized. */ -function bBoxCanBeOptimizedOverNativeGetBBox (angle, hasMatrixTransform) { +function bBoxCanBeOptimizedOverNativeGetBBox (angle, hasAMatrixTransform) { const angleModulo90 = angle % 90; const closeTo90 = angleModulo90 < -89.99 || angleModulo90 > 89.99; const closeTo0 = angleModulo90 > -0.001 && angleModulo90 < 0.001; - return hasMatrixTransform || !(closeTo0 || closeTo90); + return hasAMatrixTransform || !(closeTo0 || closeTo90); } /** @@ -979,13 +995,15 @@ export const getBBoxWithTransform = function (elem, addSVGElementFromJson, pathA // TODO: why ellipse and not circle const elemNames = ['ellipse', 'path', 'line', 'polyline', 'polygon']; if (elemNames.includes(elem.tagName)) { - bb = goodBb = getBBoxOfElementAsPath(elem, addSVGElementFromJson, pathActions); + goodBb = getBBoxOfElementAsPath(elem, addSVGElementFromJson, pathActions); + bb = goodBb; } else if (elem.tagName === 'rect') { // Look for radius const rx = elem.getAttribute('rx'); const ry = elem.getAttribute('ry'); if (rx || ry) { - bb = goodBb = getBBoxOfElementAsPath(elem, addSVGElementFromJson, pathActions); + goodBb = getBBoxOfElementAsPath(elem, addSVGElementFromJson, pathActions); + bb = goodBb; } } } @@ -1012,7 +1030,12 @@ export const getBBoxWithTransform = function (elem, addSVGElementFromJson, pathA return bb; }; -// TODO: This is problematic with large stroke-width and, for example, a single horizontal line. The calculated BBox extends way beyond left and right sides. +/** + * @param {Element} elem + * @returns {Float} + * @todo This is problematic with large stroke-width and, for example, a single + * horizontal line. The calculated BBox extends way beyond left and right sides. + */ function getStrokeOffsetForBBox (elem) { const sw = elem.getAttribute('stroke-width'); return (!isNaN(sw) && elem.getAttribute('stroke') !== 'none') ? sw / 2 : 0; @@ -1090,16 +1113,16 @@ export const getStrokedBBox = function (elems, addSVGElementFromJson, pathAction * Note that 0-opacity, off-screen etc elements are still considered "visible" * for this function. * @function module:utilities.getVisibleElements -* @param {Element} parent - The parent DOM element to search within +* @param {Element} parentElement - The parent DOM element to search within * @returns {Element[]} All "visible" elements. */ -export const getVisibleElements = function (parent) { - if (!parent) { - parent = $(editorContext_.getSVGContent()).children(); // Prevent layers from being included +export const getVisibleElements = function (parentElement) { + if (!parentElement) { + parentElement = $(editorContext_.getSVGContent()).children(); // Prevent layers from being included } const contentElems = []; - $(parent).children().each(function (i, elem) { + $(parentElement).children().each(function (i, elem) { if (elem.getBBox) { contentElems.push(elem); } @@ -1148,7 +1171,7 @@ export const getRotationAngleFromTransformList = function (tlist, toRad) { * @param {boolean} [toRad=false] - When true returns the value in radians rather than degrees * @returns {Float} The angle in degrees or radians */ -export let getRotationAngle = function (elem, toRad) { +export let getRotationAngle = function (elem, toRad) { // eslint-disable-line import/no-mutable-exports const selected = elem || editorContext_.getSelectedElements()[0]; // find the rotation transform (if any) and set it const tlist = getTransformList(selected); @@ -1169,13 +1192,14 @@ export const getRefElem = function (attrVal) { * Get a DOM element by ID within the SVG root element. * @function module:utilities.getElem * @param {string} id - String with the element's new ID -* @returns {Element} +* @returns {?Element} */ export const getElem = (supportsSelectors()) ? function (id) { // querySelector lookup return svgroot_.querySelector('#' + id); - } : supportsXpath() + } + : supportsXpath() ? function (id) { // xpath lookup return domdoc_.evaluate( @@ -1183,7 +1207,8 @@ export const getElem = (supportsSelectors()) domcontainer_, function () { return NS.SVG; }, 9, - null).singleNodeValue; + null + ).singleNodeValue; } : function (id) { // jQuery lookup: twice as slow as xpath in FF @@ -1242,12 +1267,11 @@ export const cleanupElement = function (element) { delete defaults.ry; } - for (const attr in defaults) { - const val = defaults[attr]; + Object.entries(defaults).forEach(([attr, val]) => { if (element.getAttribute(attr) === String(val)) { element.removeAttribute(attr); } - } + }); }; /** @@ -1343,6 +1367,15 @@ export const copyElem = function (el, getNextId) { return newEl; }; +/** + * Whether a value is `null` or `undefined`. + * @param {Any} val + * @returns {boolean} + */ +export const isNullish = (val) => { + return val === null || val === undefined; +}; + /** * Overwrite methods for unit testing. * @function module:utilities.mock diff --git a/editor/xdomain-svgedit-config-es.js b/editor/xdomain-svgedit-config-es.js index daa62815..e8d13a58 100644 --- a/editor/xdomain-svgedit-config-es.js +++ b/editor/xdomain-svgedit-config-es.js @@ -1,4 +1,5 @@ import svgEditor from './svg-editor.js'; + svgEditor.setConfig({ canvasName: 'xdomain', // Namespace this allowedOrigins: ['*'] diff --git a/firefox-extension/content/svg-edit-overlay.js b/firefox-extension/content/svg-edit-overlay.js index 06be3886..ccc7523a 100644 --- a/firefox-extension/content/svg-edit-overlay.js +++ b/firefox-extension/content/svg-edit-overlay.js @@ -1,3 +1,7 @@ +/** + * Opens the dialog with the SVG Editor. + * @returns {undefined} + */ function startSvgEdit () { // eslint-disable-line no-unused-vars const url = 'chrome://svg-edit/content/editor/svg-editor.html'; window.openDialog(url, 'SVG Editor', 'width=1024,height=700,menubar=no,toolbar=no'); diff --git a/firefox-extension/handlers.js b/firefox-extension/handlers.js index 51d51c69..d33cb0ec 100644 --- a/firefox-extension/handlers.js +++ b/firefox-extension/handlers.js @@ -3,6 +3,11 @@ jQuery(function () { if (!window.Components) return; + /** + * Offer choice of file. + * @param {boolean} readflag + * @returns {nsILocalFile} + */ function mozFilePicker (readflag) { const fp = window.Components.classes['@mozilla.org/filepicker;1'] .createInstance(Components.interfaces.nsIFilePicker); @@ -19,16 +24,16 @@ jQuery(function () { netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); const file = mozFilePicker(true); if (!file) { - return null; + return; } const inputStream = Components.classes['@mozilla.org/network/file-input-stream;1'].createInstance(Components.interfaces.nsIFileInputStream); - inputStream.init(file, 0x01, parseInt('00004', 8), null); + inputStream.init(file, 0x01, 0o00004, null); const sInputStream = Components.classes['@mozilla.org/scriptableinputstream;1'].createInstance(Components.interfaces.nsIScriptableInputStream); sInputStream.init(inputStream); svgCanvas.setSvgString(sInputStream.read(sInputStream.available())); } catch (e) { - console.log('Exception while attempting to load' + e); + console.log('Exception while attempting to load' + e); // eslint-disable-line no-console } }, save (svg, str) { @@ -39,16 +44,16 @@ jQuery(function () { } if (!file.exists()) { - file.create(0, parseInt('0664', 8)); + file.create(0, 0o0664); } const out = Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream); - out.init(file, 0x20 | 0x02, parseInt('00004', 8), null); + out.init(file, 0x20 | 0x02, 0o00004, null); // eslint-disable-line no-bitwise out.write(str, str.length); out.flush(); out.close(); } catch (e) { - alert(e); + alert(e); // eslint-disable-line no-alert } } }); diff --git a/jsdoc-check-overly-generic-types.js b/jsdoc-check-overly-generic-types.js index 02429d1c..8664d894 100644 --- a/jsdoc-check-overly-generic-types.js +++ b/jsdoc-check-overly-generic-types.js @@ -3,6 +3,7 @@ * @todo Fork find-in-files to get ignore pattern support */ const fif = require('find-in-files'); + (async () => { /** * @typedef {PlainObject} FileResult @@ -15,7 +16,7 @@ const fileMatchPatterns = ['editor']; * Keys are file name strings * @type {Object.} */ -let results = await Promise.all(fileMatchPatterns.map(async (fileMatchPattern) => { +let results = await Promise.all(fileMatchPatterns.map((fileMatchPattern) => { return fif.find( { // We grab to the end of the line as the `line` result for `find-in-files` @@ -46,22 +47,28 @@ Object.entries(results).forEach(([file, res]) => { }); */ }); -console.log(`${output}\nTotal failures found: ${total}.\n`); +console.log(`${output}\nTotal failures found: ${total}.\n`); // eslint-disable-line no-console +/** + * @external FindInFilesResult + * @type {PlainObject} + * @property {string[]} matches The matched strings + * @property {Integer} count The number of matches + * @property {string[]} line The lines that were matched. The docs mistakenly indicate the property is named `lines`; see {@link https://github.com/kaesetoast/find-in-files/pull/19}. + */ + +/** + * Eliminates known false matches against overly generic types. + * @param {string} file + * @param {external:FindInFilesResult} res + * @returns {undefined} + */ function reduceFalseMatches (file, res) { switch (file) { case 'editor/external/jamilih/jml-es.js': case 'editor/xdomain-svgedit-config-iife.js': // Ignore res.line = []; break; - case 'editor/external/dynamic-import-polyfill/importModule.js': - res.line = res.line.filter((line) => { - return ![ - '* @returns {*} The return depends on the export of the targeted module.', - '* @returns {ArbitraryModule|*} The return depends on the export of the targeted module.' - ].includes(line); - }); - break; case 'editor/embedapi.js': res.line = res.line.filter((line) => { return ![ diff --git a/opera-widget/handlers.js b/opera-widget/handlers.js index 30e391ae..e5d2a549 100644 --- a/opera-widget/handlers.js +++ b/opera-widget/handlers.js @@ -1,5 +1,7 @@ /* globals jQuery, svgCanvas */ -// Note: This JavaScript file must be included as the last script on the main HTML editor page to override the open/save handlers +/* eslint-disable no-console */ +// Note: This JavaScript file must be included as the last script on the main +// HTML editor page to override the open/save handlers jQuery(function () { if (window.opera && window.opera.io && window.opera.io.filesystem) { svgCanvas.setCustomHandlers({ @@ -31,9 +33,9 @@ jQuery(function () { console.log('Open file failed.'); } }, - save (window, svg) { + save (win, svg) { try { - window.opera.io.filesystem.browseForSave( + win.opera.io.filesystem.browseForSave( new Date().getTime(), /* mountpoint name */ '', /* default location */ function (file) { diff --git a/package-lock.json b/package-lock.json index 4d880f3c..81f7afaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3696,9 +3696,8 @@ } }, "eslint-plugin-jsdoc": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-3.9.1.tgz", - "integrity": "sha512-kSQ62uraDa9QSv/5YncMZoKLScqrq7kt4lGGokH7Iyuqqzo2rZhERdrkPELLdnX4jWwkh+gYFZBt0PVIseKH1g==", + "version": "git+https://github.com/brettz9/eslint-plugin-jsdoc.git#11de4556f5c3c357b3a5521640136638a40f7636", + "from": "git+https://github.com/brettz9/eslint-plugin-jsdoc.git#ArrayPatternDeploy", "dev": true, "requires": { "comment-parser": "^0.5.0", diff --git a/package.json b/package.json index 2fe7522a..37b436b7 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "example": "examples", "test": "test" }, - "engines": {}, + "engines": { + "node": ">= 7.6.0" + }, "scripts": { "prepublishOnly": "npm run test-prep", "build-config": "rollup -c rollup-config.config.js", @@ -83,7 +85,7 @@ "eslint-config-standard": "12.0.0", "eslint-plugin-compat": "2.6.3", "eslint-plugin-import": "2.14.0", - "eslint-plugin-jsdoc": "https://github.com/brettz9/eslint-plugin-jsdoc#origin/ArrayPattern", + "eslint-plugin-jsdoc": "https://github.com/brettz9/eslint-plugin-jsdoc#ArrayPatternDeploy", "eslint-plugin-markdown": "^1.0.0-rc.0", "eslint-plugin-node": "8.0.0", "eslint-plugin-promise": "4.0.1", diff --git a/rollup-config.config.js b/rollup-config.config.js index 6f2122af..f55374c3 100644 --- a/rollup-config.config.js +++ b/rollup-config.config.js @@ -33,6 +33,7 @@ window.svgEditor.modules = false; }) ]; +/* eslint-disable import/no-anonymous-default-export */ export default [ { input: 'svgedit-config-es.js', diff --git a/rollup.config.js b/rollup.config.js index 14dc1e7d..83bd267f 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -8,16 +8,17 @@ import babel from 'rollup-plugin-babel'; import {terser} from 'rollup-plugin-terser'; import replace from 'rollup-plugin-re'; -const {lstatSync, readdirSync} = require('fs'); +const {lstatSync, readdirSync} = require('fs'); // eslint-disable-line import/no-commonjs +const {join, basename} = require('path'); // eslint-disable-line import/no-commonjs + const localeFiles = readdirSync('editor/locale'); const extensionFiles = readdirSync('editor/extensions'); -const {join, basename} = require('path'); const isDirectory = (source) => { return lstatSync(source).isDirectory(); }; const getDirectories = (source) => { - return readdirSync(source).map(name => join(source, name)).filter(isDirectory); + return readdirSync(source).map((nme) => join(source, nme)).filter(isDirectory); }; const extensionLocaleDirs = getDirectories('editor/extensions/ext-locale'); const extensionLocaleFiles = []; @@ -27,6 +28,18 @@ extensionLocaleDirs.forEach((dir) => { }); }); +/** + * @external RollupConfig + * @type {PlainObject} + * @see {@link https://rollupjs.org/guide/en#big-list-of-options} + */ + +/** + * @param {PlainObject} config + * @param {boolean} config.minifying + * @param {string} [config.format='umd'} = {}] + * @returns {external:RollupConfig} + */ function getRollupObject ({minifying, format = 'umd'} = {}) { const nonMinified = { input: 'editor/svg-editor.js', @@ -69,9 +82,7 @@ export default [ name: `svgEditorExtensionLocale_${basename(dir)}_${lang}`, file: `dist/extensions/ext-locale/${basename(dir)}/${file}` }, - plugins: [ - babel() - ] + plugins: [babel()] }; }), { @@ -80,9 +91,7 @@ export default [ format: 'iife', file: `dist/redirect-on-lacking-support.js` }, - plugins: [ - babel() - ] + plugins: [babel()] }, { input: 'editor/jspdf/jspdf.plugin.svgToPdf.js', @@ -90,9 +99,7 @@ export default [ format: 'iife', file: `dist/jspdf.plugin.svgToPdf.js` }, - plugins: [ - babel() - ] + plugins: [babel()] }, { input: 'editor/extensions/imagelib/index.js', @@ -124,9 +131,7 @@ export default [ format: 'iife', file: 'dist/dom-polyfill.js' }, - plugins: [ - babel() - ] + plugins: [babel()] }, { input: 'editor/canvg/canvg.js', @@ -135,16 +140,14 @@ export default [ name: 'canvg', file: 'dist/canvg.js' }, - plugins: [ - babel() - ] + plugins: [babel()] }, ...localeFiles.map((localeFile) => { // lang.*.js const localeRegex = /^lang\.([\w-]+?)\.js$/; const lang = localeFile.match(localeRegex); if (!lang) { - return; + return undefined; } return { input: 'editor/locale/' + localeFile, @@ -154,7 +157,8 @@ export default [ file: 'dist/locale/' + localeFile }, plugins: [ - babel() // Probably don't need here, but... + // Probably don't need here, but... + babel() ] }; }), @@ -162,7 +166,7 @@ export default [ // ext-*.js const extensionName = extensionFile.match(/^ext-(.+?)\.js$/); if (!extensionName) { - return; + return undefined; } return { input: 'editor/extensions/' + extensionFile, @@ -188,10 +192,10 @@ export default [ // For now, we'll replace with globals // We'll still make at least one import: editor/ext-locale/storage/ `import '../svgpathseg.js';` - ].map((test) => { + ].map((tst) => { return { match: /editor\/extensions/, - test, + test: tst, replace: '' }; }) diff --git a/screencasts/svgopen2010/script.js b/screencasts/svgopen2010/script.js index c1ded792..0f20e549 100644 --- a/screencasts/svgopen2010/script.js +++ b/screencasts/svgopen2010/script.js @@ -9,9 +9,9 @@ const byId = function (id) { return id; }; -const query = function (query, root) { - if (!query) { return []; } - if (typeof query !== 'string') { return [...query]; } +const query = function (qry, root) { + if (!qry) { return []; } + if (typeof qry !== 'string') { return [...qry]; } if (typeof root === 'string') { root = byId(root); if (!root) { return []; } @@ -19,17 +19,17 @@ const query = function (query, root) { root = root || document; const rootIsDoc = (root.nodeType === 9); - const doc = rootIsDoc ? root : (root.ownerDocument || document); + const dcmnt = rootIsDoc ? root : (root.ownerDocument || document); // rewrite the query to be ID rooted - if (!rootIsDoc || ('>~+'.includes(query[0]))) { + if (!rootIsDoc || ('>~+'.includes(qry[0]))) { root.id = root.id || ('qUnique' + (ctr++)); - query = '#' + root.id + ' ' + query; + qry = '#' + root.id + ' ' + qry; } // don't choke on something like ".yada.yada >" - if ('>~+'.includes(query.slice(-1))) { query += ' *'; } + if ('>~+'.includes(qry.slice(-1))) { qry += ' *'; } - return [...doc.querySelectorAll(query)]; + return [...dcmnt.querySelectorAll(qry)]; }; const ua = navigator.userAgent; @@ -66,9 +66,11 @@ Slide.prototype = { _buildList: [], _visited: false, _currentState: '', - _states: [ 'distant-slide', 'far-past', + _states: [ + 'distant-slide', 'far-past', 'past', 'current', 'future', - 'far-future', 'distant-slide' ], + 'far-future', 'distant-slide' + ], setState (state) { if (typeof state !== 'string') { state = this._states[state]; @@ -85,8 +87,8 @@ Slide.prototype = { /* this._runAutos(); */ - const _t = this; - setTimeout(function () { _t._runAutos(); }, 400); + const that = this; + setTimeout(function () { that._runAutos(); }, 400); }, _makeCounter () { if (!this._count || !this._node) { return; } @@ -121,18 +123,18 @@ Slide.prototype = { if (idx >= 0) { const elem = this._buildList.splice(idx, 1)[0]; const transitionEnd = isWK ? 'webkitTransitionEnd' : (isFF ? 'mozTransitionEnd' : 'oTransitionEnd'); - const _t = this; + const that = this; if (canTransition()) { const l = function (evt) { elem.parentNode.removeEventListener(transitionEnd, l); - _t._runAutos(); + that._runAutos(); }; elem.parentNode.addEventListener(transitionEnd, l); elem.classList.remove('to-build'); } else { setTimeout(function () { elem.classList.remove('to-build'); - _t._runAutos(); + that._runAutos(); }, 400); } } @@ -156,22 +158,22 @@ const SlideShow = function (slides) { const h = window.location.hash; try { - this.current = parseInt(h.split('#slide')[1], 10); + this.current = parseInt(h.split('#slide')[1]); } catch (e) { /* squeltch */ } this.current = isNaN(this.current) ? 1 : this.current; - const _t = this; + const that = this; doc.addEventListener('keydown', - function (e) { _t.handleKeys(e); }); + function (e) { that.handleKeys(e); }); doc.addEventListener('mousewheel', - function (e) { _t.handleWheel(e); }); + function (e) { that.handleWheel(e); }); doc.addEventListener('DOMMouseScroll', - function (e) { _t.handleWheel(e); }); + function (e) { that.handleWheel(e); }); doc.addEventListener('touchstart', - function (e) { _t.handleTouchStart(e); }); + function (e) { that.handleTouchStart(e); }); doc.addEventListener('touchend', - function (e) { _t.handleTouchEnd(e); }); + function (e) { that.handleTouchEnd(e); }); window.addEventListener('popstate', - function (e) { _t.go(e.state); }); + function (e) { that.go(e.state); }); this._update(); }; @@ -242,7 +244,7 @@ SlideShow.prototype = { } }, handleKeys (e) { - if (/^(input|textarea)$/i.test(e.target.nodeName)) return; + if ((/^(input|textarea)$/i).test(e.target.nodeName)) return; switch (e.keyCode) { case 37: // left arrow @@ -282,15 +284,23 @@ document.querySelector('#toggle-gradients').addEventListener('click', toggleGrad const counters = document.querySelectorAll('.counter'); const slides = document.querySelectorAll('.slide'); +/** + * Show or hide the counters. + * @returns {undefined} + */ function toggleCounter () { [...counters].forEach(function (el) { el.style.display = (el.offsetHeight) ? 'none' : 'block'; }); } +/** + * Add or remove `reduced` (size) class. + * @returns {undefined} + */ function toggleSize () { [...slides].forEach(function (el) { - if (!/reduced/.test(el.className)) { + if (!(/reduced/).test(el.className)) { el.classList.add('reduced'); } else { el.classList.remove('reduced'); @@ -298,9 +308,13 @@ function toggleSize () { }); } +/** + * Add or remove `no-transitions` class. + * @returns {undefined} + */ function toggleTransitions () { [...slides].forEach(function (el) { - if (!/no-transitions/.test(el.className)) { + if (!(/no-transitions/).test(el.className)) { el.classList.add('no-transitions'); } else { el.classList.remove('no-transitions'); @@ -308,9 +322,13 @@ function toggleTransitions () { }); } +/** + * Add or remove `no-gradients` class. + * @returns {undefined} + */ function toggleGradients () { [...slides].forEach(function (el) { - if (!/no-gradients/.test(el.className)) { + if (!(/no-gradients/).test(el.className)) { el.classList.add('no-gradients'); } else { el.classList.remove('no-gradients'); diff --git a/test/contextmenu_test.js b/test/contextmenu_test.js index c5391536..f2dc36d8 100644 --- a/test/contextmenu_test.js +++ b/test/contextmenu_test.js @@ -8,6 +8,10 @@ QUnit.log((details) => { } }); +/** + * Tear down tests, resetting custom menus. + * @returns {undefined} + */ function tearDown () { contextmenu.resetCustomMenus(); } @@ -39,7 +43,7 @@ QUnit.test('Test svgedit.contextmenu does not add invalid menu item', function ( QUnit.test('Test svgedit.contextmenu adds valid menu item', function (assert) { assert.expect(2); - const validItem = {id: 'valid', label: 'anicelabel', action () { alert('testing'); }}; + const validItem = {id: 'valid', label: 'anicelabel', action () { console.log('testing'); }}; contextmenu.add(validItem); assert.ok(contextmenu.hasCustomHandler('valid'), 'Valid menu item is added.'); @@ -50,8 +54,8 @@ QUnit.test('Test svgedit.contextmenu adds valid menu item', function (assert) { QUnit.test('Test svgedit.contextmenu rejects valid duplicate menu item id', function (assert) { assert.expect(1); - const validItem1 = {id: 'valid', label: 'anicelabel', action () { alert('testing'); }}; - const validItem2 = {id: 'valid', label: 'anicelabel', action () { alert('testingtwice'); }}; + const validItem1 = {id: 'valid', label: 'anicelabel', action () { console.log('testing'); }}; + const validItem2 = {id: 'valid', label: 'anicelabel', action () { console.log('testingtwice'); }}; contextmenu.add(validItem1); contextmenu.add(validItem2); diff --git a/test/coords_test.js b/test/coords_test.js index 48837d3d..594a2dd3 100644 --- a/test/coords_test.js +++ b/test/coords_test.js @@ -18,6 +18,11 @@ const svg = document.createElementNS(NS.SVG, 'svg'); svgroot.append(svg); let elemId = 1; + +/** + * Set up tests with mock data. + * @returns {undefined} + */ function setUp () { // Mock out editor context. utilities.init( @@ -38,13 +43,17 @@ function setUp () { getGridSnapping () { return false; }, getDrawing () { return { - getNextId () { return '' + elemId++; } + getNextId () { return String(elemId++); } }; } } ); } +/** + * Tear down tests, removing elements. + * @returns {undefined} + */ function tearDown () { while (svg.hasChildNodes()) { svg.firstChild.remove(); diff --git a/test/draw_test.js b/test/draw_test.js index 479041df..6393d198 100644 --- a/test/draw_test.js +++ b/test/draw_test.js @@ -65,8 +65,7 @@ const currentDrawing_ = new draw.Drawing(svgcontent, idprefix); const getCurrentDrawing = function () { return currentDrawing_; }; -const setCurrentGroup = (cg) => { -}; +const setCurrentGroup = (cg) => { /* */ }; draw.init( /** * @implements {module:draw.DrawCanvasInit} @@ -77,11 +76,15 @@ draw.init( } ); +/** + * @param {module:utilities.SVGElementJSON} jsonMap + * @returns {SVGElement} + */ function createSVGElement (jsonMap) { - const elem = document.createElementNS(NS.SVG, jsonMap['element']); - for (const attr in jsonMap['attr']) { - elem.setAttribute(attr, jsonMap['attr'][attr]); - } + const elem = document.createElementNS(NS.SVG, jsonMap.element); + Object.entries(jsonMap.attr).forEach(([attr, value]) => { + elem.setAttribute(attr, value); + }); return elem; } @@ -143,12 +146,7 @@ const cleanupSVG = function (svgElem) { while (svgElem.firstChild) { svgElem.firstChild.remove(); } }; -QUnit.module('svgedit.draw.Drawing', { - beforeEach () { - }, - afterEach () { - } -}); +QUnit.module('svgedit.draw.Drawing'); QUnit.test('Test draw module', function (assert) { assert.expect(4); @@ -157,7 +155,7 @@ QUnit.test('Test draw module', function (assert) { assert.equal(typeof draw, typeof {}); assert.ok(draw.Drawing); - assert.equal(typeof draw.Drawing, typeof function () {}); + assert.equal(typeof draw.Drawing, typeof function () { /* */ }); }); QUnit.test('Test document creation', function (assert) { @@ -311,7 +309,7 @@ QUnit.test('Test releaseId()', function (assert) { QUnit.test('Test getNumLayers', function (assert) { assert.expect(3); const drawing = new draw.Drawing(svg); - assert.equal(typeof drawing.getNumLayers, typeof function () {}); + assert.equal(typeof drawing.getNumLayers, typeof function () { /* */ }); assert.equal(drawing.getNumLayers(), 0); setupSVGWith3Layers(svg); @@ -329,7 +327,7 @@ QUnit.test('Test hasLayer', function (assert) { const drawing = new draw.Drawing(svg); drawing.identifyLayers(); - assert.equal(typeof drawing.hasLayer, typeof function () {}); + assert.equal(typeof drawing.hasLayer, typeof function () { /* */ }); assert.ok(!drawing.hasLayer('invalid-layer')); assert.ok(drawing.hasLayer(LAYER3)); @@ -447,7 +445,7 @@ QUnit.test('Test getCurrentLayer()', function (assert) { drawing.identifyLayers(); assert.ok(drawing.getCurrentLayer); - assert.equal(typeof drawing.getCurrentLayer, typeof function () {}); + assert.equal(typeof drawing.getCurrentLayer, typeof function () { /* */ }); assert.ok(drawing.getCurrentLayer()); assert.equal(drawing.getCurrentLayer(), drawing.all_layers[2].getGroup()); @@ -462,7 +460,7 @@ QUnit.test('Test setCurrentLayer() and getCurrentLayerName()', function (assert) drawing.identifyLayers(); assert.ok(drawing.setCurrentLayer); - assert.equal(typeof drawing.setCurrentLayer, typeof function () {}); + assert.equal(typeof drawing.setCurrentLayer, typeof function () { /* */ }); drawing.setCurrentLayer(LAYER2); assert.equal(drawing.getCurrentLayerName(), LAYER2); @@ -485,7 +483,7 @@ QUnit.test('Test setCurrentLayerName()', function (assert) { drawing.identifyLayers(); assert.ok(drawing.setCurrentLayerName); - assert.equal(typeof drawing.setCurrentLayerName, typeof function () {}); + assert.equal(typeof drawing.setCurrentLayerName, typeof function () { /* */ }); const oldName = drawing.getCurrentLayerName(); const newName = 'New Name'; @@ -519,7 +517,7 @@ QUnit.test('Test createLayer()', function (assert) { drawing.identifyLayers(); assert.ok(drawing.createLayer); - assert.equal(typeof drawing.createLayer, typeof function () {}); + assert.equal(typeof drawing.createLayer, typeof function () { /* */ }); const NEW_LAYER_NAME = 'Layer A'; const layerG = drawing.createLayer(NEW_LAYER_NAME, mockHrService); @@ -553,7 +551,7 @@ QUnit.test('Test mergeLayer()', function (assert) { assert.equal(drawing.getCurrentLayer(), layers[2]); assert.ok(drawing.mergeLayer); - assert.equal(typeof drawing.mergeLayer, typeof function () {}); + assert.equal(typeof drawing.mergeLayer, typeof function () { /* */ }); drawing.mergeLayer(mockHrService); @@ -623,7 +621,7 @@ QUnit.test('Test mergeAllLayers()', function (assert) { drawing.identifyLayers(); assert.ok(drawing.mergeAllLayers); - assert.equal(typeof drawing.mergeAllLayers, typeof function () {}); + assert.equal(typeof drawing.mergeAllLayers, typeof function () { /* */ }); drawing.mergeAllLayers(mockHrService); @@ -661,7 +659,7 @@ QUnit.test('Test cloneLayer()', function (assert) { drawing.identifyLayers(); assert.ok(drawing.cloneLayer); - assert.equal(typeof drawing.cloneLayer, typeof function () {}); + assert.equal(typeof drawing.cloneLayer, typeof function () { /* */ }); const clone = drawing.cloneLayer('clone', mockHrService); @@ -703,7 +701,7 @@ QUnit.test('Test getLayerVisibility()', function (assert) { drawing.identifyLayers(); assert.ok(drawing.getLayerVisibility); - assert.equal(typeof drawing.getLayerVisibility, typeof function () {}); + assert.equal(typeof drawing.getLayerVisibility, typeof function () { /* */ }); assert.ok(drawing.getLayerVisibility(LAYER1)); assert.ok(drawing.getLayerVisibility(LAYER2)); assert.ok(drawing.getLayerVisibility(LAYER3)); @@ -719,7 +717,7 @@ QUnit.test('Test setLayerVisibility()', function (assert) { drawing.identifyLayers(); assert.ok(drawing.setLayerVisibility); - assert.equal(typeof drawing.setLayerVisibility, typeof function () {}); + assert.equal(typeof drawing.setLayerVisibility, typeof function () { /* */ }); drawing.setLayerVisibility(LAYER3, false); drawing.setLayerVisibility(LAYER2, true); @@ -743,7 +741,7 @@ QUnit.test('Test getLayerOpacity()', function (assert) { drawing.identifyLayers(); assert.ok(drawing.getLayerOpacity); - assert.equal(typeof drawing.getLayerOpacity, typeof function () {}); + assert.equal(typeof drawing.getLayerOpacity, typeof function () { /* */ }); assert.strictEqual(drawing.getLayerOpacity(LAYER1), 1.0); assert.strictEqual(drawing.getLayerOpacity(LAYER2), 1.0); assert.strictEqual(drawing.getLayerOpacity(LAYER3), 1.0); @@ -759,7 +757,7 @@ QUnit.test('Test setLayerOpacity()', function (assert) { drawing.identifyLayers(); assert.ok(drawing.setLayerOpacity); - assert.equal(typeof drawing.setLayerOpacity, typeof function () {}); + assert.equal(typeof drawing.setLayerOpacity, typeof function () { /* */ }); drawing.setLayerOpacity(LAYER1, 0.4); drawing.setLayerOpacity(LAYER2, 'invalid-string'); @@ -802,18 +800,18 @@ QUnit.test('Test svgedit.draw.randomizeIds()', function (assert) { // Confirm in LET_DOCUMENT_DECIDE mode that the document decides // if there is a nonce. let drawing = new draw.Drawing(svgN.cloneNode(true)); - assert.ok(!!drawing.getNonce()); + assert.ok(drawing.getNonce()); drawing = new draw.Drawing(svg.cloneNode(true)); assert.ok(!drawing.getNonce()); // Confirm that a nonce is set once we're in ALWAYS_RANDOMIZE mode. draw.randomizeIds(true, drawing); - assert.ok(!!drawing.getNonce()); + assert.ok(drawing.getNonce()); // Confirm new drawings in ALWAYS_RANDOMIZE mode have a nonce. drawing = new draw.Drawing(svg.cloneNode(true)); - assert.ok(!!drawing.getNonce()); + assert.ok(drawing.getNonce()); drawing.clearNonce(); assert.ok(!drawing.getNonce()); @@ -822,7 +820,7 @@ QUnit.test('Test svgedit.draw.randomizeIds()', function (assert) { // but that their se:nonce attribute is left alone. draw.randomizeIds(false, drawing); assert.ok(!drawing.getNonce()); - assert.ok(!!drawing.getSvgElem().getAttributeNS(NS.SE, 'nonce')); + assert.ok(drawing.getSvgElem().getAttributeNS(NS.SE, 'nonce')); drawing = new draw.Drawing(svg.cloneNode(true)); assert.ok(!drawing.getNonce()); diff --git a/test/history_test.js b/test/history_test.js index 6b1b7a62..5f9b1c6c 100644 --- a/test/history_test.js +++ b/test/history_test.js @@ -3,16 +3,16 @@ import {NS} from '../editor/namespaces.js'; import * as transformlist from '../editor/svgtransformlist.js'; import * as utilities from '../editor/utilities.js'; -import * as history from '../editor/history.js'; +import * as hstory from '../editor/history.js'; // TODO(codedread): Write tests for handling history events. // Mocked out methods. -transformlist.changeRemoveElementFromListMap((elem) => {}); +transformlist.changeRemoveElementFromListMap((elem) => { /* */ }); utilities.mock({ getHref (elem) { return '#foo'; }, - setHref (elem, val) {}, + setHref (elem, val) { /* */ }, getRotationAngle (elem) { return 0; } }); @@ -36,10 +36,10 @@ QUnit.module('svgedit.history'); class MockCommand { constructor (optText) { this.text_ = optText; } - apply () {} - unapply () {} + apply () { /* */ } // eslint-disable-line class-methods-use-this + unapply () { /* */ } // eslint-disable-line class-methods-use-this getText () { return this.text_; } - elements () { return []; } + elements () { return []; } // eslint-disable-line class-methods-use-this } /* @@ -48,9 +48,17 @@ class MockHistoryEventHandler { } */ +/** + * Set up tests (with undo manager). + * @returns {undefined} + */ function setUp () { - undoMgr = new history.UndoManager(); + undoMgr = new hstory.UndoManager(); } +/** + * Tear down tests, destroying undo manager. + * @returns {undefined} + */ function tearDown () { undoMgr = null; } @@ -58,19 +66,19 @@ function tearDown () { QUnit.test('Test svgedit.history package', function (assert) { assert.expect(13); - 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 () {}); - assert.equal(typeof history.InsertElementCommand, typeof function () {}); - assert.equal(typeof history.ChangeElementCommand, typeof function () {}); - assert.equal(typeof history.RemoveElementCommand, typeof function () {}); - assert.equal(typeof history.BatchCommand, typeof function () {}); - assert.equal(typeof history.UndoManager, typeof 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 () { /* */ }); + assert.equal(typeof hstory.InsertElementCommand, typeof function () { /* */ }); + assert.equal(typeof hstory.ChangeElementCommand, typeof function () { /* */ }); + assert.equal(typeof hstory.RemoveElementCommand, typeof function () { /* */ }); + assert.equal(typeof hstory.BatchCommand, typeof function () { /* */ }); + assert.equal(typeof hstory.UndoManager, typeof function () { /* */ }); }); QUnit.test('Test UndoManager methods', function (assert) { @@ -86,12 +94,12 @@ QUnit.test('Test UndoManager methods', function (assert) { assert.ok(undoMgr.getNextRedoCommandText); assert.equal(typeof undoMgr, typeof {}); - assert.equal(typeof undoMgr.addCommandToHistory, typeof function () {}); - assert.equal(typeof undoMgr.getUndoStackSize, typeof function () {}); - assert.equal(typeof undoMgr.getRedoStackSize, typeof function () {}); - assert.equal(typeof undoMgr.resetUndoStack, typeof function () {}); - assert.equal(typeof undoMgr.getNextUndoCommandText, typeof function () {}); - assert.equal(typeof undoMgr.getNextRedoCommandText, typeof function () {}); + assert.equal(typeof undoMgr.addCommandToHistory, typeof function () { /* */ }); + assert.equal(typeof undoMgr.getUndoStackSize, typeof function () { /* */ }); + assert.equal(typeof undoMgr.getRedoStackSize, typeof function () { /* */ }); + assert.equal(typeof undoMgr.resetUndoStack, typeof function () { /* */ }); + assert.equal(typeof undoMgr.getNextUndoCommandText, typeof function () { /* */ }); + assert.equal(typeof undoMgr.getNextRedoCommandText, typeof function () { /* */ }); tearDown(); }); @@ -312,8 +320,8 @@ QUnit.test('Test MoveElementCommand', function (assert) { let move = new history.MoveElementCommand(div3, div1, divparent); assert.ok(move.unapply); assert.ok(move.apply); - assert.equal(typeof move.unapply, typeof function () {}); - assert.equal(typeof move.apply, typeof function () {}); + assert.equal(typeof move.unapply, typeof function () { /* */ }); + assert.equal(typeof move.apply, typeof function () { /* */ }); move.unapply(); assert.equal(divparent.firstElementChild, div3); @@ -325,7 +333,7 @@ QUnit.test('Test MoveElementCommand', function (assert) { assert.equal(divparent.firstElementChild.nextElementSibling, div2); assert.equal(divparent.lastElementChild, div3); - move = new history.MoveElementCommand(div1, null, divparent); + move = new hstory.MoveElementCommand(div1, null, divparent); move.unapply(); assert.equal(divparent.firstElementChild, div2); @@ -337,7 +345,7 @@ QUnit.test('Test MoveElementCommand', function (assert) { assert.equal(divparent.firstElementChild.nextElementSibling, div2); assert.equal(divparent.lastElementChild, div3); - move = new history.MoveElementCommand(div2, div5, div4); + move = new hstory.MoveElementCommand(div2, div5, div4); move.unapply(); assert.equal(divparent.firstElementChild, div1); @@ -361,11 +369,11 @@ QUnit.test('Test InsertElementCommand', function (assert) { setUp(); - let insert = new history.InsertElementCommand(div3); + let insert = new hstory.InsertElementCommand(div3); assert.ok(insert.unapply); assert.ok(insert.apply); - assert.equal(typeof insert.unapply, typeof function () {}); - assert.equal(typeof insert.apply, typeof function () {}); + assert.equal(typeof insert.unapply, typeof function () { /* */ }); + assert.equal(typeof insert.apply, typeof function () { /* */ }); insert.unapply(); assert.equal(divparent.childElementCount, 2); @@ -379,7 +387,7 @@ QUnit.test('Test InsertElementCommand', function (assert) { assert.equal(div1.nextElementSibling, div2); assert.equal(div2.nextElementSibling, div3); - insert = new history.InsertElementCommand(div2); + insert = new hstory.InsertElementCommand(div2); insert.unapply(); assert.equal(divparent.childElementCount, 2); @@ -404,11 +412,11 @@ QUnit.test('Test RemoveElementCommand', function (assert) { const div6 = document.createElement('div'); div6.id = 'div6'; - let remove = new history.RemoveElementCommand(div6, null, divparent); + let remove = new hstory.RemoveElementCommand(div6, null, divparent); assert.ok(remove.unapply); assert.ok(remove.apply); - assert.equal(typeof remove.unapply, typeof function () {}); - assert.equal(typeof remove.apply, typeof function () {}); + assert.equal(typeof remove.unapply, typeof function () { /* */ }); + assert.equal(typeof remove.apply, typeof function () { /* */ }); remove.unapply(); assert.equal(divparent.childElementCount, 4); @@ -423,7 +431,7 @@ QUnit.test('Test RemoveElementCommand', function (assert) { assert.equal(div1.nextElementSibling, div2); assert.equal(div2.nextElementSibling, div3); - remove = new history.RemoveElementCommand(div6, div2, divparent); + remove = new hstory.RemoveElementCommand(div6, div2, divparent); remove.unapply(); assert.equal(divparent.childElementCount, 4); @@ -447,12 +455,12 @@ QUnit.test('Test ChangeElementCommand', function (assert) { setUp(); div1.setAttribute('title', 'new title'); - let change = new history.ChangeElementCommand(div1, + let change = new hstory.ChangeElementCommand(div1, {title: 'old title', class: 'foo'}); assert.ok(change.unapply); assert.ok(change.apply); - assert.equal(typeof change.unapply, typeof function () {}); - assert.equal(typeof change.apply, typeof function () {}); + assert.equal(typeof change.unapply, typeof function () { /* */ }); + assert.equal(typeof change.apply, typeof function () { /* */ }); change.unapply(); assert.equal(div1.getAttribute('title'), 'old title'); @@ -463,7 +471,7 @@ QUnit.test('Test ChangeElementCommand', function (assert) { assert.ok(!div1.getAttribute('class')); div1.textContent = 'inner text'; - change = new history.ChangeElementCommand(div1, + change = new hstory.ChangeElementCommand(div1, {'#text': null}); change.unapply(); @@ -473,7 +481,7 @@ QUnit.test('Test ChangeElementCommand', function (assert) { assert.equal(div1.textContent, 'inner text'); div1.textContent = ''; - change = new history.ChangeElementCommand(div1, + change = new hstory.ChangeElementCommand(div1, {'#text': 'old text'}); change.unapply(); @@ -502,7 +510,7 @@ QUnit.test('Test ChangeElementCommand', function (assert) { }); gethrefvalue = '#newhref'; - change = new history.ChangeElementCommand(rect, + change = new hstory.ChangeElementCommand(rect, {'#href': '#oldhref'}); assert.equal(justCalled, 'getHref'); @@ -518,12 +526,12 @@ QUnit.test('Test ChangeElementCommand', function (assert) { const line = document.createElementNS(NS.SVG, 'line'); line.setAttribute('class', 'newClass'); - change = new history.ChangeElementCommand(line, {class: 'oldClass'}); + change = new hstory.ChangeElementCommand(line, {class: 'oldClass'}); assert.ok(change.unapply); assert.ok(change.apply); - assert.equal(typeof change.unapply, typeof function () {}); - assert.equal(typeof change.apply, typeof function () {}); + assert.equal(typeof change.unapply, typeof function () { /* */ }); + assert.equal(typeof change.apply, typeof function () { /* */ }); change.unapply(); assert.equal(line.getAttribute('class'), 'oldClass'); @@ -542,15 +550,15 @@ QUnit.test('Test BatchCommand', function (assert) { let concatResult = ''; MockCommand.prototype.apply = function () { concatResult += this.text_; }; - const batch = new history.BatchCommand(); + 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, typeof function () {}); - assert.equal(typeof batch.apply, typeof function () {}); - assert.equal(typeof batch.addSubCommand, typeof function () {}); - assert.equal(typeof batch.isEmpty, typeof function () {}); + assert.equal(typeof batch.unapply, typeof function () { /* */ }); + assert.equal(typeof batch.apply, typeof function () { /* */ }); + assert.equal(typeof batch.addSubCommand, typeof function () { /* */ }); + assert.equal(typeof batch.isEmpty, typeof function () { /* */ }); assert.ok(batch.isEmpty()); @@ -563,13 +571,13 @@ QUnit.test('Test BatchCommand', function (assert) { batch.apply(); assert.equal(concatResult, 'abc'); - MockCommand.prototype.apply = function () {}; + MockCommand.prototype.apply = function () { /* */ }; MockCommand.prototype.unapply = function () { concatResult += this.text_; }; concatResult = ''; batch.unapply(); assert.equal(concatResult, 'cba'); - MockCommand.prototype.unapply = function () {}; + MockCommand.prototype.unapply = function () { /* */ }; tearDown(); }); diff --git a/test/jQuery.attr_test.js b/test/jQuery.attr_test.js index 69ddc8f9..351fdaa1 100644 --- a/test/jQuery.attr_test.js +++ b/test/jQuery.attr_test.js @@ -1,5 +1,9 @@ /* eslint-env qunit */ +/* eslint-disable import/unambiguous */ + +// Todo: Incomplete! + // log function QUnit.log((details) => { if (window.console && window.console.log) { diff --git a/test/math_test.js b/test/math_test.js index b849b672..5b4ec399 100644 --- a/test/math_test.js +++ b/test/math_test.js @@ -20,9 +20,9 @@ QUnit.test('Test svgedit.math package', function (assert) { assert.ok(math.transformPoint); assert.ok(math.isIdentity); assert.ok(math.matrixMultiply); - assert.equal(typeof math.transformPoint, typeof function () {}); - assert.equal(typeof math.isIdentity, typeof function () {}); - assert.equal(typeof math.matrixMultiply, typeof function () {}); + assert.equal(typeof math.transformPoint, typeof function () { /* */ }); + assert.equal(typeof math.isIdentity, typeof function () { /* */ }); + assert.equal(typeof math.matrixMultiply, typeof function () { /* */ }); }); QUnit.test('Test svgedit.math.transformPoint() function', function (assert) { diff --git a/test/qunit/qunit-assert-almostEquals.js b/test/qunit/qunit-assert-almostEquals.js index d7f679a8..e33265ce 100644 --- a/test/qunit/qunit-assert-almostEquals.js +++ b/test/qunit/qunit-assert-almostEquals.js @@ -1,5 +1,12 @@ 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. + * @param {Float} actual + * @param {Float} expected + * @param {string} message + * @returns {undefined} + */ function almostEquals (actual, expected, message) { message = message || (actual + ' did not equal ' + expected); this.pushResult({ @@ -10,6 +17,10 @@ function almostEquals (actual, expected, message) { }); } +/** + * @param {external:qunit} QUnit + * @returns {external:qunit} The same instance passed in after extending + */ export default function extend (QUnit) { QUnit.extend(QUnit.assert, { almostEquals diff --git a/test/qunit/qunit-assert-close.js b/test/qunit/qunit-assert-close.js index 5f8646ad..c0dfe3b2 100644 --- a/test/qunit/qunit-assert-close.js +++ b/test/qunit/qunit-assert-close.js @@ -10,7 +10,7 @@ * @param {string} [message] Defaults to structured message * @returns {undefined} */ -function close (actual, expected, maxDifference, message) { +function close (actual, expected, maxDifference, message) { // eslint-disable-line no-shadow const actualDiff = (actual === expected) ? 0 : Math.abs(actual - expected), result = actualDiff <= maxDifference; message = message || (actual + ' should be within ' + maxDifference + ' (inclusive) of ' + expected + (result ? '' : '. Actual: ' + actualDiff)); @@ -96,6 +96,10 @@ function notClosePercent (actual, expected, minPercentDifference, message) { this.pushResult({result, actual, expected, message}); } +/** + * @param {external:qunit} QUnit + * @returns {external:qunit} The same instance passed in after extending + */ export default function extend (QUnit) { QUnit.extend(QUnit.assert, { close, diff --git a/test/qunit/qunit-assert-expectOutOfBoundsException.js b/test/qunit/qunit-assert-expectOutOfBoundsException.js index 20d911c3..895eec2a 100644 --- a/test/qunit/qunit-assert-expectOutOfBoundsException.js +++ b/test/qunit/qunit-assert-expectOutOfBoundsException.js @@ -1,3 +1,10 @@ +/** + * Expects an out of bounds `INDEX_SIZE_ERR` exception. + * @param {GenericObject} obj + * @param {GenericCallback} fn + * @param {Any} arg1 + * @returns {undefined} + */ function expectOutOfBoundsException (obj, fn, arg1) { const expected = true; const message = 'Caught an INDEX_SIZE_ERR exception'; @@ -10,9 +17,13 @@ function expectOutOfBoundsException (obj, fn, arg1) { } } const actual = result; - console.log('aaa', result, actual, expected); this.pushResult({result, actual, expected, message}); } + +/** + * @param {external:qunit} QUnit + * @returns {external:qunit} The same instance passed in after extending + */ export default function extend (QUnit) { QUnit.extend(QUnit.assert, { expectOutOfBoundsException diff --git a/test/recalculate_test.js b/test/recalculate_test.js index e7b86d5e..8b069d9d 100644 --- a/test/recalculate_test.js +++ b/test/recalculate_test.js @@ -20,6 +20,11 @@ const svg = document.createElementNS(NS.SVG, 'svg'); svgroot.append(svg); let elemId = 1; + +/** + * Initilize modules to set up the tests. + * @returns {undefined} + */ function setUp () { utilities.init( /** @@ -39,7 +44,7 @@ function setUp () { getGridSnapping () { return false; }, getDrawing () { return { - getNextId () { return '' + elemId++; } + getNextId () { return String(elemId++); } }; } } @@ -51,13 +56,17 @@ function setUp () { { getSVGRoot () { return svg; }, getStartTransform () { return ''; }, - setStartTransform () {} + setStartTransform () { /* */ } } ); } let elem; +/** + * Initialize for tests and set up `rect` element. + * @returns {undefined} + */ function setUpRect () { setUp(); elem = document.createElementNS(NS.SVG, 'rect'); @@ -68,6 +77,10 @@ function setUpRect () { svg.append(elem); } +/** + * Initialize for tests and set up `text` element with `tspan` child. + * @returns {undefined} + */ function setUpTextWithTspan () { setUp(); elem = document.createElementNS(NS.SVG, 'text'); @@ -84,6 +97,10 @@ function setUpTextWithTspan () { svg.append(elem); } +/** + * Tear down the tests (empty the svg element). + * @returns {undefined} + */ function tearDown () { while (svg.hasChildNodes()) { svg.firstChild.remove(); diff --git a/test/select_test.js b/test/select_test.js index 8f4e9523..9b7570dd 100644 --- a/test/select_test.js +++ b/test/select_test.js @@ -1,6 +1,6 @@ /* eslint-env qunit */ -import {NS} from '../editor/namespaces.js'; import * as select from '../editor/select.js'; +import {NS} from '../editor/namespaces.js'; // log function QUnit.log((details) => { @@ -23,16 +23,20 @@ const mockConfig = { */ const mockFactory = { createSVGElement (jsonMap) { - const elem = document.createElementNS(NS.SVG, jsonMap['element']); - for (const attr in jsonMap.attr) { - elem.setAttribute(attr, jsonMap.attr[attr]); - } + 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; } }; +/** + * Potentially reusable test set-up. + * @returns {undefined} + */ function setUp () { svgroot = mockFactory.createSVGElement({ element: 'svg', @@ -66,6 +70,10 @@ function setUpWithInit () { } */ +/** + * Tear down the test by emptying our sandbox area. + * @returns {undefined} + */ function tearDown () { while (sandbox.hasChildNodes()) { sandbox.firstChild.remove(); @@ -81,10 +89,10 @@ QUnit.test('Test svgedit.select package', function (assert) { assert.ok(select.init); assert.ok(select.getSelectorManager); assert.equal(typeof select, typeof {}); - assert.equal(typeof select.Selector, typeof function () {}); - assert.equal(typeof select.SelectorManager, typeof function () {}); - assert.equal(typeof select.init, typeof function () {}); - assert.equal(typeof select.getSelectorManager, typeof function () {}); + assert.equal(typeof select.Selector, typeof function () { /* */ }); + assert.equal(typeof select.SelectorManager, typeof function () { /* */ }); + assert.equal(typeof select.init, typeof function () { /* */ }); + assert.equal(typeof select.getSelectorManager, typeof function () { /* */ }); }); QUnit.test('Test Selector DOM structure', function (assert) { diff --git a/test/sinon/sinon-qunit.js b/test/sinon/sinon-qunit.js index d719de78..bf7637de 100644 --- a/test/sinon/sinon-qunit.js +++ b/test/sinon/sinon-qunit.js @@ -1,6 +1,20 @@ // Adapted from https://www.npmjs.com/package/sinon-test -export default function ({sinon, QUnit}) { +/** + * @external QUnit + */ +/** + * @external sinon + */ + +/** + * Adds methods to Sinon using a QUnit implementation. + * @param {PlainObject} implementations + * @param {external:sinon} implementations.sinon + * @param {external:QUnit} implementations.QUnit + * @returns {undefined} + */ +export default function sinonQunit ({sinon, QUnit}) { sinon.assert.fail = function (msg) { QUnit.ok(false, msg); }; @@ -10,7 +24,7 @@ export default function ({sinon, QUnit}) { }; const qTest = QUnit.test; - QUnit.test = function (testName, callback) { + QUnit.test = function (testName, callback) { // eslint-disable-line promise/prefer-await-to-callbacks return qTest(testName, sinon.test(callback)); }; } diff --git a/test/svgtransformlist_test.js b/test/svgtransformlist_test.js index a8732223..f2ce5691 100644 --- a/test/svgtransformlist_test.js +++ b/test/svgtransformlist_test.js @@ -21,6 +21,10 @@ QUnit.log((details) => { const svgroot = document.querySelector('#svgroot'); let svgcontent, rect, circle; +/** + * Set up tests, adding elements. + * @returns {undefined} + */ function setUp () { svgcontent = svgroot.appendChild(document.createElementNS(NS.SVG, 'svg')); rect = svgcontent.appendChild(document.createElementNS(NS.SVG, 'rect')); @@ -29,6 +33,10 @@ function setUp () { circle.id = 'c'; } +/** + * Tear down tests, emptying SVG root, and resetting list map. + * @returns {undefined} + */ function tearDown () { transformlist.resetListMap(); while (svgroot.hasChildNodes()) { @@ -82,7 +90,7 @@ QUnit.test('Test SVGTransformList.initialize()', function (assert) { const t = svgcontent.createSVGTransform(); assert.ok(t); assert.ok(rxform.initialize); - assert.equal(typeof rxform.initialize, typeof function () {}); + assert.equal(typeof rxform.initialize, typeof function () { /* */ }); rxform.initialize(t); assert.equal(rxform.numberOfItems, 1); assert.equal(cxform.numberOfItems, 0); @@ -110,8 +118,8 @@ QUnit.test('Test SVGTransformList.appendItem() and getItem()', function (assert) assert.ok(rxform.appendItem); assert.ok(rxform.getItem); - assert.equal(typeof rxform.appendItem, typeof function () {}); - assert.equal(typeof rxform.getItem, typeof function () {}); + assert.equal(typeof rxform.appendItem, typeof function () { /* */ }); + assert.equal(typeof rxform.getItem, typeof function () { /* */ }); rxform.appendItem(t1); rxform.appendItem(t2); @@ -145,7 +153,7 @@ QUnit.test('Test SVGTransformList.removeItem()', function (assert) { const t1 = svgcontent.createSVGTransform(), t2 = svgcontent.createSVGTransform(); assert.ok(rxform.removeItem); - assert.equal(typeof rxform.removeItem, typeof function () {}); + assert.equal(typeof rxform.removeItem, typeof function () { /* */ }); rxform.appendItem(t1); rxform.appendItem(t2); @@ -168,7 +176,7 @@ QUnit.test('Test SVGTransformList.replaceItem()', function (assert) { const cxform = transformlist.getTransformList(circle); assert.ok(rxform.replaceItem); - assert.equal(typeof rxform.replaceItem, typeof function () {}); + assert.equal(typeof rxform.replaceItem, typeof function () { /* */ }); const t1 = svgcontent.createSVGTransform(), t2 = svgcontent.createSVGTransform(), @@ -205,7 +213,7 @@ QUnit.test('Test SVGTransformList.insertItemBefore()', function (assert) { const cxform = transformlist.getTransformList(circle); assert.ok(rxform.insertItemBefore); - assert.equal(typeof rxform.insertItemBefore, typeof function () {}); + assert.equal(typeof rxform.insertItemBefore, typeof function () { /* */ }); const t1 = svgcontent.createSVGTransform(), t2 = svgcontent.createSVGTransform(), diff --git a/test/test1.js b/test/test1.js index 2efffa02..30117dce 100644 --- a/test/test1.js +++ b/test/test1.js @@ -40,7 +40,8 @@ const svgCanvas = new SvgCanvas( extensions: ['ext-arrows.js', 'ext-connector.js', 'ext-eyedropper.js'], initTool: 'select', wireframe: false - }); + } +); const // svgroot = document.getElementById('svgroot'), @@ -188,7 +189,7 @@ QUnit.test('Test import math elements inside a foreignObject', function (assert) // see Bug https://bugs.webkit.org/show_bug.cgi?id=35042 const math = fo.firstChild; - assert.equal(!!math, true, 'Math element exists'); + 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'); @@ -227,12 +228,13 @@ QUnit.test('Test importing SVG remaps IDs', function (assert) { /* const doc = */ svgCanvas.setSvgString( '' + - 'Layer 1' + - '' + - '' + - '' + - '' + - ''); + 'Layer 1' + + '' + + '' + + '' + + '' + + '' + ); svgCanvas.importSvgString( '' + diff --git a/test/ui-tests/accessibility.js b/test/ui-tests/accessibility.js index e025f763..e2001e5f 100644 --- a/test/ui-tests/accessibility.js +++ b/test/ui-tests/accessibility.js @@ -6,14 +6,14 @@ import axeCheck from 'axe-testcafe'; fixture`TestCafe Axe accessibility tests (Editor - no parameters)` .page`http://localhost:8000/editor/svg-editor.html`; -test('Editor - no parameters', async t => { +test('Editor - no parameters', async (t) => { await axeCheck(t); // , axeContent, axeOptions: https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axerun }); fixture`TestCafe Axe accessibility tests (Editor - with all extensions)` .page`http://localhost:8000/editor/svg-editor.html?extensions=ext-arrows.js,ext-closepath.js,ext-foreignobject.js,ext-helloworld.js,ext-mathjax.js,ext-php_savefile.js,ext-server_moinsave.js,ext-server_opensave.js,ext-webappfind.js,ext-xdomain-messaging.js`; -test('Editor ES - with all extensions', async t => { +test('Editor ES - with all extensions', async (t) => { await axeCheck(t); // , axeContent, axeOptions: https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axerun }); diff --git a/test/ui-tests/ui.js b/test/ui-tests/ui.js index 94511680..6c15c01a 100644 --- a/test/ui-tests/ui.js +++ b/test/ui-tests/ui.js @@ -6,14 +6,14 @@ import {Selector} from 'testcafe'; fixture`TestCafe UI tests` .page`http://localhost:8000/editor/svg-editor.html`; -test('Editor - No parameters: Export button', async t => { +test('Editor - No parameters: Export button', async (t) => { await t .click('#dialog_buttons > input[type=button][value=OK]') .click('#main_icon') .expect(Selector('#tool_export')).ok('Has open button'); }); -test('Editor - No parameters: Export button clicking', async t => { +test('Editor - No parameters: Export button clicking', async (t) => { await t .click('#dialog_buttons > input[type=button][value=OK]') .click('#main_icon') diff --git a/test/units_test.js b/test/units_test.js index cf386dc7..09248998 100644 --- a/test/units_test.js +++ b/test/units_test.js @@ -8,6 +8,10 @@ QUnit.log((details) => { } }); +/** + * Set up tests, supplying mock data. + * @returns {undefined} + */ function setUp () { units.init( /** @@ -35,7 +39,7 @@ QUnit.test('Test svgedit.units.shortFloat()', function (assert) { setUp(); assert.ok(units.shortFloat); - assert.equal(typeof units.shortFloat, typeof function () {}); + assert.equal(typeof units.shortFloat, typeof function () { /* */ }); const {shortFloat} = units; assert.equal(shortFloat(0.00000001), 0); @@ -51,7 +55,7 @@ QUnit.test('Test svgedit.units.isValidUnit()', function (assert) { setUp(); assert.ok(units.isValidUnit); - assert.equal(typeof units.isValidUnit, typeof function () {}); + assert.equal(typeof units.isValidUnit, typeof function () { /* */ }); const {isValidUnit} = units; assert.ok(isValidUnit('0')); @@ -79,7 +83,7 @@ QUnit.test('Test svgedit.units.convertUnit()', function (assert) { setUp(); assert.ok(units.convertUnit); - assert.equal(typeof units.convertUnit, typeof function () {}); + assert.equal(typeof units.convertUnit, typeof function () { /* */ }); // cm in default setup assert.equal(units.convertUnit(42), 1.1113); assert.equal(units.convertUnit(42, 'px'), 42); diff --git a/test/utilities_bbox_test.js b/test/utilities_bbox_test.js index c5ff45de..a436567c 100644 --- a/test/utilities_bbox_test.js +++ b/test/utilities_bbox_test.js @@ -16,14 +16,25 @@ QUnit.log((details) => { } }); +/** + * Create an SVG element for a mock. + * @param {module:utilities.SVGElementJSON} jsonMap + * @returns {SVGElement} + */ function mockCreateSVGElement (jsonMap) { - const elem = document.createElementNS(NS.SVG, jsonMap['element']); - for (const attr in jsonMap['attr']) { - elem.setAttribute(attr, jsonMap['attr'][attr]); - } + const elem = document.createElementNS(NS.SVG, jsonMap.element); + Object.entries(jsonMap.attr).forEach(([attr, value]) => { + elem.setAttribute(attr, value); + }); return elem; } let mockaddSVGElementFromJsonCallCount = 0; + +/** + * Mock of {@link module:utilities.EditorContext#addSVGElementFromJson}. + * @param {module:utilities.SVGElementJSON} json + * @returns {SVGElement} + */ function mockaddSVGElementFromJson (json) { const elem = mockCreateSVGElement(json); svgroot.append(elem); @@ -32,7 +43,7 @@ function mockaddSVGElementFromJson (json) { } const mockPathActions = { resetOrientation (pth) { - if (pth == null || pth.nodeName !== 'path') { return false; } + if (utilities.isNullish(pth) || pth.nodeName !== 'path') { return false; } const tlist = transformlist.getTransformList(pth); const m = math.transformListToTransform(tlist).matrix; tlist.clear(); @@ -57,6 +68,7 @@ const mockPathActions = { path.replacePathSeg(type, i, pts, pth); } // path.reorientGrads(pth, m); + return undefined; } }; @@ -75,8 +87,6 @@ QUnit.module('svgedit.utilities_bbox', { transformlist.resetListMap(); path.init(null); mockaddSVGElementFromJsonCallCount = 0; - }, - afterEach () { } }); @@ -167,7 +177,7 @@ QUnit.test('Test getBBoxWithTransform and a rotation transform', function (asser const rect = {x: 10, y: 10, width: 10, height: 20}; const angle = 45; - const origin = {x: 15, y: 20}; + const origin = {x: 15, y: 20}; // eslint-disable-line no-shadow 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 + ')'} @@ -256,7 +266,7 @@ QUnit.test('Test getBBoxWithTransform with rotation and matrix transforms', func const rect = {x: 10, y: 10, width: 10, height: 20}; const angle = 45; - const origin = {x: 15, y: 20}; + const origin = {x: 15, y: 20}; // eslint-disable-line no-shadow tx = 10; // tx right ty = 10; // tx down txInRotatedSpace = Math.sqrt(tx * tx + ty * ty); // translate in rotated 45 space. @@ -443,10 +453,23 @@ QUnit.test('Test getStrokedBBox with no stroke-width attribute', function (asser g.remove(); }); +/** + * Returns radians for degrees. + * @param {Float} degrees + * @returns {Float} + */ function radians (degrees) { return degrees * Math.PI / 180; } -function rotatePoint (point, angle, origin) { + +/** + * + * @param {module:utilities.BBoxObject} point + * @param {Float} angle + * @param {module:math.XYObject} origin + * @returns {module:math.XYObject} + */ +function rotatePoint (point, angle, origin) { // eslint-disable-line no-shadow if (!origin) { origin = {x: 0, y: 0}; } @@ -458,7 +481,14 @@ function rotatePoint (point, angle, origin) { y: x * Math.sin(theta) + y * Math.cos(theta) + origin.y }; } -function rotateRect (rect, angle, origin) { +/** + * + * @param {module:utilities.BBoxObject} rect + * @param {Float} angle + * @param {module:math.XYObject} origin + * @returns {module:utilities.BBoxObject} + */ +function rotateRect (rect, angle, origin) { // eslint-disable-line no-shadow 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); diff --git a/test/utilities_performance_test.js b/test/utilities_performance_test.js index 7db1e0d1..aa65a7a8 100644 --- a/test/utilities_performance_test.js +++ b/test/utilities_performance_test.js @@ -14,13 +14,24 @@ QUnit.log((details) => { const currentLayer = document.getElementById('layer1'); +/** + * Create an SVG element for a mock. + * @param {module:utilities.SVGElementJSON} jsonMap + * @returns {SVGElement} + */ function mockCreateSVGElement (jsonMap) { - const elem = document.createElementNS(NS.SVG, jsonMap['element']); - for (const attr in jsonMap['attr']) { - elem.setAttribute(attr, jsonMap['attr'][attr]); - } + 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}. + * @param {module:utilities.SVGElementJSON} json + * @returns {SVGElement} + */ function mockaddSVGElementFromJson (json) { const elem = mockCreateSVGElement(json); currentLayer.append(elem); @@ -31,27 +42,28 @@ function mockaddSVGElementFromJson (json) { const groupWithMatrixTransform = document.getElementById('svg_group_with_matrix_transform'); const textWithMatrixTransform = document.getElementById('svg_text_with_matrix_transform'); +/** + * Toward performance testing, fill document with clones of element. + * @param {SVGElement} elem + * @param {Integer} count + * @returns {undefined} + */ function fillDocumentByCloningElement (elem, count) { const elemId = elem.getAttribute('id') + '-'; for (let index = 0; index < count; index++) { 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 parent = elem.parentNode; - parent.append(clone); + const {parentNode} = elem; + parentNode.append(clone); } } -QUnit.module('svgedit.utilities_performance', { - beforeEach () { - }, - afterEach () { - } -}); +QUnit.module('svgedit.utilities_performance'); const mockPathActions = { resetOrientation (path) { - if (path == null || path.nodeName !== 'path') { return false; } + if (utilities.isNullish(path) || path.nodeName !== 'path') { return false; } const tlist = transformlist.getTransformList(path); const m = math.transformListToTransform(tlist).matrix; tlist.clear(); @@ -64,10 +76,13 @@ const mockPathActions = { for (let i = 0; i < len; ++i) { const seg = segList.getItem(i); const type = seg.pathSegType; - if (type === 1) { continue; } + if (type === 1) { + continue; + } const pts = []; ['', 1, 2].forEach(function (n, j) { - const x = seg['x' + n], y = seg['y' + n]; + const x = seg['x' + n], + 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); @@ -77,6 +92,7 @@ const mockPathActions = { } // utilities.reorientGrads(path, m); + return undefined; } }; @@ -144,11 +160,11 @@ QUnit.test('Test svgCanvas.getStrokedBBox() performance with matrix transforms', // The second pass is two to ten times faster. setTimeout(function () { - const count = children.length; + const ct = children.length; - const start = lastTime = now = Date.now(); + const strt = lastTime = now = Date.now(); // Skip the first child which is the title. - for (let index = 1; index < count; index++) { + 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; @@ -157,10 +173,10 @@ QUnit.test('Test svgCanvas.getStrokedBBox() performance with matrix transforms', max = Math.max(max, delta); } - total = lastTime - start; - const ave = total / count; - assert.ok(ave < 2, 'svgedit.utilities.getStrokedBBox average execution time is less than 1 ms'); - console.log('Pass2 svgCanvas.getStrokedBBox total ms ' + total + ', ave ms ' + ave.toFixed(1) + ',\t min/max ' + min + ' ' + max); + total = lastTime - strt; + const avg = total / ct; + assert.ok(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); done(); }); diff --git a/test/utilities_test.js b/test/utilities_test.js index cd4c017b..919166c7 100644 --- a/test/utilities_test.js +++ b/test/utilities_test.js @@ -1,8 +1,8 @@ /* eslint-env qunit */ -import {NS} from '../editor/namespaces.js'; -import * as utilities from '../editor/utilities.js'; import * as browser from '../editor/browser.js'; +import * as utilities from '../editor/utilities.js'; +import {NS} from '../editor/namespaces.js'; // log function QUnit.log((details) => { @@ -11,19 +11,29 @@ QUnit.log((details) => { } }); +/** + * Create an element for test. + * @param {module:utilities.SVGElementJSON} jsonMap + * @returns {SVGElement} + */ function mockCreateSVGElement (jsonMap) { - const elem = document.createElementNS(NS.SVG, jsonMap['element']); - for (const attr in jsonMap['attr']) { - elem.setAttribute(attr, jsonMap['attr'][attr]); - } + 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; } -const mockPathActions = {resetOrientation () {}}; +const mockPathActions = {resetOrientation () { /* */ }}; let mockHistorySubCommands = []; const mockHistory = { BatchCommand: class { @@ -34,7 +44,8 @@ const mockHistory = { } }, RemoveElementCommand: class { - constructor (elem, nextSibling, parent) { // Longhand needed since used as a constructor + // Longhand needed since used as a constructor + constructor (elem, nextSibling, parent) { this.elem = elem; this.nextSibling = nextSibling; this.parent = parent; @@ -51,9 +62,28 @@ const mockCount = { addToSelection: 0, addCommandToHistory: 0 }; -function mockClearSelection () { mockCount.clearSelection++; } -function mockAddToSelection () { mockCount.addToSelection++; } -function mockAddCommandToHistory () { mockCount.addCommandToHistory++; } + +/** + * Increments clear seleciton count for mock test. + * @returns {undefined} + */ +function mockClearSelection () { + mockCount.clearSelection++; +} +/** +* Increments add selection count for mock test. + * @returns {undefined} + */ +function mockAddToSelection () { + mockCount.addToSelection++; +} +/** +* Increments add command to history count for mock test. + * @returns {undefined} + */ +function mockAddCommandToHistory () { + mockCount.addCommandToHistory++; +} const svg = document.createElementNS(NS.SVG, 'svg'); const sandbox = document.getElementById('sandbox'); @@ -70,8 +100,7 @@ QUnit.module('svgedit.utilities', { mockCount.addToSelection = 0; mockCount.addCommandToHistory = 0; }, - afterEach () { - } + afterEach () { /* */ } }); QUnit.test('Test svgedit.utilities package', function (assert) { @@ -79,7 +108,7 @@ QUnit.test('Test svgedit.utilities package', function (assert) { assert.ok(utilities); assert.ok(utilities.toXml); - assert.equal(typeof utilities.toXml, typeof function () {}); + assert.equal(typeof utilities.toXml, typeof function () { /* */ }); }); QUnit.test('Test svgedit.utilities.toXml() function', function (assert) { @@ -259,6 +288,10 @@ QUnit.test('Test getPathDFromElement', function (assert) { }); QUnit.test('Test getBBoxOfElementAsPath', function (assert) { + /** + * Wrap `utilities.getBBoxOfElementAsPath` to convert bbox to object for testing. + * @implements {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.