|
|
|
let allPapers = []; |
|
let filteredPapers = []; |
|
let searchKeywords = []; |
|
let removeSearchKeywords = []; |
|
let currentPage = 1; |
|
let papersPerPage = 10; |
|
let selectedYearMin = 1960; |
|
let selectedYearMax = 2025; |
|
let yearFilterActive = false; |
|
let selectedConferences = []; |
|
let conferenceFilterActive = false; |
|
let conferencesData = {}; |
|
let includeNoAbstract = true; |
|
let abstractFilterActive = false; |
|
let includeNoAuthor = true; |
|
let authorFilterActive = false; |
|
let showOnlyStarred = false; |
|
let starredFilterActive = false; |
|
let starredPapers = []; |
|
|
|
|
|
const multilingualKeywords = [ |
|
'multilingual', 'crosslingual', 'multi lingual', 'cross lingual', |
|
'multi-lingual', 'cross-lingual', 'low-resource language', 'low resource language', |
|
'multi-language', 'multi language', 'cross-language', 'cross language', |
|
'language transfer', |
|
'code-switching', 'code switching', 'language adaptation', |
|
'language pair', 'bilingual', 'trilingual', 'polyglot', |
|
'translation', "nmt", |
|
'transliteration', |
|
'multilingual bert', 'xlm', 'mbert', 'xlm-roberta', |
|
'language identification', 'language detection' |
|
] |
|
|
|
|
|
const languageNames = [ |
|
'english', 'afrikaans', 'albanian', 'amharic', 'arabic', 'armenian', 'azerbaijani', 'basque', 'belarusian', 'bengali', 'bosnian', 'bulgarian', 'catalan', 'cebuano', 'chinese', 'croatian', 'czech', 'danish', 'dutch', 'esperanto', 'estonian', 'filipino', 'finnish', 'french', 'galician', 'georgian', 'german', 'greek', 'gujarati', 'haitian', 'hausa', 'hawaiian', 'hebrew', 'hindi', 'hmong', 'hungarian', 'icelandic', 'igbo', 'indonesian', 'irish', 'italian', 'japanese', 'javanese', 'kannada', 'kazakh', 'khmer', 'korean', 'kurdish', 'kyrgyz', 'lao', 'latin', 'latvian', 'lithuanian', 'luxembourgish', 'macedonian', 'malagasy', 'malay', 'malayalam', 'maltese', 'maori', 'marathi', 'mongolian', 'myanmar', 'nepali', 'norwegian', 'odia', 'pashto', 'persian', 'polish', 'portuguese', 'punjabi', 'romanian', 'russian', 'samoan', 'scots gaelic', 'serbian', 'sesotho', 'shona', 'sindhi', 'sinhala', 'slovak', 'slovenian', 'somali', 'spanish', 'sundanese', 'swahili', 'swedish', 'tagalog', 'tajik', 'tamil', 'telugu', 'thai', 'turkish', 'ukrainian', 'urdu', 'uzbek', 'vietnamese', 'welsh', 'xhosa', 'yiddish', 'yoruba', 'zulu', |
|
|
|
'mandarin', 'cantonese', 'hindi', 'urdu', 'bengali', 'tamil', 'telugu', 'marathi', 'gujarati', 'kannada', 'malayalam', 'punjabi', 'odia', 'assamese', 'maithili', 'sanskrit', 'kashmiri', 'konkani', 'manipuri', 'nepali', 'sindhi', 'dogri', 'bodo', 'santali', 'khasi', 'mizo', 'garo', 'naga', 'tibetan', 'dzongkha', 'sikkimese', 'lepcha', 'limbu', 'tamang', 'gurung', 'magar', 'tharu', 'rajbanshi', 'bhojpuri', 'awadhi', 'chhattisgarhi', 'haryanvi', 'braj', 'bundeli', 'bagheli', 'chhattisgarhi', 'garhwali', 'kumaoni', 'jaunsari', 'kangri', 'mandeali', 'himachali', 'pahari', 'dogri', 'kashmiri', 'ladakhi', 'balti', 'shina', 'burushaski', 'khowar', 'wakhi', 'domaki', 'kohistani', 'torwali', 'palula', 'sawi', 'gawar-bati', 'kalasha', 'dameli', 'phalura', 'savi', 'kundal shahi', 'bateri', 'chilisso', 'gawro', 'kalkoti', 'kashmiri', 'kohistani', 'palula', 'sawi', 'shina', 'torwali', 'wakhi', 'yidgha', 'zangskari', 'balti', 'burushaski', 'domaki', 'khowar', 'shina', 'wakhi', 'domaki', 'kohistani', 'torwali', 'palula', 'sawi', 'gawar-bati', 'kalasha', 'dameli', 'phalura', 'savi', 'kundal shahi', 'bateri', 'chilisso', 'gawro', 'kalkoti', 'kashmiri', 'kohistani', 'palula', 'sawi', 'shina', 'torwali', 'wakhi', 'yidgha', 'zangskari', |
|
'african', 'indian', 'indic', 'asian', "indigenous", |
|
]; |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
initializeApp(); |
|
}); |
|
|
|
async function initializeApp() { |
|
try { |
|
await Promise.all([ |
|
loadBibTeXFile(), |
|
loadConferencesData() |
|
]); |
|
setupEventListeners(); |
|
populateConferenceSelect(); |
|
updateAbstractFilterInfo(); |
|
updateAuthorFilterInfo(); |
|
updateStarredFilterInfo(); |
|
updateSearchKeywordsDisplay(); |
|
updateRemoveKeywordsDisplay(); |
|
updateStarredCount(); |
|
console.log('Initial filter states:', { |
|
includeNoAbstract, |
|
abstractFilterActive, |
|
includeNoAuthor, |
|
authorFilterActive |
|
}); |
|
filterPapers(); |
|
} catch (error) { |
|
console.error('Error initializing app:', error); |
|
showError('Failed to load papers. Please refresh the page.'); |
|
} |
|
} |
|
|
|
async function loadBibTeXFile() { |
|
const loadingIndicator = document.getElementById('loadingIndicator'); |
|
const papersList = document.getElementById('papersList'); |
|
|
|
try { |
|
loadingIndicator.style.display = 'block'; |
|
papersList.style.display = 'none'; |
|
|
|
|
|
let response = await fetch('data/multilingual_papers.bib'); |
|
|
|
|
|
if (response.ok) { |
|
|
|
const bibText = await response.text(); |
|
allPapers = parseBibTeX(bibText); |
|
console.log(`Loaded ${allPapers.length} multilingual papers from multilingual_papers.bib`); |
|
} else { |
|
|
|
console.error('Multilingual papers file not found. Please run the python script to generate the file.'); |
|
throw new Error('Multilingual papers file not found. Please run the python script to generate the file.'); |
|
} |
|
|
|
filteredPapers = [...allPapers]; |
|
updateResultCount(); |
|
|
|
loadingIndicator.style.display = 'none'; |
|
papersList.style.display = 'grid'; |
|
|
|
} catch (error) { |
|
console.error('Error loading BibTeX file:', error); |
|
loadingIndicator.style.display = 'none'; |
|
showError('Failed to load the BibTeX file. Please check if the file exists.'); |
|
} |
|
} |
|
|
|
async function loadConferencesData() { |
|
try { |
|
const response = await fetch('data/unique_conferences.json'); |
|
|
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP error! status: ${response.status}`); |
|
} |
|
conferencesData = await response.json(); |
|
console.log('Available conference keys:', Object.keys(conferencesData)); |
|
} catch (error) { |
|
console.error('Error loading conferences data:', error); |
|
conferencesData = {}; |
|
} |
|
} |
|
|
|
function populateConferenceSelect() { |
|
const conferenceSelect = document.getElementById('conferenceSelect'); |
|
if (!conferenceSelect || !conferencesData) return; |
|
|
|
|
|
conferenceSelect.innerHTML = ''; |
|
|
|
|
|
const sortedConferences = Object.entries(conferencesData) |
|
.sort((a, b) => { |
|
|
|
if (a[0].includes('Others')) return 1; |
|
if (b[0].includes('Others')) return -1; |
|
|
|
return b[1].count - a[1].count; |
|
}); |
|
|
|
|
|
sortedConferences.forEach(([conferenceName, conferenceData]) => { |
|
const option = document.createElement('option'); |
|
option.value = conferenceName; |
|
option.textContent = `${conferenceName} (${conferenceData.count})`; |
|
conferenceSelect.appendChild(option); |
|
}); |
|
|
|
|
|
updateConferenceCountDisplay(); |
|
updateSelectedConferencesDisplay(); |
|
} |
|
|
|
function parseBibTeX(bibText) { |
|
const papers = []; |
|
const entries = bibText.split(/(?=@)/); |
|
|
|
for (const entry of entries) { |
|
if (!entry.trim()) continue; |
|
|
|
try { |
|
const paper = parseBibTeXEntry(entry); |
|
if (paper && (paper.title || paper.abstract)) { |
|
papers.push(paper); |
|
} |
|
} catch (error) { |
|
console.warn('Error parsing entry:', error); |
|
} |
|
} |
|
|
|
return papers; |
|
} |
|
|
|
function parseBibTeXEntry(entry) { |
|
const paper = {}; |
|
|
|
|
|
const typeMatch = entry.match(/@(\w+)\{([^,]+)/); |
|
if (typeMatch) { |
|
paper.type = typeMatch[1]; |
|
paper.key = typeMatch[2]; |
|
} |
|
|
|
|
|
const fields = ['title', 'author', 'abstract', 'year', 'booktitle', 'journal', 'pages']; |
|
|
|
for (const field of fields) { |
|
const regex = new RegExp(`${field}\\s*=\\s*\\{([^}]*)\\}|${field}\\s*=\\s*"([^"]*)"`, 'gi'); |
|
const match = entry.match(regex); |
|
if (match) { |
|
let value = match[0].replace(new RegExp(`${field}\\s*=\\s*[{"']?|["}']?$`, 'gi'), ''); |
|
|
|
value = cleanLatexCommands(value); |
|
paper[field] = value.trim(); |
|
} |
|
} |
|
|
|
|
|
if (paper.booktitle) { |
|
paper.conference = paper.booktitle; |
|
} else if (paper.journal) { |
|
paper.conference = paper.journal; |
|
} |
|
|
|
|
|
if (!paper.year && paper.key) { |
|
const yearMatch = paper.key.match(/\d{4}/); |
|
if (yearMatch) { |
|
paper.year = yearMatch[0]; |
|
} |
|
} |
|
|
|
|
|
paper.isMultilingual = isMultilingualPaper(paper); |
|
paper.keywords = extractKeywords(paper); |
|
|
|
return paper; |
|
} |
|
|
|
function cleanLatexCommands(text) { |
|
if (!text) return ''; |
|
|
|
return text |
|
.replace(/\\[a-zA-Z]+\{([^}]*)\}/g, '$1') |
|
.replace(/\\[a-zA-Z]+/g, '') |
|
.replace(/\{\\?([^}]*)\}/g, '$1') |
|
.replace(/\\"/g, '"') |
|
.replace(/\\'/g, "'") |
|
.replace(/\\&/g, '&') |
|
.replace(/\\%/g, '%') |
|
.replace(/\\_/g, '_') |
|
.replace(/\\\$/g, '$') |
|
.replace(/\s+/g, ' ') |
|
.trim(); |
|
} |
|
|
|
function isMultilingualPaper(paper) { |
|
const text = `${paper.title || ''} ${paper.abstract || ''}`.toLowerCase(); |
|
|
|
|
|
for (const keyword of multilingualKeywords) { |
|
if (text.includes(keyword.toLowerCase())) { |
|
return true; |
|
} |
|
} |
|
|
|
|
|
for (const language of languageNames) { |
|
if (text.includes(language.toLowerCase())) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
function extractKeywords(paper) { |
|
const keywords = []; |
|
const text = `${paper.title || ''} ${paper.abstract || ''}`.toLowerCase(); |
|
|
|
|
|
for (const keyword of multilingualKeywords) { |
|
if (text.includes(keyword.toLowerCase())) { |
|
keywords.push(keyword); |
|
} |
|
} |
|
|
|
|
|
for (const language of languageNames) { |
|
if (text.includes(language.toLowerCase())) { |
|
keywords.push(language); |
|
} |
|
} |
|
|
|
|
|
const additionalPatterns = [ |
|
{ pattern: /(\d+)\s*languages?/gi, keyword: 'multiple languages' }, |
|
{ pattern: /(\d+)\s*lingual/gi, keyword: 'multiple lingual' }, |
|
{ pattern: /translation\s+model/gi, keyword: 'translation model' }, |
|
{ pattern: /cross\s*lingual/gi, keyword: 'cross-lingual' }, |
|
{ pattern: /multi\s*lingual/gi, keyword: 'multilingual' }, |
|
{ pattern: /zero\s*shot/gi, keyword: 'zero-shot' }, |
|
{ pattern: /few\s*shot/gi, keyword: 'few-shot' }, |
|
{ pattern: /code\s*switch/gi, keyword: 'code-switching' }, |
|
{ pattern: /language\s+transfer/gi, keyword: 'language transfer' }, |
|
{ pattern: /language\s+adaptation/gi, keyword: 'language adaptation' }, |
|
{ pattern: /language\s+identification/gi, keyword: 'language identification' }, |
|
{ pattern: /language\s+detection/gi, keyword: 'language detection' } |
|
]; |
|
|
|
for (const { pattern, keyword } of additionalPatterns) { |
|
if (pattern.test(text)) { |
|
keywords.push(keyword); |
|
} |
|
} |
|
|
|
|
|
return [...new Set(keywords)].sort(); |
|
} |
|
|
|
function updateAuthorFilterInfo() { |
|
const authorFilterInfo = document.getElementById('authorFilterInfo'); |
|
if (authorFilterInfo) { |
|
if (authorFilterActive) { |
|
authorFilterInfo.textContent = '(Showing only papers with author information)'; |
|
} else { |
|
authorFilterInfo.textContent = '(Showing all papers)'; |
|
} |
|
} |
|
} |
|
|
|
function updateStarredFilterInfo() { |
|
const starredFilterInfo = document.getElementById('starredFilterInfo'); |
|
if (starredFilterInfo) { |
|
if (starredFilterActive) { |
|
starredFilterInfo.textContent = `(Showing only ${starredPapers.length} starred papers)`; |
|
} else { |
|
starredFilterInfo.textContent = '(Showing all papers)'; |
|
} |
|
} |
|
} |
|
|
|
function setupEventListeners() { |
|
|
|
const searchInput = document.getElementById('searchInput'); |
|
const addSearchKeyword = document.getElementById('addSearchKeyword'); |
|
const clearSearch = document.getElementById('clearSearch'); |
|
|
|
searchInput.addEventListener('keypress', (e) => { |
|
if (e.key === 'Enter') { |
|
addSearchKeywordFromInput(); |
|
} |
|
}); |
|
addSearchKeyword.addEventListener('click', addSearchKeywordFromInput); |
|
clearSearch.addEventListener('click', clearAllSearchKeywords); |
|
|
|
|
|
const removeSearchInput = document.getElementById('removeSearchInput'); |
|
const addRemoveKeyword = document.getElementById('addRemoveKeyword'); |
|
const clearRemoveSearch = document.getElementById('clearRemoveSearch'); |
|
|
|
removeSearchInput.addEventListener('keypress', (e) => { |
|
if (e.key === 'Enter') { |
|
addRemoveKeywordFromInput(); |
|
} |
|
}); |
|
addRemoveKeyword.addEventListener('click', addRemoveKeywordFromInput); |
|
clearRemoveSearch.addEventListener('click', clearAllRemoveKeywords); |
|
|
|
|
|
const yearInputMin = document.getElementById('yearInputMin'); |
|
const yearInputMax = document.getElementById('yearInputMax'); |
|
const yearRange = document.getElementById('yearRange'); |
|
const clearYearFilter = document.getElementById('clearYearFilter'); |
|
|
|
yearInputMin.addEventListener('input', debounce((e) => { |
|
const value = parseInt(e.target.value) || 1960; |
|
const maxValue = parseInt(yearInputMax.value) || 2025; |
|
|
|
if (value >= 1960 && value <= 2025) { |
|
if (value <= maxValue) { |
|
selectedYearMin = value; |
|
yearFilterActive = true; |
|
updateYearRangeDisplay(); |
|
filterPapers(); |
|
} else { |
|
|
|
selectedYearMin = value; |
|
selectedYearMax = value; |
|
yearInputMax.value = value; |
|
yearFilterActive = true; |
|
updateYearRangeDisplay(); |
|
filterPapers(); |
|
} |
|
} |
|
}, 300)); |
|
|
|
yearInputMax.addEventListener('input', debounce((e) => { |
|
const value = parseInt(e.target.value) || 2025; |
|
const minValue = parseInt(yearInputMin.value) || 1960; |
|
|
|
if (value >= 1960 && value <= 2025) { |
|
if (value >= minValue) { |
|
selectedYearMax = value; |
|
yearFilterActive = true; |
|
updateYearRangeDisplay(); |
|
filterPapers(); |
|
} else { |
|
|
|
selectedYearMax = value; |
|
selectedYearMin = value; |
|
yearInputMin.value = value; |
|
yearFilterActive = true; |
|
updateYearRangeDisplay(); |
|
filterPapers(); |
|
} |
|
} |
|
}, 300)); |
|
|
|
clearYearFilter.addEventListener('click', () => { |
|
yearInputMin.value = 1960; |
|
yearInputMax.value = 2025; |
|
selectedYearMin = 1960; |
|
selectedYearMax = 2025; |
|
yearFilterActive = false; |
|
updateYearRangeDisplay(); |
|
filterPapers(); |
|
}); |
|
|
|
|
|
const conferenceSelect = document.getElementById('conferenceSelect'); |
|
const clearConferenceFilter = document.getElementById('clearConferenceFilter'); |
|
|
|
conferenceSelect.addEventListener('change', () => { |
|
const selectedOptions = Array.from(conferenceSelect.selectedOptions).map(option => option.value); |
|
selectedConferences = selectedOptions; |
|
conferenceFilterActive = selectedConferences.length > 0; |
|
console.log('Selected conferences:', selectedConferences); |
|
updateConferenceCountDisplay(); |
|
updateSelectedConferencesDisplay(); |
|
filterPapers(); |
|
}); |
|
|
|
clearConferenceFilter.addEventListener('click', () => { |
|
conferenceSelect.selectedIndex = -1; |
|
selectedConferences = []; |
|
conferenceFilterActive = false; |
|
updateConferenceCountDisplay(); |
|
updateSelectedConferencesDisplay(); |
|
filterPapers(); |
|
}); |
|
|
|
|
|
const includeNoAbstractCheckbox = document.getElementById('includeNoAbstract'); |
|
|
|
if (includeNoAbstractCheckbox) { |
|
includeNoAbstractCheckbox.addEventListener('change', () => { |
|
includeNoAbstract = includeNoAbstractCheckbox.checked; |
|
abstractFilterActive = !includeNoAbstract; |
|
console.log('Abstract filter changed:', { includeNoAbstract, abstractFilterActive }); |
|
updateAbstractFilterInfo(); |
|
filterPapers(); |
|
}); |
|
console.log('Abstract filter checkbox found and event listener added'); |
|
} else { |
|
console.error('Abstract filter checkbox not found!'); |
|
} |
|
|
|
|
|
const includeNoAuthorCheckbox = document.getElementById('includeNoAuthor'); |
|
|
|
if (includeNoAuthorCheckbox) { |
|
includeNoAuthorCheckbox.addEventListener('change', () => { |
|
includeNoAuthor = includeNoAuthorCheckbox.checked; |
|
authorFilterActive = !includeNoAuthor; |
|
console.log('Author filter changed:', { includeNoAuthor, authorFilterActive }); |
|
updateAuthorFilterInfo(); |
|
filterPapers(); |
|
}); |
|
console.log('Author filter checkbox found and event listener added'); |
|
} else { |
|
console.error('Author filter checkbox not found!'); |
|
} |
|
|
|
|
|
const showOnlyStarredCheckbox = document.getElementById('showOnlyStarred'); |
|
|
|
if (showOnlyStarredCheckbox) { |
|
showOnlyStarredCheckbox.addEventListener('change', () => { |
|
showOnlyStarred = showOnlyStarredCheckbox.checked; |
|
starredFilterActive = showOnlyStarred; |
|
console.log('Starred filter changed:', { showOnlyStarred, starredFilterActive }); |
|
updateStarredFilterInfo(); |
|
filterPapers(); |
|
}); |
|
console.log('Starred filter checkbox found and event listener added'); |
|
} else { |
|
console.error('Starred filter checkbox not found!'); |
|
} |
|
|
|
|
|
const prevPage = document.getElementById('prevPage'); |
|
const nextPage = document.getElementById('nextPage'); |
|
|
|
prevPage.addEventListener('click', () => { |
|
if (currentPage > 1) { |
|
currentPage--; |
|
displayPapers(); |
|
} |
|
}); |
|
|
|
nextPage.addEventListener('click', () => { |
|
const totalPages = Math.ceil(filteredPapers.length / papersPerPage); |
|
if (currentPage < totalPages) { |
|
currentPage++; |
|
displayPapers(); |
|
} |
|
}); |
|
|
|
|
|
const pageInput = document.getElementById('pageInput'); |
|
const goToPageBtn = document.getElementById('goToPage'); |
|
|
|
pageInput.addEventListener('keypress', (e) => { |
|
if (e.key === 'Enter') { |
|
goToPage(); |
|
} |
|
}); |
|
|
|
goToPageBtn.addEventListener('click', goToPage); |
|
|
|
|
|
const modal = document.getElementById('paperModal'); |
|
|
|
window.addEventListener('click', (event) => { |
|
if (event.target === modal) { |
|
modal.style.display = 'none'; |
|
} |
|
}); |
|
|
|
|
|
const saveStatus = document.getElementById('saveStatus'); |
|
const loadStatus = document.getElementById('loadStatus'); |
|
const exportBibTeX = document.getElementById('exportBibTeX'); |
|
const exportStarred = document.getElementById('exportStarred'); |
|
|
|
if (saveStatus) { |
|
saveStatus.addEventListener('click', saveCurrentStatus); |
|
} |
|
|
|
if (loadStatus) { |
|
loadStatus.addEventListener('click', loadStatusFromFile); |
|
} |
|
|
|
if (exportBibTeX) { |
|
exportBibTeX.addEventListener('click', exportFilteredResults); |
|
} |
|
|
|
if (exportStarred) { |
|
exportStarred.addEventListener('click', exportStarredPapers); |
|
} |
|
} |
|
|
|
function addSearchKeywordFromInput() { |
|
const searchInput = document.getElementById('searchInput'); |
|
const keyword = searchInput.value.trim(); |
|
|
|
if (keyword && !searchKeywords.includes(keyword.toLowerCase())) { |
|
searchKeywords.push(keyword.toLowerCase()); |
|
searchInput.value = ''; |
|
updateSearchKeywordsDisplay(); |
|
filterPapers(); |
|
} |
|
} |
|
|
|
function removeSearchKeyword(keyword) { |
|
searchKeywords = searchKeywords.filter(k => k !== keyword); |
|
updateSearchKeywordsDisplay(); |
|
filterPapers(); |
|
} |
|
|
|
function clearAllSearchKeywords() { |
|
searchKeywords = []; |
|
updateSearchKeywordsDisplay(); |
|
filterPapers(); |
|
} |
|
|
|
function updateSearchKeywordsDisplay() { |
|
const container = document.getElementById('searchKeywordsContainer'); |
|
const clearSearch = document.getElementById('clearSearch'); |
|
|
|
container.innerHTML = ''; |
|
|
|
searchKeywords.forEach(keyword => { |
|
const tag = document.createElement('div'); |
|
tag.className = 'search-keyword-tag'; |
|
tag.innerHTML = ` |
|
<span>${keyword}</span> |
|
<button class="remove-keyword" onclick="removeSearchKeyword('${keyword}')" title="Remove keyword"> |
|
<i class="fas fa-times"></i> |
|
</button> |
|
`; |
|
container.appendChild(tag); |
|
}); |
|
|
|
if (searchKeywords.length > 0) { |
|
clearSearch.classList.add('visible'); |
|
} else { |
|
clearSearch.classList.remove('visible'); |
|
} |
|
} |
|
|
|
function addRemoveKeywordFromInput() { |
|
const removeSearchInput = document.getElementById('removeSearchInput'); |
|
const keyword = removeSearchInput.value.trim(); |
|
|
|
if (keyword && !removeSearchKeywords.includes(keyword.toLowerCase())) { |
|
removeSearchKeywords.push(keyword.toLowerCase()); |
|
removeSearchInput.value = ''; |
|
updateRemoveKeywordsDisplay(); |
|
filterPapers(); |
|
} |
|
} |
|
|
|
function removeKeyword(keyword) { |
|
removeSearchKeywords = removeSearchKeywords.filter(k => k !== keyword); |
|
updateRemoveKeywordsDisplay(); |
|
filterPapers(); |
|
} |
|
|
|
function clearAllRemoveKeywords() { |
|
removeSearchKeywords = []; |
|
updateRemoveKeywordsDisplay(); |
|
filterPapers(); |
|
} |
|
|
|
function updateRemoveKeywordsDisplay() { |
|
const container = document.getElementById('removeKeywordsContainer'); |
|
const clearRemoveSearch = document.getElementById('clearRemoveSearch'); |
|
|
|
container.innerHTML = ''; |
|
|
|
removeSearchKeywords.forEach(keyword => { |
|
const tag = document.createElement('div'); |
|
tag.className = 'remove-keyword-tag'; |
|
tag.innerHTML = ` |
|
<span>${keyword}</span> |
|
<button class="remove-keyword" onclick="removeKeyword('${keyword}')" title="Remove keyword"> |
|
<i class="fas fa-times"></i> |
|
</button> |
|
`; |
|
container.appendChild(tag); |
|
}); |
|
|
|
if (removeSearchKeywords.length > 0) { |
|
clearRemoveSearch.classList.add('visible'); |
|
} else { |
|
clearRemoveSearch.classList.remove('visible'); |
|
} |
|
} |
|
|
|
function updatePaginationInfo(start, end, total) { |
|
const paginationInfo = document.getElementById('paginationInfo'); |
|
if (total === 0) { |
|
paginationInfo.textContent = 'No papers found'; |
|
} else { |
|
paginationInfo.textContent = `Showing ${start}-${end} of ${total} papers`; |
|
} |
|
} |
|
|
|
function updatePaginationControls(totalPages) { |
|
const prevPage = document.getElementById('prevPage'); |
|
const nextPage = document.getElementById('nextPage'); |
|
const pageNumbers = document.getElementById('pageNumbers'); |
|
|
|
|
|
prevPage.disabled = currentPage <= 1; |
|
nextPage.disabled = currentPage >= totalPages; |
|
|
|
|
|
pageNumbers.innerHTML = ''; |
|
|
|
if (totalPages <= 1) { |
|
updatePageInput(); |
|
return; |
|
} |
|
|
|
const maxVisiblePages = 5; |
|
let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2)); |
|
let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1); |
|
|
|
|
|
if (endPage - startPage + 1 < maxVisiblePages) { |
|
startPage = Math.max(1, endPage - maxVisiblePages + 1); |
|
} |
|
|
|
|
|
if (startPage > 1) { |
|
addPageNumber(1); |
|
if (startPage > 2) { |
|
addEllipsis(); |
|
} |
|
} |
|
|
|
|
|
for (let i = startPage; i <= endPage; i++) { |
|
addPageNumber(i); |
|
} |
|
|
|
|
|
if (endPage < totalPages) { |
|
if (endPage < totalPages - 1) { |
|
addEllipsis(); |
|
} |
|
addPageNumber(totalPages); |
|
} |
|
|
|
updatePageInput(); |
|
} |
|
|
|
function addEllipsis() { |
|
const pageNumbers = document.getElementById('pageNumbers'); |
|
const ellipsis = document.createElement('span'); |
|
ellipsis.className = 'page-ellipsis'; |
|
ellipsis.textContent = '...'; |
|
pageNumbers.appendChild(ellipsis); |
|
} |
|
|
|
function updatePageInput() { |
|
const pageInput = document.getElementById('pageInput'); |
|
const goToPageBtn = document.getElementById('goToPage'); |
|
const totalPages = Math.ceil(filteredPapers.length / papersPerPage); |
|
|
|
if (pageInput && goToPageBtn) { |
|
pageInput.max = totalPages; |
|
pageInput.value = currentPage; |
|
goToPageBtn.disabled = currentPage < 1 || currentPage > totalPages; |
|
} |
|
} |
|
|
|
function goToPage() { |
|
const pageInput = document.getElementById('pageInput'); |
|
const totalPages = Math.ceil(filteredPapers.length / papersPerPage); |
|
const inputValue = parseInt(pageInput.value); |
|
|
|
if (inputValue && inputValue >= 1 && inputValue <= totalPages) { |
|
currentPage = inputValue; |
|
displayPapers(); |
|
} else { |
|
|
|
pageInput.value = currentPage; |
|
showNotification('Please enter a valid page number between 1 and ' + totalPages, 'warning'); |
|
} |
|
} |
|
|
|
function addPageNumber(pageNum) { |
|
const pageNumbers = document.getElementById('pageNumbers'); |
|
const pageBtn = document.createElement('button'); |
|
pageBtn.className = `page-number ${pageNum === currentPage ? 'active' : ''}`; |
|
pageBtn.textContent = pageNum; |
|
pageBtn.addEventListener('click', () => { |
|
currentPage = pageNum; |
|
displayPapers(); |
|
}); |
|
pageNumbers.appendChild(pageBtn); |
|
} |
|
|
|
function filterPapers() { |
|
let filtered = [...allPapers]; |
|
console.log('Starting filterPapers with:', { |
|
totalPapers: allPapers.length, |
|
abstractFilterActive, |
|
includeNoAbstract, |
|
authorFilterActive, |
|
includeNoAuthor |
|
}); |
|
|
|
|
|
if (searchKeywords.length > 0) { |
|
filtered = filtered.filter(paper => { |
|
|
|
const title = (paper.title || '').toLowerCase(); |
|
const author = (paper.author || '').toLowerCase(); |
|
const abstract = (paper.abstract || '').toLowerCase(); |
|
|
|
return searchKeywords.some(keyword => |
|
title.includes(keyword) || author.includes(keyword) || abstract.includes(keyword) |
|
); |
|
}); |
|
} |
|
|
|
|
|
if (removeSearchKeywords.length > 0) { |
|
const beforeCount = filtered.length; |
|
filtered = filtered.filter(paper => { |
|
|
|
const title = (paper.title || '').toLowerCase(); |
|
const abstract = (paper.abstract || '').toLowerCase(); |
|
|
|
return !removeSearchKeywords.some(keyword => |
|
title.includes(keyword) || abstract.includes(keyword) |
|
); |
|
}); |
|
const removedCount = beforeCount - filtered.length; |
|
console.log(`Remove search filter: ${beforeCount} -> ${filtered.length} papers (removed ${removedCount} papers with keywords: ${removeSearchKeywords.join(', ')})`); |
|
} |
|
|
|
|
|
if (yearFilterActive) { |
|
filtered = filtered.filter(paper => { |
|
const paperYear = parseInt(paper.year) || 0; |
|
return paperYear >= selectedYearMin && paperYear <= selectedYearMax; |
|
}); |
|
} |
|
|
|
|
|
if (conferenceFilterActive) { |
|
console.log('Applying conference filter for:', selectedConferences); |
|
const beforeCount = filtered.length; |
|
filtered = filtered.filter(paper => { |
|
const paperConference = paper.conference || ''; |
|
const matches = selectedConferences.some(selectedConf => { |
|
|
|
|
|
const paperConfLower = paperConference.toLowerCase(); |
|
const selectedConfLower = selectedConf.toLowerCase(); |
|
|
|
|
|
|
|
if (conferencesData[selectedConf] && conferencesData[selectedConf].conferences) { |
|
const confMatch = conferencesData[selectedConf].conferences.some(confName => { |
|
const confNameLower = confName.toLowerCase(); |
|
const matches = (paperConfLower == confNameLower); |
|
if (matches) { |
|
console.log('Conference list match found for:', selectedConf, 'with conference name:', confName); |
|
} |
|
return matches; |
|
}); |
|
return confMatch; |
|
} |
|
|
|
return false; |
|
}); |
|
return matches; |
|
}); |
|
console.log(`Conference filter: ${beforeCount} -> ${filtered.length} papers`); |
|
} |
|
|
|
|
|
if (abstractFilterActive) { |
|
const beforeCount = filtered.length; |
|
const papersWithAbstract = filtered.filter(paper => { |
|
const hasAbstract = paper.abstract && paper.abstract.trim().length > 0; |
|
return hasAbstract; |
|
}).length; |
|
const papersWithoutAbstract = beforeCount - papersWithAbstract; |
|
|
|
filtered = filtered.filter(paper => { |
|
const hasAbstract = paper.abstract && paper.abstract.trim().length > 0; |
|
return hasAbstract; |
|
}); |
|
console.log(`Abstract filter: ${beforeCount} -> ${filtered.length} papers (excluding ${papersWithoutAbstract} papers without abstracts)`); |
|
} else { |
|
const papersWithAbstract = filtered.filter(paper => { |
|
const hasAbstract = paper.abstract && paper.abstract.trim().length > 0; |
|
return hasAbstract; |
|
}).length; |
|
const papersWithoutAbstract = filtered.length - papersWithAbstract; |
|
console.log(`Abstract filter inactive - showing all papers (${papersWithAbstract} with abstracts, ${papersWithoutAbstract} without abstracts)`); |
|
} |
|
|
|
|
|
if (authorFilterActive) { |
|
const beforeCount = filtered.length; |
|
const papersWithAuthor = filtered.filter(paper => { |
|
const hasAuthor = paper.author && paper.author.trim().length > 0 && paper.author.toLowerCase() !== 'unknown authors'; |
|
return hasAuthor; |
|
}).length; |
|
const papersWithoutAuthor = beforeCount - papersWithAuthor; |
|
|
|
filtered = filtered.filter(paper => { |
|
const hasAuthor = paper.author && paper.author.trim().length > 0 && paper.author.toLowerCase() !== 'unknown authors'; |
|
return hasAuthor; |
|
}); |
|
console.log(`Author filter: ${beforeCount} -> ${filtered.length} papers (excluding ${papersWithoutAuthor} papers without author info)`); |
|
} else { |
|
const papersWithAuthor = filtered.filter(paper => { |
|
const hasAuthor = paper.author && paper.author.trim().length > 0 && paper.author.toLowerCase() !== 'unknown authors'; |
|
return hasAuthor; |
|
}).length; |
|
const papersWithoutAuthor = filtered.length - papersWithAuthor; |
|
console.log(`Author filter inactive - showing all papers (${papersWithAuthor} with author info, ${papersWithoutAuthor} without author info)`); |
|
} |
|
|
|
|
|
if (starredFilterActive) { |
|
const beforeCount = filtered.length; |
|
filtered = filtered.filter(paper => { |
|
return starredPapers.includes(paper.key); |
|
}); |
|
console.log(`Starred filter: ${beforeCount} -> ${filtered.length} papers (showing only starred papers)`); |
|
} else { |
|
console.log(`Starred filter inactive - showing all papers`); |
|
} |
|
|
|
|
|
filtered.sort((a, b) => { |
|
const aHasAbstract = a.abstract && a.abstract.trim().length > 0; |
|
const bHasAbstract = b.abstract && b.abstract.trim().length > 0; |
|
|
|
|
|
|
|
|
|
|
|
if (aHasAbstract && !bHasAbstract) return -1; |
|
if (!aHasAbstract && bHasAbstract) return 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0; |
|
}); |
|
|
|
filteredPapers = filtered; |
|
currentPage = 1; |
|
displayPapers(); |
|
} |
|
|
|
function updateYearRangeDisplay() { |
|
const yearRange = document.getElementById('yearRange'); |
|
if (yearFilterActive) { |
|
if (selectedYearMin === 1960 && selectedYearMax === 2025) { |
|
yearRange.textContent = 'All Years'; |
|
} else { |
|
yearRange.textContent = `${selectedYearMin} - ${selectedYearMax}`; |
|
} |
|
} else { |
|
yearRange.textContent = 'All Years'; |
|
} |
|
} |
|
|
|
function updateConferenceCountDisplay() { |
|
const conferenceCount = document.getElementById('conferenceCount'); |
|
if (conferenceFilterActive) { |
|
conferenceCount.textContent = `${selectedConferences.length} Conference${selectedConferences.length !== 1 ? 's' : ''} Selected`; |
|
} else { |
|
conferenceCount.textContent = 'All Conferences'; |
|
} |
|
} |
|
|
|
function updateAbstractFilterInfo() { |
|
const abstractFilterInfo = document.getElementById('abstractFilterInfo'); |
|
if (abstractFilterActive) { |
|
abstractFilterInfo.textContent = '(Showing only papers with abstracts)'; |
|
} else { |
|
abstractFilterInfo.textContent = '(Showing all papers)'; |
|
} |
|
} |
|
|
|
function updateSelectedConferencesDisplay() { |
|
const selectedConferencesDiv = document.getElementById('selectedConferences'); |
|
if (!selectedConferencesDiv) return; |
|
|
|
selectedConferencesDiv.innerHTML = ''; |
|
|
|
selectedConferences.forEach(conference => { |
|
const tag = document.createElement('div'); |
|
tag.className = 'conference-tag'; |
|
tag.innerHTML = ` |
|
${conference} |
|
<button class="remove-tag" onclick="removeConference('${conference}')"> |
|
<i class="fas fa-times"></i> |
|
</button> |
|
`; |
|
selectedConferencesDiv.appendChild(tag); |
|
}); |
|
} |
|
|
|
function removeConference(conference) { |
|
const conferenceSelect = document.getElementById('conferenceSelect'); |
|
const option = Array.from(conferenceSelect.options).find(opt => opt.value === conference); |
|
if (option) { |
|
option.selected = false; |
|
} |
|
|
|
selectedConferences = selectedConferences.filter(c => c !== conference); |
|
conferenceFilterActive = selectedConferences.length > 0; |
|
updateConferenceCountDisplay(); |
|
updateSelectedConferencesDisplay(); |
|
filterPapers(); |
|
} |
|
|
|
function displayPapers() { |
|
const papersList = document.getElementById('papersList'); |
|
const noResults = document.getElementById('noResults'); |
|
|
|
if (filteredPapers.length === 0) { |
|
papersList.style.display = 'none'; |
|
noResults.style.display = 'block'; |
|
updatePaginationInfo(0, 0, 0); |
|
updatePaginationControls(0); |
|
} else { |
|
papersList.style.display = 'grid'; |
|
noResults.style.display = 'none'; |
|
|
|
|
|
const totalPages = Math.ceil(filteredPapers.length / papersPerPage); |
|
const startIndex = (currentPage - 1) * papersPerPage; |
|
const endIndex = Math.min(startIndex + papersPerPage, filteredPapers.length); |
|
const currentPagePapers = filteredPapers.slice(startIndex, endIndex); |
|
|
|
papersList.innerHTML = currentPagePapers.map(paper => createPaperCard(paper)).join(''); |
|
|
|
|
|
const paperCards = document.querySelectorAll('.paper-card'); |
|
paperCards.forEach((card, index) => { |
|
card.addEventListener('click', () => { |
|
showPaperModal(currentPagePapers[index]); |
|
}); |
|
}); |
|
|
|
updatePaginationInfo(startIndex + 1, endIndex, filteredPapers.length); |
|
updatePaginationControls(totalPages); |
|
updatePageInput(); |
|
} |
|
|
|
updateResultCount(); |
|
} |
|
|
|
function getConferenceKey(paper) { |
|
if (!paper.conference || !conferencesData) return 'Unknown'; |
|
|
|
const paperConference = paper.conference.toLowerCase(); |
|
console.log('Looking for conference key for:', paper.conference); |
|
|
|
|
|
for (const [conferenceKey, conferenceData] of Object.entries(conferencesData)) { |
|
|
|
if (conferenceData.conferences) { |
|
for (const confName of conferenceData.conferences) { |
|
|
|
if (paperConference.toLowerCase() == confName.toLowerCase()) { |
|
console.log('Found match by conference name:', conferenceKey, 'for:', confName); |
|
return conferenceKey; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
if (conferencesData["Others (820 Venues)"]) { |
|
console.log('No specific match found, returning Others (820 Venues)'); |
|
return "Others (820 Venues)"; |
|
} |
|
|
|
console.log('No match found, returning Unknown'); |
|
return 'Unknown'; |
|
} |
|
|
|
function createPaperCard(paper) { |
|
const title = paper.title || 'Untitled'; |
|
const authors = paper.author || 'Unknown authors'; |
|
const abstract = paper.abstract || 'No abstract available'; |
|
const year = paper.year || 'Unknown year'; |
|
const conferenceKey = getConferenceKey(paper); |
|
const hasAbstract = paper.abstract && paper.abstract.trim().length > 0; |
|
const hasAuthor = paper.author && paper.author.trim().length > 0 && paper.author.toLowerCase() !== 'unknown authors'; |
|
const isStarred = isPaperStarred(paper.key); |
|
|
|
let cardClasses = 'paper-card'; |
|
if (!hasAbstract) cardClasses += ' no-abstract'; |
|
if (!hasAuthor) cardClasses += ' no-author'; |
|
|
|
return ` |
|
<div class="${cardClasses}"> |
|
<div class="paper-header"> |
|
<h3 class="paper-title">${title}</h3> |
|
<button class="star-button ${isStarred ? 'starred' : ''}" |
|
data-paper-key="${paper.key}" |
|
onclick="toggleStar('${paper.key}', event)" |
|
title="${isStarred ? 'Remove from starred' : 'Add to starred'}"> |
|
<i class="${isStarred ? 'fas' : 'far'} fa-star"></i> |
|
</button> |
|
</div> |
|
<p class="paper-authors">${authors}</p> |
|
<p class="paper-abstract">${abstract}</p> |
|
<div class="paper-meta"> |
|
<span class="paper-year">${year}</span> |
|
<span class="paper-conference">${conferenceKey}</span> |
|
</div> |
|
</div> |
|
`; |
|
} |
|
|
|
function showPaperModal(paper) { |
|
const modal = document.getElementById('paperModal'); |
|
const modalTitle = document.getElementById('modalTitle'); |
|
const modalAuthors = document.getElementById('modalAuthors'); |
|
const modalAbstract = document.getElementById('modalAbstract'); |
|
const modalYear = document.getElementById('modalYear'); |
|
const modalConference = document.getElementById('modalConference'); |
|
const modalKeywords = document.getElementById('modalKeywords'); |
|
const modalStarButton = document.getElementById('modalStarButton'); |
|
const modalFindPaperButton = document.getElementById('modalFindPaperButton'); |
|
|
|
modalTitle.textContent = paper.title || 'Untitled'; |
|
modalAuthors.textContent = paper.author || 'Unknown authors'; |
|
modalAbstract.textContent = paper.abstract || 'No abstract available'; |
|
modalYear.textContent = paper.year || 'Unknown year'; |
|
|
|
|
|
|
|
|
|
if (!paper.conference || !conferencesData){ |
|
modalConference.textContent = 'Unknown conference'; |
|
} else { |
|
modalConference.textContent = paper.conference || 'Unknown conference' |
|
} |
|
|
|
|
|
|
|
modalKeywords.textContent = paper.keywords.length > 0 ? paper.keywords.join(', ') : 'No keywords'; |
|
|
|
|
|
if (modalStarButton) { |
|
const isStarred = isPaperStarred(paper.key); |
|
modalStarButton.className = `star-button ${isStarred ? 'starred' : ''}`; |
|
modalStarButton.setAttribute('data-paper-key', paper.key); |
|
modalStarButton.setAttribute('title', isStarred ? 'Remove from starred' : 'Add to starred'); |
|
modalStarButton.onclick = (event) => toggleStar(paper.key, event); |
|
|
|
const icon = modalStarButton.querySelector('i'); |
|
if (icon) { |
|
icon.className = `${isStarred ? 'fas' : 'far'} fa-star`; |
|
} |
|
} |
|
|
|
|
|
if (modalFindPaperButton) { |
|
modalFindPaperButton.onclick = () => searchPaperOnGoogle(paper.title); |
|
} |
|
|
|
modal.style.display = 'block'; |
|
} |
|
|
|
function searchPaperOnGoogle(paperTitle) { |
|
if (!paperTitle || paperTitle === 'Untitled') { |
|
return; |
|
} |
|
|
|
|
|
const encodedTitle = encodeURIComponent(paperTitle); |
|
const googleSearchUrl = `https://www.google.com/search?q=${encodedTitle}`; |
|
|
|
|
|
window.open(googleSearchUrl, '_blank'); |
|
} |
|
|
|
function generateBibTeXContent(papers) { |
|
let content = ''; |
|
|
|
for (const paper of papers) { |
|
|
|
content += `@${paper.type}{${paper.key},\n`; |
|
|
|
if (paper.title) { |
|
content += ` title = {${paper.title}},\n`; |
|
} |
|
if (paper.author) { |
|
content += ` author = {${paper.author}},\n`; |
|
} |
|
if (paper.abstract) { |
|
content += ` abstract = {${paper.abstract}},\n`; |
|
} |
|
if (paper.year) { |
|
content += ` year = {${paper.year}},\n`; |
|
} |
|
if (paper.booktitle) { |
|
content += ` booktitle = {${paper.booktitle}},\n`; |
|
} |
|
if (paper.journal) { |
|
content += ` journal = {${paper.journal}},\n`; |
|
} |
|
if (paper.pages) { |
|
content += ` pages = {${paper.pages}},\n`; |
|
} |
|
|
|
|
|
content = content.replace(/,\n$/, '\n'); |
|
content += '}\n\n'; |
|
} |
|
|
|
return content; |
|
} |
|
|
|
function showNotification(message, type = 'success', duration = 10000) { |
|
|
|
const notification = document.createElement('div'); |
|
notification.className = `notification notification-${type}`; |
|
|
|
let icon = 'fa-info-circle'; |
|
if (type === 'error') icon = 'fa-exclamation-triangle'; |
|
else if (type === 'warning') icon = 'fa-exclamation-circle'; |
|
else if (type === 'success') icon = 'fa-check-circle'; |
|
|
|
notification.innerHTML = ` |
|
<div class="notification-content"> |
|
<i class="fas ${icon}"></i> |
|
<span>${message}</span> |
|
<button class="notification-close" onclick="this.parentElement.parentElement.remove()"> |
|
<i class="fas fa-times"></i> |
|
</button> |
|
</div> |
|
`; |
|
|
|
|
|
document.body.appendChild(notification); |
|
|
|
|
|
setTimeout(() => { |
|
if (notification.parentElement) { |
|
notification.remove(); |
|
} |
|
}, duration); |
|
} |
|
|
|
function updateResultCount() { |
|
const resultCount = document.getElementById('resultCount'); |
|
const removeResultCount = document.getElementById('removeResultCount'); |
|
const total = allPapers.length; |
|
const filtered = filteredPapers.length; |
|
|
|
if (searchKeywords.length === 0) { |
|
resultCount.textContent = `Total: ${total} papers`; |
|
} else { |
|
resultCount.textContent = `Found ${filtered} of ${total} papers (${searchKeywords.length} keyword${searchKeywords.length !== 1 ? 's' : ''})`; |
|
} |
|
|
|
if (removeSearchKeywords.length === 0) { |
|
removeResultCount.textContent = 'All papers included'; |
|
} else { |
|
|
|
const papersAfterSearch = searchKeywords.length > 0 ? |
|
allPapers.filter(paper => { |
|
const title = (paper.title || '').toLowerCase(); |
|
const author = (paper.author || '').toLowerCase(); |
|
const abstract = (paper.abstract || '').toLowerCase(); |
|
return searchKeywords.some(keyword => |
|
title.includes(keyword) || author.includes(keyword) || abstract.includes(keyword) |
|
); |
|
}) : |
|
allPapers; |
|
const removedCount = papersAfterSearch.length - filtered; |
|
removeResultCount.textContent = `Removed ${removedCount} papers (${removeSearchKeywords.length} keyword${removeSearchKeywords.length !== 1 ? 's' : ''})`; |
|
} |
|
} |
|
|
|
function showError(message) { |
|
const papersList = document.getElementById('papersList'); |
|
const loadingIndicator = document.getElementById('loadingIndicator'); |
|
|
|
loadingIndicator.style.display = 'none'; |
|
papersList.style.display = 'none'; |
|
|
|
papersList.innerHTML = ` |
|
<div class="no-results"> |
|
<i class="fas fa-exclamation-triangle"></i> |
|
<h3>Error</h3> |
|
<p>${message}</p> |
|
</div> |
|
`; |
|
papersList.style.display = 'block'; |
|
} |
|
|
|
function matchesSearchField(paper, query, field) { |
|
const title = (paper.title || '').toLowerCase(); |
|
const author = (paper.author || '').toLowerCase(); |
|
const abstract = (paper.abstract || '').toLowerCase(); |
|
|
|
switch (field) { |
|
case 'title': |
|
return title.includes(query); |
|
case 'author': |
|
return author.includes(query); |
|
case 'abstract': |
|
return abstract.includes(query); |
|
case 'title+author': |
|
return title.includes(query) || author.includes(query); |
|
case 'title+abstract': |
|
return title.includes(query) || abstract.includes(query); |
|
case 'author+abstract': |
|
return author.includes(query) || abstract.includes(query); |
|
case 'all': |
|
default: |
|
return title.includes(query) || author.includes(query) || abstract.includes(query); |
|
} |
|
} |
|
|
|
|
|
function debounce(func, wait) { |
|
let timeout; |
|
return function executedFunction(...args) { |
|
const later = () => { |
|
clearTimeout(timeout); |
|
func(...args); |
|
}; |
|
clearTimeout(timeout); |
|
timeout = setTimeout(later, wait); |
|
}; |
|
} |
|
|
|
|
|
function toggleStar(paperKey, event) { |
|
event.stopPropagation(); |
|
|
|
const index = starredPapers.indexOf(paperKey); |
|
if (index > -1) { |
|
|
|
starredPapers.splice(index, 1); |
|
showNotification('Paper removed from starred list', 'success', 1000); |
|
} else { |
|
|
|
starredPapers.push(paperKey); |
|
showNotification('Paper added to starred list', 'success', 1000); |
|
} |
|
|
|
|
|
updateStarButton(paperKey); |
|
updateStarredCount(); |
|
updateStarredFilterInfo(); |
|
} |
|
|
|
function isPaperStarred(paperKey) { |
|
return starredPapers.includes(paperKey); |
|
} |
|
|
|
function updateStarButton(paperKey) { |
|
const starButtons = document.querySelectorAll(`[data-paper-key="${paperKey}"]`); |
|
starButtons.forEach(button => { |
|
const icon = button.querySelector('i'); |
|
if (isPaperStarred(paperKey)) { |
|
button.classList.add('starred'); |
|
icon.className = 'fas fa-star'; |
|
} else { |
|
button.classList.remove('starred'); |
|
icon.className = 'far fa-star'; |
|
} |
|
}); |
|
} |
|
|
|
function updateStarredCount() { |
|
const exportStarred = document.getElementById('exportStarred'); |
|
if (exportStarred) { |
|
|
|
exportStarred.innerHTML = ` |
|
<i class="fas fa-star"></i> |
|
Export Starred (${starredPapers.length}) |
|
`; |
|
} |
|
} |
|
|
|
function exportStarredPapers() { |
|
if (starredPapers.length === 0) { |
|
showNotification('No starred papers to export', 'warning'); |
|
return; |
|
} |
|
|
|
|
|
const starredPaperObjects = allPapers.filter(paper => starredPapers.includes(paper.key)); |
|
|
|
const bibTeXContent = generateBibTeXContent(starredPaperObjects); |
|
const blob = new Blob([bibTeXContent], { type: 'text/plain' }); |
|
const url = URL.createObjectURL(blob); |
|
const a = document.createElement('a'); |
|
a.href = url; |
|
const now = new Date(); |
|
const timestamp = now.toISOString().slice(0, 19).replace(/:/g, '-').replace('T', '_'); |
|
a.download = `starred_papers_${starredPapers.length}_papers_${timestamp}.bib`; |
|
document.body.appendChild(a); |
|
a.click(); |
|
document.body.removeChild(a); |
|
URL.revokeObjectURL(url); |
|
|
|
showNotification(`Exported ${starredPapers.length} starred papers to BibTeX file!`); |
|
} |
|
|
|
|
|
function saveCurrentStatus() { |
|
const status = { |
|
timestamp: new Date().toISOString(), |
|
filters: { |
|
searchKeywords: searchKeywords, |
|
removeSearchKeywords: removeSearchKeywords, |
|
yearFilter: { |
|
min: selectedYearMin, |
|
max: selectedYearMax, |
|
active: yearFilterActive |
|
}, |
|
conferenceFilter: { |
|
selectedConferences: selectedConferences, |
|
active: conferenceFilterActive |
|
}, |
|
abstractFilter: { |
|
includeNoAbstract: includeNoAbstract, |
|
active: abstractFilterActive |
|
}, |
|
authorFilter: { |
|
includeNoAuthor: includeNoAuthor, |
|
active: authorFilterActive |
|
}, |
|
starredFilter: { |
|
showOnlyStarred: showOnlyStarred, |
|
active: starredFilterActive |
|
} |
|
}, |
|
pagination: { |
|
currentPage: currentPage, |
|
papersPerPage: papersPerPage |
|
}, |
|
results: { |
|
totalPapers: allPapers.length, |
|
filteredPapersCount: filteredPapers.length, |
|
filteredPaperIds: filteredPapers.map(paper => paper.key) |
|
}, |
|
starredPapers: starredPapers |
|
}; |
|
|
|
const blob = new Blob([JSON.stringify(status, null, 2)], { type: 'application/json' }); |
|
const url = URL.createObjectURL(blob); |
|
const a = document.createElement('a'); |
|
a.href = url; |
|
const now = new Date(); |
|
const timestamp = now.toISOString().slice(0, 19).replace(/:/g, '-').replace('T', '_'); |
|
a.download = `multilingual_papers_filter_${timestamp}.json`; |
|
document.body.appendChild(a); |
|
a.click(); |
|
document.body.removeChild(a); |
|
URL.revokeObjectURL(url); |
|
|
|
showNotification('Filter status saved successfully!'); |
|
} |
|
|
|
|
|
function loadStatusFromFile() { |
|
const input = document.createElement('input'); |
|
input.type = 'file'; |
|
input.accept = '.json'; |
|
input.onchange = function(event) { |
|
const file = event.target.files[0]; |
|
if (file) { |
|
const reader = new FileReader(); |
|
reader.onload = function(e) { |
|
try { |
|
const status = JSON.parse(e.target.result); |
|
applyLoadedStatus(status); |
|
showNotification('Filter status loaded successfully!'); |
|
} catch (error) { |
|
console.error('Error parsing status file:', error); |
|
showNotification('Error: Invalid status file format', 'error'); |
|
} |
|
}; |
|
reader.readAsText(file); |
|
} |
|
}; |
|
input.click(); |
|
} |
|
|
|
|
|
function applyLoadedStatus(status) { |
|
if (!status.filters) { |
|
showNotification('Error: Invalid status file format', 'error'); |
|
return; |
|
} |
|
|
|
const filters = status.filters; |
|
|
|
|
|
searchKeywords = filters.searchKeywords || []; |
|
removeSearchKeywords = filters.removeSearchKeywords || []; |
|
|
|
|
|
if (filters.yearFilter) { |
|
selectedYearMin = filters.yearFilter.min || 1960; |
|
selectedYearMax = filters.yearFilter.max || 2025; |
|
yearFilterActive = filters.yearFilter.active || false; |
|
|
|
|
|
const yearInputMin = document.getElementById('yearInputMin'); |
|
const yearInputMax = document.getElementById('yearInputMax'); |
|
if (yearInputMin) yearInputMin.value = selectedYearMin; |
|
if (yearInputMax) yearInputMax.value = selectedYearMax; |
|
updateYearRangeDisplay(); |
|
} |
|
|
|
|
|
if (filters.conferenceFilter) { |
|
selectedConferences = filters.conferenceFilter.selectedConferences || []; |
|
conferenceFilterActive = filters.conferenceFilter.active || false; |
|
|
|
|
|
const conferenceSelect = document.getElementById('conferenceSelect'); |
|
if (conferenceSelect) { |
|
|
|
conferenceSelect.selectedIndex = -1; |
|
|
|
selectedConferences.forEach(conference => { |
|
const option = Array.from(conferenceSelect.options).find(opt => opt.value === conference); |
|
if (option) { |
|
option.selected = true; |
|
} |
|
}); |
|
} |
|
updateConferenceCountDisplay(); |
|
updateSelectedConferencesDisplay(); |
|
} |
|
|
|
|
|
if (filters.abstractFilter) { |
|
includeNoAbstract = filters.abstractFilter.includeNoAbstract !== undefined ? |
|
filters.abstractFilter.includeNoAbstract : true; |
|
abstractFilterActive = filters.abstractFilter.active || false; |
|
|
|
|
|
const includeNoAbstractCheckbox = document.getElementById('includeNoAbstract'); |
|
if (includeNoAbstractCheckbox) { |
|
includeNoAbstractCheckbox.checked = includeNoAbstract; |
|
} |
|
updateAbstractFilterInfo(); |
|
} |
|
|
|
|
|
if (filters.authorFilter) { |
|
includeNoAuthor = filters.authorFilter.includeNoAuthor !== undefined ? |
|
filters.authorFilter.includeNoAuthor : true; |
|
authorFilterActive = filters.authorFilter.active || false; |
|
|
|
|
|
const includeNoAuthorCheckbox = document.getElementById('includeNoAuthor'); |
|
if (includeNoAuthorCheckbox) { |
|
includeNoAuthorCheckbox.checked = includeNoAuthor; |
|
} |
|
updateAuthorFilterInfo(); |
|
} |
|
|
|
|
|
if (filters.starredFilter) { |
|
showOnlyStarred = filters.starredFilter.showOnlyStarred !== undefined ? |
|
filters.starredFilter.showOnlyStarred : false; |
|
starredFilterActive = filters.starredFilter.active || false; |
|
|
|
|
|
const showOnlyStarredCheckbox = document.getElementById('showOnlyStarred'); |
|
if (showOnlyStarredCheckbox) { |
|
showOnlyStarredCheckbox.checked = showOnlyStarred; |
|
} |
|
updateStarredFilterInfo(); |
|
} |
|
|
|
|
|
if (status.pagination) { |
|
currentPage = status.pagination.currentPage || 1; |
|
papersPerPage = status.pagination.papersPerPage || 10; |
|
} |
|
|
|
|
|
if (status.starredPapers) { |
|
starredPapers = status.starredPapers || []; |
|
} |
|
|
|
|
|
updateSearchKeywordsDisplay(); |
|
updateRemoveKeywordsDisplay(); |
|
updateStarredCount(); |
|
|
|
|
|
filterPapers(); |
|
|
|
console.log('Applied loaded status:', status); |
|
} |
|
|
|
|
|
function exportFilteredResults() { |
|
if (filteredPapers.length === 0) { |
|
showNotification('No papers to export', 'warning'); |
|
return; |
|
} |
|
|
|
const bibTeXContent = generateBibTeXContent(filteredPapers); |
|
const blob = new Blob([bibTeXContent], { type: 'text/plain' }); |
|
const url = URL.createObjectURL(blob); |
|
const a = document.createElement('a'); |
|
a.href = url; |
|
const now = new Date(); |
|
const timestamp = now.toISOString().slice(0, 19).replace(/:/g, '-').replace('T', '_'); |
|
a.download = `multilingual_papers_filtered_${filteredPapers.length}_papers_${timestamp}.bib`; |
|
document.body.appendChild(a); |
|
a.click(); |
|
document.body.removeChild(a); |
|
URL.revokeObjectURL(url); |
|
|
|
showNotification(`Exported ${filteredPapers.length} papers to BibTeX file!`); |
|
} |