Notebooks 2.0 is here.

Unlisted
Edited
Jun 29
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function toConfig(plugins) {
return Object.entries(plugins).map(([name, params = {}]) => ({name, params}));
}
Insert cell
// Use a helper to make the configuration less verbose.
plugins = toConfig({
// Add all default plugins, override some options.
"preset-default": {
overrides: {
// Disable some default plugins.
removeViewBox: false,
minifyStyles: false,
}
},

// Enable builtin plugins that are disabled by default
convertStyleToAttrs: {},
removeDimensions: {},
// Do not use a dynamic prefix here!
prefixIds: {}

})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof svg_original = textView(await (await toFile(source.value)).text())
Insert cell
viewof svg_processed = textView(await awaitDone(runWorker(svg_original)))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
pipe(
fetch("https://raw.githubusercontent.com/svg/svgo/v3.0.0/README.md"),
res => res.text(),
str => {
const from = "## Built-in plugins\n";
const to = "## Other Ways to Use SVGO\n";
const table = md`${str.slice(str.indexOf(from) + from.length, str.indexOf(to))}`;
return table;
},
)
Insert cell
function pluginSchema({description, default: enabled, options = {}}) {
return {
description,
type: "object",
properties: {
enabled: {type: "boolean", default: enabled},
...options && {
options: {
type: "object",
properties: options
},
}
},
};
}
Insert cell
import {inputTemplate} from "@mootari/input-template"
Insert cell
class FormBuilder {
_labelFromDescription;

constructor({labelFromDescription = false} = {}) {
this._labelFromDescription = labelFromDescription;
}

_mapSchema(schema) {
const {title, description} = schema;
}

assertProps(schema, required, optional = []) {
try {
const allowed = new Set([...required, ...optional]);
for(const k of required) if(schema[k] == null) throw new Error(`Missing required property "${k}"`);
for(const k of Object.keys(schema)) {
if(!allowed.has(k)) throw new Error(`Unexpected property "${k}"`);
}
}
catch(error) {
console.warn(error);
}
return schema;
}

getLabel(schema) {
return (this._labelFromDescription ? schema.description : "") || schema.title;
}

getDescription(schema) {
return this._labelFromDescription && schema.description ? schema.title : undefined;
}

render(schema) {
if(schema.enum) return this.renderEnum(schema);
switch(schema.type) {
case "object": return this.renderObject(schema);
case "number": return this.renderNumber(schema);
case "integer": return this.renderInteger(schema);
case "boolean": return this.renderBoolean(schema);
case "string": return this.renderString(schema);
//case "array": return this.renderArray(schema);
}

return this.renderUnknown(schema)
}

renderObject(schema) {
const s = this.assertProps(schema, ["type", "properties"], ["title", "description"]);
const label = this.getLabel(s);
const desc = this.getDescription(s);
const toForm = ([key, schema]) => [key, this.render({title: key, ...schema}, key)];
const entries = Object.entries(s.properties).map(toForm);
const form = Inputs.form(Object.fromEntries(entries));
return label ? this._withDescription(inputTemplate(form, {label}), desc) : form;
}

// renderArray(schema) {
// const s = this.assertProps(schema, ["type", "items"], ["title", "description", "default"]);
// }

renderEnum(schema) {
const s = this.assertProps(schema, ["enum"], ["default", "title", "description"]);
const form = Inputs.radio(s.enum, {label: this.getLabel(s), value: s.default});
return this._withDescription(form, this.getDescription(schema));
}

renderNumber(schema) {
const s = this.assertProps(schema, ["type"], ["default", "title", "description", "minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"]);

const form = Inputs.number([
s.minimum ?? (s.exclusiveMinimum ? s.exclusiveMinimum + Number.EPSILON : -Infinity),
s.maximum ?? (s.exclusiveMaximum ? s.exclusiveMaximum + Number.EPSILON : Infinity)
], {step: s.multipleOf, label: this.getLabel(s), value: s.default});
return this._withDescription(form, this.getDescription(s));
}

renderInteger(s) {
return this.renderNumber({...s, multipleOf: 1});
}

renderBoolean(schema) {
const s = this.assertProps(schema, ["type"], ["default", "title", "description"]);
const form = Inputs.toggle({label: this.getLabel(s), value: s.default});
return this._withDescription(form, this.getDescription(s));
}

renderString(schema) {
const s = this.assertProps(schema, ["type"], ["default", "title", "description", "pattern"]);
const form = Inputs.text({label: this.getLabel(s), value: s.default, pattern: s.pattern});
return this._withDescription(form, this.getDescription(s));
}

renderUnknown(schema) {
return htl.html`<div style="font:13px var(--sans-serif);color:red">Unsupported type: ${schema.type}`;
}

_withDescription(form, description) {
if(!description) return form;
const el = form.querySelector("& > label");
if(el) el.title = description;
return form;
}
}
Insert cell
Insert cell
Insert cell
optionsSchemas = ({
cleanupAttrs: {
description: "cleanup attributes from newlines, trailing, and repeating spaces",
default: true,
params: {
newlines: { type: "boolean", default: true },
trim: { type: "boolean", default: true },
spaces: { type: "boolean", default: true }
},
},

mergeStyles: {
description: "merge multiple style elements into one",
default: true,
},

inlineStyles: {
description: "move and merge styles from <style> elements to element style attributes",
default: true,
params: {
onlyMatchedOnce: {
type: "boolean",
default: true,
description: "inline only selectors that match once"
},
removeMatchedSelectors: {
type: "boolean",
default: true,
description: "clean up matched selectors, leave selectors that hadn't matched"
},
useMqs: {
type: "array",
items: { type: "string" },
default: ["", "screen"],
description: "what media queries to be used; empty string element for styles outside media queries"
},
usePseudos: {
type: "array",
items: { type: "string" },
default: [""],
description: "what pseudo-classes/-elements to be used; empty string element for all non-pseudo-classes and/or -elements"
}
}
},

removeDoctype: {
description: "remove doctype declaration",
default: true
},

removeXMLProcInst: {
description: "remove XML processing instructions",
default: true
},

removeComments: {
description: "remove comments",
default: true
},

removeMetadata: {
description: "removes <metadata>",
default: true
},

removeTitle: {
description: "removes <title>",
default: true
},

removeDesc: {
description: "removes <desc>",
default: true,
params: {
removeAny: {
type: "boolean",
default: true,
description: "disable to remove only standard editors content or empty elements 'cause it can be used for accessibility; enable to remove any description."
}
}
},

removeUselessDefs: {
description: "removes elements in <defs> without id",
default: true
},

removeXMLNS: {
description: "removes the xmlns attribute (for inline SVG)",
default: false
},

removeEditorsNSData: {
description: "remove editors namespaces, elements, and attributes",
default: true,
params: {
additionalNamespaces: {
type: "array",
items: {type: "string"},
default: undefined,
}
}
},

removeEmptyAttrs: {
description: "remove empty attributes",
default: true
},

removeHiddenElems: {
description: "remove hidden elements",
default: true,
params: {
isHidden: {
type: "boolean",
default: true,
description: "hidden visibility"
},
displayNone: {
type: "boolean",
default: true,
description: "display=\"none\""
},
opacity0: { type: "boolean", default: true, description: "opacity=\"0\"" },
circleR0: {
type: "boolean",
default: true,
description: "circle with zero radius"
},
ellipseRX0: {
type: "boolean",
default: true,
description: "ellipse with zero x-axis radius"
},
ellipseRY0: {
type: "boolean",
default: true,
description: "ellipse with zero y-axis radius"
},
rectWidth0: {
type: "boolean",
default: true,
description: "rectangle with zero width"
},
rectHeight0: {
type: "boolean",
default: true,
description: "rectangle with zero height"
},
patternWidth0: {
type: "boolean",
default: true,
description: "pattern with zero width"
},
patternHeight0: {
type: "boolean",
default: true,
description: "pattern with zero height"
},
imageWidth0: {
type: "boolean",
default: true,
description: "image with zero width"
},
imageHeight0: {
type: "boolean",
default: true,
description: "image with zero height"
},
pathEmptyD: {
type: "boolean",
default: true,
description: "path with empty data"
},
polylineEmptyPoints: {
type: "boolean",
default: true,
description: "polyline with empty points"
},
polygonEmptyPoints: {
type: "boolean",
default: true,
description: "polygon with empty points"
}
}
},

removeEmptyText: {
description: "remove empty Text elements",
default: true,
params: {
text: {
type: "boolean",
default: true,
description: "remove empty text element"
},
tspan: {
type: "boolean",
default: true,
description: "remove empty tspan element"
},
tref: {
type: "boolean",
default: true,
description: "remove tref with empty xlink:href attribute"
}
}
},

removeEmptyContainers: {
description: "remove empty Container elements",
default: true
},

removeViewBox: {
description: "remove viewBox attribute when possible",
default: true
},

cleanupEnableBackground: {
description: "remove or cleanup enable-background attribute when possible",
default: true
},

minifyStyles: {
description: "minify <style> elements content with CSSO",
default: true,
type: "object",
properties: {
usage: {
type: "group",
type: "object",
properties: {
force: {
type: "boolean",
default: false,
description: "force to use usage data even if it unsafe (document contains <script> or on* attributes)"
},
ids: { type: "boolean", default: true },
classes: { type: "boolean", default: true },
tags: { type: "boolean", default: true }
}
}
}
},

convertStyleToAttrs: {
description: "convert styles into attributes",
default: false,
params: {
keepImportant: { type: "boolean", default: false }
}
},

convertColors: {
description: "convert colors (from rgb() to #rrggbb, from #rrggbb to #rgb)",
default: true,
params: {
currentColor: {
type: "boolean",
default: false,
description: "convert colors to currentColor"
},
names2hex: {
type: "boolean",
default: true,
description: "convert color name keyword to long hex"
},
rgb2hex: {
type: "boolean",
default: true,
description: "convert rgb() to long hex"
},
shorthex: {
type: "boolean",
default: true,
description: "convert long hex to short hex"
},
shortname: {
type: "boolean",
default: true,
description: "convert hex to short name"
}
}
},

// todo
convertPathData: {
description: "convert Path data to relative or absolute (whichever is shorter), convert one segment to another, trim useless delimiters, smart rounding, and much more",
default: true,
params: {
applyTransforms: { type: "boolean", default: true },
applyTransformsStroked: {
type: "boolean",
default: true,
},
// todo
makeArcs: {
type: "object",
properties: {
threshold: {
type: "number",
default: 2.5,
description: "coefficient of rounding error"
},
tolerance: {
type: "number",
default: 0.5,
description: "percentage of radius"
}
},
},
straightCurves: {
type: "boolean",
default: true,
description: "convert straight curves into lines segments"
},
lineShorthands: {
type: "boolean",
default: true,
description: "horizontal and vertical line shorthands"
},
curveSmoothShorthands: {
type: "boolean",
default: true,
description: "convert curves into smooth shorthands"
},
/*!*/ floatPrecision: { type: "integer", minimum: 0, default: 3, description: "" },
transformPrecision: { type: "integer", minimum: 0, default: 5, description: "" },
removeUseless: {
type: "boolean",
default: true,
description: "remove useless non-first path segments"
},
collapseRepeated: {
type: "boolean",
default: true,
description: "collapse repeated commands"
},
utilizeAbsolute: { type: "boolean", default: true, description: "" },
/*!*/ leadingZero: { type: "boolean", default: true, description: "" },
/*!*/ negativeExtraSpace: {
type: "boolean",
default: true,
description: ""
},
/*!*/ noSpaceAfterFlags: {
type: "boolean",
default: false,
description: ""
},
forceAbsolutePath: { type: "boolean", default: false, description: "" }
}
},

// todo
convertTransform: {
description: "collapse multiple transforms into one, convert matrices to the short aliases, and much more",
default: true,
params: {
convertToShorts: { type: "boolean", default: true, description: "" },
degPrecision: { type: "integer", minimum: 0, default: 2, description: "" },
floatPrecision: { type: "integer", minimum: 0, default: 3, description: "" },
transformPrecision: { type: "integer", minimum: 0, default: 5, description: "" },
matrixToTransform: { type: "boolean", default: true, description: "" },
shortTranslate: { type: "boolean", default: true, description: "" },
shortScale: { type: "boolean", default: true, description: "" },
shortRotate: { type: "boolean", default: true, description: "" },
removeUseless: { type: "boolean", default: true, description: "" },
collapseIntoOne: { type: "boolean", default: true, description: "" },
leadingZero: { type: "boolean", default: true, description: "" },
negativeExtraSpace: { type: "boolean", default: false, description: "" }
}
},

// todo
removeUnknownsAndDefaults: {
description: "remove unknown elements content and attributes, remove attributes with default values",
default: true,
params: {
unknownContent: { type: "boolean", default: true, description: "" },
unknownAttrs: { type: "boolean", default: true, description: "" },
defaultAttrs: { type: "boolean", default: true, description: "" },
uselessOverrides: { type: "boolean", default: true, description: "" },
keepDataAttrs: { type: "boolean", default: true, description: "" },
keepAriaAttrs: { type: "boolean", default: true, description: "" },
keepRoleAttr: { type: "boolean", default: false, description: "" }
}
},

removeNonInheritableGroupAttrs: {
description: "remove non-inheritable group's \"presentation\" attributes",
default: true
},

removeUselessStrokeAndFill: {
description: "remove useless stroke and fill attributes",
default: true,
params: {
stroke: { type: "boolean", default: true, description: "remove stroke*" },
fill: { type: "boolean", default: true, description: "remove fill*" },
removeNone: { type: "boolean", default: false, description: "" }
}
},

removeUnusedNS: {
description: "remove unused namespaces declaration",
default: true
},

// todo
prefixIds: {
description: "prefix IDs and classes with the SVG filename or an arbitrary string",
default: false,
params: {
prefix: { type: "string", default: "prefix", description: "" },
delim: { type: "string", default: "__", description: "" },
prefixIds: { type: "boolean", default: true, description: "" },
prefixClassNames: { type: "boolean", default: true, description: "" }
}
},

cleanupIds: {
description: "remove unused and minify used IDs",
default: true,
params: {
remove: { type: "boolean", default: true, description: "" },
minify: { type: "boolean", default: true, description: "" },
preserve: { type: "array", items: {type: "string"}, default: [], description: "" },
preservePrefixes: { type: "array", items: {type: "string"}, default: [], description: "" },
force: { type: "boolean", default: false, description: "" }
}
},

cleanupNumericValues: {
description: "round numeric values to the fixed precision, remove default px units",
default: true,
type: "object",
properties: {
/*!*/ floatPrecision: { type: "integer", minimum: 0, default: 3, description: "" },
leadingZero: {
type: "boolean",
default: true,
description: "remove leading zero"
},
defaultPx: {
type: "boolean",
default: true,
description: "remove default \"px\" units"
},
convertToPx: {
type: "boolean",
default: true,
description: "convert absolute values to pixels"
}
}
},

cleanupListOfValues: {
description: "round numeric values in attributes that take a list of numbers (like viewBox or enable-background)",
default: false,
params: {
/*!*/ floatPrecision: { type: "integer", minimum: 0, default: 3, description: "" },
leadingZero: {
type: "boolean",
default: true,
description: "remove leading zero"
},
defaultPx: {
type: "boolean",
default: true,
description: "remove default \"px\" units"
},
convertToPx: {
type: "boolean",
default: true,
description: "convert absolute values to pixels"
}
}
},

moveElemsAttrsToGroup: {
description: "move elements' attributes to their enclosing group",
default: true
},

moveGroupAttrsToElems: {
description: "move some group attributes to the contained elements",
default: true
},

collapseGroups: {
description: "collapse useless groups",
default: true
},

removeRasterImages: {
description: "remove raster images",
default: false
},

// todo
mergePaths: {
description: "merge multiple Paths into one",
default: true,
type: "object",
properties: {
force: { type: "boolean", default: false, description: "" },
/*!*/ floatPrecision: { type: "number", default: 3, description: "" },
/*!*/ noSpaceAfterFlags: {
type: "boolean",
default: false,
description: ""
}
}
},

// todo
convertShapeToPath: {
description: "convert some basic shapes to <path>",
default: true,
params: {
convertArcs: {
type: "boolean",
default: false,
description: "convert circle and ellipse"
},
/*!*/ floatPrecision: { type: "integer", minimum: 0, default: 3, description: "" }
}
},

convertEllipseToCircle: {
description: "convert non-eccentric <ellipse> to <circle>",
default: true
},

sortAttrs: {
description: "sort element attributes for epic readability",
default: true,
params: {
order: {
type: "array",
items: {type: "string"},
default: [
"id",
"width",
"height",
"x",
"x1",
"x2",
"y",
"y1",
"y2",
"cx",
"cy",
"r",
"fill",
"stroke",
"marker",
"d",
"points"
],
description: ""
},
xmlnsOrder: {
enum: ["front", "alphabetical"],
default: "front",
description: ""
}
}
},

sortDefsChildren: {
description: "sort children of <defs> in order to improve compression",
default: true
},

removeDimensions: {
description: "remove width/height and add viewBox if it's missing (opposite to removeViewBox, disable it first)",
default: false
},

// todo
removeAttrs: {
description: "remove attributes by pattern",
default: false,
params: {
elemSeparator: { type: "string", default: ":", description: "" },
preserveCurrentColor: {
type: "boolean",
default: false,
description: ""
},
/* TODO */ attrs: {type: "TODO"}
}
},

// todo
removeAttributesBySelector: {
description: "removes attributes of elements that match a CSS selector",
default: false,
params: {
/* TODO */ selectors: {type: "TODO"}
}
},

})
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more