@qualweb/act-rules 0.7.7 → 0.8.1
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/rules/QW-ACT-R30.js
CHANGED
|
@@ -17,7 +17,7 @@ class QW_ACT_R30 extends AtomicRule_object_1.AtomicRule {
|
|
|
17
17
|
execute(element) {
|
|
18
18
|
const test = new evaluation_1.Test();
|
|
19
19
|
const accessibleName = window.AccessibilityUtils.getAccessibleName(element);
|
|
20
|
-
const elementText = window.DomUtils.
|
|
20
|
+
const elementText = window.DomUtils.getVisibleText(element);
|
|
21
21
|
const isIconValue = this.isIcon(elementText, accessibleName, element);
|
|
22
22
|
if (accessibleName === undefined) {
|
|
23
23
|
test.verdict = evaluation_1.Verdict.FAILED;
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import type { QWElement } from '@qualweb/qw-element';
|
|
2
2
|
import { AtomicRule } from '../lib/AtomicRule.object';
|
|
3
3
|
declare class QW_ACT_R37 extends AtomicRule {
|
|
4
|
+
private disabledSelectorCache?;
|
|
4
5
|
execute(element: QWElement): void;
|
|
5
|
-
private
|
|
6
|
+
private getDisabledSelectors;
|
|
7
|
+
private isDisabledGroup;
|
|
8
|
+
private hasDisqualifyingShadow;
|
|
9
|
+
private splitShadowLayers;
|
|
10
|
+
private shadowLengthToPx;
|
|
11
|
+
private resolveSolidBackground;
|
|
12
|
+
private getBackground;
|
|
13
|
+
private isImage;
|
|
6
14
|
private evaluateGradient;
|
|
15
|
+
private gradientStopsToCheck;
|
|
7
16
|
private parseGradientString;
|
|
8
17
|
private getColorInGradient;
|
|
9
|
-
private getTextSize;
|
|
10
|
-
private getBackground;
|
|
11
|
-
private isImage;
|
|
12
18
|
private parseRGBString;
|
|
13
19
|
private flattenColors;
|
|
14
20
|
private getContrast;
|
|
@@ -16,6 +22,10 @@ declare class QW_ACT_R37 extends AtomicRule {
|
|
|
16
22
|
private hasValidContrastRatio;
|
|
17
23
|
private isBold;
|
|
18
24
|
private equals;
|
|
25
|
+
private isFullyTransparent;
|
|
26
|
+
private isHumanLanguage;
|
|
27
|
+
private getTextSize;
|
|
28
|
+
private emit;
|
|
19
29
|
}
|
|
20
30
|
export { QW_ACT_R37 };
|
|
21
31
|
//# sourceMappingURL=QW-ACT-R37.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QW-ACT-R37.d.ts","sourceRoot":"","sources":["../../src/rules/QW-ACT-R37.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAQrD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"QW-ACT-R37.d.ts","sourceRoot":"","sources":["../../src/rules/QW-ACT-R37.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAQrD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AA2BtD,cAAM,UAAW,SAAQ,UAAU;IAEjC,OAAO,CAAC,qBAAqB,CAAC,CAA8C;IAM5E,OAAO,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI;IA+FjC,OAAO,CAAC,oBAAoB;IA6B5B,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,gBAAgB;IAgDxB,OAAO,CAAC,oBAAoB;IA2B5B,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,cAAc;IAiCtB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,IAAI;CAMb;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/rules/QW-ACT-R37.js
CHANGED
|
@@ -17,172 +17,233 @@ const applicability_1 = require("@qualweb/util/applicability");
|
|
|
17
17
|
const evaluation_1 = require("@qualweb/core/evaluation");
|
|
18
18
|
const AtomicRule_object_1 = require("../lib/AtomicRule.object");
|
|
19
19
|
const colorjs_io_1 = __importDefault(require("colorjs.io"));
|
|
20
|
+
const WHITE = { red: 255, green: 255, blue: 255, alpha: 1 };
|
|
21
|
+
const INPUT_TAGS = ['input', 'select', 'textarea'];
|
|
22
|
+
const GRADIENT_REGEX = /((\w-?)*gradient.*)/gm;
|
|
20
23
|
class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
|
|
21
24
|
execute(element) {
|
|
22
25
|
var _a;
|
|
23
|
-
|
|
24
|
-
if (!visible)
|
|
26
|
+
if (!window.DomUtils.isElementVisible(element))
|
|
25
27
|
return;
|
|
26
28
|
const nodeName = element.getElementTagName();
|
|
27
|
-
const isInputField =
|
|
29
|
+
const isInputField = INPUT_TAGS.includes(nodeName);
|
|
28
30
|
const elementText = element.getElementOwnText().trim();
|
|
29
31
|
const placeholder = (_a = element.getElementAttribute('placeholder')) === null || _a === void 0 ? void 0 : _a.trim();
|
|
30
|
-
if (elementText === '' && !isInputField && !placeholder)
|
|
32
|
+
if (elementText === '' && !isInputField && !placeholder)
|
|
31
33
|
return;
|
|
32
|
-
}
|
|
33
34
|
if (!element.isElementHTMLElement())
|
|
34
35
|
return;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (disableWidget && selectors && selectors.includes(elementSelectors))
|
|
41
|
-
return;
|
|
42
|
-
if (disableWidget.getElementSelector() === elementSelectors)
|
|
43
|
-
return;
|
|
44
|
-
const children = disableWidget.getElementChildren();
|
|
45
|
-
if (children) {
|
|
46
|
-
for (const child of children) {
|
|
47
|
-
if (child.getElementSelector() === elementSelectors)
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const role = window.AccessibilityUtils.getElementRole(element);
|
|
53
|
-
if (role === 'group') {
|
|
54
|
-
const disable = element.getElementAttribute('disabled') !== null;
|
|
55
|
-
const ariaDisable = element.getElementAttribute('aria-disabled') !== null;
|
|
56
|
-
if (disable || ariaDisable)
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const test = new evaluation_1.Test();
|
|
36
|
+
const elementSelector = element.getElementSelector();
|
|
37
|
+
if (this.getDisabledSelectors(window.disabledWidgets).has(elementSelector))
|
|
38
|
+
return;
|
|
39
|
+
if (this.isDisabledGroup(element))
|
|
40
|
+
return;
|
|
60
41
|
const fgColor = element.getElementStyleProperty('color', null);
|
|
61
|
-
|
|
42
|
+
const bgColor = this.getBackground(element);
|
|
62
43
|
const opacity = parseFloat(element.getElementStyleProperty('opacity', null) || '1');
|
|
63
44
|
const fontSize = element.getElementStyleProperty('font-size', null);
|
|
64
45
|
const fontWeight = element.getElementStyleProperty('font-weight', null);
|
|
65
46
|
const fontFamily = element.getElementStyleProperty('font-family', null);
|
|
66
47
|
const fontStyle = element.getElementStyleProperty('font-style', null);
|
|
67
48
|
const textShadow = element.getElementStyleProperty('text-shadow', null);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const vs = Math.abs(parseInt(pixelValues[1].replace('px', ''), 10));
|
|
73
|
-
const blur = parseInt(pixelValues[2].replace('px', ''), 10);
|
|
74
|
-
if (blur > 0 || hs > 1 || vs > 1) {
|
|
75
|
-
test.verdict = evaluation_1.Verdict.WARNING;
|
|
76
|
-
test.resultCode = 'W1';
|
|
77
|
-
test.addElement(element);
|
|
78
|
-
this.addTestResult(test);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
49
|
+
const test = new evaluation_1.Test();
|
|
50
|
+
if (this.hasDisqualifyingShadow(textShadow, parseFloat(fontSize))) {
|
|
51
|
+
this.emit(test, element, evaluation_1.Verdict.WARNING, 'W1');
|
|
52
|
+
return;
|
|
82
53
|
}
|
|
83
54
|
if (this.isImage(bgColor)) {
|
|
84
|
-
test
|
|
85
|
-
test.resultCode = 'W2';
|
|
86
|
-
test.addElement(element);
|
|
87
|
-
this.addTestResult(test);
|
|
55
|
+
this.emit(test, element, evaluation_1.Verdict.WARNING, 'W2');
|
|
88
56
|
return;
|
|
89
57
|
}
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (this.isHumanLanguage(
|
|
94
|
-
this.evaluateGradient(test, element,
|
|
58
|
+
const gradientMatch = bgColor.match(GRADIENT_REGEX);
|
|
59
|
+
if (gradientMatch) {
|
|
60
|
+
const text = elementText || placeholder || '';
|
|
61
|
+
if (this.isHumanLanguage(text)) {
|
|
62
|
+
this.evaluateGradient(test, element, gradientMatch[0], fgColor, opacity, fontSize, fontWeight, fontStyle, fontFamily, text);
|
|
95
63
|
}
|
|
96
64
|
else {
|
|
97
|
-
test
|
|
98
|
-
test.resultCode = 'P2';
|
|
99
|
-
test.addElement(element);
|
|
100
|
-
this.addTestResult(test);
|
|
65
|
+
this.emit(test, element, evaluation_1.Verdict.PASSED, 'P2');
|
|
101
66
|
}
|
|
102
67
|
return;
|
|
103
68
|
}
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
while (!parsedBG || (parsedBG.red === 0 && parsedBG.green === 0 && parsedBG.blue === 0 && parsedBG.alpha === 0)) {
|
|
109
|
-
const parent = elementAux.getElementParent();
|
|
110
|
-
if (!parent)
|
|
111
|
-
break;
|
|
112
|
-
const parentOpacity = parseFloat(parent.getElementStyleProperty('opacity', null) || '1');
|
|
113
|
-
parsedBG = this.parseRGBString(this.getBackground(parent));
|
|
114
|
-
if (parsedBG)
|
|
115
|
-
parsedBG.alpha *= parentOpacity;
|
|
116
|
-
elementAux = parent;
|
|
69
|
+
const background = this.resolveSolidBackground(element, opacity);
|
|
70
|
+
if (background.kind === 'cantTell') {
|
|
71
|
+
this.emit(test, element, evaluation_1.Verdict.WARNING, background.resultCode);
|
|
72
|
+
return;
|
|
117
73
|
}
|
|
118
|
-
|
|
119
|
-
|
|
74
|
+
const parsedBG = background.color;
|
|
75
|
+
const parsedFG = this.parseRGBString(fgColor);
|
|
76
|
+
if (!parsedFG)
|
|
77
|
+
return;
|
|
78
|
+
parsedFG.alpha *= opacity;
|
|
79
|
+
if (this.equals(parsedBG, parsedFG))
|
|
80
|
+
return;
|
|
81
|
+
const textToVerify = elementText || placeholder || '';
|
|
82
|
+
if (!this.isHumanLanguage(textToVerify)) {
|
|
83
|
+
this.emit(test, element, evaluation_1.Verdict.PASSED, 'P2');
|
|
84
|
+
return;
|
|
120
85
|
}
|
|
121
|
-
|
|
122
|
-
|
|
86
|
+
const contrastRatio = this.getContrast(parsedBG, parsedFG);
|
|
87
|
+
const isValid = this.hasValidContrastRatio(contrastRatio, fontSize, this.isBold(fontWeight));
|
|
88
|
+
this.emit(test, element, isValid ? evaluation_1.Verdict.PASSED : evaluation_1.Verdict.FAILED, isValid ? 'P1' : 'F1');
|
|
89
|
+
}
|
|
90
|
+
getDisabledSelectors(widgets) {
|
|
91
|
+
var _a;
|
|
92
|
+
const cache = this.disabledSelectorCache;
|
|
93
|
+
if (cache && cache.source === widgets) {
|
|
94
|
+
return cache.selectors;
|
|
123
95
|
}
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
else {
|
|
138
|
-
test.verdict = evaluation_1.Verdict.PASSED;
|
|
139
|
-
test.resultCode = 'P2';
|
|
140
|
-
test.addElement(element);
|
|
141
|
-
this.addTestResult(test);
|
|
142
|
-
}
|
|
96
|
+
const selectors = new Set();
|
|
97
|
+
for (const widget of widgets !== null && widgets !== void 0 ? widgets : []) {
|
|
98
|
+
const accNameSelectors = window.AccessibilityUtils.getAccessibleNameSelector(widget);
|
|
99
|
+
if (typeof accNameSelectors === 'string') {
|
|
100
|
+
selectors.add(accNameSelectors);
|
|
101
|
+
}
|
|
102
|
+
else if (Array.isArray(accNameSelectors)) {
|
|
103
|
+
for (const selector of accNameSelectors)
|
|
104
|
+
selectors.add(selector);
|
|
105
|
+
}
|
|
106
|
+
selectors.add(widget.getElementSelector());
|
|
107
|
+
for (const child of (_a = widget.getElementChildren()) !== null && _a !== void 0 ? _a : []) {
|
|
108
|
+
selectors.add(child.getElementSelector());
|
|
143
109
|
}
|
|
144
110
|
}
|
|
111
|
+
this.disabledSelectorCache = { source: widgets, selectors };
|
|
112
|
+
return selectors;
|
|
145
113
|
}
|
|
146
|
-
|
|
147
|
-
|
|
114
|
+
isDisabledGroup(element) {
|
|
115
|
+
if (window.AccessibilityUtils.getElementRole(element) !== 'group')
|
|
116
|
+
return false;
|
|
117
|
+
const disabled = element.getElementAttribute('disabled') !== null;
|
|
118
|
+
const ariaDisabled = element.getElementAttribute('aria-disabled') === 'true';
|
|
119
|
+
return disabled || ariaDisabled;
|
|
148
120
|
}
|
|
149
|
-
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
121
|
+
hasDisqualifyingShadow(textShadow, fontSizePx) {
|
|
122
|
+
if (!textShadow)
|
|
123
|
+
return false;
|
|
124
|
+
const trimmed = textShadow.trim();
|
|
125
|
+
if (trimmed === '' || trimmed === 'none')
|
|
126
|
+
return false;
|
|
127
|
+
for (const layer of this.splitShadowLayers(trimmed)) {
|
|
128
|
+
const lengths = layer.match(/-?\d*\.?\d+(px|rem|em)/g);
|
|
129
|
+
if (!lengths || lengths.length < 2)
|
|
130
|
+
continue;
|
|
131
|
+
const horizontal = Math.abs(this.shadowLengthToPx(lengths[0], fontSizePx));
|
|
132
|
+
const vertical = Math.abs(this.shadowLengthToPx(lengths[1], fontSizePx));
|
|
133
|
+
const blur = lengths[2] ? this.shadowLengthToPx(lengths[2], fontSizePx) : 0;
|
|
134
|
+
if (blur > 0 || horizontal > 1 || vertical > 1)
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
splitShadowLayers(value) {
|
|
140
|
+
const layers = [];
|
|
141
|
+
let depth = 0;
|
|
142
|
+
let current = '';
|
|
143
|
+
for (const char of value) {
|
|
144
|
+
if (char === '(')
|
|
145
|
+
depth++;
|
|
146
|
+
else if (char === ')')
|
|
147
|
+
depth = Math.max(0, depth - 1);
|
|
148
|
+
if (char === ',' && depth === 0) {
|
|
149
|
+
layers.push(current.trim());
|
|
150
|
+
current = '';
|
|
164
151
|
}
|
|
165
152
|
else {
|
|
166
|
-
|
|
167
|
-
isValid = isValid && this.hasValidContrastRatio(this.getContrast(color, parsedFG), fontSize, this.isBold(fontWeight));
|
|
168
|
-
}
|
|
153
|
+
current += char;
|
|
169
154
|
}
|
|
170
|
-
test.verdict = isValid ? evaluation_1.Verdict.PASSED : evaluation_1.Verdict.FAILED;
|
|
171
|
-
test.resultCode = isValid ? 'P3' : 'F2';
|
|
172
155
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
156
|
+
if (current.trim())
|
|
157
|
+
layers.push(current.trim());
|
|
158
|
+
return layers;
|
|
159
|
+
}
|
|
160
|
+
shadowLengthToPx(length, fontSizePx) {
|
|
161
|
+
const numeric = parseFloat(length);
|
|
162
|
+
if (length.endsWith('rem'))
|
|
163
|
+
return numeric * 16;
|
|
164
|
+
if (length.endsWith('em'))
|
|
165
|
+
return numeric * (Number.isFinite(fontSizePx) ? fontSizePx : 16);
|
|
166
|
+
return numeric;
|
|
167
|
+
}
|
|
168
|
+
resolveSolidBackground(element, opacity) {
|
|
169
|
+
let parsed = this.parseRGBString(this.getBackground(element));
|
|
170
|
+
if (parsed)
|
|
171
|
+
parsed.alpha *= opacity;
|
|
172
|
+
let current = element;
|
|
173
|
+
while (!parsed || this.isFullyTransparent(parsed)) {
|
|
174
|
+
const parent = current.getElementParent();
|
|
175
|
+
if (!parent)
|
|
176
|
+
break;
|
|
177
|
+
const parentBg = this.getBackground(parent);
|
|
178
|
+
if (this.isImage(parentBg))
|
|
179
|
+
return { kind: 'cantTell', resultCode: 'W2' };
|
|
180
|
+
if (parentBg.match(GRADIENT_REGEX))
|
|
181
|
+
return { kind: 'cantTell', resultCode: 'W3' };
|
|
182
|
+
const parentOpacity = parseFloat(parent.getElementStyleProperty('opacity', null) || '1');
|
|
183
|
+
parsed = this.parseRGBString(parentBg);
|
|
184
|
+
if (parsed)
|
|
185
|
+
parsed.alpha *= parentOpacity;
|
|
186
|
+
current = parent;
|
|
176
187
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
188
|
+
if (!parsed || parsed.alpha === 0) {
|
|
189
|
+
parsed = Object.assign({}, WHITE);
|
|
190
|
+
}
|
|
191
|
+
if (parsed.alpha < 1) {
|
|
192
|
+
parsed = this.flattenColors(parsed, WHITE);
|
|
193
|
+
}
|
|
194
|
+
return { kind: 'color', color: parsed };
|
|
195
|
+
}
|
|
196
|
+
getBackground(element) {
|
|
197
|
+
const bgImg = element.getElementStyleProperty('background-image', null);
|
|
198
|
+
if (bgImg && bgImg !== 'none' && bgImg !== '')
|
|
199
|
+
return bgImg;
|
|
200
|
+
const bgColor = element.getElementStyleProperty('background-color', null);
|
|
201
|
+
return bgColor && bgColor !== '' && bgColor !== 'transparent'
|
|
202
|
+
? bgColor
|
|
203
|
+
: element.getElementStyleProperty('background', null);
|
|
204
|
+
}
|
|
205
|
+
isImage(background) {
|
|
206
|
+
const lower = background.toLowerCase();
|
|
207
|
+
return lower.includes('.jpg') || lower.includes('.png') || lower.includes('.svg') || lower.includes('url(');
|
|
208
|
+
}
|
|
209
|
+
evaluateGradient(test, element, parsedGradientString, fgColor, opacity, fontSize, fontWeight, fontStyle, fontFamily, elementText) {
|
|
210
|
+
if (!parsedGradientString.startsWith('linear-gradient')) {
|
|
211
|
+
this.emit(test, element, evaluation_1.Verdict.WARNING, 'W3');
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const colors = this.parseGradientString(parsedGradientString);
|
|
215
|
+
if (colors.length === 0) {
|
|
216
|
+
this.emit(test, element, evaluation_1.Verdict.WARNING, 'W3');
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const parsedFG = this.parseRGBString(fgColor);
|
|
220
|
+
if (!parsedFG) {
|
|
221
|
+
this.emit(test, element, evaluation_1.Verdict.WARNING, 'W3');
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
parsedFG.alpha *= opacity;
|
|
225
|
+
const bold = this.isBold(fontWeight);
|
|
226
|
+
const stopsToCheck = this.gradientStopsToCheck(element, colors, fontSize, fontWeight, fontStyle, fontFamily, elementText);
|
|
227
|
+
const isValid = stopsToCheck.every((stop) => this.hasValidContrastRatio(this.getContrast(stop, parsedFG), fontSize, bold));
|
|
228
|
+
this.emit(test, element, isValid ? evaluation_1.Verdict.PASSED : evaluation_1.Verdict.FAILED, isValid ? 'P3' : 'F2');
|
|
229
|
+
}
|
|
230
|
+
gradientStopsToCheck(element, colors, fontSize, fontWeight, fontStyle, fontFamily, elementText) {
|
|
231
|
+
var _a;
|
|
232
|
+
const textSize = this.getTextSize(fontFamily.toLowerCase().replace(/['"]+/g, ''), parseInt(fontSize.replace('px', ''), 10), this.isBold(fontWeight), fontStyle.toLowerCase().includes('italic'), elementText);
|
|
233
|
+
const elementWidth = parseInt(((_a = element.getElementStyleProperty('width', null)) !== null && _a !== void 0 ? _a : '').replace('px', ''), 10);
|
|
234
|
+
if (textSize !== -1 && Number.isFinite(elementWidth) && elementWidth > 0) {
|
|
235
|
+
const lastCharRatio = textSize / elementWidth;
|
|
236
|
+
const lastCharColor = this.getColorInGradient(colors[0], colors[colors.length - 1], lastCharRatio);
|
|
237
|
+
return [colors[0], lastCharColor];
|
|
238
|
+
}
|
|
239
|
+
return colors;
|
|
180
240
|
}
|
|
181
241
|
parseGradientString(gradient) {
|
|
242
|
+
var _a;
|
|
182
243
|
const regex = /rgb(a?)\((\d+), (\d+), (\d+)+(, +(\d)+)?\)/gm;
|
|
183
|
-
const
|
|
244
|
+
const matches = (_a = gradient.match(regex)) !== null && _a !== void 0 ? _a : [];
|
|
184
245
|
const colors = [];
|
|
185
|
-
for (const stringColor of
|
|
246
|
+
for (const stringColor of matches) {
|
|
186
247
|
const parsed = this.parseRGBString(stringColor);
|
|
187
248
|
if (parsed)
|
|
188
249
|
colors.push(parsed);
|
|
@@ -190,48 +251,42 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
|
|
|
190
251
|
return colors;
|
|
191
252
|
}
|
|
192
253
|
getColorInGradient(fromColor, toColor, ratio) {
|
|
254
|
+
const r = Math.max(0, Math.min(1, ratio));
|
|
193
255
|
return {
|
|
194
|
-
red: fromColor.red + (toColor.red - fromColor.red) *
|
|
195
|
-
green: fromColor.green + (toColor.green - fromColor.green) *
|
|
196
|
-
blue: fromColor.blue + (toColor.blue - fromColor.blue) *
|
|
256
|
+
red: fromColor.red + (toColor.red - fromColor.red) * r,
|
|
257
|
+
green: fromColor.green + (toColor.green - fromColor.green) * r,
|
|
258
|
+
blue: fromColor.blue + (toColor.blue - fromColor.blue) * r,
|
|
197
259
|
alpha: 1
|
|
198
260
|
};
|
|
199
261
|
}
|
|
200
|
-
getTextSize(font, fontSize, bold, italic, text) {
|
|
201
|
-
return window.DomUtils.getTextSize(font, fontSize, bold, italic, text);
|
|
202
|
-
}
|
|
203
|
-
getBackground(element) {
|
|
204
|
-
const bgImg = element.getElementStyleProperty('background-image', null);
|
|
205
|
-
if (bgImg && bgImg !== 'none' && bgImg !== '')
|
|
206
|
-
return bgImg;
|
|
207
|
-
const bgColor = element.getElementStyleProperty('background-color', null);
|
|
208
|
-
return (bgColor && bgColor !== '' && bgColor !== 'transparent') ? bgColor : element.getElementStyleProperty('background', null);
|
|
209
|
-
}
|
|
210
|
-
isImage(s) {
|
|
211
|
-
const lower = s.toLowerCase();
|
|
212
|
-
return lower.includes('.jpg') || lower.includes('.png') || lower.includes('.svg') || lower.includes('url(');
|
|
213
|
-
}
|
|
214
262
|
parseRGBString(colorString) {
|
|
215
263
|
var _a;
|
|
216
|
-
if (!colorString || colorString === 'transparent' || colorString === 'none')
|
|
264
|
+
if (!colorString || colorString === 'transparent' || colorString === 'none') {
|
|
217
265
|
return { red: 0, green: 0, blue: 0, alpha: 0 };
|
|
266
|
+
}
|
|
218
267
|
const rgb = colorString.match(/^rgb\((\d+), (\d+), (\d+)\)/);
|
|
219
|
-
if (rgb)
|
|
220
|
-
return { red: parseInt(rgb[1]), green: parseInt(rgb[2]), blue: parseInt(rgb[3]), alpha: 1.0 };
|
|
268
|
+
if (rgb) {
|
|
269
|
+
return { red: parseInt(rgb[1], 10), green: parseInt(rgb[2], 10), blue: parseInt(rgb[3], 10), alpha: 1.0 };
|
|
270
|
+
}
|
|
221
271
|
const rgba = colorString.match(/^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/);
|
|
222
|
-
if (rgba)
|
|
223
|
-
return {
|
|
272
|
+
if (rgba) {
|
|
273
|
+
return {
|
|
274
|
+
red: parseInt(rgba[1], 10),
|
|
275
|
+
green: parseInt(rgba[2], 10),
|
|
276
|
+
blue: parseInt(rgba[3], 10),
|
|
277
|
+
alpha: Math.round(parseFloat(rgba[4]) * 100) / 100
|
|
278
|
+
};
|
|
279
|
+
}
|
|
224
280
|
try {
|
|
225
|
-
const
|
|
226
|
-
const srgb = color.to('srgb');
|
|
281
|
+
const srgb = new colorjs_io_1.default(colorString).to('srgb');
|
|
227
282
|
return {
|
|
228
283
|
red: Math.round(srgb.coords[0] * 255),
|
|
229
284
|
green: Math.round(srgb.coords[1] * 255),
|
|
230
285
|
blue: Math.round(srgb.coords[2] * 255),
|
|
231
|
-
alpha: (_a =
|
|
286
|
+
alpha: (_a = srgb.alpha) !== null && _a !== void 0 ? _a : 1
|
|
232
287
|
};
|
|
233
288
|
}
|
|
234
|
-
catch (
|
|
289
|
+
catch (_b) {
|
|
235
290
|
return undefined;
|
|
236
291
|
}
|
|
237
292
|
}
|
|
@@ -251,16 +306,16 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
|
|
|
251
306
|
return (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05);
|
|
252
307
|
}
|
|
253
308
|
getLuminance(c) {
|
|
254
|
-
const
|
|
255
|
-
v
|
|
309
|
+
const [r, g, b] = [c.red, c.green, c.blue].map((value) => {
|
|
310
|
+
const v = value / 255;
|
|
256
311
|
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
257
312
|
});
|
|
258
|
-
return
|
|
313
|
+
return r * 0.2126 + g * 0.7152 + b * 0.0722;
|
|
259
314
|
}
|
|
260
315
|
hasValidContrastRatio(contrast, fontSize, isBold) {
|
|
261
316
|
const size = parseFloat(fontSize);
|
|
262
317
|
const threshold = (isBold && size >= 18.66) || size >= 24 ? 3 : 4.5;
|
|
263
|
-
return
|
|
318
|
+
return contrast + 0.02 >= threshold;
|
|
264
319
|
}
|
|
265
320
|
isBold(fontWeight) {
|
|
266
321
|
return !!fontWeight && ['bold', 'bolder', '700', '800', '900'].includes(fontWeight);
|
|
@@ -268,6 +323,21 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
|
|
|
268
323
|
equals(c1, c2) {
|
|
269
324
|
return c1.red === c2.red && c1.green === c2.green && c1.blue === c2.blue && c1.alpha === c2.alpha;
|
|
270
325
|
}
|
|
326
|
+
isFullyTransparent(c) {
|
|
327
|
+
return c.red === 0 && c.green === 0 && c.blue === 0 && c.alpha === 0;
|
|
328
|
+
}
|
|
329
|
+
isHumanLanguage(text) {
|
|
330
|
+
return window.DomUtils.isHumanLanguage(text);
|
|
331
|
+
}
|
|
332
|
+
getTextSize(font, fontSize, bold, italic, text) {
|
|
333
|
+
return window.DomUtils.getTextSize(font, fontSize, bold, italic, text);
|
|
334
|
+
}
|
|
335
|
+
emit(test, element, verdict, resultCode) {
|
|
336
|
+
test.verdict = verdict;
|
|
337
|
+
test.resultCode = resultCode;
|
|
338
|
+
test.addElement(element);
|
|
339
|
+
this.addTestResult(test);
|
|
340
|
+
}
|
|
271
341
|
}
|
|
272
342
|
exports.QW_ACT_R37 = QW_ACT_R37;
|
|
273
343
|
__decorate([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qualweb/act-rules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "ACT rules module for qualweb web accessibility evaluator",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/*"
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"author": "João Vicente",
|
|
46
46
|
"license": "ISC",
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@qualweb/core": "^0.8.
|
|
48
|
+
"@qualweb/core": "^0.8.12",
|
|
49
49
|
"@qualweb/locale": "0.2.2",
|
|
50
|
-
"@qualweb/qw-page": "0.3.
|
|
51
|
-
"@qualweb/util": "0.
|
|
50
|
+
"@qualweb/qw-page": "0.3.7",
|
|
51
|
+
"@qualweb/util": "0.7.1",
|
|
52
52
|
"@tsconfig/recommended": "^1.0.3",
|
|
53
53
|
"@types/mocha": "^10.0.6",
|
|
54
54
|
"@types/node": "^16.11.11",
|