@qualweb/act-rules 0.7.5 → 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
- getBackground(element: QWElement): string;
7
- isImage(color: string): boolean;
8
- evaluateGradient(test: Test, element: QWElement, parsedGradientString: any, fgColor: any, opacity: number, fontSize: string, fontWeight: string, fontStyle: string, fontFamily: string, elementText: string): boolean;
9
- isHumanLanguage(text: string): boolean;
10
- equals(color1: any, color2: any): boolean;
11
- getGradientDirection(gradient: string): string | undefined;
12
- parseGradientString(gradient: string, opacity: number): any;
13
- parseRGBString(colorString: string, opacity: number): any;
14
- getRelativeLuminance(red: number, green: number, blue: number): number;
15
- flattenColors(fgColor: any, bgColor: any): any;
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
- getContrast(bgColor: any, fgColor: any): number;
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,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;IAuOjC,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"}
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"}
@@ -19,35 +19,33 @@ const AtomicRule_object_1 = require("../lib/AtomicRule.object");
19
19
  const colorjs_io_1 = __importDefault(require("colorjs.io"));
20
20
  class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
21
21
  execute(element) {
22
+ var _a;
22
23
  const visible = window.DomUtils.isElementVisible(element);
23
- if (!visible) {
24
+ if (!visible)
24
25
  return;
25
- }
26
- const hasTextNode = element.hasTextNode();
27
- const elementText = element.getElementOwnText();
28
- if (!hasTextNode && elementText.trim() === '') {
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) {
29
31
  return;
30
32
  }
31
- const isHTML = element.isElementHTMLElement();
32
- if (!isHTML) {
33
+ if (!element.isElementHTMLElement())
33
34
  return;
34
- }
35
35
  const disabledWidgets = window.disabledWidgets;
36
36
  const elementSelectors = element.getElementSelector();
37
37
  for (const disableWidget of disabledWidgets || []) {
38
- const selectors = window.AccessibilityUtils.getAccessibleNameSelector(disableWidget);
39
- if (disableWidget && selectors && selectors.includes(elementSelectors)) {
38
+ const selectorsResult = window.AccessibilityUtils.getAccessibleNameSelector(disableWidget);
39
+ const selectors = typeof selectorsResult === 'string' ? [selectorsResult] : selectorsResult;
40
+ if (disableWidget && selectors && selectors.includes(elementSelectors))
40
41
  return;
41
- }
42
- if (disableWidget.getElementSelector() === elementSelectors) {
42
+ if (disableWidget.getElementSelector() === elementSelectors)
43
43
  return;
44
- }
45
44
  const children = disableWidget.getElementChildren();
46
45
  if (children) {
47
46
  for (const child of children) {
48
- if (child.getElementSelector() === elementSelectors) {
47
+ if (child.getElementSelector() === elementSelectors)
49
48
  return;
50
- }
51
49
  }
52
50
  }
53
51
  }
@@ -55,27 +53,25 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
55
53
  if (role === 'group') {
56
54
  const disable = element.getElementAttribute('disabled') !== null;
57
55
  const ariaDisable = element.getElementAttribute('aria-disabled') !== null;
58
- if (disable || ariaDisable) {
56
+ if (disable || ariaDisable)
59
57
  return;
60
- }
61
58
  }
62
59
  const test = new evaluation_1.Test();
63
60
  const fgColor = element.getElementStyleProperty('color', null);
64
61
  let bgColor = this.getBackground(element);
65
- const opacity = parseFloat(element.getElementStyleProperty('opacity', null));
62
+ const opacity = parseFloat(element.getElementStyleProperty('opacity', null) || '1');
66
63
  const fontSize = element.getElementStyleProperty('font-size', null);
67
64
  const fontWeight = element.getElementStyleProperty('font-weight', null);
68
65
  const fontFamily = element.getElementStyleProperty('font-family', null);
69
66
  const fontStyle = element.getElementStyleProperty('font-style', null);
70
67
  const textShadow = element.getElementStyleProperty('text-shadow', null);
71
- if (textShadow.trim() !== 'none') {
72
- const properties = textShadow.trim().split(' ');
73
- if (properties.length === 6) {
74
- const vs = parseInt(properties[3], 0);
75
- const hs = parseInt(properties[4], 0);
76
- const blur = parseInt(properties[5], 0);
77
- const validateTextShadow = vs === 0 && hs === 0 && blur > 0 && blur <= 15;
78
- 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) {
79
75
  test.verdict = evaluation_1.Verdict.WARNING;
80
76
  test.resultCode = 'W1';
81
77
  test.addElement(element);
@@ -94,9 +90,8 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
94
90
  const regexGradient = /((\w-?)*gradient.*)/gm;
95
91
  let regexGradientMatches = bgColor.match(regexGradient);
96
92
  if (regexGradientMatches) {
97
- if (this.isHumanLanguage(elementText)) {
98
- const parsedGradientString = regexGradientMatches[0];
99
- 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 || "");
100
95
  }
101
96
  else {
102
97
  test.verdict = evaluation_1.Verdict.PASSED;
@@ -104,85 +99,40 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
104
99
  test.addElement(element);
105
100
  this.addTestResult(test);
106
101
  }
102
+ return;
107
103
  }
108
- else {
109
- let parsedBG = this.parseRGBString(bgColor, opacity);
110
- let elementAux = element;
111
- let opacityAUX;
112
- while (parsedBG === undefined ||
113
- (parsedBG.red === 0 && parsedBG.green === 0 && parsedBG.blue === 0 && parsedBG.alpha === 0)) {
114
- const parent = elementAux.getElementParent();
115
- if (parent) {
116
- bgColor = this.getBackground(parent);
117
- if (this.isImage(bgColor)) {
118
- test.verdict = evaluation_1.Verdict.WARNING;
119
- test.resultCode = 'W2';
120
- test.addElement(element);
121
- this.addTestResult(test);
122
- return;
123
- }
124
- else {
125
- regexGradientMatches = bgColor.match(regexGradient);
126
- if (regexGradientMatches) {
127
- const parsedGradientString = regexGradientMatches[0];
128
- if (this.evaluateGradient(test, element, parsedGradientString, fgColor, opacity, fontSize, fontWeight, fontStyle, fontFamily, elementText)) {
129
- return;
130
- }
131
- }
132
- else {
133
- opacityAUX = parseFloat(parent.getElementStyleProperty('opacity', null));
134
- parsedBG = this.parseRGBString(parent.getElementStyleProperty('background', null), opacityAUX);
135
- elementAux = parent;
136
- }
137
- }
138
- }
139
- else {
140
- break;
141
- }
142
- }
143
- if (parsedBG === undefined ||
144
- (parsedBG.red === 0 && parsedBG.green === 0 && parsedBG.blue === 0 && parsedBG.alpha === 0)) {
145
- parsedBG = { red: 255, green: 255, blue: 255, alpha: 1 };
146
- }
147
- let secondElement = elementAux;
148
- if (parsedBG.alpha !== 1) {
149
- let secondOpacity = opacityAUX;
150
- let parsedSecondBG = parsedBG;
151
- while (parsedSecondBG && parsedSecondBG.alpha !== 1) {
152
- const parent = secondElement.getElementParent();
153
- if (!parent) {
154
- break;
155
- }
156
- else {
157
- secondOpacity = parseFloat(parent.getElementStyleProperty('opacity', null));
158
- parsedSecondBG = this.parseRGBString(parent.getElementStyleProperty('background', null), secondOpacity);
159
- secondElement = parent;
160
- }
161
- }
162
- if (parsedSecondBG && parsedSecondBG.alpha === 1) {
163
- const outputRed = Math.round(parsedBG.red * parsedBG.alpha + parsedSecondBG.red * (1.0 - parsedBG.alpha));
164
- const outputGreen = Math.round(parsedBG.green * parsedBG.alpha + parsedSecondBG.green * (1.0 - parsedBG.alpha));
165
- const outputBlue = Math.round(parsedBG.blue * parsedBG.alpha + parsedSecondBG.blue * (1.0 - parsedBG.alpha));
166
- parsedBG = { red: outputRed, green: outputGreen, blue: outputBlue, alpha: 1 };
167
- }
168
- }
169
- 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;
170
127
  if (!this.equals(parsedBG, parsedFG)) {
171
- if (this.isHumanLanguage(elementText)) {
128
+ const textToVerify = elementText || placeholder || "";
129
+ if (this.isHumanLanguage(textToVerify)) {
172
130
  const contrastRatio = this.getContrast(parsedBG, parsedFG);
173
131
  const isValid = this.hasValidContrastRatio(contrastRatio, fontSize, this.isBold(fontWeight));
174
- if (isValid) {
175
- test.verdict = evaluation_1.Verdict.PASSED;
176
- test.resultCode = 'P1';
177
- test.addElement(element);
178
- this.addTestResult(test);
179
- }
180
- else {
181
- test.verdict = evaluation_1.Verdict.FAILED;
182
- test.resultCode = 'F1';
183
- test.addElement(element);
184
- this.addTestResult(test);
185
- }
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);
186
136
  }
187
137
  else {
188
138
  test.verdict = evaluation_1.Verdict.PASSED;
@@ -193,192 +143,130 @@ class QW_ACT_R37 extends AtomicRule_object_1.AtomicRule {
193
143
  }
194
144
  }
195
145
  }
196
- getBackground(element) {
197
- const backgroundImage = element.getElementStyleProperty('background-image', null);
198
- if (backgroundImage === 'none') {
199
- let bg = element.getElementStyleProperty('background', null);
200
- if (bg === '') {
201
- bg = element.getElementStyleProperty('background-color', null);
202
- }
203
- return bg;
204
- }
205
- else {
206
- return backgroundImage;
207
- }
208
- }
209
- isImage(color) {
210
- return (color.toLowerCase().includes('.jpeg') ||
211
- color.toLowerCase().includes('.jpg') ||
212
- color.toLowerCase().includes('.png') ||
213
- color.toLowerCase().includes('.svg'));
146
+ isHumanLanguage(text) {
147
+ return window.DomUtils.isHumanLanguage(text);
214
148
  }
215
149
  evaluateGradient(test, element, parsedGradientString, fgColor, opacity, fontSize, fontWeight, fontStyle, fontFamily, elementText) {
216
150
  if (parsedGradientString.startsWith('linear-gradient')) {
217
- const gradientDirection = this.getGradientDirection(parsedGradientString);
218
- if (gradientDirection === 'to do') {
219
- const colors = this.parseGradientString(parsedGradientString, opacity);
220
- let isValid = true;
221
- let contrastRatio;
222
- const textSize = this.getTextSize(fontFamily.toLowerCase().replace(/['"]+/g, ''), parseInt(fontSize.replace('px', '')), this.isBold(fontWeight), fontStyle.toLowerCase().includes('italic'), elementText);
223
- if (textSize !== -1) {
224
- const elementWidth = element.getElementStyleProperty('width', null);
225
- const lastCharRatio = textSize / parseInt(elementWidth.replace('px', ''));
226
- const lastCharBgColor = this.getColorInGradient(colors[0], colors[colors.length - 1], lastCharRatio);
227
- contrastRatio = this.getContrast(colors[0], this.parseRGBString(fgColor, opacity));
228
- isValid = isValid && this.hasValidContrastRatio(contrastRatio, fontSize, this.isBold(fontWeight));
229
- contrastRatio = this.getContrast(lastCharBgColor, this.parseRGBString(fgColor, opacity));
230
- isValid = isValid && this.hasValidContrastRatio(contrastRatio, fontSize, this.isBold(fontWeight));
231
- }
232
- else {
233
- for (const color of colors) {
234
- contrastRatio = this.getContrast(color, this.parseRGBString(fgColor, opacity));
235
- isValid = isValid && this.hasValidContrastRatio(contrastRatio, fontSize, this.isBold(fontWeight));
236
- }
237
- }
238
- if (isValid) {
239
- test.verdict = evaluation_1.Verdict.PASSED;
240
- test.resultCode = 'P3';
241
- }
242
- else {
243
- test.verdict = evaluation_1.Verdict.FAILED;
244
- test.resultCode = 'F2';
245
- }
246
- }
247
- else if (gradientDirection === 'to left' || gradientDirection === 'to right') {
248
- test.verdict = evaluation_1.Verdict.WARNING;
249
- 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));
250
164
  }
251
165
  else {
252
- test.verdict = evaluation_1.Verdict.WARNING;
253
- test.resultCode = 'W3';
166
+ for (const color of colors) {
167
+ isValid = isValid && this.hasValidContrastRatio(this.getContrast(color, parsedFG), fontSize, this.isBold(fontWeight));
168
+ }
254
169
  }
170
+ test.verdict = isValid ? evaluation_1.Verdict.PASSED : evaluation_1.Verdict.FAILED;
171
+ test.resultCode = isValid ? 'P3' : 'F2';
255
172
  }
256
173
  else {
174
+ test.verdict = evaluation_1.Verdict.WARNING;
257
175
  test.resultCode = 'W3';
258
176
  }
259
177
  test.addElement(element);
260
178
  this.addTestResult(test);
261
179
  return true;
262
180
  }
263
- isHumanLanguage(text) {
264
- return window.DomUtils.isHumanLanguage(text);
265
- }
266
- equals(color1, color2) {
267
- return (color1.red === color2.red &&
268
- color1.green === color2.green &&
269
- color1.blue === color2.blue &&
270
- color1.alpha === color2.alpha);
271
- }
272
- getGradientDirection(gradient) {
273
- const direction = gradient.replace('linear-gradient(', '').split(',')[0];
274
- if (direction) {
275
- if (direction === '90deg')
276
- return 'to right';
277
- if (direction === '-90deg')
278
- return 'to left';
279
- return direction;
280
- }
281
- return undefined;
282
- }
283
- parseGradientString(gradient, opacity) {
181
+ parseGradientString(gradient) {
284
182
  const regex = /rgb(a?)\((\d+), (\d+), (\d+)+(, +(\d)+)?\)/gm;
285
183
  const colorsMatch = gradient.match(regex);
286
184
  const colors = [];
287
185
  for (const stringColor of colorsMatch || []) {
288
- colors.push(this.parseRGBString(stringColor, opacity));
186
+ const parsed = this.parseRGBString(stringColor);
187
+ if (parsed)
188
+ colors.push(parsed);
289
189
  }
290
190
  return colors;
291
191
  }
292
- parseRGBString(colorString, opacity) {
293
- const rgbRegex = /^rgb\((\d+), (\d+), (\d+)\)/;
294
- const rgbaRegex = /^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/;
295
- const oklchRegex = /^oklch\((\d*(\.\d+)?) (\d*(\.\d+)?) (\d*(\.\d+)?)\)/;
296
- const oklch2Regex = /^oklch\((\d*(\.\d+)?) (\d*(\.\d+)?) (\d*(\.\d+)?) \/ (\d*(\.\d+)?)\)/;
297
- if (colorString === 'transparent') {
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')
298
217
  return { red: 0, green: 0, blue: 0, alpha: 0 };
299
- }
300
- let match = colorString.match(rgbRegex);
301
- if (match) {
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');
302
227
  return {
303
- red: parseInt(match[1], 10),
304
- green: parseInt(match[2], 10),
305
- blue: parseInt(match[3], 10),
306
- alpha: opacity
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
307
232
  };
308
233
  }
309
- match = colorString.match(rgbaRegex);
310
- if (match) {
311
- return {
312
- red: parseInt(match[1], 10),
313
- green: parseInt(match[2], 10),
314
- blue: parseInt(match[3], 10),
315
- alpha: Math.round(parseFloat(match[4]) * 100) / 100
316
- };
317
- }
318
- match = colorString.match(oklch2Regex);
319
- if (match) {
320
- const oklchColor = new colorjs_io_1.default("oklch", [parseFloat(match[1]), parseFloat(match[2]), parseFloat(match[3])]);
321
- const rgba = oklchColor.to("srgb");
322
- return {
323
- red: rgba.srgb.red,
324
- green: rgba.srgb.green,
325
- blue: rgba.srgb.blue,
326
- alpha: parseFloat(match[4])
327
- };
328
- }
329
- match = colorString.match(oklchRegex);
330
- if (match) {
331
- const oklchColor = new colorjs_io_1.default("oklch", [parseFloat(match[1]), parseFloat(match[2]), parseFloat(match[3])]);
332
- const rgba = oklchColor.to("srgb");
333
- return {
334
- red: rgba.srgb.red,
335
- green: rgba.srgb.green,
336
- blue: rgba.srgb.blue,
337
- alpha: rgba.alpha
338
- };
234
+ catch (e) {
235
+ return undefined;
339
236
  }
340
237
  }
341
- getRelativeLuminance(red, green, blue) {
342
- const rSRGB = red / 255;
343
- const gSRGB = green / 255;
344
- const bSRGB = blue / 255;
345
- const r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow((rSRGB + 0.055) / 1.055, 2.4);
346
- const g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow((gSRGB + 0.055) / 1.055, 2.4);
347
- const b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow((bSRGB + 0.055) / 1.055, 2.4);
348
- return 0.2126 * r + 0.7152 * g + 0.0722 * b;
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
+ };
349
246
  }
350
- flattenColors(fgColor, bgColor) {
351
- const fgAlpha = fgColor['alpha'];
352
- const red = (1 - fgAlpha) * bgColor['red'] + fgAlpha * fgColor['red'];
353
- const green = (1 - fgAlpha) * bgColor['green'] + fgAlpha * fgColor['green'];
354
- const blue = (1 - fgAlpha) * bgColor['blue'] + fgAlpha * fgColor['blue'];
355
- const alpha = fgColor['alpha'] + bgColor['alpha'] * (1 - fgColor['alpha']);
356
- return { red: red, green: green, blue: blue, alpha: alpha };
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);
357
252
  }
358
- isBold(fontWeight) {
359
- return !!fontWeight && ['bold', 'bolder', '700', '800', '900'].includes(fontWeight);
360
- }
361
- getContrast(bgColor, fgColor) {
362
- if (fgColor.alpha < 1) {
363
- fgColor = this.flattenColors(fgColor, bgColor);
364
- }
365
- const bL = this.getRelativeLuminance(bgColor['red'], bgColor['green'], bgColor['blue']);
366
- const fL = this.getRelativeLuminance(fgColor['red'], fgColor['green'], fgColor['blue']);
367
- 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;
368
259
  }
369
260
  hasValidContrastRatio(contrast, fontSize, isBold) {
370
- const isSmallFont = (isBold && parseFloat(fontSize) < 18.6667) || (!isBold && parseFloat(fontSize) < 24);
371
- const expectedContrastRatio = isSmallFont ? 4.5 : 3;
372
- return contrast >= expectedContrastRatio;
261
+ const size = parseFloat(fontSize);
262
+ const threshold = (isBold && size >= 18.66) || size >= 24 ? 3 : 4.5;
263
+ return (contrast + 0.02) >= threshold;
373
264
  }
374
- getTextSize(font, fontSize, bold, italic, text) {
375
- return window.DomUtils.getTextSize(font, fontSize, bold, italic, text);
265
+ isBold(fontWeight) {
266
+ return !!fontWeight && ['bold', 'bolder', '700', '800', '900'].includes(fontWeight);
376
267
  }
377
- getColorInGradient(fromColor, toColor, ratio) {
378
- const red = fromColor['red'] + (toColor['red'] - fromColor['red']) * ratio;
379
- const green = fromColor['green'] + (toColor['green'] - fromColor['green']) * ratio;
380
- const blue = fromColor['blue'] + (toColor['blue'] - fromColor['blue']) * ratio;
381
- 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;
382
270
  }
383
271
  }
384
272
  exports.QW_ACT_R37 = QW_ACT_R37;
@@ -387,7 +275,6 @@ __decorate([
387
275
  applicability_1.ElementIsHTMLElement,
388
276
  (0, applicability_1.ElementIsNot)(['html', 'head', 'body', 'script', 'style', 'meta']),
389
277
  applicability_1.ElementIsVisible,
390
- applicability_1.ElementHasText,
391
278
  __metadata("design:type", Function),
392
279
  __metadata("design:paramtypes", [Function]),
393
280
  __metadata("design:returntype", void 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qualweb/act-rules",
3
- "version": "0.7.5",
3
+ "version": "0.7.6",
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.4",
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.4",
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",