text: {
  	"translatorID": "b6e39b57-8942-4d11-8259-342c46ce395f",
  	"translatorType": 2,
  	"label": "BibLaTeX",
  	"creator": "Simon Kornblith, Richard Karnesky and Anders Johansson - update for Robust Links by Shawn M. Jones",
  	"target": "bib",
  	"minVersion": "2.1.9",
  	"maxVersion": "null",
  	"priority": 100,
  	"inRepository": true,
  	"configOptions": {
  		"getCollections": true
  	},
  	"displayOptions": {
  		"exportCharset": "UTF-8",
  		"exportNotes": false,
  		"exportFileData": false,
  		"useJournalAbbreviation": false
  	},
  	"lastUpdated": "2019-12-15 14:26:00"
  }
  
  /*
    ***** BEGIN LICENSE BLOCK *****
  
    Copyright © 2019 Simon Kornblith, Richard Karnesky and Anders Johansson
  
    This file is part of Zotero.
  
    Zotero is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
  
    Zotero 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 Affero General Public License for more details.
  
    You should have received a copy of the GNU Affero General Public License
    along with Zotero. If not, see <http://www.gnu.org/licenses/>.
  
    ***** END LICENSE BLOCK *****
  */
  
  // %a = first listed creator surname
  // %y = year
  // %t = first word of title
  var citeKeyFormat = "%a_%t_%y";
  
  
  var fieldMap = {
  	location: "place",
  	chapter: "chapter",
  	edition: "edition",
  	title: "title",
  	volume: "volume",
  	rights: "rights", // it's rights in zotero nowadays
  	isbn: "ISBN",
  	issn: "ISSN",
  	url: "url",
  	doi: "DOI",
  	series: "series",
  	shorttitle: "shortTitle",
  	holder: "assignee",
  	abstract: "abstractNote",
  	volumes: "numberOfVolumes",
  	version: "version",
  	eventtitle: "conferenceName",
  	pages: "pages",
  	pagetotal: "numPages"
  };
  // more conversions done below with special rules
  
  /**
   * Identifiers from item.extra
   * Copied from BibTeX
   */
  // Exported in BibTeX and BibLaTeX
  var revExtraIds = {
  	LCCN: 'lccn',
  	MR: 'mrnumber',
  	Zbl: 'zmnumber',
  	PMCID: 'pmcid',
  	PMID: 'pmid',
  	DOI: 'doi'
  };
  
  // Imported by BibTeX. Exported by BibLaTeX only
  var revEprintIds = {
  	// eprinttype: Zotero label
  
  	// From BibLaTeX manual
  	arXiv: 'arxiv', // Sorry, but no support for eprintclass yet
  	JSTOR: 'jstor',
  	// PMID: 'pubmed', // Not sure if we should do this instead
  	HDL: 'hdl',
  	GoogleBooksID: 'googlebooks'
  };
  
  function parseExtraFields(extra) {
  	var lines = extra.split(/[\r\n]+/);
  	var fields = [];
  	for (var i = 0; i < lines.length; i++) {
  		var rec = { raw: lines[i] };
  		var line = lines[i].trim();
  		var splitAt = line.indexOf(':');
  		if (splitAt > 1) {
  			rec.field = line.substr(0, splitAt).trim();
  			rec.value = line.substr(splitAt + 1).trim();
  		}
  		fields.push(rec);
  	}
  	return fields;
  }
  
  function extraFieldsToString(extra) {
  	var str = '';
  	for (var i = 0; i < extra.length; i++) {
  		if (!extra[i].raw) {
  			str += '\n' + extra[i].field + ': ' + extra[i].value;
  		}
  		else {
  			str += '\n' + extra[i].raw;
  		}
  	}
  
  	return str.substr(1);
  }
  
  // POTENTIAL ISSUES
  // accessDate:"accessDate", //only written on attached webpage snapshots by zotero
  
  
  var zotero2biblatexTypeMap = {
  	book: "book",
  	bookSection: "incollection",
  	journalArticle: "article",
  	magazineArticle: "article",
  	newspaperArticle: "article",
  	thesis: "thesis",
  	letter: "letter",
  	manuscript: "unpublished",
  	interview: "misc",
  	film: "movie",
  	artwork: "artwork",
  	webpage: "online",
  	conferencePaper: "inproceedings",
  	report: "report",
  	bill: "legislation",
  	case: "jurisdiction",
  	hearing: "jurisdiction",
  	patent: "patent",
  	statute: "legislation",
  	email: "letter",
  	map: "misc",
  	blogPost: "online",
  	instantMessage: "misc",
  	forumPost: "online",
  	audioRecording: "audio",
  	presentation: "unpublished",
  	videoRecording: "video",
  	tvBroadcast: "misc",
  	radioBroadcast: "misc",
  	podcast: "audio",
  	computerProgram: "software",
  	document: "misc",
  	encyclopediaArticle: "inreference",
  	dictionaryEntry: "inreference"
  };
  
  
  var alwaysMap = {
  	"|": "{\\textbar}",
  	"<": "{\\textless}",
  	">": "{\\textgreater}",
  	"~": "{\\textasciitilde}",
  	"^": "{\\textasciicircum}",
  	"\\": "{\\textbackslash}",
  	"{": "\\{",
  	"}": "\\}"
  };
  
  
  // to map ISO language codes (tries to follow IETF RFC5646) to babel
  // language codes used in biblatex. Taken from Babel manual 3.9h.
  var babelLanguageMap = {
  	af: "afrikaans",
  	ar: "arabic",
  	// bahasa (see malay and indonesian)
  	eu: "basque",
  	br: "breton",
  	bg: "bulgarian",
  	ca: "catalan",
  	hr: "croatian",
  	cz: "czech",
  	da: "danish",
  	nl: "dutch",
  	en: {
  		"": "english", // same as american
  		US: "american",
  		GB: "british",
  		CA: "canadian",
  		AU: "australian",
  		NZ: "newzealand"
  	},
  	eo: "esperanto",
  	et: "estonian",
  	// ethiop (package for many languages)
  	fa: "farsi",
  	fi: "finnish",
  	fr: {
  		"": "french",
  		CA: "canadien"
  		// frenchle (a special package)
  	},
  	fur: "friulan",
  	gl: "galician",
  	de: {
  		"": "german",
  		AT: "austrian",
  		"DE-1996": "ngerman", // these are valid IETF language codes
  		"AT-1996": "naustrian",
  		1996: "ngerman"
  	},
  	el: {
  		"": "greek",
  		polyton: "polutonikogreek"
  	},
  	he: "hebrew",
  	hi: "hindi",
  	is: "icelandic",
  	id: "indonesian", // aliases: bahasai, indon
  	ia: "interlingua",
  	ga: "irish",
  	it: "italian",
  	ja: "japanese",
  	la: "latin",
  	lv: "latvian",
  	lt: "lithuanian",
  	dsb: "lowersorbian",
  	hu: "magyar",
  	zlm: "malay", // aliases: bahasam, melayu (currently, there's no
  	// real difference between bahasam and bahasai in babel)
  	mn: "mongolian",
  	se: "samin",
  	nn: "nynorsk", // nynorsk
  	nb: "norsk", // bokmål
  	no: "norwegian", // "no" could be used, norwegian is an alias for "norsk" in babel
  	zh: {
  		"": "pinyin", // only supported chinese in babel is the romanization pinyin?
  		Latn: "pinyin"
  	},
  	pl: "polish",
  	pt: {
  		"": "portuguese",
  		PT: "portuguese",
  		BR: "brazil"
  	},
  	ro: "romanian",
  	rm: "romansh",
  	ru: "russian",
  	gd: "scottish",
  	sr: {
  		"": "serbian", // latin script as default?
  		Cyrl: "serbianc",
  		Latn: "serbian",
  	},
  	sk: "slovak",
  	sl: "slovene",
  	// spanglish (pseudo language)
  	es: "spanish",
  	sv: "swedish",
  	th: "thaicjk", // thaicjk preferred?
  	tr: "turkish",
  	tk: "turkmen",
  	uk: "ukrainian",
  	hsb: "uppersorbian",
  	vi: "vietnamese",
  	cy: "welsh",
  };
  
  
  // some fields are, in fact, macros.  If that is the case then we should not put the
  // data in the braces as it will cause the macros to not expand properly
  function writeField(field, value, isMacro, noEscape) {
  	if (!value && typeof value != "number") return;
  	value += ""; // convert integers to strings
  	Zotero.write(",\n\t" + field + " = ");
  	if (!isMacro) Zotero.write("{");
  	// url field is preserved, for use with \href and \url
  	// Other fields (DOI?) may need similar treatment
  	if (!noEscape && !isMacro && !(field == "url" || field == "doi" || field == "file" || field == "lccn")) {
  		// var titleCase = isTitleCase(value);	//figure this out before escaping all the characters
  		// I hope these are all the escape characters! (except for < > which are handled later)
  		value = value.replace(/[|~^\\{}]/g, mapEscape).replace(/[#$%&_]/g, "\\$&");
  		// convert the HTML markup allowed in Zotero for rich text to TeX
  		value = mapHTMLmarkup(value);
  		// escape < > if mapHTMLmarkup did not convert some
  		value = value.replace(/[<>]/g, mapEscape);
  
  
  		// Case of words with uppercase characters in non-initial positions is preserved with braces.
  		// we're looking at all unicode letters
  		var protectCaps = new ZU.XRegExp("\\b\\p{Letter}+\\p{Uppercase_Letter}\\p{Letter}*", 'g');
  		if (field != "pages") {
  			value = ZU.XRegExp.replace(value, protectCaps, "{$0}");
  		}
  
  		// Page ranges should use double dash
  		if (field == "pages") {
  			value = value.replace(/[-\u2012-\u2015\u2053]+/g, "--");
  		}
  	}
  	// we write utf8
  	// convert the HTML markup allowed in Zotero for rich text to TeX; excluding doi/url/file shouldn't be necessary, but better to be safe;
  	if (!((field == "url") || (field == "doi") || (field == "file"))) value = mapHTMLmarkup(value);
  	Zotero.write(value);
  	if (!isMacro) Zotero.write("}");
  }
  
  function mapHTMLmarkup(characters) {
  	// converts the HTML markup allowed in Zotero for rich text to TeX
  	// since  < and > have already been escaped, we need this rather hideous code - I couldn't see a way around it though.
  	// italics and bold
  	characters = characters.replace(/\{\\textless\}i\{\\textgreater\}(((?!\{\\textless\}\/i{\\textgreater\}).)+)\{\\textless\}\/i{\\textgreater\}/g, "\\textit{$1}").replace(/\{\\textless\}b\{\\textgreater\}(((?!\{\\textless\}\/b{\\textgreater\}).)+)\{\\textless\}\/b{\\textgreater\}/g, "\\textbf{$1}");
  	// sub and superscript
  	characters = characters.replace(/\{\\textless\}sup\{\\textgreater\}(((?!\{\\textless\}\/sup\{\\textgreater\}).)+)\{\\textless\}\/sup{\\textgreater\}/g, "$^{\\textrm{$1}}$").replace(/\{\\textless\}sub\{\\textgreater\}(((?!\{\\textless\}\/sub\{\\textgreater\}).)+)\{\\textless\}\/sub\{\\textgreater\}/g, "$_{\\textrm{$1}}$");
  	// two variants of small caps
  	characters = characters.replace(/\{\\textless\}span\sstyle="small-caps"\{\\textgreater\}(((?!\{\\textless\}\/span\{\\textgreater\}).)+)\{\\textless\}\/span{\\textgreater\}/g, "\\textsc{$1}").replace(/\{\\textless\}sc\{\\textgreater\}(((?!\{\\textless\}\/sc\{\\textgreater\}).)+)\{\\textless\}\/sc\{\\textgreater\}/g, "\\textsc{$1}");
  	return characters;
  }
  
  function mapEscape(character) {
  	return alwaysMap[character];
  }
  
  // a little substitution function for BibTeX keys, where we don't want LaTeX
  // escaping, but we do want to preserve the base characters
  
  function tidyAccents(s) {
  	var r = s.toLowerCase();
  
  	// XXX Remove conditional when we drop Zotero 2.1.x support
  	// This is supported in Zotero 3.0 and higher
  	if (ZU.removeDiacritics !== undefined) {
  		r = ZU.removeDiacritics(r, true);
  	}
  	else {
  		// We fall back on the replacement list we used previously
  		r = r.replace(new RegExp("[ä]", 'g'), "ae");
  		r = r.replace(new RegExp("[ö]", 'g'), "oe");
  		r = r.replace(new RegExp("[ü]", 'g'), "ue");
  		r = r.replace(new RegExp("[àáâãå]", 'g'), "a");
  		r = r.replace(new RegExp("æ", 'g'), "ae");
  		r = r.replace(new RegExp("ç", 'g'), "c");
  		r = r.replace(new RegExp("[èéêë]", 'g'), "e");
  		r = r.replace(new RegExp("[ìíîï]", 'g'), "i");
  		r = r.replace(new RegExp("ñ", 'g'), "n");
  		r = r.replace(new RegExp("[òóôõ]", 'g'), "o");
  		r = r.replace(new RegExp("œ", 'g'), "oe");
  		r = r.replace(new RegExp("[ùúû]", 'g'), "u");
  		r = r.replace(new RegExp("[ýÿ]", 'g'), "y");
  	}
  
  	return r;
  }
  
  var numberRe = /^[0-9]+/;
  // Below is a list of words that should not appear as part of the citation key
  // it includes the indefinite articles of English, German, French and Spanish, as well as a small set of English prepositions whose
  // force is more grammatical than lexical, i.e. which are likely to strike many as 'insignificant'.
  // The assumption is that most who want a title word in their key would prefer the first word of significance.
  var citeKeyTitleBannedRe = /\b(a|an|the|some|from|on|in|to|of|do|with|der|die|das|ein|eine|einer|eines|einem|einen|un|une|la|le|l'|el|las|los|al|uno|una|unos|unas|de|des|del|d')(\s+|\b)|(<\/?(i|b|sup|sub|sc|span style="small-caps"|span)>)/g;
  var citeKeyConversionsRe = /%([a-zA-Z])/;
  
  var citeKeyConversions = {
  	a: function (flags, item) {
  		if (item.creators && item.creators[0] && item.creators[0].lastName) {
  			return item.creators[0].lastName.toLowerCase().replace(/ /g, "_").replace(/,/g, "");
  		}
  		return "noauthor";
  	},
  	t: function (flags, item) {
  		if (item.title) {
  			return item.title.toLowerCase().replace(citeKeyTitleBannedRe, "").split(/\s+/g)[0];
  		}
  		return "notitle";
  	},
  	y: function (flags, item) {
  		if (item.date) {
  			var date = Zotero.Utilities.strToDate(item.date);
  			if (date.year && numberRe.test(date.year)) {
  				return date.year;
  			}
  		}
  		return "nodate";
  	}
  };
  
  // checks whether an item contains any creator of type ctype
  function creatorCheck(item, ctype) {
  	if (item.creators && item.creators.length) {
  		for (var i = 0; i < item.creators.length; i++) {
  			if (item.creators[i].creatorType == ctype) {
  				return true; // found a ctype creator
  			}
  		}
  	}
  	// didn't find any ctype creator (or no creators at all)
  	return false;
  }
  
  function buildCiteKey(item, extraFields, citekeys) {
  	if (extraFields) {
  		const citationKey = extraFields.findIndex(field => field.field && field.value && field.field.toLowerCase() === 'citation key');
  		if (citationKey >= 0) return extraFields.splice(citationKey, 1)[0].value;
  	}
  
  	if (item.citationKey) return item.citationKey;
  
  	var basekey = "";
  	var counter = 0;
  	var citeKeyFormatRemaining = citeKeyFormat;
  	while (citeKeyConversionsRe.test(citeKeyFormatRemaining)) {
  		if (counter > 100) {
  			Zotero.debug("Pathological BibTeX format: " + citeKeyFormat);
  			break;
  		}
  		var m = citeKeyFormatRemaining.match(citeKeyConversionsRe);
  		if (m.index > 0) {
  			// add data before the conversion match to basekey
  			basekey += citeKeyFormatRemaining.substr(0, m.index);
  		}
  		var flags = ""; // for now
  		var f = citeKeyConversions[m[1]];
  		if (typeof (f) == "function") {
  			var value = f(flags, item);
  			Zotero.debug("Got value " + value + " for %" + m[1]);
  			// add conversion to basekey
  			basekey += value;
  		}
  		citeKeyFormatRemaining = citeKeyFormatRemaining.substr(m.index + m.length);
  		counter++;
  	}
  	if (citeKeyFormatRemaining.length > 0) {
  		basekey += citeKeyFormatRemaining;
  	}
  
  	// for now, remove any characters not explicitly known to be allowed;
  	// we might want to allow UTF-8 citation keys in the future, depending
  	// on implementation support.
  	//
  	// no matter what, we want to make sure we exclude
  	// " # % ' ( ) , = { } ~ and backslash
  	// however, we want to keep the base characters
  
  	basekey = tidyAccents(basekey);
  	// use legacy pattern for all old items to not break existing usages
  	var citeKeyCleanRe = /[^a-z0-9!$&*+\-./:;<>?[\]^_`|]+/g;
  	// but use the simple pattern for all newly added items
  	// or always if the hiddenPref is set
  	// extensions.zotero.translators.BibLaTeX.export.simpleCitekey
  	if ((Zotero.getHiddenPref && Zotero.getHiddenPref('BibLaTeX.export.simpleCitekey'))
  			|| (item.dateAdded && parseInt(item.dateAdded.substr(0, 4)) >= 2020)) {
  		citeKeyCleanRe = /[^a-z0-9_-]/g;
  	}
  	basekey = basekey.replace(citeKeyCleanRe, "");
  	var citekey = basekey;
  	var i = 0;
  	while (citekeys[citekey]) {
  		i++;
  		citekey = basekey + "-" + i;
  	}
  	citekeys[citekey] = true;
  	return citekey;
  }
  
  var filePathSpecialChars = '\\\\:;{}$'; // $ for Mendeley
  var encodeFilePathRE = new RegExp('[' + filePathSpecialChars + ']', 'g');
  
  function encodeFilePathComponent(value) {
  	if (!value) return '';
  	return value.replace(encodeFilePathRE, "\\$&");
  }
  
  function doExport() {
  	// Zotero.write("% biblatex export generated by Zotero "+Zotero.Utilities.getVersion());
  	// to make sure the BOM gets ignored
  	Zotero.write("\n");
  
  	var first = true;
  	var citekeys = {};
  	var item;
  	// eslint-disable-next-line no-cond-assign
  	while (item = Zotero.nextItem()) {
  		// don't export standalone notes and attachments
  		if (item.itemType == "note" || item.itemType == "attachment") continue;
  
  		var noteused = false; // a switch for keeping track whether the
  		// field "note" has been written to
  		// determine type
  		var type = zotero2biblatexTypeMap[item.itemType];
  		if (typeof (type) == "function") {
  			type = type(item);
  		}
  
  		// inbook is reasonable at times, using a bookauthor should
  		// indicate this
  		if (item.itemType == "bookSection"
  			&& creatorCheck(item, "bookAuthor")) type = "inbook";
  
  		// a book without author but with editors is a collection
  		if (item.itemType == "book" && !creatorCheck(item, "author")
  			&& creatorCheck(item, "editor")) type = "collection";
  
  		// biblatex recommends us to use mvbook for multi-volume book
  		// i.e. a book with "# of vols" filled
  		if (type == "book" && item.numberOfVolumes) type = "mvbook";
  
  		if (!type) type = "misc";
  
  		var extraFields = item.extra ? parseExtraFields(item.extra) : null;
  		var citekey = buildCiteKey(item, extraFields, citekeys);
  
  		// write citation key (removed the comma)
  		Zotero.write((first ? "" : "\n\n") + "@" + type + "{" + citekey);
  		first = false;
  
  		for (var field in fieldMap) {
  			if (item[fieldMap[field]]) {
  				writeField(field, item[fieldMap[field]]);
  			}
  		}
  
  		// Fields needing special treatment and not easily translatable via fieldMap
  		// e.g. where fieldname translation is dependent upon type, or special transformations
  		// has to be made
  
  		// all kinds of numbers except patents, which need post-processing
  		if (item.reportNumber || item.seriesNumber || item.billNumber || item.episodeNumber || item.number && !item.patentNumber) {
  			writeField("number", item.reportNumber || item.seriesNumber || item.billNumber || item.episodeNumber || item.number);
  		}
  
  		// split numeric and nonnumeric issue specifications (for journals) into "number" and "issue"
  		if (item.issue) { // issue
  			var jnumber = parseInt(item.issue);
  			if (!isNaN(jnumber)) {
  				writeField("number", jnumber);
  			}
  			else {
  				writeField("issue", item.issue);
  			}
  		}
  
  
  		// publicationTitles and special titles
  		if (item.publicationTitle) {
  			if (item.itemType == "bookSection" || item.itemType == "conferencePaper" || item.itemType == "dictionaryEntry" || item.itemType == "encyclopediaArticle") {
  				writeField("booktitle", item.publicationTitle);
  			}
  			else if (item.itemType == "magazineArticle" || item.itemType == "newspaperArticle") {
  				writeField("journaltitle", item.publicationTitle);
  			}
  			else if (item.itemType == "journalArticle") {
  				if (Zotero.getOption("useJournalAbbreviation") && item.journalAbbreviation) {
  					writeField("journaltitle", item.journalAbbreviation);
  				}
  				else {
  					writeField("journaltitle", item.publicationTitle);
  					writeField("shortjournal", item.journalAbbreviation);
  				}
  			}
  		}
  
  		if (item.websiteTitle || item.forumTitle || item.blogTitle || item.programTitle) {
  			writeField("titleaddon", item.websiteTitle || item.forumTitle || item.blogTitle || item.programTitle);
  		}
  
  
  		// publishers
  		if (item.publisher) {
  			if (item.itemType == "thesis" || item.itemType == "report") {
  				writeField("institution", item.publisher);
  			}
  			else {
  				writeField("publisher", item.publisher);
  			}
  		}
  
  		// things concerning "type"
  		if (item.itemType == "letter") {
  			if (item.letterType) {
  				writeField("type", item.letterType);
  			}
  			else {
  				writeField("type", "Letter"); // this isn't optimal, perhaps later versions of biblatex will add some suitable localization key
  			}
  		}
  		else if (item.itemType == "email") {
  			writeField("type", "E-mail");
  		}
  		else if (item.itemType == "thesis"
  					&& (!item.thesisType || item.thesisType.search(/ph\.?d/i) != -1)) {
  			writeField("type", "phdthesis");
  		}
  		else if (item.manuscriptType || item.thesisType || item.websiteType || item.presentationType || item.reportType || item.mapType) {
  			writeField("type", item.manuscriptType || item.thesisType || item.websiteType || item.presentationType || item.reportType || item.mapType);
  		}
  		else if (item.itemType == "patent") {
  			// see https://tex.stackexchange.com/questions/447383/biblatex-biber-patent-citation-support-based-on-zoterobbl-output/447508
  			if (!item.patentNumber) {
  				writeField("type", "patent");
  			}
  			else if (item.patentNumber.startsWith("US")) {
  				writeField("type", "patentus");
  				writeField("number", item.patentNumber.replace(/^US/, ""));
  			}
  			else if (item.patentNumber.startsWith("EP")) {
  				writeField("type", "patenteu");
  				writeField("number", item.patentNumber.replace(/^EP/, ""));
  			}
  			else if (item.patentNumber.startsWith("GB")) {
  				writeField("type", "patentuk");
  				writeField("number", item.patentNumber.replace(/^GB/, ""));
  			}
  			else if (item.patentNumber.startsWith("DE")) {
  				writeField("type", "patentde");
  				writeField("number", item.patentNumber.replace(/^DE/, ""));
  			}
  			else if (item.patentNumber.startsWith("FR")) {
  				writeField("type", "patentfr");
  				writeField("number", item.patentNumber.replace(/^FR/, ""));
  			}
  			else {
  				writeField("type", "patent");
  				writeField("number", item.patentNumber);
  			}
  		}
  
  		if (item.presentationType || item.manuscriptType) {
  			writeField("howpublished", item.presentationType || item.manuscriptType);
  		}
  
  		// case of specific eprint-archives in archive-fields
  		if (item.archive && item.archiveLocation) {
  			if (item.archive == "arXiv" || item.archive == "arxiv") {
  				writeField("eprinttype", "arxiv");
  				writeField("eprint", item.archiveLocation);
  				if (item.callNumber) { // assume call number is used for arxiv class
  					writeField("eprintclass", item.callNumber);
  				}
  			}
  			else if (item.archive == "JSTOR" || item.archive == "jstor") {
  				writeField("eprinttype", "jstor");
  				writeField("eprint", item.archiveLocation);
  			}
  			else if (item.archive == "PubMed" || item.archive == "pubmed") {
  				writeField("eprinttype", "pubmed");
  				writeField("eprint", item.archiveLocation);
  			}
  			else if (item.archive == "HDL" || item.archive == "hdl") {
  				writeField("eprinttype", "hdl");
  				writeField("eprint", item.archiveLocation);
  			}
  			else if (item.archive == "googlebooks" || item.archive == "Google Books") {
  				writeField("eprinttype", "googlebooks");
  				writeField("eprint", item.archiveLocation);
  			}
  		}
  
  		// presentations have a meetingName field which we want to
  		// map to note
  		if (item.meetingName) {
  			writeField("note", item.meetingName);
  			noteused = true;
  		}
  
  		if (item.creators && item.creators.length) {
  			// split creators into subcategories
  			var author = "";
  			var bookauthor = "";
  			var commentator = "";
  			var editor = "";
  			var editora = "";
  			var editorb = "";
  			var holder = "";
  			var translator = "";
  			var noEscape = false;
  
  			for (let i = 0; i < item.creators.length; i++) {
  				var creator = item.creators[i];
  				var creatorString;
  
  				if (creator.firstName) {
  					var fname = creator.firstName.split(/\s*,!?\s*/);
  					fname.push(fname.shift()); // If we have a Jr. part(s), it should precede first name
  					creatorString = creator.lastName + ", " + fname.join(', ');
  				}
  				else {
  					creatorString = creator.lastName;
  				}
  
  				creatorString = creatorString.replace(/[|<>~^\\{}]/g, mapEscape)
  					.replace(/([#$%&_])/g, "\\$1");
  
  				if (creator.fieldMode == true) { // fieldMode true, assume corporate author
  					creatorString = "{" + creatorString + "}";
  					noEscape = true;
  				}
  				else {
  					creatorString = creatorString.replace(/ (and) /gi, ' {$1} ');
  				}
  
  				if (creator.creatorType == "author" || creator.creatorType == "interviewer" || creator.creatorType == "inventor" || creator.creatorType == "director" || creator.creatorType == "programmer" || creator.creatorType == "artist" || creator.creatorType == "podcaster" || creator.creatorType == "presenter") {
  					author += " and " + creatorString;
  				}
  				else if (creator.creatorType == "bookAuthor") {
  					bookauthor += " and " + creatorString;
  				}
  				else if (creator.creatorType == "commenter") {
  					commentator += " and " + creatorString;
  				}
  				else if (creator.creatorType == "editor") {
  					editor += " and " + creatorString;
  				}
  				else if (creator.creatorType == "translator") {
  					translator += " and " + creatorString;
  				}
  				else if (creator.creatorType == "seriesEditor") { // let's call them redacors
  					editorb += " and " + creatorString;
  				}
  				else { // the rest into editora with editoratype = collaborator
  					editora += " and " + creatorString;
  				}
  			}
  
  			// remove first " and " string
  			if (author) {
  				writeField("author", author.substr(5), false, noEscape);
  			}
  			if (bookauthor) {
  				writeField("bookauthor", bookauthor.substr(5), false, noEscape);
  			}
  			if (commentator) {
  				writeField("commentator", commentator.substr(5), false, noEscape);
  			}
  			if (editor) {
  				writeField("editor", editor.substr(5), false, noEscape);
  			}
  			if (editora) {
  				writeField("editora", editora.substr(5), false, noEscape);
  				writeField("editoratype", "collaborator");
  			}
  			if (editorb) {
  				writeField("editorb", editorb.substr(5), false, noEscape);
  				writeField("editorbtype", "redactor");
  			}
  			if (holder) {
  				writeField("holder", holder.substr(5), false, noEscape);
  			}
  			if (translator) {
  				writeField("translator", translator.substr(5), false, noEscape);
  			}
  		}
  
  		if (item.accessDate) {
  			writeField("urldate", Zotero.Utilities.strToISO(item.accessDate));
  		}
  
  		// TODO enable handling of date ranges when that's added to zotero
  		if (item.date) {
  			writeField("date", Zotero.Utilities.strToISO(item.date));
  		}
  
  		// Map Languages to biblatex-field "langid" (used for
  		// hyphenation with a correct setting of the "autolang" option)
  		// if possible. See babelLanguageMap above for languagecodes to use
  		if (item.language) {
  			var langcode = item.language.match(/^([a-z]{2,3})(?:[^a-z](.+))?$/i); // not too strict
  			if (langcode) {
  				var lang = babelLanguageMap[langcode[1]];
  				if (typeof lang == 'string') {
  					// if there are no variants for this language
  					writeField("langid", lang);
  				}
  				else if (typeof lang == 'object') {
  					var variant = lang[langcode[2]];
  					if (variant) {
  						writeField("langid", variant);
  					}
  					else {
  						writeField("langid", lang[""]); // use default variant
  					}
  				}
  			}
  		}
  
  		if (extraFields) {
  			// Export identifiers
  			// Dedicated fields
  			for (let i = 0; i < extraFields.length; i++) {
  				var rec = extraFields[i];
  				if (!rec.field) continue;
  
  				if (!revExtraIds[rec.field] && !revEprintIds[rec.field]) continue;
  
  				var value = rec.value.trim();
  				if (!value) continue;
  
  				var label = revExtraIds[rec.field];
  				if (label) {
  					writeField(label, '{' + value + '}', true);
  				}
  				else {
  					label = revEprintIds[rec.field];
  					if (label) {
  						writeField('eprinttype', label);
  						writeField('eprint', '{' + value + '}', true);
  					}
  				}
  				extraFields.splice(i, 1);
  				i--;
  			}
  
  			var extra = extraFieldsToString(extraFields);
  			if (extra && !noteused) writeField("note", extra);
  		}
  
  		if (item.tags && item.tags.length) {
  			var tagString = "";
  			for (let i = 0; i < item.tags.length; i++) {
  				tagString += ", " + item.tags[i].tag;
  			}
  			writeField("keywords", tagString.substr(2));
  		}
  
  
  		if (item.notes && Zotero.getOption("exportNotes")) {
  			for (let i = 0; i < item.notes.length; i++) {
  				var note = item.notes[i];
  				writeField("annotation", Zotero.Utilities.unescapeHTML(note.note));
  			}
  		}
  
  		var foundRobustLink = false;
  		var versionurl;
  		var versiondate;
  		var originalurl;
  
  		if (item.attachments) {
  			var attachmentString = "";
  
  			for (let i = 0; i < item.attachments.length; i++) {
  				var attachment = item.attachments[i];
  
  				if ( attachment.title == "Robust Link" ) {
  					var parser = new DOMParser();
  					var htmlDoc = parser.parseFromString( attachment.note, 'text/html' );
  					var anchors = htmlDoc.getElementsByTagName('a');
  
  					for ( var j=0; j < anchors.length; j++ ) {
  						if (!versionurl) {
  							versionurl = anchors[j].getAttribute('data-versionurl');
  						}
  						
  						if (!versiondate) {
  							versiondate = anchors[j].getAttribute('data-versiondate');
  						}
  						
  						if (!originalurl) {
  							originalurl = anchors[j].getAttribute('data-originalurl');
  						}
  						
  						foundRobustLink = true;
  					}
  				}
  
  				if (Zotero.getOption("exportFileData") && attachment.saveFile) {
  					attachment.saveFile(attachment.defaultPath, true);
  					attachmentString += ";" + encodeFilePathComponent(attachment.title) + ":"
  						+ encodeFilePathComponent(attachment.defaultPath) + ":"
  						+ encodeFilePathComponent(attachment.mimeType);
  				}
  				else if (attachment.localPath) {
  					attachmentString += ";" + encodeFilePathComponent(attachment.title) + ":"
  						+ encodeFilePathComponent(attachment.localPath) + ":"
  						+ encodeFilePathComponent(attachment.mimeType);
  				}
  			}
  
  			if (attachmentString) {
  				writeField("file", attachmentString.substr(1));
  			}
  		}
  
  		if (foundRobustLink === true) {
  			writeField("versionurl", versionurl);
  			writeField("versiondate", versiondate);
  			writeField("originalurl", originalurl);	
  		}
  
  		Zotero.write("\n}");
  	}
  }
  