25
25
* like so:
26
26
*
27
27
* ```js
28
- * import {h} from 'hastscript'
29
28
* import deepmerge from 'deepmerge'
29
+ * import {h} from 'hastscript'
30
30
* import {defaultSchema, sanitize} from 'hast-util-sanitize'
31
31
*
32
32
* const schema = deepmerge(defaultSchema, {attributes: {'*': ['className']}})
69
69
*
70
70
* ```js
71
71
* ancestors: {
72
- * li : ['ol', 'ul '],
72
+ * tbody : ['table '],
73
73
* // …
74
74
* tr: ['table']
75
75
* }
76
76
* ```
77
77
* @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:
79
79
* `defaultSchema.attributes`).
80
80
*
81
81
* The special key `'*'` as a tag name defines property names allowed on all
82
82
* elements.
83
83
*
84
84
* The special value `'data*'` as a property name can be used to allow all
85
- * `data`properties.
85
+ * `data` properties.
86
86
*
87
87
* For example:
88
88
*
89
89
* ```js
90
90
* attributes: {
91
91
* a: ['href'],
92
+ * // …
92
93
* img: ['src', 'longDesc'],
93
94
* // …
94
95
* '*': [
97
98
* 'acceptCharset',
98
99
* // …
99
100
* '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'
126
103
* ]
127
- * // …
128
104
* }
129
105
* ```
130
106
*
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.
143
122
* @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`).
145
124
*
146
125
* For example:
147
126
*
148
127
* ```js
149
- * clobber: ['name ', 'id ']
128
+ * clobber: ['id ', 'name ']
150
129
* ```
151
130
* @property {string | null | undefined } [clobberPrefix]
152
131
* Prefix to use before clobbering properties (default:
161
140
* Map of *property names* to allowed protocols (default:
162
141
* `defaultSchema.protocols`).
163
142
*
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 .
168
147
*
169
148
* For example:
170
149
*
171
150
* ```js
172
151
* protocols: {
173
- * href: ['http', 'https', 'mailto'],
152
+ * href: ['http', 'https', 'irc', 'ircs', ' mailto', 'xmpp '],
174
153
* // …
175
154
* longDesc: ['http', 'https']
176
155
* }
177
156
* ```
178
157
* @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`).
184
160
*
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 .
188
164
*
189
165
* For example:
190
166
*
191
167
* ```js
192
168
* required: {
193
- * input: {type: 'checkbox', disabled: true }
169
+ * input: {disabled: true, type: 'checkbox' }
194
170
* }
195
171
* ```
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`.
196
177
* @property {Array<string> | null | undefined } [strip]
197
178
* List of tag names to strip from the tree (default: `defaultSchema.strip`).
198
179
*
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.
201
183
*
202
184
* For example:
203
185
*
211
193
*
212
194
* ```js
213
195
* tagNames: [
214
- * 'h1 ',
215
- * 'h2 ',
216
- * 'h3 ',
196
+ * 'a ',
197
+ * 'abbr ',
198
+ * 'b ',
217
199
* // …
218
- * 'strike ',
219
- * 'summary ',
220
- * 'details '
200
+ * 'ul ',
201
+ * 'var ',
202
+ * 'wbr '
221
203
* ]
222
204
* ```
223
205
*
@@ -241,7 +223,7 @@ const own = {}.hasOwnProperty
241
223
* @param {Readonly<Nodes> } node
242
224
* Unsafe tree.
243
225
* @param {Readonly<Schema> | null | undefined } [options]
244
- * Schema defining how to sanitize (default: `defaultSchema`).
226
+ * Configuration (default: `defaultSchema`).
245
227
* @returns {Nodes }
246
228
* New, safe tree.
247
229
*/
@@ -382,7 +364,6 @@ function element(state, unsafe) {
382
364
state . stack . pop ( )
383
365
384
366
let safeElement = false
385
- let index = - 1
386
367
387
368
if (
388
369
name . length > 0 &&
@@ -395,6 +376,8 @@ function element(state, unsafe) {
395
376
// ancestor.
396
377
if ( state . schema . ancestors && own . call ( state . schema . ancestors , name ) ) {
397
378
const ancestors = state . schema . ancestors [ name ]
379
+ let index = - 1
380
+
398
381
safeElement = false
399
382
400
383
while ( ++ index < ancestors . length ) {
0 commit comments