MediaWiki:MARCimporterJS.js
Nota: Após publicar, você pode ter que limpar o "cache" do seu navegador para ver as alterações.
- Firefox / Safari: Pressione Shift enquanto clica Recarregar, ou pressione Ctrl-F5 ou Ctrl-R (⌘-R no Mac)
- Google Chrome: Pressione Ctrl-Shift-R (⌘-Shift-R no Mac)
- Internet Explorer/Edge: PressioneCtrl enquanto clica Recarregar, ou Pressione Ctrl-F5
- Opera: Pressione Ctrl-F5.
/* jshint esversion: 10 */
function mainFunc() {
/* Este script é dividido basicamente em 4 partes: um handler para registros
MARC em ISO 2709, um handler para registros no formato "MARC tags", uma
configuração para registros bibliográficos e uma configuração para registros
de autoridade. O script foi baseado no Módulo:MARCimporter, usando Lua.
*/
// inicializa as variáveis básicas do registro
let leader = '';
let baseAddressOfData = 0;
let directory = 0;
let dataValuesGroup = '';
// inicializa as variáveis auxiliares
let directoryEntry = 0;
let entryTag = 0;
let entryDataLen = 0;
let entryInitPos = 0;
// Objeto que contém os dados dos campos. Formato:
// { tag: '', len: '', initPos: '', data: '', ind1: '', ind2: '' }
let dataField = {};
// Array que contém cada objeto dataField. Formato: [ {}, {}, {}, {}, {} ]
let dataFields = [];
// campos especiais
let controlField003 = '';
let controlField006 = '';
let controlField007 = '';
let controlField008 = '';
let dataField040 = '';
// objeto que contém as configurações do registro bibliográfico
let bibRecordParams = {};
// objeto que contém as configurações do registro de autoridade
let autRecordParams = {};
// variáveis finais de retorno
let url = '';
let fieldQueryString = '';
let fieldTemplates = '';
let templates = '';
function normalizeData(data) {
const newdata = data
.replace(/\|(.)/g, ' $$$1 ') // substitui "|a" por " $a "
.replace(/(\d)p\./, '$1 p.') // "1p." por "1 p."
.replace(/(\d)cm/, '$1 cm') // "1cm" por "1 cm"
.replace(/(\d)ed\./, '$1 ed.') // "1ed." por "1 ed."
.replace(/\.\s?-$/, '.') // ". -" por "."
.replace(/(\$.*)(\$w.*)/, '$2 $1') // move o subcampo $w para a frente
.replace(/(\s\$9\s.*)/, '') // remove o subcampo $9
.replace(/(\$z.*)(\$u.*)/, '$2 $1') // move o subcampo $u para a frente
.replace(/[\n\r]/g, ''); // remove line feed e carriage return
return newdata;
}
function isoRecordHandler(isoRec) {
/* MARC ISO 2709 record handler:
Em Lua, o MediaWiki substitui caracteres de controle (RS, US, GS) pelo
caractere de "desconhecido" (losango com interrogação) e, aqui,
substituo esses caracteres por um pipe (é necessário substituir
por um caractere da faixa ASCII). Em JavaScript talvez isso não precisasse
ser feito mas, para manter a mesma lógica do script em Lua, decidi deixar
assim mesmo.
*/
let record = isoRec;
record = record.replace(/\p{Cc}/gu, '|');
// configuração das variáveis básicas do registro:
// obtém o líder (Ex.: 00898nam a2200277 a 4500)
leader = record.substring(0, 25);
// obtém o endereço base dos dados
baseAddressOfData = parseInt(leader.substring(12, 17), 10); // Ex.: 00277
// obtém o diretório (-1 para não pegar RS)
// Ex: 0010010000000050017000100080041000270200027000680400017000950...
directory = record.substring(24, baseAddressOfData - 1);
// obtém os dados dos campos em um único grupo
dataValuesGroup = record.substring(baseAddressOfData);
// é necessário transformar a string em uma cadeia de bytes para posterior
// separação dos dados
dataValuesGroup = new TextEncoder().encode(dataValuesGroup);
// enquanto o tamanho do diretório for maior que 0...
while (directory.length > 0) {
directoryEntry = directory.substring(0, 12); // 245008100177
entryTag = directoryEntry.substring(0, 3); // 245
entryDataLen = parseInt(directoryEntry.substring(3, 7), 10); // 0081
entryInitPos = parseInt(directoryEntry.substring(7, 13), 10); // 00177
// cria um objeto com os dados...
dataField = {
initPos: entryInitPos,
len: entryDataLen,
tag: entryTag,
};
// e insere cada objeto dataField na array dataFields
dataFields.push(dataField);
// esvazia o diretório de 12 em 12
directory = directory.substring(12);
}
for (dataField of dataFields) {
// configura as variáveis para separação dos campos
const i = dataField.initPos;
const j = dataField.initPos + dataField.len - 1;
// localiza o dado em dataValuesGroup, transforma bytes novamente em
// strings, normaliza o dado e armazena no objeto
let data = dataValuesGroup.slice(i, j);
const utf8decoder = new TextDecoder();
data = utf8decoder.decode(data);
data = normalizeData(data);
dataField.data = data;
// remove propriedades agora desnecessárias
delete dataField.len;
delete dataField.initPos;
// obtém os campos de controle
if (dataField.tag === '006') {
controlField006 = dataField.data.replace(/[|#$]/g, ' ');
}
if (dataField.tag === '007') {
controlField007 = dataField.data.replace(/[|#$\r]/g, ' ');
}
if (dataField.tag === '008') {
controlField008 = dataField.data.replace(/[|#$-]/g, ' ');
}
}
}
function marcTagsRecordHandler(marcTags) {
// MARC tags handler
let record = marcTags;
record
.replace(/\t\s/, ' ') // LC bib handling (\t+\s)
.replace(/\t/, ' ') // LC aut handling (\t)
// eslint-disable-next-line no-irregular-whitespace
.replace(/ /, ' ') // Pergamum handling (control fields) (non-breaking space)
// eslint-disable-next-line no-irregular-whitespace
.replace(/ /, ' '); // Pergamum handling (data fields) (non-breaking space)
record += '\n';
const pattern1 = /^FMT/;
const pattern2 = /^LDR/;
if (pattern1.test(record) || pattern2.test(record)) {
// Aleph record handling
record = record
.replace(/\t/g, ' ') // 1
.replace(/^(FMT\s[A-Z].*?\n)/, '') // 2
.replace(/LDR\s([0\s-]{4}.*?\n)/, '000 $1') // 3
.replace(/\n(\d{3})\s(\|.\s)/g, '\n$1 $2') // 4
.replace(/\n(\d{3})(\d)/g, '\n$1 $2') // 5
.replace(/\n(\d{3}\s\d\s)/g, '\n$1 '); // 6 (manter essa ordem)
}
// configuração das variáveis básicas do registro
// obtém o líder
leader = record.match(/^000\s([\d\s-]{4}.*?)\n/);
if (leader) {
leader = leader[1];
leader = leader.replace(/[|#$-]/g, ' ');
}
// obtém os campos de controle
controlField003 = record.match(/\n003\s(.*?)\n/);
if (controlField003) {
controlField003 = controlField003[1];
}
controlField006 = record.match(/\n006\s([a-z].*?)\n/) || '';
if (controlField006) {
controlField006 = controlField006[1];
controlField006 = controlField006.replace(/[|#$-]/g, ' ');
}
controlField007 = record.match(/\n007\s([a-z].*?)\n/) || '';
if (controlField007) {
controlField007 = controlField007[1];
controlField007 = controlField007.replace(/[|#$\r-]/g, ' ');
}
controlField008 = record.match(/008\s([\d\s]{5}.*?)\n/) || '';
if (controlField008) {
controlField008 = controlField008[1];
controlField008 = controlField008.replace(/[|#$-]/g, ' ');
}
// para cada linha do registro, identifica o campo e seu conteúdo
// (o conteúdo inclui os indicadores)
const fields = record.split('\n');
// seleciona somente os campos desejados (campos 9XX e AAA são descartados)
const pattern3 = /^([0-8]\d\d)\s([0-9_\s][0-9_\s]\s[$|].*)/;
const selectedFields = fields.filter((value) => value.match(pattern3));
for (const field of selectedFields) {
// para cada campo...
const match = field.match(pattern3);
// identifica a tag
const tagMatch = match[1];
// e o dado da tag
let dataMatch = match[2];
// transforma o dado para o formato igual ao manipulado pelo
// MARC ISO 2709 record handler
dataMatch = dataMatch.replace(/\s?\|(.)\s/g, ' $$$1 ');
// normaliza o dado
dataMatch = normalizeData(dataMatch);
// cria o objeto com o dado...
dataField = {
tag: tagMatch,
data: dataMatch,
};
// e insere cada objeto dataField na array dataFields
dataFields.push(dataField);
}
}
// para registros de autoridade da BN, insere "CA-BN ANO"
function sourceDataFoundBn() {
if (
leader.charAt(6) === 'z' &&
(controlField003 === 'Br' || controlField003 === 'BR-RjBN')
) {
// cria um objeto com o dado...
dataField = {
tag: '670',
data: `## $a CA-BN ${new Date().getFullYear()}`,
};
// e insere o objeto dataField na array dataFields
dataFields.push(dataField);
}
}
// para registros de autoridade da LC, insere "CA-LC ANO"
function sourceDataFoundLc() {
if (leader.charAt(6) === 'z' && dataField040.match('a DLC')) {
// cria um objeto com os dados...
dataField = {
tag: '670',
data: `## $a CA-LC ${new Date().getFullYear()}`,
};
// e insere o objeto dataField na array dataFields
dataFields.push(dataField);
}
}
function unifiedHandler() {
// eslint-disable-next-line no-shadow
for (const [index, dataField] of dataFields.entries()) {
// verifica se a tag é a 040
if (dataField.tag === '040') {
// se for, adiciona o subcampo para a agência modificadora do registro
// e marca como verdadeiro a presença deste campo
dataFields[index].data = `${dataFields[index].data} $d BR-FlWIK`;
dataField040 = dataField.data;
}
// verifica se a tag é uma das seguintes
if (dataField.tag === '092' || dataField.tag === '595') {
// se for, exclua da tabela dataFields
dataFields.splice(index, 1);
}
// TODO: fazer função para deletar campos
}
// se não há campo 040, então será criado agora
if (!dataField040) {
// cria um objeto com o dado...
dataField = {
tag: '040',
data: '## $a BR-FlWIK $b por $c BR-FlWIK',
};
// e insere o objeto dataField na array dataFields
dataFields.push(dataField);
}
// ordena os campos por tags
dataFields.sort((a, b) => a.tag - b.tag);
// adiciona a fonte positiva do dado
sourceDataFoundBn();
sourceDataFoundLc();
// eslint-disable-next-line no-shadow
for (const [index, dataField] of dataFields.entries()) {
// não pode haver índice 0 na query string para o formulário de edição
const i = index + 1;
// se os campos forem maior que 009, então gerarão indicadores
if (dataField.tag > 9) {
dataField.ind1 = dataField.data.charAt(0).replace(/[ _]/, '#');
dataField.ind2 = dataField.data.charAt(1).replace(/[ _]/, '#');
dataField.data = dataField.data.substring(3);
dataField.data = dataField.data.normalize('NFC');
dataField.order = index;
}
// se, também, os campos estiverem entre 010 e 830 (com exceção para 856),
// criará a query string do link para o formulário e a sintaxe da
// Predefinição Field
if (
parseInt(dataField.tag, 10) === 10 ||
(parseInt(dataField.tag, 10) > 12 &&
parseInt(dataField.tag, 10) < 831) ||
parseInt(dataField.tag, 10) === 856
) {
// query string, Predefinição Field
fieldQueryString +=
`&Field[${i}][tag]=${dataField.tag}` +
`&Field[${i}][ind1]=${dataField.ind1}` +
`&Field[${i}][ind2]=${dataField.ind2}` +
`&Field[${i}][data]=${dataField.data}`;
// template string, Predefinição Field
fieldTemplates +=
'{{Field\n' +
`|tag=${dataField.tag}\n` +
`|ind1=${dataField.ind1}\n` +
`|ind2=${dataField.ind2}\n` +
`|data=${dataField.data}\n` +
'}}\n';
}
}
}
// function to transform a 3rd level object to URL query strings
const makeUrlParams = (obj, recType) => {
if (recType === 'Registro bibliográfico') {
url = new URL(
`${window.location.origin}/wiki/Special:FormEdit/BibRecord`,
);
} else {
url = new URL(
`${window.location.origin}/wiki/Special:FormEdit/AutRecord`,
);
}
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object') {
for (const [key2, value2] of Object.entries(value)) {
if (typeof value2 === 'object') {
for (const [key3, value3] of Object.entries(value2)) {
url.searchParams.set(`${key}[${key2}][${key3}]`, value3);
}
} else {
url.searchParams.set(`${key}[${key2}]`, value2);
}
}
} else {
url.searchParams.set(key, value);
}
}
return url;
};
// function to transform a 2nd level object to Template params
const makeTemplateParams = (obj) => {
templates = `{{${Object.keys(obj)[0]}\n`;
for (const [, value] of Object.entries(obj)) {
if (typeof value === 'object') {
for (const [key2, value2] of Object.entries(value)) {
templates += `|${key2}=${value2}\n`;
}
}
}
templates += `}}\n${fieldTemplates}{{EndOfRecord}}`;
return templates;
};
function setBibVars() {
// inicializa as variáveis derivadas (registro bibliográfico)
// líder
const recordStatus = leader.charAt(5);
const typeOfRecord = leader.charAt(6);
const bibliographicLevel = leader.charAt(7);
let encodingLevel = leader.charAt(17);
if (encodingLevel === ' ') encodingLevel = '';
let descriptiveCatalogingForm = leader.charAt(18);
if (descriptiveCatalogingForm === ' ') descriptiveCatalogingForm = '';
let multipartResourceRecordLevel = leader.charAt(19);
if (multipartResourceRecordLevel === ' ') multipartResourceRecordLevel = '';
// control field 008
const dateEnteredOnFile = controlField008.substring(0, 6);
const typeOfDate = controlField008.charAt(6);
const date1 = controlField008.substring(7, 11);
const date2 = controlField008.substring(11, 15);
const placeOfPublication = controlField008.substring(15, 18);
let illustrations = controlField008.charAt(18);
if (illustrations === ' ') illustrations = '';
let targetAudience = controlField008.charAt(22);
if (targetAudience === ' ') targetAudience = '';
let formOfItem = controlField008.charAt(23);
if (formOfItem === ' ') formOfItem = 'r';
let natureOfContents = controlField008.charAt(24);
if (natureOfContents === ' ') natureOfContents = '';
let governmentPublication = controlField008.charAt(28);
if (governmentPublication === ' ' || governmentPublication === '0') {
governmentPublication = '';
}
let conferencePublication = controlField008.charAt(29);
if (conferencePublication === '0') conferencePublication = '';
let festschrift = controlField008.charAt(30);
if (festschrift === '0') festschrift = '';
let index = controlField008.charAt(31);
if (index === '0' || index === ' ') index = '';
let literaryForm = controlField008.charAt(33);
if (literaryForm === '0' || literaryForm === ' ') literaryForm = '';
let biography = controlField008.charAt(34);
if (biography === ' ') biography = '';
const language = controlField008.substring(35, 38);
let modifiedRecord = controlField008.charAt(38);
if (modifiedRecord === ' ' || modifiedRecord.match('\r'))
modifiedRecord = '';
let catalogingSource = controlField008.charAt(39);
if (catalogingSource === ' ') catalogingSource = '';
bibRecordParams = {
BibRecord: {
dateEnteredOnFile,
recordStatus,
typeOfRecord,
bibliographicLevel,
encodingLevel,
descriptiveCatalogingForm,
multipartResourceRecordLevel,
controlField006,
controlField007,
typeOfDate,
date1,
date2,
placeOfPublication,
illustrations,
targetAudience,
formOfItem,
natureOfContents,
governmentPublication,
conferencePublication,
festschrift,
index,
literaryForm,
biography,
language,
modifiedRecord,
catalogingSource,
},
};
}
function setAutVars() {
// inicializa as variáveis derivadas (registro de autoridade)
// líder
const recordStatus = leader.charAt(5);
let encodingLevel = leader.charAt(17);
if (encodingLevel === ' ') encodingLevel = '';
let punctuationPolicy = leader.charAt(18);
if (punctuationPolicy === ' ' || punctuationPolicy === '4')
punctuationPolicy = '';
// control field 008
const dateEnteredOnFile = controlField008.substring(0, 6);
let directOrIndirectGeogSubdiv = controlField008.charAt(6);
if (directOrIndirectGeogSubdiv === ' ') directOrIndirectGeogSubdiv = '';
let romanizationScheme = controlField008.charAt(7);
if (romanizationScheme === ' ') romanizationScheme = '';
let languageOfCatalog = controlField008.charAt(8);
if (languageOfCatalog === ' ') languageOfCatalog = '';
const kindOfRecord = controlField008.charAt(9);
const descriptiveCatalogingRules = controlField008.charAt(10);
const subjectHeadingSystem = controlField008.charAt(11);
const typeOfSeries = controlField008.charAt(12);
let numberedOrUnnumberedSeries = controlField008.charAt(13);
if (numberedOrUnnumberedSeries === ' ') numberedOrUnnumberedSeries = '';
const headingUseMainOrAddedEntry = controlField008.charAt(14);
let headingUseSubjectAddedEntry = controlField008.charAt(15);
if (headingUseSubjectAddedEntry === ' ') headingUseSubjectAddedEntry = '';
let headingUseSeriesAddedEntry = controlField008.charAt(16);
if (headingUseSeriesAddedEntry === ' ') headingUseSeriesAddedEntry = '';
let typeOfSubjectSubdivision = controlField008.charAt(17);
if (typeOfSubjectSubdivision === ' ') typeOfSubjectSubdivision = '';
let typeOfGovernmentAgency = controlField008.charAt(28);
if (typeOfGovernmentAgency === ' ') typeOfGovernmentAgency = '';
let referenceEvaluation = controlField008.charAt(29);
if (referenceEvaluation === ' ') referenceEvaluation = '';
const recordUpdateInProcess = controlField008.charAt(31);
let undifferentiatedPersonalName = controlField008.charAt(32);
if (undifferentiatedPersonalName === ' ') undifferentiatedPersonalName = '';
let levelOfEstablishment = controlField008.charAt(33);
if (levelOfEstablishment === ' ') levelOfEstablishment = '';
let modifiedRecord = controlField008.charAt(38);
if (modifiedRecord === ' ' || modifiedRecord.match('\r'))
modifiedRecord = '';
let catalogingSource = controlField008.charAt(39);
if (catalogingSource === ' ') catalogingSource = '';
autRecordParams = {
AutRecord: {
dateEnteredOnFile,
recordStatus,
encodingLevel,
punctuationPolicy,
directOrIndirectGeogSubdiv,
romanizationScheme,
languageOfCatalog,
kindOfRecord,
descriptiveCatalogingRules,
subjectHeadingSystem,
typeOfSeries,
numberedOrUnnumberedSeries,
headingUseMainOrAddedEntry,
headingUseSubjectAddedEntry,
headingUseSeriesAddedEntry,
typeOfSubjectSubdivision,
typeOfGovernmentAgency,
referenceEvaluation,
recordUpdateInProcess,
undifferentiatedPersonalName,
levelOfEstablishment,
modifiedRecord,
catalogingSource,
},
};
}
// lê o registro, lida com o HTML
// lê o arquivo de upload
const input = document.body.querySelector('textarea');
const uploadedFile = document.querySelector('.upload');
uploadedFile.addEventListener('change', () => {
const reader = new FileReader();
reader.addEventListener('load', () => {
input.value = reader.result;
});
reader.readAsText(uploadedFile.files[0]);
});
const readRecordBtn = document.body.querySelector('#readRecord');
readRecordBtn.addEventListener('click', () => {
// zera os valores, caso o botão seja clicado mais de uma vez
dataFields = [];
fieldQueryString = '';
fieldTemplates = '';
const record = input.value;
if (record) {
const initialIsoPattern = /^\d\d\d\d/;
if (initialIsoPattern.test(record)) {
isoRecordHandler(record);
} else {
marcTagsRecordHandler(record);
}
unifiedHandler();
// define se é "Registro bibliográfico" ou se é "Registro de autoridade"
const recordType = document.body.querySelector('input:checked').value;
if (recordType === 'Registro bibliográfico') {
setBibVars();
url = makeUrlParams(bibRecordParams, recordType);
templates = makeTemplateParams(bibRecordParams);
} else {
setAutVars();
url = makeUrlParams(autRecordParams, recordType);
templates = makeTemplateParams(autRecordParams);
}
// cria o link para a importação do registro
url.search += `${fieldQueryString}`;
const importClass = document.body.querySelector('.importLink');
importClass.innerHTML = '';
const createRecordLink = document.createElement('a');
createRecordLink.setAttribute('href', url.href);
createRecordLink.setAttribute('target', '_blank');
createRecordLink.innerHTML = '> <b>Importar registro</b>';
importClass.appendChild(createRecordLink);
// cria div para pré-visualização dos dados do registro
const templatePreview = document.body.querySelector('.templatePreview');
templatePreview.innerHTML = '';
const h2 = document.createElement('h2');
h2.innerText = 'Pré-visualização do registro';
templatePreview.appendChild(h2);
const preTag = document.createElement('pre');
preTag.innerText = templates;
templatePreview.appendChild(preTag);
} else {
alert('É necessário colar um registro MARC na caixa de texto');
}
});
}
if (document.readyState === 'complete' || document.readyState !== 'loading') {
mainFunc();
} else {
document.addEventListener('DOMContentLoaded', mainFunc);
}