diff --git a/ipywidgets/widgets/widget_bool.py b/ipywidgets/widgets/widget_bool.py index e6ecede30b..189890bfd2 100644 --- a/ipywidgets/widgets/widget_bool.py +++ b/ipywidgets/widgets/widget_bool.py @@ -53,9 +53,13 @@ class ToggleButton(_Bool): value : {True,False} value of the toggle button: True-pressed, False-unpressed description : str - description displayed next to the button + description displayed on the button icon: str font-awesome icon name + style: instance of DescriptionStyle + styling customizations + button_style: enum + button predefined styling """ _view_name = Unicode('ToggleButtonView').tag(sync=True) _model_name = Unicode('ToggleButtonModel').tag(sync=True) diff --git a/ipywidgets/widgets/widget_button.py b/ipywidgets/widgets/widget_button.py index e2fb3387ac..3316e8a7a4 100644 --- a/ipywidgets/widgets/widget_button.py +++ b/ipywidgets/widgets/widget_button.py @@ -35,7 +35,7 @@ class Button(DOMWidget, CoreWidget): Parameters ---------- description: str - description displayed next to the button + description displayed on the button icon: str font-awesome icon names, without the 'fa-' prefix disabled: bool diff --git a/ipywidgets/widgets/widget_description.py b/ipywidgets/widgets/widget_description.py index 46e8fd70b2..b36641f4fa 100644 --- a/ipywidgets/widgets/widget_description.py +++ b/ipywidgets/widgets/widget_description.py @@ -3,7 +3,7 @@ """Contains the DOMWidget class""" -from traitlets import Unicode +from traitlets import Bool, Unicode from .widget import Widget, widget_serialization, register from .trait_types import InstanceDict from .widget_style import Style @@ -21,6 +21,7 @@ class DescriptionWidget(DOMWidget, CoreWidget): """Widget that has a description label to the side.""" _model_name = Unicode('DescriptionModel').tag(sync=True) description = Unicode('', help="Description of the control.").tag(sync=True) + description_allow_html = Bool(False, help="Accept HTML in the description.").tag(sync=True) style = InstanceDict(DescriptionStyle, help="Styling customizations").tag(sync=True, **widget_serialization) def _repr_keys(self): diff --git a/packages/base-manager/package.json b/packages/base-manager/package.json index ea07c3ecfe..404f0861f0 100644 --- a/packages/base-manager/package.json +++ b/packages/base-manager/package.json @@ -35,7 +35,8 @@ "@jupyter-widgets/base": "^5.0.0-alpha.3", "@jupyterlab/services": "^6.0.0", "@lumino/coreutils": "^1.4.2", - "base64-js": "^1.2.1" + "base64-js": "^1.2.1", + "sanitize-html": "^1.20" }, "devDependencies": { "@types/base64-js": "^1.2.5", @@ -43,6 +44,7 @@ "@types/chai-as-promised": "^7.1.0", "@types/expect.js": "^0.3.29", "@types/mocha": "^8.2.2", + "@types/sanitize-html": "^1.20", "@types/sinon": "^10.0.2", "@types/sinon-chai": "^3.2.2", "chai": "^4.0.0", diff --git a/packages/base-manager/src/latex.ts b/packages/base-manager/src/latex.ts new file mode 100644 index 0000000000..f9beff90f4 --- /dev/null +++ b/packages/base-manager/src/latex.ts @@ -0,0 +1,193 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ +// Some magic for deferring mathematical expressions to MathJax +// by hiding them from the Markdown parser. +// Some of the code here is adapted with permission from Davide Cervone +// under the terms of the Apache2 license governing the MathJax project. +// Other minor modifications are also due to StackExchange and are used with +// permission. + +const inline = '$'; // the inline math delimiter + +// MATHSPLIT contains the pattern for math delimiters and special symbols +// needed for searching for math in the text input. +const MATHSPLIT = + /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[{}$]|[{}]|(?:\n\s*)+|@@\d+@@|\\\\(?:\(|\)|\[|\]))/i; + +/** + * Break up the text into its component parts and search + * through them for math delimiters, braces, linebreaks, etc. + * Math delimiters must match and braces must balance. + * Don't allow math to pass through a double linebreak + * (which will be a paragraph). + */ +export function removeMath(text: string): { text: string; math: string[] } { + const math: string[] = []; // stores math strings for later + let start: number | null = null; + let end: string | null = null; + let last: number | null = null; + let braces = 0; + let deTilde: (text: string) => string; + + // Except for extreme edge cases, this should catch precisely those pieces of the markdown + // source that will later be turned into code spans. While MathJax will not TeXify code spans, + // we still have to consider them at this point; the following issue has happened several times: + // + // `$foo` and `$bar` are variables. --> $foo ` and `$bar are variables. + const hasCodeSpans = /`/.test(text); + if (hasCodeSpans) { + text = text + .replace(/~/g, '~T') + .replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, (wholematch) => + wholematch.replace(/\$/g, '~D') + ); + deTilde = (text: string) => { + return text.replace(/~([TD])/g, (wholematch, character) => + character === 'T' ? '~' : inline + ); + }; + } else { + deTilde = (text: string) => { + return text; + }; + } + + let blocks = text.replace(/\r\n?/g, '\n').split(MATHSPLIT); + + for (let i = 1, m = blocks.length; i < m; i += 2) { + const block = blocks[i]; + if (block.charAt(0) === '@') { + // + // Things that look like our math markers will get + // stored and then retrieved along with the math. + // + blocks[i] = '@@' + math.length + '@@'; + math.push(block); + } else if (start !== null) { + // + // If we are in math, look for the end delimiter, + // but don't go past double line breaks, and + // and balance braces within the math. + // + if (block === end) { + if (braces) { + last = i; + } else { + blocks = processMath(start, i, deTilde, math, blocks); + start = null; + end = null; + last = null; + } + } else if (block.match(/\n.*\n/)) { + if (last !== null) { + i = last; + blocks = processMath(start, i, deTilde, math, blocks); + } + start = null; + end = null; + last = null; + braces = 0; + } else if (block === '{') { + braces++; + } else if (block === '}' && braces) { + braces--; + } + } else { + // + // Look for math start delimiters and when + // found, set up the end delimiter. + // + if (block === inline || block === '$$') { + start = i; + end = block; + braces = 0; + } else if (block === '\\\\(' || block === '\\\\[') { + start = i; + end = block.slice(-1) === '(' ? '\\\\)' : '\\\\]'; + braces = 0; + } else if (block.substr(1, 5) === 'begin') { + start = i; + end = '\\end' + block.substr(6); + braces = 0; + } + } + } + if (start !== null && last !== null) { + blocks = processMath(start, last, deTilde, math, blocks); + start = null; + end = null; + last = null; + } + return { text: deTilde(blocks.join('')), math }; +} + +/** + * Put back the math strings that were saved, + * and clear the math array (no need to keep it around). + */ +export function replaceMath(text: string, math: string[]): string { + /** + * Replace a math placeholder with its corresponding group. + * The math delimiters "\\(", "\\[", "\\)" and "\\]" are replaced + * removing one backslash in order to be interpreted correctly by MathJax. + */ + const process = (match: string, n: number): string => { + let group = math[n]; + if ( + group.substr(0, 3) === '\\\\(' && + group.substr(group.length - 3) === '\\\\)' + ) { + group = '\\(' + group.substring(3, group.length - 3) + '\\)'; + } else if ( + group.substr(0, 3) === '\\\\[' && + group.substr(group.length - 3) === '\\\\]' + ) { + group = '\\[' + group.substring(3, group.length - 3) + '\\]'; + } + return group; + }; + // Replace all the math group placeholders in the text + // with the saved strings. + return text.replace(/@@(\d+)@@/g, process); +} + +/** + * Process math blocks. + * + * The math is in blocks i through j, so + * collect it into one block and clear the others. + * Replace &, <, and > by named entities. + * For IE, put
at the ends of comments since IE removes \n. + * Clear the current math positions and store the index of the + * math, then push the math string onto the storage array. + * The preProcess function is called on all blocks if it has been passed in + */ +function processMath( + i: number, + j: number, + preProcess: (input: string) => string, + math: string[], + blocks: string[] +): string[] { + let block = blocks + .slice(i, j + 1) + .join('') + .replace(/&/g, '&') // use HTML entity for & + .replace(//g, '>'); // use HTML entity for > + if (navigator && navigator.appName === 'Microsoft Internet Explorer') { + block = block.replace(/(%[^\n]*)\n/g, '$1
\n'); + } + while (j > i) { + blocks[j] = ''; + j--; + } + blocks[i] = '@@' + math.length + '@@'; // replace the current block text with a unique tag to find later + if (preProcess) { + block = preProcess(block); + } + math.push(block); + return blocks; +} diff --git a/packages/base-manager/src/manager-base.ts b/packages/base-manager/src/manager-base.ts index dac6137d59..2d3f5e27cf 100644 --- a/packages/base-manager/src/manager-base.ts +++ b/packages/base-manager/src/manager-base.ts @@ -26,9 +26,41 @@ import { } from '@jupyter-widgets/base'; import { base64ToBuffer, bufferToBase64, hexToBuffer } from './utils'; +import { removeMath, replaceMath } from './latex'; +import sanitize from 'sanitize-html'; const PROTOCOL_MAJOR_VERSION = PROTOCOL_VERSION.split('.', 1)[0]; +/** + * Sanitize HTML-formatted descriptions. + */ +function default_inline_sanitize(s: string): string { + const allowedTags = [ + 'a', + 'abbr', + 'b', + 'code', + 'em', + 'i', + 'img', + 'li', + 'ol', + 'span', + 'strong', + 'ul', + ]; + const allowedAttributes = { + '*': ['aria-*', 'style', 'title'], + a: ['href'], + img: ['src'], + style: ['media', 'type'], + }; + return sanitize(s, { + allowedTags: allowedTags, + allowedAttributes: allowedAttributes, + }); +} + export interface IState extends PartialJSONObject { buffers?: IBase64Buffers[]; model_name: string; @@ -467,6 +499,13 @@ export abstract class ManagerBase implements IWidgetManager { return Promise.resolve(url); } + inline_sanitize(source: string): string { + const parts = removeMath(source); + // Sanitize tags for inline output. + const sanitized = default_inline_sanitize(parts['text']); + return replaceMath(sanitized, parts['math']); + } + /** * The comm target name to register */ diff --git a/packages/base/src/manager.ts b/packages/base/src/manager.ts index aa002847cb..ddefcae543 100644 --- a/packages/base/src/manager.ts +++ b/packages/base/src/manager.ts @@ -190,4 +190,6 @@ export interface IWidgetManager { * The default implementation just returns the original url. */ resolveUrl(url: string): Promise; + + inline_sanitize(s: string): string; } diff --git a/packages/base/test/src/dummy-manager.ts b/packages/base/test/src/dummy-manager.ts index feeb69e37a..792104b930 100644 --- a/packages/base/test/src/dummy-manager.ts +++ b/packages/base/test/src/dummy-manager.ts @@ -316,6 +316,10 @@ export class DummyManager implements widgets.IWidgetManager { return Promise.resolve(url); } + inline_sanitize(s: string): string { + return s; + } + /** * Dictionary of model ids and model instance promises */ diff --git a/packages/controls/src/widget_bool.ts b/packages/controls/src/widget_bool.ts index 6ff16c8561..6f58446c92 100644 --- a/packages/controls/src/widget_bool.ts +++ b/packages/controls/src/widget_bool.ts @@ -79,7 +79,12 @@ export class CheckboxView extends DescriptionView { return; } const description = this.model.get('description'); - this.descriptionSpan.innerHTML = description; + if (this.model.get('description_allow_html')) { + this.descriptionSpan.innerHTML = + this.model.widget_manager.inline_sanitize(description); + } else { + this.descriptionSpan.textContent = description; + } this.typeset(this.descriptionSpan); this.descriptionSpan.title = description; this.checkbox.title = description; diff --git a/packages/controls/src/widget_description.ts b/packages/controls/src/widget_description.ts index 0da85a0085..40ea975278 100644 --- a/packages/controls/src/widget_description.ts +++ b/packages/controls/src/widget_description.ts @@ -41,6 +41,7 @@ export class DescriptionModel extends DOMWidgetModel { _view_module_version: JUPYTER_CONTROLS_VERSION, _model_module_version: JUPYTER_CONTROLS_VERSION, description: '', + description_allow_html: false, }; } } @@ -53,6 +54,11 @@ export class DescriptionView extends DOMWidgetView { this.label.style.display = 'none'; this.listenTo(this.model, 'change:description', this.updateDescription); + this.listenTo( + this.model, + 'change:description_allow_html', + this.updateDescription + ); this.listenTo(this.model, 'change:tabbable', this.updateTabindex); this.updateDescription(); this.updateTabindex(); @@ -68,7 +74,12 @@ export class DescriptionView extends DOMWidgetView { if (description.length === 0) { this.label.style.display = 'none'; } else { - this.label.innerHTML = description; + if (this.model.get('description_allow_html')) { + this.label.innerHTML = + this.model.widget_manager.inline_sanitize(description); + } else { + this.label.textContent = description; + } this.typeset(this.label); this.label.style.display = ''; } diff --git a/packages/html-manager/package.json b/packages/html-manager/package.json index 396883a220..7d7593863c 100644 --- a/packages/html-manager/package.json +++ b/packages/html-manager/package.json @@ -52,6 +52,7 @@ "devDependencies": { "@types/mocha": "^8.2.2", "@types/node": "^15.12.2", + "@types/sanitize-html": "^1.20", "chai": "^4.0.0", "css-loader": "^5.2.6", "file-loader": "^6.2.0", diff --git a/packages/schema/jupyterwidgetmodels.latest.json b/packages/schema/jupyterwidgetmodels.latest.json index 6cab34448b..a8b2f51874 100644 --- a/packages/schema/jupyterwidgetmodels.latest.json +++ b/packages/schema/jupyterwidgetmodels.latest.json @@ -666,6 +666,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -796,6 +802,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -1191,6 +1203,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes.", @@ -1308,6 +1326,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes.", @@ -1425,6 +1449,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": "reference to new instance", "help": "", @@ -1530,6 +1560,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -2051,6 +2087,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes.", @@ -2434,6 +2476,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -2553,6 +2601,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable button", @@ -2691,6 +2745,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -2842,6 +2902,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": "reference to new instance", "help": "", @@ -2966,6 +3032,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -3115,6 +3187,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -3264,6 +3342,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -3503,7 +3587,7 @@ "type": "string" }, { - "default": "GridBoxView", + "default": "FloatsInputView", "help": "", "name": "_view_name", "type": "string" @@ -3517,14 +3601,28 @@ }, { "default": [], - "help": "List of widget children", - "items": { - "type": "reference", - "widget": "Widget" - }, - "name": "children", + "help": "", + "name": "allowed_tags", "type": "array" }, + { + "default": "", + "help": "Description of the control.", + "name": "description", + "type": "string" + }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, + { + "default": ".1f", + "help": "", + "name": "format", + "type": "string" + }, { "default": "reference to new instance", "help": "", @@ -3532,6 +3630,27 @@ "type": "reference", "widget": "Layout" }, + { + "allow_none": true, + "default": null, + "help": "", + "name": "max", + "type": "float" + }, + { + "allow_none": true, + "default": null, + "help": "", + "name": "min", + "type": "float" + }, + { + "default": "reference to new instance", + "help": "Styling customizations", + "name": "style", + "type": "reference", + "widget": "DescriptionStyle" + }, { "allow_none": true, "default": null, @@ -3539,22 +3658,35 @@ "name": "tabbable", "type": "bool" }, + { + "default": "", + "enum": ["primary", "success", "info", "warning", "danger", ""], + "help": "Use a predefined styling for the tags.", + "name": "tag_style", + "type": "string" + }, { "allow_none": true, "default": null, "help": "A tooltip caption.", "name": "tooltip", "type": "string" + }, + { + "default": [], + "help": "List of float tags", + "name": "value", + "type": "array" } ], "model": { "module": "@jupyter-widgets/controls", - "name": "GridBoxModel", + "name": "FloatsInputModel", "version": "2.0.0" }, "view": { "module": "@jupyter-widgets/controls", - "name": "GridBoxView", + "name": "FloatsInputView", "version": "2.0.0" } }, @@ -3582,7 +3714,7 @@ "type": "string" }, { - "default": "HBoxModel", + "default": "GridBoxModel", "help": "", "name": "_model_name", "type": "string" @@ -3600,7 +3732,7 @@ "type": "string" }, { - "default": "HBoxView", + "default": "GridBoxView", "help": "", "name": "_view_name", "type": "string" @@ -3708,6 +3840,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": "reference to new instance", "help": "", @@ -3813,6 +3951,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": "reference to new instance", "help": "", @@ -4029,6 +4173,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": "reference to new instance", "help": "", @@ -4153,6 +4303,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -4301,6 +4457,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -4449,6 +4611,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -4645,6 +4813,150 @@ "version": "2.0.0" } }, + { + "attributes": [ + { + "default": [], + "help": "CSS classes applied to widget DOM element", + "items": { + "type": "string" + }, + "name": "_dom_classes", + "type": "array" + }, + { + "default": "@jupyter-widgets/controls", + "help": "", + "name": "_model_module", + "type": "string" + }, + { + "default": "2.0.0", + "help": "", + "name": "_model_module_version", + "type": "string" + }, + { + "default": "LabelModel", + "help": "", + "name": "_model_name", + "type": "string" + }, + { + "default": "@jupyter-widgets/controls", + "help": "", + "name": "_view_module", + "type": "string" + }, + { + "default": "2.0.0", + "help": "", + "name": "_view_module_version", + "type": "string" + }, + { + "default": "IntsInputView", + "help": "", + "name": "_view_name", + "type": "string" + }, + { + "default": true, + "help": "", + "name": "allow_duplicates", + "type": "bool" + }, + { + "default": [], + "help": "", + "name": "allowed_tags", + "type": "array" + }, + { + "default": "", + "help": "Description of the control.", + "name": "description", + "type": "string" + }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, + { + "default": ".3g", + "help": "", + "name": "format", + "type": "string" + }, + { + "default": "reference to new instance", + "help": "", + "name": "layout", + "type": "reference", + "widget": "Layout" + }, + { + "allow_none": true, + "default": null, + "help": "", + "name": "max", + "type": "int" + }, + { + "allow_none": true, + "default": null, + "help": "", + "name": "min", + "type": "int" + }, + { + "default": "reference to new instance", + "help": "Styling customizations", + "name": "style", + "type": "reference", + "widget": "DescriptionStyle" + }, + { + "allow_none": true, + "default": null, + "help": "Is widget tabbable?", + "name": "tabbable", + "type": "bool" + }, + { + "default": "", + "enum": ["primary", "success", "info", "warning", "danger", ""], + "help": "Use a predefined styling for the tags.", + "name": "tag_style", + "type": "string" + }, + { + "allow_none": true, + "default": null, + "help": "A tooltip caption.", + "name": "tooltip", + "type": "string" + }, + { + "default": [], + "help": "List of int tags", + "name": "value", + "type": "array" + } + ], + "model": { + "module": "@jupyter-widgets/controls", + "name": "IntsInputModel", + "version": "2.0.0" + }, + "view": { + "module": "@jupyter-widgets/controls", + "name": "IntsInputView", + "version": "2.0.0" + } + }, { "attributes": [ { @@ -4698,6 +5010,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": "reference to new instance", "help": "", @@ -4992,6 +5310,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -5103,6 +5427,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -5322,6 +5652,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -5437,6 +5773,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -5558,6 +5900,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -5687,6 +6035,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -5820,6 +6174,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -6239,6 +6599,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": "reference to new instance", "help": "", @@ -6351,6 +6717,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -6468,6 +6840,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -6728,6 +7106,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes.", @@ -6856,6 +7240,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes", @@ -7145,6 +7535,12 @@ "name": "description", "type": "string" }, + { + "default": false, + "help": "Accept HTML in the description.", + "name": "description_allow_html", + "type": "bool" + }, { "default": false, "help": "Enable or disable user changes.", diff --git a/packages/schema/jupyterwidgetmodels.latest.md b/packages/schema/jupyterwidgetmodels.latest.md index c213b9d2b6..3eb36670e3 100644 --- a/packages/schema/jupyterwidgetmodels.latest.md +++ b/packages/schema/jupyterwidgetmodels.latest.md @@ -118,6 +118,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'FloatTextView'` | `continuous_update` | boolean | `false` | Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `max` | number (float) | `100.0` | Max value @@ -141,6 +142,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'IntTextView'` | `continuous_update` | boolean | `false` | Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `max` | number (integer) | `100` | Max value @@ -213,6 +215,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'CheckboxView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes. `indent` | boolean | `true` | Indent the control to align with other controls with a description. `layout` | reference to Layout widget | reference to new instance | @@ -234,6 +237,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'ColorPickerView'` | `concise` | boolean | `false` | Display short version with just a color selector. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes. `layout` | reference to Layout widget | reference to new instance | `style` | reference to DescriptionStyle widget | reference to new instance | Styling customizations @@ -255,6 +259,7 @@ Attribute | Type | Default | Help `allow_duplicates` | boolean | `true` | `allowed_tags` | array | `[]` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `layout` | reference to Layout widget | reference to new instance | `style` | reference to DescriptionStyle widget | reference to new instance | Styling customizations `tabbable` | `null` or boolean | `null` | Is widget tabbable? @@ -274,6 +279,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'ComboboxView'` | `continuous_update` | boolean | `true` | Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `ensure_option` | boolean | `false` | If set, ensure value is in options. Implies continuous_update=False. `layout` | reference to Layout widget | reference to new instance | @@ -367,6 +373,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'DatePickerView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes. `layout` | reference to Layout widget | reference to new instance | `max` | `null` or Date | `null` | @@ -389,6 +396,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'DatetimeView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes. `layout` | reference to Layout widget | reference to new instance | `max` | `null` or Datetime | `null` | @@ -436,6 +444,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'DropdownView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `index` | `null` or number (integer) | `null` | Selected index `layout` | reference to Layout widget | reference to new instance | @@ -457,6 +466,7 @@ Attribute | Type | Default | Help `accept` | string | `''` | File types to accept, empty string for all `button_style` | string (one of `'primary'`, `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the button. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable button `error` | string | `''` | Error message `icon` | string | `'upload'` | Font-awesome icon name, without the 'fa-' prefix. @@ -481,6 +491,7 @@ Attribute | Type | Default | Help `base` | number (float) | `10.0` | Base for the logarithm `continuous_update` | boolean | `true` | Update the value of the widget as the user is holding the slider. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `max` | number (float) | `4.0` | Max value for the exponent @@ -507,6 +518,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'ProgressView'` | `bar_style` | `null` or string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the progess bar. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `layout` | reference to Layout widget | reference to new instance | `max` | number (float) | `100.0` | Max value `min` | number (float) | `0.0` | Min value @@ -529,6 +541,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'FloatRangeSliderView'` | `continuous_update` | boolean | `true` | Update the value of the widget as the user is sliding the slider. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `max` | number (float) | `100.0` | Max value @@ -555,6 +568,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'FloatSliderView'` | `continuous_update` | boolean | `true` | Update the value of the widget as the user is holding the slider. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `max` | number (float) | `100.0` | Max value @@ -581,6 +595,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'FloatTextView'` | `continuous_update` | boolean | `false` | Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `step` | `null` or number (float) | `null` | Minimum step to increment the value @@ -603,6 +618,7 @@ Attribute | Type | Default | Help `allow_duplicates` | boolean | `true` | `allowed_tags` | array | `[]` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `format` | string | `'.1f'` | `layout` | reference to Layout widget | reference to new instance | `max` | `null` or number (float) | `null` | @@ -659,6 +675,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'HTMLMathView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `layout` | reference to Layout widget | reference to new instance | `placeholder` | string | `'\u200b'` | Placeholder text to display when nothing has been typed `style` | reference to DescriptionStyle widget | reference to new instance | Styling customizations @@ -678,6 +695,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'HTMLView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `layout` | reference to Layout widget | reference to new instance | `placeholder` | string | `'\u200b'` | Placeholder text to display when nothing has been typed `style` | reference to DescriptionStyle widget | reference to new instance | Styling customizations @@ -717,6 +735,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'ProgressView'` | `bar_style` | string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the progess bar. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `layout` | reference to Layout widget | reference to new instance | `max` | number (integer) | `100` | Max value `min` | number (integer) | `0` | Min value @@ -739,6 +758,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'IntRangeSliderView'` | `continuous_update` | boolean | `true` | Update the value of the widget as the user is sliding the slider. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `max` | number (integer) | `100` | Max value @@ -765,6 +785,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'IntSliderView'` | `continuous_update` | boolean | `true` | Update the value of the widget as the user is holding the slider. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `max` | number (integer) | `100` | Max value @@ -791,6 +812,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'IntTextView'` | `continuous_update` | boolean | `false` | Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `step` | number (integer) | `1` | Minimum step to increment the value @@ -813,6 +835,7 @@ Attribute | Type | Default | Help `allow_duplicates` | boolean | `true` | `allowed_tags` | array | `[]` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `format` | string | `'.3g'` | `layout` | reference to Layout widget | reference to new instance | `max` | `null` or number (integer) | `null` | @@ -835,6 +858,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'LabelView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `layout` | reference to Layout widget | reference to new instance | `placeholder` | string | `'\u200b'` | Placeholder text to display when nothing has been typed `style` | reference to DescriptionStyle widget | reference to new instance | Styling customizations @@ -867,6 +891,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'DatetimeView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes. `layout` | reference to Layout widget | reference to new instance | `max` | `null` or Datetime | `null` | @@ -889,6 +914,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'PasswordView'` | `continuous_update` | boolean | `true` | Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `placeholder` | string | `'\u200b'` | Placeholder text to display when nothing has been typed @@ -909,6 +935,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'PlayView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `interval` | number (integer) | `100` | The time between two animation steps (ms). `layout` | reference to Layout widget | reference to new instance | @@ -949,6 +976,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'RadioButtonsView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `index` | `null` or number (integer) | `null` | Selected index `layout` | reference to Layout widget | reference to new instance | @@ -969,6 +997,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'SelectView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `index` | `null` or number (integer) | `null` | Selected index `layout` | reference to Layout widget | reference to new instance | @@ -990,6 +1019,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'SelectMultipleView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `index` | array of number (integer) | `[]` | Selected indices `layout` | reference to Layout widget | reference to new instance | @@ -1012,6 +1042,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'SelectionRangeSliderView'` | `continuous_update` | boolean | `true` | Update the value of the widget as the user is holding the slider. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `index` | array | `[0, 0]` | Min and max selected indices `layout` | reference to Layout widget | reference to new instance | @@ -1035,6 +1066,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'SelectionSliderView'` | `continuous_update` | boolean | `true` | Update the value of the widget as the user is holding the slider. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `index` | number (integer) | `0` | Selected index `layout` | reference to Layout widget | reference to new instance | @@ -1109,6 +1141,7 @@ Attribute | Type | Default | Help `allow_duplicates` | boolean | `true` | `allowed_tags` | array | `[]` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `layout` | reference to Layout widget | reference to new instance | `style` | reference to DescriptionStyle widget | reference to new instance | Styling customizations `tabbable` | `null` or boolean | `null` | Is widget tabbable? @@ -1129,6 +1162,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'TextView'` | `continuous_update` | boolean | `true` | Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `placeholder` | string | `'\u200b'` | Placeholder text to display when nothing has been typed @@ -1150,6 +1184,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'TextareaView'` | `continuous_update` | boolean | `true` | Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `layout` | reference to Layout widget | reference to new instance | `placeholder` | string | `'\u200b'` | Placeholder text to display when nothing has been typed @@ -1171,6 +1206,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'TimeView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes. `layout` | reference to Layout widget | reference to new instance | `max` | `null` or Time | `null` | @@ -1194,6 +1230,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'ToggleButtonView'` | `button_style` | string (one of `'primary'`, `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the button. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes. `icon` | string | `''` | Font-awesome icon. `layout` | reference to Layout widget | reference to new instance | @@ -1216,6 +1253,7 @@ Attribute | Type | Default | Help `_view_name` | string | `'ToggleButtonsView'` | `button_style` | `null` or string (one of `'primary'`, `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the buttons. `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes `icons` | array of string | `[]` | Icons names for each button (FontAwesome names without the fa- prefix). `index` | `null` or number (integer) | `null` | Selected index @@ -1268,6 +1306,7 @@ Attribute | Type | Default | Help `_view_module_version` | string | `'2.0.0'` | `_view_name` | string | `'ValidView'` | `description` | string | `''` | Description of the control. +`description_allow_html` | boolean | `false` | Accept HTML in the description. `disabled` | boolean | `false` | Enable or disable user changes. `layout` | reference to Layout widget | reference to new instance | `readout` | string | `'Invalid'` | Message displayed when the value is False diff --git a/tests/jlogo-small.png b/tests/jlogo-small.png new file mode 100644 index 0000000000..6bffd5b176 Binary files /dev/null and b/tests/jlogo-small.png differ diff --git a/tests/test_sanitizer.ipynb b/tests/test_sanitizer.ipynb new file mode 100644 index 0000000000..0759ea3e9a --- /dev/null +++ b/tests/test_sanitizer.ipynb @@ -0,0 +1,99 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ipywidgets import *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Checkbox(description=\"

italic

bold

\", description_allow_html=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Checkbox(description=\"

italic

bold

\", description_allow_html=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Text(description=\" NOT styled\", description_allow_html=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Text(description=\" styled\", description_allow_html=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Textarea(description=\" underlined\", description_allow_html=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "VBox((Text(description=\"$ y < a, x $\", allow_html=True),\n", + " Text(description=\"$ y < a, x $\", description_allow_html=True)\n", + " ))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "VBox((Label(\"$ y < a, x > a$\"), HTMLMath(\"$ y < a, x > a$\")))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/yarn.lock b/yarn.lock index f9a88efd89..545d4be12c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1983,6 +1983,13 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/sanitize-html@^1.20": + version "1.27.2" + resolved "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-1.27.2.tgz#f7bf16ca4b1408278f97ae737f0377a08a10b35c" + integrity sha512-DrH26m7CV6PB4YVckjbSIx+xloB7HBolr9Ctm0gZBffSu5dDV4yJKFQGPquJlReVW+xmg59gx+b/8/qYHxZEuw== + dependencies: + htmlparser2 "^4.1.0" + "@types/scheduler@*": version "0.16.1" resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" @@ -3693,6 +3700,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0: resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== +domhandler@^3.0.0: + version "3.3.0" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" + integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== + dependencies: + domelementtype "^2.0.1" + domhandler@^4.0.0, domhandler@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" @@ -3700,7 +3714,7 @@ domhandler@^4.0.0, domhandler@^4.2.0: dependencies: domelementtype "^2.2.0" -domutils@^2.5.2: +domutils@^2.0.0, domutils@^2.5.2: version "2.7.0" resolved "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg== @@ -4717,6 +4731,16 @@ html-escaper@^2.0.0: resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +htmlparser2@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" + integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + domutils "^2.0.0" + entities "^2.0.0" + htmlparser2@^6.0.0: version "6.1.0" resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" @@ -7411,7 +7435,7 @@ postcss@^6.0, postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.11, postcss@^6.0.14, source-map "^0.6.1" supports-color "^5.4.0" -postcss@^7.0.2: +postcss@^7.0.2, postcss@^7.0.27: version "7.0.36" resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb" integrity sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw== @@ -8090,6 +8114,16 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sanitize-html@^1.20: + version "1.27.5" + resolved "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz#6c8149462adb23e360e1bb71cc0bae7f08c823c7" + integrity sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A== + dependencies: + htmlparser2 "^4.1.0" + lodash "^4.17.15" + parse-srcset "^1.0.2" + postcss "^7.0.27" + sanitize-html@~2.3.3: version "2.3.3" resolved "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.3.3.tgz#3db382c9a621cce4c46d90f10c64f1e9da9e8353"