2525 * like so:
2626 *
2727 * ```js
28- * import {h} from 'hastscript'
2928 * import deepmerge from 'deepmerge'
29+ * import {h} from 'hastscript'
3030 * import {defaultSchema, sanitize} from 'hast-util-sanitize'
3131 *
3232 * const schema = deepmerge(defaultSchema, {attributes: {'*': ['className']}})
6969 *
7070 * ```js
7171 * ancestors: {
72- * li : ['ol', 'ul '],
72+ * tbody : ['table '],
7373 * // …
7474 * tr: ['table']
7575 * }
7676 * ```
7777 * @property {Record<string, Array<PropertyDefinition>> | null | undefined } [attributes]
78- * Map of tag names to allowed * property names* (default:
78+ * Map of tag names to allowed property names (default:
7979 * `defaultSchema.attributes`).
8080 *
8181 * The special key `'*'` as a tag name defines property names allowed on all
8282 * elements.
8383 *
8484 * The special value `'data*'` as a property name can be used to allow all
85- * `data`properties.
85+ * `data` properties.
8686 *
8787 * For example:
8888 *
8989 * ```js
9090 * attributes: {
9191 * a: ['href'],
92+ * // …
9293 * img: ['src', 'longDesc'],
9394 * // …
9495 * '*': [
9798 * 'acceptCharset',
9899 * // …
99100 * 'vSpace',
100- * 'width',
101- * 'itemProp'
102- * ]
103- * }
104- * ```
105- *
106- * Instead of a single string, which allows any *property value* of that
107- * property name, it’s also possible to provide an array to allow several
108- * values.
109- * For example, `input: ['type']` allows the `type` attribute set to any
110- * value on inputs.
111- * But `input: [['type', 'checkbox', 'radio']]` allows `type` only when set
112- * to one of the allowed values (`'checkbox'` or `'radio'`).
113- *
114- * You can also use regexes, so for example `span: [['className', /^hljs-/]]`
115- * allows any class that starts with `hljs-` on `span` elements.
116- *
117- * This is how the default GitHub schema allows only disabled checkbox
118- * inputs:
119- *
120- * ```js
121- * attributes: {
122- * // …
123- * input: [
124- * ['type', 'checkbox'],
125- * ['disabled', true]
101+ * 'value',
102+ * 'width'
126103 * ]
127- * // …
128104 * }
129105 * ```
130106 *
131- * Attributes also plays well with properties that accept space- or
132- * comma-separated values, such as `class`.
133- * Say you wanted to allow certain classes on `span` elements for syntax
134- * highlighting, that can be done like this:
135- *
136- * ```js
137- * // …
138- * span: [
139- * ['className', 'token', 'number', 'operator']
140- * ]
141- * // …
142- * ```
107+ * Instead of a single string in the array, which allows any property value
108+ * for the field, you can use an array to allow several values.
109+ * For example, `input: ['type']` allows `type` set to any value on `input`s.
110+ * But `input: [['type', 'checkbox', 'radio']]` allows `type` when set to
111+ * `'checkbox'` or `'radio'`.
112+ *
113+ * You can use regexes, so for example `span: [['className', /^hljs-/]]`
114+ * allows any class that starts with `hljs-` on `span`s.
115+ *
116+ * When comma- or space-separated values are used (such as `className`), each
117+ * value in is checked individually.
118+ * For example, to allow certain classes on `span`s for syntax highlighting,
119+ * use `span: [['className', 'number', 'operator', 'token']]`.
120+ * This will allow `'number'`, `'operator'`, and `'token'` classes, but drop
121+ * others.
143122 * @property {Array<string> | null | undefined } [clobber]
144- * List of * property names* that clobber (default: `defaultSchema.clobber`).
123+ * List of property names that clobber (default: `defaultSchema.clobber`).
145124 *
146125 * For example:
147126 *
148127 * ```js
149- * clobber: ['name ', 'id ']
128+ * clobber: ['id ', 'name ']
150129 * ```
151130 * @property {string | null | undefined } [clobberPrefix]
152131 * Prefix to use before clobbering properties (default:
161140 * Map of *property names* to allowed protocols (default:
162141 * `defaultSchema.protocols`).
163142 *
164- * The listed property names can be set to URLs that are local (relative to
165- * the current website, such as `this`, `#this`, `/this`, or `?this`) or
166- * remote (such as `https://example.com`), in which case they must have a
167- * protocol that is allowed here .
143+ * This defines URLs that are always allowed to have local URLs (relative to
144+ * the current website, such as `this`, `#this`, `/this`, or `?this`), and
145+ * only allowed to have remote URLs (such as `https://example.com`) if they
146+ * use a known protocol .
168147 *
169148 * For example:
170149 *
171150 * ```js
172151 * protocols: {
173- * href: ['http', 'https', 'mailto'],
152+ * href: ['http', 'https', 'irc', 'ircs', ' mailto', 'xmpp '],
174153 * // …
175154 * longDesc: ['http', 'https']
176155 * }
177156 * ```
178157 * @property {Record<string, Record<string, Properties[keyof Properties]>> | null | undefined } [required]
179- * Map of tag names to required *property names* and their default *property
180- * value* (default: `defaultSchema.required`).
181- *
182- * If the defined keys do not exist in an element’s properties, they are added
183- * and set to the specified value.
158+ * Map of tag names to required property names with a default value
159+ * (default: `defaultSchema.required`).
184160 *
185- * Note that properties are first checked based on the schema at `attributes`,
186- * so properties could be removed by that step and then added again through
187- * `required` .
161+ * This defines properties that must be set.
162+ * If a field does not exist (after the element was made safe), these will be
163+ * added with the given value .
188164 *
189165 * For example:
190166 *
191167 * ```js
192168 * required: {
193- * input: {type: 'checkbox', disabled: true }
169+ * input: {disabled: true, type: 'checkbox' }
194170 * }
195171 * ```
172+ *
173+ * > 👉 **Note**: properties are first checked based on `schema.attributes`,
174+ * > then on `schema.required`.
175+ * > That means properties could be removed by `attributes` and then added
176+ * > again with `required`.
196177 * @property {Array<string> | null | undefined } [strip]
197178 * List of tag names to strip from the tree (default: `defaultSchema.strip`).
198179 *
199- * By default, unsafe elements are replaced by their children.
200- * Some elements should however be entirely stripped from the tree.
180+ * By default, unsafe elements (those not in `schema.tagNames`) are replaced
181+ * by what they contain.
182+ * This option can drop their contents.
201183 *
202184 * For example:
203185 *
211193 *
212194 * ```js
213195 * tagNames: [
214- * 'h1 ',
215- * 'h2 ',
216- * 'h3 ',
196+ * 'a ',
197+ * 'abbr ',
198+ * 'b ',
217199 * // …
218- * 'strike ',
219- * 'summary ',
220- * 'details '
200+ * 'ul ',
201+ * 'var ',
202+ * 'wbr '
221203 * ]
222204 * ```
223205 *
@@ -241,7 +223,7 @@ const own = {}.hasOwnProperty
241223 * @param {Readonly<Nodes> } node
242224 * Unsafe tree.
243225 * @param {Readonly<Schema> | null | undefined } [options]
244- * Schema defining how to sanitize (default: `defaultSchema`).
226+ * Configuration (default: `defaultSchema`).
245227 * @returns {Nodes }
246228 * New, safe tree.
247229 */
@@ -382,7 +364,6 @@ function element(state, unsafe) {
382364 state . stack . pop ( )
383365
384366 let safeElement = false
385- let index = - 1
386367
387368 if (
388369 name . length > 0 &&
@@ -395,6 +376,8 @@ function element(state, unsafe) {
395376 // ancestor.
396377 if ( state . schema . ancestors && own . call ( state . schema . ancestors , name ) ) {
397378 const ancestors = state . schema . ancestors [ name ]
379+ let index = - 1
380+
398381 safeElement = false
399382
400383 while ( ++ index < ancestors . length ) {
0 commit comments