J. McKenzie Alexander
jalex@lse.ac.uk
This is a demonstration of a JavaScript package providing
a way to insert citations(both author-year and 'vancouver' style) and bibliographies into webpages from a BibTeX bibliography file.
All of the heavy lifting is done by citation.js
,
which interprets the BibTeX entries and formats the bibliography.
What this package does is scan the webpage text for citation keys which conform to
basic natbib syntax: \cite
, \citet
, \citep
,
\nocite
, \fullcite
, or \citeauthor
.
There are limitations to how the first three are implemented,
as I'm not trying to write a complete replacement for the natbib package. For complicated
citations, there is an alternative format described below.
Here is a basic demonstration. The text in the left column shows the literal text which
is included in the source of the webpage. The text on the right shows the way it is formatted by Biblify.
What you see, here, is trivial cosmetic formatting but, behind the scenes, the package also
extracts the relevant entry from a BibTeX bibliography file and gets ready to
format a bibliography using citation.js
. The bibliography can inserted anywhere in
the webpage. In this case, there is a main bibliography at the end of the page. However, partial
bibliographies contained within any HTML element can be extracted and inserted, allowing
for section-by-section bibliographies to be created, as well.
Chicago
APA
BJPS
\cite{Alexander:2007}
\cite{Alexander:2007}
\cite{Alexander:2007}
\cite{Alexander:2007}
\cite[pg. 120]{Morley:2016}
\cite[pg. 120]{Morley:2016}
\cite[pg. 120]{Morley:2016}
\cite[pg. 120]{Morley:2016}
\citet{Schuppli/Fraser:2007}
\citet{Schuppli/Fraser:2007}
\citet{Schuppli/Fraser:2007}
\citet{Schuppli/Fraser:2007}
\citet{Glinow/etal:2004}
\citet{Glinow/etal:2004}
\citet{Glinow/etal:2004}
\citet{Glinow/etal:2004}
\citet[pg. 120]{Morley:2016}
\citet[pg. 120]{Morley:2016}
\citet[pg. 120]{Morley:2016}
\citet[pg. 120]{Morley:2016}
\citet{Quine:1948, Quine:1960}
\citet{Quine:1948, Quine:1960}
\citet{Quine:1948, Quine:1960}
\citet{Quine:1948, Quine:1960}
\citep[pg. 120]{Quine:1948}
\citep[pg. 120]{Quine:1948}
\citep[pg. 120]{Quine:1948}
\citep[pg. 120]{Quine:1948}
\citep[see][]{Quine:1948}
\citep[see][]{Quine:1948}
\citep[see][]{Quine:1948}
\citep[see][]{Quine:1948}
\citep[][pg. 120]{Quine:1948}
\citep[][pg. 120]{Quine:1948}
\citep[][pg. 120]{Quine:1948}
\citep[][pg. 120]{Quine:1948}
\citep[see][pg. 120]{Quine:1948}
\citep[see][pg. 120]{Quine:1948}
\citep[see][pg. 120]{Quine:1948}
\citep[see][pg. 120]{Quine:1948}
\nocite{Skyrms:1982,Skyrms:1984}
\nocite{Skyrms:1982,Skyrms:1984}
\nocite{Skyrms:1982,Skyrms:1984}
\nocite{Skyrms:1982,Skyrms:1984}
\fullcite{Glinow/etal:2004}
\citep{Skyrms:1982, Skyrms:1984, Skyrms:1988, Quine:1948, Quine:1960, Anscombe:1958, Anscombe:1963, Anscombe:1979}
\citet{Skyrms:1982, Skyrms:1984, Skyrms:1988, Quine:1948, Quine:1960, Anscombe:1958, Anscombe:1963, Anscombe:1979}
\fullcite{Glinow/etal:2004}
\citep{Skyrms:1982, Skyrms:1984, Skyrms:1988, Quine:1948, Quine:1960, Anscombe:1958, Anscombe:1963, Anscombe:1979}
\citet{Skyrms:1982, Skyrms:1984, Skyrms:1988, Quine:1948, Quine:1960, Anscombe:1958, Anscombe:1963, Anscombe:1979}
\fullcite{Glinow/etal:2004}
\citep{Skyrms:1982, Skyrms:1984, Skyrms:1988, Quine:1948, Quine:1960, Anscombe:1958, Anscombe:1963, Anscombe:1979}
\citet{Skyrms:1982, Skyrms:1984, Skyrms:1988, Quine:1948, Quine:1960, Anscombe:1958, Anscombe:1963, Anscombe:1979}
\citeauthor{Skyrms:1982}
\citeauthor{Skyrms:1982}
\citeauthor{Lehrer/Wagner:1981}
\citeauthor{Lehrer/Wagner:1981}
\citeauthor{Laslett/etal:1972}
\citeauthor{Laslett/etal:1972}
\citeauthor*{Laslett/etal:1972}
\citeauthor*{Laslett/etal:1972}
First, download Biblify.
Second, either download a copy of citation.js
or link to it from a server.
And add jQuery.
Assuming you link to citation.js
from a server, put the following in your webpage:
<script src="https://cdn.jsdelivr.net/npm/citation-js@0.7.14/build/citation.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"></script>
<script src="./path/to/biblify.js"></script>
You need to load the files in that order, as citation.min.js
defines some functions
which are used by biblify.js
during its initialisation.
You also need jQuery loaded...
Once you've done that, you can quickly configure Biblify by telling it where to find your BibTeX
bibliography. By default, it will process all the citations in the page
and append them to the end of the page in a bibliography formatted in the Chicago-style.
<script>
Biblify.configure({
bibfile: './my_bibliography.bib'
});
</script>
Additional options are:
Option
Default
Description
addTemplate
[]
This should be set to an array of dictionaries specifying the path to the CSL
template file to add, the name for it, and — if needed — a function
which does additional post-processing on the string generated.
bibfile
undefined
This should be set to a string containing the relative path to the BibTeX bibliography file.
The default value of undefined
means an error will be thrown unless a valid
path is provided.
defer
false
A boolean value indicating whether to defer processing citations on the page. The default value of false
means that processing will begin as soon as the bibliography file is loaded and parsed.
A value of true
means that you are going to trigger the processing of citations yourself.
placement
'body'
A CSS selector identifying the element to which the bibliography should be appended once all of the
citations have been found. If defer
is true
, then this option has no effect.
scan
'body'
A CSS class selector indicating which element should have its text nodes scanned for citations.
The default value means that the entire web page is scanned. If defer
is true, then this option has no effect.
template
'chicago'
A string stating what bibliography format to use. The options included in
Biblify are "apa" (American Psychological Association), "harvard1" (Harvard style),
"vancouver" (Vancouver style), "ajp" (Australasian Journal of Philosophy),
"bjps" (British Journal for the Philosophy of Science), and "chicago" (Chicago style)
Once Biblify is configured, it will load the bibliography file from the server (along with
any additional templates) and
start parsing the file unless the option defer
is set to true
. If you want to have fine-grain control over what
elements are parsed and where the bibliographies go — for example, if you want
to have multiple bibliographies (e.g., you have a markdown file containing comments on
essays, and you want each set of comments to have its own bibliography) — you need to defer.
If your bibliography file is large, it can take some time before it's ready to use. Not a lot of time from a human perspective, but long enough that if you wrote some JavaScript and had it immediately executed you might get errors because the bibliography file hasn't finished parsing. If you haven't deferred, you don't need to worry because Biblify will hold off on searching for citations until it's good to go.
However, if you are going to manually construct bibliographies yourself, how do you know when to start?
Biblify triggers a 'bibliography-ready'
event
once the bibliography file has been parsed and any additional templates have been
loaded. You can register an event
listener to run after that even has been fired.
Here's how to replicate the default behaviour of Biblify with deferring:
<script>
Biblify.configure({
bibfile: './my_bibliography.bib',
defer: true
});
document.addEventListener('bibliography-ready', function() {
Biblify.processCitations('body');
Biblify.insertBibliography('body');
});
</script>
The processCitations()
function can be given either a CSS selector or
a javascript object representing an element node: it will
recursively search all of the text nodes in that element and format all of the
citations it finds. The function insertBibliography()
also accepts either a CSS selector
or an element node, and it will append the bibliography to that element. That's it!
Suppose you want to organise your bibliography by sections or subsections. How do you do that? The way you do this is to tell Biblify to only process the citations in an element other than the main body. Once it's done that, you can then put only the references found in that element someplace. In what follows, I assume that Biblify has been configured and deferred, and so I am only showing the code which does something. Here's an example:
<div id='section1'>
<h3>This is the first section</h3>
<p>
If you read only two essays by Quine, you
should read \cite{Quine:1948} and
\cite{Quine:1951}, as they are classics.
</p>
</div>
<strong>References.</strong>
<div id='bib1'></div>
<div id='section2'>
<h3>This is the second section</h3>
<p>
If you read only two essays by Anscombe, you
should read \cite{Anscombe:1958} and \cite{Anscombe:1979},
as they are classics.
</p>
</div>
<strong>References.</strong>
<div id='bib2'></div>
<script>
document.addEventListener('bibliography-ready', function() {
Biblify.processCitations('#section1');
Biblify.insertBibliography('#bib1');
Biblify.clearCitations();
Biblify.processCitations('#section2');
Biblify.insertBibliography('#bib2');
});
</script>
If you read only two essays by Quine, you should read \cite{Quine:1948} and \cite{Quine:1951}, as they are classics.
If you read only two essays by Anscombe, you should read \cite{Anscombe:1958} and \cite{Anscombe:1979}, as they are classics.
In order to clear the citations before processing the second section, it is
necessary to call Biblify.clearCitations()
in order to empty
the list of citations.
There's a more efficient way to create sectional bibliographies, taking into account the two necessary functions accept nodes as arguments.
<section>
<h3>This is the first section</h3>
<p>
If you read only two essays by Quine, you
should read \cite{Quine:1948} and
\cite{Quine:1951}, as they are classics.
</p>
</section>
<section>
<h3>This is the second section</h3>
<p>
If you read only two essays by Anscombe, you
should read \cite{Anscombe:1958} and \cite{Anscombe:1979},
as they are classics.
</p>
</section>
<script>
document.addEventListener('bibliography-ready', function() {
$("section").each(function() {
Biblify.clearCitations();
Biblify.processCitations(this);
$(this).append("<strong>References</strong>");
Biblify.insertBibliography(this);
});
});
</script>
If you read only two books by Quine, you should read \cite{Quine:1960} and \cite{Quine:1961}, as they are classics.
If you read only two books by Anscombe, you should read \cite{Anscombe:1963} and \cite{Anscombe:1981}, as they are classics.
Even if you throw away the citations, Biblify remembers all the
citations that it has found so far. If you want to insert all of the citations
it has found in a master bibliography at the end of the page, simply
call Biblify.insertBibliography(selector, { all: true })
.
You can change the citation and bibliography style by calling Biblify.useTemplate()
between
passes and specifying the name of a different template. The next two sections demonstrate that.
The quick brown fox jumped over the lazy dogs. The quick brown fox \citep{Allais:1953a}, according to \citet[pg. 120]{Baratella:2023}, did in fact \citep[see][]{Carnap/etal:1929} jump over the lazy dogs \citep{Bergstrom/Bergstrom:1999}. The quick brown fox, according to \cite{Bohman:1991} and \citet{Frank:1995b}, did in fact jump \citep{Dagg:2004} over the lazy dog \citep{Ehrlich:2012}. The quick, argued \cite{Glinow/etal:2004}, and brown fox \citep{Loy/etal:2020} did supposed jump, said \citet{Schuppli/Fraser:2007} over the lazy dog \citep{MaynardSmith:1968}.
References
The quick brown fox jumped over the lazy dogs. The quick brown fox \citep{Allais:1953a}, according to \citet[pg. 120]{Baratella:2023}, did in fact \citep[see][]{Carnap/etal:1929} jump over the lazy dogs \citep{Bergstrom/Bergstrom:1999}. The quick brown fox, according to \cite{Bohman:1991} and \citet{Frank:1995b}, did in fact jump \citep{Dagg:2004} over the lazy dog \citep{Ehrlich:2012}. The quick, argued \cite{Glinow/etal:2004}, and brown fox \citep{Loy/etal:2020} did supposed jump, said \citet{Schuppli/Fraser:2007} over the lazy dog \citep{MaynardSmith:1968}.
References
So-called Vancouver style citations are very different from author-year. Here, the bibliography items are listed in the order in which they are cited in the text, and inline citations are by their numerical index, appearing between parentheses or brackets, as a superscript or as normal text. Multiple references are sorted according to their order with ranges collapsed whenever possible. Here is an example:
Lorem ipsum dolor, sit amet consectetur adipisicing elit.\cite{Pfeffer:2015} Quis at sed, perferendis sequi accusantium est velit,\cite{Allais:1953a, Baratella:2023, Carnap/etal:1929} eius saepe neque ullam dolorum,\cite{Bergstrom/Bergstrom:1999} molestias, odio praesentium non a dolorem aut necessitatibus consectetur? Voluptatibus ratione unde blanditiis fugit distinctio hic fugiat, quibusdam velit pariatur, qui illo, quis earum suscipit\cite{Bohman:1991, Frank:1995b, Dagg:2004, Ehrlich:2012}. Necessitatibus, quas sint,\cite{Hains:2016, Kruse:2013} sit ea aspernatur fugit perspiciatis nihil enim suscipit accusamus provident nobis.\cite{Glinow/etal:2004, Loy/etal:2020, Schuppli/Fraser:2007, MaynardSmith:1968} Laudantium in culpa, nisi error illum facilis voluptatum voluptatibus voluptate aliquam iste inventore libero?\cite{Moore/Fresco:2012, Hofbauer/Sigmund:2002} Tenetur provident labore aliquid,\cite{Ohtsuki:2007p1203} voluptatibus vitae hic dolore,\cite{Kaneko/Suzuki:1994} quo vero tempora alias quos, repudiandae unde beatae\cite{Rodriguez:2021}.
And now, something different\cite{Carnap/etal:1929,Schuppli/Fraser:2007,Ehrlich:2012}. And see also \cite{Bohman:1991,Frank:1995b,Bohman:1991,Frank:1995b,Frank:1995b} and \cite{MaynardSmith:1968, Moore/Fresco:2012, Hofbauer/Sigmund:2002, Ohtsuki:2007p1203}. And this \cite{Bohman:1991, Frank:1995b, Dagg:2004, Glinow/etal:2004, Hofbauer/Sigmund:2002, Ohtsuki:2007p1203, Kaneko/Suzuki:1994}.
The default formatting of the Vancouver-style citations isn't very pretty,
so there are some CSS selectors you can target. Each of the inline citations is
positioned in a span.biblify-vancouver
.
The bibliography has the following structure:
<div class='csl-bib-body'>
<div class='csl-entry'>
<div class='csl-left-margin'>
This is where the numerical index lives.
</div>
<div class='csl-right-inline'>
This is where the full bibliography entry lives.
</div>
</div>
</div>
In order to help prettify the appearance of Vancouver-style citations, Biblify injects the following CSS at the very beginning of the web page. If you add your own CSS later, you can override it.
span.biblify-vancouver {
font-weight: bold;
}
div.biblify-vancouver-template div.csl-entry {
display: grid;
grid-template-columns: 18pt 1fr;
column-gap: 8pt;
}
div.biblify-vancouver-template div.csl-entry div.csl-right-inline {
width: auto;
}
References
In general, the bibliography generated has a class of the form
'biblify-NAME-template'
added to it, where the NAME
refers to the name of the template. This allows you to target different
styles of bibliography with CSS, if you want.
Biblify comes equipped with a few of the more standard citation styles, by default, but it
is possible to install more citation styles. This
is done by passing appropriate options to the configure
function.
Where can you find additional citation styles?
Zotero has a great page with many styles available.
You can download these styles and put them into the same directory as the .html file on
your server.
If you want to install new styles, the way to do this is demonstrated below. You
pass an additional option to configure, addTemplates
, which takes as its
value an array of dictionaries containing information about the new templates to
install. These dictionaries have two options which must be specified: name
,
path
, and one optional one, postprocessor
.
function do_something(str) {
// This is a silly example but it shows the idea.
return str.toUpperCase();
}
Biblify.configure({
bibfile: './bibliography.bib',
template: 'chicago',
defer: true,
addTemplates: [
{
name: 'ergo',
path: './src/ergo.csl'
},
{
name: 'econometrica',
path: './src/econometrica.csl',
postprocessor: do_something,
}
]
});
The option name
is the internal name used to refer to the
template. It is the option you will pass to Biblify.useTemplate(name)
.
The option path
is the path to the csl
file
stating how the bibliography should be formatted.
The reason there is an option called postprocessor
is because of how
Biblify works in the background. It creates the appropriate text to insert for
the \citet
and \cite
values by
asking citation.js
to generate an inline citation for the items in the
specified key — which might include several items. Consider a citation with
several people, something like the following
\citep{Skyrms:1982, Skyrms:1984, Skyrms:1988, Bergstrom/Bergstrom:1999, Anscombe:1958, Anscombe:1963, Anscombe:1979, Glinow/etal:2004}
This looks like the following, in different styles:
Notice that there are a number of differences between these entries. One is the sort order. Another is whether there is a ',' before the first occurrence of the year, as in the APA format. This something that Biblify has to consider when it goes about constructing the appropriate content for \citet from the citation. Biblify, internally, uses regular expressions to handle this in easy cases like APA and Chicago, but the lovingly weird BJPS format requires a little more work. Other formats might need further adjustment in ways that I cannot anticipate. The post-processor should be a function which takes a string and which returns a string.
Here are examples of Ergo and Econometrica. Recall that, for Econometrica,
the \citet
output
is run through the post-processor and so is now uppercase.
Here are a number of references: \cite{Brandenberger/Dekel:1987b,Kahneman/Tversky:1979,Rubinstein:1982,Sen:1977b}.
Here are a number of references: \citep{Brandenberger/Dekel:1987b,Kahneman/Tversky:1979,Rubinstein:1982,Sen:1977b}.
References
Here are a number of references: \cite{Brandenberger/Dekel:1987b,Kahneman/Tversky:1979,Rubinstein:1982,Sen:1977b}.
Here are a number of references: \citep{Brandenberger/Dekel:1987b,Kahneman/Tversky:1979,Rubinstein:1982,Sen:1977b}.
References
Biblify loads the specified BibTeX bibliography file hosted on the server (or on your local computer) and then splits it into a big array containing individual entries. It does this by breaking at empty lines, assuming that a bibliography entry is formatted something like this:
@article{ Quine:1948,
author = {W. V. O. Quine},
journal = {The Review of Metaphysics},
month = {September},
number = {5},
pages = {21--38},
title = {On What There Is},
volume = {2},
year = {1948}
}
Each individual entry is stored in a string, and the key is extracted using a regular expression. A dictionary is created mapping keys to BibTeX entries, so that once the BibTeX file is parsed, looking up citations will be fast. N.B. If you use cross-references in your bibliography file, Biblify will not work. You will need to use another program like bibtool to remove the cross-references.
The code then walks through all of the individual text nodes contained within the
HTML element specified, and
checks to see if the text matches against a complex regular expression representing
all of the elements shown above. (Note that this means it will also match meaningless
expressions like \fullnociteauthort*{somekey:1999}
. If that happens,
it will do something but probably not what you want. So only include the meaningful expressions.)
If it finds one, it then does the following:
cite
, citet
,
citep
, citeauthor
, fullcite
, or nocite
).Cite
object using citation.js
.<span>
element with the custom attribute
data-bibtex
set to the particular key. This is useful because — as we will see
in a moment — you can use that attribute-key pair to perform some neat manipulations.
Cite
object to a list for later insertion into the bibliography,
Multiple keys are supported for \cite
, \citet
,
\citep
, \nocite
and \fullcite
.
The order of the entries is determined by the citation.js
engine.
Notice that if the author's name is not exactly the same, then weird
things will happen: e.g., \citep{Quine:1948, Quine:1951, Quine:1961}.
The \citeauthor
interpreter constructs the authors/editors
names manually, so the formatting might be a little different than what the
style template would suggest.
The inline citations are <span>
elements with a data attribute holding the BibTeX key
for the citation. It turns out that the bibliography automatically generated by citation.js
also contains a reference for the BibTeX key in an attribute. This makes it possible to use a little
bit of jQuery to create tooltips for each inline citation! The current page uses
Shoelace for the tooltips in the paragraphs below.
Move the mouse over the two references below (with the dashed line beneath) and the reference
will appear in a tooltip!
This is some text which contains a citation to \citet{Layard:2005} and an oblique reference to another paper \citep[see][pp. 28-50]{Morley:2016}. Here is are two parenthetic reference: \citep{Brown/vonNeumann:1950} and \citep{vonNeumann:1928}.
In addition, the Biblify.insertBibliography()
function accept an optional second argument, which is a dictionary of options affecting its
behaviour. At the moment, there are two recognised keys: 'all', which says whether it should insert
all of the references it has encountered on the page (true
or false
),
and 'postprocessor', which should be a function that accepts a string as an
argument and returns a string.
This function is called with a string containing the html that will be inserted. You can
do post-processing on this if you want to do additional work. For example, the main bibliography
at the end of this page is post-processed using Autolinker.js
to convert any URLs contained in the bibliography into working links.
If your main BibTeX bibliography contains a lot of files, most of them won't be used. Since Biblify loads
the bibliography file in its entirety, this can generate unnecessary network traffic. If you want
to extract just the bibliography entries which are used in a web page, Biblify provides a helper
function which lets you download a bibliography file containing just the citations it has
encountered. If you call this function after processing all of the references in a web page, it will
contain all of the needed entries. Simply call Biblify.downloadCitations(filename)
and it will download a minimal bibliography file.