{ "translatorID": "47533cd7-ccaa-47a7-81bb-71c45e68a74d", "label": "Bibliothèque nationale de France", "creator": "Florian Ziche, Sylvain Machefert", "target": "^https?://[^/]*catalogue\\.bnf\\.fr", "minVersion": "3.0", "maxVersion": "", "priority": 100, "inRepository": true, "translatorType": 4, "browserSupport": "g", "lastUpdated": "2017-07-07 11:47:44" } /* * Bibliothèque nationale de France Translator * Copyright (C) 2010 Florian Ziche, ziche@noos.fr * * 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 . */ /* Bnf namespace. */ var BnfClass = function() { //Private members /* Map MARC responsibility roles to Zotero creator types. See http://archive.ifla.org/VI/3/p1996-1/appx-c.htm. */ function getCreatorType(aut) { var typeAut = aut['4'].trim(); switch (typeAut) { case "005": case "250": case "275": case "590": //performer case "755": //vocalist return "performer"; case "040": case "130": //book designer case "740": //type designer case "750": //typographer case "350": //engraver case "360": //etcher case "430": //illuminator case "440": //illustrator case "510": //lithographer case "530": //metal engraver case "600": //photographer case "705": //sculptor case "760": //wood engraver return "artist"; case "070": case "305": case "330": case undefined: return "author"; case "020": case "210": case "212": return "commenter"; case "180": return "cartographer"; case "220": case "340": return "editor"; case "230": return "composer"; case "245": return "inventor"; case "255": case "695": //scientific advisor case "727": //thesis advisor return "counsel"; case "300": return "director"; case "400": //funder case "723": //sponsor return "sponsor"; case "460": return "interviewee"; case "470": return "interviewer"; case "480": //librettist case "520": //lyricist return "wordsBy"; case "605": return "presenter"; case "630": return "producer"; case "635": return "programmer"; case "660": return "recipient"; case "090": //author of dialog case "690": //scenarist return "scriptwriter"; case "730": return "translator"; //Ignore (no matching Zotero creatorType): case "320": //donor case "610": //printer case "650": //publisher return undefined; //Default case "205": default: return "contributor"; } }; /* Fix creators (MARC translator is not perfect). */ function getCreators(record, item) { //Clear creators item.creators = new Array(); // Extract creators (700, 701 & 702) for (var i = 700; i < 703; i++) { var authorTag = record.getFieldSubfields(i); for (var j in authorTag) { var aut = authorTag[j]; var authorText = ""; if (aut.b) { authorText = aut['a'] + ", " + aut['b']; } else { authorText = aut['a']; } var type = getCreatorType(aut); if (type) { item.creators.push(Zotero.Utilities.cleanAuthor(authorText, type, true)); } } } // Extract corporate creators (710, 711 & 712) for (var i = 710; i < 713; i++) { var authorTag = record.getFieldSubfields(i); for (var j in authorTag) { if (authorTag[j]['a']) { var type = getCreatorType(authorTag[j]); if (type) { item.creators.push({ lastName: authorTag[j]['a'], creatorType: type, fieldMode: true }); } } } } }; //Add tag, if not present yet function addTag(item, tag) { for (var t in item.tags) { if (item.tags[t] == tag) { return; } } item.tags.push(tag); }; //Tagging function getTags(record, item) { var pTag = record.getFieldSubfields("600"); if (pTag) { for (var j in pTag) { var tagText = false; var person = pTag[j]; tagText = person.a; if (person.b) { tagText += ", " + person.b; } if (person.c) { tagText += ", " + person.c; } if (person.f) { tagText += " (" + person.f + ")"; } addTag(item, tagText); } } pTag = record.getFieldSubfields("601"); if (pTag) { for (var j in pTag) { var tagText = false; var person = pTag[j]; tagText = person.a; addTag(item, tagText); } } pTag = record.getFieldSubfields("605"); if (pTag) { for (var j in pTag) { var tagText = false; var person = pTag[j]; tagText = person.a; addTag(item, tagText); } } pTag = record.getFieldSubfields("606"); if (pTag) { for (var j in pTag) { var tagText = false; var person = pTag[j]; tagText = person.a; addTag(item, tagText); } } pTag = record.getFieldSubfields("607"); if (pTag) { for (var j in pTag) { var tagText = false; var person = pTag[j]; tagText = person.a; addTag(item, tagText); } } pTag = record.getFieldSubfields("602"); if (pTag) { for (var j in pTag) { var tagText = false; var person = pTag[j]; tagText = person.a; if (person.f) { tagText += " (" + person.f + ")"; } addTag(item, tagText); } } pTag = record.getFieldSubfields("604"); if (pTag) { for (var j in pTag) { var tagText = false; var person = pTag[j]; tagText = person.a; if (person.b) { tagText += ", " + person.b; } if (person.f) { tagText += " (" + person.f + ")"; } if (person.t) { tagText += ", " + person.t; } addTag(item, tagText); } } }; //Get series (repeatable) function getSeries(record, item) { var seriesText = false; var seriesTag = record.getFieldSubfields("225"); if (seriesTag && seriesTag.length > 1) { for (var j in seriesTag) { var series = seriesTag[j]; if (seriesText) { seriesText += "; "; } else { seriesText = ""; } seriesText += series.a; if (series.v) { seriesText += ", " + series.v; } } if (seriesText) { delete item.seriesNumber; item.series = seriesText; } } //Try 461 if (!item.series) { seriesTag = record.getFieldSubfields("461"); if (seriesTag) { for (var j in seriesTag) { var series = seriesTag[j]; if (seriesText) { seriesText += "; "; } else { seriesText = ""; } seriesText += series.t; } } if (seriesText) { delete item.seriesNumber; item.series = seriesText; } } }; //Add extra text function addExtra(noteText, extra) { if (extra) { if (noteText) { if (!/\.$/.exec(noteText)) { noteText += ". "; } else { noteText += " "; } } else { noteText = ""; } noteText += Zotero.Utilities.trim(extra); } return noteText; } //Assemble extra information function getExtra(record, item) { var noteText = false; //Material description var noteTag = record.getFieldSubfields("215"); if (noteTag) { for (var j in noteTag) { var note = noteTag[j]; noteText = addExtra(noteText, note.c); noteText = addExtra(noteText, note.d); noteText = addExtra(noteText, note.e); } } //Note noteTag = record.getFieldSubfields("300"); if (noteTag) { for (var j in noteTag) { var note = noteTag[j]; noteText = addExtra(noteText, note.a); } } //Edition history notes noteTag = record.getFieldSubfields("305"); if (noteTag) { for (var j in noteTag) { var note = noteTag[j]; noteText = addExtra(noteText, note.a); } } if (noteText) { if (!/\.$/.exec(noteText)) { noteText += "."; } item.extra = noteText; } }; //Get title from 200 function getTitle(record, item) { var titleTag = record.getFieldSubfields("200"); if (titleTag) { titleTag = titleTag[0]; var titleText = titleTag.a; if (titleTag.e) { if (!/^[,\.:;-]/.exec(titleTag.e)) { titleText += ": "; } titleText += titleTag.e; } if (titleTag.h) { titleText += ", " + titleTag.h; if (titleTag.i) { titleText += ": " + titleTag.i; } } else if (titleTag.i) { titleText += ", " + titleTag.i; } item.title = titleText; } }; function getCote(record, item) { item.callNumber = ""; var coteTag = record.getFieldSubfields("930"); if (coteTag.length) { item.callNumber += coteTag[0]['c'] + "-" + coteTag[0]['a']; } }; //Do BnF specific Unimarc postprocessing function postprocessMarc(record, newItem) { //Title getTitle(record, newItem); //Fix creators getCreators(record, newItem); //Fix callNumber getCote(record, newItem); //Store perennial url from 003 as attachment and accession number var url = record.getField("003"); if (url && url.length > 0 && url[0][1]) { newItem.attachments.push({ title: 'Lien vers la notice du catalogue', url: url[0][1], mimeType: 'text/html', snapshot: false }); } //Country (102a) record._associateDBField(newItem, "102", "a", "country"); //Try to retrieve volumes/pages from 215d if (!newItem.pages) { var dimTag = record.getFieldSubfields("215"); for (var j in dimTag) { var dim = dimTag[j]; if (dim.a) { var pages = /[^\d]*(\d+)\s+p\..*/.exec(dim.a); if (pages) { newItem.numPages = pages[1]; } var vols = /[^\d]*(\d+)\s+vol\..*/.exec(dim.a); if (vols) { newItem.numberOfVolumes = vols[1]; } } } } //Series getSeries(record, newItem); //Extra getExtra(record, newItem); //Tagging getTags(record, newItem); //Repository newItem.libraryCatalog = "BnF Catalogue général (http://catalogue.bnf.fr)"; }; //Public members /* Get the UNIMARC URL for a given single result page. */ this.reformURL = function(url) { url = url.replace(/(^.*\/ark:\/12148\/cb[0-9]+[a-z]*)(.*$)/, "$1.unimarc"); // Zotero.debug("URL1 "+ url); return url; }; /* Get the results table from a list page, if any. Looks for //table[@class="ListeNotice"]. */ this.getResultsTable = function(doc) { try { var xPath = '//div[@class="liste-notices"]'; var xPathObject = doc.evaluate(xPath, doc, null, XPathResult.ANY_TYPE, null).iterateNext(); return xPathObject; } catch (x) { Zotero.debug(x.lineNumber + " " + x.message); } return undefined; }; /* Get selectable search items from a list page. Loops through //td[@class="mn_partienoticesynthetique"], extracting the single items URLs from their onclick attribute, thier titles by assembling the spans for each cell. */ this.getSelectedItems = function(doc) { var items = new Object(); var cellPath = '//div[@class="liste-notices"]/div[@class="notice-item"]'; var cells = doc.evaluate(cellPath, doc, null, XPathResult.ANY_TYPE, null); while (cell = cells.iterateNext()) { var link = doc.evaluate('./div[@class="notice-contenu"]/div[@class="notice-synthese"]/a', cell, null, XPathResult.ANY_TYPE, null).iterateNext(); var title = doc.evaluate('./h2', link, null, XPathResult.ANY_TYPE, null).iterateNext(); if (title) { title = ZU.trim(title.textContent); } else { // 2016-01-26 : Sometimes there is no H2, we then need to get everything, example when searching for : // "Se souvenir de Tonnay-Charente" title = ZU.trim(link.textContent); } var documentYear = doc.evaluate('./div[@class="notice-ordre"]', cell, null, XPathResult.ANY_TYPE, null).iterateNext(); if (documentYear) { title += " / " + documentYear.textContent; } var url = link.href; items[url] = title; } return items; }; //Check for Gallica URL (digital version available), if found, set item.url function checkGallica(record, item) { var url = record.getFieldSubfields("856"); if (url && url.length > 0 && url[0].u) { item.url = url[0].u; } } /* Process UNIMARC URL. */ this.processMarcUrl = function(newDoc, url) { /* Init MARC record. */ // Load MARC var translator = Zotero.loadTranslator("import"); translator.setTranslator("a6ee60df-1ddc-4aae-bb25-45e0537be973"); translator.getTranslatorObject(function(obj) { var record = new obj.record(); /* Get table cell containing MARC code. */ var elmts = newDoc.evaluate('//div[@class="notice-detail"]/div/div[@class="zone"]', newDoc, null, XPathResult.ANY_TYPE, null); /* Line loop. */ var elmt, tag, content; var ind = ""; while (elmt = elmts.iterateNext()) { var line = Zotero.Utilities.superCleanString(elmt.textContent); if (line.length == 0) { continue; } if (line.substring(0, 6) == " ") { content += " " + line.substring(6); continue; } else { if (tag) { record.addField(tag, ind, content); } } line = line.replace(/[_\t\xA0]/g, " "); // nbsp tag = line.substr(0, 3); if (tag[0] != "0" || tag[1] != "0") { ind = line.substr(3, 2); content = line.substr(5).replace(/\$([a-z]|[0-9])/g, obj.subfieldDelimiter + "$1"); content = content.replace(/ˆ([^‰]+)‰/g, "$1"); } else { if (tag == "000") { tag = undefined; record.leader = "0000" + line.substr(8); } else { content = line.substr(3); } } } // case last zone if (tag) { record.addField(tag, ind, content); } //Create item var newItem = new Zotero.Item(); record.translate(newItem); //Do specific Unimarc postprocessing postprocessMarc(record, newItem); //Check for Gallica URL checkGallica(record, newItem); //We have to restore the public view for the next actions //by the users, which is achieved by opening the url with //public ending again. if (url.indexOf('.public')==-1) { url = url.replace('.unimarc', '') + '.public'; } ZU.doGet(url, function(text) {; newItem.complete(); }); }); }; }; /* Global BnfClass object. */ var Bnf = new BnfClass(); /* Translator API implementation. */ function detectWeb(doc, url) { var resultRegexp = /ark:\/12148\/cb[0-9]+/i; //Single result ? if (resultRegexp.test(url)) { return "single"; } //Muliple result ? else if (Bnf.getResultsTable(doc)) { return "multiple"; } //No items return undefined; } function doWeb(doc, url) { /* Check type. */ var type = detectWeb(doc, url); Zotero.debug("type " + type); if (!type) { return; } /* Build array of MARC URLs. */ var urls = new Array(); switch (type) { case "multiple": var items = Bnf.getSelectedItems(doc); if (!items) { return true; } /* Let user select items. */ Zotero.selectItems(items, function(items) { for (var i in items) { urls.push(Bnf.reformURL(i)); } if (urls.length > 0) { //Z.debug(urls) Zotero.Utilities.processDocuments(urls, function(doc) { Bnf.processMarcUrl.call(Bnf, doc, urls[0]); }); } }); break; case "single": urls = [Bnf.reformURL(url)]; Zotero.Utilities.processDocuments(urls, function(doc) { Bnf.processMarcUrl.call(Bnf, doc, url); }); break; default: // nothing to do break; } } /** BEGIN TEST CASES **/ var testCases = [] /** END TEST CASES **/