use crate::core::component::Context;
use crate::html::assets::Asset;
use crate::html::{html, Markup, PreEscaped};
use crate::{join, join_pair, AutoDefault, Weight};
// Define el origen del recurso JavaScript y cómo debe cargarse en el navegador.
//
// Los distintos modos de carga permiten optimizar el rendimiento y controlar el comportamiento del
// script en relación con el análisis del documento HTML y la ejecución del resto de scripts.
//
// - [`From`] – Carga estándar con la etiqueta ``. El parámetro `name` se usa como identificador interno del
/// script.
///
/// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
pub fn inline(name: impl Into, f: F) -> Self
where
F: Fn(&mut Context) -> String + Send + Sync + 'static,
{
JavaScript {
source: Source::Inline(name.into(), Box::new(f)),
..Default::default()
}
}
/// Crea un **script embebido** que se ejecuta cuando **el DOM está listo**.
///
/// El código se envuelve en un `addEventListener('DOMContentLoaded',function(){...})` que lo
/// ejecuta tras analizar el documento HTML, **no** espera imágenes ni otros recursos externos.
/// Útil para inicializaciones que no dependen de `await`. El parámetro `name` se usa como
/// identificador interno del script.
///
/// Los scripts con `defer` se ejecutan antes de `DOMContentLoaded`.
///
/// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
pub fn on_load(name: impl Into, f: F) -> Self
where
F: Fn(&mut Context) -> String + Send + Sync + 'static,
{
JavaScript {
source: Source::OnLoad(name.into(), Box::new(f)),
..Default::default()
}
}
/// Crea un **script embebido** con un **manejador asíncrono**.
///
/// El código se envuelve en un `addEventListener('DOMContentLoaded',async()=>{...})`, que
/// emplea una función `async` para que el cuerpo devuelto por la función *closure* pueda usar
/// `await`. Ideal para hidratar la interfaz, cargar módulos dinámicos o realizar lecturas
/// iniciales.
///
/// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
pub fn on_load_async(name: impl Into, f: F) -> Self
where
F: Fn(&mut Context) -> String + Send + Sync + 'static,
{
JavaScript {
source: Source::OnLoadAsync(name.into(), Box::new(f)),
..Default::default()
}
}
// **< JavaScript BUILDER >*********************************************************************
/// Asocia una **versión** al recurso (usada para control de la caché del navegador).
///
/// Si `version` está vacío, **no** se añade ningún parámetro a la URL.
pub fn with_version(mut self, version: impl Into) -> Self {
self.version = version.into();
self
}
/// Modifica el **peso** del recurso.
///
/// Los recursos se renderizan de menor a mayor peso. Por defecto es `0`, que respeta el orden
/// de creación.
pub fn with_weight(mut self, value: Weight) -> Self {
self.weight = value;
self
}
}
impl Asset for JavaScript {
/// Devuelve el nombre del recurso, utilizado como clave única.
///
/// Para scripts externos es la ruta del recurso; para scripts embebidos, un identificador.
fn name(&self) -> &str {
match &self.source {
Source::From(path) => path,
Source::Defer(path) => path,
Source::Async(path) => path,
Source::Inline(name, _) => name,
Source::OnLoad(name, _) => name,
Source::OnLoadAsync(name, _) => name,
}
}
fn weight(&self) -> Weight {
self.weight
}
// **< JavaScript RENDER >**********************************************************************
fn render(&self, cx: &mut Context) -> Markup {
match &self.source {
Source::From(path) => html! {
script src=(join_pair!(path, "?v=", self.version.as_str())) {};
},
Source::Defer(path) => html! {
script src=(join_pair!(path, "?v=", self.version.as_str())) defer {};
},
Source::Async(path) => html! {
script src=(join_pair!(path, "?v=", self.version.as_str())) async {};
},
Source::Inline(_, f) => html! {
script { (PreEscaped((f)(cx))) };
},
Source::OnLoad(_, f) => html! { script { (PreEscaped(join!(
"document.addEventListener(\"DOMContentLoaded\",function(){", (f)(cx), "});"
))) } },
Source::OnLoadAsync(_, f) => html! { script { (PreEscaped(join!(
"document.addEventListener(\"DOMContentLoaded\",async()=>{", (f)(cx), "});"
))) } },
}
}
}