{ "translatorID":"ba4cd274-f24e-42cf-8ff2-ccfc603aacf3", "translatorType":2, "label":"BibLaTeX-with-hacks", "creator":"Simon Kornblith, Richard Karnesky and Anders Johansson", "target":"bib", "minVersion":"2.1.9", "maxVersion":"null", "priority":100, "inRepository":false, "configOptions":{"getCollections":"true"}, "displayOptions": { "exportCharset": "UTF-8", "exportNotes": false, "exportFileData": false, "useJournalAbbreviation": false }, "lastUpdated":"2014-11-17 00:36" } //%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", 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' }; // 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 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": "{\\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 = 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\}/, "\\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 // in 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)/g; var citeKeyConversionsRe = /%([a-zA-Z])/; var citeKeyCleanRe = /[^a-z0-9\!\$\&\*\+\-\.\/\:\;\<\>\?\[\]\^\_\`\|]+/g; 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 ""; }, "t": function (flags, item) { if (item["title"]) { return item["title"].toLowerCase().replace(citeKeyTitleBannedRe, "").split(/\s+/g)[0]; } return ""; }, "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 "????"; } } //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 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 = 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 = basekey + value; } citeKeyFormatRemaining = citeKeyFormatRemaining.substr(m.index + m.length); counter++; } if (citeKeyFormatRemaining.length > 0) { basekey = 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); 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) { 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"); Zotero.write("% This is a file in the format of BibLaTeX (http://www.ctan.org/pkg/biblatex), using features unsupported by standard BibTeX, and generated with the BibLaTeX-with-hacks translator of Zotero " + Zotero.Utilities.getVersion() + "\n\n"); var first = true; var citekeys = new Object(); var item; 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 books if(type == "book" && item.volume) type = "mvbook" if(!type) type = "misc"; //If you want a custom citekey: just use //"biblatexcitekey[mycitekey] in the Extra field var citekey = ""; if (item.extra && item.extra.search(/biblatexcitekey\[[^\]]+\]/) != -1) { citekey = item.extra.match(/biblatexcitekey\[([^\]]+)\]/)[1]; item.extra = item.extra.replace(/biblatexcitekey\[([^\]]+)\]/, ""); } if(!citekey) { // create a unique citation key citekey = buildCiteKey(item, 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 if(item.reportNumber || item.seriesNumber || item.patentNumber || item.billNumber || item.episodeNumber || item.number) { writeField("number", item.reportNumber || item.seriesNumber || item.patentNumber || 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")){ 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); } 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 (var i=0; i\~\^\\\{\}]/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 == "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 == "inventor") { holder += " 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", commenter.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(item.extra) { // this is for extracting fields not available in Zotero that one want's to use // in BibLaTex. Add the data in Zotero's 'extra' field e.g. like this and it // will be extracted: // biblatexdata[chapter=3;origyear=1909] if(/biblatexdata\[[^\]]+\]/.test(item.extra)){ var ex = item.extra.match(/biblatexdata\[([^\]]+)\]/)[1]; item.extra = item.extra.replace(/biblatexdata\[[^\]]+\]/, ""); var blf = ex.split(";"); for (var i=0; i