AnP

AnPScriptsLoader.ecma.js

El AnPScriptsLoader es clase almacenada en la librería con el mismo nombre dentro de los Scripts ECMAScript/JavaScript que nos permite hacer una gestión controlada y dinámica de los recursos que queramos consumir para la aplicación que queremos desarrollar. Esta librería se basa en la creación dinámica de los elementos HTML que se encargan de la carga de Scripts y estilos CSS, SCRIPT y LINK consecutivamente. Una vez tiene todo cargado ejecuta el Callback que da inicio a la aplicación AnP.

AnPScriptsLoader
AnPScriptsLoaderglobalpublicAnPScriptsLoaderRequiredNullishObject<String, Any|null>|String|Integer|BooleaninputsRequiredFunctionCallbackcallback
NameRequiredNullableTypedDefault Value
inputsTrueTrueObject<String, Any|null>|String|Integer|Boolean
callbackTrueFalseFunctionCallback
NameRequiredNullableTypedDefault Value
FunctionCallback
voidglobalpublicFunctionCallback
NameRequiredNullableTypedDefault Value
NameRequiredNullableTypedDefault Value

Todo el proceso de carga de los ficheros viene siendo un proceso asíncrono gestionado mediante Callbacks, evitando que se bloquee el hilo de proceso del Script mediante la utilización de los eventos de las etiquetas HTML SCRIPT y LINK tanto de "onload" como "onerror". Dicha asincronicidad se basaría en dos procesos anidados uno dentro de otro donde el primero determina el nivel y el segundo los ficheros a cargar en dicho nivel, permitiendo con ello gestionar las dependencias en cargad e una forma adecuada. Un ejemplo de orden de carga por niveles donde cada nivel irá sucesivo al siguiente pero los archivos que éste posea se cargarán conjuntamente independientemente del orden sería el siguiente:

  1. Nivel 1
    • https://errorsmanager.k3y.pw/ecma/ErrorsManager.ecma.js
    • https://cdn.k3y.pw/data/scripts/Highlight.v11.10.0.min.js
    • https://wmarkdown.k3y.pw/ecma/WMarkDown.ecma.js
    • https://cdn.k3y.pw/data/scripts/Highlight.v11.10.0.min.js
    • https://cdn.k3y.pw/data/scripts/tex-mml-chtml.v3.2.2.js
    • https://cdn.k3y.pw/data/scripts/mermaid.v10.9.1.min.js
  2. Nivel 2
    • /ecma/Application/AnP.ecma.js
  3. Nivel 3
    • /ecma/Managers/Settings.ecma.js
    • /ecma/Application/URI.ecma.js
    • /ecma/Managers/Globals.ecma.js
    • /ecma/Managers/I18N.ecma.js
    • /ecma/Managers/PrintTypes.ecma.js
    • /ecma/Managers/Threads.ecma.js
    • /ecma/Application/Attributes.ecma.js
    • /ecma/Application/HTMLPreload.ecma.js
  4. Nivel 4
    • /ecma/Components/Base.ecma.js
Este mismo sistema puede aplicarse perfectamente a los estilos.

Esta librería está diseñada principalmente para la carga del Framework AnP y proyectos heredados que puedan pertenecer al mismo ecosistema, pero eso no quita que pueda ser usado para cargar asíncronamente otros conjuntos de librerías. Para poder iniciar un proyecto a partir de la carga de dichos ficheros se puede hacer de la siguiente manera:

  • typehtml
  • characters2843
  • lines60
<!DOCTYPE html>
<html lang="es">
    <head>
        <title>Test - AnPScriptsLoader</title>
        <meta http-equiv="content-type" content="text/html;charset=utf-8" />
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />

        <script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="https://anp.k3y.pw/ecma/AnPScriptsLoader.ecma.js" data-crossorigin="anonymous" charset="utf-8"></script>

        <script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" charset="utf-8">

            /** @type {AnPScriptsLoader} */
            new AnPScriptsLoader({
                styles : [
                    "https://cdn.k3y.pw/css/Roboto.v30.local.css", 
                    "https://cdn.k3y.pw/css/RobotoMono.v23.local.css", 
                    "https://cdn.k3y.pw/css/FA6F.v6.5.2.local.css", 
                    "https://anp.k3y.pw/scss/AnPWeb.css", 
                    "https://wmarkdown.k3y.pw/css/WMarkDown.icons.FontAwesome.css", 
                    "https://wmarkdown.k3y.pw/css/WMarkDown.web.icons.css", 
                    "https://wmarkdown.k3y.pw/scss/WMarkDown.scss", 
                    "https://cdn.k3y.pw/data/styles/Highlight.v11.10.0.min.css"
                ], 
                scripts : [[
                    "https://errorsmanager.k3y.pw/ecma/ErrorsManager.ecma.js", 
                    "https://cdn.k3y.pw/data/scripts/Highlight.v11.10.0.min.js", 
                    "https://wmarkdown.k3y.pw/ecma/WMarkDown.ecma.js", 
                    "https://cdn.k3y.pw/data/scripts/tex-mml-chtml.v3.2.2.js", 
                    "https://cdn.k3y.pw/data/scripts/mermaid.v10.9.1.min.js"
                ], [
                    "https://anp.k3y.pw/ecma/Application/AnP.ecma.js"
                ], [
                    "https://anp.k3y.pw/ecma/Managers/Settings.ecma.js", 
                    "https://anp.k3y.pw/ecma/Application/URI.ecma.js", 
                    "https://anp.k3y.pw/ecma/Managers/Globals.ecma.js", 
                    "https://anp.k3y.pw/ecma/Managers/I18N.ecma.js", 
                    "https://anp.k3y.pw/ecma/Managers/PrintTypes.ecma.js", 
                    "https://anp.k3y.pw/ecma/Managers/Threads.ecma.js", 
                    "https://anp.k3y.pw/ecma/Application/Attributes.ecma.js", 
                    "https://anp.k3y.pw/ecma/Application/HTMLPreload.ecma.js"
                ], [
                    "/ecma/Components/Base.ecma.js"
                ]]
            }, () => {

                /** @type {WMarkDown} */
                const wmarkdown = new WMarkDown({dictionary : "https://wmarkdown.k3y.pw/json/WMarkDown.dict.es.kyman.json"});

                // TODO
            });

        </script>

    </head>
    <body>
        <!-- TODO -->
    </body>
</html>

Por otro lado, tenemos la opción de hacer una carga dinámica y asíncrona completamente bajo ECMAScript/JavaScript de la siguiente manera:

  • typejs
  • characters576
  • lines25
"use strict";

/** @type {number} */
const preload = setInterval(() => {

    /** @type {HTMLHeadElement|null} */
    const head = document.querySelector("head");

    if(!head)
        return;
    clearInterval(preload);

    /** @type {HTMLScriptElement} */
    const script = head.appendChild(document.createElement("script"));

    script.setAttribute("https://anp.k3y.pw/ecma/AnPScriptsLoader.ecma.js");
    script.onload = () => new AnPScriptsLoader({
        mode : "wmarkdown", 
        styles : ["/scss/AnPWeb.css"]
    }, () => {
        // TODO
    });

}, 100);

Este método dinámico de creación del objeto está diseñado principalmente para casos de entornos de desarrollo donde el dominio es dependiente del entorno de pruebas o el entorno de producción, entre otras posibilidades que puedan surgir dependiendo del servidor, como el hecho de que el AnP esté SelfHosteado. Al entenderse que este Script se ejecuta sobre el HTML de forma directa, éste pueda resumirse de la siguiente forma:

  • typehtml
  • characters1269
  • lines36
<!DOCTYPE html>
<html lang="es">
    <head>
        <title>Test - AnPScriptsLoader</title>
        <meta http-equiv="content-type" content="text/html;charset=utf-8" />
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />

        <script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" charset="utf-8">

            /** @type {string} */
            const domain = window.location.href.match(/^[^\:]+\:\/{2}([^\/]+)/)[1], 
                  /** @type {HTMLScriptElement} */
                  script = document.querySelector("head").appendChild(document.createElement("script"));

            script.setAttribute("https://anp." + domain + "/ecma/AnPScriptsLoader.ecma.js");
            script.onload = () => new AnPScriptsLoader({
                domain : domain, 
                mode : "wmarkdown", 
                styles : ["/scss/AnPWeb.css"]
            }, () => {

                /** @type {WMarkDown} */
                const wmarkdown = new WMarkDown({dictionary : "https://wmarkdown." + domain + "/json/WMarkDown.dict.es.kyman.json"});

                // TODO
            });

        </script>

    </head>
    <body>
        <!-- TODO -->
    </body>
</html>

Este es el motivo por el cual puedan existir proyectos que contengan dichas estructuras para hacer control del dominio entre otras cosas.

Parámetros de entrada

La creación del objeto AnPScriptsLoader que gestiona dicha precarga asíncrona tiene los siguientes parámetros de configuración los cuales se integran dentro de un diccionario:

  • domain (String = k3y.pw): Determina el dominio del cual se cargan Scripts como los del AnP, WMarkDown, ErrorsManager, etc.
  • mode (String): Determina el modo de trabajo para determinar los Scripts y estilos por defecto los cuales adjuntar.
  • scripts (Array): Determina los Scripts a cargar.
  • styles (Array): Determina los estilos a cargar.
  • allow_print (Boolean = true): Verifica si se permite la impresión de los resultados de carga en la consola del navegador o no.
  • maximum_tryes (Integer = 5): Determina el número de intentos máximos para cargar un archivo antes de desestimar su carga.
Estos parámetros de entrada en el diccionario pueden ser ampliados y utilizados en los Links de Scripts y estilos para definir elementos concretos. Ésto se analizará más adelante.
A continuación se definirán ciertos conceptos de la entrada de parámetros pero no todos pues se definirán en otro punto más adelante.

El dominio (domain) se define como parámetro para que los archivos por defecto imlpementados en el modo (mode) de trabajo se determine el origen delos ficheros por el hecho de que éstos pueden estar en entorno local, como los ficheros del propio AnP por si el desarrollador o entidad tienen en Self Hosting dichos contenidos o proyectos. Si no se define, por defecto será k3y.pw.

Por otro lado, la librería está diseñada para avisar vía Consola del Navegador del estado de las cargas de los ficheros, pero es un elemento opcional que por defecto está habilitado y está definido en el parámetro allow_print, donde podemos definirlo como false para deshabilitarlo.

Finalmente, es importante entender que los ficheros pueden dar problemas a la hora de ser cargados por varios motivos, algunos de ellos pueden ser los siguientes:

  • El ancho de banda del servidor está limitado y un exceso de peticiones puede retornar una respuesta con error HTTP.
  • Al realizarse múltiples peticiones simultáneas desde el mismo sitio puede dar como respuesta por parte del servidor una limitación de respuestas sin error, ya sea para proteger al propio servidor de un posible ataque DDOS o por limitaciones técnicas del mismo.
  • La propia aplicación de servicio de servidor Web puede estar limitada a un consumo limitado de peticiones simultáneas limitando sus respuestas sin error.

Por este motivo existe la posibilidad de reintentar la carga de los ficheros mediante el parámetro maximum_tryes, el cual está configurado a un máximo de 5 intentos por fichero, pero dicho valor puede ser alterado implementando dicho parámetro en la entrada de parámetros de la librería.

Es importante tener en cuenta que las peticiones pueden tener un retraso elevado, sobretodo si se espera con un Timeout muy elevado, por lo que implementar una alta cantidad de intentos puede relentizar enormemente la carga de la Web por culpa de ciertos recursos ahí establecidos.
Gestión de ficheros y modos

Los ficheros que se gestionan en esta librería son meros Scripts ECMAScript/JavaScript y ficheros de estilos CSS. La carga de dichos ficheros se basa en la creación de un elemento en la cabecera HTML de tipo SCRIPT en el caso de los Scripts ECMAScript/JavaScript; y en el caso de los archivos CSS con la etiqueta HTML LINK. Dichas etiquetas contienen una serie de atributos para definir dichos ficheros, las cuales serían las siguientes:

  • typehtml
  • characters839
  • lines13

<!-- Etiqueta para un archivo SASS/SCSS. -->
<link type="text/css;charset=utf-8" data-language="SASS/CSS3" rel="stylesheet" href="ARCHIVO.css" data-scss="ARCHIVO.scss" data-css-map="ARCHIVO.css.map" data-crossorigin="anonymous" charset="utf-8" />

<!-- Etiqueta para un archivo CSS. -->
<link type="text/css;charset=utf-8" data-language="CSS3" rel="stylesheet" href="ARCHIVO.css" data-crossorigin="anonymous" charset="utf-8" />

<!-- Etiqueta para un archivo ECMAScript. -->
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="ARCHIVO.ecma.js" data-crossorigin="anonymous" charset="utf-8"></script>

<!-- Etiqueta para un archivo JavaScript. -->
<script data-type="text/javascript;charset=utf-8" data-language="JavaScript 1.8.5" src="ARCHIVO.js" data-crossorigin="anonymous" charset="utf-8"></script>

Las etiquetas están expuestas a vulnerabilidad pues les faltan atributos como integrity para determinar que el fichero cargado es el original, entre otras posibilidades, pero por dificultades a la hora de definirlo de una forma simplista se optó por la omisión de dicho parámetro, lo mismo que aislar mediante el prefijo data- el atributo crossorigin.
Los atributos data-type y data-language vienen del estándar antiguo de HTML4 y el antiguo XHTML, hablamos de la época del motor Trident MSIE6 del Internet Explorer, donde se establecían ciertos atributos informativos para la ayuda a interpretar los contenidos, sobretodo siendo una época donde había ciertas capacidades de los navegadores a interpretar otros lenguajes. Estas etiquetas las guardamos de forma simbólica mediante el prefijo data-, pero son totalmente omitibles, pero forzados internamente por defecto por la librería.

Como podemos observar por la construcción de las etiquetas, esta librería distingue los archivos de estilos compilados SASS de los archivo no compilados íntegramente CSS, lo mismo que los archivos de Scripts ECMAScript de los íntegramente JavaScript. En todos estos casos se distinguen internamente por la extensión utilizada en dichos ficheros. De esta forma tendríamos la siguiente relación en base a su extensión:

  • .scss: Sería para definir archivos compilados mediante SASS. Los atributos data-scss y data-css-map son simplemente informativos a desarrolladores y curiosos suponiendo que se usen compiladores como RubySass y de esta forma, ser lo más transparente posible con los recursos utilizados en el proyecto.
  • .css: Sería para definir archivos íntegramente desarrollados en CSS puro.
  • .ecma.js: Sería para definir archivos que no sean puramente JavaScript, teniendo elementos que pertenezcan al estándar de ECMA antes que de JavaScript clásico.
  • .js: Sería para definir un Script JavaScript íntegramente con esta filosofía.
La diferencia que se destaca aquí entre el JavaScript y el ECMAScript es la sintaxis y recursos utilizados pues el ECMAScript está más definido a entornos NodeJS o equivalentes, y el JavaScript para el navegador principalmente, aunque eso no quita que parte de los estándarse de JavaScript para navegadores admitan recursos de NodeJS entre otros.
AnPScriptsLoader.prototype.FILES

Sabiendo ya dichas propiedades de los ficheros entramos en la propiedad anteriormente mencionada la cual viene siendo la distribución de los ficheros mediante niveles según hereden los unos de los otros. Para empezar, hemos de aclarar si usamos los modos de trabajo para hacer uso de AnP, éstos pueden condicionar el nivel en el cual establecer nuestros ficheros. Para entender esto mejor, a continuación se expone esta constante.

AnPScriptsLoader.prototype.FILES
Object<String, Object<String, Array<Array<String>>>>objectpublicAnPScriptsLoader.prototype.FILES
NameRequiredNullableTypedDefault Value
NameRequiredNullableTypedDefault Value
Se pone constante en cursiva para indicar que sólo es informativo puesto que a la hora de definir una propiedad de un tipado basado en una función éstas no poseen dichas propiedades estáticas en ECMASCript/JavaScript.
  • typejs
  • characters2293
  • lines62
"use strict";

/** @type {Object.<string, Object.<string, Array.<Array.<string>>>>} */
AnPScriptsLoader.prototype.FILES = {
    settings : {
        scripts : [[
            "https://errorsmanager.{domain}/ecma/ErrorsManager.ecma.js"
        ], [
            "https://anp.{domain}/ecma/Application/AnP.ecma.js"
        ], [
            "https://anp.{domain}/ecma/Managers/Settings.ecma.js", 
            "https://anp.{domain}/ecma/Application/URI.ecma.js", 
            "https://anp.{domain}/ecma/Managers/Globals.ecma.js"
        ]]
    }, 
    i18n : {
        dependences : ["settings"], 
        scripts : [[], [], [
            "https://anp.{domain}/ecma/Managers/I18N.ecma.js"
        ]]
    }, 
    core : {
        dependences : ["i18n"], 
        scripts : [[], [], [
            "https://anp.{domain}/ecma/Managers/PrintTypes.ecma.js", 
            "https://anp.{domain}/ecma/Managers/Threads.ecma.js"
        ]]
    }, 
    gui : {
        dependences : ["core"], 
        styles : [[
            "https://anp.{domain}/scss/AnP.scss", 
            "https://anp.{domain}/css/AnP.icons.css"
        ]], 
        scripts : [[], [], [
            "https://anp.{domain}/ecma/Application/Attributes.ecma.js", 
            "https://anp.{domain}/ecma/Application/HTMLPreload.ecma.js"
        ], [
            "https://anp.{domain}/ecma/Components/Base.ecma.js"
        ]]
    }, 
    wmarkdown : {
        dependences : ["gui.scripts"], 
        styles : [[
            "https://cdn.{domain}/css/Roboto.v30.local.css", 
            "https://cdn.{domain}/css/RobotoMono.v23.local.css", 
            "https://cdn.{domain}/css/FA6F.v6.5.2.local.css", 
            "https://wmarkdown.{domain}/css/WMarkDown.icons.FontAwesome.css", 
            "https://wmarkdown.{domain}/css/WMarkDown.web.icons.css", 
            "https://wmarkdown.{domain}/scss/WMarkDown.scss", 
            "https://cdn.{domain}/data/styles/Highlight.v11.10.0.min.css"
        ]], 
        scripts : [[
            "https://cdn.{domain}/data/scripts/Highlight.v11.10.0.min.js", 
            "https://cdn.{domain}/data/scripts/tex-mml-chtml.v3.2.2.js", 
            "https://cdn.{domain}/data/scripts/mermaid.v10.9.1.min.js"
        ], [
            "https://wmarkdown.{domain}/ecma/WMarkDown.ecma.js"
        ]]
    }
};

Las URL de los ficheros vemos que tienen claves encapsuladas entre llaves, en este caso {domain}. Esto indica una variable dentro del texto, y se resolverá con respecto a lo que tenga la entrada de valores del objeto que gestiona la carga: el AnPScriptsLoader. En este caso, el parámetro domain se puede especificar directamente en la entrada o éste quedaría por defecto como k3y.pw. Se pueden usar variables con cualquier clave, pero éstas han de estar definidas en la entrada de parámetros, de lo contrario, aparecerán tal cual se dejan, pudiendo ocasionar respuestas inesperadas.

Como podemos observar, aquí tenemos definidos los modos de la carga de los ficheros los cuales pueden ser alterados o ampliados mediante el uso del diccionario que contiene AnPScriptsLoader.prototype.FILES. Cada modo es otro diccionario el cual posee las siguientes claves:

  • dependences: Nos indica si éste hereda de uno o varios otros modos, añadiendo los ficheros que éste posee al conjunto de los otros, en el orden de niveles establecido.
  • styles: Este elemento contienen todos los archivos de estilos ordenados por niveles.
  • scripts: Este elemento contienen todos los archivos de Scripts ordenados por niveles.

Los niveles vienen siendo un Array bidimensional donde su Array anidado es un nivel con todos los archivos que éste posee; y el Array maestro contiene todos los niveles de ficheros. Si nosotros queremos agregar un fichero al segundo nivel, primero hemos de indicar el primer nivel, aunque éste esté vacío para establecer dónde implementarlo cara sus dependencias. Si no tiene dependencias en el caso de agregar elementos en el segundo o mayor nivel, estando el o los primeros niveles vacíos, éstos se ignorarán ante la carga, pero existirán por la posibilidad de que dicho modo pueda ser usado como dependencia de otro.

No se habla del orden en el que se meten los archivos dentro de un nivel porque se entiende que la dependencia es con otros ficheros no pertenencientes a este nivel, sino a niveles anteriores.
El primer nivel será siempre para archivos no dependientes de otros.
En caso de haber más niveles de los que quiero definir en un modo en sus dependencias, los niveles que excedan de los definidos no es necesario establecerlos. Véase el modo wmarkdown el cual se sitúa al final.

Cada modo está orientado a una función concreta las cuales son:

  • settings: Este modo es para cargar únicamente el AnP para uso básico de elementos estáticos y la gestión de configuración y carga de ficheros.
  • i18n: Este modo amplía el modo settings con la capacidad de poder gestionar también textos internacionalizados.
  • core: Este modo habilita todas las capacidades del AnP en modo Core, es decir, todas aquellas librerías que no trabajen de forma directa el GUI y puedan ser implementadas en entornos de texto, terminal, consola o simple apoyo Background.
  • gui: Este modo habilita todas las capacidades Core y de GUI del AnP.
  • wmarkdown: Este modo habilita todas las capacidades del Core y del GUI de AnP conjunto con todas las capacidades Web cliente del WMarkDown para páginas Web y gestión de contenidos mediante este lenguage de marcas.