@qualweb/act-rules 0.7.4 → 0.7.6
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.
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import type { QWElement } from '@qualweb/qw-element';
|
|
2
|
-
import { Test } from '@qualweb/core/evaluation';
|
|
3
2
|
import { AtomicRule } from '../lib/AtomicRule.object';
|
|
4
3
|
declare class QW_ACT_R37 extends AtomicRule {
|
|
5
4
|
execute(element: QWElement): void;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
parseRGBString
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
private isHumanLanguage;
|
|
6
|
+
private evaluateGradient;
|
|
7
|
+
private parseGradientString;
|
|
8
|
+
private getColorInGradient;
|
|
9
|
+
private getTextSize;
|
|
10
|
+
private getBackground;
|
|
11
|
+
private isImage;
|
|
12
|
+
private parseRGBString;
|
|
13
|
+
private flattenColors;
|
|
14
|
+
private getContrast;
|
|
15
|
+
private getLuminance;
|
|
16
|
+
private hasValidContrastRatio;
|
|
16
17
|
private isBold;
|
|
17
|
-
|
|
18
|
-
hasValidContrastRatio(contrast: number, fontSize: string, isBold: boolean): boolean;
|
|
19
|
-
getTextSize(font: string, fontSize: number, bold: boolean, italic: boolean, text: string): number;
|
|
20
|
-
getColorInGradient(fromColor: any, toColor: any, ratio: number): any;
|
|
18
|
+
private equals;
|
|
21
19
|
}
|
|
22
20
|
export { QW_ACT_R37 };
|
|
23
21
|
//# 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,
|
|
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;AAUtD,cAAM,UAAW,SAAQ,UAAU;IAKjC,OAAO,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI;IAqIjC,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,gBAAgB;IA+BxB,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,MAAM;CAGf;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/rules/QW-ACT-R37.js
CHANGED
|
@@ -8,42 +8,44 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
exports.QW_ACT_R37 = void 0;
|
|
13
16
|
const applicability_1 = require("@qualweb/util/applicability");
|
|
14
17
|
const evaluation_1 = require("@qualweb/core/evaluation");
|
|
15
18
|
const AtomicRule_object_1 = require("../lib/AtomicRule.object");
|
|
19
|
+
const colorjs_io_1 = __importDefault(require("colorjs.io"));
|
|
16
20
|
class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
|
|
17
21
|
execute(element) {
|
|
22
|
+
var _a;
|
|
18
23
|
const visible = window.DomUtils.isElementVisible(element);
|
|
19
|
-
if (!visible)
|
|
24
|
+
if (!visible)
|
|
20
25
|
return;
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
const elementText = element.getElementOwnText();
|
|
24
|
-
|
|
26
|
+
const nodeName = element.getElementTagName();
|
|
27
|
+
const isInputField = ['input', 'select', 'textarea'].includes(nodeName);
|
|
28
|
+
const elementText = element.getElementOwnText().trim();
|
|
29
|
+
const placeholder = (_a = element.getElementAttribute('placeholder')) === null || _a === void 0 ? void 0 : _a.trim();
|
|
30
|
+
if (elementText === '' && !isInputField && !placeholder) {
|
|
25
31
|
return;
|
|
26
32
|
}
|
|
27
|
-
|
|
28
|
-
if (!isHTML) {
|
|
33
|
+
if (!element.isElementHTMLElement())
|
|
29
34
|
return;
|
|
30
|
-
}
|
|
31
35
|
const disabledWidgets = window.disabledWidgets;
|
|
32
36
|
const elementSelectors = element.getElementSelector();
|
|
33
37
|
for (const disableWidget of disabledWidgets || []) {
|
|
34
|
-
const
|
|
35
|
-
|
|
38
|
+
const selectorsResult = window.AccessibilityUtils.getAccessibleNameSelector(disableWidget);
|
|
39
|
+
const selectors = typeof selectorsResult === 'string' ? [selectorsResult] : selectorsResult;
|
|
40
|
+
if (disableWidget && selectors && selectors.includes(elementSelectors))
|
|
36
41
|
return;
|
|
37
|
-
|
|
38
|
-
if (disableWidget.getElementSelector() === elementSelectors) {
|
|
42
|
+
if (disableWidget.getElementSelector() === elementSelectors)
|
|
39
43
|
return;
|
|
40
|
-
}
|
|
41
44
|
const children = disableWidget.getElementChildren();
|
|
42
45
|
if (children) {
|
|
43
46
|
for (const child of children) {
|
|
44
|
-
if (child.getElementSelector() === elementSelectors)
|
|
47
|
+
if (child.getElementSelector() === elementSelectors)
|
|
45
48
|
return;
|
|
46
|
-
}
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
}
|
|
@@ -51,27 +53,25 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
|
|
|
51
53
|
if (role === 'group') {
|
|
52
54
|
const disable = element.getElementAttribute('disabled') !== null;
|
|
53
55
|
const ariaDisable = element.getElementAttribute('aria-disabled') !== null;
|
|
54
|
-
if (disable || ariaDisable)
|
|
56
|
+
if (disable || ariaDisable)
|
|
55
57
|
return;
|
|
56
|
-
}
|
|
57
58
|
}
|
|
58
59
|
const test = new evaluation_1.Test();
|
|
59
60
|
const fgColor = element.getElementStyleProperty('color', null);
|
|
60
61
|
let bgColor = this.getBackground(element);
|
|
61
|
-
const opacity = parseFloat(element.getElementStyleProperty('opacity', null));
|
|
62
|
+
const opacity = parseFloat(element.getElementStyleProperty('opacity', null) || '1');
|
|
62
63
|
const fontSize = element.getElementStyleProperty('font-size', null);
|
|
63
64
|
const fontWeight = element.getElementStyleProperty('font-weight', null);
|
|
64
65
|
const fontFamily = element.getElementStyleProperty('font-family', null);
|
|
65
66
|
const fontStyle = element.getElementStyleProperty('font-style', null);
|
|
66
67
|
const textShadow = element.getElementStyleProperty('text-shadow', null);
|
|
67
|
-
if (textShadow.trim() !== 'none') {
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const blur = parseInt(
|
|
73
|
-
|
|
74
|
-
if (validateTextShadow) {
|
|
68
|
+
if (textShadow && textShadow.trim() !== 'none' && textShadow.trim() !== '') {
|
|
69
|
+
const pixelValues = textShadow.match(/(-?\d+px)/g);
|
|
70
|
+
if (pixelValues && pixelValues.length >= 3) {
|
|
71
|
+
const hs = Math.abs(parseInt(pixelValues[0].replace('px', ''), 10));
|
|
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
75
|
test.verdict = evaluation_1.Verdict.WARNING;
|
|
76
76
|
test.resultCode = 'W1';
|
|
77
77
|
test.addElement(element);
|
|
@@ -90,9 +90,8 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
|
|
|
90
90
|
const regexGradient = /((\w-?)*gradient.*)/gm;
|
|
91
91
|
let regexGradientMatches = bgColor.match(regexGradient);
|
|
92
92
|
if (regexGradientMatches) {
|
|
93
|
-
if (this.isHumanLanguage(elementText)) {
|
|
94
|
-
|
|
95
|
-
this.evaluateGradient(test, element, parsedGradientString, fgColor, opacity, fontSize, fontWeight, fontStyle, fontFamily, elementText);
|
|
93
|
+
if (this.isHumanLanguage(elementText || placeholder || "")) {
|
|
94
|
+
this.evaluateGradient(test, element, regexGradientMatches[0], fgColor, opacity, fontSize, fontWeight, fontStyle, fontFamily, elementText || placeholder || "");
|
|
96
95
|
}
|
|
97
96
|
else {
|
|
98
97
|
test.verdict = evaluation_1.Verdict.PASSED;
|
|
@@ -100,85 +99,40 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
|
|
|
100
99
|
test.addElement(element);
|
|
101
100
|
this.addTestResult(test);
|
|
102
101
|
}
|
|
102
|
+
return;
|
|
103
103
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
opacityAUX = parseFloat(parent.getElementStyleProperty('opacity', null));
|
|
130
|
-
parsedBG = this.parseRGBString(parent.getElementStyleProperty('background', null), opacityAUX);
|
|
131
|
-
elementAux = parent;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (parsedBG === undefined ||
|
|
140
|
-
(parsedBG.red === 0 && parsedBG.green === 0 && parsedBG.blue === 0 && parsedBG.alpha === 0)) {
|
|
141
|
-
parsedBG = { red: 255, green: 255, blue: 255, alpha: 1 };
|
|
142
|
-
}
|
|
143
|
-
let secondElement = elementAux;
|
|
144
|
-
if (parsedBG.alpha !== 1) {
|
|
145
|
-
let secondOpacity = opacityAUX;
|
|
146
|
-
let parsedSecondBG = parsedBG;
|
|
147
|
-
while (parsedSecondBG && parsedSecondBG.alpha !== 1) {
|
|
148
|
-
const parent = secondElement.getElementParent();
|
|
149
|
-
if (!parent) {
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
secondOpacity = parseFloat(parent.getElementStyleProperty('opacity', null));
|
|
154
|
-
parsedSecondBG = this.parseRGBString(parent.getElementStyleProperty('background', null), secondOpacity);
|
|
155
|
-
secondElement = parent;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
if (parsedSecondBG && parsedSecondBG.alpha === 1) {
|
|
159
|
-
const outputRed = Math.round(parsedBG.red * parsedBG.alpha + parsedSecondBG.red * (1.0 - parsedBG.alpha));
|
|
160
|
-
const outputGreen = Math.round(parsedBG.green * parsedBG.alpha + parsedSecondBG.green * (1.0 - parsedBG.alpha));
|
|
161
|
-
const outputBlue = Math.round(parsedBG.blue * parsedBG.alpha + parsedSecondBG.blue * (1.0 - parsedBG.alpha));
|
|
162
|
-
parsedBG = { red: outputRed, green: outputGreen, blue: outputBlue, alpha: 1 };
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
const parsedFG = this.parseRGBString(fgColor, opacity);
|
|
104
|
+
let parsedBG = this.parseRGBString(bgColor);
|
|
105
|
+
if (parsedBG)
|
|
106
|
+
parsedBG.alpha *= opacity;
|
|
107
|
+
let elementAux = element;
|
|
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;
|
|
117
|
+
}
|
|
118
|
+
if (!parsedBG || parsedBG.alpha === 0) {
|
|
119
|
+
parsedBG = { red: 255, green: 255, blue: 255, alpha: 1 };
|
|
120
|
+
}
|
|
121
|
+
if (parsedBG.alpha < 1) {
|
|
122
|
+
parsedBG = this.flattenColors(parsedBG, { red: 255, green: 255, blue: 255, alpha: 1 });
|
|
123
|
+
}
|
|
124
|
+
const parsedFG = this.parseRGBString(fgColor);
|
|
125
|
+
if (parsedFG) {
|
|
126
|
+
parsedFG.alpha *= opacity;
|
|
166
127
|
if (!this.equals(parsedBG, parsedFG)) {
|
|
167
|
-
|
|
128
|
+
const textToVerify = elementText || placeholder || "";
|
|
129
|
+
if (this.isHumanLanguage(textToVerify)) {
|
|
168
130
|
const contrastRatio = this.getContrast(parsedBG, parsedFG);
|
|
169
131
|
const isValid = this.hasValidContrastRatio(contrastRatio, fontSize, this.isBold(fontWeight));
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
this.addTestResult(test);
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
test.verdict = evaluation_1.Verdict.FAILED;
|
|
178
|
-
test.resultCode = 'F1';
|
|
179
|
-
test.addElement(element);
|
|
180
|
-
this.addTestResult(test);
|
|
181
|
-
}
|
|
132
|
+
test.verdict = isValid ? evaluation_1.Verdict.PASSED : evaluation_1.Verdict.FAILED;
|
|
133
|
+
test.resultCode = isValid ? 'P1' : 'F1';
|
|
134
|
+
test.addElement(element);
|
|
135
|
+
this.addTestResult(test);
|
|
182
136
|
}
|
|
183
137
|
else {
|
|
184
138
|
test.verdict = evaluation_1.Verdict.PASSED;
|
|
@@ -189,168 +143,130 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
|
|
|
189
143
|
}
|
|
190
144
|
}
|
|
191
145
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (backgroundImage === 'none') {
|
|
195
|
-
let bg = element.getElementStyleProperty('background', null);
|
|
196
|
-
if (bg === '') {
|
|
197
|
-
bg = element.getElementStyleProperty('background-color', null);
|
|
198
|
-
}
|
|
199
|
-
return bg;
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
return backgroundImage;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
isImage(color) {
|
|
206
|
-
return (color.toLowerCase().includes('.jpeg') ||
|
|
207
|
-
color.toLowerCase().includes('.jpg') ||
|
|
208
|
-
color.toLowerCase().includes('.png') ||
|
|
209
|
-
color.toLowerCase().includes('.svg'));
|
|
146
|
+
isHumanLanguage(text) {
|
|
147
|
+
return window.DomUtils.isHumanLanguage(text);
|
|
210
148
|
}
|
|
211
149
|
evaluateGradient(test, element, parsedGradientString, fgColor, opacity, fontSize, fontWeight, fontStyle, fontFamily, elementText) {
|
|
212
150
|
if (parsedGradientString.startsWith('linear-gradient')) {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
isValid = isValid && this.hasValidContrastRatio(contrastRatio, fontSize, this.isBold(fontWeight));
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
for (const color of colors) {
|
|
230
|
-
contrastRatio = this.getContrast(color, this.parseRGBString(fgColor, opacity));
|
|
231
|
-
isValid = isValid && this.hasValidContrastRatio(contrastRatio, fontSize, this.isBold(fontWeight));
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (isValid) {
|
|
235
|
-
test.verdict = evaluation_1.Verdict.PASSED;
|
|
236
|
-
test.resultCode = 'P3';
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
test.verdict = evaluation_1.Verdict.FAILED;
|
|
240
|
-
test.resultCode = 'F2';
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
else if (gradientDirection === 'to left' || gradientDirection === 'to right') {
|
|
244
|
-
test.verdict = evaluation_1.Verdict.WARNING;
|
|
245
|
-
test.resultCode = 'W3';
|
|
151
|
+
const colors = this.parseGradientString(parsedGradientString);
|
|
152
|
+
let isValid = true;
|
|
153
|
+
const parsedFG = this.parseRGBString(fgColor);
|
|
154
|
+
if (!parsedFG)
|
|
155
|
+
return false;
|
|
156
|
+
parsedFG.alpha *= opacity;
|
|
157
|
+
const textSize = this.getTextSize(fontFamily.toLowerCase().replace(/['"]+/g, ''), parseInt(fontSize.replace('px', '')), this.isBold(fontWeight), fontStyle.toLowerCase().includes('italic'), elementText);
|
|
158
|
+
if (textSize !== -1) {
|
|
159
|
+
const elementWidth = element.getElementStyleProperty('width', null);
|
|
160
|
+
const lastCharRatio = textSize / parseInt(elementWidth.replace('px', ''));
|
|
161
|
+
const lastCharBgColor = this.getColorInGradient(colors[0], colors[colors.length - 1], lastCharRatio);
|
|
162
|
+
isValid = isValid && this.hasValidContrastRatio(this.getContrast(colors[0], parsedFG), fontSize, this.isBold(fontWeight));
|
|
163
|
+
isValid = isValid && this.hasValidContrastRatio(this.getContrast(lastCharBgColor, parsedFG), fontSize, this.isBold(fontWeight));
|
|
246
164
|
}
|
|
247
165
|
else {
|
|
248
|
-
|
|
249
|
-
|
|
166
|
+
for (const color of colors) {
|
|
167
|
+
isValid = isValid && this.hasValidContrastRatio(this.getContrast(color, parsedFG), fontSize, this.isBold(fontWeight));
|
|
168
|
+
}
|
|
250
169
|
}
|
|
170
|
+
test.verdict = isValid ? evaluation_1.Verdict.PASSED : evaluation_1.Verdict.FAILED;
|
|
171
|
+
test.resultCode = isValid ? 'P3' : 'F2';
|
|
251
172
|
}
|
|
252
173
|
else {
|
|
174
|
+
test.verdict = evaluation_1.Verdict.WARNING;
|
|
253
175
|
test.resultCode = 'W3';
|
|
254
176
|
}
|
|
255
177
|
test.addElement(element);
|
|
256
178
|
this.addTestResult(test);
|
|
257
179
|
return true;
|
|
258
180
|
}
|
|
259
|
-
|
|
260
|
-
return window.DomUtils.isHumanLanguage(text);
|
|
261
|
-
}
|
|
262
|
-
equals(color1, color2) {
|
|
263
|
-
return (color1.red === color2.red &&
|
|
264
|
-
color1.green === color2.green &&
|
|
265
|
-
color1.blue === color2.blue &&
|
|
266
|
-
color1.alpha === color2.alpha);
|
|
267
|
-
}
|
|
268
|
-
getGradientDirection(gradient) {
|
|
269
|
-
const direction = gradient.replace('linear-gradient(', '').split(',')[0];
|
|
270
|
-
if (direction) {
|
|
271
|
-
if (direction === '90deg')
|
|
272
|
-
return 'to right';
|
|
273
|
-
if (direction === '-90deg')
|
|
274
|
-
return 'to left';
|
|
275
|
-
return direction;
|
|
276
|
-
}
|
|
277
|
-
return undefined;
|
|
278
|
-
}
|
|
279
|
-
parseGradientString(gradient, opacity) {
|
|
181
|
+
parseGradientString(gradient) {
|
|
280
182
|
const regex = /rgb(a?)\((\d+), (\d+), (\d+)+(, +(\d)+)?\)/gm;
|
|
281
183
|
const colorsMatch = gradient.match(regex);
|
|
282
184
|
const colors = [];
|
|
283
185
|
for (const stringColor of colorsMatch || []) {
|
|
284
|
-
|
|
186
|
+
const parsed = this.parseRGBString(stringColor);
|
|
187
|
+
if (parsed)
|
|
188
|
+
colors.push(parsed);
|
|
285
189
|
}
|
|
286
190
|
return colors;
|
|
287
191
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
192
|
+
getColorInGradient(fromColor, toColor, ratio) {
|
|
193
|
+
return {
|
|
194
|
+
red: fromColor.red + (toColor.red - fromColor.red) * ratio,
|
|
195
|
+
green: fromColor.green + (toColor.green - fromColor.green) * ratio,
|
|
196
|
+
blue: fromColor.blue + (toColor.blue - fromColor.blue) * ratio,
|
|
197
|
+
alpha: 1
|
|
198
|
+
};
|
|
199
|
+
}
|
|
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
|
+
parseRGBString(colorString) {
|
|
215
|
+
var _a;
|
|
216
|
+
if (!colorString || colorString === 'transparent' || colorString === 'none')
|
|
292
217
|
return { red: 0, green: 0, blue: 0, alpha: 0 };
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
218
|
+
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 };
|
|
221
|
+
const rgba = colorString.match(/^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/);
|
|
222
|
+
if (rgba)
|
|
223
|
+
return { red: parseInt(rgba[1]), green: parseInt(rgba[2]), blue: parseInt(rgba[3]), alpha: Math.round(parseFloat(rgba[4]) * 100) / 100 };
|
|
224
|
+
try {
|
|
225
|
+
const color = new colorjs_io_1.default(colorString);
|
|
226
|
+
const srgb = color.to('srgb');
|
|
296
227
|
return {
|
|
297
|
-
red:
|
|
298
|
-
green:
|
|
299
|
-
blue:
|
|
300
|
-
alpha:
|
|
228
|
+
red: Math.round(srgb.coords[0] * 255),
|
|
229
|
+
green: Math.round(srgb.coords[1] * 255),
|
|
230
|
+
blue: Math.round(srgb.coords[2] * 255),
|
|
231
|
+
alpha: (_a = color.alpha) !== null && _a !== void 0 ? _a : 1
|
|
301
232
|
};
|
|
302
233
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
return {
|
|
306
|
-
red: parseInt(match[1], 10),
|
|
307
|
-
green: parseInt(match[2], 10),
|
|
308
|
-
blue: parseInt(match[3], 10),
|
|
309
|
-
alpha: Math.round(parseFloat(match[4]) * 100) / 100
|
|
310
|
-
};
|
|
234
|
+
catch (e) {
|
|
235
|
+
return undefined;
|
|
311
236
|
}
|
|
312
237
|
}
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
flattenColors(fgColor, bgColor) {
|
|
323
|
-
const fgAlpha = fgColor['alpha'];
|
|
324
|
-
const red = (1 - fgAlpha) * bgColor['red'] + fgAlpha * fgColor['red'];
|
|
325
|
-
const green = (1 - fgAlpha) * bgColor['green'] + fgAlpha * fgColor['green'];
|
|
326
|
-
const blue = (1 - fgAlpha) * bgColor['blue'] + fgAlpha * fgColor['blue'];
|
|
327
|
-
const alpha = fgColor['alpha'] + bgColor['alpha'] * (1 - fgColor['alpha']);
|
|
328
|
-
return { red: red, green: green, blue: blue, alpha: alpha };
|
|
238
|
+
flattenColors(fg, bg) {
|
|
239
|
+
const alpha = fg.alpha;
|
|
240
|
+
return {
|
|
241
|
+
red: Math.round((1 - alpha) * bg.red + alpha * fg.red),
|
|
242
|
+
green: Math.round((1 - alpha) * bg.green + alpha * fg.green),
|
|
243
|
+
blue: Math.round((1 - alpha) * bg.blue + alpha * fg.blue),
|
|
244
|
+
alpha: fg.alpha + bg.alpha * (1 - fg.alpha)
|
|
245
|
+
};
|
|
329
246
|
}
|
|
330
|
-
|
|
331
|
-
|
|
247
|
+
getContrast(bg, fg) {
|
|
248
|
+
const finalFG = fg.alpha < 1 ? this.flattenColors(fg, bg) : fg;
|
|
249
|
+
const L1 = this.getLuminance(bg);
|
|
250
|
+
const L2 = this.getLuminance(finalFG);
|
|
251
|
+
return (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05);
|
|
332
252
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
return (Math.max(fL, bL) + 0.05) / (Math.min(fL, bL) + 0.05);
|
|
253
|
+
getLuminance(c) {
|
|
254
|
+
const a = [c.red, c.green, c.blue].map(v => {
|
|
255
|
+
v /= 255;
|
|
256
|
+
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
257
|
+
});
|
|
258
|
+
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
|
|
340
259
|
}
|
|
341
260
|
hasValidContrastRatio(contrast, fontSize, isBold) {
|
|
342
|
-
const
|
|
343
|
-
const
|
|
344
|
-
return contrast >=
|
|
261
|
+
const size = parseFloat(fontSize);
|
|
262
|
+
const threshold = (isBold && size >= 18.66) || size >= 24 ? 3 : 4.5;
|
|
263
|
+
return (contrast + 0.02) >= threshold;
|
|
345
264
|
}
|
|
346
|
-
|
|
347
|
-
return
|
|
265
|
+
isBold(fontWeight) {
|
|
266
|
+
return !!fontWeight && ['bold', 'bolder', '700', '800', '900'].includes(fontWeight);
|
|
348
267
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const green = fromColor['green'] + (toColor['green'] - fromColor['green']) * ratio;
|
|
352
|
-
const blue = fromColor['blue'] + (toColor['blue'] - fromColor['blue']) * ratio;
|
|
353
|
-
return { red: red, green: green, blue: blue, alpha: 1 };
|
|
268
|
+
equals(c1, c2) {
|
|
269
|
+
return c1.red === c2.red && c1.green === c2.green && c1.blue === c2.blue && c1.alpha === c2.alpha;
|
|
354
270
|
}
|
|
355
271
|
}
|
|
356
272
|
exports.QW_ACT_R37 = QW_ACT_R37;
|
|
@@ -359,7 +275,6 @@ __decorate([
|
|
|
359
275
|
applicability_1.ElementIsHTMLElement,
|
|
360
276
|
(0, applicability_1.ElementIsNot)(['html', 'head', 'body', 'script', 'style', 'meta']),
|
|
361
277
|
applicability_1.ElementIsVisible,
|
|
362
|
-
applicability_1.ElementHasText,
|
|
363
278
|
__metadata("design:type", Function),
|
|
364
279
|
__metadata("design:paramtypes", [Function]),
|
|
365
280
|
__metadata("design:returntype", void 0)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QW-ACT-R76.d.ts","sourceRoot":"","sources":["../../src/rules/QW-ACT-R76.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAQrD,OAAO,EAAE,IAAI,EAAW,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"QW-ACT-R76.d.ts","sourceRoot":"","sources":["../../src/rules/QW-ACT-R76.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAQrD,OAAO,EAAE,IAAI,EAAW,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGtD,cAAM,UAAW,SAAQ,UAAU;IAMjC,OAAO,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI;IAwMjC,aAAa,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM;IAczC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAS/B,gBAAgB,CACd,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,SAAS,EAClB,oBAAoB,EAAE,GAAG,EACzB,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO;IAoDV,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAItC,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO;IASzC,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAY1D,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG;IAW3D,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG;IAyDzD,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAYtE,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,GAAG,GAAG;IAU9C,OAAO,CAAC,MAAM;IAId,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,GAAG,MAAM;IAW/C,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO;IAWnF,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAIjG,kBAAkB,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG;CAOrE;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/rules/QW-ACT-R76.js
CHANGED
|
@@ -8,11 +8,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
exports.QW_ACT_R76 = void 0;
|
|
13
16
|
const applicability_1 = require("@qualweb/util/applicability");
|
|
14
17
|
const evaluation_1 = require("@qualweb/core/evaluation");
|
|
15
18
|
const AtomicRule_object_1 = require("../lib/AtomicRule.object");
|
|
19
|
+
const colorjs_io_1 = __importDefault(require("colorjs.io"));
|
|
16
20
|
class QW_ACT_R76 extends AtomicRule_object_1.AtomicRule {
|
|
17
21
|
execute(element) {
|
|
18
22
|
const disabledWidgets = window.disabledWidgets;
|
|
@@ -258,6 +262,8 @@ class QW_ACT_R76 extends AtomicRule_object_1.AtomicRule {
|
|
|
258
262
|
parseRGBString(colorString, opacity) {
|
|
259
263
|
const rgbRegex = /^rgb\((\d+), (\d+), (\d+)\)/;
|
|
260
264
|
const rgbaRegex = /^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/;
|
|
265
|
+
const oklchRegex = /^oklch\((\d*(\.\d+)?) (\d*(\.\d+)?) (\d*(\.\d+)?)\)/;
|
|
266
|
+
const oklch2Regex = /^oklch\((\d*(\.\d+)?) (\d*(\.\d+)?) (\d*(\.\d+)?) \/ (\d*(\.\d+)?)\)/;
|
|
261
267
|
if (colorString === 'transparent') {
|
|
262
268
|
return { red: 0, green: 0, blue: 0, alpha: 0 };
|
|
263
269
|
}
|
|
@@ -279,6 +285,28 @@ class QW_ACT_R76 extends AtomicRule_object_1.AtomicRule {
|
|
|
279
285
|
alpha: Math.round(parseFloat(match[4]) * 100) / 100
|
|
280
286
|
};
|
|
281
287
|
}
|
|
288
|
+
match = colorString.match(oklch2Regex);
|
|
289
|
+
if (match) {
|
|
290
|
+
const oklchColor = new colorjs_io_1.default("oklch", [parseFloat(match[1]), parseFloat(match[2]), parseFloat(match[3])]);
|
|
291
|
+
const rgba = oklchColor.to("srgb");
|
|
292
|
+
return {
|
|
293
|
+
red: rgba.srgb.red,
|
|
294
|
+
green: rgba.srgb.green,
|
|
295
|
+
blue: rgba.srgb.blue,
|
|
296
|
+
alpha: parseFloat(match[4])
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
match = colorString.match(oklchRegex);
|
|
300
|
+
if (match) {
|
|
301
|
+
const oklchColor = new colorjs_io_1.default("oklch", [parseFloat(match[1]), parseFloat(match[2]), parseFloat(match[3])]);
|
|
302
|
+
const rgba = oklchColor.to("srgb");
|
|
303
|
+
return {
|
|
304
|
+
red: rgba.srgb.red,
|
|
305
|
+
green: rgba.srgb.green,
|
|
306
|
+
blue: rgba.srgb.blue,
|
|
307
|
+
alpha: rgba.alpha
|
|
308
|
+
};
|
|
309
|
+
}
|
|
282
310
|
}
|
|
283
311
|
getRelativeLuminance(red, green, blue) {
|
|
284
312
|
const rSRGB = red / 255;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qualweb/act-rules",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"description": "ACT rules module for qualweb web accessibility evaluator",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/*"
|
|
@@ -45,16 +45,17 @@
|
|
|
45
45
|
"author": "João Vicente",
|
|
46
46
|
"license": "ISC",
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@qualweb/core": "^0.8.
|
|
48
|
+
"@qualweb/core": "^0.8.6",
|
|
49
49
|
"@qualweb/locale": "0.2.2",
|
|
50
50
|
"@qualweb/qw-page": "0.3.2",
|
|
51
|
-
"@qualweb/util": "0.6.
|
|
51
|
+
"@qualweb/util": "0.6.5",
|
|
52
52
|
"@tsconfig/recommended": "^1.0.3",
|
|
53
53
|
"@types/mocha": "^10.0.6",
|
|
54
54
|
"@types/node": "^16.11.11",
|
|
55
55
|
"@types/node-fetch": "^2.6.11",
|
|
56
56
|
"ajv": "^6.12.6",
|
|
57
57
|
"chai": "4.5.0",
|
|
58
|
+
"colorjs.io": "^0.5.2",
|
|
58
59
|
"mocha": "^10.2.0",
|
|
59
60
|
"node-fetch": "^2.7.0",
|
|
60
61
|
"prettier": "^3.1.1",
|