// ==UserScript==
// @name DOI, SCImago Journal Rank and JIF for Google Scholar
// @namespace https://greasyfork.org/
// @version 1.6
// @description Show DOI, journal name, ISSN, SJR, H-Index, and JIF on Google Scholar search results
// @author Bui Quoc Dung
// @match https://scholar.google.com/*
// @grant GM_xmlhttpRequest
// @connect api.crossref.org
// @connect www.scimagojr.com
// @connect wos-journal.info
// @connect *
// @require https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==
(function () {
'use strict';
const CROSSREF_API = 'https://api.crossref.org/works';
const SJR_SEARCH_URL = 'https://www.scimagojr.com/journalsearch.php?q=';
const SJR_BASE_URL = 'https://www.scimagojr.com/';
const WOS_JOURNAL_URL = 'https://wos-journal.info/?jsearch=';
const DOI_REGEX = /\b(10\.\d{4,}(?:\.\d+)*\/(?:(?!["&'<>])\S)+)\b/gi;
const MAX_SINGLE_LINE_LENGTH = 80;
const ICONS = {
CrossRef: 'https://www.crossref.org/favicon.ico',
SJR: 'https://www.scimagojr.com/favicon.ico',
Clarivate: 'https://www.google.com/s2/favicons?sz=64&domain=clarivate.com.cn',
Journal: 'https://www.crossref.org/favicon.ico'
};
function createFaviconIcon(url, alt) {
const img = document.createElement('img');
img.src = url;
img.alt = alt;
img.title = alt;
img.style.width = '16px';
img.style.height = '16px';
img.style.verticalAlign = 'text-bottom';
img.style.marginRight = '4px';
return img;
}
function getFaviconFromUrl(url) {
try {
const u = new URL(url);
return `https://www.google.com/s2/favicons?sz=64&domain=${u.hostname}`;
} catch {
return null;
}
}
function shouldBreakIntoTwoLines(doi1, doi2) {
const totalLength = (doi1 ? doi1.length : 0) + (doi2 ? doi2.length : 0);
return totalLength > MAX_SINGLE_LINE_LENGTH;
}
function fetchDOI(titleLink, callback) {
if (!titleLink) return callback(null, null);
GM_xmlhttpRequest({
method: 'GET',
url: titleLink.href,
onload: res => {
const matches = res.responseText.match(DOI_REGEX);
const doi1 = matches?.[0]?.replace(/\/(full|pdf|epdf|abs|abstract)(\/.*)?$/i, '') || null;
const title = encodeURIComponent(titleLink.textContent.trim());
const crossrefUrl = `${CROSSREF_API}?query.title=${title}&rows=1`;
GM_xmlhttpRequest({
method: 'GET',
url: crossrefUrl,
onload: crRes => {
try {
const data = JSON.parse(crRes.responseText);
const doi2 = data.message.items?.[0]?.DOI || null;
callback(doi1, doi2);
} catch {
callback(doi1, null);
}
},
onerror: () => callback(doi1, null)
});
},
onerror: () => callback(null, null)
});
}
function queryCrossRef(title, callback) {
const url = `${CROSSREF_API}?query.title=${encodeURIComponent(title)}&rows=1`;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function (res) {
try {
const data = JSON.parse(res.responseText);
const item = data.message.items?.[0];
if (!item) return callback(null);
const journal = item['container-title']?.[0] || null;
const issn = item.ISSN?.[0] || null;
callback({ journal, issn });
} catch (e) {
callback(null);
}
},
onerror: () => callback(null)
});
}
function querySJRByISSN(issn, callback) {
if (!issn) return callback(null);
const searchUrl = `${SJR_SEARCH_URL}${encodeURIComponent(issn)}`;
GM_xmlhttpRequest({
method: 'GET',
url: searchUrl,
onload: function (res) {
const doc = new DOMParser().parseFromString(res.responseText, "text/html");
const firstLink = doc.querySelector('.search_results a');
if (!firstLink) return callback(null);
const href = firstLink.getAttribute('href');
if (!href) return callback(null);
const journalUrl = SJR_BASE_URL + href;
GM_xmlhttpRequest({
method: 'GET',
url: journalUrl,
onload: function (res2) {
const doc2 = new DOMParser().parseFromString(res2.responseText, "text/html");
const container = doc2.querySelector('div.journalgrid');
if (!container) return callback(null);
const sjrPs = container.querySelectorAll('p.hindexnumber');
let sjrValue = null;
let quartile = null;
let hIndex = null;
if (sjrPs.length >= 2) {
sjrValue = sjrPs[0].childNodes[0]?.textContent.trim();
quartile = sjrPs[0].querySelector('span')?.textContent.trim();
hIndex = sjrPs[1].textContent.trim();
}
let result = '';
if (sjrValue && quartile) result += `SJR ${quartile}; ${sjrValue}`;
else if (sjrValue) result += `SJR ${sjrValue}`;
if (hIndex) result += ` | H-Index: ${hIndex}`;
callback(result || null);
},
onerror: () => callback(null)
});
},
onerror: () => callback(null)
});
}
function queryJIFByISSN(issn, callback) {
if (!issn) {
console.log('queryJIFByISSN: No ISSN provided');
return callback(null);
}
const url = `https://wos-journal.info/?jsearch=${encodeURIComponent(issn)}`;
console.log('queryJIFByISSN: Requesting URL:', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: res => {
console.log('queryJIFByISSN: Response received');
const doc = new DOMParser().parseFromString(res.responseText, "text/html");
const titleElems = doc.querySelectorAll('.title.col-4.col-md-3');
const contentElems = doc.querySelectorAll('.content.col-8.col-md-9');
console.log('queryJIFByISSN: Found title elements count:', titleElems.length);
console.log('queryJIFByISSN: Found content elements count:', contentElems.length);
if (!titleElems.length || !contentElems.length || titleElems.length !== contentElems.length) {
console.log('queryJIFByISSN: Titles and contents count mismatch or missing');
return callback(null);
}
let jifValue = null;
for (let i = 0; i < titleElems.length; i++) {
const titleText = titleElems[i].textContent.trim();
if (titleText === 'Journal Impact Factor (JIF):') {
jifValue = contentElems[i].textContent.trim();
console.log('queryJIFByISSN: Found JIF text:', jifValue);
break;
}
}
if (jifValue && !isNaN(jifValue)) {
callback(jifValue);
} else {
console.log('queryJIFByISSN: JIF not found or invalid number');
callback(null);
}
},
onerror: err => {
console.log('queryJIFByISSN: Request error', err);
callback(null);
}
});
}
function processEntry(entry) {
if (entry.querySelector('.sjr-crossref-info')) return;
const titleLink = entry.querySelector('.gs_rt a');
const titleText = titleLink?.textContent.trim();
if (!titleText) return;
const infoDiv = document.createElement('div');
infoDiv.className = 'sjr-crossref-info';
infoDiv.style.marginTop = '4px';
entry.appendChild(infoDiv);
const line1 = document.createElement('div');
line1.textContent = 'Loading DOI...';
infoDiv.appendChild(line1);
const line2 = document.createElement('div');
line2.textContent = 'Loading Journal...';
infoDiv.appendChild(line2);
const line3 = document.createElement('div');
line3.textContent = 'Loading SJR and JIF...';
infoDiv.appendChild(line3);
fetchDOI(titleLink, (doi1, doi2) => {
const doiHref1 = doi1 ? `https://doi.org/${doi1}` : null;
const doiHref2 = doi2 ? `https://doi.org/${doi2}` : null;
line1.innerHTML = '';
if (doi1 && doi2 && doi1.toLowerCase() !== doi2.toLowerCase()) {
const useTwoLines = shouldBreakIntoTwoLines(doi1, doi2);
if (useTwoLines) {
const line1a = document.createElement('div');
const fav1 = createFaviconIcon(getFaviconFromUrl(titleLink.href), 'Source');
line1a.appendChild(fav1);
line1a.innerHTML += `DOI: <a href="${doiHref1}" target="_blank">${doi1}</a>`;
infoDiv.insertBefore(line1a, line2);
const fav2 = createFaviconIcon(ICONS.CrossRef, 'CrossRef');
line1.appendChild(fav2);
line1.innerHTML += `CrossRef: <a href="${doiHref2}" target="_blank">${doi2}</a>`;
} else {
const fav1 = createFaviconIcon(getFaviconFromUrl(titleLink.href), 'Source');
line1.appendChild(fav1);
line1.innerHTML += `DOI: <a href="${doiHref1}" target="_blank">${doi1}</a> | `;
const fav2 = createFaviconIcon(ICONS.CrossRef, 'CrossRef');
line1.appendChild(fav2);
line1.innerHTML += `CrossRef: <a href="${doiHref2}" target="_blank">${doi2}</a>`;
}
} else if (doi1 || doi2) {
const doi = doi1 || doi2;
const href = `https://doi.org/${doi}`;
const fav = doi1 ? createFaviconIcon(getFaviconFromUrl(titleLink.href), 'Source') : createFaviconIcon(ICONS.CrossRef, 'CrossRef');
line1.appendChild(fav);
line1.innerHTML += `DOI: <a href="${href}" target="_blank">${doi}</a>`;
} else {
line1.textContent = 'DOI: Not found';
}
queryCrossRef(titleText, result => {
if (!result) {
line2.innerHTML = '';
const journalIcon = createFaviconIcon(ICONS.Journal, 'Journal');
line2.appendChild(journalIcon);
line2.appendChild(document.createTextNode('Journal info: Not found'));
line3.innerHTML = '';
const iconSJR = createFaviconIcon(ICONS.SJR, 'SJR');
line3.appendChild(iconSJR);
line3.appendChild(document.createTextNode('SJR info: Not found'));
return;
}
const { journal, issn } = result;
line2.innerHTML = '';
const journalIcon = createFaviconIcon(ICONS.Journal, 'Journal');
line2.appendChild(journalIcon);
if (journal) {
const issnText = issn ? ` (ISSN: ${issn})` : '';
line2.appendChild(document.createTextNode(`${journal}${issnText}`));
} else {
line2.appendChild(document.createTextNode('Journal info: Not found'));
}
querySJRByISSN(issn, sjrInfo => {
line3.innerHTML = '';
const iconSJR = createFaviconIcon(ICONS.SJR, 'SJR');
line3.appendChild(iconSJR);
line3.appendChild(document.createTextNode(sjrInfo || 'SJR info: Not found'));
queryJIFByISSN(issn, jif => {
if (jif) {
const clarivateIcon = createFaviconIcon(ICONS.Clarivate, 'Clarivate');
line3.appendChild(document.createTextNode(' | '));
line3.appendChild(clarivateIcon);
line3.appendChild(document.createTextNode(`JIF: ${jif}`));
}
});
});
});
});
}
const observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (!(node instanceof HTMLElement)) continue;
const entries = node.matches('.gs_ri') ? [node] : node.querySelectorAll?.('.gs_ri') || [];
entries.forEach(entry => processEntry(entry));
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
document.querySelectorAll('.gs_ri').forEach(entry => processEntry(entry));
})();