1
2// noinspection ES6ConvertVarToLetConst
3window.combos = (function (module) {
4
5
6    let cloneScriptElement = function (element) {
7        let clone = document.createElement(element.localName);
8        clone.text = element.text
9        Array.from(element.attributes).forEach(attr => clone.setAttribute(attr.name, attr.value));
10        return clone;
11    }
12
13    let executeInlineScript = function (targetElement) {
14        let inlineScriptElements = Array.from(targetElement.querySelectorAll("script:not([src])"));
15        let executionsError = [];
16        for (const scriptElement of inlineScriptElements) {
17            try {
18                eval(scriptElement.text);
19            } catch (e) {
20                executionsError.push({"error": e, "element": scriptElement});
21            }
22        }
23        if (executionsError.length > 0) {
24            let msg = "";
25            for (const error of executionsError) {
26                msg += `Railbar error: the script element (${error.element.className}) returns the following error ${error.error.message}\n`;
27            }
28            throw Error(msg);
29        }
30    }
31
32    module.html = {
33
34        /**
35         * Load a html fragment and executes the script inside if any
36         * @param htmlFragment
37         * @param targetElement
38         */
39        "loadFragment": function (htmlFragment, targetElement) {
40
41            // Trim to never return a text node of whitespace as the result
42            targetElement.insertAdjacentHTML('beforeend', htmlFragment.trim());
43
44            // Execute the src scripts first (the inline script may depend on it)
45            let srcScriptElements = Array.from(targetElement.querySelectorAll("script[src]"));
46
47            // no src script, load all inline and return
48            if (srcScriptElements.length === 0) {
49                executeInlineScript(targetElement);
50                return;
51            }
52
53            // We have src script, we need to load all of them, then load all inline and return
54
55            // The loader manager job is to known when all src script have loaded (script with an url)
56            //
57            // It knows how many script need to be loaded and decrease a counter when it happens.
58            // When the counter is null, it will dispatch the all-module-loaded event that will trigger
59            // the execution of inline script
60            let allModuleLoadedEventType = 'all-module-loaded';
61            const allModuleLoaded = new Event(allModuleLoadedEventType);
62            let loaderManager = (function (scriptToLoadCount) {
63                let loadModule = {};
64                let moduleToLoad = scriptToLoadCount;
65                // this function is called back when a script src is loaded
66                loadModule.decrease = function () {
67                    moduleToLoad--;
68                    if (moduleToLoad <= 0) {
69                        targetElement.dispatchEvent(allModuleLoaded);
70                    }
71                }
72                return loadModule;
73            })(srcScriptElements.length);
74
75            // Load all srcScript Element and add the decrease function has callback
76            for (const scriptElement of srcScriptElements) {
77                let clone = cloneScriptElement(scriptElement);
78                // noinspection JSCheckFunctionSignatures
79                scriptElement.parentNode.replaceChild(clone, scriptElement);
80                clone.addEventListener("load", loaderManager.decrease)
81            }
82
83            // Evaluate all inline scripts when all src script have loaded
84            // (ie the loader manager dispatch this event when all scripts have loaded)
85            targetElement.addEventListener(allModuleLoadedEventType, function () {
86                executeInlineScript(targetElement);
87            })
88
89        }
90    }
91    return module;
92})(window.combos || {});
93