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), "});" ))) } }, } } }