diff options
context:
space:
mode:
-rw-r--r--README.txt45
-rw-r--r--assets/cgit.css40
-rw-r--r--assets/cgit.js26
-rw-r--r--source/ui-blame.c4
-rw-r--r--source/ui-commit.c12
-rw-r--r--source/ui-diff.c71
-rw-r--r--source/ui-log.c41
-rw-r--r--source/ui-refs.c29
-rw-r--r--source/ui-repolist.c17
-rw-r--r--source/ui-shared.c51
-rw-r--r--source/ui-ssdiff.c11
-rw-r--r--source/ui-ssdiff.h7
-rw-r--r--source/ui-stats.c25
-rw-r--r--source/ui-summary.c20
-rw-r--r--source/ui-tag.c10
-rw-r--r--source/ui-tree.c32
-rwxr-xr-xtests/t0005-markup.sh99
-rwxr-xr-xtests/t0113-tag.sh16
-rwxr-xr-xtests/t0201-filters.sh4
-rwxr-xr-xtests/t0206-trailers.sh8
-rwxr-xr-xtests/t0208-mailmap.sh2
-rwxr-xr-xtests/t0506-link-trailers.sh2
22 files changed, 398 insertions, 174 deletions
diff --git a/README.txt b/README.txt
index d19950e..844b6d0 100644
--- a/README.txt
+++ b/README.txt
@@ -59,6 +59,51 @@ repositories to display. A fully commented starting point with every option at
its default is in custom/cgitrc, and MANUAL.txt documents each option in full.
+Customization
+-------------
+
+Every page is one document with the same skeleton, so a site stylesheet or
+script added through the css and js options has stable hooks to work with.
+
+* The root is div#cgit. It carries data-page with the page name, summary, log,
+ tree and so on, and data-repo with the repository url on repository pages,
+ so a rule can target one page or one repository. In embedded mode it also
+ carries the class cgit-embedded.
+* Inside it sit header#header, nav.tabs with the search form, nav.path for the
+ breadcrumb, main.content and footer.footer. Each nav, listing and option form
+ carries an aria-label, and the active tab and the current pager link carry
+ aria-current.
+* Every listing is a table.list with a second class naming it, repolist,
+ summary, refs, log or tree. Each section of a listing is a tbody named
+ repos, branches, tags, log, clone or tree, and each row carries its kind,
+ repo, branch, tag, commit, dir, blob, link or mod. A file row's link also
+ carries ext- followed by the file extension.
+* The commit and tag pages use table.commit-info, the trailer table is
+ table.commit-trailers, the diffstat is table.diffstat and each file of a diff
+ is div.file, or tbody.file in the side by side view, with the path in
+ data-path. The blob and blame pages open with div.blob-header.
+* Ages are time elements with an age-* class and the timestamp in data-ut. Line
+ numbers are anchors named n followed by the line.
+* Colours, fonts and metrics are custom properties on div#cgit, so a theme can
+ redefine those alone. Every rule in cgit.css starts with div#cgit, and a rule
+ of your own needs the same prefix to win.
+* The script exposes window.cgit.updateAges and window.cgit.highlightLines for
+ a page that changes the rows or the lines after load.
+
+A theme that only wants a colour change is a few lines.
+
+ div#cgit {
+ --link: light-dark(#7a1f1f, #f0a0a0);
+ --font-sans: Georgia, serif;
+ }
+
+ div#cgit[data-page='log'] table.list tr.commit:hover {
+ background: var(--surface);
+ }
+
+tests/t0005-markup.sh checks that every hook named here is still emitted.
+
+
Filter extensions
-----------------
diff --git a/assets/cgit.css b/assets/cgit.css
index 90fa074..dbd807c 100644
--- a/assets/cgit.css
+++ b/assets/cgit.css
@@ -350,8 +350,8 @@ div#cgit table.list tr.nohover:hover {
background: var(--bg);
}
-div#cgit table.list tr.nohover td:empty {
- height: calc(1lh + 0.6em);
+div#cgit table.list tbody + tbody > tr:first-child > th {
+ padding-top: calc(1lh + 0.95em);
}
div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
@@ -436,12 +436,12 @@ div#cgit table.list td a:hover {
color: var(--link);
}
-div#cgit div#summary {
+div#cgit div#about {
vertical-align: top;
margin-bottom: 1em;
}
-div#cgit div#summary pre {
+div#cgit div#about pre {
overflow-x: auto;
}
@@ -783,17 +783,25 @@ div#cgit div.cgit-panel {
margin-top: 1.5em;
}
-div#cgit div.cgit-panel table {
+div#cgit div.cgit-panel .fields {
+ display: grid;
+ grid-template-columns: max-content max-content;
+ align-items: center;
+ justify-items: start;
+ padding-top: 0.5em;
border: solid 1px var(--border-mid);
background-color: var(--surface);
}
-div#cgit div.cgit-panel th {
- text-align: center;
+div#cgit div.cgit-panel label,
+div#cgit div.cgit-panel select,
+div#cgit div.cgit-panel input {
+ margin: 0.25em 0.5em;
}
-div#cgit div.cgit-panel td {
- padding: 0.25em 0.5em;
+div#cgit div.cgit-panel input[type="submit"] {
+ grid-column: 2;
+ justify-self: start;
}
div#cgit table.commit-info th {
@@ -998,23 +1006,23 @@ div#cgit a.button {
padding: 0 0.5em;
}
-div#cgit ul.pager {
+div#cgit nav.pager ul {
list-style-type: none;
text-align: center;
margin: 1em 0 0 0;
padding: 0;
}
-div#cgit ul.pager li {
+div#cgit nav.pager li {
display: inline-block;
margin: 0.25em 0.5em;
}
-div#cgit ul.pager a {
+div#cgit nav.pager a {
color: var(--muted);
}
-div#cgit ul.pager .current {
+div#cgit nav.pager .current {
font-weight: bold;
}
@@ -1192,7 +1200,7 @@ div#cgit table.ssdiff td.add {
min-width: 50%;
}
-div#cgit table.ssdiff td.add_dark {
+div#cgit table.ssdiff td.add-dark {
color: var(--fg);
background: var(--ss-add-bg2);
min-width: 50%;
@@ -1209,7 +1217,7 @@ div#cgit table.ssdiff td.del {
min-width: 50%;
}
-div#cgit table.ssdiff td.del_dark {
+div#cgit table.ssdiff td.del-dark {
color: var(--fg);
background: var(--ss-del-bg2);
min-width: 50%;
@@ -1226,7 +1234,7 @@ div#cgit table.ssdiff td.changed {
min-width: 50%;
}
-div#cgit table.ssdiff td.changed_dark {
+div#cgit table.ssdiff td.changed-dark {
color: var(--fg);
background: var(--ss-chg-bg2);
min-width: 50%;
diff --git a/assets/cgit.js b/assets/cgit.js
index 78adfd0..2c0a828 100644
--- a/assets/cgit.js
+++ b/assets/cgit.js
@@ -54,12 +54,13 @@ function render_age(element, age) {
}
/*
- * Measures every age on the page against the clock and books the next pass.
- * There is no point coming back before the finest unit on the page could
- * change, so a page already counted in hours is left alone for minutes and
- * one counted in years for a day.
+ * Measures every age on the page against the clock and returns how many
+ * seconds may pass before one of them could read differently. There is no
+ * point coming back before the finest unit on the page could change, so a
+ * page already counted in hours is left alone for minutes and one counted in
+ * years for a day.
*/
-function refresh_ages() {
+function update_ages() {
var bucket, elements, i, age;
var delay = 24 * 3600;
var now = Math.round(new Date().getTime() / 1000);
@@ -82,11 +83,19 @@ function refresh_ages() {
}
}
- window.setTimeout(refresh_ages, delay * 1000);
+ return delay;
+}
+
+function refresh_ages() {
+ window.setTimeout(refresh_ages, update_ages() * 1000);
}
document.addEventListener("DOMContentLoaded", refresh_ages, false);
+// A site script that adds rows of its own can ask for one pass over them.
+window.cgit = window.cgit || {};
+window.cgit.updateAges = update_ages;
+
})();
/*
@@ -139,4 +148,9 @@ function place_bar() {
document.addEventListener("DOMContentLoaded", place_bar, false);
window.addEventListener("hashchange", place_bar, false);
+// A site script that moves the lines about can ask for the bar to be laid
+// again.
+window.cgit = window.cgit || {};
+window.cgit.highlightLines = place_bar;
+
})();
diff --git a/source/ui-blame.c b/source/ui-blame.c
index c020dbc..5d88f94 100644
--- a/source/ui-blame.c
+++ b/source/ui-blame.c
@@ -294,11 +294,11 @@ static void print_blame_page(const struct object_id *oid, const char *path,
cgit_set_title_from_path(path);
cgit_print_layout_start();
- htmlf("blob: %s (", oid_to_hex(oid));
+ htmlf("<div class='blob-header'>blob: %s (", oid_to_hex(oid));
cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path);
html(") (");
cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path);
- html(")\n");
+ html(")</div>\n");
// A NUL past the window buffer_is_binary sniffs would end html_txt
// early while the hash and line number columns still cover the whole
diff --git a/source/ui-commit.c b/source/ui-commit.c
index 1fdc5fa..1ac96aa 100644
--- a/source/ui-commit.c
+++ b/source/ui-commit.c
@@ -31,7 +31,7 @@
static void print_ident_row(const char *role, const char *name, const char *email,
timestamp_t date, int tz)
{
- htmlf("<tr><th>%s</th><td>", role);
+ htmlf("<tr><th scope='row'>%s</th><td>", role);
cgit_open_filter(ctx.repo->email_filter, email ? email : "", "commit");
html_txt(name);
if (ctx.cfg.enable_plain_email) {
@@ -69,7 +69,7 @@ static int print_parent_rows(struct commit *commit, const char *rev, const char
}
if (!parents)
oidcpy(first, &p->item->object.oid);
- html("<tr><th>parent</th><td colspan='2' class='oid'><div class='spanning'>");
+ html("<tr><th scope='row'>parent</th><td colspan='2' class='oid'><div class='spanning'>");
parent_hex = label = oid_to_hex(&p->item->object.oid);
info = ctx.repo->enable_subject_links ? cgit_parse_commit(parent) : NULL;
if (info)
@@ -178,7 +178,7 @@ static void print_trailers(const char *msg)
html("</td></tr>\n");
strbuf_reset(&key);
strbuf_addbuf(&key, &iter.key);
- html("<tr><th>");
+ html("<tr><th scope='row'>");
html_txt(key.buf);
html("</th><td>");
open = 1;
@@ -279,7 +279,7 @@ void cgit_print_commit(char *hex, const char *prefix)
// The spanning cells are kept out of the column widths, so a hash row
// never pushes the dates away from the names above it.
- html("<tr><th>commit</th><td colspan='2' class='oid'><div class='spanning'>");
+ html("<tr><th scope='row'>commit</th><td colspan='2' class='oid'><div class='spanning'>");
commit_hex = oid_to_hex(&commit->object.oid);
cgit_commit_link(commit_hex, NULL, NULL, ctx.qry.head, commit_hex, prefix);
html(" (");
@@ -290,7 +290,7 @@ void cgit_print_commit(char *hex, const char *prefix)
// together, leaving the tree beside the download links.
parents = print_parent_rows(commit, hex, prefix, &first_oid);
- html("<tr><th>tree</th><td colspan='2' class='oid'><div class='spanning'>");
+ html("<tr><th scope='row'>tree</th><td colspan='2' class='oid'><div class='spanning'>");
tree_rev = xstrdup(hex);
cgit_tree_link(oid_to_hex(get_commit_tree_oid(commit)), NULL, NULL, ctx.qry.head, tree_rev, NULL);
if (prefix) {
@@ -301,7 +301,7 @@ void cgit_print_commit(char *hex, const char *prefix)
html("</div></td></tr>\n");
if (ctx.repo->snapshots) {
- html("<tr><th>download</th><td colspan='2' class='oid'><div class='spanning'>");
+ html("<tr><th scope='row'>download</th><td colspan='2' class='oid'><div class='spanning'>");
cgit_print_snapshot_links(ctx.repo, hex, "<br>");
html("</div></td></tr>\n");
}
diff --git a/source/ui-diff.c b/source/ui-diff.c
index f258e9f..5c9cd1c 100644
--- a/source/ui-diff.c
+++ b/source/ui-diff.c
@@ -234,6 +234,13 @@ static void print_file_header(const struct object_id *old_oid, char *old_path, u
int subproject;
subproject = (S_ISGITLINK(old_mode) || S_ISGITLINK(new_mode));
+ // The side by side view opens its file group before the header row,
+ // so only the unified view opens one here.
+ if (!use_ssdiff) {
+ html("<div class='file' data-path='");
+ html_attr(new_path);
+ html("'>");
+ }
html("<div class='head'>");
html("diff --git a/");
html_txt(old_path);
@@ -282,6 +289,20 @@ static void print_file_header(const struct object_id *old_oid, char *old_path, u
}
/*
+ * Closes the group print_file_header or cgit_ssdiff_header_begin opened. The
+ * side by side footer closes its own, so the unified view is the only one with
+ * work to do here.
+ */
+static void end_file(void)
+{
+ // The unified table keeps its whitespace, so no newline follows.
+ if (use_ssdiff)
+ cgit_ssdiff_footer();
+ else
+ html("</div>");
+}
+
+/*
* The length passed counts the terminator, because a renderer strips the last
* byte of every line it is given.
*/
@@ -315,12 +336,11 @@ static void print_truncated(const char *path)
cgit_diff_link("View it on its own page", NULL, NULL, ctx.qry.head, ctx.qry.oid, ctx.qry.oid2,
path);
html(".");
- if (use_ssdiff) {
+ if (use_ssdiff)
html("</td></tr>");
- cgit_ssdiff_footer();
- } else {
+ else
html("</div>");
- }
+ end_file();
}
static struct fileinfo *reserve_item(void)
@@ -353,7 +373,7 @@ static void collect_body(struct diff_filepair *pair, struct fileinfo *item, int
html_capture_begin(body);
if (use_ssdiff)
- cgit_ssdiff_header_begin();
+ cgit_ssdiff_header_begin(pair->two->path);
print_file_header(&pair->one->oid, pair->one->path, pair->one->mode,
&pair->two->oid, pair->two->path, pair->two->mode);
if (use_ssdiff)
@@ -383,8 +403,7 @@ static void collect_body(struct diff_filepair *pair, struct fileinfo *item, int
if (*binary)
print_binary_differs();
}
- if (use_ssdiff)
- cgit_ssdiff_footer();
+ end_file();
if (render_suppressed) {
// Setting the length back would keep the grown allocation,
@@ -491,7 +510,7 @@ static void filepair_cb(struct diff_filepair *pair)
idx = item_idx++;
current_filepair = pair;
if (use_ssdiff) {
- cgit_ssdiff_header_begin();
+ cgit_ssdiff_header_begin(pair->two->path);
line_fn = cgit_ssdiff_line_cb;
}
print_file_header(&pair->one->oid, pair->one->path, pair->one->mode,
@@ -505,8 +524,7 @@ static void filepair_cb(struct diff_filepair *pair)
}
if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
print_subproject_lines(pair, line_fn);
- if (use_ssdiff)
- cgit_ssdiff_footer();
+ end_file();
return;
}
if (cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size, &new_size,
@@ -514,8 +532,7 @@ static void filepair_cb(struct diff_filepair *pair)
cgit_print_error("Error running diff");
if (binary)
print_binary_differs();
- if (use_ssdiff)
- cgit_ssdiff_footer();
+ end_file();
}
static void print_raw_patch(const struct object_id *old_tree_oid, const struct object_id *new_tree_oid)
@@ -553,14 +570,11 @@ void cgit_print_diff_ctrls(void)
html("<div class='cgit-panel'>\n");
html("<strong>diff options</strong>");
- html("<form method='get'>");
+ html("<form method='get' aria-label='Diff options'>");
cgit_add_hidden_formfields(1, 0, ctx.qry.page);
- html("<table>");
- html("<tr><td colspan='2'></td></tr>");
- html("<tr>");
- html("<td class='label'>context:</td>");
- html("<td class='ctrl'>");
- html("<select name='context'>");
+ html("<div class='fields'>");
+ html("<label for='diff-context'>context:</label>");
+ html("<select id='diff-context' name='context'>");
selected = ctx.qry.context;
if (!selected)
selected = DEFAULT_DIFF_CONTEXT;
@@ -569,27 +583,20 @@ void cgit_print_diff_ctrls(void)
for (i = 15; i <= MAX_DIFF_CONTEXT_LINES; i += 5)
html_intoption(i, cgit_fmt("%d", i), selected);
html("</select>");
- html("</td>");
- html("</tr><tr>");
- html("<td class='label'>space:</td>");
- html("<td class='ctrl'>");
- html("<select name='ignorews'>");
+ html("<label for='diff-space'>space:</label>");
+ html("<select id='diff-space' name='ignorews'>");
html_intoption(0, "include", ctx.qry.ignorews);
html_intoption(1, "ignore", ctx.qry.ignorews);
html("</select>");
- html("</td>");
- html("</tr><tr>");
- html("<td class='label'>mode:</td>");
- html("<td class='ctrl'>");
- html("<select name='dt'>");
+ html("<label for='diff-mode'>mode:</label>");
+ html("<select id='diff-mode' name='dt'>");
selected = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
html_intoption(0, "unified", selected);
html_intoption(1, "ssdiff", selected);
html_intoption(2, "stat only", selected);
- html("</select></td></tr>");
- html("<tr><td></td><td class='ctrl'>");
+ html("</select>");
html("<input type='submit' value='reload'>");
- html("</td></tr></table>");
+ html("</div>");
html("</form>\n");
html("</div>\n");
}
diff --git a/source/ui-log.c b/source/ui-log.c
index 86cb756..9f21114 100644
--- a/source/ui-log.c
+++ b/source/ui-log.c
@@ -227,7 +227,7 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
print_graph_above(revs, &graphbuf, columns);
info = cgit_parse_commit(commit);
- htmlf("<tr%s>", ctx.qry.showmsg ? " class='logheader'" : "");
+ htmlf("<tr class='commit%s'>", ctx.qry.showmsg ? " logheader" : "");
if (revs->graph) {
html("<td class='commitgraph'>");
@@ -372,11 +372,11 @@ static void print_pager(struct rev_info *revs, int ofs, int cnt)
{
struct commit *more = get_revision(revs);
- html("</table>\n");
+ html("</tbody>\n</table>\n");
// A single page needs no pager, and an empty list has nothing to page.
if (ofs <= 0 && !more)
return;
- html("<ul class='pager'>");
+ html("<nav class='pager' aria-label='Pager'><ul>");
if (ofs > 0) {
html("<li>");
cgit_log_link(
@@ -393,7 +393,7 @@ static void print_pager(struct rev_info *revs, int ofs, int cnt)
);
html("</li>");
}
- html("</ul>");
+ html("</ul></nav>");
}
/*
@@ -562,15 +562,15 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
if (pager) {
cgit_print_layout_start();
- html("<table class='list'>\n");
+ html("<table class='list log' aria-label='Commits'>\n");
}
- html("<tr class='nohover'>");
+ html("<tbody class='log'>\n<tr class='nohover'>");
if (commit_graph)
html("<th></th>");
else
- html("<th class='left'>Age</th>");
- html("<th class='left'>Commit message");
+ html("<th class='left' scope='col'>Age</th>");
+ html("<th class='left' scope='col'>Commit message");
if (pager) {
html(" (");
cgit_log_link(
@@ -580,15 +580,15 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
);
html(")");
}
- html("</th><th class='left col-author'>Author</th>");
+ html("</th><th class='left col-author' scope='col'>Author</th>");
if (rev.graph)
- html("<th class='left'>Age</th>");
+ html("<th class='left' scope='col'>Age</th>");
if (ctx.repo->enable_log_filecount) {
- html("<th class='left col-count'>Files</th>");
+ html("<th class='left col-count' scope='col'>Files</th>");
columns++;
}
if (ctx.repo->enable_log_linecount) {
- html("<th class='left col-count'>Lines</th>");
+ html("<th class='left col-count' scope='col'>Lines</th>");
columns++;
}
html("</tr>\n");
@@ -617,13 +617,16 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
if (pager) {
print_pager(&rev, ofs, cnt);
cgit_print_layout_end();
- } else if ((commit = get_revision(&rev))) {
- htmlf("<tr class='nohover'><td colspan='%d'>", columns);
- cgit_log_link(
- "[...]", NULL, NULL, ctx.qry.head, NULL, ctx.qry.vpath, 0,
- NULL, NULL, ctx.qry.showmsg, ctx.qry.follow
- );
- html("</td></tr>\n");
+ } else {
+ if ((commit = get_revision(&rev))) {
+ htmlf("<tr class='nohover'><td colspan='%d'>", columns);
+ cgit_log_link(
+ "[...]", NULL, NULL, ctx.qry.head, NULL, ctx.qry.vpath, 0,
+ NULL, NULL, ctx.qry.showmsg, ctx.qry.follow
+ );
+ html("</td></tr>\n");
+ }
+ html("</tbody>\n");
}
if (must_free_tip)
diff --git a/source/ui-refs.c b/source/ui-refs.c
index d89e3ea..e4a0dd0 100644
--- a/source/ui-refs.c
+++ b/source/ui-refs.c
@@ -79,10 +79,11 @@ static void collect_branches(struct reflist *list)
static void print_branch_header(void)
{
html(
- "<tr class='nohover'><th class='left'>Branch</th>"
- "<th class='left'>Commit message</th>"
- "<th class='left col-author'>Author</th>"
- "<th colspan='2' class='left'>Age</th></tr>\n"
+ "<tbody class='branches'>\n"
+ "<tr class='nohover'><th class='left' scope='col'>Branch</th>"
+ "<th class='left' scope='col'>Commit message</th>"
+ "<th class='left col-author' scope='col'>Author</th>"
+ "<th colspan='2' class='left' scope='col'>Age</th></tr>\n"
);
}
@@ -93,7 +94,7 @@ static int print_branch(struct refinfo *ref)
if (!info)
return 1;
- html("<tr><td>");
+ html("<tr class='branch'><td>");
cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL, ctx.qry.showmsg, 0);
html("</td><td>");
@@ -128,10 +129,11 @@ static void collect_tags(struct reflist *list)
static void print_tag_header(void)
{
html(
- "<tr class='nohover'><th class='left'>Tag</th>"
- "<th class='left'>Download</th>"
- "<th class='left col-author'>Author</th>"
- "<th colspan='2' class='left'>Age</th></tr>\n"
+ "<tbody class='tags'>\n"
+ "<tr class='nohover'><th class='left' scope='col'>Tag</th>"
+ "<th class='left' scope='col'>Download</th>"
+ "<th class='left col-author' scope='col'>Author</th>"
+ "<th colspan='2' class='left' scope='col'>Age</th></tr>\n"
);
}
@@ -152,7 +154,7 @@ static int print_tag(struct refinfo *ref)
return 1;
}
- html("<tr><td>");
+ html("<tr class='tag'><td>");
cgit_tag_link(name, NULL, NULL, name);
html("</td><td>");
if (ctx.repo->snapshots && (obj->type == OBJ_COMMIT))
@@ -255,6 +257,7 @@ static void print_branches_page(int pagesize)
if (page.size < list.count)
print_ref_pager(page.start, page.size, list.count, "heads");
+ html("</tbody>\n");
cgit_free_reflist_inner(&list);
}
@@ -278,6 +281,7 @@ static void print_tags_page(int pagesize)
if (page.size < list.count)
print_ref_pager(page.start, page.size, list.count, "tags");
+ html("</tbody>\n");
cgit_free_reflist_inner(&list);
}
@@ -305,6 +309,7 @@ void cgit_print_branches(int maxcount)
if (maxcount < list.count)
print_refs_link("heads");
+ html("</tbody>\n");
cgit_free_reflist_inner(&list);
}
@@ -328,6 +333,7 @@ void cgit_print_tags(int maxcount)
if (maxcount < list.count)
print_refs_link("tags");
+ html("</tbody>\n");
cgit_free_reflist_inner(&list);
}
@@ -335,7 +341,7 @@ void cgit_print_tags(int maxcount)
void cgit_print_refs(void)
{
cgit_print_layout_start();
- html("<table class='list'>\n");
+ html("<table class='list refs' aria-label='Branches and tags'>\n");
if (ctx.qry.path && starts_with(ctx.qry.path, "heads")) {
print_branches_page(ctx.cfg.max_ref_count);
@@ -343,7 +349,6 @@ void cgit_print_refs(void)
print_tags_page(ctx.cfg.max_ref_count);
} else {
cgit_print_branches(ctx.cfg.max_ref_count);
- html("<tr class='nohover'><td colspan='5'></td></tr>\n");
cgit_print_tags(ctx.cfg.max_ref_count);
}
html("</table>\n");
diff --git a/source/ui-repolist.c b/source/ui-repolist.c
index 031cb0d..6dcc976 100644
--- a/source/ui-repolist.c
+++ b/source/ui-repolist.c
@@ -181,7 +181,7 @@ static int any_repos_visible(void)
*/
static void print_column_header(const char *title, const char *column, const char *currenturl)
{
- htmlf("<th class='left col-%s'><a href='", column);
+ htmlf("<th class='left col-%s' scope='col'><a href='", column);
html_attr(currenturl);
htmlf("?s=%s", column);
if (ctx.qry.search) {
@@ -193,14 +193,14 @@ static void print_column_header(const char *title, const char *column, const cha
static void print_header_row(const char *currenturl)
{
- html("<tr class='nohover'>");
+ html("<tbody class='repos'>\n<tr class='nohover'>");
print_column_header("Name", "name", currenturl);
print_column_header("Description", "desc", currenturl);
if (ctx.cfg.enable_index_owner)
print_column_header("Owner", "owner", currenturl);
print_column_header("Idle", "idle", currenturl);
if (ctx.cfg.enable_index_links)
- html("<th class='left col-links'>Links</th>");
+ html("<th class='left col-links' scope='col'>Links</th>");
html("</tr>\n");
}
@@ -224,7 +224,8 @@ static void print_repo_row(const char *currenturl, int sublevel)
{
char *repourl;
- htmlf("<tr><td class='col-name %s'>", sublevel ? "sublevel-repo" : "toplevel-repo");
+ htmlf("<tr class='repo'><td class='col-name %s'>",
+ sublevel ? "sublevel-repo" : "toplevel-repo");
cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
html("</td><td class='col-desc'>");
repourl = cgit_repourl(ctx.repo->url);
@@ -267,7 +268,7 @@ static void print_pager(int total, int pagelen, char *search, char *sort)
int i, ofs;
char *class;
- html("<ul class='pager'>\n");
+ html("<nav class='pager' aria-label='Pager'><ul>\n");
for (i = 0, ofs = 0; ofs < total; i++, ofs = i * pagelen) {
class = (ctx.qry.ofs == ofs) ? "current" : NULL;
html("<li>");
@@ -275,7 +276,7 @@ static void print_pager(int total, int pagelen, char *search, char *sort)
ofs, 0);
html("</li>");
}
- html("</ul>\n");
+ html("</ul></nav>\n");
}
static int cmp_str(const char *s1, const char *s2)
@@ -420,7 +421,7 @@ void cgit_print_repolist(void)
sort_repolist("section");
currenturl = cgit_currenturl();
- html("<table class='list repolist'>\n");
+ html("<table class='list repolist' aria-label='Repositories'>\n");
for (i = 0; i < cgit_repolist.count; i++) {
char *section;
@@ -448,6 +449,8 @@ void cgit_print_repolist(void)
}
print_repo_row(currenturl, !column_sorted && section);
}
+ if (shown)
+ html("</tbody>\n");
html("</table>\n");
if (hits > ctx.cfg.max_repo_count)
print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search, ctx.qry.sort);
diff --git a/source/ui-shared.c b/source/ui-shared.c
index 4417975..0f2f1e3 100644
--- a/source/ui-shared.c
+++ b/source/ui-shared.c
@@ -104,6 +104,22 @@ static void site_url(const char *page, const char *search, const char *sort, int
}
}
+/*
+ * Write the class of a link, and mark the link as the current one for
+ * assistive technology when the class says it is the active tab or the
+ * current page of a pager.
+ */
+static void link_class(const char *class)
+{
+ if (!class)
+ return;
+ html(" class='");
+ html_attr(class);
+ html("'");
+ if (!strcmp(class, "active") || !strcmp(class, "current"))
+ html(" aria-current='page'");
+}
+
static void site_link(const char *page, const char *name, const char *title,
const char *class, const char *search, const char *sort, int ofs, int always_root)
{
@@ -113,11 +129,7 @@ static void site_link(const char *page, const char *name, const char *title,
html_attr(title);
html("'");
}
- if (class) {
- html(" class='");
- html_attr(class);
- html("'");
- }
+ link_class(class);
html(" href='");
site_url(page, search, sort, ofs, always_root);
html("'>");
@@ -142,11 +154,7 @@ static const char *repolink(const char *title, const char *class, const char *pa
html_attr(title);
html("'");
}
- if (class) {
- html(" class='");
- html_attr(class);
- html("'");
- }
+ link_class(class);
html(" href='");
if (ctx.cfg.virtual_root) {
html_url_path(ctx.cfg.virtual_root);
@@ -631,7 +639,7 @@ static void print_repo_tabs(void)
static void print_repo_search(void)
{
- html("<form class='search' method='get' action='");
+ html("<form class='search' role='search' method='get' action='");
if (ctx.cfg.virtual_root) {
char *fileurl = cgit_fileurl(ctx.qry.repo, "log", ctx.qry.vpath, NULL);
html_url_path(fileurl);
@@ -669,7 +677,7 @@ static void print_site_search(void)
{
char *currenturl = cgit_currenturl();
- html("<form class='search' method='get' action='");
+ html("<form class='search' role='search' method='get' action='");
html_attr(currenturl);
html("'>\n");
html("<input class='txt' type='search' name='q' aria-label='Search' value='");
@@ -1703,12 +1711,27 @@ void cgit_print_pageheader(void)
{
const struct object_id *pinned;
- html("<div id='cgit'>\n");
+ // The root names the page and the repository so a stylesheet or script
+ // can tell them apart without reading the tabs.
+ html("<div id='cgit'");
+ if (ctx.cfg.embedded)
+ html(" class='cgit-embedded'");
+ if (ctx.qry.page) {
+ html(" data-page='");
+ html_attr(ctx.qry.page);
+ html("'");
+ }
+ if (ctx.repo) {
+ html(" data-repo='");
+ html_attr(ctx.repo->url);
+ html("'");
+ }
+ html(">\n");
if (!ctx.env.authenticated || ctx.cfg.enable_header)
print_header();
if (ctx.env.authenticated)
- html("<nav class='tabs'>\n");
+ html("<nav class='tabs' aria-label='Pages'>\n");
if (ctx.env.authenticated && ctx.repo && ctx.empty_repo) {
// Every other tab needs a commit to render and the search form
// has nothing to search, so an empty repo gets summary alone.
diff --git a/source/ui-ssdiff.c b/source/ui-ssdiff.c
index ad543f4..afa25ea 100644
--- a/source/ui-ssdiff.c
+++ b/source/ui-ssdiff.c
@@ -201,7 +201,7 @@ static void print_row(const char *class, int old_line_no, char *old_line,
} else if (old_line) {
htmlf("<td class='lineno'></td><td class='%s'>", class);
} else {
- htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
+ htmlf("<td class='lineno'></td><td class='%s-dark'>", class);
}
if (old_line) {
if (lcs)
@@ -217,7 +217,7 @@ static void print_row(const char *class, int old_line_no, char *old_line,
} else if (new_line) {
htmlf("<td class='lineno'></td><td class='%s'>", class);
} else {
- htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
+ htmlf("<td class='lineno'></td><td class='%s-dark'>", class);
}
if (new_line) {
if (lcs)
@@ -401,10 +401,13 @@ void cgit_ssdiff_line_cb(char *line, int len)
line[len - 1] = terminator;
}
-void cgit_ssdiff_header_begin(void)
+void cgit_ssdiff_header_begin(const char *path)
{
current_old_line = -1;
current_new_line = -1;
+ html("<tbody class='file' data-path='");
+ html_attr(path);
+ html("'>\n");
html("<tr><td class='space' colspan='4'></td></tr>\n");
html("<tr><td class='head' colspan='4'>");
}
@@ -417,5 +420,5 @@ void cgit_ssdiff_header_end(void)
void cgit_ssdiff_footer(void)
{
print_deferred_lines();
- html("<tr><td class='foot' colspan='4'></td></tr>\n");
+ html("<tr><td class='foot' colspan='4'></td></tr>\n</tbody>\n");
}
diff --git a/source/ui-ssdiff.h b/source/ui-ssdiff.h
index f6489c8..9417277 100644
--- a/source/ui-ssdiff.h
+++ b/source/ui-ssdiff.h
@@ -22,9 +22,12 @@
extern void cgit_ssdiff_line_cb(char *line, int len);
-extern void cgit_ssdiff_header_begin(void);
+/*
+ * Open the group of rows one file's diff occupies, header row included, and
+ * close it again after the last line. path lands on the group as data-path.
+ */
+extern void cgit_ssdiff_header_begin(const char *path);
extern void cgit_ssdiff_header_end(void);
-
extern void cgit_ssdiff_footer(void);
#endif // CGIT_UI_SSDIFF_H
diff --git a/source/ui-stats.c b/source/ui-stats.c
index 3960b14..55ed370 100644
--- a/source/ui-stats.c
+++ b/source/ui-stats.c
@@ -332,10 +332,10 @@ static void print_authors(struct string_list *authors, int max_rows, const struc
size_t i, column, rows;
long total;
- html("<table class='stats'>\n<tr><th>Author</th>");
+ html("<table class='stats'>\n<tr><th scope='col'>Author</th>");
for (column = 0; column < labels->nr; column++)
- htmlf("<th>%s</th>", labels->items[column].string);
- html("<th>Total</th></tr>\n");
+ htmlf("<th scope='col'>%s</th>", labels->items[column].string);
+ html("<th scope='col'>Total</th></tr>\n");
// The row count arrives through ofs, which carries -1 for "all".
rows = (max_rows <= 0 || (size_t)max_rows > authors->nr) ? authors->nr : (size_t)max_rows;
@@ -375,29 +375,28 @@ static void print_options_form(const struct cgit_period *period, int top)
html("<div class='cgit-panel'>\n");
html("<strong>stat options</strong>");
- html("<form method='get'>");
+ html("<form method='get' aria-label='Stat options'>");
cgit_add_hidden_formfields(1, 0, "stats");
- html("<table><tr><td colspan='2'></td></tr>\n");
+ html("<div class='fields'>");
if (ctx.repo->max_stats > 1) {
choices = ctx.repo->max_stats;
- html("<tr><td class='label'>Period:</td>");
- html("<td class='ctrl'><select name='period'>");
+ html("<label for='stats-period'>Period:</label>");
+ html("<select id='stats-period' name='period'>");
for (i = 0; i < choices; i++)
html_option(cgit_fmt("%c", periods[i].code), periods[i].name,
cgit_fmt("%c", period->code));
- html("</select></td></tr>\n");
+ html("</select>\n");
}
- html("<tr><td class='label'>Authors:</td>");
- html("<td class='ctrl'><select name='ofs'>");
+ html("<label for='stats-authors'>Authors:</label>");
+ html("<select id='stats-authors' name='ofs'>");
html_intoption(10, "10", top);
html_intoption(25, "25", top);
html_intoption(50, "50", top);
html_intoption(100, "100", top);
html_intoption(-1, "all", top);
- html("</select></td></tr>\n");
- html("<tr><td></td><td class='ctrl'>");
+ html("</select>\n");
html("<input type='submit' value='reload'>");
- html("</td></tr></table>");
+ html("</div>");
html("</form>\n");
html("</div>\n");
}
diff --git a/source/ui-summary.c b/source/ui-summary.c
index 0b33a96..eb04ecd 100644
--- a/source/ui-summary.c
+++ b/source/ui-summary.c
@@ -44,8 +44,9 @@ static void print_clone_url(const char *url)
// cgit_add_clone_urls may call back no times at all, so the heading
// waits for a first url rather than being printed ahead of the walk.
if (clone_urls_printed++ == 0) {
- htmlf("<tr class='nohover'><td colspan='%d'></td></tr>\n", columns);
- htmlf("<tr class='nohover'><th colspan='%d' class='left'>Clone</th></tr>\n", columns);
+ html("<tbody class='clone'>\n");
+ htmlf("<tr class='nohover'><th colspan='%d' class='left' scope='col'>Clone</th></tr>\n",
+ columns);
}
cgit_print_clone_row(url, columns);
@@ -113,19 +114,18 @@ static char *resolve_about_path(const char *filename, const char *ref, const cha
void cgit_print_summary(void)
{
- int columns = log_columns();
-
cgit_print_layout_start();
- html("<table class='list'>\n");
+ // One table, so the columns line up across the sections, each of
+ // which is a body of its own.
+ html("<table class='list summary' aria-label='Summary'>\n");
cgit_print_branches(ctx.cfg.summary_branches);
- htmlf("<tr class='nohover'><td colspan='%d'></td></tr>\n", columns);
cgit_print_tags(ctx.cfg.summary_tags);
- if (ctx.cfg.summary_log > 0) {
- htmlf("<tr class='nohover'><td colspan='%d'></td></tr>\n", columns);
+ if (ctx.cfg.summary_log > 0)
cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL, NULL, NULL, 0, 0, 0);
- }
clone_urls_printed = 0;
cgit_add_clone_urls(print_clone_url);
+ if (clone_urls_printed)
+ html("</tbody>\n");
html("</table>\n");
cgit_print_layout_end();
}
@@ -159,7 +159,7 @@ void cgit_print_repo_readme(const char *path)
goto done;
}
- html("<div id='summary'>");
+ html("<div id='about'>");
if (!ctx.repo->about_filter) {
// With no about-filter there is nothing to turn the readme
// into safe HTML, so it is escaped rather than served raw.
diff --git a/source/ui-tag.c b/source/ui-tag.c
index 6811e0c..559cb54 100644
--- a/source/ui-tag.c
+++ b/source/ui-tag.c
@@ -19,7 +19,7 @@
static void print_name_row(const char *revname, const struct object_id *oid)
{
- html("<tr><th>tag name</th><td>");
+ html("<tr><th scope='row'>tag name</th><td>");
html_txt(revname);
if (oid)
htmlf(" (<span class='oid'>%s</span>)", oid_to_hex(oid));
@@ -28,7 +28,7 @@ static void print_name_row(const char *revname, const struct object_id *oid)
static void print_date_row(struct taginfo *info)
{
- html("<tr><th>tag date</th><td><time datetime='");
+ html("<tr><th scope='row'>tag date</th><td><time datetime='");
html_attr(show_date(info->tagger_date, info->tagger_tz, cgit_date_mode(DATE_ISO8601_STRICT)));
html("'>");
html_txt(show_date(info->tagger_date, info->tagger_tz, cgit_date_mode(DATE_ISO8601)));
@@ -37,7 +37,7 @@ static void print_date_row(struct taginfo *info)
static void print_tagger_row(struct taginfo *info)
{
- html("<tr><th>tagged by</th><td>");
+ html("<tr><th scope='row'>tagged by</th><td>");
// A tag can carry a tagger with no address, and a filter must not be
// handed a NULL argument.
cgit_open_filter(ctx.repo->email_filter, info->tagger_email ? info->tagger_email : "", "tag");
@@ -52,14 +52,14 @@ static void print_tagger_row(struct taginfo *info)
static void print_object_row(struct object *obj)
{
- html("<tr><th>tagged object</th><td class='oid'>");
+ html("<tr><th scope='row'>tagged object</th><td class='oid'>");
cgit_object_link(obj, 1);
html("</td></tr>\n");
}
static void print_download_row(const char *revname)
{
- html("<tr><th>download</th><td class='oid'>");
+ html("<tr><th scope='row'>download</th><td class='oid'>");
cgit_print_snapshot_links(ctx.repo, revname, "<br>");
html("</td></tr>\n");
}
diff --git a/source/ui-tree.c b/source/ui-tree.c
index 4d829fe..5b0cdea 100644
--- a/source/ui-tree.c
+++ b/source/ui-tree.c
@@ -197,13 +197,13 @@ static bool print_object(const struct object_id *oid, const char *path,
cgit_set_title_from_path(path);
cgit_print_layout_start();
- htmlf("blob: %s (", oid_to_hex(oid));
+ htmlf("<div class='blob-header'>blob: %s (", oid_to_hex(oid));
cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path);
if (ctx.repo->enable_blame && !is_binary) {
html(") (");
cgit_blame_link("blame", NULL, NULL, ctx.qry.head, rev, path);
}
- html(")\n");
+ html(")</div>\n");
if (is_binary)
print_binary_buffer(buf, size);
@@ -300,7 +300,14 @@ static void print_ls_row(const struct object_id *oid, const char *pathname,
}
}
- html("<tr><td class='ls-mode'>");
+ if (S_ISGITLINK(mode))
+ html("<tr class='mod'><td class='ls-mode'>");
+ else if (S_ISDIR(mode))
+ html("<tr class='dir'><td class='ls-mode'>");
+ else if (S_ISLNK(mode))
+ html("<tr class='link'><td class='ls-mode'>");
+ else
+ html("<tr class='blob'><td class='ls-mode'>");
cgit_print_filemode(mode);
html("</td><td>");
if (S_ISGITLINK(mode)) {
@@ -309,9 +316,14 @@ static void print_ls_row(const struct object_id *oid, const char *pathname,
print_dir_chain(oid, name, walk->rev, &fullpath);
} else {
char *ext = strrchr(name, '.');
+
strbuf_addstr(&class, "ls-blob");
- if (ext)
- strbuf_addf(&class, " %s", ext + 1);
+ // The extension becomes a class a stylesheet can pick files out
+ // by, prefixed so that a name such as x.button cannot land on a
+ // class of the theme's own.
+ if (ext && ext[1] && strspn(ext + 1, "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") == strlen(ext + 1))
+ strbuf_addf(&class, " ext-%s", ext + 1);
cgit_tree_link(name, NULL, class.buf, ctx.qry.head, walk->rev, fullpath.buf);
}
// A target longer than any path is no link, and reading it would pull
@@ -404,18 +416,18 @@ static void ls_flush(struct walk_tree_context *walk)
static void ls_head(void)
{
cgit_print_layout_start();
- html("<table class='list'>\n");
+ html("<table class='list tree' aria-label='Files'>\n<tbody class='tree'>\n");
html("<tr class='nohover'>");
- html("<th class='left ls-mode'>Mode</th>");
- html("<th class='left'>Name</th>");
- html("<th class='right'>Size</th>");
+ html("<th class='left ls-mode' scope='col'>Mode</th>");
+ html("<th class='left' scope='col'>Name</th>");
+ html("<th class='right' scope='col'>Size</th>");
html("<th class='ls-links'></th>");
html("</tr>\n");
}
static void ls_tail(void)
{
- html("</table>\n");
+ html("</tbody>\n</table>\n");
cgit_print_layout_end();
}
diff --git a/tests/t0005-markup.sh b/tests/t0005-markup.sh
new file mode 100755
index 0000000..2a8d572
--- /dev/null
+++ b/tests/t0005-markup.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+# Guards the hooks the README promises a site stylesheet or script, so a
+# rename in the markup fails here instead of in someone's theme. Each check
+# names the page it renders and the attribute or element it expects.
+
+test_description='Check the markup hooks a theme relies on'
+. ./setup.sh
+
+test_expect_success 'set up a repository with a file that has an extension' '
+ mkrepo repos/hooks 2 &&
+ (
+ cd repos/hooks &&
+ mkdir dir &&
+ echo "int x;" >dir/hello.c &&
+ git add dir &&
+ git commit -m sources
+ ) &&
+ {
+ echo "virtual-root=/" &&
+ echo "cache-size=0" &&
+ echo "enable-blame=1" &&
+ echo "max-stats=year" &&
+ echo "readme=:file-1" &&
+ echo "repo.url=hooks" &&
+ echo "repo.path=$PWD/repos/hooks/.git"
+ } >hooksrc
+'
+
+hooks() { CGIT_CONFIG="$PWD/hooksrc" QUERY_STRING="url=$1" cgit; }
+
+test_expect_success 'the root names the page and the repository' '
+ hooks "" >tmp &&
+ grep "<div id=.cgit. data-page=.repolist.>" tmp &&
+ hooks "hooks/log/" >tmp &&
+ grep "<div id=.cgit. data-page=.log. data-repo=.hooks.>" tmp
+'
+
+test_expect_success 'the landmarks and the search form are in place' '
+ grep "<header id=.header.>" tmp &&
+ grep "<nav class=.tabs. aria-label=.Pages.>" tmp &&
+ grep "<form class=.search. role=.search." tmp &&
+ grep "<main class=.content.>" tmp &&
+ grep "<footer class=.footer.>" tmp
+'
+
+test_expect_success 'the listings name themselves, their sections and their rows' '
+ grep "<table class=.list log. aria-label=.Commits.>" tmp &&
+ grep "<tbody class=.log.>" tmp &&
+ grep "<tr class=.commit.>" tmp &&
+ grep "class=.active. aria-current=.page." tmp &&
+ grep "<th class=.left. scope=.col.>" tmp &&
+ hooks "" >tmp &&
+ grep "<table class=.list repolist. aria-label=.Repositories.>" tmp &&
+ grep "<tbody class=.repos.>" tmp &&
+ grep "<tr class=.repo.>" tmp &&
+ hooks "hooks/" >tmp &&
+ grep "<table class=.list summary. aria-label=.Summary.>" tmp &&
+ grep "<tbody class=.branches.>" tmp &&
+ grep "<tr class=.branch.>" tmp &&
+ hooks "hooks/tree/" >tmp &&
+ grep "<table class=.list tree. aria-label=.Files.>" tmp &&
+ grep "<tr class=.dir.>" tmp &&
+ grep "<tr class=.blob.>" tmp &&
+ hooks "hooks/tree/dir/" >tmp &&
+ grep "class=.ls-blob ext-c." tmp
+'
+
+test_expect_success 'the diff pages wrap each file and label their controls' '
+ hooks "hooks/commit/" >tmp &&
+ grep "<table class=.commit-info.>" tmp &&
+ grep "<th scope=.row.>author</th>" tmp &&
+ grep "<div class=.file. data-path=.dir/hello.c.>" tmp &&
+ grep "<label for=.diff-context.>" tmp &&
+ grep "<select id=.diff-context. name=.context.>" tmp &&
+ grep "<form method=.get. aria-label=.Diff options.>" tmp &&
+ hooks "hooks/diff/&ss=1" >tmp &&
+ grep "<tbody class=.file. data-path=.dir/hello.c.>" tmp &&
+ hooks "hooks/stats/" >tmp &&
+ grep "<label for=.stats-authors.>" tmp
+'
+
+test_expect_success 'the blob, blame and about pages open with their own containers' '
+ hooks "hooks/tree/dir/hello.c" >tmp &&
+ grep "<div class=.blob-header.>" tmp &&
+ grep "<a id=.n1." tmp &&
+ hooks "hooks/blame/dir/hello.c" >tmp &&
+ grep "<div class=.blob-header.>" tmp &&
+ hooks "hooks/about/" >tmp &&
+ grep "<div id=.about.>" tmp
+'
+
+test_expect_success 'the stylesheet and the script name only the current hooks' '
+ ! grep "_dark\|#summary\|ul.pager\|td:empty" "$TEST_OUTPUT_DIRECTORY/../assets/cgit.css" &&
+ grep "window.cgit.updateAges" "$TEST_OUTPUT_DIRECTORY/../assets/cgit.js" &&
+ grep "window.cgit.highlightLines" "$TEST_OUTPUT_DIRECTORY/../assets/cgit.js"
+'
+
+test_done
diff --git a/tests/t0113-tag.sh b/tests/t0113-tag.sh
index 90763bd..2c9dc25 100755
--- a/tests/t0113-tag.sh
+++ b/tests/t0113-tag.sh
@@ -22,15 +22,15 @@ two lines." v1.0 &&
test_expect_success 'generate foo/tag/?h=v1.0' 'cgit_url "foo/tag/&h=v1.0" >tmp'
test_expect_success 'find the name, date and tagger rows' '
- grep "<tr><th>tag name</th><td>v1.0 (<span class=.oid.>[0-9a-f]*</span>)</td></tr>" tmp &&
- grep "<tr><th>tag date</th><td><time datetime=" tmp &&
- grep "<tr><th>tagged by</th><td>C O Mitter &lt;committer@example.com&gt;</td></tr>" tmp
+ grep "<tr><th scope=.row.>tag name</th><td>v1.0 (<span class=.oid.>[0-9a-f]*</span>)</td></tr>" tmp &&
+ grep "<tr><th scope=.row.>tag date</th><td><time datetime=" tmp &&
+ grep "<tr><th scope=.row.>tagged by</th><td>C O Mitter &lt;committer@example.com&gt;</td></tr>" tmp
'
test_expect_success 'the object row names the full commit id' '
commit=$(git -C repos/foo rev-parse v1.0^{commit}) &&
- grep "<tr><th>tagged object</th><td class=.oid.><a href=./foo/commit/?h=v1.0&amp;id=$commit.>commit $commit</a></td></tr>" tmp &&
- grep "<tr><th>download</th><td class=.oid.>" tmp
+ grep "<tr><th scope=.row.>tagged object</th><td class=.oid.><a href=./foo/commit/?h=v1.0&amp;id=$commit.>commit $commit</a></td></tr>" tmp &&
+ grep "<tr><th scope=.row.>download</th><td class=.oid.>" tmp
'
test_expect_success 'the message follows with no blank line before the body' '
@@ -40,9 +40,9 @@ test_expect_success 'the message follows with no blank line before the body' '
test_expect_success 'a lightweight tag gets the shorter table' '
cgit_url "foo/tag/&h=light" >tmp &&
- grep "<tr><th>tag name</th><td>light</td></tr>" tmp &&
- grep "<tr><th>tagged object</th><td class=.oid.><a" tmp &&
- ! grep "<th>tagged by</th>" tmp
+ grep "<tr><th scope=.row.>tag name</th><td>light</td></tr>" tmp &&
+ grep "<tr><th scope=.row.>tagged object</th><td class=.oid.><a" tmp &&
+ ! grep "<th scope=.row.>tagged by</th>" tmp
'
test_done
diff --git a/tests/t0201-filters.sh b/tests/t0201-filters.sh
index 444932a..11fa5f8 100755
--- a/tests/t0201-filters.sh
+++ b/tests/t0201-filters.sh
@@ -30,7 +30,7 @@ do
"
test_expect_success "check whether the $prefix about filter works" '
- grep "<div id='"'"'summary'"'"'>a+b HELLO$" tmp
+ grep "<div id='"'"'about'"'"'>a+b HELLO$" tmp
'
test_expect_success "generate filter-$prefix/commit/" "
@@ -61,7 +61,7 @@ do
test_expect_success "the $prefix about filter output stays inside its div" "
cgit_url 'filter-$prefix/about/' >tmp &&
- grep \"<div id='summary'>a+b HELLO\" tmp
+ grep \"<div id='about'>a+b HELLO\" tmp
"
done
diff --git a/tests/t0206-trailers.sh b/tests/t0206-trailers.sh
index e8e615c..5e10dfc 100755
--- a/tests/t0206-trailers.sh
+++ b/tests/t0206-trailers.sh
@@ -55,15 +55,15 @@ test_expect_success 'the body ends where the block begins' '
'
test_expect_success 'a value reaches the trailer filter with its key and page' '
- grep "<tr><th>Fixes</th><td>Fixes commit 1234567 (.PLAIN COMMIT.)</td></tr>" tmp
+ grep "<tr><th scope=.row.>Fixes</th><td>Fixes commit 1234567 (.PLAIN COMMIT.)</td></tr>" tmp
'
test_expect_success 'a run of one key, whatever its case, collapses into one row' '
- grep "<tr><th>Signed-off-by</th><td><mapped@example.com> commit MAPPED NAME &LT;MAPPED@EXAMPLE.COM&GT;<br><committer@example.com> commit C O MITTER &LT;COMMITTER@EXAMPLE.COM&GT;</td></tr>" tmp
+ grep "<tr><th scope=.row.>Signed-off-by</th><td><mapped@example.com> commit MAPPED NAME &LT;MAPPED@EXAMPLE.COM&GT;<br><committer@example.com> commit C O MITTER &LT;COMMITTER@EXAMPLE.COM&GT;</td></tr>" tmp
'
test_expect_success 'an ident follows the mailmap and keeps what trails the address' '
- grep "<tr><th>Acked-by</th><td><some@example.com> commit SOME ONE &LT;SOME@EXAMPLE.COM&GT; (maintainer)</td></tr>" tmp
+ grep "<tr><th scope=.row.>Acked-by</th><td><some@example.com> commit SOME ONE &LT;SOME@EXAMPLE.COM&GT; (maintainer)</td></tr>" tmp
'
test_expect_success 'a key with no value is printed as written' '
@@ -75,7 +75,7 @@ test_expect_success 'a line git could not split gets a row of its own' '
'
test_expect_success 'the escaped value is what the filter sees' '
- grep "<tr><th>Link</th><td>Link commit HTTPS://EXAMPLE.COM/?A=1&AMP;B=2</td></tr>" tmp
+ grep "<tr><th scope=.row.>Link</th><td>Link commit HTTPS://EXAMPLE.COM/?A=1&AMP;B=2</td></tr>" tmp
'
test_expect_success 'a message with no block gets no table' '
diff --git a/tests/t0208-mailmap.sh b/tests/t0208-mailmap.sh
index ca08de0..86f6d50 100755
--- a/tests/t0208-mailmap.sh
+++ b/tests/t0208-mailmap.sh
@@ -44,7 +44,7 @@ test_expect_success 'the log shows the mapped author' '
test_expect_success 'the commit page shows the mapped author and address' '
first=$(git -C repos/mapped rev-parse HEAD~1) &&
cgit_url "mapped/commit/&id=$first" >tmp &&
- grep "<th>author</th><td>New Name &lt;new@example.com&gt;</td>" tmp &&
+ grep "<th scope=.row.>author</th><td>New Name &lt;new@example.com&gt;</td>" tmp &&
! grep "old@example.com" tmp
'
diff --git a/tests/t0506-link-trailers.sh b/tests/t0506-link-trailers.sh
index f6fedf2..5736c5a 100755
--- a/tests/t0506-link-trailers.sh
+++ b/tests/t0506-link-trailers.sh
@@ -55,7 +55,7 @@ test_expect_success 'point cgit at it through the trailer filter' '
test_expect_success CGIT_LUA 'the Fixes object name links to its commit page' '
cgit_url "linky/commit/" >tmp &&
- grep "<th>Fixes</th><td><a href=....id=1234567.>1234567</a> (.earlier.)</td>" tmp
+ grep "<th scope=.row.>Fixes</th><td><a href=....id=1234567.>1234567</a> (.earlier.)</td>" tmp
'
test_expect_success CGIT_LUA 'the bare URL links to itself' '