// Global variables 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 = []; // Array to store starred paper keys // Multilingual keywords for filtering 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' ] // Language names for filtering 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', // Additional language variations and names '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", ]; // Initialize the application document.addEventListener('DOMContentLoaded', function() { initializeApp(); }); async function initializeApp() { try { await Promise.all([ loadBibTeXFile(), loadConferencesData() ]); setupEventListeners(); populateConferenceSelect(); updateAbstractFilterInfo(); updateAuthorFilterInfo(); updateStarredFilterInfo(); updateSearchKeywordsDisplay(); updateRemoveKeywordsDisplay(); updateStarredCount(); // Initialize starred count 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'; // Try to load the multilingual papers file first let response = await fetch('data/multilingual_papers.bib'); // let response = await fetch('https://github.com/crystina-z/multilingual-paperbase/raw/refs/heads/master/data/multilingual_papers.bib'); if (response.ok) { // Load from pre-filtered multilingual papers file const bibText = await response.text(); allPapers = parseBibTeX(bibText); console.log(`Loaded ${allPapers.length} multilingual papers from multilingual_papers.bib`); } else { // If multilingual file doesn't exist, report error and ask user to run python script 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'); // const response = await fetch('https://github.com/crystina-z/multilingual-paperbase/raw/refs/heads/master/data/unique_conferences.json'); // const response = await fetch('https://github.com/crystina-z/multilingual-paperbase/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; // Clear existing options conferenceSelect.innerHTML = ''; // Sort conferences by count (descending), but put "Others (820 Venues)" at the bottom const sortedConferences = Object.entries(conferencesData) .sort((a, b) => { // Put "Others (820 Venues)" at the bottom if (a[0].includes('Others')) return 1; if (b[0].includes('Others')) return -1; // Sort by count descending return b[1].count - a[1].count; }); // Add options to select sortedConferences.forEach(([conferenceName, conferenceData]) => { const option = document.createElement('option'); option.value = conferenceName; option.textContent = `${conferenceName} (${conferenceData.count})`; conferenceSelect.appendChild(option); }); // Initialize the display 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 = {}; // Extract entry type and key const typeMatch = entry.match(/@(\w+)\{([^,]+)/); if (typeMatch) { paper.type = typeMatch[1]; paper.key = typeMatch[2]; } // Extract fields 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'), ''); // Clean up LaTeX commands value = cleanLatexCommands(value); paper[field] = value.trim(); } } // Extract conference information if (paper.booktitle) { paper.conference = paper.booktitle; } else if (paper.journal) { paper.conference = paper.journal; } // Extract year from key if not found in fields if (!paper.year && paper.key) { const yearMatch = paper.key.match(/\d{4}/); if (yearMatch) { paper.year = yearMatch[0]; } } // Determine if paper is multilingual paper.isMultilingual = isMultilingualPaper(paper); paper.keywords = extractKeywords(paper); return paper; } function cleanLatexCommands(text) { if (!text) return ''; return text .replace(/\\[a-zA-Z]+\{([^}]*)\}/g, '$1') // Remove LaTeX commands with braces .replace(/\\[a-zA-Z]+/g, '') // Remove simple LaTeX commands .replace(/\{\\?([^}]*)\}/g, '$1') // Remove braces .replace(/\\"/g, '"') // Replace escaped quotes .replace(/\\'/g, "'") // Replace escaped apostrophes .replace(/\\&/g, '&') // Replace escaped ampersands .replace(/\\%/g, '%') // Replace escaped percent signs .replace(/\\_/g, '_') // Replace escaped underscores .replace(/\\\$/g, '$') // Replace escaped dollar signs .replace(/\s+/g, ' ') // Normalize whitespace .trim(); } function isMultilingualPaper(paper) { const text = `${paper.title || ''} ${paper.abstract || ''}`.toLowerCase(); // Check for multilingual keywords for (const keyword of multilingualKeywords) { if (text.includes(keyword.toLowerCase())) { return true; } } // Check for language names 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(); // Extract multilingual keywords for (const keyword of multilingualKeywords) { if (text.includes(keyword.toLowerCase())) { keywords.push(keyword); } } // Extract language names for (const language of languageNames) { if (text.includes(language.toLowerCase())) { keywords.push(language); } } // Additional keyword extraction based on common patterns 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); } } // Remove duplicates and sort 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() { // Search input 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); // Remove search input 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); // Year filter 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 { // If min is greater than max, set max to min 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 { // If max is less than min, set min to max 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(); }); // Conference filter 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(); }); // Abstract filter const includeNoAbstractCheckbox = document.getElementById('includeNoAbstract'); if (includeNoAbstractCheckbox) { includeNoAbstractCheckbox.addEventListener('change', () => { includeNoAbstract = includeNoAbstractCheckbox.checked; abstractFilterActive = !includeNoAbstract; // When unchecked, we filter out papers without abstracts 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!'); } // Author filter const includeNoAuthorCheckbox = document.getElementById('includeNoAuthor'); if (includeNoAuthorCheckbox) { includeNoAuthorCheckbox.addEventListener('change', () => { includeNoAuthor = includeNoAuthorCheckbox.checked; authorFilterActive = !includeNoAuthor; // When unchecked, we filter out papers without author info 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!'); } // Starred filter const showOnlyStarredCheckbox = document.getElementById('showOnlyStarred'); if (showOnlyStarredCheckbox) { showOnlyStarredCheckbox.addEventListener('change', () => { showOnlyStarred = showOnlyStarredCheckbox.checked; starredFilterActive = showOnlyStarred; // When checked, we filter to show only starred papers 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!'); } // Pagination controls 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(); } }); // Page input functionality const pageInput = document.getElementById('pageInput'); const goToPageBtn = document.getElementById('goToPage'); pageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { goToPage(); } }); goToPageBtn.addEventListener('click', goToPage); // Modal const modal = document.getElementById('paperModal'); window.addEventListener('click', (event) => { if (event.target === modal) { modal.style.display = 'none'; } }); // Export and save/load buttons 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 = ` ${keyword} `; 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 = ` ${keyword} `; 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'); // Update previous/next buttons prevPage.disabled = currentPage <= 1; nextPage.disabled = currentPage >= totalPages; // Generate page numbers 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); // Adjust start page if we're near the end if (endPage - startPage + 1 < maxVisiblePages) { startPage = Math.max(1, endPage - maxVisiblePages + 1); } // Add first page and ellipsis if needed if (startPage > 1) { addPageNumber(1); if (startPage > 2) { addEllipsis(); } } // Add visible page numbers for (let i = startPage; i <= endPage; i++) { addPageNumber(i); } // Add last page and ellipsis if needed 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 { // Reset input to current page if invalid 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 }); // Apply search filter if (searchKeywords.length > 0) { filtered = filtered.filter(paper => { // Check if paper contains any of the search keywords in any field 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) ); }); } // Apply remove search filter (remove papers containing the specified keywords) if (removeSearchKeywords.length > 0) { const beforeCount = filtered.length; filtered = filtered.filter(paper => { // Check if paper contains any of the removal keywords in title or abstract only 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(', ')})`); } // Apply year filter if (yearFilterActive) { filtered = filtered.filter(paper => { const paperYear = parseInt(paper.year) || 0; return paperYear >= selectedYearMin && paperYear <= selectedYearMax; }); } // Apply conference filter 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 => { // Check if the paper's conference name contains the selected conference name // This handles cases where the conference name might be a substring const paperConfLower = paperConference.toLowerCase(); const selectedConfLower = selectedConf.toLowerCase(); // Check if the selected conference is a key in conferencesData // and if the paper's conference is in the conferences list 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`); } // Apply abstract filter 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)`); } // Apply author filter 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)`); } // Apply starred filter 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`); } // Sort papers: papers with abstracts and known conferences first, then papers with abstracts but unknown conferences, then papers without abstracts filtered.sort((a, b) => { const aHasAbstract = a.abstract && a.abstract.trim().length > 0; const bHasAbstract = b.abstract && b.abstract.trim().length > 0; // const aConferenceKey = getConferenceKey(a); // const bConferenceKey = getConferenceKey(b); // const aHasUnknownConference = aConferenceKey === 'Unknown'; // const bHasUnknownConference = bConferenceKey === 'Unknown'; if (aHasAbstract && !bHasAbstract) return -1; if (!aHasAbstract && bHasAbstract) return 1; // // First priority: papers with abstracts and known conferences // if (aHasAbstract && !aHasUnknownConference && (!bHasAbstract || bHasUnknownConference)) return -1; // if (bHasAbstract && !bHasUnknownConference && (!aHasAbstract || aHasUnknownConference)) return 1; // // Second priority: papers with abstracts but unknown conferences // if (aHasAbstract && aHasUnknownConference && (!bHasAbstract || (bHasAbstract && !bHasUnknownConference))) return -1; // if (bHasAbstract && bHasUnknownConference && (!aHasAbstract || (aHasAbstract && !aHasUnknownConference))) return 1; // // Third priority: papers without abstracts (unknown conferences come last) // if (!aHasAbstract && !bHasAbstract) { // if (aHasUnknownConference && !bHasUnknownConference) return 1; // if (!aHasUnknownConference && bHasUnknownConference) return -1; // } return 0; }); filteredPapers = filtered; currentPage = 1; // Reset to first page when filtering 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} `; 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'; // Calculate pagination 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(''); // Add click listeners to paper cards 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); // Find the conference key that matches this paper for (const [conferenceKey, conferenceData] of Object.entries(conferencesData)) { // Check if the paper's conference is in the conferences list if (conferenceData.conferences) { for (const confName of conferenceData.conferences) { // if (paperConference.includes(confName.toLowerCase())) { if (paperConference.toLowerCase() == confName.toLowerCase()) { console.log('Found match by conference name:', conferenceKey, 'for:', confName); return conferenceKey; } } } } // If no specific match found, check if it should be in "Others (820 Venues)" // This handles cases where the paper's conference is not in any specific category 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 `

${title}

${authors}

${abstract}

${year} ${conferenceKey}
`; } 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'; // Get conference information using the same logic as in createPaperCard // const conferenceKey = getConferenceKey(paper); if (!paper.conference || !conferencesData){ modalConference.textContent = 'Unknown conference'; } else { modalConference.textContent = paper.conference || 'Unknown conference' } // const conferenceKey = paper.conference || 'Unknown conference'; // modalConference.textContent = paper.conference || 'Unknown conference'; modalKeywords.textContent = paper.keywords.length > 0 ? paper.keywords.join(', ') : 'No keywords'; // Update modal star button 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`; } } // Set up Find Paper button if (modalFindPaperButton) { modalFindPaperButton.onclick = () => searchPaperOnGoogle(paper.title); } modal.style.display = 'block'; } function searchPaperOnGoogle(paperTitle) { if (!paperTitle || paperTitle === 'Untitled') { return; } // Encode the paper title for URL const encodedTitle = encodeURIComponent(paperTitle); const googleSearchUrl = `https://www.google.com/search?q=${encodedTitle}`; // Open Google search in a new tab window.open(googleSearchUrl, '_blank'); } function generateBibTeXContent(papers) { let content = ''; for (const paper of papers) { // Reconstruct the original BibTeX entry 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`; } // Remove trailing comma and add closing brace content = content.replace(/,\n$/, '\n'); content += '}\n\n'; } return content; } function showNotification(message, type = 'success', duration = 10000) { // Create notification element 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 = `
${message}
`; // Add to page document.body.appendChild(notification); // Auto-remove after duration 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 { // Calculate how many papers were removed by the remove search 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 = `

Error

${message}

`; 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); } } // Utility function for debouncing function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Star functionality function toggleStar(paperKey, event) { event.stopPropagation(); // Prevent triggering the card click const index = starredPapers.indexOf(paperKey); if (index > -1) { // Remove from starred starredPapers.splice(index, 1); showNotification('Paper removed from starred list', 'success', 1000); } else { // Add to starred starredPapers.push(paperKey); showNotification('Paper added to starred list', 'success', 1000); } // Update the star button appearance updateStarButton(paperKey); updateStarredCount(); updateStarredFilterInfo(); // Update filter info when starred papers change } 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) { // Clear the button content and rebuild it with the updated count exportStarred.innerHTML = ` Export Starred (${starredPapers.length}) `; } } function exportStarredPapers() { if (starredPapers.length === 0) { showNotification('No starred papers to export', 'warning'); return; } // Get the actual paper objects for starred papers 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!`); } // Save current filtering status and document IDs to JSON 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 // Save starred papers list }; 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!'); } // Load filtering status from JSON file 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(); } // Apply loaded filtering status function applyLoadedStatus(status) { if (!status.filters) { showNotification('Error: Invalid status file format', 'error'); return; } const filters = status.filters; // Apply search keywords searchKeywords = filters.searchKeywords || []; removeSearchKeywords = filters.removeSearchKeywords || []; // Apply year filter if (filters.yearFilter) { selectedYearMin = filters.yearFilter.min || 1960; selectedYearMax = filters.yearFilter.max || 2025; yearFilterActive = filters.yearFilter.active || false; // Update UI const yearInputMin = document.getElementById('yearInputMin'); const yearInputMax = document.getElementById('yearInputMax'); if (yearInputMin) yearInputMin.value = selectedYearMin; if (yearInputMax) yearInputMax.value = selectedYearMax; updateYearRangeDisplay(); } // Apply conference filter if (filters.conferenceFilter) { selectedConferences = filters.conferenceFilter.selectedConferences || []; conferenceFilterActive = filters.conferenceFilter.active || false; // Update UI const conferenceSelect = document.getElementById('conferenceSelect'); if (conferenceSelect) { // Clear current selection conferenceSelect.selectedIndex = -1; // Select the loaded conferences selectedConferences.forEach(conference => { const option = Array.from(conferenceSelect.options).find(opt => opt.value === conference); if (option) { option.selected = true; } }); } updateConferenceCountDisplay(); updateSelectedConferencesDisplay(); } // Apply abstract filter if (filters.abstractFilter) { includeNoAbstract = filters.abstractFilter.includeNoAbstract !== undefined ? filters.abstractFilter.includeNoAbstract : true; abstractFilterActive = filters.abstractFilter.active || false; // Update UI const includeNoAbstractCheckbox = document.getElementById('includeNoAbstract'); if (includeNoAbstractCheckbox) { includeNoAbstractCheckbox.checked = includeNoAbstract; } updateAbstractFilterInfo(); } // Apply author filter if (filters.authorFilter) { includeNoAuthor = filters.authorFilter.includeNoAuthor !== undefined ? filters.authorFilter.includeNoAuthor : true; authorFilterActive = filters.authorFilter.active || false; // Update UI const includeNoAuthorCheckbox = document.getElementById('includeNoAuthor'); if (includeNoAuthorCheckbox) { includeNoAuthorCheckbox.checked = includeNoAuthor; } updateAuthorFilterInfo(); } // Apply starred filter if (filters.starredFilter) { showOnlyStarred = filters.starredFilter.showOnlyStarred !== undefined ? filters.starredFilter.showOnlyStarred : false; starredFilterActive = filters.starredFilter.active || false; // Update UI const showOnlyStarredCheckbox = document.getElementById('showOnlyStarred'); if (showOnlyStarredCheckbox) { showOnlyStarredCheckbox.checked = showOnlyStarred; } updateStarredFilterInfo(); } // Apply pagination if (status.pagination) { currentPage = status.pagination.currentPage || 1; papersPerPage = status.pagination.papersPerPage || 10; } // Apply starred papers if (status.starredPapers) { starredPapers = status.starredPapers || []; } // Update displays updateSearchKeywordsDisplay(); updateRemoveKeywordsDisplay(); updateStarredCount(); // Update starred count display // Re-apply filters filterPapers(); console.log('Applied loaded status:', status); } // Export current filtered results to BibTeX 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!`); }