@qualweb/act-rules 0.8.0 → 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.
@@ -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 isHumanLanguage;
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;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"}
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"}
@@ -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
- const visible = window.DomUtils.isElementVisible(element);
24
- if (!visible)
26
+ if (!window.DomUtils.isElementVisible(element))
25
27
  return;
26
28
  const nodeName = element.getElementTagName();
27
- const isInputField = ['input', 'select', 'textarea'].includes(nodeName);
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 disabledWidgets = window.disabledWidgets;
36
- const elementSelectors = element.getElementSelector();
37
- for (const disableWidget of disabledWidgets || []) {
38
- const selectorsResult = window.AccessibilityUtils.getAccessibleNameSelector(disableWidget);
39
- const selectors = typeof selectorsResult === 'string' ? [selectorsResult] : selectorsResult;
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
- let bgColor = this.getBackground(element);
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
- 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
- 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.verdict = evaluation_1.Verdict.WARNING;
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 regexGradient = /((\w-?)*gradient.*)/gm;
91
- let regexGradientMatches = bgColor.match(regexGradient);
92
- if (regexGradientMatches) {
93
- if (this.isHumanLanguage(elementText || placeholder || "")) {
94
- this.evaluateGradient(test, element, regexGradientMatches[0], fgColor, opacity, fontSize, fontWeight, fontStyle, fontFamily, elementText || placeholder || "");
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.verdict = evaluation_1.Verdict.PASSED;
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
- 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;
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
- if (!parsedBG || parsedBG.alpha === 0) {
119
- parsedBG = { red: 255, green: 255, blue: 255, alpha: 1 };
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
- if (parsedBG.alpha < 1) {
122
- parsedBG = this.flattenColors(parsedBG, { red: 255, green: 255, blue: 255, alpha: 1 });
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 parsedFG = this.parseRGBString(fgColor);
125
- if (parsedFG) {
126
- parsedFG.alpha *= opacity;
127
- if (!this.equals(parsedBG, parsedFG)) {
128
- const textToVerify = elementText || placeholder || "";
129
- if (this.isHumanLanguage(textToVerify)) {
130
- const contrastRatio = this.getContrast(parsedBG, parsedFG);
131
- const isValid = this.hasValidContrastRatio(contrastRatio, fontSize, this.isBold(fontWeight));
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);
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
- isHumanLanguage(text) {
147
- return window.DomUtils.isHumanLanguage(text);
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
- evaluateGradient(test, element, parsedGradientString, fgColor, opacity, fontSize, fontWeight, fontStyle, fontFamily, elementText) {
150
- if (parsedGradientString.startsWith('linear-gradient')) {
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));
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
- for (const color of colors) {
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
- else {
174
- test.verdict = evaluation_1.Verdict.WARNING;
175
- test.resultCode = 'W3';
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
- test.addElement(element);
178
- this.addTestResult(test);
179
- return true;
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 colorsMatch = gradient.match(regex);
244
+ const matches = (_a = gradient.match(regex)) !== null && _a !== void 0 ? _a : [];
184
245
  const colors = [];
185
- for (const stringColor of colorsMatch || []) {
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) * ratio,
195
- green: fromColor.green + (toColor.green - fromColor.green) * ratio,
196
- blue: fromColor.blue + (toColor.blue - fromColor.blue) * ratio,
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 { red: parseInt(rgba[1]), green: parseInt(rgba[2]), blue: parseInt(rgba[3]), alpha: Math.round(parseFloat(rgba[4]) * 100) / 100 };
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 color = new colorjs_io_1.default(colorString);
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 = color.alpha) !== null && _a !== void 0 ? _a : 1
286
+ alpha: (_a = srgb.alpha) !== null && _a !== void 0 ? _a : 1
232
287
  };
233
288
  }
234
- catch (e) {
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 a = [c.red, c.green, c.blue].map(v => {
255
- v /= 255;
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 a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
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 (contrast + 0.02) >= threshold;
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.8.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.11",
48
+ "@qualweb/core": "^0.8.12",
49
49
  "@qualweb/locale": "0.2.2",
50
- "@qualweb/qw-page": "0.3.6",
51
- "@qualweb/util": "0.7.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",