diff --git a/docs/app-min.js b/docs/app-min.js
index 249dad7..4969367 100644
--- a/docs/app-min.js
+++ b/docs/app-min.js
@@ -1 +1 @@
-class TableManager{constructor(t,e,n){this.container=document.getElementById(t),this.searchInput=document.getElementById(e),this.clearIcon=document.getElementById(n),this.setupSearchFunctionality()}generateTable(t){if(!t||0===Object.keys(t).length)return void(this.container.innerHTML="
No data available.
");const e=Object.keys(t[Object.keys(t)[0]]),n=`\n \n \n \n Service Name \n ${e.map((t=>`${t} `)).join("")}\n \n \n \n ${Object.entries(t).map((([t,n])=>`\n \n ${t} \n ${e.map((t=>`\n \n ${"yes"===n[t]?"✅":"❌"}\n \n `)).join("")}\n \n `)).join("")}\n \n
\n `;this.container.innerHTML=n}setupSearchFunctionality(){this.searchInput?.addEventListener("input",(()=>{const t=this.searchInput.value.toLowerCase(),e=this.container.querySelectorAll("table tbody tr");this.clearIcon.style.display=t?"block":"none",e.forEach((e=>{const n=e.querySelector("td:first-child")?.textContent.toLowerCase();e.style.display=n?.includes(t)?"":"none"}))})),this.clearIcon?.addEventListener("click",(()=>{this.searchInput.value="",this.clearIcon.style.display="none",this.searchInput.dispatchEvent(new Event("input"))}))}}class ComparisonManager{constructor(t,e,n,a){this.container=document.getElementById(t),this.select1=document.getElementById(e),this.select2=document.getElementById(n),this.data=a,this.setupEventListeners(),this.populateDropdowns()}populateDropdowns(){const t=Object.keys(this.data[Object.keys(this.data)[0]]);[this.select1,this.select2].forEach((e=>{t.forEach((t=>{const n=document.createElement("option");n.value=t,n.textContent=t,e.appendChild(n)}))}))}generateCompareTable(){const t=this.select1.value,e=this.select2.value;if(!t||!e)return;const n=`\n X \n \n \n \n Service Name \n ${t} \n ${e} \n \n \n \n ${Object.entries(this.data).map((([n,a])=>`\n \n ${n} \n ${"yes"===a[t]?"✅":"❌"} \n ${"yes"===a[e]?"✅":"❌"} \n \n `)).join("")}\n \n
\n `;this.container.innerHTML=n,this.container.style.display="block",this.setupCloseButton()}setupEventListeners(){[this.select1,this.select2].forEach((t=>{t?.addEventListener("change",(()=>this.generateCompareTable()))}))}setupCloseButton(){document.getElementById("close-compare")?.addEventListener("click",(()=>{this.container.style.display="none"}))}}class PricingManager{constructor(t){this.container=document.getElementById(t)}generatePricingTable(t){if(!t?.plans?.length)return void(this.container.innerHTML="Error: Invalid pricing data structure.
");const e=Object.keys(t.plans[0]).filter((t=>"name"!==t)),n=`\n \n \n \n Plans \n ${e.map((t=>`${t} `)).join("")}\n \n \n \n ${t.plans.map((t=>`\n \n ${t.name} \n ${e.map((e=>`${t[e]} `)).join("")}\n \n `)).join("")}\n \n
\n `;this.container.innerHTML=n}}document.addEventListener("DOMContentLoaded",(async()=>{const t=new TableManager("file-hosts-table-container","host-search-input","clear-host-search"),e=new TableManager("adult-hosts-table-container","adult-host-search-input","clear-adult-host-search"),n=new PricingManager("pricing");try{const[a,s,i]=await Promise.all([fetch("./json/file-hosts.json").then((t=>t.json())),fetch("./json/adult-hosts.json").then((t=>t.json())),fetch("./json/pricing.json").then((t=>t.json()))]);t.generateTable(a),e.generateTable(s),n.generatePricingTable(i),new ComparisonManager("compare-table-container","provider1","provider2",a)}catch(a){const s="Error loading data. Please try again later.
";t.container.innerHTML=s,e.container.innerHTML=s,n.container.innerHTML=s}}));
\ No newline at end of file
+const debounce=(e,t=300)=>{let n;return(...a)=>{clearTimeout(n),n=setTimeout((()=>e(...a)),t)}};class TableManager{constructor(e,t,n){this.container=document.querySelector(`#${e}`),this.searchInput=document.querySelector(`#${t}`),this.clearIcon=document.querySelector(`#${n}`),this._setupSearchFunctionality()}generateTable(e={}){if(!Object.keys(e).length)return void(this.container.innerHTML="No data available.
");const t=Object.keys(e[Object.keys(e)[0]]);this.container.innerHTML=`\n \n \n \n Service Name \n ${t.map((e=>`${e} `)).join("")}\n \n \n \n ${Object.entries(e).map((([e,n])=>`\n \n ${e} \n ${t.map((e=>`\n \n ${"yes"===n[e]?"✅":"❌"}\n \n `)).join("")}\n \n `)).join("")}\n \n
\n `}_setupSearchFunctionality(){const e=debounce((()=>{const e=this.searchInput.value.toLowerCase(),t=this.container.querySelectorAll("table tbody tr");this.clearIcon.style.display=e?"block":"none",t.forEach((t=>{const n=t.querySelector("td:first-child")?.textContent.toLowerCase();t.style.display=n?.includes(e)?"":"none"}))}));this.searchInput?.addEventListener("input",e),this.clearIcon?.addEventListener("click",(()=>{this.searchInput.value="",this.clearIcon.style.display="none",e()}))}}class ComparisonManager{constructor(e,t,n,a){this.container=document.querySelector(`#${e}`),this.select1=document.querySelector(`#${t}`),this.select2=document.querySelector(`#${n}`),this.data=a,this._populateDropdowns(),this._setupEventListeners()}_populateDropdowns(){const e=Object.keys(this.data[Object.keys(this.data)[0]]);[this.select1,this.select2].forEach((t=>{e.forEach((e=>{const n=document.createElement("option");n.value=e,n.textContent=e,t.appendChild(n)}))}))}_generateCompareTable(){const e=this.select1.value,t=this.select2.value;e&&t&&(this.container.innerHTML=`\n \n Close \n \n \n \n \n \n \n \n Service Name \n ${e} \n ${t} \n \n \n \n ${Object.entries(this.data).map((([n,a])=>`\n \n ${n} \n ${"yes"===a[e]?"✅":"❌"} \n ${"yes"===a[t]?"✅":"❌"} \n \n `)).join("")}\n \n
\n `,this.container.style.display="block",this._setupCloseButton())}_setupEventListeners(){[this.select1,this.select2].forEach((e=>{e?.addEventListener("change",(()=>this._generateCompareTable()))}))}_setupCloseButton(){this.container.querySelector("#close-compare")?.addEventListener("click",(()=>{this.container.style.display="none"}))}}class PricingManager{constructor(e){this.container=document.querySelector(`#${e}-table-container`)}generatePricingTable(e={}){if(!e.plans?.length)return void(this.container.innerHTML="Error: Invalid pricing data structure.
");const t=Object.keys(e.plans[0]).filter((e=>"name"!==e));this.container.innerHTML=`\n \n \n \n Plans \n ${t.map((e=>`${e} `)).join("")}\n \n \n \n ${e.plans.map((e=>`\n \n ${e.name} \n ${t.map((t=>`\n ${e[t]} \n `)).join("")}\n \n `)).join("")}\n \n
\n `}}document.addEventListener("DOMContentLoaded",(async()=>{const e=new TableManager("file-hosts-table-container","host-search-input","clear-host-search"),t=new TableManager("adult-hosts-table-container","adult-host-search-input","clear-adult-host-search"),n=new PricingManager("pricing");try{const[a,r,o]=await Promise.all([fetch("./json/file-hosts.json").then((e=>e.json())),fetch("./json/adult-hosts.json").then((e=>e.json())),fetch("./json/pricing.json").then((e=>e.json()))]);e.generateTable(a),t.generateTable(r),n.generatePricingTable(o),new ComparisonManager("compare-table-container","provider1","provider2",a)}catch(a){const r="Error loading data. Please try again later.
";e.container.innerHTML=r,t.container.innerHTML=r,n.container.innerHTML=r}})),document.addEventListener("DOMContentLoaded",(()=>{const e=document.getElementById("darkModeToggle"),t=localStorage.getItem("theme");t&&document.documentElement.setAttribute("data-theme",t),e.addEventListener("click",(()=>{const e="dark"===document.documentElement.getAttribute("data-theme")?"light":"dark";document.documentElement.setAttribute("data-theme",e),localStorage.setItem("theme",e)}))}));
\ No newline at end of file
diff --git a/docs/app.js b/docs/app.js
index c93efcd..282d536 100644
--- a/docs/app.js
+++ b/docs/app.js
@@ -3,261 +3,225 @@
* Handles data fetching, table generation, search functionality, and comparison features
*/
-// Class to handle table-related operations
+// Utility to debounce frequent events
+const debounce = (func, delay = 300) => {
+ let timeout;
+ return (...args) => {
+ clearTimeout(timeout);
+ timeout = setTimeout(() => func(...args), delay);
+ };
+};
+
+// Class to manage table operations
class TableManager {
- /**
- * @param {string} containerId - ID of the table container element
- * @param {string} searchInputId - ID of the search input element
- * @param {string} clearIconId - ID of the clear search icon element
- */
- constructor(containerId, searchInputId, clearIconId) {
- this.container = document.getElementById(containerId);
- this.searchInput = document.getElementById(searchInputId);
- this.clearIcon = document.getElementById(clearIconId);
- this.setupSearchFunctionality();
+ constructor(containerId, searchInputId, clearIconId) {
+ this.container = document.querySelector(`#${containerId}`);
+ this.searchInput = document.querySelector(`#${searchInputId}`);
+ this.clearIcon = document.querySelector(`#${clearIconId}`);
+ this._setupSearchFunctionality();
+ }
+
+ generateTable(data = {}) {
+ if (!Object.keys(data).length) {
+ this.container.innerHTML = 'No data available.
';
+ return;
}
-
- /**
- * 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 = 'No data available.
';
- return;
- }
-
- const providers = Object.keys(data[Object.keys(data)[0]]);
- const tableHTML = `
-
-
+
+ const providers = Object.keys(data[Object.keys(data)[0]]);
+ this.container.innerHTML = `
+
+
+
+ Service Name
+ ${providers.map(provider => `${provider} `).join('')}
+
+
+
+ ${Object.entries(data).map(([host, providerData]) => `
- Service Name
- ${providers.map(provider => `${provider} `).join('')}
+ ${host}
+ ${providers.map(provider => `
+
+ ${providerData[provider] === 'yes' ? '✅' : '❌'}
+
+ `).join('')}
-
-
- ${Object.entries(data).map(([host, providerData]) => `
-
- ${host}
- ${providers.map(provider => `
-
- ${providerData[provider] === 'yes' ? '✅' : '❌'}
-
- `).join('')}
-
- `).join('')}
-
-
- `;
-
- 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';
- });
+ `).join('')}
+
+
+ `;
+ }
+
+ _setupSearchFunctionality() {
+ const handleSearch = debounce(() => {
+ 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'));
+ });
+
+ this.searchInput?.addEventListener('input', handleSearch);
+ this.clearIcon?.addEventListener('click', () => {
+ this.searchInput.value = '';
+ this.clearIcon.style.display = 'none';
+ handleSearch();
+ });
+ }
+}
+
+// Class to manage comparison operations
+class ComparisonManager {
+ constructor(containerId, select1Id, select2Id, data) {
+ this.container = document.querySelector(`#${containerId}`);
+ this.select1 = document.querySelector(`#${select1Id}`);
+ this.select2 = document.querySelector(`#${select2Id}`);
+ this.data = data;
+
+ this._populateDropdowns();
+ this._setupEventListeners();
+ }
+
+ _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);
});
+ });
+ }
+
+ _generateCompareTable() {
+ const provider1 = this.select1.value;
+ const provider2 = this.select2.value;
+
+ if (!provider1 || !provider2) return;
+
+ this.container.innerHTML = `
+
+ Close
+
+
+
+
+
+
+
+ Service Name
+ ${provider1}
+ ${provider2}
+
+
+
+ ${Object.entries(this.data).map(([host, providerData]) => `
+
+ ${host}
+ ${providerData[provider1] === 'yes' ? '✅' : '❌'}
+ ${providerData[provider2] === 'yes' ? '✅' : '❌'}
+
+ `).join('')}
+
+
+ `;
+ this.container.style.display = 'block';
+ this._setupCloseButton();
+ }
+
+ _setupEventListeners() {
+ [this.select1, this.select2].forEach(select => {
+ select?.addEventListener('change', () => this._generateCompareTable());
+ });
+ }
+
+ _setupCloseButton() {
+ this.container.querySelector('#close-compare')?.addEventListener('click', () => {
+ this.container.style.display = 'none';
+ });
+ }
+}
+
+// Class to manage pricing operations
+class PricingManager {
+ constructor(containerId) {
+ this.container = document.querySelector(`#${containerId}-table-container`);
+ }
+
+ generatePricingTable(data = {}) {
+ if (!data.plans?.length) {
+ this.container.innerHTML = 'Error: Invalid pricing data structure.
';
+ return;
}
+
+ const services = Object.keys(data.plans[0]).filter(key => key !== 'name');
+ this.container.innerHTML = `
+
+
+
+ Plans
+ ${services.map(service => `${service} `).join('')}
+
+
+
+ ${data.plans.map(plan => `
+
+ ${plan.name}
+ ${services.map(service => `
+ ${plan[service]}
+ `).join('')}
+
+ `).join('')}
+
+
+ `;
+ }
+}
+
+// Main application initialization
+document.addEventListener('DOMContentLoaded', async () => {
+ 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 {
+ 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())
+ ]);
+
+ fileHostsTable.generateTable(fileHostsData);
+ adultHostsTable.generateTable(adultHostsData);
+ pricingManager.generatePricingTable(pricingData);
+
+ new ComparisonManager('compare-table-container', 'provider1', 'provider2', fileHostsData);
+ } catch (error) {
+ console.error('Error initializing application:', error);
+ const errorMessage = 'Error loading data. Please try again later.
';
+ fileHostsTable.container.innerHTML = errorMessage;
+ adultHostsTable.container.innerHTML = errorMessage;
+ pricingManager.container.innerHTML = errorMessage;
+ }
+});
+document.addEventListener('DOMContentLoaded', () => {
+ const darkModeToggle = document.getElementById('darkModeToggle');
+
+ // Check for saved theme preference
+ const savedTheme = localStorage.getItem('theme');
+ if (savedTheme) {
+ document.documentElement.setAttribute('data-theme', savedTheme);
}
- // 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]]);
+ darkModeToggle.addEventListener('click', () => {
+ const currentTheme = document.documentElement.getAttribute('data-theme');
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
- [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 = `
- X
-
-
-
- Service Name
- ${provider1}
- ${provider2}
-
-
-
- ${Object.entries(this.data).map(([host, providerData]) => `
-
- ${host}
- ${providerData[provider1] === 'yes' ? '✅' : '❌'}
- ${providerData[provider2] === 'yes' ? '✅' : '❌'}
-
- `).join('')}
-
-
- `;
-
- 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 = 'Error: Invalid pricing data structure.
';
- return;
- }
-
- const services = Object.keys(data.plans[0]).filter(key => key !== 'name');
- const tableHTML = `
-
-
-
- Plans
- ${services.map(service => `${service} `).join('')}
-
-
-
- ${data.plans.map(plan => `
-
- ${plan.name}
- ${services.map(service => `${plan[service]} `).join('')}
-
- `).join('')}
-
-
- `;
-
- 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 = 'Error loading data. Please try again later.
';
- fileHostsTable.container.innerHTML = errorMessage;
- adultHostsTable.container.innerHTML = errorMessage;
- pricingManager.container.innerHTML = errorMessage;
- }
- });
\ No newline at end of file
+ document.documentElement.setAttribute('data-theme', newTheme);
+ localStorage.setItem('theme', newTheme);
+ });
+});
\ No newline at end of file
diff --git a/docs/index.html b/docs/index.html
index bc3d2de..abebc70 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -4,11 +4,11 @@
-
-
-
+
+
+
Debrid Services Comparison
-
+
-
- Debrid Services Comparison
-
-
-
+
-
- A comparison of available hosts and pricing for AllDebrid , Real-Debrid , LinkSnappy ,
- Premiumize , Debrid-Link , TorBox and Mega-Debrid .
-
- Contents:
-
- Signing Up
- Referral Links
- If you are signing up for any above service, Please consider
- buying from my referral link:
-
- Non-referral Links
-
- Pricing
-
-
-
-
- [ * ] = 7 Days free trail at Alldebrid requires a phone
- verification.
-
- Up-to-date Pricing
- You can check pricing for each individual service at:
-
- Available Hosts
- File Hosts
-
-
- X
+
+
-
-
-
-
-
- Compare Providers
-
-
-
Provider 1:
-
+
+ Pricing
+
+
+
Loading pricing data...
-
- Provider 2:
-
-
-
-
- ×
-
-
+
+ [ * ] = 7 Days free trial at Alldebrid requires phone verification.
+
+
+ Up-to-date Pricing
+ For the most current pricing details, please visit the official websites:
+
+
+
-
Adult Hosts
-
-
-
-
+
+ Available Hosts
+
+ File Hosts
+
+
+
+
Loading file hosts...
+
+
+
+
+ Compare Providers
+
+
+ Provider 1:
+
+
+
+ Provider 2:
+
+
+
+
+
+
+
Loading comparison...
+
+
+
+
+ Adult Hosts
+
+
+
+
Loading adult hosts...
+
+
+
+
+ Live status
+ Track the most current host status updates on the official service pages:
+
+
+
-
Live status
-
Up-to-date list of hosts for individual services can tracked
- from:
-
-
-
-
Last updated: January 14, 2025
-
-