Google's DolphinGemma: AI Unlocks the Secrets of Dolphin Communication
1s1t5w
In a groundbreaking intersection of artificial intelligence and marine biology, Google has unveiled DolphinGemma, an advanced AI model designed to an…
More →
CCTV video shows moment of Russian drone strike in Kharkiv, Ukraine
5o1le
Seventeen people were wounded in a Russian drone strike on the eastern Ukrainian city of Kharkiv early Thursday, including children, a pregnant woman…
More →
Reaction to recovery of bodies of two Israeli-American hostages from Gaza
2o494c
Israel has recovered the bodies of two Israeli-American hostages taken in Hamas’ October 7, 2023, attack that ignited the war in the Gaza Strip.
More →
Trump announces travel ban and restrictions on 19 countries
a4v63
President Donald Trump has signed a travel ban on 12 countries, primarily in Africa and the Middle East, from coming to America.
More →
Trump suspends entry of international students studying at Harvard
244h6x
US President Donald Trump suspended for an initial six months the entry into the United States of foreign nationals seeking to study or participate i…
More →
The BTS effect: K-beauty brands target US despite tariffs
2x6131
On the back of the popularity for cultural exports such as K-Pop and their roaring online sales through Amazon, South Korea's cosmetic startups a…
More →
Zelenskiy suggests truce until meeting with Putin can be arranged
3c3d2r
Ukrainian President Volodymyr Zelenskiy proposed 'a ceasefire until the leaders meet,' adding that Kyiv would 'be grateful' for suppo…
More →
Ukraine releases video of its drone attack on Russia war planes
2n6j5g
Ukraine's security service released footage Wednesday of its attack on Russian war planes at multiple airbases across the country. In the span of…
More →
EU 'strongly regrets' US steel tariffs, says talks ongoing
425z45
European Union trade commissioner Maros Sefcovic said that he strongly regretted the US decision to levy 50% tariffs on steel and aluminum and that i…
More →
`;
container.appendChild(feedSection);
const targetElement = document.getElementById(feedConfig.targetElementId);
if (!targetElement) {
console.error(`Elemento target no encontrado: ${feedConfig.targetElementId}`);
const errorDiv = document.createElement('div');
errorDiv.className = 'error-rss';
errorDiv.innerHTML = `Error de configuración: Contenedor para "${sanitizeHtml(feedConfig.title)}" no encontrado.`;
// Adjuntar al feedSection en lugar de al container principal para errores por feed
feedSection.querySelector('.rss-items-wrapper').innerHTML = errorDiv.outerHTML;
return;
}
try {
const apiUrl = `https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(feedConfig.url)}`;
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`Error de red/servidor (${response.status}) al ar rss2json para ${sanitizeHtml(feedConfig.title)}.`);
}
const data = await response.json();
if (data.status !== 'ok') {
// Intenta mostrar el mensaje de error de rss2json si está disponible
const apiErrorMessage = data.message ? `API Error: ${data.message}` : `Error desconocido de rss2json`;
throw new Error(`${apiErrorMessage} para ${sanitizeHtml(feedConfig.title)}.`);
}
if (!data.items || data.items.length === 0) {
targetElement.innerHTML = '';
return;
}
targetElement.innerHTML = ''; // Limpiar "Cargando"
const items = data.items.slice(0, maxItemsPerFeed);
items.forEach((item, index) => {
const title = sanitizeHtml(item.title || 'Sin título');
const link = item.link || '#';
const rawSnippet = item.description || item.content || ''; // rss2json pone el contenido en description
const cleanSnippet = sanitizeHtml(rawSnippet);
const truncatedSnippet = truncateText(cleanSnippet, snippetMaxLength);
const imageUrl = extractImageUrl(item); // Usa la función modificada
const itemDiv = document.createElement('div');
itemDiv.className = 'rss-item';
// onerror para imagen: intenta cargar el placeholder si la imagen principal falla
const imageOnErrorScript = `this.onerror=null; this.src='${placeholderImageUrl}'; this.classList.add('image-load-error');`;
// Si quieres el layout de "1 grande, 2 pequeños" DENTRO de CADA feed
// necesitarías modificar la lógica de cómo se añaden los items aquí.
// Por ahora, mantendré la lógica original de este script que parece ser:
// - El primer item (.featured-item) se añade directamente.
// - Los siguientes items se agrupan en un .small-item-container con .small-item.
// Esta lógica ya estaba en tu script, la he mantenido.
let itemHTML = `
`;
// La lógica de featured-item y small-item-container ya estaba en tu script.
// Si quieres el layout 1 grande, 2 pequeños, 1 grande, 2 pequeños etc.
// esta lógica debería funcionar para el primer grupo (1 grande, N-1 pequeños).
// Si maxItemsPerFeed es 3, tendrás 1 grande y 2 pequeños.
if (index === 0 && maxItemsPerFeed > 1) { // Solo si hay más de 1 item, el primero es "featured"
itemDiv.classList.add('featured-rss-item'); // Renombrada para evitar colisión con AFCM
itemDiv.innerHTML = itemHTML;
targetElement.appendChild(itemDiv);
} else {
// Agrupar los siguientes en un contenedor si no existe ya para este feed
let smallItemsPairContainer = targetElement.querySelector('.secondary-rss-items-pair');
if (!smallItemsPairContainer) {
smallItemsPairContainer = document.createElement('div');
smallItemsPairContainer.className = 'secondary-rss-items-pair'; // Contenedor para pares
targetElement.appendChild(smallItemsPairContainer);
}
const secondaryItemDiv = document.createElement('div');
secondaryItemDiv.className = 'rss-item secondary-rss-item'; // Ítem individual del par
secondaryItemDiv.innerHTML = itemHTML;
smallItemsPairContainer.appendChild(secondaryItemDiv);
}
});
} catch (error) {
console.error(`Error al obtener o procesar el feed "${sanitizeHtml(feedConfig.title)}":`, error.message, error);
targetElement.innerHTML = ``;
}
}
if (mainWidgetContainer) {
if (feedsConfig && feedsConfig.length > 0) {
feedsConfig.forEach((config) => { // No es necesario `async` aquí en el forEach si `fetchAndDisplayFeed` es `async`
fetchAndDisplayFeed(config, mainWidgetContainer); // No esperamos aquí, dejamos que se ejecuten en paralelo
});
// El separador
se añade después de que todos los fetchAndDisplayFeed se hayan INICIADO,
// no necesariamente completado. Si el orden es crítico, se necesitaría un enfoque diferente.
// Para simplificar y asegurar que el
se añade después de cada sección de feed:
// (Esta parte del separador es un poco compleja de hacer bien con async/await en un forEach,
// la forma original de añadirlo podría ser más robusta si el orden importa visualmente)
// Por ahora, lo eliminamos de aquí para no complicar y lo gestionamos con CSS (gap en #dual-rss)
} else {
mainWidgetContainer.innerHTML = "";
}
} else {
console.error("Contenedor principal del widget 'dual-rss' no encontrado.");
}
});
//]]>
Follow us on social networks! 5s334x