moderanize the app.js
This commit is contained in:
parent
5318ff0c5e
commit
e05a86c74b
1 changed files with 261 additions and 171 deletions
432
docs/app.js
432
docs/app.js
|
|
@ -1,173 +1,263 @@
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
/**
|
||||||
const fileHostsTableContainer = document.getElementById('file-hosts-table-container');
|
* @fileoverview Main application file for Debrid Services Comparison
|
||||||
const hostSearchInput = document.getElementById('host-search-input');
|
* Handles data fetching, table generation, search functionality, and comparison features
|
||||||
const clearHostSearchIcon = document.getElementById('clear-host-search');
|
*/
|
||||||
const provider1Select = document.getElementById('provider1');
|
|
||||||
const provider2Select = document.getElementById('provider2');
|
|
||||||
const compareTableContainer = document.getElementById('compare-table-container');
|
|
||||||
const closeCompareButton = document.getElementById('close-compare');
|
|
||||||
|
|
||||||
const adultHostsTableContainer = document.getElementById('adult-hosts-table-container');
|
// Class to handle table-related operations
|
||||||
const adultHostSearchInput = document.getElementById('adult-host-search-input');
|
class TableManager {
|
||||||
const clearAdultHostSearchIcon = document.getElementById('clear-adult-host-search');
|
/**
|
||||||
|
* @param {string} containerId - ID of the table container element
|
||||||
const pricingSection = document.getElementById('pricing'); // Targeting the 'Pricing' section
|
* @param {string} searchInputId - ID of the search input element
|
||||||
|
* @param {string} clearIconId - ID of the clear search icon element
|
||||||
// Initially hide the comparison table container
|
*/
|
||||||
compareTableContainer.style.display = 'none';
|
constructor(containerId, searchInputId, clearIconId) {
|
||||||
|
this.container = document.getElementById(containerId);
|
||||||
try {
|
this.searchInput = document.getElementById(searchInputId);
|
||||||
const [fileHostsResponse, adultHostsResponse, pricingResponse] = await Promise.all([
|
this.clearIcon = document.getElementById(clearIconId);
|
||||||
fetch('./json/file-hosts.json'),
|
this.setupSearchFunctionality();
|
||||||
fetch('./json/adult-hosts.json'),
|
|
||||||
fetch('./json/pricing.json') // Fetching the pricing data
|
|
||||||
]);
|
|
||||||
const fileHostsData = await fileHostsResponse.json();
|
|
||||||
const adultHostsData = await adultHostsResponse.json();
|
|
||||||
const pricingData = await pricingResponse.json(); // Parsed pricing data
|
|
||||||
|
|
||||||
// Function to generate a table
|
|
||||||
const generateTable = (data, tableId) => {
|
|
||||||
const container = document.getElementById(tableId);
|
|
||||||
let tableHTML = `<table id="${tableId.replace('-container', '')}"><thead><tr><th>Service Name</th>`;
|
|
||||||
const providers = Object.keys(data[Object.keys(data)[0]]);
|
|
||||||
providers.forEach(provider => {
|
|
||||||
tableHTML += `<th>${provider}</th>`;
|
|
||||||
});
|
|
||||||
tableHTML += '</tr></thead><tbody>';
|
|
||||||
|
|
||||||
for (const host in data) {
|
|
||||||
tableHTML += `<tr><td>${host}</td>`;
|
|
||||||
providers.forEach(provider => {
|
|
||||||
const status = data[host][provider];
|
|
||||||
const icon = status === 'yes' ? '✅' : '❌';
|
|
||||||
tableHTML += `<td style="text-align: center;">${icon}</td>`;
|
|
||||||
});
|
|
||||||
tableHTML += '</tr>';
|
|
||||||
}
|
|
||||||
tableHTML += '</tbody></table>';
|
|
||||||
container.innerHTML = tableHTML;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to generate the pricing table
|
|
||||||
const generatePricingTable = (pricingData) => {
|
|
||||||
let tableHTML = '<table><thead><tr><th>Plans</th>';
|
|
||||||
// Extract service names from the first plan object
|
|
||||||
if (pricingData.plans && pricingData.plans.length > 0) {
|
|
||||||
const services = Object.keys(pricingData.plans[0]).filter(key => key !== 'name');
|
|
||||||
services.forEach(service => {
|
|
||||||
tableHTML += `<th>${service}</th>`;
|
|
||||||
});
|
|
||||||
tableHTML += '</tr></thead><tbody>';
|
|
||||||
|
|
||||||
pricingData.plans.forEach(plan => {
|
|
||||||
tableHTML += '<tr class=pricing-tb>';
|
|
||||||
tableHTML += `<td>${plan.name}</td>`;
|
|
||||||
services.forEach(service => {
|
|
||||||
tableHTML += `<td>${plan[service]}</td>`;
|
|
||||||
});
|
|
||||||
tableHTML += '</tr>';
|
|
||||||
});
|
|
||||||
|
|
||||||
tableHTML += '</tbody></table>';
|
|
||||||
pricingSection.innerHTML = tableHTML;
|
|
||||||
} else {
|
|
||||||
pricingSection.innerHTML = '<p>Error: Pricing data structure is incorrect.</p>';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to populate provider dropdowns
|
|
||||||
const populateProviderDropdowns = (data) => {
|
|
||||||
const providers = Object.keys(data[Object.keys(data)[0]]);
|
|
||||||
providers.forEach(provider => {
|
|
||||||
const option1 = document.createElement('option');
|
|
||||||
option1.value = provider;
|
|
||||||
option1.textContent = provider;
|
|
||||||
provider1Select.appendChild(option1);
|
|
||||||
|
|
||||||
const option2 = document.createElement('option');
|
|
||||||
option2.value = provider;
|
|
||||||
option2.textContent = provider;
|
|
||||||
provider2Select.appendChild(option2);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to generate the comparison table
|
|
||||||
const generateCompareTable = (data, provider1, provider2) => {
|
|
||||||
let tableHTML = '<table id="compare-table"><thead><tr><th>Service Name</th><th>' + provider1 + '</th><th>' + provider2 + '</th></tr></thead><tbody>';
|
|
||||||
|
|
||||||
for (const host in data) {
|
|
||||||
const status1 = data[host][provider1];
|
|
||||||
const status2 = data[host][provider2];
|
|
||||||
const icon1 = status1 === 'yes' ? '✅' : '❌';
|
|
||||||
const icon2 = status2 === 'yes' ? '✅' : '❌';
|
|
||||||
tableHTML += `<tr><td>${host}</td><td>${icon1}</td><td>${icon2}</td></tr>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
tableHTML += '</tbody></table>';
|
|
||||||
compareTableContainer.innerHTML = `<button id="close-compare" title="Close Comparison">X</button>` + tableHTML;
|
|
||||||
// Re-add the event listener for the close button after it's generated
|
|
||||||
document.getElementById('close-compare').addEventListener('click', () => {
|
|
||||||
compareTableContainer.style.display = 'none';
|
|
||||||
});
|
|
||||||
compareTableContainer.style.display = 'block';
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function for search functionality with clear icon visibility
|
|
||||||
const setupSearch = (searchInput, clearIcon, tableContainerId) => {
|
|
||||||
searchInput.addEventListener('input', () => {
|
|
||||||
const searchTerm = searchInput.value.toLowerCase();
|
|
||||||
const rows = document.querySelectorAll(`#${tableContainerId} table tbody tr`);
|
|
||||||
if (!rows) return;
|
|
||||||
|
|
||||||
// Show/hide clear icon based on input
|
|
||||||
clearIcon.style.display = searchTerm ? 'block' : 'none';
|
|
||||||
|
|
||||||
rows.forEach(row => {
|
|
||||||
const serviceName = row.querySelector('td:first-child').textContent.toLowerCase();
|
|
||||||
row.style.display = serviceName.includes(searchTerm) ? '' : 'none';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
clearIcon.addEventListener('click', () => {
|
|
||||||
searchInput.value = '';
|
|
||||||
clearIcon.style.display = 'none';
|
|
||||||
// Trigger the input event to refresh the table
|
|
||||||
searchInput.dispatchEvent(new Event('input'));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initial table generation and dropdown population
|
|
||||||
generateTable(fileHostsData, 'file-hosts-table-container');
|
|
||||||
generateTable(adultHostsData, 'adult-hosts-table-container');
|
|
||||||
populateProviderDropdowns(fileHostsData);
|
|
||||||
generatePricingTable(pricingData); // Generate pricing table
|
|
||||||
|
|
||||||
// Setup search for both tables
|
|
||||||
setupSearch(hostSearchInput, clearHostSearchIcon, 'file-hosts-table-container');
|
|
||||||
setupSearch(adultHostSearchInput, clearAdultHostSearchIcon, 'adult-hosts-table-container');
|
|
||||||
|
|
||||||
// Comparison functionality
|
|
||||||
const handleCompare = () => {
|
|
||||||
const provider1 = provider1Select.value;
|
|
||||||
const provider2 = provider2Select.value;
|
|
||||||
if (provider1 && provider2) {
|
|
||||||
generateCompareTable(fileHostsData, provider1, provider2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
provider1Select.addEventListener('change', handleCompare);
|
|
||||||
provider2Select.addEventListener('change', handleCompare);
|
|
||||||
|
|
||||||
// Close comparison functionality
|
|
||||||
closeCompareButton.addEventListener('click', () => {
|
|
||||||
compareTableContainer.style.display = 'none';
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching or parsing JSON:', error);
|
|
||||||
fileHostsTableContainer.innerHTML = '<p>Error loading file hosts data.</p>';
|
|
||||||
adultHostsTableContainer.innerHTML = '<p>Error loading adult hosts data.</p>';
|
|
||||||
pricingSection.innerHTML = '<p>Error loading pricing data.</p>';
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
/**
|
||||||
|
* Generates a table from provided data
|
||||||
|
* @param {Object} data - Table data object
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
generateTable(data) {
|
||||||
|
if (!data || Object.keys(data).length === 0) {
|
||||||
|
this.container.innerHTML = '<p>No data available.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const providers = Object.keys(data[Object.keys(data)[0]]);
|
||||||
|
const tableHTML = `
|
||||||
|
<table id="${this.container.id.replace('-container', '')}">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Service Name</th>
|
||||||
|
${providers.map(provider => `<th>${provider}</th>`).join('')}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${Object.entries(data).map(([host, providerData]) => `
|
||||||
|
<tr>
|
||||||
|
<td>${host}</td>
|
||||||
|
${providers.map(provider => `
|
||||||
|
<td style="text-align: center;">
|
||||||
|
${providerData[provider] === 'yes' ? '✅' : '❌'}
|
||||||
|
</td>
|
||||||
|
`).join('')}
|
||||||
|
</tr>
|
||||||
|
`).join('')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.container.innerHTML = tableHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up search functionality for the table
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
setupSearchFunctionality() {
|
||||||
|
this.searchInput?.addEventListener('input', () => {
|
||||||
|
const searchTerm = this.searchInput.value.toLowerCase();
|
||||||
|
const rows = this.container.querySelectorAll('table tbody tr');
|
||||||
|
|
||||||
|
this.clearIcon.style.display = searchTerm ? 'block' : 'none';
|
||||||
|
|
||||||
|
rows.forEach(row => {
|
||||||
|
const serviceName = row.querySelector('td:first-child')?.textContent.toLowerCase();
|
||||||
|
row.style.display = serviceName?.includes(searchTerm) ? '' : 'none';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.clearIcon?.addEventListener('click', () => {
|
||||||
|
this.searchInput.value = '';
|
||||||
|
this.clearIcon.style.display = 'none';
|
||||||
|
this.searchInput.dispatchEvent(new Event('input'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class to handle comparison functionality
|
||||||
|
class ComparisonManager {
|
||||||
|
/**
|
||||||
|
* @param {string} containerId - ID of the comparison container
|
||||||
|
* @param {string} select1Id - ID of the first provider select element
|
||||||
|
* @param {string} select2Id - ID of the second provider select element
|
||||||
|
* @param {Object} data - Comparison data object
|
||||||
|
*/
|
||||||
|
constructor(containerId, select1Id, select2Id, data) {
|
||||||
|
this.container = document.getElementById(containerId);
|
||||||
|
this.select1 = document.getElementById(select1Id);
|
||||||
|
this.select2 = document.getElementById(select2Id);
|
||||||
|
this.data = data;
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.populateDropdowns();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates provider dropdowns with available options
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
populateDropdowns() {
|
||||||
|
const providers = Object.keys(this.data[Object.keys(this.data)[0]]);
|
||||||
|
|
||||||
|
[this.select1, this.select2].forEach(select => {
|
||||||
|
providers.forEach(provider => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = provider;
|
||||||
|
option.textContent = provider;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates comparison table for selected providers
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
generateCompareTable() {
|
||||||
|
const provider1 = this.select1.value;
|
||||||
|
const provider2 = this.select2.value;
|
||||||
|
|
||||||
|
if (!provider1 || !provider2) return;
|
||||||
|
|
||||||
|
const tableHTML = `
|
||||||
|
<button id="close-compare" title="Close Comparison">X</button>
|
||||||
|
<table id="compare-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Service Name</th>
|
||||||
|
<th>${provider1}</th>
|
||||||
|
<th>${provider2}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${Object.entries(this.data).map(([host, providerData]) => `
|
||||||
|
<tr>
|
||||||
|
<td>${host}</td>
|
||||||
|
<td>${providerData[provider1] === 'yes' ? '✅' : '❌'}</td>
|
||||||
|
<td>${providerData[provider2] === 'yes' ? '✅' : '❌'}</td>
|
||||||
|
</tr>
|
||||||
|
`).join('')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.container.innerHTML = tableHTML;
|
||||||
|
this.container.style.display = 'block';
|
||||||
|
this.setupCloseButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up event listeners for comparison functionality
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
setupEventListeners() {
|
||||||
|
[this.select1, this.select2].forEach(select => {
|
||||||
|
select?.addEventListener('change', () => this.generateCompareTable());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up close button functionality
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
setupCloseButton() {
|
||||||
|
document.getElementById('close-compare')?.addEventListener('click', () => {
|
||||||
|
this.container.style.display = 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class to handle pricing table generation
|
||||||
|
class PricingManager {
|
||||||
|
/**
|
||||||
|
* @param {string} containerId - ID of the pricing section container
|
||||||
|
*/
|
||||||
|
constructor(containerId) {
|
||||||
|
this.container = document.getElementById(containerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates pricing table from provided data
|
||||||
|
* @param {Object} data - Pricing data object
|
||||||
|
*/
|
||||||
|
generatePricingTable(data) {
|
||||||
|
if (!data?.plans?.length) {
|
||||||
|
this.container.innerHTML = '<p>Error: Invalid pricing data structure.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const services = Object.keys(data.plans[0]).filter(key => key !== 'name');
|
||||||
|
const tableHTML = `
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Plans</th>
|
||||||
|
${services.map(service => `<th>${service}</th>`).join('')}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${data.plans.map(plan => `
|
||||||
|
<tr class="pricing-tb">
|
||||||
|
<td>${plan.name}</td>
|
||||||
|
${services.map(service => `<td>${plan[service]}</td>`).join('')}
|
||||||
|
</tr>
|
||||||
|
`).join('')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.container.innerHTML = tableHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main application initialization
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
// Initialize table managers
|
||||||
|
const fileHostsTable = new TableManager(
|
||||||
|
'file-hosts-table-container',
|
||||||
|
'host-search-input',
|
||||||
|
'clear-host-search'
|
||||||
|
);
|
||||||
|
|
||||||
|
const adultHostsTable = new TableManager(
|
||||||
|
'adult-hosts-table-container',
|
||||||
|
'adult-host-search-input',
|
||||||
|
'clear-adult-host-search'
|
||||||
|
);
|
||||||
|
|
||||||
|
const pricingManager = new PricingManager('pricing');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch all required data concurrently
|
||||||
|
const [fileHostsData, adultHostsData, pricingData] = await Promise.all([
|
||||||
|
fetch('./json/file-hosts.json').then(res => res.json()),
|
||||||
|
fetch('./json/adult-hosts.json').then(res => res.json()),
|
||||||
|
fetch('./json/pricing.json').then(res => res.json())
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Generate tables and initialize comparison
|
||||||
|
fileHostsTable.generateTable(fileHostsData);
|
||||||
|
adultHostsTable.generateTable(adultHostsData);
|
||||||
|
pricingManager.generatePricingTable(pricingData);
|
||||||
|
|
||||||
|
// Initialize comparison manager
|
||||||
|
new ComparisonManager(
|
||||||
|
'compare-table-container',
|
||||||
|
'provider1',
|
||||||
|
'provider2',
|
||||||
|
fileHostsData
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing application:', error);
|
||||||
|
const errorMessage = '<p>Error loading data. Please try again later.</p>';
|
||||||
|
fileHostsTable.container.innerHTML = errorMessage;
|
||||||
|
adultHostsTable.container.innerHTML = errorMessage;
|
||||||
|
pricingManager.container.innerHTML = errorMessage;
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue