// ==UserScript==
// @name Semantic Scholar Enhanced Citation Button with BibTeX from Page
// @namespace http://tampermonkey.net/
// @version 0.9
// @description Add a styled button to Semantic Scholar cited and citing papers that fetches BibTeX and copies to clipboard
// @author Posty
// @match https://www.semanticscholar.org/paper/*
// @grant GM_xmlhttpRequest
// @connect www.semanticscholar.org
// @license GPL-3.0 License
// ==/UserScript==
(function() {
'use strict';
// 调试日志函数(只保留error)
const debug = {
error: (...args) => console.error('[CitationButton]', ...args)
};
// 检查元素是否存在
function checkElement(selector, context = document) {
const element = context.querySelector(selector);
if (!element) {
debug.error(`Element not found with selector: ${selector}`);
}
return element;
}
// 获取 BibTeX 的函数,通过抓取网页
function fetchBibTeX(fullUrl) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: fullUrl,
onload: function(response) {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(response.responseText, 'text/html');
const bibtexElement = doc.querySelector('pre.bibtex-citation[data-nosnippet="true"]');
if (bibtexElement) {
const bibtex = bibtexElement.textContent.trim();
resolve(bibtex);
} else {
reject(new Error('BibTeX element not found in page'));
}
} catch (error) {
reject(new Error('Failed to parse page HTML: ' + error.message));
}
},
onerror: function(error) {
reject(new Error('Page request failed: ' + error));
}
});
});
}
function showCopySuccess() {
const notification = document.createElement('div');
notification.textContent = '复制成功';
Object.assign(notification.style, {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: 'rgba(173, 216, 230, 0.9)',
padding: '10px 20px',
borderRadius: '5px',
boxShadow: '0 0 10px rgba(0,0,0,0.2)',
fontSize: '16px',
color: '#000',
zIndex: '9999'
});
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 1000);
}
// 创建自定义按钮
function createCustomButton(paperId, fullUrl) {
try {
const button = document.createElement('button');
button.textContent = 'Get BibTeX';
button.dataset.paperId = paperId;
Object.assign(button.style, {
marginLeft: '10px',
padding: '6px 12px',
border: 'none',
borderRadius: '4px',
backgroundColor: '#007bff', // 蓝色背景
color: '#ffffff', // 白色文字
cursor: 'pointer',
fontSize: '12px',
fontWeight: '500',
textTransform: 'uppercase',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
transition: 'background-color 0.2s'
});
// 鼠标悬停效果
button.addEventListener('mouseover', () => {
button.style.backgroundColor = '#0056b3'; // 深蓝色
});
button.addEventListener('mouseout', () => {
button.style.backgroundColor = '#007bff'; // 恢复原色
});
// 点击事件:获取 BibTeX 并复制到剪贴板
button.addEventListener('click', () => {
fetchBibTeX(fullUrl)
.then(bibtex => {
navigator.clipboard.writeText(bibtex)
.then(() => {
showCopySuccess();
})
.catch(err => {
debug.error('Failed to copy to clipboard:', err);
});
})
.catch(error => {
debug.error('Error fetching BibTeX:', error);
});
});
return button;
} catch (error) {
debug.error('Failed to create button:', error);
return null;
}
}
// 处理引文条目的通用函数
function processCitationEntries(container, listSelector, entrySelector, sectionName) {
try {
const citationList = checkElement(listSelector, container);
if (!citationList) {
debug.error(`${sectionName} citation list container not found`);
return;
}
const citationEntries = citationList.querySelectorAll(entrySelector);
if (citationEntries.length === 0) {
return;
}
citationEntries.forEach((entry, index) => {
try {
const paperId = entry.dataset.paperId || 'unknown';
let fullUrl = '';
const linkElement = checkElement('a.link-button--show-visited', entry);
if (linkElement) {
const href = linkElement.getAttribute('href');
fullUrl = `https://www.semanticscholar.org${href}`;
} else {
debug.error(`Link element not found in ${sectionName} entry ${index + 1}`);
return;
}
const controlContainer = checkElement('div.cl-paper__bulleted-row.cl-paper-controls', entry);
let targetContainer;
if (controlContainer) {
targetContainer = controlContainer;
} else {
targetContainer = checkElement('.cl-paper__bulleted-row', entry);
if (!targetContainer) {
debug.error(`No suitable container found in ${sectionName} entry ${index + 1}`);
return;
}
}
if (targetContainer.querySelector('.custom-citation-button')) {
return;
}
const button = createCustomButton(paperId, fullUrl);
if (button) {
button.className = 'custom-citation-button';
targetContainer.appendChild(button);
}
} catch (error) {
debug.error(`Error processing ${sectionName} entry ${index + 1}:`, error);
}
});
} catch (error) {
debug.error(`Failed to process ${sectionName} entries:`, error);
}
}
// 主函数
function initializeButtons() {
try {
// 处理 Cited Papers
const citedContainer = checkElement('#cited-papers');
if (citedContainer) {
processCitationEntries(
citedContainer,
'#cited-papers > div.card-content > div > div.citation-list__citations',
'.cl-paper-row.citation-list__paper-row',
'Cited Papers'
);
}
// 处理 Citing Papers
const citingContainer = checkElement('#citing-papers');
if (citingContainer) {
processCitationEntries(
citingContainer,
'#citing-papers > div.card-content > div',
'.cl-paper-row.citation-list__paper-row',
'Citing Papers'
);
}
} catch (error) {
debug.error('Initialization failed:', error);
}
}
// 页面加载完成后执行
function waitForPageLoad() {
if (document.readyState === 'complete') {
initializeButtons();
} else {
window.addEventListener('load', () => {
initializeButtons();
});
}
}
// 处理动态加载内容
function observeChanges() {
const observer = new MutationObserver(() => {
initializeButtons();
});
const citedTarget = checkElement('#cited-papers');
if (citedTarget) {
observer.observe(citedTarget, { childList: true, subtree: true });
}
const citingTarget = checkElement('#citing-papers');
if (citingTarget) {
observer.observe(citingTarget, { childList: true, subtree: true });
}
}
try {
waitForPageLoad();
observeChanges();
} catch (error) {
debug.error('Script startup failed:', error);
}
})();