Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Dictionary</title> | |
| <style> | |
| body { | |
| font-family: sans-serif; | |
| margin: 20px; | |
| background-color: #f4f4f4; | |
| } | |
| #container { | |
| width: 80%; | |
| margin: 0 auto; | |
| background-color: white; | |
| padding: 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); | |
| } | |
| input[type="file"], input[type="text"] { /* Keep file inputs for flexibility */ | |
| padding: 10px; | |
| margin-bottom: 10px; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| width: calc(100% - 22px); | |
| } | |
| #search { | |
| margin-top: 20px; | |
| width: 98%; | |
| } | |
| #results { | |
| list-style: none; | |
| padding: 0; | |
| margin: 0; | |
| } | |
| #results li { | |
| padding: 15px; | |
| border-bottom: 1px solid #eee; | |
| margin-bottom: 5px; | |
| background-color: #fafafa; | |
| border-radius: 4px; | |
| } | |
| #results li:last-child { | |
| border-bottom: none; | |
| } | |
| .word-cell { | |
| font-weight: bold; | |
| font-size: 1.1em; | |
| margin-bottom: 5px; | |
| } | |
| .translation-cell { | |
| font-style: italic; | |
| color: #333; | |
| margin-bottom: 5px; | |
| } | |
| .additional-cell { | |
| color: #555; | |
| margin-bottom: 3px; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| .error-message { | |
| color: red; | |
| margin-top: 5px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="container"> | |
| <h2>Dictionary</h2> | |
| <!-- File inputs are kept but hidden --> | |
| <input type="file" id="excelFile" accept=".xlsx, .xls, .csv" style="display: none;"> | |
| <input type="file" id="mappingFile" accept=".txt" style="display: none;"> | |
| <input type="text" id="search" placeholder="Search..."> | |
| <div id="errorMessageContainer" class="error-message"></div> | |
| <ul id="results"></ul> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script> | |
| <script> | |
| const excelFileInput = document.getElementById('excelFile'); | |
| const mappingFileInput = document.getElementById('mappingFile'); | |
| const searchInput = document.getElementById('search'); | |
| const resultsList = document.getElementById('results'); | |
| const errorMessageContainer = document.getElementById('errorMessageContainer'); | |
| let excelData = []; | |
| let mapping = {}; | |
| // --- Helper Functions --- | |
| // Generic function to fetch and process a file | |
| async function fetchAndProcessFile(filename, fileType, processFunction) { | |
| try { | |
| const response = await fetch(filename); | |
| if (!response.ok) { | |
| if (response.status === 404){ //only display if file not found. | |
| throw new Error(`${fileType} file not found: ${filename}`); | |
| } | |
| throw new Error(`HTTP error! status: ${response.status} for ${filename}`); | |
| } | |
| const data = (fileType === 'Excel') ? await response.arrayBuffer() : await response.text(); | |
| processFunction(data); | |
| } catch (error) { | |
| console.error(`Error loading or processing ${fileType} file:`, error); | |
| errorMessageContainer.textContent = error.message; // Display the error | |
| } | |
| } | |
| // Process Excel file | |
| function processExcel(data) { | |
| const workbook = XLSX.read(data, { type: 'array', raw: false }); | |
| const sheetName = workbook.SheetNames[0]; | |
| excelData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], { header: 1, raw: false }); | |
| if (Object.keys(mapping).length > 0) { //Only display if it's loaded. | |
| displayData(); // Initial display after both files are loaded | |
| } | |
| } | |
| // Process mapping file | |
| function processMapping(text) { | |
| parseMapping(text); | |
| if (excelData.length > 0){ // display if loaded. | |
| displayData(); | |
| } | |
| } | |
| function parseMapping(text) { | |
| mapping = {}; // Reset mapping | |
| const lines = text.trim().split('\n'); | |
| lines.forEach(line => { | |
| const [column, fieldName] = line.trim().split('|'); | |
| if (column && fieldName) { | |
| mapping[column.trim()] = fieldName.trim(); | |
| } | |
| }); | |
| //console.log("Parsed Mapping:", mapping); | |
| } | |
| function displayData(filteredData = null) { | |
| resultsList.innerHTML = ''; // Clear previous results | |
| errorMessageContainer.textContent = ''; // Clear previous error messages | |
| const dataToDisplay = filteredData || excelData; | |
| dataToDisplay.forEach(row => { | |
| if (row.length === 0) return; // Skip empty rows | |
| const listItem = document.createElement('li'); | |
| let hasContent = false; | |
| // Check and add word cell | |
| if (mapping['A'] ) { // Always map 'A' if it exists in the mapping | |
| const colIndexA = 'A'.charCodeAt(0) - 'A'.charCodeAt(0); | |
| if (row[colIndexA] != undefined){ //Check the column if it's empty | |
| const wordCell = document.createElement('div'); | |
| wordCell.className = 'word-cell'; | |
| wordCell.textContent = row[colIndexA]; | |
| listItem.appendChild(wordCell); | |
| hasContent = true; | |
| } | |
| } | |
| //Check and add B cell | |
| if (mapping['B']) { | |
| const colIndexB = 'B'.charCodeAt(0) - 'A'.charCodeAt(0); | |
| if (row[colIndexB] != undefined){ | |
| const translationCell = document.createElement('div'); | |
| translationCell.className = 'translation-cell'; | |
| translationCell.textContent = row[colIndexB]; | |
| listItem.appendChild(translationCell); | |
| hasContent = true; | |
| } | |
| } | |
| // Check and add additional cells (C onwards) | |
| for (let i = 2; i < 26; i++) { // Iterate from C (index 2) up to Z | |
| const columnLetter = String.fromCharCode('A'.charCodeAt(0) + i); | |
| if (mapping[columnLetter]) { | |
| const colIndex = columnLetter.charCodeAt(0) - 'A'.charCodeAt(0); | |
| if(row[colIndex] != undefined) { // Check if the cell is empty | |
| const additionalCell = document.createElement('div'); | |
| additionalCell.className = 'additional-cell'; | |
| additionalCell.textContent = `${mapping[columnLetter]}: ${row[colIndex]}`; | |
| listItem.appendChild(additionalCell); | |
| hasContent = true; | |
| } | |
| } | |
| } | |
| if (hasContent) { | |
| resultsList.appendChild(listItem); | |
| } | |
| }); | |
| } | |
| // --- Event Listeners --- | |
| searchInput.addEventListener('input', () => { | |
| const searchTerm = searchInput.value.toLowerCase().trim(); | |
| if (!searchTerm) { | |
| displayData(); //Display All data. | |
| return; | |
| } | |
| const filteredData = excelData.filter(row => { | |
| return row.some(cell => { | |
| if (typeof cell === 'string') { | |
| return cell.toLowerCase().includes(searchTerm); | |
| } else if (typeof cell === 'number') { | |
| return cell.toString().toLowerCase().includes(searchTerm); | |
| } | |
| return false; | |
| }); | |
| }); | |
| displayData(filteredData); | |
| }); | |
| // --- Initial Load --- | |
| // Attempt to load both files automatically on page load | |
| fetchAndProcessFile('mapping.txt', 'Mapping', processMapping); | |
| // Try loading with different extensions | |
| const excelExtensions = ['.xlsx', '.xls', '.csv']; | |
| let excelLoaded = false; | |
| async function loadExcelWithDifferentExtensions() { | |
| for (const ext of excelExtensions) { | |
| if (excelLoaded) break; // Exit if already loaded | |
| try { | |
| await fetchAndProcessFile(`dictionary${ext}`, 'Excel', (data) => { | |
| processExcel(data); | |
| excelLoaded = true; // Set the flag once successfully loaded | |
| }); | |
| } | |
| catch (error) | |
| { | |
| // console.error to avoid double errors. | |
| console.error(`dictionary${ext} did not load. `); | |
| } | |
| } | |
| if (!excelLoaded) | |
| { | |
| // if no files found display error. | |
| errorMessageContainer.textContent = "No Excel file found (dictionary.xlsx, dictionary.xls, or dictionary.csv)"; | |
| } | |
| } | |
| loadExcelWithDifferentExtensions(); | |
| </script> | |
| </body> | |
| </html> |