diff --git a/Dalloz Bibliotheque.js b/Dalloz Bibliotheque.js new file mode 100644 index 0000000000..174ca5d657 --- /dev/null +++ b/Dalloz Bibliotheque.js @@ -0,0 +1,171 @@ +{ + "translatorID": "2ea86ad9-71ca-410c-9126-9d7d98722acf", + "label": "Dalloz Bibliothèque", + "creator": "Alexandre Mimms", + "target": "https?://(www\\.)?bibliotheque\\.lefebvre\\.dalloz\\.fr", + "minVersion": "5.0", + "maxVersion": "", + "priority": 100, + "inRepository": true, + "translatorType": 4, + "browserSupport": "gcsibv", + "lastUpdated": "2024-04-23 10:41:20" +} + +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2024 Alexandre Mimms + + 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 . + + ***** END LICENSE BLOCK ***** +*/ + + +function detectWeb(doc, url) { + if (url.includes('/ouvrage/')) { + return 'book'; + } + else if (url.includes('/recherche') && getSearchResults(doc, true)) { + return 'multiple'; + } + return false; +} + +function getSearchResults(doc, checkOnly) { + var items = {}; + var found = false; + var rows = doc.querySelectorAll('.result-list-grid-item'); + + for (let row of rows) { + let href = row.querySelectorAll("a")[0].href; + let title = ZU.trimInternal(row.querySelectorAll(".detail-title")[0].innerText); + if (!href || !title) continue; + if (checkOnly) return true; + found = true; + items[href] = title; + } + return found ? items : false; +} + +async function doWeb(doc, url) { + if (detectWeb(doc, url) == 'multiple') { + let items = await Zotero.selectItems(getSearchResults(doc, false)); + if (!items) return; + for (let url of Object.keys(items)) { + await scrape(await requestDocument(url)); + } + } + else { + await scrape(doc, url); + } +} + +async function scrape(doc, url = doc.location.href) { + let edition, date, marque, collection, isbn, auteurs; + const editions = doc.querySelectorAll(".editions-box"); + for (let ed of editions) { + if (ed.querySelectorAll("a")[0].href == url) { + edition = text(ed, ".editions-edition"); + date = text(ed, ".editions-date"); + } + } + + const infoGen = doc.querySelectorAll(".notice-header-grid-item"); + for (let infoBox of infoGen) { + + const value = ZU.trimInternal(infoBox.innerText); + + if (infoBox.querySelector(".auteurs")) { + auteurs = ZU.trimInternal(infoBox.querySelector(".auteurs").innerText).split(" • "); + Z.debug(auteurs); + } + + if (value.startsWith("Collection")) { collection = value.split(" : ")[1]; } + else if (value.startsWith("Marque")) { marque = value.split(" : ")[1]; } + else if (value.startsWith("ISBN")) { isbn = value.split(" : ")[1]; } + } + + const titre = ZU.trimInternal(text(doc, ".title")); + const abstract = ZU.trimInternal(text(doc, ".description")).replace("Description", ""); + + let newItem = new Z.Item("book"); + + for (let auteur of auteurs) { + newItem.creators.push(ZU.cleanAuthor(auteur, "author")); + } + + newItem.title = titre; + newItem.date = date; + newItem.abstractNote = abstract; + newItem.ISBN = ZU.cleanISBN(isbn); + newItem.edition = edition; + newItem.publisher = marque; + newItem.language = "fr"; + newItem.series = collection; + + newItem.complete(); +} + +/** BEGIN TEST CASES **/ +var testCases = [ + { + "type": "web", + "url": "https://bibliotheque.lefebvre-dalloz.fr/recherche?query=livre", + "items": "multiple" + }, + { + "type": "web", + "url": "https://bibliotheque.lefebvre-dalloz.fr/ouvrage/grands-arrets/grands-arrets-jurisprudence-civile-t1_9782247154579", + "items": [ + { + "itemType": "book", + "title": "Les grands arrêts de la jurisprudence civile T1", + "creators": [ + { + "firstName": "Henri", + "lastName": "Capitant", + "creatorType": "author" + }, + { + "firstName": "Yves", + "lastName": "Lequette", + "creatorType": "author" + }, + { + "firstName": "François", + "lastName": "Terré", + "creatorType": "author" + } + ], + "date": "Avril 2015", + "ISBN": "9782247154579", + "abstractNote": "La 13e édition des Grands arrêts de la jurisprudence civile coïncide avec le quatre-vingtième anniversaire de leur parution sous la signature de Henri Capitant. C'est dire que cet ouvrage est le précurseur de tous les recueils de Grands arrêts actuellement existants. Jamais démenti, son succès vient de ce qu'il offre un accès direct aux grandes décisions qui ont permis au Code civil de s'adapter à la réalité sociale contemporaine.L'ouvrage est scindé en deux tomes.Le premier volume réunit la totalité des matières étudiées, d'une université à l'autre, en licence 1 : Introduction, mais aussi droit des personnes, droit de la famille et droit des biens.S'y ajoutent le droit des régimes matrimoniaux et celui des successions et des libéralités qui, situés au confluent du droit de la famille et du droit du patrimoine, sont le prolongement naturel des disciplines précédentes.Le second volume rassemble la théorie générale des obligations (acte juridique, responsabilité, quasi-contrats, régime général) ainsi que les disciplines qui évoluent dans son orbite : contrats spéciaux, sûretés. Il correspond aux matières généralement enseignées en licence 2 et en licence 3.À l'occasion de cette 13e édition, les auteurs ont procédé à une importante mise à jour : nombre de commentaires ont été partiellement ou totalement réécrits pour prendre en compte les évolutions survenues depuis la précédente édition, il y a huit ans.", + "edition": "13e édition", + "language": "fr", + "libraryCatalog": "Dalloz Bibliothèque", + "publisher": "DALLOZ", + "series": "Grands arrêts", + "attachments": [], + "tags": [], + "notes": [], + "seeAlso": [] + } + ] + } +] +/** END TEST CASES **/ diff --git a/Dalloz.js b/Dalloz.js new file mode 100644 index 0000000000..84864ad14b --- /dev/null +++ b/Dalloz.js @@ -0,0 +1,270 @@ +{ + "translatorID": "a59e99a6-42b0-4be6-bb0c-1ff688c3a8b3", + "label": "Dalloz", + "creator": "Alexandre Mimms", + "target": "https?://(www\\.)?dalloz\\.fr", + "minVersion": "5.0", + "maxVersion": "", + "priority": 100, + "inRepository": true, + "translatorType": 4, + "browserSupport": "gcsibv", + "lastUpdated": "2024-04-23 09:45:03" +} + +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2024 Alexandre Mimms + + 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 . + + ***** END LICENSE BLOCK ***** +*/ +// TODO +// - Make sure that the case report are correctly saved. +// - PDF import : needs reverse engineering the internal api of the service. Seems like a quite complex one. + +const citationAvecNumero = /^([\D]+)\s*(\d{4}),?\s?(n°\s?\d+) *,?\s*(p\.\s?\d+)*/; +const citationSansNumero = /^([\D]+)\s*(\d{4}),?\s*(p\.\d+)?/; +const regAnnee = /\d{4}/; +const docTypeId = /id=([^%_]+)(?:%2F|_)?/; + +const codeDocument = new Map([ + ["ENCY", "dictionary-entry"], + ["JA", "journalArticle"], + ["AJ", "journalArticle"], + ["ACTU", "blogPost"], + ["RFDA", "journalArticle"], + ["CONSCONST", "case"], + ["CONSTIT", "journalArticle"], + ["DIPI", "journalArticle"], + ["DS", "journalArticle"], + ["JA", "journalArticle"], + ["JT", "journalArticle"], + ["JS", "journalArticle"], + ["JCAS", "journalArticle"], + ["LEGI", "journalArticle"], + ["CAHJ", "journalArticle"], + ["RDI", "journalArticle"], + ["RDSS", "journalArticle"], + ["RECU", "journalArticle"], + ["LEBO", "case"], + ["REV", "journalArticle"], + ["RMC", "journalArticle"], + ["RSC", "journalArticle"], + ["RTD", "journalArticle"], + ["RPR", "journalArticle"], + ["RCJ", "journalArticle"], + +]); + + + +function detectWeb(doc, url) { + if (url.includes('/documentation/Document')) { // Checks if the page is a document. + let id = url.match(docTypeId)[1]; + Z.debug(id); + if (codeDocument.get(id)) { return codeDocument.get(id) } + else if (idStartsWithKey(id)) { return codeDocument.get(id.substring(0, 2)); } // Gets the value of the key if it is a shorthand. + // Returns the type of the document according to the ID - refer to the const Map declared. + } + else if (url.includes('/documentation/Liste') && getSearchResults(doc, true)) { // Checks if the page is a list of results. + return 'multiple'; + } + return false; +} + +// The following function checks if the ID passed as argument has an associated key (some IDs start with the same letters - easier than filing all available IDs). +function idStartsWithKey(string) { + for (let key of codeDocument.keys()) { + if (key.startsWith(string.substring(0, 2))) { + return true; + } + } + return false; +} + +function scrapeJournalArticle(doc, url = doc.location.href) { + // Since searches trigger a "< >" markup around the searched words, we have to edit that away before storing the values. + let refDoc = ZU.trimInternal(text(doc, ".refDoc").replace(/[<>]/g, "")); // gets the reference + let page, revue, numRevue, date; + let auteurs = []; + + // Loop over the "signatures" of the document, and store the author in the list. + for (let signature of doc.querySelectorAll(".chronSIGNATURE")) { + auteurs.push(signature.innerText.replace(/[<>]/g, "").split(',')[0]); + } + + if (citationAvecNumero.test(refDoc)) { + refDoc = refDoc.split(citationAvecNumero); + } + else if (citationSansNumero.test(refDoc)) { + refDoc = refDoc.split(citationSansNumero); + } + + for (let item of refDoc) { + if (item.startsWith("p")) { + page = item.replace("p.", ""); + } + else if (item.startsWith("n")) { + numRevue = item.replace("n°", ""); + } + else if (regAnnee.test(item)) { + date = item; + } + else if (item !== "") { + revue = item; + } + } + + let newItem = new Z.Item("journalArticle"); + + newItem.title = ZU.trimInternal(text(doc, ".chronTITRE")).replace(/[<>]/g, ""); + for (let auth of auteurs) { // loop over the list of authors and set them as authors. + newItem.creators.push(ZU.cleanAuthor(auth, "author")); + } + + newItem.publicationTitle = revue; + newItem.abstractNote = ZU.trimInternal(text(doc, "#RESUFRAN")).replace(/[<>]/g, ""); + if (numRevue !== "") newItem.issue = numRevue; + newItem.pages = page; + newItem.date = date; + newItem.url = url; + newItem.language = "fr"; + newItem.complete(); +} + +function scrapeCase(doc, url = doc.location.href) { + let juridiction, titre, abstract, formation, date, volume, mentionPublication, numeroAffaire; + + if (url.includes("LEBON")) { + juridiction = "Conseil d'État"; + // Since searches trigger a "< >" markup around the searched words, we have to edit that away before storing the values. + titre = ZU.trimInternal(text(doc, ".jurisJURI")).replace(/[<>]/g, ""); // gets the title of the document + abstract = ZU.trimInternal(text(doc, ".jurisSOMMAIRE")).replace(/[<>]/g, ""); // gets the abstract + formation = ZU.trimInternal(text(doc, ".jurisCHAM").replace(/[<>]/g, "")); // gets the reference + date = ZU.trimInternal(text(doc, ".jurisDATE").replace(/[<>]/g, "")); + volume = date.split("-")[2]; + mentionPublication = ZU.trimInternal(text(doc, ".commentPopupNDC b").replace(/[<>]/g, "")); + numeroAffaire = ZU.trimInternal(text(doc, ".jurisNAAF").replace(/[<>]/g, "").replace("n° ", "")); + } + else { + juridiction = ZU.trimInternal(text(doc, ".book-header-title-caselaw__juridiction")); + date = ZU.trimInternal(text(doc, ".book-header-title-caselaw__date")); + numeroAffaire = ZU.trimInternal(text(doc, ".book-header-title-caselaw__references")); + abstract = ""; + + if (juridiction == "Conseil constitutionnel") { titre = `Cons. constit., ${numeroAffaire}, ${date}`; } + else { titre = `${juridiction}, ${date}, ${numeroAffaire}`; } + + } + + let newItem = new Z.Item("case"); + + newItem.caseName = titre; + newItem.court = juridiction; + newItem.reporter = mentionPublication; + newItem.abstractNote = abstract.replace("Sommaire : ", ""); + newItem.court = juridiction; + newItem.dateDecided = date; + newItem.reporterVolume = volume || ""; + newItem.docketNumber = numeroAffaire; + newItem.language = "fr"; + newItem.url = url; + newItem.extra = formation; + newItem.complete(); +} + + +// This function is basically as it was set by the template. I modified it so it is specific to Dalloz. +function getSearchResults(doc, checkOnly) { + var items = {}; + var found = false; + var rows = doc.querySelectorAll('.result-content'); + for (let row of rows) { + let href = attr(row, "a", "href"); + let title = ZU.trimInternal(text(row, "a")); + if (!href || !title) continue; + if (checkOnly) return true; + found = true; + items[href] = title; + } + return found ? items : false; +} + +// Nothing changed here neither. +async function doWeb(doc, url) { + const docType = detectWeb(doc, url); // calling detectWeb once and passing it to scrape function, + // so we don't have to call it multiple times to check in the scrape function what type of document it is. + + if (docType == 'multiple') { + let items = await Zotero.selectItems(getSearchResults(doc, false)); + if (!items) return; + for (let url of Object.keys(items)) { + await scrape(await requestDocument(url)); + } + } + else { + await scrape(doc, url, docType); + } +} + +async function scrape(doc, url = doc.location.href, docType) { + if (docType == "journalArticle") { + scrapeJournalArticle(doc, url); + } + else if (docType == "case") { + scrapeCase(doc, url); + } +} + + +/** BEGIN TEST CASES **/ +var testCases = [ + +] +/** END TEST CASES **/ +/** BEGIN TEST CASES **/ +var testCases = [ + { + "type": "web", + "url": "https://www.dalloz.fr/dalloz", + "detectedItemType": false, + "items": [] + }, + { + "type": "web", + "url": "https://www.dalloz.fr/documentation/Document?ctxt=0_YSR0MD1jb25zdGl0dXRpb27Cp3gkc2Y9c2ltcGxlLXNlYXJjaA%3D%3D&ctxtl=0_cyRwYWdlTnVtPTHCp3MkdHJpZGF0ZT1GYWxzZcKncyRzb3J0PSNkZWZhdWx0X0Rlc2PCp3Mkc2xOYlBhZz0yMMKncyRpc2Fibz1UcnVlwqdzJHBhZ2luZz1UcnVlwqdzJG9uZ2xldD3Cp3MkZnJlZXNjb3BlPVRydWXCp3Mkd29JUz1GYWxzZcKncyR3b1NQQ0g9RmFsc2XCp3MkZmxvd01vZGU9RmFsc2XCp3MkYnE9wqdzJHNlYXJjaExhYmVsPcKncyRzZWFyY2hDbGFzcz3Cp3Mkej0wREJGQzhEQi8xOEUwNjY0Mw%3D%3D&id=CONSCONST_LIEUVIDE_2024-01-18_20231076QPC", + "items": [ + { + "itemType": "case", + "caseName": "Cons. constit., n° 2023-1076 QPC, 18 janvier 2024", + "creators": [], + "dateDecided": "18 janvier 2024", + "court": "Conseil constitutionnel", + "docketNumber": "n° 2023-1076 QPC", + "language": "fr", + "url": "https://www.dalloz.fr/documentation/Document?ctxt=0_YSR0MD1jb25zdGl0dXRpb27Cp3gkc2Y9c2ltcGxlLXNlYXJjaA%3D%3D&ctxtl=0_cyRwYWdlTnVtPTHCp3MkdHJpZGF0ZT1GYWxzZcKncyRzb3J0PSNkZWZhdWx0X0Rlc2PCp3Mkc2xOYlBhZz0yMMKncyRpc2Fibz1UcnVlwqdzJHBhZ2luZz1UcnVlwqdzJG9uZ2xldD3Cp3MkZnJlZXNjb3BlPVRydWXCp3Mkd29JUz1GYWxzZcKncyR3b1NQQ0g9RmFsc2XCp3MkZmxvd01vZGU9RmFsc2XCp3MkYnE9wqdzJHNlYXJjaExhYmVsPcKncyRzZWFyY2hDbGFzcz3Cp3Mkej0wREJGQzhEQi8xOEUwNjY0Mw%3D%3D&id=CONSCONST_LIEUVIDE_2024-01-18_20231076QPC", + "attachments": [], + "tags": [], + "notes": [], + "seeAlso": [] + } + ] + } +] +/** END TEST CASES **/ diff --git a/Jus Politicum.js b/Jus Politicum.js new file mode 100644 index 0000000000..5272c91437 --- /dev/null +++ b/Jus Politicum.js @@ -0,0 +1,142 @@ +{ + "translatorID": "aeb7f19b-0907-4117-bef4-08e36af4d31f", + "label": "Jus Politicum", + "creator": "Alexandre Mimms", + "target": "https?://(www\\.)?juspoliticum\\.com", + "minVersion": "5.0", + "maxVersion": "", + "priority": 100, + "inRepository": true, + "translatorType": 4, + "browserSupport": "gcsibv", + "lastUpdated": "2024-04-23 10:45:20" +} + +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2024 Alexandre Mimms + + 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 . + + ***** END LICENSE BLOCK ***** +*/ + + +function detectWeb(doc, url) { + if (url.includes('/article/')) { + return 'journalArticle'; + } + else if (url.includes('/searches') && getSearchResults(doc, true)) { + return 'multiple'; + } + return false; +} + +function getSearchResults(doc, checkOnly) { + var items = {}; + var found = false; + var rows = doc.querySelectorAll('#search-section h2 a'); + for (let row of rows) { + let href = row.href; + let title = ZU.trimInternal(row.textContent); + if (!href || !title) continue; + if (checkOnly) return true; + found = true; + items[href] = title; + } + return found ? items : false; +} + +async function doWeb(doc, url) { + if (detectWeb(doc, url) == 'multiple') { + let items = await Zotero.selectItems(getSearchResults(doc, false)); + if (!items) return; + for (let url of Object.keys(items)) { + await scrape(await requestDocument(url)); + } + } + else { + await scrape(doc, url); + } +} + +async function scrape(doc, url = doc.location.href) { + const abstract = ZU.trimInternal(text(doc, "#content")); + const titre = ZU.trimInternal(text(doc, "h2")); + const numero = text(doc, ".release-title .num").replace("N°", ""); + const linkURL = doc.querySelectorAll(".documentsAssocies a")[0].href; + const auteurs = text(doc, ".article-author").split(", "); + + let newItem = new Zotero.Item("journalArticle"); + + for (let auteur of auteurs) { + newItem.creators.push(ZU.cleanAuthor(auteur, "author")); + } + + newItem.title = titre; + newItem.issue = numero; + newItem.abstractNote = abstract; + newItem.url = url; + + newItem.attachments = [{ + url: linkURL, + title: "Full text PDF", + mimeType: "application/pdf", + }]; + + newItem.complete(); +} +/** BEGIN TEST CASES **/ +var testCases = [ + { + "type": "web", + "url": "https://juspoliticum.com/searches?expression=constitution&release=&author=&theme=", + "items": "multiple" + }, + { + "type": "web", + "url": "https://juspoliticum.com/article/Situation-presente-du-constitutionnalisme-Quelques-reflexions-sur-l-idee-de-democratie-par-le-droit-25.html", + "items": [ + { + "itemType": "journalArticle", + "title": "Situation présente du constitutionnalisme. Quelques réflexions sur l’idée de démocratie par le droit", + "creators": [ + { + "firstName": "Jean-Marie", + "lastName": "Denquin", + "creatorType": "author", + "fieldMode": true + } + ], + "abstractNote": "Le constitutionnalisme est aujourd’hui identifié, à tort ou à raison, à l’idée d’une « démocratie par le droit », laquelle recouvre souvent un projet d’accomplissement des droits fondamentaux par des moyens juridiques. L’article analyse le déplacement que cela implique du point de vue du sens du mot « démocratie », mais aussi l’effet de survalorisation du droit qui en résulte. Ce phénomène de sacralisation du droit explique aussi comment on en est venu à confondre constitutionnalisme et droit constitutionnel. Plusieurs idées centrales du premier sont devenues, dans le second, des techniques dont on présume le caractère non problématique et la neutralité. Cela est démontré par une étude de l’évolution récente des notions de séparation des pouvoirs et de hiérarchie des normes, notamment dans la jurisprudence du Conseil constitutionnel. The present Situation of Constitutionalism. Some Thoughts about the Idea of Democracy by LawConstitutionalism is, more often than not, equated with a notion of “achieving democracy through the law”. This entails the notion that, in a democracy, the law is expected to ensure the development of fundamental rights. The purpose of the article is to analyse the shift thus involved in the concept of democracy, as well as the high expectations law has to meet in order to achieve these goals. It is suggested that this involves a collapse of “constitutionalism” into “constitutional law”. Several important aspects of constitutionalism, such as the separation of powers or the existence of a hierarchy of norms, are transformed into technical words of art which courts use as if they were neutral and uncontroversial. Die aktuelle Lage des Konstitutionalismus’. Ansichten über die Frage der Demokratie durch RechtDer Konstitutionalismus (Verfassungsstaat) ist heute oft mit der Idee einer „Demokratie durch Recht“ identifiziert. In diesem Sinne wird meistens Demokratie als Verwirklichung der Grundrechte durch juristische Mitteln angesehen. Dadurch erfährt der Begriff Demokratie eine Verschiebung. Zudem erfährt der Begriff des Rechts eine Aufwertung ja sogar eine Überbewertung, die zu einer Verwechslung von Konstitutionalismus und Verfassungsrecht führt. Mehrere zentrale Elemente des Konstitutionalismus sind im Verfassungsrecht zu blossen Techniken geworden, die man als unproblematisch und neutral postuliert. Dies wird im vorliegenden Aufsatz am Beispiel der neuesten Entwicklungen der Gewaltenteilung und der Normenhierarchie verdeutlicht.", + "issue": "1", + "libraryCatalog": "Jus Politicum", + "url": "https://juspoliticum.com/article/Situation-presente-du-constitutionnalisme-Quelques-reflexions-sur-l-idee-de-democratie-par-le-droit-25.html", + "attachments": [ + { + "title": "Full text PDF", + "mimeType": "application/pdf" + } + ], + "tags": [], + "notes": [], + "seeAlso": [] + } + ] + } +] +/** END TEST CASES **/ diff --git a/Lextenso.js b/Lextenso.js new file mode 100644 index 0000000000..65ba475be5 --- /dev/null +++ b/Lextenso.js @@ -0,0 +1,189 @@ +{ + "translatorID": "3243d081-22c0-452c-8298-9d8a9fb5de2f", + "label": "Lextenso", + "creator": "Alexandre Mimms", + "target": "https?://(www\\.)?labase-lextenso\\.fr/", + "minVersion": "5.0", + "maxVersion": "", + "priority": 100, + "inRepository": true, + "translatorType": 4, + "browserSupport": "gcsibv", + "lastUpdated": "2024-04-23 11:30:31" +} + +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2024 Alexandre Mimms + + 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 . + + ***** END LICENSE BLOCK ***** +*/ + + +function detectWeb(doc, url) { + if (url.includes('/lextenso/rechercher') && getSearchResults(doc, true)) { + return 'multiple'; + } + else if (doc.querySelector(".node-type-ouvrage")) { + return 'book'; + } + else if (doc.querySelector(".lextenso-document-article")) { + return 'journalArticle'; + } + return false; +} + + +async function scrapeJournalArticle(doc, url) { + const references = ZU.trimInternal(text(doc, ".document-metadata-origin").replace("Issu de ", "")).split(" - "); + const revue = references[0]; + const numeroRevue = references[1]; + const page = references[2]; + const titre = ZU.trimInternal(text(doc, "#page-title")); + const auteurs = doc.querySelectorAll(".document-metadata-authors-name"); + const abstract = ZU.trimInternal(text(doc, ".cChapeau")); + const date = ZU.trimInternal(text(doc, ".document-metadata-date").replace("Date de parution : ", "")); + + let newItem = new Z.Item("journalArticle"); + newItem.title = titre; + + for (let auteur of auteurs) { + newItem.creators.push(ZU.cleanAuthor(auteur.innerText, "author")); + } + + newItem.date = date; + newItem.abstractNote = abstract; + newItem.publicationTitle = revue; + if (numeroRevue) { newItem.issue = numeroRevue.replace(/n°[0]?/, ""); } + if (page) { newItem.pages = page.replace(/page\s?/, ""); } + newItem.complete(); +} + +async function scrapeBook(doc, url) { + // weirdly enough no real information is displayed on the book summary page, but + // some info, like ISBN, is shown on individual pages. + // So, we get the first url to one of those individual pages, then request it so we + // can fetch the information. + // I did not yet find a way to fetch the number of page or edition. + // I tried accessing the link of the shop, where those are displayed, but the request + // fails. + const firstItemUrl = doc.querySelectorAll(".book-summary-list li a")[0].href; + Z.debug(firstItemUrl); + const indivPage = await requestDocument(firstItemUrl); + + // Accessing the metadata - reversing the list, since there can be multiple authors + // the end of the list will always be the same, so easier and surer to do it like that. + const ref = text(indivPage, ".document-metadata-ref .value", 0).split(", ").reverse(); + const date = ref[2]; + const publisher = ref[1]; + const isbn = ref[0]; + + const auteurs = indivPage.querySelectorAll(".document-metadata-authors-name"); + + const titre = text(doc, "#page-title"); + + let newItem = new Z.Item("book"); + newItem.title = titre; + + for (let auteur of auteurs) { + newItem.creators.push(ZU.cleanAuthor(auteur.innerText, "author")); + } + + newItem.date = date; + newItem.publisher = publisher; + newItem.ISBN = isbn; + newItem.pages = page.replace(/page\s?/, ""); + newItem.url = url; + newItem.language = "fr"; + newItem.complete(); +} + + +function getSearchResults(doc, checkOnly) { + var items = {}; + var found = false; + + var rows = doc.querySelectorAll('.hit'); + for (let row of rows) { + let href = row.querySelectorAll("a")[0].href; + let title = ZU.trimInternal(row.querySelectorAll("h3")[0].innerText); + if (!href || !title) continue; + if (checkOnly) return true; + found = true; + items[href] = title; + } + return found ? items : false; +} + +async function doWeb(doc, url) { + const docType = detectWeb(doc, url); + if (docType == 'multiple') { + let items = await Zotero.selectItems(getSearchResults(doc, false)); + if (!items) return; + for (let url of Object.keys(items)) { + await scrape(await requestDocument(url), docType); + } + } + else { + await scrape(doc, url, docType); + } +} + +async function scrape(doc, url = doc.location.href, docType) { + if (docType == "journalArticle") { + scrapeJournalArticle(doc, url); + } + else if (docType == "book") { + scrapeBook(doc, url); + } +} + +/** BEGIN TEST CASES **/ +var testCases = [ + { + "type": "web", + "url": "https://www.labase-lextenso.fr/", + "detectedItemType": false, + "items": [] + }, + { + "type": "web", + "url": "https://www.labase-lextenso.fr/revue-generale-du-droit-des-assurances/RGA201v5", + "items": [ + { + "itemType": "journalArticle", + "title": "La priorité du tiers lésé sur l'indemnité d'assurance", + "creators": [ + { + "firstName": "James", + "lastName": "Landel", + "creatorType": "author" + } + ], + "abstractNote": "Action directe ; C. assur., art. L. 124-3 ; Somme versée par l’assureur du responsable à la personne indiquée comme « preneur d'assurance / assuré » et « conducteur » sur le constat amiable ; Condamnation de l’assureur envers le tiers lésé, propriétaire du véhicule ; Montant ; Cour d’appel : soustraction des sommes payées au tiers lésé une somme versée à un tiers ; Cassation", + "libraryCatalog": "Lextenso", + "attachments": [], + "tags": [], + "notes": [], + "seeAlso": [] + } + ] + } +] +/** END TEST CASES **/ diff --git a/Vie Publique.js b/Vie Publique.js new file mode 100644 index 0000000000..03801672e5 --- /dev/null +++ b/Vie Publique.js @@ -0,0 +1,245 @@ +{ + "translatorID": "858fa86d-82e2-43ca-9fc7-cf75b98101cb", + "label": "Vie Publique", + "creator": "Alexandre Mimms", + "target": "https?://(www\\.)?vie-publique\\.fr/", + "minVersion": "5.0", + "maxVersion": "", + "priority": 100, + "inRepository": true, + "translatorType": 4, + "browserSupport": "gcsibv", + "lastUpdated": "2024-04-23 12:20:52" +} + +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2024 Alexandre Mimms + + 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 . + + ***** END LICENSE BLOCK ***** +*/ + +function detectWeb(doc, url) { + if (url.includes('/rapport/')) { + return 'report'; + } + else if (url.includes('/discours')) { + return 'presentation'; + } + else if (url.includes('/recherche') && getSearchResults(doc, true)) { + return 'multiple'; + } + return false; +} + + +function scrapeRapport(doc, url) { + let page, reportType; + const titre = text(doc, "h1"); + const auteursString = doc.querySelectorAll(".book--author a"); + const auteursMoraux = doc.querySelectorAll(".book--author-moral a"); + const abstract = text(doc, "#fiche-item-présentation"); + const information = doc.querySelectorAll(".tabpanel--technique--details li"); + + if (information.length > 0) { + for (let info of information) { + let value = info.innerText + if (value.startsWith('Pagination')) { + page = info.innerText.replace("Pagination : ", "").replace(" pages", ""); + } + else if (value.startsWith('Type de document')) { + reportType = information[0].innerText.replace("Type de document : ", ""); + } + } + } + + const date = text(doc, ".field--name-field-date-remise"); + + const pdfLink = doc.querySelectorAll(".book--actionsBox a")[0].href; + + const tags = doc.querySelectorAll(".vp-item-tag"); + + let newItem = new Z.Item('report'); + newItem.title = titre || ""; + newItem.date = date; + + for (let aut of auteursString) { + newItem.creators.push(ZU.cleanAuthor(aut.innerText, "author")) + } + + if (auteursMoraux.length > 1) { + for (let autMoral of auteursMoraux) { + newItem.institution += ", " + autMoral.innerText; + } + } + else { newItem.institution = auteursMoraux[0].innerText; } + + newItem.abstractNote = abstract; + newItem.pages = page; + newItem.reportType = reportType; + newItem.url = url; + + newItem.attachments = [{ + url: pdfLink, + title: "Full Text PDF", + mimeType: "application/pdf", + snapshot: false + }]; + + for (let tag of tags) { + newItem.tags.push({ tag: tag.innerText }); + } + + newItem.complete(); +} + +function scrapeSpeech(doc, url) { + const titre = text(doc, "h1"); + const auteursString = doc.querySelectorAll(".line-intervenant a"); + const date = text(doc, ".datetime"); + const tags = doc.querySelectorAll(".vp-item-tag"); + + let newItem = new Z.Item('presentation'); + newItem.title = titre || ""; + newItem.date = date; + newItem.url = url; + + for (let aut of auteursString) { + newItem.creators.push(ZU.cleanAuthor(aut, "author")); + } + + for (let tag of tags) { + newItem.tags.push({ tag: tag.innerText }); + } + + newItem.complete(); +} + + + +function getSearchResults(doc, checkOnly) { + var items = {}; + var found = false; + var rows = doc.querySelectorAll('h3 > a'); + for (let row of rows) { + let href = row.href; + let title = ZU.trimInternal(row.textContent); + if (!href || !title) continue; + if (checkOnly) return true; + found = true; + items[href] = title; + } + return found ? items : false; +} + +async function doWeb(doc, url) { + const docType = detectWeb(doc, url); + if (docType == 'multiple') { + let items = await Zotero.selectItems(getSearchResults(doc, false)); + if (!items) return; + for (let url of Object.keys(items)) { + await scrape(await requestDocument(url)); + } + } + else { + await scrape(doc, url, docType); + } +} + +async function scrape(doc, url = doc.location.href) { + const docType = detectWeb(doc, url); + if (docType == "report") { + scrapeRapport(doc, url); + } + else if (docType == "presentation") { + scrapeSpeech(doc, url); + } +} + +/** BEGIN TEST CASES **/ +var testCases = [ + { + "type": "web", + "url": "https://www.vie-publique.fr/", + "detectedItemType": false, + "items": [] + }, + { + "type": "web", + "url": "https://www.vie-publique.fr/rapport/286137-les-outre-mer-dans-la-constitution", + "items": [ + { + "itemType": "report", + "title": "Rapport d'information (...) sur les outre-mer dans la Constitution", + "creators": [ + { + "firstName": "Stéphane", + "lastName": "Artano", + "creatorType": "author" + } + ], + "date": "18 juillet 2022", + "abstractNote": "Le 29 juin 2022, les membres de la Délégation sénatoriale aux outre-mer et ceux de l'Association des juristes en droit des outre-mer (AJDOM) ont échangé au Sénat sur la situation des outre-mer dans la Constitution et débattu des trajectoires d'avenir pour les territoires concernés.\nCette réunion conjointe s'est déroulée autour de deux tables rondes.\nLa première, consacrée à la Nouvelle-Calédonie, a permis de pointer plusieurs interrogations. Il ressort notamment que la question du corps électoral est sans doute la plus sensible, à la fois politiquement et juridiquement, et qu'il sera très difficile de faire l'impasse sur une révision de la Constitution.\nLa seconde, axée sur les collectivités régies par les articles 73 et 74 de la Constitution, a mis en évidence les défauts et le caractère artificiel de cette dichotomie affichée.", + "institution": "Sénat. Délégation aux outre-mer", + "libraryCatalog": "Vie Publique", + "pages": "67", + "reportType": "Rapport parlementaire", + "url": "https://www.vie-publique.fr/rapport/286137-les-outre-mer-dans-la-constitution", + "attachments": [ + { + "title": "Full Text PDF", + "mimeType": "application/pdf", + "snapshot": false + } + ], + "tags": [ + { + "tag": "Collectivité d'outre mer" + }, + { + "tag": "Collectivités territoriales" + }, + { + "tag": "Constitution" + }, + { + "tag": "Institutions" + }, + { + "tag": "Institutions de l'Etat" + }, + { + "tag": "Outre-mer" + }, + { + "tag": "Statut juridique" + } + ], + "notes": [], + "seeAlso": [] + } + ] + }, + { + "type": "web", + "url": "https://www.vie-publique.fr/recherche?search_api_fulltext=constitution&f%5B0%5D=categories%3Arapport", + "items": "multiple" + } +] +/** END TEST CASES **/