Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Insert citations from Zotero as you write in ShareLaTeX
// ==UserScript==
// @version ∞
// @name Zotero ShareLaTeX Cite-as-you-Write
// @namespace https://github.com/dlukes
// @author dlukes
// @description Insert citations from Zotero into ShareLaTeX as you write.
// @match *://sharelatex.korpus.cz/*
// @run-at document-end
// @grant unsafeWindow
// @grant GM.xmlHttpRequest
// ==/UserScript==
/* Zotero ShareLaTeX Cite-as-you-Write
* ===================================
*
* This userscript leverages the Better BibTeX Zotero extension in
* order to make it possible to insert citations into ShareLaTeX
* documents via the Zotero popup.
*
* Installation
* ------------
*
* You need a userscript manager for your web browser of choice, e.g.
* GreaseMonkey or TamperMonkey are popular browser extensions that
* serve this purpose. Once you've installed it, add this userscript
* (refer to the documentation of your userscript manager for
* information on how to do this).
*
* After you've added the script, you'll probably want to configure the
* following things:
*
* - set @match above to match the URL of your ShareLaTex server
* - go through the TODOs below and customize at will based on the
* provided guidelines
*
* Usage
* -----
*
* The userscript provides two additional keyboard shortcuts when using
* ShareLaTeX, which are by default:
*
* - Ctrl+. -- calls up the Zotero popup, allows you to put together a
* citation, and inserts it into the document
* - Ctrl+Shift+. -- inserts a Zotero collection exported as a
* Bib(La)TeX bibliography database into the document. This is
* intended as an easy way to update your ShareLaTeX .bib file after
* you've made edits to the bibliography in Zotero.
*
* It determines the Zotero collection to generate your bibliography
* from by searching your .bib file for a collection declaration in
* the following format:
*
* % -*- zotero-sharelatex-cayw-collection: <library-number>/<collection-name>.<format> -*-
*
* E.g. the following will generate a biblatex bibliography for a
* collection named NLP within your private Zotero library (0):
*
* % -*- zotero-sharelatex-cayw-collection: 0/NLP.biblatex -*-
*
* To figure out the identifier for a collection, right-click on the
* collection in Zotero, select Download Better BibTeX export, and
* inspect the generated URLs.
*
* If no collection declaration is provided, it will ask whether to
* export your entire personal library, which can take a while if
* there are many items and the export is not cached.
*
* Cf. https://retorque.re/zotero-better-bibtex/push-and-pull/ for
* more information.
*/
var COLLECTION_RE = /-\*-\s*zotero-sharelatex-cayw-collection:\s*(.*?)\s*-\*-/;
var ABSTRACT_RE = /^ abstract = \{[\s\S]*?^( \w+ = \{)/gm;
function zotError() {
var msg = "Can't reach the bibliography database! Make sure that Zotero is " +
"running and the Better BibTeX extension for Zotero is installed.";
console.error(msg);
alert(msg);
}
function zotWarnAndAsk() {
var msg = "No collection declaration found in file. Specify one in the following " +
"format:\n\n" +
" % -*- zotero-sharelatex-cayw-collection: <library-number>/<collection-name>.<format> -*-\n\n" +
"E.g. the following will generate a biblatex bibliography for a collection named " +
"NLP within your private Zotero library (0):\n\n" +
" % -*- zotero-sharelatex-cayw-collection: 0/NLP.biblatex -*-\n\n" +
"To figure out the identifier for a collection, right-click on the collection " +
"in Zotero, select Download Better BibTeX export, and inspect the generated " +
"URLs.\n\n" +
"As a default, I can also just try to insert a bibliography based on your " +
"entire private library, but that may take a while, depending on its size. " +
"Proceed?";
console.warn(msg);
return confirm(msg);
}
function getAceEditor() {
var ace = unsafeWindow.ace;
return ace.edit(document.querySelector(".ace-editor-body"));
}
function zoteroFetchAndInsert(url, postProcessFunc) {
GM.xmlHttpRequest({
method: "GET",
url: url,
headers: {
"Zotero-Allowed-Request": true
},
onload: function(resp) {
var editor = getAceEditor();
var content = postProcessFunc(resp.responseText);
// cursor position = an object of the form {column: x, row: y}
var cursorPosition = editor.getCursorPosition();
editor.session.insert(cursorPosition, content);
},
onerror: zotError
});
}
function zoteroInsertBibliography() {
var editor = getAceEditor();
var doc = editor.session.toString();
var match = COLLECTION_RE.exec(doc);
var collection;
if (match) {
collection = "collection?/" + match[1];
} else {
if (!zotWarnAndAsk()) return;
collection = "library?/0/library.biblatex";
}
zoteroFetchAndInsert(
"http://localhost:23119/better-bibtex/" + collection,
function(responseText) {
// TODO: you can manipulate the string before it's inserted --
// e.g. get rid of abstracts
return responseText.replace(ABSTRACT_RE, "$1");
}
);
}
function zoteroCite() {
zoteroFetchAndInsert(
// TODO: customize citation format by modifying the URL
"http://localhost:23119/better-bibtex/cayw?format=latex",
function(responseText) {
// TODO: you can manipulate the string before it's inserted
return responseText;
}
);
}
window.onkeyup = function(e) {
// TODO: you can customize the keyboard shortcuts here
if (e.ctrlKey && e.shiftKey && e.keyCode === 190) {
zoteroInsertBibliography();
} else if (e.ctrlKey && e.keyCode === 190) {
zoteroCite();
}
};
@nrepina

This comment has been minimized.

Copy link

nrepina commented Jul 21, 2019

Hi! I'd very much like to implement your script, but I'm having trouble getting it to work. I have Zotero/BBT installed and running on my Mac, and the script running on Overleaf through Tampermonkey (on google chrome). However, no CAYW pop-up shows up using the keyboard shortcuts. I'm not familiar with how to troubleshoot this, do you have any suggestions? Thank you!

@dlukes

This comment has been minimized.

Copy link
Owner Author

dlukes commented Jul 22, 2019

Hi, I wasn't quite sure if it works on the official Overleaf site, since I use it on a local install of the community version of Overleaf/ShareLaTeX, but I just tried it out and it seems it's working fine :) The only thing you need to modify is the header of the userscript by adding a match statement which will trigger the userscript to run on the overleaf.com website, something like this:

  // ==UserScript==
  // @version         ∞
  // @name            Zotero ShareLaTeX Cite-as-you-Write
  // @namespace       https://github.com/dlukes
  // @author          dlukes
  // @description     Insert citations from Zotero into ShareLaTeX as you write.
- // @match           *://sag.korpus.cz/*
+ // @match           *://www.overleaf.com/*
  // @run-at          document-end
  // @grant           unsafeWindow
  // @grant           GM.xmlHttpRequest
  // ==/UserScript==

Oh and maybe one other thing you might want to change -- the keyboard shortcut. By default, it's Ctrl+., but that also seems to trigger recompilation in Overleaf, which might be annoying (you probably don't want to recompile your document each time you add a citation).

E.g. this will use Ctrl+, (and Ctrl+Shift+,) instead to insert a citation (and generate the bibliography):

  window.onkeyup = function(e) {
-   if (e.ctrlKey && e.shiftKey && e.keyCode === 190) {
+   if (e.ctrlKey && e.shiftKey && e.keyCode === 188) {
      zoteroInsertBibliography();
-   } else if (e.ctrlKey && e.keyCode === 190) {
+   } else if (e.ctrlKey && e.keyCode === 188) {
      zoteroCite();
    }
};

One last thing, the focus transfer between the Zotero window and the browser window is kind of broken on macOS and Windows -- after inserting a citation, the focus stays on Zotero. But that seems to be a general problem with Zotero cite-as-you-write, my colleagues have been telling me that this is happening to them even with the Word plugin for instance. It works fine on Linux though, if you have the option to use that :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.