- Enhancement: Return a Promise for Editor's `setCustomHandlers`,

`loadFromString`, `loadFromDataURI` so known when ready and set
- Docs (Refactoring): Formally specify `Promise` resolve type;
    add `typedef` for dialog result object; add an
    `ArbitraryCallbackResult` type
master
Brett Zamir 2019-04-05 22:59:41 +08:00
parent 5abd183c43
commit 91c0dc549b
11 changed files with 83 additions and 52 deletions

View File

@ -1,5 +1,13 @@
# SVG-Edit CHANGES
## ?
- Enhancement: Return a Promise for Editor's `setCustomHandlers`,
`loadFromString`, `loadFromDataURI` so known when ready and set
- Docs (Refactoring): Formally specify `Promise` resolve type;
add `typedef` for dialog result object; add an
`ArbitraryCallbackResult` type
## 4.3.0
- Fix: Droplets for gradient pickers can now be double-clicked in

View File

@ -23,13 +23,19 @@ export default function jQueryPluginDBox ($, strings = {ok: 'Ok', cancel: 'Cance
btnHolder = $('#dialog_buttons'),
dialogContent = $('#dialog_content');
/**
* @typedef {PlainObject} module:jQueryPluginDBox.PromiseResultObject
* @property {string|true} response
* @property {boolean} checked
*/
/**
* Resolves to `false` (if cancelled), for prompts and selects
* without checkboxes, it resolves to the value of the form control. For other
* types without checkboxes, it resolves to `true`. For checkboxes, it resolves
* to an object with the `response` key containing the same value as the previous
* mentioned (string or `true`) and a `checked` (boolean) property.
* @typedef {Promise} module:jQueryPluginDBox.PromiseResult
* @typedef {Promise<boolean|string|module:jQueryPluginDBox.PromiseResultObject>} module:jQueryPluginDBox.PromiseResult
*/
/**
* @typedef {PlainObject} module:jQueryPluginDBox.SelectOption

View File

@ -388,7 +388,7 @@ export default {
/**
* @param {"start"|"mid"|"end"} pos
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
async function showTextPrompt (pos) {
let def = $('#' + pos + '_marker').val();
@ -426,7 +426,7 @@ export default {
// callback function for a toolbar button click
/**
* @param {Event} ev
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
async function setArrowFromButton (ev) {
const parts = this.id.split('_');

View File

@ -240,7 +240,7 @@ export default {
}
/**
* @param {Event} ev
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
function setArrowFromButton (ev) {
const parts = this.id.split('_');

View File

@ -40,7 +40,7 @@ function addScriptAtts (script, atts) {
* @function module:importModule.importSetGlobalDefault
* @param {string|string[]} url
* @param {module:importModule.ImportConfig} config
* @returns {Promise} The value to which it resolves 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 function importSetGlobalDefault (url, config) {
return importSetGlobal(url, {...config, returnDefault: true});
@ -49,7 +49,7 @@ export function importSetGlobalDefault (url, config) {
* @function module:importModule.importSetGlobal
* @param {string|string[]} url
* @param {module:importModule.ImportConfig} config
* @returns {Promise} The promise resolves to either an `ArbitraryModule` or
* @returns {Promise<ArbitraryModule>} 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: glob, returnDefault}) {
@ -68,7 +68,7 @@ export async function importSetGlobal (url, {global: glob, returnDefault}) {
* @author Brett Zamir (other items are from `dynamic-import-polyfill`)
* @param {string|string[]} url
* @param {PlainObject} [atts={}]
* @returns {Promise} Resolves to `undefined` or rejects with an `Error` upon a
* @returns {Promise<void|Error>} Resolves to `undefined` or rejects with an `Error` upon a
* script loading error
*/
export function importScript (url, atts = {}) {
@ -112,14 +112,14 @@ export function importScript (url, atts = {}) {
}
/**
*
* @param {string|string[]} url
* @param {PlainObject} [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.
*/
*
* @param {string|string[]} url
* @param {PlainObject} [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) => {

View File

@ -127,7 +127,7 @@ export const init = (editor) => {
* @function module:locale.readLang
* @param {module:locale.LocaleStrings} langData See {@tutorial LocaleDocs}
* @fires module:svgcanvas.SvgCanvas#event:ext-addLangData
* @returns {Promise} Resolves to [`LangAndData`]{@link module:locale.LangAndData}
* @returns {Promise<module:locale.LangAndData>} Resolves to [`LangAndData`]{@link module:locale.LangAndData}
*/
export const readLang = async function (langData) {
const more = await editor_.addLangData(langParam);
@ -366,7 +366,7 @@ export const readLang = async function (langData) {
* @fires module:svgcanvas.SvgCanvas#event:ext-addLangData
* @fires module:svgcanvas.SvgCanvas#event:ext-langReady
* @fires module:svgcanvas.SvgCanvas#event:ext-langChanged
* @returns {Promise} Resolves to result of {@link module:locale.readLang}
* @returns {Promise<module:locale.LangAndData>} Resolves to result of {@link module:locale.readLang}
*/
export const putLocale = async function (givenParam, goodLangs, conf) {
if (givenParam) {

View File

@ -318,7 +318,7 @@ let svgCanvas, urldata,
* @param {PlainObject} [opts={}]
* @param {boolean} [opts.noAlert]
* @throws {Error} Upon failure to load SVG
* @returns {Promise} Resolves to undefined upon success (or if `noAlert` is
* @returns {Promise<void>} Resolves to undefined upon success (or if `noAlert` is
* falsey, though only until after the `alert` is closed); rejects if SVG
* loading fails and `noAlert` is truthy.
*/
@ -348,13 +348,13 @@ function getImportLocale ({defaultLang, defaultName}) {
* @param {PlainObject} localeInfo
* @param {string} [localeInfo.name] Defaults to `defaultName` of {@link module:SVGEditor~getImportLocale}
* @param {string} [localeInfo.lang=defaultLang] Defaults to `defaultLang` of {@link module:SVGEditor~getImportLocale}
* @returns {Promise} Resolves to {@link module:locale.LocaleStrings}
* @returns {Promise<module:locale.LocaleStrings>} Resolves to {@link module:locale.LocaleStrings}
*/
return async function importLocaleDefaulting ({name = defaultName, lang = defaultLang} = {}) {
/**
*
* @param {string} language
* @returns {Promise} Resolves to {@link module:locale.LocaleStrings}
* @returns {Promise<module:locale.LocaleStrings>} Resolves to {@link module:locale.LocaleStrings}
*/
function importLocale (language) {
const url = `${curConfig.extPath}ext-locale/${name}/${language}.js`;
@ -607,10 +607,10 @@ editor.setConfig = function (opts, cfgCfg) {
* Allows one to override default SVGEdit `open`, `save`, and
* `export` editor behaviors.
* @param {module:SVGEditor.CustomHandler} opts Extension mechanisms may call `setCustomHandlers` with three functions: `opts.open`, `opts.save`, and `opts.exportImage`
* @returns {undefined}
* @returns {Promise<void>}
*/
editor.setCustomHandlers = function (opts) {
editor.ready(function () {
return editor.ready(function () {
if (opts.open) {
$('#tool_open > input[type="file"]').remove();
$('#tool_open').show();
@ -798,7 +798,7 @@ editor.init = function () {
* @fires module:svgcanvas.SvgCanvas#event:ext-langReady
* @fires module:svgcanvas.SvgCanvas#event:ext-langChanged
* @fires module:svgcanvas.SvgCanvas#event:extensions_added
* @returns {Promise} Resolves to result of {@link module:locale.readLang}
* @returns {Promise<module:locale.LangAndData>} Resolves to result of {@link module:locale.readLang}
*/
const extAndLocaleFunc = async function () {
// const lang = ('lang' in curPrefs) ? curPrefs.lang : null;
@ -1884,7 +1884,7 @@ editor.init = function () {
/**
* @param {PlainObject} [opts={}]
* @param {boolean} [opts.cancelDeletes=false}]
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
async function promptImgURL ({cancelDeletes = false} = {}) {
let curhref = svgCanvas.getHref(selectedElement);
@ -3041,7 +3041,7 @@ editor.init = function () {
* @param {external:Window} win
* @param {module:svgcanvas.SvgCanvas#event:extension_added} ext
* @listens module:svgcanvas.SvgCanvas#event:extension_added
* @returns {Promise|undefined} Resolves to `undefined`
* @returns {Promise<void>|undefined} Resolves to `undefined`
*/
const extAdded = async function (win, ext) {
if (!ext) {
@ -4324,7 +4324,7 @@ editor.init = function () {
/**
*
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
const makeHyperlink = async function () {
if (!Utils.isNullish(selectedElement) || multiselected) {
@ -4432,7 +4432,7 @@ editor.init = function () {
/**
* @fires module:svgcanvas.SvgCanvas#event:ext-onNewDocument
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
const clickClear = async function () {
const [x, y] = curConfig.dimensions;
@ -4487,7 +4487,7 @@ editor.init = function () {
let loadingURL;
/**
*
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
const clickExport = async function () {
const imgType = await $.select('Select an image type for export: ', [
@ -4724,7 +4724,7 @@ editor.init = function () {
/**
*
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
const saveSourceEditor = async function () {
if (!editingsource) { return; }
@ -4853,7 +4853,7 @@ editor.init = function () {
/**
*
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
const cancelOverlays = async function () {
$('#dialog_box').hide();
@ -6045,7 +6045,7 @@ editor.init = function () {
};
/**
* @returns {Promise} Resolves to boolean indicating `true` if there were no changes
* @returns {Promise<boolean>} Resolves to boolean indicating `true` if there were no changes
* and `false` after the user confirms.
*/
editor.openPrep = function () {
@ -6213,7 +6213,7 @@ editor.init = function () {
* @param {module:locale.LocaleStrings} allStrings See {@tutorial LocaleDocs}
* @fires module:svgcanvas.SvgCanvas#event:ext-langReady
* @fires module:svgcanvas.SvgCanvas#event:ext-langChanged
* @returns {Promise} A Promise which resolves to `undefined`
* @returns {Promise<void>} A Promise which resolves to `undefined`
*/
const setLang = editor.setLang = async function (lang, allStrings) {
editor.langChanged = true;
@ -6336,14 +6336,14 @@ editor.init = function () {
/**
* @callback module:SVGEditor.ReadyCallback
* @returns {Promise|undefined}
* @returns {Promise<void>|undefined}
*/
/**
* Queues a callback to be invoked when the editor is ready (or
* to be invoked immediately if it is already ready--i.e.,
* if `runCallbacks` has been run).
* @param {module:SVGEditor.ReadyCallback} cb Callback to be queued to invoke
* @returns {Promise} Resolves when all callbacks, including the supplied have resolved
* @returns {Promise<ArbitraryCallbackResult>} Resolves when all callbacks, including the supplied have resolved
*/
editor.ready = function (cb) { // eslint-disable-line promise/prefer-await-to-callbacks
return new Promise((resolve, reject) => { // eslint-disable-line promise/avoid-new
@ -6357,7 +6357,7 @@ editor.ready = function (cb) { // eslint-disable-line promise/prefer-await-to-ca
/**
* Invokes the callbacks previous set by `svgEditor.ready`
* @returns {Promise} Resolves to `undefined` if all callbacks succeeded and rejects otherwise
* @returns {Promise<void>} Resolves to `undefined` if all callbacks succeeded and rejects otherwise
*/
editor.runCallbacks = async function () {
try {
@ -6380,10 +6380,10 @@ editor.runCallbacks = async function () {
* @param {string} str The SVG string to load
* @param {PlainObject} [opts={}]
* @param {boolean} [opts.noAlert=false] Option to avoid alert to user and instead get rejected promise
* @returns {Promise}
* @returns {Promise<void>}
*/
editor.loadFromString = function (str, {noAlert} = {}) {
editor.ready(async function () {
return editor.ready(async function () {
try {
await loadSvgString(str, {noAlert});
} catch (err) {
@ -6416,7 +6416,7 @@ editor.disableUI = function (featList) {
* @param {PlainObject} [opts={}] May contain properties: `cache`, `callback`
* @param {boolean} [opts.cache]
* @param {boolean} [opts.noAlert]
* @returns {Promise} Resolves to `undefined` or rejects upon bad loading of
* @returns {Promise<void>} Resolves to `undefined` or rejects upon bad loading of
* the SVG (or upon failure to parse the loaded string) when `noAlert` is
* enabled
*/
@ -6457,10 +6457,10 @@ editor.loadFromURL = function (url, {cache, noAlert} = {}) {
* @param {string} str The Data URI to base64-decode (if relevant) and load
* @param {PlainObject} [opts={}]
* @param {boolean} [opts.noAlert]
* @returns {Promise} Resolves to `undefined` and rejects if loading SVG string fails and `noAlert` is enabled
* @returns {Promise<void>} Resolves to `undefined` and rejects if loading SVG string fails and `noAlert` is enabled
*/
editor.loadFromDataURI = function (str, {noAlert} = {}) {
editor.ready(function () {
return editor.ready(function () {
let base64 = false;
let pre = str.match(/^data:image\/svg\+xml;base64,/);
if (pre) {
@ -6481,7 +6481,7 @@ editor.loadFromDataURI = function (str, {noAlert} = {}) {
* @param {module:svgcanvas.ExtensionInitCallback} init Config to be invoked on this module
* @param {module:svgcanvas.ExtensionInitArgs} initArgs
* @throws {Error} If called too early
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
editor.addExtension = function (name, init, initArgs) {
// Note that we don't want this on editor.ready since some extensions

View File

@ -1113,7 +1113,7 @@ const runExtensions = this.runExtensions = function (action, vars, returnArray,
/**
* @function module:svgcanvas.ExtensionInitResponse#addLangData
* @param {module:svgcanvas.SvgCanvas#event:ext-addLangData} arg
* @returns {Promise} Resolves to {@link module:locale.ExtensionLocaleData}
* @returns {Promise<module:locale.ExtensionLocaleData>} Resolves to {@link module:locale.ExtensionLocaleData}
*/
/**
* @function module:svgcanvas.ExtensionInitResponse#onNewDocument
@ -1136,7 +1136,7 @@ const runExtensions = this.runExtensions = function (action, vars, returnArray,
* @callback module:svgcanvas.ExtensionInitCallback
* @this module:SVGEditor
* @param {module:svgcanvas.ExtensionArgumentObject} arg
* @returns {Promise} Resolves to [ExtensionInitResponse]{@link module:svgcanvas.ExtensionInitResponse} or `undefined`
* @returns {Promise<module:svgcanvas.ExtensionInitResponse|void>} Resolves to [ExtensionInitResponse]{@link module:svgcanvas.ExtensionInitResponse} or `undefined`
*/
/**
* @typedef {PlainObject} module:svgcanvas.ExtensionInitArgs
@ -1152,7 +1152,7 @@ const runExtensions = this.runExtensions = function (action, vars, returnArray,
* @fires module:svgcanvas.SvgCanvas#event:extension_added
* @throws {TypeError|Error} `TypeError` if `extInitFunc` is not a function, `Error`
* if extension of supplied name already exists
* @returns {Promise} Resolves to `undefined`
* @returns {Promise<void>} Resolves to `undefined`
*/
this.addExtension = async function (name, extInitFunc, {$: jq, importLocale}) {
if (typeof extInitFunc !== 'function') {
@ -1416,7 +1416,7 @@ canvas.call = call;
/**
* The promise return, if present, resolves to `undefined`
* (`extension_added`, `exported`, `saved`)
* @typedef {Promise|undefined} module:svgcanvas.EventHandlerReturn
* @typedef {Promise<void>|void} module:svgcanvas.EventHandlerReturn
*/
/**
@ -3827,7 +3827,7 @@ this.svgToString = function (elem, indent) {
* Converts a given image file to a data URL when possible, then runs a given callback.
* @function module:svgcanvas.SvgCanvas#embedImage
* @param {string} src - The path/URL of the image
* @returns {Promise} Resolves to Data URL (string|false)
* @returns {Promise<string|false>} Resolves to a Data URL (string|false)
*/
this.embedImage = function (src) {
// Todo: Remove this Promise in favor of making an async/await `Image.load` utility
@ -3967,7 +3967,7 @@ let canvg;
* @param {boolean} [opts.avoidEvent]
* @fires module:svgcanvas.SvgCanvas#event:exported
* @todo Confirm/fix ICO type
* @returns {Promise} Resolves to {@link module:svgcanvas.ImageExportedResults}
* @returns {Promise<module:svgcanvas.ImageExportedResults>} Resolves to {@link module:svgcanvas.ImageExportedResults}
*/
this.rasterExport = async function (imgType, quality, exportWindowName, opts = {}) {
const type = imgType === 'ICO' ? 'BMP' : (imgType || 'PNG');
@ -4053,7 +4053,7 @@ this.rasterExport = async function (imgType, quality, exportWindowName, opts = {
* @param {string} [exportWindowName] Will also be used for the download file name here
* @param {external:jsPDF.OutputType} [outputType="dataurlstring"]
* @fires module:svgcanvas.SvgCanvas#event:exportedPDF
* @returns {Promise} Resolves to {@link module:svgcanvas.PDFExportedResults}
* @returns {Promise<module:svgcanvas.PDFExportedResults>} Resolves to {@link module:svgcanvas.PDFExportedResults}
*/
this.exportPDF = async function (
exportWindowName,

View File

@ -10,10 +10,16 @@
* @typedef {null|boolean|Float|string|GenericArray|PlainObject} JSON
*/
/**
* This should only be used when the return result from a callback
* is not known as to type.
* @typedef {*} ArbitraryCallbackResult
*/
/**
* @callback GenericCallback
* @param {...*} args Signature dependent on the function
* @returns {*} Return dependent on the function
* @returns {ArbitraryCallbackResult} Return dependent on the function
*/
/**

View File

@ -76,16 +76,24 @@ function reduceFalseMatches (file, res) {
].includes(line);
});
break;
case 'editor/external/dynamic-import-polyfill/importModule.js':
res.line = res.line.filter((line) => {
return ![
'* @returns {Promise<*>} The value to which it resolves depends on the export of the targeted module.',
'* @returns {Promise<*>} Resolves to value of loading module or rejects with'
].includes(line);
});
break;
case 'editor/typedefs.js':
res.line = res.line.filter((line) => {
return ![
'* @typedef {number} Float',
'* @typedef {*} ArbitraryCallbackResult',
'* @typedef {Object} ArbitraryObject',
'* @typedef {Object} ArbitraryModule',
'* @typedef {Array} GenericArray',
'* @typedef {*} Any',
'* @param {...*} args Signature dependent on the function',
'* @returns {*} Return dependent on the function'
'* @param {...*} args Signature dependent on the function'
].includes(line);
});
break;

View File

@ -3,12 +3,15 @@
// https://github.com/helen-dikareva/axe-testcafe
import axeCheck from 'axe-testcafe';
/**
* @external AxeResult
*/
/**
* @external TestcafeTest
*/
/**
* @param {external.TestcafeTest} t
* @returns {Promise}
* @returns {Promise<external:AxeResult>}
*/
function axeCheckWithConfig (t) {
return axeCheck(