316 lines
7.5 KiB
JavaScript
316 lines
7.5 KiB
JavaScript
|
import { util } from '@citation-js/core';
|
||
|
import config from '../config.js';
|
||
|
const stopWords = new Set(['the', 'a', 'an']);
|
||
|
const unsafeChars = /(?:<\/?.*?>|[\u0020-\u002F\u003A-\u0040\u005B-\u005E\u0060\u007B-\u007F])+/g;
|
||
|
const unicode = /[^\u0020-\u007F]+/g;
|
||
|
function firstWord(text) {
|
||
|
if (!text) {
|
||
|
return '';
|
||
|
} else {
|
||
|
return text.normalize('NFKD').replace(unicode, '').split(unsafeChars).find(word => word.length && !stopWords.has(word.toLowerCase()));
|
||
|
}
|
||
|
}
|
||
|
const name = new util.Translator([{
|
||
|
source: 'given',
|
||
|
target: 'given'
|
||
|
}, {
|
||
|
source: 'family',
|
||
|
target: 'family'
|
||
|
}, {
|
||
|
source: 'suffix',
|
||
|
target: 'suffix'
|
||
|
}, {
|
||
|
source: 'prefix',
|
||
|
target: 'non-dropping-particle'
|
||
|
}, {
|
||
|
source: 'family',
|
||
|
target: 'literal',
|
||
|
when: {
|
||
|
source: false,
|
||
|
target: {
|
||
|
family: false,
|
||
|
given: false
|
||
|
}
|
||
|
}
|
||
|
}]);
|
||
|
const months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
|
||
|
export const TYPE = 'BibTeX type';
|
||
|
export const LABEL = 'BibTeX label';
|
||
|
export const MONTHS = {
|
||
|
jan: 1,
|
||
|
feb: 2,
|
||
|
mar: 3,
|
||
|
apr: 4,
|
||
|
may: 5,
|
||
|
jun: 6,
|
||
|
jul: 7,
|
||
|
aug: 8,
|
||
|
sep: 9,
|
||
|
oct: 10,
|
||
|
nov: 11,
|
||
|
dec: 12,
|
||
|
january: 1,
|
||
|
february: 2,
|
||
|
march: 3,
|
||
|
april: 4,
|
||
|
may: 5,
|
||
|
june: 6,
|
||
|
july: 7,
|
||
|
august: 8,
|
||
|
september: 9,
|
||
|
october: 10,
|
||
|
november: 11,
|
||
|
december: 12
|
||
|
};
|
||
|
export const TYPE_KEYS = {
|
||
|
bathesis: 'Bachelor\'s thesis',
|
||
|
mathesis: 'Master\'s thesis',
|
||
|
phdthesis: 'PhD thesis',
|
||
|
candthesis: 'Candidate thesis',
|
||
|
techreport: 'technical report',
|
||
|
resreport: 'research report',
|
||
|
software: 'computer software',
|
||
|
datacd: 'data cd',
|
||
|
audiocd: 'audio cd',
|
||
|
patent: 'patent',
|
||
|
patentde: 'German patent',
|
||
|
patenteu: 'European patent',
|
||
|
patentfr: 'French patent',
|
||
|
patentuk: 'British patent',
|
||
|
patentus: 'U.S. patent',
|
||
|
patreq: 'patent request',
|
||
|
patreqde: 'German patent request',
|
||
|
patreqeu: 'European patent request',
|
||
|
patreqfr: 'French patent request',
|
||
|
patrequk: 'British patent request',
|
||
|
patrequs: 'U.S. patent request'
|
||
|
};
|
||
|
export const STANDARD_NUMBERS_PATTERN = /(^(?:ISAN )?(?:[0-9a-f]{4}-){4}[0-9a-z](?:-(?:[0-9a-f]{4}-){2}[0-9a-z])?$)|(^(?:979-?0-?|M-?)(?:\d{9}|(?=[\d-]{11}$)\d+-\d+-\d)$)|(^ISRN .{1,36}$)|(^(?:ISWC )?T-?\d{9}-?\d$)/i;
|
||
|
export function parseDate(date) {
|
||
|
const parts = date.split('T')[0].replace(/[?~%]$/, '').split('-');
|
||
|
const year = +parts[0].replace(/^Y(?=-?\d{4}\d+)/, '').replace(/X/g, '0');
|
||
|
const month = +parts[1];
|
||
|
const day = +parts[2];
|
||
|
if (!month || month > 20) {
|
||
|
return [year];
|
||
|
} else if (!day) {
|
||
|
return [year, month];
|
||
|
} else {
|
||
|
return [year, month, day];
|
||
|
}
|
||
|
}
|
||
|
export function parseMonth(value) {
|
||
|
if (value == null) {
|
||
|
return [];
|
||
|
}
|
||
|
if (+value) {
|
||
|
return [parseInt(value, 10)];
|
||
|
}
|
||
|
value = value.trim().toLowerCase();
|
||
|
if (value in MONTHS) {
|
||
|
return [MONTHS[value]];
|
||
|
}
|
||
|
const parts = value.split(/\s+/);
|
||
|
let month;
|
||
|
let day;
|
||
|
if (parts[0] in MONTHS) {
|
||
|
month = MONTHS[parts[0]];
|
||
|
day = parseInt(parts[1]);
|
||
|
} else if (parts[1] in MONTHS) {
|
||
|
month = MONTHS[parts[1]];
|
||
|
day = parseInt(parts[0]);
|
||
|
}
|
||
|
return day ? [month, day] : month ? [month] : [];
|
||
|
}
|
||
|
export function formatLabel(author, issued, suffix, title) {
|
||
|
let label = '';
|
||
|
if (author && author[0]) {
|
||
|
label += firstWord(author[0].family || author[0].literal);
|
||
|
}
|
||
|
if (issued && issued['date-parts'] && issued['date-parts'][0]) {
|
||
|
label += issued['date-parts'][0][0];
|
||
|
}
|
||
|
if (suffix) {
|
||
|
label += suffix;
|
||
|
} else if (title) {
|
||
|
label += firstWord(title);
|
||
|
}
|
||
|
return label;
|
||
|
}
|
||
|
export const Converters = {
|
||
|
PICK: {
|
||
|
toTarget(...args) {
|
||
|
return args.find(Boolean);
|
||
|
},
|
||
|
toSource(value) {
|
||
|
return [value];
|
||
|
}
|
||
|
},
|
||
|
DATE: {
|
||
|
toTarget(date) {
|
||
|
const parts = date.split('/').map(part => part && part !== '..' ? parseDate(part) : undefined);
|
||
|
return isNaN(parts[0][0]) ? {
|
||
|
literal: date
|
||
|
} : {
|
||
|
'date-parts': parts
|
||
|
};
|
||
|
},
|
||
|
toSource(date) {
|
||
|
if ('date-parts' in date) {
|
||
|
return date['date-parts'].map(datePart => datePart.map(datePart => datePart.toString().padStart(2, '0')).join('-')).join('/');
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
YEAR_MONTH: {
|
||
|
toTarget(year, month, day) {
|
||
|
if (isNaN(+year)) {
|
||
|
return {
|
||
|
literal: year
|
||
|
};
|
||
|
} else if (!isNaN(+day) && !isNaN(+month)) {
|
||
|
return {
|
||
|
'date-parts': [[+year, +month, +day]]
|
||
|
};
|
||
|
} else {
|
||
|
return {
|
||
|
'date-parts': [[+year, ...parseMonth(month)]]
|
||
|
};
|
||
|
}
|
||
|
},
|
||
|
toSource(date) {
|
||
|
if ('date-parts' in date) {
|
||
|
const [year, month, day] = date['date-parts'][0];
|
||
|
return [year.toString(), month ? day ? `${months[month - 1]} ${day}` : month : undefined];
|
||
|
} else {
|
||
|
return [];
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
EPRINT: {
|
||
|
toTarget(id, type) {
|
||
|
if (type === 'pubmed') {
|
||
|
return id;
|
||
|
}
|
||
|
},
|
||
|
toSource(id) {
|
||
|
return [id, 'pubmed'];
|
||
|
}
|
||
|
},
|
||
|
EVENT_TITLE: {
|
||
|
toTarget(title, addon) {
|
||
|
if (addon) {
|
||
|
title += ' (' + addon + ')';
|
||
|
}
|
||
|
return title;
|
||
|
},
|
||
|
toSource(title) {
|
||
|
return title.match(/^(.+)(?: \((.+)\))?$/).slice(1, 3);
|
||
|
}
|
||
|
},
|
||
|
HOW_PUBLISHED: {
|
||
|
toTarget(howPublished) {
|
||
|
if (howPublished.startsWith('http')) {
|
||
|
return howPublished;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
KEYWORDS: {
|
||
|
toTarget(list) {
|
||
|
return list.join(',');
|
||
|
},
|
||
|
toSource(list) {
|
||
|
return list.split(',');
|
||
|
}
|
||
|
},
|
||
|
LABEL: {
|
||
|
toTarget(label) {
|
||
|
return [label, label];
|
||
|
},
|
||
|
toSource(id, label, author, issued, suffix, title) {
|
||
|
let safeId;
|
||
|
if (id === null) {
|
||
|
safeId = 'null';
|
||
|
} else if (id === undefined) {
|
||
|
safeId = 'undefined';
|
||
|
} else {
|
||
|
safeId = id.toString().replace(unsafeChars, '');
|
||
|
}
|
||
|
if (config.format.useIdAsLabel) {
|
||
|
return safeId;
|
||
|
}
|
||
|
if (label && !unsafeChars.test(label)) {
|
||
|
return label;
|
||
|
} else {
|
||
|
return formatLabel(author, issued, suffix, title) || safeId;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
NAMES: {
|
||
|
toTarget(list) {
|
||
|
return list.map(name.convertToTarget);
|
||
|
},
|
||
|
toSource(list) {
|
||
|
return list.map(name.convertToSource);
|
||
|
}
|
||
|
},
|
||
|
NAMES_ORCID: {
|
||
|
toTarget(list, orcid) {
|
||
|
return list.map((inputName, i) => {
|
||
|
var _orcid$item;
|
||
|
const outputName = name.convertToTarget(inputName);
|
||
|
if (typeof (orcid === null || orcid === void 0 || (_orcid$item = orcid.item) === null || _orcid$item === void 0 ? void 0 : _orcid$item[i]) === 'string') {
|
||
|
outputName._orcid = orcid.item[i];
|
||
|
}
|
||
|
return outputName;
|
||
|
});
|
||
|
},
|
||
|
toSource(list) {
|
||
|
const names = [];
|
||
|
const orcid = [];
|
||
|
for (let i = 0; i < list.length; i++) {
|
||
|
names.push(name.convertToSource(list[i]));
|
||
|
if (list[i]._orcid) {
|
||
|
orcid[i] = list[i]._orcid;
|
||
|
}
|
||
|
}
|
||
|
return [names, orcid.length ? {
|
||
|
item: orcid
|
||
|
} : undefined];
|
||
|
}
|
||
|
},
|
||
|
PAGES: {
|
||
|
toTarget(pages) {
|
||
|
return pages.replace(/[–—]/, '-');
|
||
|
},
|
||
|
toSource(pages) {
|
||
|
return pages.replace('-', '--');
|
||
|
}
|
||
|
},
|
||
|
STANDARD_NUMBERS: {
|
||
|
toTarget(...args) {
|
||
|
return args.find(Boolean);
|
||
|
},
|
||
|
toSource(number) {
|
||
|
const match = number.toString().match(STANDARD_NUMBERS_PATTERN);
|
||
|
return match ? match.slice(1, 5) : [];
|
||
|
}
|
||
|
},
|
||
|
STATUS: {
|
||
|
toSource(state) {
|
||
|
if (/^(inpreparation|submitted|forthcoming|inpress|prepublished)$/i.test(state)) {
|
||
|
return state;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
TITLE: {
|
||
|
toTarget(title, subtitle, addon) {
|
||
|
if (subtitle) {
|
||
|
title += ': ' + subtitle;
|
||
|
}
|
||
|
return title;
|
||
|
},
|
||
|
toSource(title) {
|
||
|
return [title];
|
||
|
}
|
||
|
}
|
||
|
};
|