|
async function loadFragments() { |
|
|
|
const fragmentElements = Array.from(document.querySelectorAll('[id^="fragment-"]')); |
|
|
|
class FetchQueue { |
|
constructor(maxConcurrent = 3) { |
|
this.queue = []; |
|
this.maxConcurrent = maxConcurrent; |
|
this.activeFetches = 0; |
|
this.maxRetries = 3; |
|
this.baseDelay = 1000; |
|
} |
|
|
|
async sleep(ms) { |
|
return new Promise(resolve => setTimeout(resolve, ms)); |
|
} |
|
|
|
async fetchWithRetry(fragmentPath, retryCount = 0) { |
|
try { |
|
const response = await fetch(fragmentPath); |
|
if (!response.ok) { |
|
throw new Error(`HTTP error! status: ${response.status}`); |
|
} |
|
return await response.text(); |
|
} catch (error) { |
|
if (retryCount < this.maxRetries) { |
|
|
|
const delay = this.baseDelay * Math.pow(2, retryCount); |
|
console.warn(`Retry ${retryCount + 1}/${this.maxRetries} for ${fragmentPath} after ${delay}ms`); |
|
await this.sleep(delay); |
|
return this.fetchWithRetry(fragmentPath, retryCount + 1); |
|
} |
|
throw error; |
|
} |
|
} |
|
|
|
async addFetch(element) { |
|
const fragmentName = element.id.replace('fragment-', ''); |
|
const fragmentPath = `fragments/${fragmentName}.html`; |
|
|
|
return new Promise(async (resolve, reject) => { |
|
try { |
|
const fetchPromise = (async () => { |
|
try { |
|
const html = await this.fetchWithRetry(fragmentPath); |
|
|
|
|
|
const temp = document.createElement('div'); |
|
temp.innerHTML = html; |
|
element.innerHTML = temp.innerHTML; |
|
|
|
|
|
const scripts = temp.getElementsByTagName('script'); |
|
Array.from(scripts).forEach(oldScript => { |
|
const newScript = document.createElement('script'); |
|
Array.from(oldScript.attributes).forEach(attr => { |
|
newScript.setAttribute(attr.name, attr.value); |
|
}); |
|
newScript.textContent = oldScript.textContent; |
|
oldScript.parentNode.removeChild(oldScript); |
|
document.body.appendChild(newScript); |
|
}); |
|
|
|
this.activeFetches--; |
|
resolve(); |
|
} catch (error) { |
|
console.error(`Failed to load fragment ${fragmentPath} after ${this.maxRetries} retries:`, error); |
|
this.activeFetches--; |
|
reject(error); |
|
} |
|
})(); |
|
|
|
this.queue.push(fetchPromise); |
|
this.activeFetches++; |
|
} catch (error) { |
|
reject(error); |
|
} |
|
}); |
|
} |
|
|
|
async processNext(element) { |
|
if (this.activeFetches < this.maxConcurrent && element) { |
|
await this.addFetch(element); |
|
} |
|
} |
|
} |
|
|
|
|
|
const fetchQueue = new FetchQueue(3); |
|
let currentIndex = 0; |
|
const elements = fragmentElements; |
|
|
|
|
|
while (currentIndex < elements.length && currentIndex < 3) { |
|
await fetchQueue.processNext(elements[currentIndex]); |
|
currentIndex++; |
|
} |
|
|
|
|
|
while (currentIndex < elements.length) { |
|
|
|
await Promise.race(fetchQueue.queue); |
|
|
|
fetchQueue.queue = fetchQueue.queue.filter(p => p.status === 'pending'); |
|
|
|
await fetchQueue.processNext(elements[currentIndex]); |
|
currentIndex++; |
|
} |
|
|
|
|
|
await Promise.all(fetchQueue.queue); |
|
} |
|
|
|
export { loadFragments } |