Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions server/src/accessibilityPatterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@

// Order based om most common types first
const patterns: string[] = [
'<div(>|)(?:.)+?>',
'<span(>|)(?:.)+?>',
'<div(?:\\s+[^>]*)?>',
'<span(?:\\s+[^>]*)?>',
// "id=\"(?:.)+?\"",
'<a (?:.)+?>(?:(?:\\s|\\S)+?(?=</a>))</a>',
'<img (?:.)+?>',
'<input (?:.)+?>',
'<head (?:.|)+?>(?:(?:\\s|\\S|)+?(?=</head>))</head>',
'<html(>|)(?:.)+?>',
'tabindex="(?:.)+?"',
'<(?:i|)frame (?:.|)+?>',
'<a(?:\\s+[^>]*)?>(.*)(?=</a>)',
'<img(?:\\s+[^>]*)?>',
'<input(?:\\s+[^>]*)?>',
'<head(?:\\s+[^>]*)?>(.*)(?=</head>)',
'<html(?:\\s+[^>]*)?>',
'tabindex=".*"',
'<i?frame(?:\\s+[^>]*)?>',
];
export const pattern: RegExp = new RegExp(patterns.join('|'), 'ig');
export const pattern: RegExp = new RegExp(patterns.join('|'), 'igs');

const nonDescriptiveAlts: string[] = [
'alt="image"',
Expand Down Expand Up @@ -63,9 +63,9 @@ export async function validateDiv(m: RegExpExecArray) {
}

export async function validateSpan(m: RegExpExecArray) {
if (!/role=(?:.*?[a-z].*?)"/i.test(m[0])) {
if (!/<span(?:.+?)(?:aria-hidden="true)(?:.+?)>/.test(m[0])) {
if (/<span(?:.+?)(?:button|btn)(?:.+?)>/.test(m[0])) {
if (!/role=(?:.*[a-z].*)"/im.test(m[0])) {
if (!/<span(?:.*)(?:aria-hidden="true)(?:.*)>/si.test(m[0])) {
if (/<span(?:.*)(?:button|btn)(?:.*)>/si.test(m[0])) {
return {
meta: m,
mess: 'Change the span to a <button>',
Expand All @@ -85,9 +85,9 @@ export async function validateSpan(m: RegExpExecArray) {
export async function validateA(m: RegExpExecArray) {
let aRegEx: RegExpExecArray;
let oldRegEx: RegExpExecArray = m;
let filteredString = m[0].replace(/<(?:\s|\S)+?>/gi, '');
if (!/(?:\S+?)/gi.test(filteredString)) {
aRegEx = /<a(?:.)+?>/i.exec(oldRegEx[0]);
let filteredString = m[0].replace(/<(?:[\s\S]*)>/gi, '');
if (!/(?:\S*)/gi.test(filteredString)) {
aRegEx = /<a(?:.*)>/i.exec(oldRegEx[0]);
aRegEx.index = oldRegEx.index;
return {
meta: aRegEx,
Expand All @@ -99,7 +99,7 @@ export async function validateA(m: RegExpExecArray) {

export async function validateImg(m: RegExpExecArray) {
// Ordered by approximate frequency of the issue
if (!/alt="(?:.*?[a-z].*?)"/i.test(m[0]) && !/alt=""/i.test(m[0])) {
if (!/alt="(?:.*[a-z].*)"/i.test(m[0]) && !/alt=""/i.test(m[0])) {
return {
meta: m,
mess: 'Provide an alt text that describes the image, or alt="" if image is purely decorative',
Expand All @@ -121,7 +121,7 @@ export async function validateImg(m: RegExpExecArray) {
};
}
// Most screen readers cut off alt text at 125 characters.
if (/alt="(?:.*?[a-z].*.{125,}?)"/i.test(m[0])) {
if (/alt="(?:.*[a-z].{125,}?)"/i.test(m[0])) {
return {
meta: m,
mess: 'Alt text is too long',
Expand All @@ -133,7 +133,7 @@ export async function validateImg(m: RegExpExecArray) {
export async function validateMeta(m: RegExpExecArray) {
let metaRegEx: RegExpExecArray;
let oldRegEx: RegExpExecArray = m;
if ((metaRegEx = /<meta(?:.+?)viewport(?:.+?)>/i.exec(oldRegEx[0]))) {
if ((metaRegEx = /<meta(?:.*)viewport(?:.*)>/i.exec(oldRegEx[0]))) {
metaRegEx.index = oldRegEx.index + metaRegEx.index;
if (!/user-scalable=yes/i.test(metaRegEx[0])) {
return {
Expand All @@ -156,16 +156,16 @@ export async function validateTitle(m: RegExpExecArray) {
let titleRegEx: RegExpExecArray;
let oldRegEx: RegExpExecArray = m;
if (!/<title>/i.test(oldRegEx[0])) {
titleRegEx = /<head(?:|.+?)>/i.exec(oldRegEx[0]);
titleRegEx = /<head(?:.*)>/i.exec(oldRegEx[0]);
titleRegEx.index = oldRegEx.index;
return {
meta: titleRegEx,
mess: 'Provide a title within the <head> tags',
severity: 1,
};
} else {
titleRegEx = /<title>(?:|.*?[a-z].*?|\s+?)<\/title>/i.exec(oldRegEx[0]);
if (/>(?:|\s+?)</i.test(titleRegEx[0])) {
titleRegEx = /<title>(?:.*[a-z].*|\s*)?<\/title>/i.exec(oldRegEx[0]);
if (/<title>\s*<\/title>/i.test(titleRegEx[0])) {
titleRegEx.index = oldRegEx.index + titleRegEx.index;
return {
meta: titleRegEx,
Expand All @@ -177,7 +177,7 @@ export async function validateTitle(m: RegExpExecArray) {
}

export async function validateHtml(m: RegExpExecArray) {
if (!/lang=(?:.*?[a-z].*?)"/i.test(m[0])) {
if (!/lang=(?:.*[a-z].*)"/i.test(m[0])) {
return {
meta: m,
mess: 'Provide a language [lang=""]',
Expand All @@ -191,7 +191,7 @@ export async function validateInput(m: RegExpExecArray) {
case /type="hidden"/i.test(m[0]):
break;
case /aria-label=/i.test(m[0]):
if (!/aria-label="(?:(?![a-z]*?)|\s|)"/i.test(m[0])) {
if (!/aria-label="(?:(?![a-z]*)|\s*)"/i.test(m[0])) {
break;
} else {
return {
Expand All @@ -201,8 +201,8 @@ export async function validateInput(m: RegExpExecArray) {
};
}
case /id=/i.test(m[0]):
if (/id="(?:.*?[a-z].*?)"/i.test(m[0])) {
let idValue = /id="(.*?[a-z].*?)"/i.exec(m[0])[1];
if (/id="(?:.*[a-z].*)"/i.test(m[0])) {
let idValue = /id="(.*[a-z].*)"/i.exec(m[0])[1];
let pattern: RegExp = new RegExp('for="' + idValue + '"', 'i');
if (pattern.test(m.input)) {
break;
Expand All @@ -221,7 +221,7 @@ export async function validateInput(m: RegExpExecArray) {
};
}
case /aria-labelledby=/i.test(m[0]):
if (!/aria-labelledby="(?:(?![a-z]*?)|\s|)"/i.test(m[0])) {
if (!/aria-labelledby="(?:(?![a-z]*?)|\s?)"/i.test(m[0])) {
// TODO: needs to check elements with the same value.
break;
} else {
Expand Down Expand Up @@ -254,7 +254,7 @@ export async function validateTab(m: RegExpExecArray) {
}

export async function validateFrame(m: RegExpExecArray) {
if (!/title=(?:.*?[a-z].*?)"/i.test(m[0])) {
if (!/title=(?:.*[a-z].*)"/i.test(m[0])) {
return {
meta: m,
mess: 'Provide a title that describes the frame\'s content [title=""]',
Expand Down
66 changes: 34 additions & 32 deletions server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,42 @@ async function validateTextDocument(textDocument: server.TextDocument): Promise<
let m: RegExpExecArray | null;
let diagnostics: server.Diagnostic[] = [];

async function _diagnostics(regEx: RegExpExecArray, diagnosticsMessage: string, severityNumber: number) {
let severity: server.DiagnosticSeverity;

switch (severityNumber) {
case 1:
severity = server.DiagnosticSeverity.Error;
break;
case 2:
severity = server.DiagnosticSeverity.Warning;
break;
case 3:
severity = server.DiagnosticSeverity.Information;
break;
case 4:
severity = server.DiagnosticSeverity.Hint;
break;
}

let diagnostic: server.Diagnostic = {
severity,
message: diagnosticsMessage,
range: {
start: textDocument.positionAt(regEx.index),
end: textDocument.positionAt(regEx.index + regEx[0].length),
},
code: 0,
source: 'web accessibility',
};

diagnostics.push(diagnostic);
}

while ((m = pattern.pattern.exec(text)) && problems < settings.maxNumberOfProblems) {
if (m != null) {
let el = m[0].slice(0, 5);
const el = m[0].slice(0, 5);

switch (true) {
// ID
// case (/id="/i.test(el)):
Expand Down Expand Up @@ -188,37 +221,6 @@ async function validateTextDocument(textDocument: server.TextDocument): Promise<
}
}

async function _diagnostics(regEx: RegExpExecArray, diagnosticsMessage: string, severityNumber: number) {
let severity: server.DiagnosticSeverity;

switch (severityNumber) {
case 1:
severity = server.DiagnosticSeverity.Error;
break;
case 2:
severity = server.DiagnosticSeverity.Warning;
break;
case 3:
severity = server.DiagnosticSeverity.Information;
break;
case 4:
severity = server.DiagnosticSeverity.Hint;
break;
}

let diagnostic: server.Diagnostic = {
severity,
message: diagnosticsMessage,
range: {
start: textDocument.positionAt(regEx.index),
end: textDocument.positionAt(regEx.index + regEx[0].length),
},
code: 0,
source: 'web accessibility',
};

diagnostics.push(diagnostic);
}
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}

Expand Down