Skip to content

comply with WCAG 2.0 for footnote backlinks #313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 16, 2018
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# EditorConfig is awesome: https://EditorConfig.org
root = true

[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false

[*.php]
indent_style = tab
insert_final_newline = true
93 changes: 69 additions & 24 deletions Michelf/MarkdownExtra.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ class MarkdownExtra extends \Michelf\Markdown {
public $fn_id_prefix = "";

/**
* Optional title attribute for footnote links and backlinks.
* Optional title attribute for footnote links.
* @var string
*/
public $fn_link_title = "";
public $fn_backlink_title = "";
public $fn_link_title = "";

/**
* Optional class attribute for footnote links and backlinks.
Expand All @@ -42,10 +41,22 @@ class MarkdownExtra extends \Michelf\Markdown {
* Content to be displayed within footnote backlinks. The default is '↩';
* the U+FE0E on the end is a Unicode variant selector used to prevent iOS
* from displaying the arrow character as an emoji.
* Optionally use '^^' and '%%' to refer to the footnote number and
* reference number respectively. {@see parseFootnotePlaceholders()}
* @var string
*/
public $fn_backlink_html = '↩︎';

/**
* Optional title and aria-label attributes for footnote backlinks for
* added accessibility (to ensure backlink uniqueness).
* Use '^^' and '%%' to refer to the footnote number and reference number
* respectively. {@see parseFootnotePlaceholders()}
* @var string
*/
public $fn_backlink_title = "";
public $fn_backlink_label = "";

/**
* Class name for table cell alignment (%% replaced left/center/right)
* For instance: 'go-%%' becomes 'go-left' or 'go-right' or 'go-center'
Expand Down Expand Up @@ -1217,8 +1228,7 @@ protected function _doTable_leadingPipe_callback($matches) {
* @param string $alignname
* @return string
*/
protected function _doTable_makeAlignAttr($alignname)
{
protected function _doTable_makeAlignAttr($alignname) {
if (empty($this->table_align_class_tmpl)) {
return " align=\"$alignname\"";
}
Expand Down Expand Up @@ -1667,24 +1677,18 @@ protected function appendFootnotes($text) {


/**
* Generates the HTML for footnotes. Called by appendFootnotes, even if footnotes are not being appended.
* Generates the HTML for footnotes. Called by appendFootnotes, even if
* footnotes are not being appended.
* @return void
*/
protected function _doFootnotes() {
$attr = "";
$attr = array();
if ($this->fn_backlink_class !== "") {
$class = $this->fn_backlink_class;
$class = $this->encodeAttribute($class);
$attr .= " class=\"$class\"";
}
if ($this->fn_backlink_title !== "") {
$title = $this->fn_backlink_title;
$title = $this->encodeAttribute($title);
$attr .= " title=\"$title\"";
$attr .= " aria-label=\"$title\"";
$attr['class'] = " class=\"$class\"";
}
$attr .= " role=\"doc-backlink\"";
$backlink_text = $this->fn_backlink_html;
$attr['role'] = " role=\"doc-backlink\"";
$num = 0;

$text = "<ol>\n\n";
Expand All @@ -1701,19 +1705,43 @@ protected function _doFootnotes() {
$footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
array($this, '_appendFootnotes_callback'), $footnote);

$attr = str_replace("%%", ++$num, $attr);
$num++;
$note_id = $this->encodeAttribute($note_id);

// Prepare backlink, multiple backlinks if multiple references
$backlink = "<a href=\"#fnref:$note_id\"$attr>$backlink_text</a>";
for ($ref_num = 2; $ref_num <= $ref_count; ++$ref_num) {
$backlink .= " <a href=\"#fnref$ref_num:$note_id\"$attr>$backlink_text</a>";
// Do not create empty backlinks if the html is blank
$backlink = "";
if (!empty($this->fn_backlink_html)) {
for ($ref_num = 1; $ref_num <= $ref_count; ++$ref_num) {
if (!empty($this->fn_backlink_title)) {
$attr['title'] = ' title="' . $this->encodeAttribute($this->fn_backlink_title) . '"';
}
if (!empty($this->fn_backlink_label)) {
$attr['label'] = ' aria-label="' . $this->encodeAttribute($this->fn_backlink_label) . '"';
}
$parsed_attr = $this->parseFootnotePlaceholders(
implode('', $attr),
$num,
$ref_num
);
$backlink_text = $this->parseFootnotePlaceholders(
$this->fn_backlink_html,
$num,
$ref_num
);
$ref_count_mark = $ref_num > 1 ? $ref_num : '';
$backlink .= " <a href=\"#fnref$ref_count_mark:$note_id\"$parsed_attr>$backlink_text</a>";
}
$backlink = trim($backlink);
}

// Add backlink to last paragraph; create new paragraph if needed.
if (preg_match('{</p>$}', $footnote)) {
$footnote = substr($footnote, 0, -4) . "&#160;$backlink</p>";
} else {
$footnote .= "\n\n<p>$backlink</p>";
if (!empty($backlink)) {
if (preg_match('{</p>$}', $footnote)) {
$footnote = substr($footnote, 0, -4) . "&#160;$backlink</p>";
} else {
$footnote .= "\n\n<p>$backlink</p>";
}
}

$text .= "<li id=\"fn:$note_id\" role=\"doc-endnote\">\n";
Expand Down Expand Up @@ -1773,6 +1801,23 @@ protected function _appendFootnotes_callback($matches) {
return "[^" . $matches[1] . "]";
}

/**
* Build footnote label by evaluating any placeholders.
* - ^^ footnote number
* - %% footnote reference number (Nth reference to footnote number)
* @param string $label
* @param int $footnote_number
* @param int $reference_number
* @return string
*/
protected function parseFootnotePlaceholders($label, $footnote_number, $reference_number) {
return str_replace(
array('^^', '%%'),
array($footnote_number, $reference_number),
$label
);
}


/**
* Abbreviations - strips abbreviations from text, stores titles in hash
Expand Down