- Fix: Ensure PHP files are present in `dist/extensions` alongside JavaScript files using them

- Fix: Bug in obtaining `extPath` in ext-server_opensave.js
- Enhancement: Add config `avoidClientSide` to avoid using client-side support by default (and always require server)
- Build: Require Node 8.5
master
Brett Zamir 2019-04-18 21:13:48 +08:00
parent 1d56d75837
commit 13835a368c
18 changed files with 397 additions and 207 deletions

View File

@ -2,6 +2,11 @@
## ? ## ?
- Fix: Ensure PHP files are present in `dist/extensions` alongside
JavaScript files using them
- Fix: Bug in obtaining `extPath` in ext-server_opensave.js
- Enhancement: Add config `avoidClientSide` to avoid using
client-side support by default (and always require server)
- Enhancement: Return a Promise for Editor's `setCustomHandlers`, - Enhancement: Return a Promise for Editor's `setCustomHandlers`,
`loadFromString`, `loadFromDataURI` so known when ready and set `loadFromString`, `loadFromDataURI` so known when ready and set
- Refactoring: Destructuring, templates, label Unicode code point - Refactoring: Destructuring, templates, label Unicode code point
@ -11,6 +16,7 @@
- Docs (Refactoring): Formally specify `Promise` resolve type; - Docs (Refactoring): Formally specify `Promise` resolve type;
add `typedef` for dialog result object; add an add `typedef` for dialog result object; add an
`ArbitraryCallbackResult` type; prefer `void` `ArbitraryCallbackResult` type; prefer `void`
- Build: Require Node 8.5
- npm: Rename `build-doc` to `build-docs`; add `open-docs` script - npm: Rename `build-doc` to `build-docs`; add `open-docs` script
- npm: Update devDeps (and our @babel/polyfill copy) - npm: Update devDeps (and our @babel/polyfill copy)

12
dist/extensions/allowedMimeTypes.php vendored Normal file
View File

@ -0,0 +1,12 @@
<?php
$allowedMimeTypesBySuffix = array(
'svg' => 'image/svg+xml;charset=UTF-8',
'png' => 'image/png',
'jpeg' => 'image/jpeg',
'bmp' => 'image/bmp',
'webp' => 'image/webp',
'pdf' => 'application/pdf'
);
?>

View File

@ -4868,7 +4868,8 @@ var svgEditorExtension_server_opensave = (function () {
var _init = _asyncToGenerator( var _init = _asyncToGenerator(
/*#__PURE__*/ /*#__PURE__*/
regeneratorRuntime.mark(function _callee5(_ref) { regeneratorRuntime.mark(function _callee5(_ref) {
var $, decode64, encode64, importLocale, strings, svgEditor, extPath, svgCanvas, getFileNameFromTitle, xhtmlEscape, clientDownloadSupport, saveSvgAction, saveImgAction, cancelled, openSvgAction, importSvgAction, importImgAction, openSvgForm, importSvgForm, importImgForm, rebuildInput; var $, decode64, encode64, importLocale, strings, svgEditor, _svgEditor$curConfig, extPath, avoidClientSide, svgCanvas, getFileNameFromTitle, xhtmlEscape, clientDownloadSupport, saveSvgAction, saveImgAction, cancelled, openSvgAction, importSvgAction, importImgAction, openSvgForm, importSvgForm, importImgForm, rebuildInput;
return regeneratorRuntime.wrap(function _callee5$(_context5) { return regeneratorRuntime.wrap(function _callee5$(_context5) {
while (1) { while (1) {
switch (_context5.prev = _context5.next) { switch (_context5.prev = _context5.next) {
@ -4974,6 +4975,10 @@ var svgEditorExtension_server_opensave = (function () {
}; };
clientDownloadSupport = function _ref6(filename, suffix, uri) { clientDownloadSupport = function _ref6(filename, suffix, uri) {
if (avoidClientSide) {
return false;
}
var support = $('<a>')[0].download === ''; var support = $('<a>')[0].download === '';
var a; var a;
@ -5006,7 +5011,7 @@ var svgEditorExtension_server_opensave = (function () {
case 7: case 7:
strings = _context5.sent; strings = _context5.sent;
svgEditor = this; svgEditor = this;
extPath = svgEditor.curConfig, svgCanvas = svgEditor.canvas; _svgEditor$curConfig = svgEditor.curConfig, extPath = _svgEditor$curConfig.extPath, avoidClientSide = _svgEditor$curConfig.avoidClientSide, svgCanvas = svgEditor.canvas;
/** /**
* *
* @returns {string} * @returns {string}

58
dist/extensions/fileopen.php vendored Normal file
View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<?php
/*
* fileopen.php
* To be used with ext-server_opensave.js for SVG-edit
*
* Licensed under the MIT License
*
* Copyright(c) 2010 Alexis Deveria
*
*/
// Very minimal PHP file, all we do is Base64 encode the uploaded file and
// return it to the editor
if (!isset($_REQUEST['type'])) {
echo 'No type given';
exit;
}
$type = $_REQUEST['type'];
if (!in_array($type, array('load_svg', 'import_svg', 'import_img'))) {
echo 'Not a recognized type';
exit;
}
require('allowedMimeTypes.php');
$file = $_FILES['svg_file']['tmp_name'];
$output = file_get_contents($file);
$prefix = '';
// Make Data URL prefix for import image
if ($type == 'import_img') {
$info = getimagesize($file);
if (!in_array($info['mime'], $allowedMimeTypesBySuffix)) {
echo 'Disallowed MIME for supplied file';
exit;
}
$prefix = 'data:' . $info['mime'] . ';base64,';
}
?>
<html>
<head>
<meta charset="utf-8" />
<title>-</title>
<script>
top.svgEditor.processFile("<?php
// This should be safe since SVG edit does its own filtering (e.g., if an SVG file contains scripts)
echo $prefix . base64_encode($output);
?>", "<?php echo $type; ?>");
</script>
</head>
<body></body>
</html>

60
dist/extensions/filesave.php vendored Normal file
View File

@ -0,0 +1,60 @@
<?php
/*
* filesave.php
* To be used with ext-server_opensave.js for SVG-edit
*
* Licensed under the MIT License
*
* Copyright(c) 2010 Alexis Deveria
*
*/
function encodeRFC5987ValueChars ($str) {
// See https://tools.ietf.org/html/rfc5987#section-3.2.1
// For better readability within headers, add back the characters escaped by rawurlencode but still allowable
// Although RFC3986 reserves "!" (%21), RFC5987 does not
return preg_replace_callback('@%(2[1346B]|5E|60|7C)@', function ($matches) {
return chr('0x' . $matches[1]);
}, rawurlencode($str));
}
require('allowedMimeTypes.php');
$mime = (!isset($_POST['mime']) || !in_array($_POST['mime'], $allowedMimeTypesBySuffix)) ? 'image/svg+xml;charset=UTF-8' : $_POST['mime'];
if (!isset($_POST['output_svg']) && !isset($_POST['output_img'])) {
die('post fail');
}
$file = '';
$suffix = '.' . array_search($mime, $allowedMimeTypesBySuffix);
if (isset($_POST['filename']) && strlen($_POST['filename']) > 0) {
$file = $_POST['filename'] . $suffix;
} else {
$file = 'image' . $suffix;
}
if ($suffix == '.svg') {
$contents = $_POST['output_svg'];
} else {
$contents = $_POST['output_img'];
$pos = (strpos($contents, 'base64,') + 7);
$contents = base64_decode(substr($contents, $pos));
}
header('Cache-Control: public');
header('Content-Description: File Transfer');
// See https://tools.ietf.org/html/rfc6266#section-4.1
header("Content-Disposition: attachment; filename*=UTF-8''" . encodeRFC5987ValueChars(
// preg_replace('@[\\\\/:*?"<>|]@', '', $file) // If we wanted to strip Windows-disallowed characters server-side (but not a security issue, so we can strip client-side instead)
$file
));
header('Content-Type: ' . $mime);
header('Content-Transfer-Encoding: binary');
echo $contents;
?>

16
dist/extensions/savefile.php vendored Normal file
View File

@ -0,0 +1,16 @@
<?php
// You must first create a file "savefile_config.php" in this extensions directory and do whatever
// checking of user credentials, etc. that you wish; otherwise anyone will be able to post SVG
// files to your server which may cause disk space or possibly security problems
require('savefile_config.php');
if (!isset($_POST['output_svg'])) {
print 'You must supply output_svg';
exit;
}
$svg = $_POST['output_svg'];
$filename = (isset($_POST['filename']) && !empty($_POST['filename']) ? preg_replace('@[\\\\/:*?"<>|]@u', '_', $_POST['filename']) : 'saved') . '.svg'; // These characters are indicated as prohibited by Windows
$fh = fopen($filename, 'w') or die("Can't open file");
fwrite($fh, $svg);
fclose($fh);
?>

81
dist/index-es.js vendored
View File

@ -28787,6 +28787,7 @@ defaultExtensions = ['ext-connector.js', 'ext-eyedropper.js', 'ext-grid.js', 'ex
* @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage. * @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage.
* @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions) * @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions)
* @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store * @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store
* @property {boolean} [avoidClientSide=false] Used by `ext-server_opensave.js`; set to `true` if you wish to always save to server and not only as fallback when client support is lacking
* @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included. * @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included.
* @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded. * @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded.
* @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage * @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage
@ -28893,8 +28894,10 @@ defaultConfig = {
// Some interaction with ext-storage.js; prevent even the loading of previously saved local storage // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage
forceStorage: false, forceStorage: false,
// Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not
emptyStorageOnDecline: false // Used by ext-storage.js; empty any prior storage if the user declines to store emptyStorageOnDecline: false,
// Used by ext-storage.js; empty any prior storage if the user declines to store
// EXTENSION (CLIENT VS. SERVER SAVING)
avoidClientSide: false
}, },
/** /**
@ -29203,45 +29206,47 @@ editor.setConfig = function (opts, cfgCfg) {
} }
$$b.each(opts, function (key, val) { $$b.each(opts, function (key, val) {
if ({}.hasOwnProperty.call(opts, key)) { if (!{}.hasOwnProperty.call(opts, key)) {
// Only allow prefs defined in defaultPrefs return;
if ({}.hasOwnProperty.call(defaultPrefs, key)) { } // Only allow prefs defined in defaultPrefs
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curPrefs, key))) {
if ({}.hasOwnProperty.call(defaultPrefs, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curPrefs, key))) {
return;
}
if (cfgCfg.allowInitialUserOverride === true) {
defaultPrefs[key] = val;
} else {
$$b.pref(key, val);
}
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || ['allowedOrigins', 'stylesheets'].includes(key) || key === 'extensions' && curConfig.lockExtensions)) {
return;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curConfig, key))) {
return;
} // Potentially overwriting of previously set config
if ({}.hasOwnProperty.call(curConfig, key)) {
if (cfgCfg.overwrite === false) {
return; return;
} }
if (cfgCfg.allowInitialUserOverride === true) { extendOrAdd(curConfig, key, val);
defaultPrefs[key] = val; } else if (cfgCfg.allowInitialUserOverride === true) {
} else { extendOrAdd(defaultConfig, key, val);
$$b.pref(key, val); } else if (defaultConfig[key] && _typeof(defaultConfig[key]) === 'object') {
} curConfig[key] = Array.isArray(defaultConfig[key]) ? [] : {};
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) { $$b.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || ['allowedOrigins', 'stylesheets'].includes(key) || key === 'extensions' && curConfig.lockExtensions)) { } else {
return; curConfig[key] = val;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curConfig, key))) {
return;
} // Potentially overwriting of previously set config
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] = Array.isArray(defaultConfig[key]) ? [] : {};
$$b.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
} else {
curConfig[key] = val;
}
} }
} }
}); });

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

81
dist/index-umd.js vendored
View File

@ -28793,6 +28793,7 @@
* @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage. * @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage.
* @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions) * @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions)
* @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store * @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store
* @property {boolean} [avoidClientSide=false] Used by `ext-server_opensave.js`; set to `true` if you wish to always save to server and not only as fallback when client support is lacking
* @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included. * @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included.
* @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded. * @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded.
* @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage * @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage
@ -28899,8 +28900,10 @@
// Some interaction with ext-storage.js; prevent even the loading of previously saved local storage // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage
forceStorage: false, forceStorage: false,
// Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not
emptyStorageOnDecline: false // Used by ext-storage.js; empty any prior storage if the user declines to store emptyStorageOnDecline: false,
// Used by ext-storage.js; empty any prior storage if the user declines to store
// EXTENSION (CLIENT VS. SERVER SAVING)
avoidClientSide: false
}, },
/** /**
@ -29209,45 +29212,47 @@
} }
$$b.each(opts, function (key, val) { $$b.each(opts, function (key, val) {
if ({}.hasOwnProperty.call(opts, key)) { if (!{}.hasOwnProperty.call(opts, key)) {
// Only allow prefs defined in defaultPrefs return;
if ({}.hasOwnProperty.call(defaultPrefs, key)) { } // Only allow prefs defined in defaultPrefs
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curPrefs, key))) {
if ({}.hasOwnProperty.call(defaultPrefs, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curPrefs, key))) {
return;
}
if (cfgCfg.allowInitialUserOverride === true) {
defaultPrefs[key] = val;
} else {
$$b.pref(key, val);
}
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || ['allowedOrigins', 'stylesheets'].includes(key) || key === 'extensions' && curConfig.lockExtensions)) {
return;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curConfig, key))) {
return;
} // Potentially overwriting of previously set config
if ({}.hasOwnProperty.call(curConfig, key)) {
if (cfgCfg.overwrite === false) {
return; return;
} }
if (cfgCfg.allowInitialUserOverride === true) { extendOrAdd(curConfig, key, val);
defaultPrefs[key] = val; } else if (cfgCfg.allowInitialUserOverride === true) {
} else { extendOrAdd(defaultConfig, key, val);
$$b.pref(key, val); } else if (defaultConfig[key] && _typeof(defaultConfig[key]) === 'object') {
} curConfig[key] = Array.isArray(defaultConfig[key]) ? [] : {};
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) { $$b.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || ['allowedOrigins', 'stylesheets'].includes(key) || key === 'extensions' && curConfig.lockExtensions)) { } else {
return; curConfig[key] = val;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curConfig, key))) {
return;
} // Potentially overwriting of previously set config
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] = Array.isArray(defaultConfig[key]) ? [] : {};
$$b.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
} else {
curConfig[key] = val;
}
} }
} }
}); });

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -14,7 +14,7 @@ export default {
const strings = await importLocale(); const strings = await importLocale();
const svgEditor = this; const svgEditor = this;
const { const {
curConfig: extPath, curConfig: {extPath, avoidClientSide},
canvas: svgCanvas canvas: svgCanvas
} = svgEditor; } = svgEditor;
@ -44,6 +44,9 @@ export default {
* @returns {boolean} * @returns {boolean}
*/ */
function clientDownloadSupport (filename, suffix, uri) { function clientDownloadSupport (filename, suffix, uri) {
if (avoidClientSide) {
return false;
}
const support = $('<a>')[0].download === ''; const support = $('<a>')[0].download === '';
let a; let a;
if (support) { if (support) {

View File

@ -181,6 +181,7 @@ const callbacks = [],
* @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage. * @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage.
* @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions) * @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions)
* @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store * @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store
* @property {boolean} [avoidClientSide=false] Used by `ext-server_opensave.js`; set to `true` if you wish to always save to server and not only as fallback when client support is lacking
* @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included. * @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included.
* @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded. * @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded.
* @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage * @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage
@ -273,7 +274,9 @@ const callbacks = [],
// EXTENSION-RELATED (STORAGE) // EXTENSION-RELATED (STORAGE)
noStorageOnLoad: false, // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage noStorageOnLoad: false, // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage
forceStorage: false, // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not forceStorage: false, // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not
emptyStorageOnDecline: false // Used by ext-storage.js; empty any prior storage if the user declines to store emptyStorageOnDecline: false, // Used by ext-storage.js; empty any prior storage if the user declines to store
// EXTENSION (CLIENT VS. SERVER SAVING)
avoidClientSide: false
}, },
/** /**
* LOCALE * LOCALE
@ -499,53 +502,54 @@ editor.setConfig = function (opts, cfgCfg) {
} }
} }
$.each(opts, function (key, val) { $.each(opts, function (key, val) {
if ({}.hasOwnProperty.call(opts, key)) { if (!{}.hasOwnProperty.call(opts, key)) {
// Only allow prefs defined in defaultPrefs return;
if ({}.hasOwnProperty.call(defaultPrefs, key)) { }
if (cfgCfg.overwrite === false && ( // Only allow prefs defined in defaultPrefs
if ({}.hasOwnProperty.call(defaultPrefs, key)) {
if (cfgCfg.overwrite === false && (
curConfig.preventAllURLConfig ||
{}.hasOwnProperty.call(curPrefs, key)
)) {
return;
}
if (cfgCfg.allowInitialUserOverride === true) {
defaultPrefs[key] = val;
} else {
$.pref(key, val);
}
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) {
if (cfgCfg.overwrite === false &&
(
curConfig.preventAllURLConfig || curConfig.preventAllURLConfig ||
{}.hasOwnProperty.call(curPrefs, key) ['allowedOrigins', 'stylesheets'].includes(key) ||
)) { (key === 'extensions' && curConfig.lockExtensions)
)
) {
return;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (
curConfig.preventAllURLConfig ||
{}.hasOwnProperty.call(curConfig, key)
)) {
return;
}
// Potentially overwriting of previously set config
if ({}.hasOwnProperty.call(curConfig, key)) {
if (cfgCfg.overwrite === false) {
return; return;
} }
if (cfgCfg.allowInitialUserOverride === true) { extendOrAdd(curConfig, key, val);
defaultPrefs[key] = val; } else if (cfgCfg.allowInitialUserOverride === true) {
} else { extendOrAdd(defaultConfig, key, val);
$.pref(key, val); } else if (defaultConfig[key] && typeof defaultConfig[key] === 'object') {
} curConfig[key] = Array.isArray(defaultConfig[key]) ? [] : {};
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) { $.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
if (cfgCfg.overwrite === false && } else {
( curConfig[key] = val;
curConfig.preventAllURLConfig ||
['allowedOrigins', 'stylesheets'].includes(key) ||
(key === 'extensions' && curConfig.lockExtensions)
)
) {
return;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (
curConfig.preventAllURLConfig ||
{}.hasOwnProperty.call(curConfig, key)
)) {
return;
}
// Potentially overwriting of previously set config
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] = Array.isArray(defaultConfig[key]) ? [] : {};
$.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
} else {
curConfig[key] = val;
}
} }
} }
}); });
@ -742,7 +746,6 @@ editor.init = function () {
}); });
editor.setConfig(urldata, {overwrite: false}); // Note: source and url (as with storagePrompt later) are not set on config but are used below editor.setConfig(urldata, {overwrite: false}); // Note: source and url (as with storagePrompt later) are not set on config but are used below
setupCurConfig(); setupCurConfig();
if (!curConfig.preventURLContentLoading) { if (!curConfig.preventURLContentLoading) {

View File

@ -28790,6 +28790,7 @@
* @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage. * @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage.
* @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions) * @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions)
* @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store * @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store
* @property {boolean} [avoidClientSide=false] Used by `ext-server_opensave.js`; set to `true` if you wish to always save to server and not only as fallback when client support is lacking
* @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included. * @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included.
* @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded. * @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded.
* @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage * @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage
@ -28896,8 +28897,10 @@
// Some interaction with ext-storage.js; prevent even the loading of previously saved local storage // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage
forceStorage: false, forceStorage: false,
// Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not
emptyStorageOnDecline: false // Used by ext-storage.js; empty any prior storage if the user declines to store emptyStorageOnDecline: false,
// Used by ext-storage.js; empty any prior storage if the user declines to store
// EXTENSION (CLIENT VS. SERVER SAVING)
avoidClientSide: false
}, },
/** /**
@ -29206,45 +29209,47 @@
} }
$$b.each(opts, function (key, val) { $$b.each(opts, function (key, val) {
if ({}.hasOwnProperty.call(opts, key)) { if (!{}.hasOwnProperty.call(opts, key)) {
// Only allow prefs defined in defaultPrefs return;
if ({}.hasOwnProperty.call(defaultPrefs, key)) { } // Only allow prefs defined in defaultPrefs
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curPrefs, key))) {
if ({}.hasOwnProperty.call(defaultPrefs, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curPrefs, key))) {
return;
}
if (cfgCfg.allowInitialUserOverride === true) {
defaultPrefs[key] = val;
} else {
$$b.pref(key, val);
}
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || ['allowedOrigins', 'stylesheets'].includes(key) || key === 'extensions' && curConfig.lockExtensions)) {
return;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curConfig, key))) {
return;
} // Potentially overwriting of previously set config
if ({}.hasOwnProperty.call(curConfig, key)) {
if (cfgCfg.overwrite === false) {
return; return;
} }
if (cfgCfg.allowInitialUserOverride === true) { extendOrAdd(curConfig, key, val);
defaultPrefs[key] = val; } else if (cfgCfg.allowInitialUserOverride === true) {
} else { extendOrAdd(defaultConfig, key, val);
$$b.pref(key, val); } else if (defaultConfig[key] && _typeof(defaultConfig[key]) === 'object') {
} curConfig[key] = Array.isArray(defaultConfig[key]) ? [] : {};
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) { $$b.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || ['allowedOrigins', 'stylesheets'].includes(key) || key === 'extensions' && curConfig.lockExtensions)) { } else {
return; curConfig[key] = val;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curConfig, key))) {
return;
} // Potentially overwriting of previously set config
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] = Array.isArray(defaultConfig[key]) ? [] : {};
$$b.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
} else {
curConfig[key] = val;
}
} }
} }
}); });

View File

@ -10,7 +10,7 @@
"test": "test" "test": "test"
}, },
"engines": { "engines": {
"node": ">= 7.6.0" "node": ">= 8.5.0"
}, },
"scripts": { "scripts": {
"prepublishOnly": "npm run test-prep", "prepublishOnly": "npm run test-prep",

View File

@ -8,7 +8,7 @@ import babel from 'rollup-plugin-babel';
import {terser} from 'rollup-plugin-terser'; import {terser} from 'rollup-plugin-terser';
import replace from 'rollup-plugin-re'; import replace from 'rollup-plugin-re';
const {lstatSync, readdirSync} = require('fs'); // eslint-disable-line import/no-commonjs const {lstatSync, readdirSync, copyFileSync} = require('fs'); // eslint-disable-line import/no-commonjs
const {join, basename} = require('path'); // eslint-disable-line import/no-commonjs const {join, basename} = require('path'); // eslint-disable-line import/no-commonjs
const localeFiles = readdirSync('editor/locale'); const localeFiles = readdirSync('editor/locale');
@ -180,6 +180,13 @@ export default [
}; };
}), }),
...extensionFiles.map((extensionFile) => { ...extensionFiles.map((extensionFile) => {
if (extensionFile.match(/\.php$/)) {
copyFileSync(
join('editor/extensions', extensionFile),
join('dist/extensions', extensionFile)
);
return undefined;
}
// ext-*.js // ext-*.js
const extensionName = extensionFile.match(/^ext-(.+?)\.js$/); const extensionName = extensionFile.match(/^ext-(.+?)\.js$/);
if (!extensionName) { if (!extensionName) {

View File

@ -28790,6 +28790,7 @@
* @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage. * @property {boolean} [noStorageOnLoad=false] Some interaction with `ext-storage.js`; prevent even the loading of previously saved local storage.
* @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions) * @property {boolean} [forceStorage=false] Some interaction with `ext-storage.js`; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not (and may be required by law in some regions)
* @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store * @property {boolean} [emptyStorageOnDecline=false] Used by `ext-storage.js`; empty any prior storage if the user declines to store
* @property {boolean} [avoidClientSide=false] Used by `ext-server_opensave.js`; set to `true` if you wish to always save to server and not only as fallback when client support is lacking
* @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included. * @property {string[]} [extensions=module:SVGEditor~defaultExtensions] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included.
* @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded. * @property {module:SVGEditor.Stylesheet[]} [stylesheets=["@default"]] An array of required stylesheets to load in parallel; include the value `"@default"` within this array to ensure all default stylesheets are loaded.
* @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage * @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage
@ -28896,8 +28897,10 @@
// Some interaction with ext-storage.js; prevent even the loading of previously saved local storage // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage
forceStorage: false, forceStorage: false,
// Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not
emptyStorageOnDecline: false // Used by ext-storage.js; empty any prior storage if the user declines to store emptyStorageOnDecline: false,
// Used by ext-storage.js; empty any prior storage if the user declines to store
// EXTENSION (CLIENT VS. SERVER SAVING)
avoidClientSide: false
}, },
/** /**
@ -29206,45 +29209,47 @@
} }
$$b.each(opts, function (key, val) { $$b.each(opts, function (key, val) {
if ({}.hasOwnProperty.call(opts, key)) { if (!{}.hasOwnProperty.call(opts, key)) {
// Only allow prefs defined in defaultPrefs return;
if ({}.hasOwnProperty.call(defaultPrefs, key)) { } // Only allow prefs defined in defaultPrefs
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curPrefs, key))) {
if ({}.hasOwnProperty.call(defaultPrefs, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curPrefs, key))) {
return;
}
if (cfgCfg.allowInitialUserOverride === true) {
defaultPrefs[key] = val;
} else {
$$b.pref(key, val);
}
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || ['allowedOrigins', 'stylesheets'].includes(key) || key === 'extensions' && curConfig.lockExtensions)) {
return;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curConfig, key))) {
return;
} // Potentially overwriting of previously set config
if ({}.hasOwnProperty.call(curConfig, key)) {
if (cfgCfg.overwrite === false) {
return; return;
} }
if (cfgCfg.allowInitialUserOverride === true) { extendOrAdd(curConfig, key, val);
defaultPrefs[key] = val; } else if (cfgCfg.allowInitialUserOverride === true) {
} else { extendOrAdd(defaultConfig, key, val);
$$b.pref(key, val); } else if (defaultConfig[key] && _typeof(defaultConfig[key]) === 'object') {
} curConfig[key] = Array.isArray(defaultConfig[key]) ? [] : {};
} else if (['extensions', 'stylesheets', 'allowedOrigins'].includes(key)) { $$b.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || ['allowedOrigins', 'stylesheets'].includes(key) || key === 'extensions' && curConfig.lockExtensions)) { } else {
return; curConfig[key] = val;
}
curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later
// Only allow other curConfig if defined in defaultConfig
} else if ({}.hasOwnProperty.call(defaultConfig, key)) {
if (cfgCfg.overwrite === false && (curConfig.preventAllURLConfig || {}.hasOwnProperty.call(curConfig, key))) {
return;
} // Potentially overwriting of previously set config
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] = Array.isArray(defaultConfig[key]) ? [] : {};
$$b.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects
} else {
curConfig[key] = val;
}
} }
} }
}); });