123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- #!/usr/bin/env perl
- use strict;
- use v5.10;
- # ====================[ footnotes.pl ]====================
- =encoding utf8
- =head1 NAME
- footnotes - An Oddmuse module for adding footnotes to Oddmuse Wiki pages.
- =head1 INSTALLATION
- footnotes is easily installable; move this file into the B<wiki/modules/>
- directory for your Oddmuse Wiki.
- =cut
- AddModuleDescription('footnotes.pl', 'Footnotes Extension');
- our ($q, $bol, @MyRules, @MyInitVariables);
- # ....................{ CONFIGURATION }....................
- =head1 CONFIGURATION
- footnotes is easily configurable; set these variables in the B<wiki/config.pl>
- file for your Oddmuse Wiki.
- =cut
- our ($FootnotePattern,
- $FootnotesPattern,
- $FootnotesHeaderText,
- @FootnoteList);
- =head2 $FootnotePattern
- A regular expression matching text within an Oddmuse Wiki page, which, when
- matched, replaces that text with a footnote reference. In other words, text
- matching this regular expression becomes a "footnote."
- If left unset, this regular expression takes one of two defaults - depending on
- which other Oddmuse markup modules are installed (so as not to conflict with
- those other Oddmuse markup modules' markup rules).
- =over
- =item (($FootnoteText))
- =over
- =item If the Creole Markup module (B<creole.pl>) is also installed, then this
- is the default regular expression for marking a footnote (where
- C<$FootnoteText> is the displayed text for that footnote).
- =back
- =item {{$FootnoteText}}
- =over
- =item If the Creole Markup module (B<creole.pl>) is not installed, then this
- is the default regular expression for marking a footnote (where
- C<$FootnoteText> is the displayed text for that footnote). This is, also,
- the old default for this module.
- =back
- =back
- =cut
- $FootnotePattern = undef;
- =head2 $FootnotesPattern
- A regular expression matching text within an Oddmuse Wiki page, which, when
- matched, replaces that text with the set of all page footnotes.
- Any page with footnotes (i.e., any page with at least one string matching the
- C<$FootnotePattern>) should collect and show those footnotes somewhere in that
- page. Luckily, there are two mechanisms for effecting this - the first via
- explicit markup, and the second via implicit fallback; these are:
- =over
- =item <footnotes>
- =over
- =item If a page has markup explicitly matched by this regular expression, that
- markup is replaced by the set of footnotes for the page.
- =back
- =item N/A
- =over
- =item Otherwise, if a page has no such markup but does have at least one
- footnote, the set of footnotes for the page is automatically situated
- between the content and footer for that page. As this may, or may not, be
- the proper place for page footnotes, you're encouraged to explicitly
- provide page markup matched by this regular expression.
- =back
- =back
- =cut
- $FootnotesPattern = '\<footnotes\>[ \t]*(\n|$)';
- =head2 $FootnotesHeaderText
- The string displayed as the header to the set of all page footnotes.
- =cut
- $FootnotesHeaderText = 'Footnotes:';
- # ....................{ INITIALIZATION }....................
- push(@MyInitVariables, \&FootnotesInit);
- sub FootnotesInit {
- @FootnoteList = ();
- if (not defined $FootnotePattern) {
- $FootnotePattern = defined &CreoleRule ? '\(\((.+?)\)\)' : '\{\{(.+?)\}\}';
- }
- }
- # ....................{ MARKUP }....................
- push(@MyRules, \&FootnotesRule);
- =head2 MARKUP
- =head3 CREATING FOOTNOTES
- footnotes handles markup resembling (assuming the Creole Markup module is also
- installed):
- (($FootnoteText))
- C<$FootnoteText> is the text for that footnote. This extension replaces that
- text (and enclosing parentheses) with a numbered link to the footnote in the set
- of all footnotes for that page - usually, at the foot of the page. As example of
- a citation for Jared Diamond's "Collapse: How Societies Choose to Fail or
- Succeed" (2005), you might write:
- History suggests that societal decline does not result from a single cause,
- but rather the confluence of several interwoven causes.((Diamond, Jared. 2005.
- **Collapse: How Societies Choose to Fail or Succeed.** %%Viking, New York.%%))
- Note that the example above embeds Wiki Creole syntax within the footnote
- definition itself. This is perfectly legal and, in fact, encouraged.
- =head3 CREATING MULTIPLE FOOTNOTES
- footnotes also handles markup resembling:
- (($FirstFootnoteText))(($NextFootnoteText))
- C<$FirstFootnoteText> and C<$NextFootnoteText> are the text for two adjacent
- footnotes. These footnote definitions will be handled and displayed as above,
- except that the numbered link for the first footnote will be visually delimited
- from the numbered link for the footnote that follows it with a ", ". As example,
- you might write:
- History suggests that societal decline does not result from a single cause,
- but rather the confluence of several interwoven causes.((Diamond, Jared. 2005.
- **Collapse: How Societies Choose to Fail or Succeed.** %%Viking, New York.%%))
- ((Tainter, Joseph. 1988. **The Collapse of Complex Societies.** %%Cambridge
- Univ Press, Cambridge, UK.%%))
- =head3 REFERENCING ANOTHER FOOTNOTE
- footnotes also handles marking resembling:
- (($FootnoteNumber))
- C<$FootnoteNumber> is the number for another footnote. This module assigns each
- footnote definition a unique number, beginning at "1". Thus, this markup allows
- you to reference one footnote definition in multiple places throughout a page.
- As example, you might write:
- History suggests that societal decline does not result from a single cause,
- but rather the confluence of several interwoven causes.((Diamond, Jared. 2005.
- **Collapse: How Societies Choose to Fail or Succeed.** %%Viking, New York.%%))
- Such causes include a human-dominated ecosystem moving to a brittle, non-
- resilient state due to climatological changes.((Weiss H, Bradley RS. 2001.
- **What drives societal collapse?** %%Science 291:609–610.%%))
- Societal decline only occurs, however, when socio-ecological systems become
- brittle and incapable of adaptation.((1))
- The final footnote, above, is a reference to the first footnote definition
- rather than a new footnote definition.
- =head3 REFERENCING A RANGE OF OTHER FOOTNOTES
- footnotes also handles marking resembling:
- (($FirstFootnoteNumber-$LastFootnoteNumber))
- C<$FirstFootnoteNumber> and C<$LastFootnoteNumber> are the numbers for two
- other footnotes. Thus, this markup allows you to reference a range of footnote
- definitions in multiple places throughout a page. As example, you might write:
- History suggests that societal decline does not result from a single cause,
- but rather the confluence of several interwoven causes.((Diamond, Jared. 2005.
- **Collapse: How Societies Choose to Fail or Succeed.** %%Viking, New York.%%))
- Such causes include a human-dominated ecosystem moving to a brittle, non-
- resilient state due to climatological changes((Weiss H, Bradley RS. 2001.
- **What drives societal collapse?** %%Science 291:609–610.%%)), external
- forcings((Tainter, Jared. 2006. **Social complexity and sustainability.**
- %%Ecol Complex 3:91–103.%%)), or internal pressures((Cullen HM, et al. 2000.
- **Climate change and the collapse of the Akkadian empire: Evidence from the
- deep sea.** %%Geology 28:379–382.%%)).
- Societal decline only occurs, however, when socio-ecological systems become
- brittle and incapable of adaptation.((1-2))((4))
- The final footnotes, above, are a reference to the first two footnote
- definitions followed by a reference to the fourth footnote definition. This
- module visually renders this disjoint list like: "1-2, 4".
- =head3 CREATING THE SET OF FOOTNOTES
- footnotes also handles markup resembling:
- <footnotes>
- This extension replaces that markup with the set of all footnotes for that page.
- Note that, if that page has no such markup, this extension automatically places
- the set of all footnotes for that page between the content and footer for that
- page. (This may or not be what you want, of course.)
- =cut
- sub FootnotesRule {
- # A "((...))" footnote anywhere in a page.
- #
- # Footnotes and the set of all footnotes must be marked so as to ensure their
- # reevaluation, as each of the footnotes might contain Wiki markup requiring
- # reevaluation (like, say, free links).
- if (m/\G($FootnotePattern)(?=([ \t]*$FootnotePattern)?)/cgs) {
- Dirty($1); # do not cache the prefixing "\G"
- my $footnote_text = $2;
- my $is_adjacent_footnote = defined $3;
- # A number range (e.g., "2-5") of references to other footnotes.
- if ($footnote_text =~ m/^(\d+)-(\d+)$/) {
- my ($footnote_number_first, $footnote_number_last) = ($1, $2);
- # '–', below, is the HTML entity for a Unicode en-dash.
- print $q->a({-href=> '#footnotes' .$footnote_number_first,
- -title=> 'Footnote #'.$footnote_number_first,
- -class=> 'footnote'
- }, $footnote_number_first.'–')
- .$q->a({-href=> '#footnotes' .$footnote_number_last,
- -title=> 'Footnote #'.$footnote_number_last,
- -class=> 'footnote'
- }, $footnote_number_last.($is_adjacent_footnote ? ', ' : ''));
- }
- # A number (e.g., "5") implying reference to another footnote.
- elsif ($footnote_text =~ m/^(\d+)$/) {
- my $footnote_number = $1;
- print $q->a({-href=> '#footnotes' .$footnote_number,
- -title=> 'Footnote #'.$footnote_number,
- -class=> 'footnote'
- }, $footnote_number.($is_adjacent_footnote ? ', ' : ''));
- }
- # Otherwise, a new footnote definition.
- else {
- push(@FootnoteList, $footnote_text);
- my $footnote_number = @FootnoteList;
- print $q->a({-href=> '#footnotes'.$footnote_number,
- -name=> 'footnote' .$footnote_number,
- -title=> 'Footnote: '. # Truncate link titles to one line.
- ( length($footnote_text) > 48
- ? substr($footnote_text, 0, 44).'...'
- : $footnote_text),
- -class=> 'footnote'
- }, $footnote_number.($is_adjacent_footnote ? ', ' : ''));
- }
- return '';
- }
- # The "<footnotes>" list of all footnotes at the foot of a page.
- elsif ($bol && m/\G($FootnotesPattern)/cgis) {
- Clean(CloseHtmlEnvironments());
- Dirty($1); # do not cache the prefixing "\G"
- if (@FootnoteList) {
- my ($oldpos, $old_) = (pos, $_);
- PrintFootnotes();
- Clean(AddHtmlEnvironment('p')); # if dirty block is looked at later, this will disappear
- ($_, pos) = ($old_, $oldpos); # restore \G (assignment order matters!)
- }
- return '';
- }
- return;
- }
- # ....................{ HTML OUTPUT }....................
- *PrintFooterFootnotesOld = \&PrintFooter;
- *PrintFooter = \&PrintFooterFootnotes;
- =head2 PrintFooterFootnotes
- Appends the list of footnotes to the footer of the page, if and only if the
- user-provided content for that page had no content matching C<$FootersPattern>.
- Thus, this function is an eleventh-hour fallback; ideally, pages providing
- footnotes also provide an explicit place to list those footnotes.
- =cut
- sub PrintFooterFootnotes {
- my @params = @_;
- if (@FootnoteList) { PrintFootnotes(); }
- PrintFooterFootnotesOld(@params);
- }
- =head2 PrintFootnotes
- Prints the list of footnotes.
- =cut
- sub PrintFootnotes {
- print
- $q->start_div({-class=> 'footnotes'})
- .$q->h2(T($FootnotesHeaderText));
- # Don't use <ol>, because we want to link from the number back to
- # its page location.
- my $footnote_number = 1;
- foreach my $footnote (@FootnoteList) {
- print
- $q->start_div({-class=> 'footnote'})
- .$q->a({-class=> 'footnote_backlink',
- -name=> 'footnotes'.$footnote_number,
- -href=> '#footnote' .$footnote_number}, $footnote_number.'.')
- .' ';
- ApplyRules($footnote, 1);
- print $q->end_div();
- $footnote_number++;
- }
- print $q->end_div();
- # Empty the footnotes, now; this prevents our calling the fallback, later.
- @FootnoteList = ();
- }
- =head1 COPYRIGHT AND LICENSE
- The information below applies to everything in this distribution,
- except where noted.
- Copyleft 2008 by B.w.Curry <http://www.raiazome.com>.
- Copyright 2004 by Alex Schroeder <alex@emacswiki.org>.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see L<http://www.gnu.org/licenses/>.
- =cut
|