html-validate 8.15.0 → 8.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core.js +271 -110
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/elements.js +13 -0
- package/dist/cjs/elements.js.map +1 -1
- package/dist/es/core.js +271 -110
- package/dist/es/core.js.map +1 -1
- package/dist/es/elements.js +13 -0
- package/dist/es/elements.js.map +1 -1
- package/dist/schema/elements.json +4 -0
- package/dist/types/browser.d.ts +19 -0
- package/dist/types/index.d.ts +19 -0
- package/package.json +4 -4
package/dist/es/core.js
CHANGED
|
@@ -801,6 +801,10 @@ const definitions = {
|
|
|
801
801
|
type: "object",
|
|
802
802
|
additionalProperties: false,
|
|
803
803
|
properties: {
|
|
804
|
+
disablable: {
|
|
805
|
+
type: "boolean",
|
|
806
|
+
title: "Disablable elements can be disabled using the disabled attribute."
|
|
807
|
+
},
|
|
804
808
|
listed: {
|
|
805
809
|
type: "boolean",
|
|
806
810
|
title: "Listed elements have a name attribute and is listed in the form and fieldset elements property."
|
|
@@ -1126,6 +1130,7 @@ function migrateElement(src) {
|
|
|
1126
1130
|
}
|
|
1127
1131
|
if (src.formAssociated) {
|
|
1128
1132
|
result.formAssociated = {
|
|
1133
|
+
disablable: Boolean(src.formAssociated.disablable),
|
|
1129
1134
|
listed: Boolean(src.formAssociated.listed)
|
|
1130
1135
|
};
|
|
1131
1136
|
} else {
|
|
@@ -1385,10 +1390,14 @@ function expandRegexValue(value) {
|
|
|
1385
1390
|
if (value instanceof RegExp) {
|
|
1386
1391
|
return value;
|
|
1387
1392
|
}
|
|
1388
|
-
const match = value.match(
|
|
1393
|
+
const match = value.match(/^\/(.*(?=\/))\/(i?)$/);
|
|
1389
1394
|
if (match) {
|
|
1390
1395
|
const [, expr, flags] = match;
|
|
1391
|
-
|
|
1396
|
+
if (expr.startsWith("^") || expr.endsWith("$")) {
|
|
1397
|
+
return new RegExp(expr, flags);
|
|
1398
|
+
} else {
|
|
1399
|
+
return new RegExp(`^${expr}$`, flags);
|
|
1400
|
+
}
|
|
1392
1401
|
} else {
|
|
1393
1402
|
return value;
|
|
1394
1403
|
}
|
|
@@ -2290,6 +2299,7 @@ class TextNode extends DOMNode {
|
|
|
2290
2299
|
}
|
|
2291
2300
|
|
|
2292
2301
|
const ROLE = Symbol("role");
|
|
2302
|
+
const TABINDEX = Symbol("tabindex");
|
|
2293
2303
|
var NodeClosed = /* @__PURE__ */ ((NodeClosed2) => {
|
|
2294
2304
|
NodeClosed2[NodeClosed2["Open"] = 0] = "Open";
|
|
2295
2305
|
NodeClosed2[NodeClosed2["EndTag"] = 1] = "EndTag";
|
|
@@ -2570,6 +2580,43 @@ class HtmlElement extends DOMNode {
|
|
|
2570
2580
|
}
|
|
2571
2581
|
this.attr[key].push(new Attribute(key, value, keyLocation, valueLocation, originalAttribute));
|
|
2572
2582
|
}
|
|
2583
|
+
/**
|
|
2584
|
+
* Get parsed tabindex for this element.
|
|
2585
|
+
*
|
|
2586
|
+
* - If `tabindex` attribute is not present `null` is returned.
|
|
2587
|
+
* - If attribute value is omitted or the empty string `null` is returned.
|
|
2588
|
+
* - If attribute value cannot be parsed `null` is returned.
|
|
2589
|
+
* - If attribute value is dynamic `0` is returned.
|
|
2590
|
+
* - Otherwise the parsed value is returned.
|
|
2591
|
+
*
|
|
2592
|
+
* This property does *NOT* take into account if the element have a default
|
|
2593
|
+
* `tabindex` (such as `<input>` have). Instead use the `focusable` metadata
|
|
2594
|
+
* property to determine this.
|
|
2595
|
+
*
|
|
2596
|
+
* @public
|
|
2597
|
+
* @since 8.16.0
|
|
2598
|
+
*/
|
|
2599
|
+
get tabIndex() {
|
|
2600
|
+
const cached = this.cacheGet(TABINDEX);
|
|
2601
|
+
if (cached !== void 0) {
|
|
2602
|
+
return cached;
|
|
2603
|
+
}
|
|
2604
|
+
const tabindex = this.getAttribute("tabindex");
|
|
2605
|
+
if (!tabindex) {
|
|
2606
|
+
return this.cacheSet(TABINDEX, null);
|
|
2607
|
+
}
|
|
2608
|
+
if (tabindex.value === null) {
|
|
2609
|
+
return this.cacheSet(TABINDEX, null);
|
|
2610
|
+
}
|
|
2611
|
+
if (tabindex.value instanceof DynamicValue) {
|
|
2612
|
+
return this.cacheSet(TABINDEX, 0);
|
|
2613
|
+
}
|
|
2614
|
+
const parsed = parseInt(tabindex.value, 10);
|
|
2615
|
+
if (isNaN(parsed)) {
|
|
2616
|
+
return this.cacheSet(TABINDEX, null);
|
|
2617
|
+
}
|
|
2618
|
+
return this.cacheSet(TABINDEX, parsed);
|
|
2619
|
+
}
|
|
2573
2620
|
/**
|
|
2574
2621
|
* Get a list of all attributes on this node.
|
|
2575
2622
|
*/
|
|
@@ -3366,6 +3413,7 @@ function isKeywordIgnored(options, keyword, matcher = (list, it) => list.include
|
|
|
3366
3413
|
|
|
3367
3414
|
const ARIA_HIDDEN_CACHE = Symbol(isAriaHidden.name);
|
|
3368
3415
|
const HTML_HIDDEN_CACHE = Symbol(isHTMLHidden.name);
|
|
3416
|
+
const INERT_CACHE = Symbol(isInert.name);
|
|
3369
3417
|
const ROLE_PRESENTATION_CACHE = Symbol(isPresentation.name);
|
|
3370
3418
|
const STYLE_HIDDEN_CACHE = Symbol(isStyleHidden.name);
|
|
3371
3419
|
function inAccessibilityTree(node) {
|
|
@@ -3378,6 +3426,9 @@ function inAccessibilityTree(node) {
|
|
|
3378
3426
|
if (isHTMLHidden(node)) {
|
|
3379
3427
|
return false;
|
|
3380
3428
|
}
|
|
3429
|
+
if (isInert(node)) {
|
|
3430
|
+
return false;
|
|
3431
|
+
}
|
|
3381
3432
|
if (isStyleHidden(node)) {
|
|
3382
3433
|
return false;
|
|
3383
3434
|
}
|
|
@@ -3419,6 +3470,24 @@ function isHTMLHidden(node, details) {
|
|
|
3419
3470
|
const result = node.cacheSet(HTML_HIDDEN_CACHE, isHTMLHiddenImpl(node));
|
|
3420
3471
|
return details ? result : result.byParent || result.bySelf;
|
|
3421
3472
|
}
|
|
3473
|
+
function isInertImpl(node) {
|
|
3474
|
+
const isInert2 = (node2) => {
|
|
3475
|
+
const inert = node2.getAttribute("inert");
|
|
3476
|
+
return inert !== null && inert.isStatic;
|
|
3477
|
+
};
|
|
3478
|
+
return {
|
|
3479
|
+
byParent: node.parent ? isInert2(node.parent) : false,
|
|
3480
|
+
bySelf: isInert2(node)
|
|
3481
|
+
};
|
|
3482
|
+
}
|
|
3483
|
+
function isInert(node, details) {
|
|
3484
|
+
const cached = node.cacheGet(INERT_CACHE);
|
|
3485
|
+
if (cached) {
|
|
3486
|
+
return details ? cached : cached.byParent || cached.bySelf;
|
|
3487
|
+
}
|
|
3488
|
+
const result = node.cacheSet(INERT_CACHE, isInertImpl(node));
|
|
3489
|
+
return details ? result : result.byParent || result.bySelf;
|
|
3490
|
+
}
|
|
3422
3491
|
function isStyleHiddenImpl(node) {
|
|
3423
3492
|
const isHidden = (node2) => {
|
|
3424
3493
|
const style = node2.getAttribute("style");
|
|
@@ -3844,7 +3913,7 @@ class Rule {
|
|
|
3844
3913
|
}
|
|
3845
3914
|
}
|
|
3846
3915
|
|
|
3847
|
-
const defaults$
|
|
3916
|
+
const defaults$x = {
|
|
3848
3917
|
allowExternal: true,
|
|
3849
3918
|
allowRelative: true,
|
|
3850
3919
|
allowAbsolute: true,
|
|
@@ -3885,7 +3954,7 @@ function matchList(value, list) {
|
|
|
3885
3954
|
}
|
|
3886
3955
|
class AllowedLinks extends Rule {
|
|
3887
3956
|
constructor(options) {
|
|
3888
|
-
super({ ...defaults$
|
|
3957
|
+
super({ ...defaults$x, ...options });
|
|
3889
3958
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
3890
3959
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
3891
3960
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -4049,7 +4118,7 @@ class AllowedLinks extends Rule {
|
|
|
4049
4118
|
}
|
|
4050
4119
|
}
|
|
4051
4120
|
|
|
4052
|
-
const defaults$
|
|
4121
|
+
const defaults$w = {
|
|
4053
4122
|
accessible: true
|
|
4054
4123
|
};
|
|
4055
4124
|
function findByTarget(target, siblings) {
|
|
@@ -4079,7 +4148,7 @@ function getDescription$1(context) {
|
|
|
4079
4148
|
}
|
|
4080
4149
|
class AreaAlt extends Rule {
|
|
4081
4150
|
constructor(options) {
|
|
4082
|
-
super({ ...defaults$
|
|
4151
|
+
super({ ...defaults$w, ...options });
|
|
4083
4152
|
}
|
|
4084
4153
|
static schema() {
|
|
4085
4154
|
return {
|
|
@@ -4158,7 +4227,7 @@ class AriaHiddenBody extends Rule {
|
|
|
4158
4227
|
}
|
|
4159
4228
|
}
|
|
4160
4229
|
|
|
4161
|
-
const defaults$
|
|
4230
|
+
const defaults$v = {
|
|
4162
4231
|
allowAnyNamable: false
|
|
4163
4232
|
};
|
|
4164
4233
|
const whitelisted = [
|
|
@@ -4200,7 +4269,7 @@ function isValidUsage(target, meta) {
|
|
|
4200
4269
|
}
|
|
4201
4270
|
class AriaLabelMisuse extends Rule {
|
|
4202
4271
|
constructor(options) {
|
|
4203
|
-
super({ ...defaults$
|
|
4272
|
+
super({ ...defaults$v, ...options });
|
|
4204
4273
|
}
|
|
4205
4274
|
documentation() {
|
|
4206
4275
|
const valid = [
|
|
@@ -4310,13 +4379,13 @@ class CaseStyle {
|
|
|
4310
4379
|
}
|
|
4311
4380
|
}
|
|
4312
4381
|
|
|
4313
|
-
const defaults$
|
|
4382
|
+
const defaults$u = {
|
|
4314
4383
|
style: "lowercase",
|
|
4315
4384
|
ignoreForeign: true
|
|
4316
4385
|
};
|
|
4317
4386
|
class AttrCase extends Rule {
|
|
4318
4387
|
constructor(options) {
|
|
4319
|
-
super({ ...defaults$
|
|
4388
|
+
super({ ...defaults$u, ...options });
|
|
4320
4389
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
4321
4390
|
}
|
|
4322
4391
|
static schema() {
|
|
@@ -4672,7 +4741,7 @@ class AttrDelimiter extends Rule {
|
|
|
4672
4741
|
}
|
|
4673
4742
|
|
|
4674
4743
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
4675
|
-
const defaults$
|
|
4744
|
+
const defaults$t = {
|
|
4676
4745
|
pattern: DEFAULT_PATTERN,
|
|
4677
4746
|
ignoreForeign: true
|
|
4678
4747
|
};
|
|
@@ -4704,7 +4773,7 @@ function generateDescription(name, pattern) {
|
|
|
4704
4773
|
}
|
|
4705
4774
|
class AttrPattern extends Rule {
|
|
4706
4775
|
constructor(options) {
|
|
4707
|
-
super({ ...defaults$
|
|
4776
|
+
super({ ...defaults$t, ...options });
|
|
4708
4777
|
this.pattern = generateRegexp(this.options.pattern);
|
|
4709
4778
|
}
|
|
4710
4779
|
static schema() {
|
|
@@ -4751,7 +4820,7 @@ class AttrPattern extends Rule {
|
|
|
4751
4820
|
}
|
|
4752
4821
|
}
|
|
4753
4822
|
|
|
4754
|
-
const defaults$
|
|
4823
|
+
const defaults$s = {
|
|
4755
4824
|
style: "auto",
|
|
4756
4825
|
unquoted: false
|
|
4757
4826
|
};
|
|
@@ -4790,7 +4859,7 @@ function describeStyle(style, unquoted) {
|
|
|
4790
4859
|
}
|
|
4791
4860
|
class AttrQuotes extends Rule {
|
|
4792
4861
|
constructor(options) {
|
|
4793
|
-
super({ ...defaults$
|
|
4862
|
+
super({ ...defaults$s, ...options });
|
|
4794
4863
|
this.style = parseStyle$3(this.options.style);
|
|
4795
4864
|
}
|
|
4796
4865
|
static schema() {
|
|
@@ -4974,12 +5043,12 @@ class AttributeAllowedValues extends Rule {
|
|
|
4974
5043
|
}
|
|
4975
5044
|
}
|
|
4976
5045
|
|
|
4977
|
-
const defaults$
|
|
5046
|
+
const defaults$r = {
|
|
4978
5047
|
style: "omit"
|
|
4979
5048
|
};
|
|
4980
5049
|
class AttributeBooleanStyle extends Rule {
|
|
4981
5050
|
constructor(options) {
|
|
4982
|
-
super({ ...defaults$
|
|
5051
|
+
super({ ...defaults$r, ...options });
|
|
4983
5052
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
4984
5053
|
}
|
|
4985
5054
|
static schema() {
|
|
@@ -5046,12 +5115,12 @@ function reportMessage$1(attr, style) {
|
|
|
5046
5115
|
return "";
|
|
5047
5116
|
}
|
|
5048
5117
|
|
|
5049
|
-
const defaults$
|
|
5118
|
+
const defaults$q = {
|
|
5050
5119
|
style: "omit"
|
|
5051
5120
|
};
|
|
5052
5121
|
class AttributeEmptyStyle extends Rule {
|
|
5053
5122
|
constructor(options) {
|
|
5054
|
-
super({ ...defaults$
|
|
5123
|
+
super({ ...defaults$q, ...options });
|
|
5055
5124
|
this.hasInvalidStyle = parseStyle$1(this.options.style);
|
|
5056
5125
|
}
|
|
5057
5126
|
static schema() {
|
|
@@ -5169,64 +5238,95 @@ class AttributeMisuse extends Rule {
|
|
|
5169
5238
|
function parsePattern(pattern) {
|
|
5170
5239
|
switch (pattern) {
|
|
5171
5240
|
case "kebabcase":
|
|
5172
|
-
return /^[a-z0-9-]
|
|
5241
|
+
return { regexp: /^[a-z0-9-]+$/, description: pattern };
|
|
5173
5242
|
case "camelcase":
|
|
5174
|
-
return /^[a-z][a-zA-Z0-9]
|
|
5243
|
+
return { regexp: /^[a-z][a-zA-Z0-9]+$/, description: pattern };
|
|
5175
5244
|
case "underscore":
|
|
5176
|
-
return /^[a-z0-9_]
|
|
5177
|
-
default:
|
|
5178
|
-
|
|
5245
|
+
return { regexp: /^[a-z0-9_]+$/, description: pattern };
|
|
5246
|
+
default: {
|
|
5247
|
+
const regexp = new RegExp(pattern);
|
|
5248
|
+
return { regexp, description: regexp.toString() };
|
|
5249
|
+
}
|
|
5179
5250
|
}
|
|
5180
5251
|
}
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5252
|
+
|
|
5253
|
+
function toArray$1(value) {
|
|
5254
|
+
return Array.isArray(value) ? value : [value];
|
|
5255
|
+
}
|
|
5256
|
+
class BasePatternRule extends Rule {
|
|
5257
|
+
/**
|
|
5258
|
+
* @param attr - Attribute holding the value.
|
|
5259
|
+
* @param options - Rule options with defaults expanded.
|
|
5260
|
+
*/
|
|
5261
|
+
constructor(attr, options) {
|
|
5262
|
+
super(options);
|
|
5263
|
+
const { pattern } = this.options;
|
|
5264
|
+
this.attr = attr;
|
|
5265
|
+
this.patterns = toArray$1(pattern).map((it) => parsePattern(it));
|
|
5266
|
+
}
|
|
5267
|
+
static schema() {
|
|
5268
|
+
return {
|
|
5269
|
+
pattern: {
|
|
5270
|
+
oneOf: [{ type: "array", items: { type: "string" }, minItems: 1 }, { type: "string" }]
|
|
5271
|
+
}
|
|
5272
|
+
};
|
|
5273
|
+
}
|
|
5274
|
+
description(context) {
|
|
5275
|
+
const { attr, patterns } = this;
|
|
5276
|
+
const { value } = context;
|
|
5277
|
+
const lead = patterns.length === 1 ? `The \`${attr}\` attribute value \`"${value}"\` does not match the configured pattern.` : `The \`${attr}\` attribute value \`"${value}"\` does not match either of the configured patterns.`;
|
|
5278
|
+
return [
|
|
5279
|
+
lead,
|
|
5280
|
+
"For consistency within the codebase the `${attr}` is required to match one or more of the following patterns:",
|
|
5281
|
+
"",
|
|
5282
|
+
...patterns.map((it) => `- \`${it.description}\``)
|
|
5283
|
+
].join("\n");
|
|
5284
|
+
}
|
|
5285
|
+
validateValue(node, value, location) {
|
|
5286
|
+
const { attr, patterns } = this;
|
|
5287
|
+
const matches = patterns.some((it) => it.regexp.test(value));
|
|
5288
|
+
if (matches) {
|
|
5289
|
+
return;
|
|
5188
5290
|
}
|
|
5189
|
-
|
|
5190
|
-
|
|
5291
|
+
const allowed = naturalJoin(patterns.map((it) => `"${it.description}"`));
|
|
5292
|
+
const message = patterns.length === 1 ? `${attr} "${value}" does not match the configured pattern ${allowed}` : `${attr} "${value}" does not match either of the configured patterns: ${allowed}`;
|
|
5293
|
+
this.report({
|
|
5294
|
+
node,
|
|
5295
|
+
message,
|
|
5296
|
+
location,
|
|
5297
|
+
context: {
|
|
5298
|
+
value
|
|
5299
|
+
}
|
|
5300
|
+
});
|
|
5191
5301
|
}
|
|
5192
5302
|
}
|
|
5193
5303
|
|
|
5194
|
-
const defaults$
|
|
5304
|
+
const defaults$p = {
|
|
5195
5305
|
pattern: "kebabcase"
|
|
5196
5306
|
};
|
|
5197
|
-
class ClassPattern extends
|
|
5307
|
+
class ClassPattern extends BasePatternRule {
|
|
5198
5308
|
constructor(options) {
|
|
5199
|
-
super({ ...defaults$
|
|
5200
|
-
this.pattern = parsePattern(this.options.pattern);
|
|
5309
|
+
super("class", { ...defaults$p, ...options });
|
|
5201
5310
|
}
|
|
5202
5311
|
static schema() {
|
|
5203
|
-
return
|
|
5204
|
-
pattern: {
|
|
5205
|
-
type: "string"
|
|
5206
|
-
}
|
|
5207
|
-
};
|
|
5312
|
+
return BasePatternRule.schema();
|
|
5208
5313
|
}
|
|
5209
|
-
documentation() {
|
|
5210
|
-
const pattern = describePattern(this.options.pattern);
|
|
5314
|
+
documentation(context) {
|
|
5211
5315
|
return {
|
|
5212
|
-
description:
|
|
5316
|
+
description: this.description(context),
|
|
5213
5317
|
url: "https://html-validate.org/rules/class-pattern.html"
|
|
5214
5318
|
};
|
|
5215
5319
|
}
|
|
5216
5320
|
setup() {
|
|
5217
5321
|
this.on("attr", (event) => {
|
|
5218
|
-
|
|
5322
|
+
const { target, key, value, valueLocation } = event;
|
|
5323
|
+
if (key.toLowerCase() !== "class") {
|
|
5219
5324
|
return;
|
|
5220
5325
|
}
|
|
5221
|
-
const classes = new DOMTokenList(
|
|
5222
|
-
classes.
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
const pattern = this.pattern.toString();
|
|
5226
|
-
const message = `Class "${cur}" does not match required pattern "${pattern}"`;
|
|
5227
|
-
this.report(event.target, message, location);
|
|
5228
|
-
}
|
|
5229
|
-
});
|
|
5326
|
+
const classes = new DOMTokenList(value, valueLocation);
|
|
5327
|
+
for (const { item, location } of classes.iterator()) {
|
|
5328
|
+
this.validateValue(target, item, location);
|
|
5329
|
+
}
|
|
5230
5330
|
});
|
|
5231
5331
|
}
|
|
5232
5332
|
}
|
|
@@ -5302,13 +5402,13 @@ class CloseOrder extends Rule {
|
|
|
5302
5402
|
}
|
|
5303
5403
|
}
|
|
5304
5404
|
|
|
5305
|
-
const defaults$
|
|
5405
|
+
const defaults$o = {
|
|
5306
5406
|
include: null,
|
|
5307
5407
|
exclude: null
|
|
5308
5408
|
};
|
|
5309
5409
|
class Deprecated extends Rule {
|
|
5310
5410
|
constructor(options) {
|
|
5311
|
-
super({ ...defaults$
|
|
5411
|
+
super({ ...defaults$o, ...options });
|
|
5312
5412
|
}
|
|
5313
5413
|
static schema() {
|
|
5314
5414
|
return {
|
|
@@ -5462,12 +5562,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
|
|
|
5462
5562
|
}
|
|
5463
5563
|
};
|
|
5464
5564
|
|
|
5465
|
-
const defaults$
|
|
5565
|
+
const defaults$n = {
|
|
5466
5566
|
style: "uppercase"
|
|
5467
5567
|
};
|
|
5468
5568
|
class DoctypeStyle extends Rule {
|
|
5469
5569
|
constructor(options) {
|
|
5470
|
-
super({ ...defaults$
|
|
5570
|
+
super({ ...defaults$n, ...options });
|
|
5471
5571
|
}
|
|
5472
5572
|
static schema() {
|
|
5473
5573
|
return {
|
|
@@ -5495,12 +5595,12 @@ class DoctypeStyle extends Rule {
|
|
|
5495
5595
|
}
|
|
5496
5596
|
}
|
|
5497
5597
|
|
|
5498
|
-
const defaults$
|
|
5598
|
+
const defaults$m = {
|
|
5499
5599
|
style: "lowercase"
|
|
5500
5600
|
};
|
|
5501
5601
|
class ElementCase extends Rule {
|
|
5502
5602
|
constructor(options) {
|
|
5503
|
-
super({ ...defaults$
|
|
5603
|
+
super({ ...defaults$m, ...options });
|
|
5504
5604
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
5505
5605
|
}
|
|
5506
5606
|
static schema() {
|
|
@@ -5560,14 +5660,14 @@ class ElementCase extends Rule {
|
|
|
5560
5660
|
}
|
|
5561
5661
|
}
|
|
5562
5662
|
|
|
5563
|
-
const defaults$
|
|
5663
|
+
const defaults$l = {
|
|
5564
5664
|
pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
|
|
5565
5665
|
whitelist: [],
|
|
5566
5666
|
blacklist: []
|
|
5567
5667
|
};
|
|
5568
5668
|
class ElementName extends Rule {
|
|
5569
5669
|
constructor(options) {
|
|
5570
|
-
super({ ...defaults$
|
|
5670
|
+
super({ ...defaults$l, ...options });
|
|
5571
5671
|
this.pattern = new RegExp(this.options.pattern);
|
|
5572
5672
|
}
|
|
5573
5673
|
static schema() {
|
|
@@ -5604,7 +5704,7 @@ class ElementName extends Rule {
|
|
|
5604
5704
|
...context.blacklist.map((cur) => `- ${cur}`)
|
|
5605
5705
|
];
|
|
5606
5706
|
}
|
|
5607
|
-
if (context.pattern !== defaults$
|
|
5707
|
+
if (context.pattern !== defaults$l.pattern) {
|
|
5608
5708
|
return [
|
|
5609
5709
|
`<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
|
|
5610
5710
|
"",
|
|
@@ -6093,7 +6193,7 @@ class EmptyTitle extends Rule {
|
|
|
6093
6193
|
}
|
|
6094
6194
|
}
|
|
6095
6195
|
|
|
6096
|
-
const defaults$
|
|
6196
|
+
const defaults$k = {
|
|
6097
6197
|
allowArrayBrackets: true,
|
|
6098
6198
|
shared: ["radio", "button", "reset", "submit"]
|
|
6099
6199
|
};
|
|
@@ -6121,7 +6221,7 @@ function getDocumentation(context) {
|
|
|
6121
6221
|
}
|
|
6122
6222
|
class FormDupName extends Rule {
|
|
6123
6223
|
constructor(options) {
|
|
6124
|
-
super({ ...defaults$
|
|
6224
|
+
super({ ...defaults$k, ...options });
|
|
6125
6225
|
}
|
|
6126
6226
|
static schema() {
|
|
6127
6227
|
return {
|
|
@@ -6270,7 +6370,7 @@ class FormDupName extends Rule {
|
|
|
6270
6370
|
}
|
|
6271
6371
|
}
|
|
6272
6372
|
|
|
6273
|
-
const defaults$
|
|
6373
|
+
const defaults$j = {
|
|
6274
6374
|
allowMultipleH1: false,
|
|
6275
6375
|
minInitialRank: "h1",
|
|
6276
6376
|
sectioningRoots: ["dialog", '[role="dialog"]', '[role="alertdialog"]']
|
|
@@ -6299,7 +6399,7 @@ function parseMaxInitial(value) {
|
|
|
6299
6399
|
}
|
|
6300
6400
|
class HeadingLevel extends Rule {
|
|
6301
6401
|
constructor(options) {
|
|
6302
|
-
super({ ...defaults$
|
|
6402
|
+
super({ ...defaults$j, ...options });
|
|
6303
6403
|
this.stack = [];
|
|
6304
6404
|
this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
|
|
6305
6405
|
this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
|
|
@@ -6450,6 +6550,46 @@ class HeadingLevel extends Rule {
|
|
|
6450
6550
|
}
|
|
6451
6551
|
}
|
|
6452
6552
|
|
|
6553
|
+
const FOCUSABLE_CACHE = Symbol(isFocusable.name);
|
|
6554
|
+
function isDisabled(element, meta) {
|
|
6555
|
+
var _a;
|
|
6556
|
+
if (!((_a = meta.formAssociated) == null ? void 0 : _a.disablable)) {
|
|
6557
|
+
return false;
|
|
6558
|
+
}
|
|
6559
|
+
const disabled = element.matches("[disabled]");
|
|
6560
|
+
if (disabled) {
|
|
6561
|
+
return true;
|
|
6562
|
+
}
|
|
6563
|
+
const fieldset = element.closest("fieldset[disabled]");
|
|
6564
|
+
if (fieldset) {
|
|
6565
|
+
return true;
|
|
6566
|
+
}
|
|
6567
|
+
return false;
|
|
6568
|
+
}
|
|
6569
|
+
function isFocusableImpl(element) {
|
|
6570
|
+
if (isHTMLHidden(element) || isInert(element) || isStyleHidden(element)) {
|
|
6571
|
+
return false;
|
|
6572
|
+
}
|
|
6573
|
+
const { tabIndex, meta } = element;
|
|
6574
|
+
if (tabIndex !== null) {
|
|
6575
|
+
return tabIndex >= 0;
|
|
6576
|
+
}
|
|
6577
|
+
if (!meta) {
|
|
6578
|
+
return false;
|
|
6579
|
+
}
|
|
6580
|
+
if (isDisabled(element, meta)) {
|
|
6581
|
+
return false;
|
|
6582
|
+
}
|
|
6583
|
+
return Boolean(meta == null ? void 0 : meta.focusable);
|
|
6584
|
+
}
|
|
6585
|
+
function isFocusable(element) {
|
|
6586
|
+
const cached = element.cacheGet(FOCUSABLE_CACHE);
|
|
6587
|
+
if (cached) {
|
|
6588
|
+
return cached;
|
|
6589
|
+
}
|
|
6590
|
+
return element.cacheSet(FOCUSABLE_CACHE, isFocusableImpl(element));
|
|
6591
|
+
}
|
|
6592
|
+
|
|
6453
6593
|
class HiddenFocusable extends Rule {
|
|
6454
6594
|
documentation(context) {
|
|
6455
6595
|
const byParent = context === "parent" ? " In this case it is being hidden by an ancestor with `aria-hidden.`" : "";
|
|
@@ -6463,7 +6603,8 @@ class HiddenFocusable extends Rule {
|
|
|
6463
6603
|
"To fix this either:",
|
|
6464
6604
|
" - Remove `aria-hidden`.",
|
|
6465
6605
|
" - Remove the element from the DOM instead.",
|
|
6466
|
-
|
|
6606
|
+
' - Use `tabindex="-1"` to remove the element from tab order.',
|
|
6607
|
+
" - Use `hidden`, `inert` or similar means to hide or disable the element."
|
|
6467
6608
|
].join("\n"),
|
|
6468
6609
|
url: "https://html-validate.org/rules/hidden-focusable.html"
|
|
6469
6610
|
};
|
|
@@ -6474,21 +6615,13 @@ class HiddenFocusable extends Rule {
|
|
|
6474
6615
|
this.on("dom:ready", (event) => {
|
|
6475
6616
|
const { document } = event;
|
|
6476
6617
|
for (const element of document.querySelectorAll(selector)) {
|
|
6477
|
-
if (
|
|
6478
|
-
|
|
6479
|
-
}
|
|
6480
|
-
if (isAriaHidden(element)) {
|
|
6481
|
-
this.validateElement(element);
|
|
6618
|
+
if (isFocusable(element) && isAriaHidden(element)) {
|
|
6619
|
+
this.reportElement(element);
|
|
6482
6620
|
}
|
|
6483
6621
|
}
|
|
6484
6622
|
});
|
|
6485
6623
|
}
|
|
6486
|
-
|
|
6487
|
-
const { meta } = element;
|
|
6488
|
-
const tabindex = element.getAttribute("tabindex");
|
|
6489
|
-
if (meta && !meta.focusable && !tabindex) {
|
|
6490
|
-
return;
|
|
6491
|
-
}
|
|
6624
|
+
reportElement(element) {
|
|
6492
6625
|
const attribute = element.getAttribute("aria-hidden");
|
|
6493
6626
|
const message = attribute ? `aria-hidden cannot be used on focusable elements` : `aria-hidden cannot be used on focusable elements (hidden by ancestor element)`;
|
|
6494
6627
|
const location = attribute ? attribute.keyLocation : element.location;
|
|
@@ -6502,43 +6635,35 @@ class HiddenFocusable extends Rule {
|
|
|
6502
6635
|
}
|
|
6503
6636
|
}
|
|
6504
6637
|
|
|
6505
|
-
const defaults$
|
|
6638
|
+
const defaults$i = {
|
|
6506
6639
|
pattern: "kebabcase"
|
|
6507
6640
|
};
|
|
6508
|
-
class IdPattern extends
|
|
6641
|
+
class IdPattern extends BasePatternRule {
|
|
6509
6642
|
constructor(options) {
|
|
6510
|
-
super({ ...defaults$
|
|
6511
|
-
this.pattern = parsePattern(this.options.pattern);
|
|
6643
|
+
super("id", { ...defaults$i, ...options });
|
|
6512
6644
|
}
|
|
6513
6645
|
static schema() {
|
|
6514
|
-
return
|
|
6515
|
-
pattern: {
|
|
6516
|
-
type: "string"
|
|
6517
|
-
}
|
|
6518
|
-
};
|
|
6646
|
+
return BasePatternRule.schema();
|
|
6519
6647
|
}
|
|
6520
|
-
documentation() {
|
|
6521
|
-
const pattern = describePattern(this.options.pattern);
|
|
6648
|
+
documentation(context) {
|
|
6522
6649
|
return {
|
|
6523
|
-
description:
|
|
6650
|
+
description: this.description(context),
|
|
6524
6651
|
url: "https://html-validate.org/rules/id-pattern.html"
|
|
6525
6652
|
};
|
|
6526
6653
|
}
|
|
6527
6654
|
setup() {
|
|
6528
6655
|
this.on("attr", (event) => {
|
|
6529
|
-
|
|
6530
|
-
if (
|
|
6656
|
+
const { target, key, value, valueLocation } = event;
|
|
6657
|
+
if (key.toLowerCase() !== "id") {
|
|
6531
6658
|
return;
|
|
6532
6659
|
}
|
|
6533
|
-
if (
|
|
6660
|
+
if (value instanceof DynamicValue) {
|
|
6534
6661
|
return;
|
|
6535
6662
|
}
|
|
6536
|
-
if (
|
|
6537
|
-
|
|
6538
|
-
const pattern = this.pattern.toString();
|
|
6539
|
-
const message = `ID "${value}" does not match required pattern "${pattern}"`;
|
|
6540
|
-
this.report(event.target, message, event.valueLocation);
|
|
6663
|
+
if (value === null) {
|
|
6664
|
+
return;
|
|
6541
6665
|
}
|
|
6666
|
+
this.validateValue(target, value, valueLocation);
|
|
6542
6667
|
});
|
|
6543
6668
|
}
|
|
6544
6669
|
}
|
|
@@ -6777,7 +6902,7 @@ class InputMissingLabel extends Rule {
|
|
|
6777
6902
|
});
|
|
6778
6903
|
}
|
|
6779
6904
|
validateInput(root, elem) {
|
|
6780
|
-
if (
|
|
6905
|
+
if (!inAccessibilityTree(elem)) {
|
|
6781
6906
|
return;
|
|
6782
6907
|
}
|
|
6783
6908
|
if (isIgnored(elem)) {
|
|
@@ -6810,7 +6935,7 @@ class InputMissingLabel extends Rule {
|
|
|
6810
6935
|
* Reports error if none of the labels are accessible.
|
|
6811
6936
|
*/
|
|
6812
6937
|
validateLabel(root, elem, labels) {
|
|
6813
|
-
const visible = labels.filter(
|
|
6938
|
+
const visible = labels.filter(inAccessibilityTree);
|
|
6814
6939
|
if (visible.length === 0) {
|
|
6815
6940
|
this.report(elem, `<${elem.tagName}> element has <label> but <label> element is hidden`);
|
|
6816
6941
|
return;
|
|
@@ -6820,10 +6945,6 @@ class InputMissingLabel extends Rule {
|
|
|
6820
6945
|
}
|
|
6821
6946
|
}
|
|
6822
6947
|
}
|
|
6823
|
-
function isVisible(elem) {
|
|
6824
|
-
const hidden = isHTMLHidden(elem) || isAriaHidden(elem);
|
|
6825
|
-
return !hidden;
|
|
6826
|
-
}
|
|
6827
6948
|
function findLabelById(root, id) {
|
|
6828
6949
|
if (!id)
|
|
6829
6950
|
return [];
|
|
@@ -6840,12 +6961,12 @@ function findLabelByParent(el) {
|
|
|
6840
6961
|
return [];
|
|
6841
6962
|
}
|
|
6842
6963
|
|
|
6843
|
-
const defaults$
|
|
6964
|
+
const defaults$h = {
|
|
6844
6965
|
maxlength: 70
|
|
6845
6966
|
};
|
|
6846
6967
|
class LongTitle extends Rule {
|
|
6847
6968
|
constructor(options) {
|
|
6848
|
-
super({ ...defaults$
|
|
6969
|
+
super({ ...defaults$h, ...options });
|
|
6849
6970
|
this.maxlength = this.options.maxlength;
|
|
6850
6971
|
}
|
|
6851
6972
|
static schema() {
|
|
@@ -6874,12 +6995,12 @@ class LongTitle extends Rule {
|
|
|
6874
6995
|
}
|
|
6875
6996
|
}
|
|
6876
6997
|
|
|
6877
|
-
const defaults$
|
|
6998
|
+
const defaults$g = {
|
|
6878
6999
|
allowLongDelay: false
|
|
6879
7000
|
};
|
|
6880
7001
|
class MetaRefresh extends Rule {
|
|
6881
7002
|
constructor(options) {
|
|
6882
|
-
super({ ...defaults$
|
|
7003
|
+
super({ ...defaults$g, ...options });
|
|
6883
7004
|
}
|
|
6884
7005
|
documentation() {
|
|
6885
7006
|
return {
|
|
@@ -7067,6 +7188,45 @@ class MultipleLabeledControls extends Rule {
|
|
|
7067
7188
|
}
|
|
7068
7189
|
}
|
|
7069
7190
|
|
|
7191
|
+
const defaults$f = {
|
|
7192
|
+
pattern: "camelcase"
|
|
7193
|
+
};
|
|
7194
|
+
class NamePattern extends BasePatternRule {
|
|
7195
|
+
constructor(options) {
|
|
7196
|
+
super("name", { ...defaults$f, ...options });
|
|
7197
|
+
}
|
|
7198
|
+
static schema() {
|
|
7199
|
+
return BasePatternRule.schema();
|
|
7200
|
+
}
|
|
7201
|
+
documentation(context) {
|
|
7202
|
+
return {
|
|
7203
|
+
description: this.description(context),
|
|
7204
|
+
url: "https://html-validate.org/rules/name-pattern.html"
|
|
7205
|
+
};
|
|
7206
|
+
}
|
|
7207
|
+
setup() {
|
|
7208
|
+
this.on("attr", (event) => {
|
|
7209
|
+
var _a;
|
|
7210
|
+
const { target, key, value, valueLocation } = event;
|
|
7211
|
+
const { meta } = target;
|
|
7212
|
+
if (!((_a = meta == null ? void 0 : meta.formAssociated) == null ? void 0 : _a.listed)) {
|
|
7213
|
+
return;
|
|
7214
|
+
}
|
|
7215
|
+
if (key.toLowerCase() !== "name") {
|
|
7216
|
+
return;
|
|
7217
|
+
}
|
|
7218
|
+
if (value instanceof DynamicValue) {
|
|
7219
|
+
return;
|
|
7220
|
+
}
|
|
7221
|
+
if (value === null) {
|
|
7222
|
+
return;
|
|
7223
|
+
}
|
|
7224
|
+
const name = value.endsWith("[]") ? value.slice(0, -2) : value;
|
|
7225
|
+
this.validateValue(target, name, valueLocation);
|
|
7226
|
+
});
|
|
7227
|
+
}
|
|
7228
|
+
}
|
|
7229
|
+
|
|
7070
7230
|
const abstractRoles = [
|
|
7071
7231
|
"command",
|
|
7072
7232
|
"composite",
|
|
@@ -10048,6 +10208,7 @@ const bundledRules = {
|
|
|
10048
10208
|
"meta-refresh": MetaRefresh,
|
|
10049
10209
|
"missing-doctype": MissingDoctype,
|
|
10050
10210
|
"multiple-labeled-controls": MultipleLabeledControls,
|
|
10211
|
+
"name-pattern": NamePattern,
|
|
10051
10212
|
"no-abstract-role": NoAbstractRole,
|
|
10052
10213
|
"no-autoplay": NoAutoplay,
|
|
10053
10214
|
"no-conditional-comment": NoConditionalComment,
|
|
@@ -12488,7 +12649,7 @@ class HtmlValidate {
|
|
|
12488
12649
|
}
|
|
12489
12650
|
|
|
12490
12651
|
const name = "html-validate";
|
|
12491
|
-
const version = "8.
|
|
12652
|
+
const version = "8.17.0";
|
|
12492
12653
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
12493
12654
|
|
|
12494
12655
|
function definePlugin(plugin) {
|