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