Source: rsLIB.js

/*
 * File autoLoader for javascript Libraries (multi-scripts). 
 * copyright @ 2015 - Juan José Guerra Haba <dinertron@gmail.com>
 * License: Free. GPL.v3
 * 
 * 'rsLIB' o RSLIB is an acronym for 'require async library' and a scripts library autoloader. It loads scripts (js, json, ...) and 
 * even style sheets (CSS). Returns a 'rsLIB' object with a 'onComplete (..)' event to pass a callback function. You only need to use 
 * it if you do use a resource immediately (variable, method, object ...) from library.
 * An options object embedded within the label 'script' call to this charger is expected with the terms:
 *  - nameLIB: name of the library to load
 *  - pathToLIB: path (relative or absolute) to the library directory with respect to html caller.
 *  - scripts: An array with the path relative to each script file you want to load. The last element of the array will not be 
 *    charged, which is only indicates the main on which to launch the 'onComplete (..)' method.
 * The call in the HTML Head might be:
 * <script id="rsLIB_ID" class="rsLIB_CLASS" type="text/javascript" src="./scripts/rsLIB.js">
 *  {
 *    nameLIB:   "PolyArea",
 *    pathToLIB: "./scripts/PolyArea/",
 *    scripts:   ["./scripts/PolyArea/PolyArea.js",
 *                "./scripts/PolyArea/css/polyArea.css",
 *                "./scripts/PolyArea/PolyArea.js"] //The latter will not be loaded, only determines the event 'onComplete (..)'
 *  }
 * </script>
 * OOP Filosofy and Closures, ECMASCRIPT-6 features such as "use-strict"
 * IMPROVEMENTS: Although it uses internally 'eval' in a controlled manner on these updates will be studied how to replace 'eval' 
 * to 'JSON.parse' or 'JSON.stringify'
 */

/** 
  * <style class="style_for_all_comments_docs_in_documentation">
  *    .code_rsLIB{padding:4px; background:whiteSmoke; color:#333333; border-radius:8px; box-shadow:1px 1px 4px gray;}
  *    .mini_rsLIB{padding:4px; font-size:smaller; color:#333333; background:lemonChiffon;}
  * </style>
  * <title>'ASYNC REQUIRE LIBRARY' OBJECT.</title>
  * <p class="mini_rsLIB">ASYNC REQUIRE LIBRARY :: INSERT THE LINKED-SCRIPTS PARAMETERS IN THE HEAD SYNCHRONOUSLY, BUT WITHOUT AJAX.</p>
  * <p>It is the loader object (global ' <b>rsLIB</b> ' variable ) with an anonymous loader function and as members:</p>
  * <ul>
  *  <li><b>options</b>: the  complete object detected as embedded options.</li>
  *  <li><b>nameLIB</b>: name of library to load.</li>
  *  <li><b>pathToLIB</b>: the path to the library, based on the situation of <b>HTML</b> caller.</li>
  *  <li><b>scripts</b>: the array of scripts to load the library.</li>
  *  <li><b>get</b>: get properties of embedded object.</li>
  *  <li><b>isset</b>: utility method to check whether a variable is undefined.</li>
  *  <li><b>onComplete (..)</b>: The method that can be passed a ' <i>callback</i> ' function as the only parameter.</li>
  * </ul>
  * <p>Import dynamically (albeit unorthodox) all the library 'assets' , it inserting  (if not previously existed) after 
  * the label linked with this file (<i>rsLIB.js</i>) in the 'head'.  It work synchronously, that is, they must wait until 
  * they are loaded and interpreted to use them immediately.</p>
  * <p>If so required (immediately) because some variable or function defined in scripts is used, should work within 
  * the event <b>onComplete(..)</b> through a function <i>callback</i>, for example:</p>
  * <pre class="code code_rsLIB">
  *   <code>
  *   rsLIB.onComplete( function(){
  *       //code to execute when the scripts are loaded.
  *   } );
  *   </code>
  * </pre>
  */
var rsLIB = (function(){
  "use-strict";
  //Configurable static variables (SEMI-CONSTANT). It affects to elements IDs and classes, and labels DOM scripts 
  var RSLIB_NAME = "rsLIB";
  var RSLIB_FILE = RSLIB_NAME + ".js";
  var RSLIB_ID = RSLIB_NAME + "_ID";
  var RSLIB_CLASS = RSLIB_NAME + "_CLASS";
  //variables and inner private methods
  /** An embedded options object within the label 'script' is expected to pass to this loader with the terms:
    *  - nameLIB: name of the library to load
    *  - pathToLIB: path (relative or absolute) to the library directory with respect to HTML caller.
    *  - scripts: An array with the path relative to each script file you want to load. The last element of the array will not be 
    *    loaded, which is only indicates the main on which to launch the 'onComplete (..)' method. */
  var _options = eval("("+RSLIB_getThisScript().innerHTML+")"); //ATENCIÓN: DEBE SER UN OBJETO O NO SURTIRÁ EFECTO
  //var _options = JSON.parse((RSLIB_getThisScript().innerHTML));//JSON.stringify
  /** External function in the 'onComplete' event to host the code that need immediacy and well make sure the scripts are loaded. */
  var _callback=null;
  /** It is the couple of callback, depending on the state of the CALLBACK is _ready or do not. This is achieved with no gap between 
    * the charging phase of script and execution callback. */
  var _ready=false;
  /** Event employee for pass an external callback function to be executed upon completion of the loaded library.
    * This can be useful if, for example, you want to immediately use a resource (variable, object, method, ...)
    * defined in the library. This should be done within the 'callback' function as incorporating the various scripts 
    * in Head of a synchronous load page 'modifies the flow execution javascript ' occurs due to the DOM modification,  
    * it can (depending on the browser) run the last library scripts. */
  function RSLIB_onComplete(callback){
    /* ... Include here  the event or use externally as a callback() function; ... */
    _callback = callback;
    if(_ready){ _callback(); }
  }
  
  /** Public utility function to check if there is a variable, if it has been defined. You must pass the 'typeof' of the
    * Variable, not the variable itself. For example: alert (RSLIB.requireIsset (typeof unknown_variable)); */
  function RSLIB_isset(typeOfvar){ return (typeOfvar.toUpperCase() !== "UNDEFINED"); }
  /** Utility function to get object properties of embedded options. */
  function RSLIB_get(prop, defaultValue){
    return (_options && _options[prop]) ? _options[prop] : defaultValue;
  }
  /** Internal utility function to take as NODE object, this script file; */
  function RSLIB_getThisScript(){
    //DETECT THIS FILE (rsLIB.js) AS A SCRIPT-NODE LINKED IN THE HEAD
    //FIRST THINGS LOOK FOR YOUR ID
    var requireScript=document.getElementById(RSLIB_ID);
    if(requireScript){ return requireScript; }
    //IF NOT FOUND, LOOK FOR HIS CLASS
    var requireHead = (document.head || document.documentElement || document.getElementsByTagName("head")[0] || document.querySelector("head"));
    var requireScripts = requireHead.getElementsByTagName("script");
    var requireScript;
    for(var i=0; i<requireScripts.length; i++){
      if( requireScripts[i].className === RSLIB_CLASS ){ 
        requireScript=requireScripts[i];
        break;
      }
    }
    if(requireScript){ return requireScript; }
    //IF NOT FOUND, LOOKING FOR HIS FILE NAME
    for(var i=0; i<requireScripts.length; i++){
      if( requireScripts[i].src.indexOf(RSLIB_FILE) > -1 ){ 
        requireScript=requireScripts[i];
        break;
      }
    }
    return requireScript;
  }
  
  //DYNAMIC LOAD FOR HEAD-HTML SCRIPTS. THE DETECTED OPTIONS IN SCRIPT-NODE CALLER FOR THIS FILE
  (function RSLIB_loadSyncScripts (options){
    //IT CHECKS BASIC OPTIONS AND IT ASSIGNS DEFAULT PROPERTY (IF NOT FOUND)
    options["scripts"]=RSLIB_get("scripts", []);
    options["nameLIB"]=RSLIB_get("nameLIB", "");
    options["pathToLIB"]=RSLIB_get("pathToLIB", "./");
     //THE LAST ELEMENT DOES NOT LOAD, BUT IT DEFINED IN 'onComplete()' EVENT.
    var _scriptCompleted = options.scripts.pop();
    options.scripts = options.scripts.reverse(); //THE LAST SHALL BE FIRST IN THE KINGDOM OF GOD
    //FOR EACH SCRIPT
    for(var i=0; i<options.scripts.length; i++){
      if(!options.scripts[i]) { continue; }
      var tipo= options.scripts[i].split(".").pop();
      var nombreExt= (options.scripts[i].indexOf("\\")>-1) ? options.scripts[i].split("\\").pop() : options.scripts[i].split("/").pop();
      nombreExt = nombreExt.replace("\\", "-").replace("/", "-");
      //BUILD SCRIPT
      var nodoScript;
      switch(tipo.toLowerCase()){
        case "css":
          nodoScript = document.createElement("link");
          nodoScript.setAttribute("type", "text/css"); //setAttribute("type", "text/css");
          nodoScript.setAttribute("rel", "stylesheet");
          nodoScript.setAttribute("media", "screen");
          nodoScript.setAttribute("href", options.scripts[i]);
          break;
        case "js": default: //also "json"
          nodoScript = document.createElement("script");
          nodoScript.setAttribute("type", "text/javascript");
          nodoScript.setAttribute("src", options.scripts[i]);
      }
      nodoScript.setAttribute("id", nombreExt);
      nodoScript.setAttribute("class", RSLIB_NAME + "_" + options.nameLIB);
      if(options.scripts[i] === _scriptCompleted){
        if (nodoScript.readyState) { // IE
          nodoScript.onreadystatechange = function () {
              if (nodoScript.readyState === 'loaded' || nodoScript.readyState === 'complete') {
                nodoScript.onreadystatechange = null;
                if(_callback){ 
                  _callback();  //onComplete();
                } else {
                  _ready=true;
                }
              }
            };
        } else { // Others
          nodoScript.onload = function() {
              if(_callback){ 
                _callback();  //onComplete();
              } else {
                _ready=true;
              }
            };
        }
      }
      //THIS FILE IS DETECTED (zLIB.js) AS A SCRIPT-NODE LINKED IN SECTION HTML-HEAD
      var requireHead = (document.head || document.documentElement || document.getElementsByTagName("head")[0] || document.querySelector("head"));
      var requireScripts = requireHead.getElementsByTagName("script");
      var requireScript;
      var insertar = true;
      for(var j=0; j<requireScripts.length; j++){
        if(requireScripts[j].src === options.scripts[i]) {
          insertar = false;
          break;
        }
        if( (requireScripts[j].id === RSLIB_ID) || (requireScripts[j].className === RSLIB_CLASS) || (requireScripts[j].src.indexOf(RSLIB_FILE) > -1) ){ 
          requireScript=requireScripts[j];
          //break;
        }
      }
      //IT IS INSERTED (if not previously existed) AFTER THIS LINKED FILE (rsLIB.js) IN THE SECTION HTML-HEAD
      if(insertar) { requireHead.insertBefore(nodoScript, requireScript.nextSibling); }
    }
     //IS REPOSITIONED AS THEY WERE
    options.scripts = options.scripts.reverse();
    //IT MAKES TRAILS DELETED FOR ALL CONFIGURATION OPTIONS
    RSLIB_getThisScript().innerHTML="";
  })(_options);
  
  //PUBLIC API
  return {
    /** <title>EMBEDDED OPTIONS.</title>
      * <p>An embedded options object within the label ' <b>script</b> ' is expected to pass to this loader with the terms:</p>
      * <ul>
      *  <li><b>nameLIB</b>: name of the library to load</li>
      *  <li><b>pathToLIB</b>: path (relative or absolute) to the library directory with respect to HTML caller.</li>
      *  <li><b>scripts</b>: An array with the path relative to each script file you want to load. The last element of the array will not be 
      *    loaded, which is only indicates the main on which to launch the ' <b>onComplete (..)</b> ' method.</li>
      * </ul> */
    options: _options,
    /** <title>NAME LIB.</title> Name of library to load */
    nameLIB: RSLIB_get("nameLIB", ""),
    /** <title>PATH TO LIB.</title> Path to the library directory with respect to <b>HTML</b> caller. */
    pathToLIB: RSLIB_get("pathToLIB", "./"),
    /** <title>SCRIPTS TO LOAD.</title> Scripts to dynamic load for the loader function. They should contain the relative path.
     * <p class="mini_rsLIB">WARNING: THE LAST ELEMENT NOT BE CHARGED, WHICH IS ONLY INDICATES THE MAIN ON WHICH TO LAUNCH THE ' <b>onComplete(..)</b> ' EVENT.</p> */
    scripts: RSLIB_get("scripts", []),
    /** <title>GET OPTION.</title> Utility function to get a property of embedded options object. */
    get: RSLIB_get,
    /** <title>IS SET OPTION.</title> Public utility function to check if there is a variable, if it has been defined. You must pass the ' <b>typeof</b> ' of the
    * Variable, not the variable itself. For example: <code class="code_rsLIB">alert (RSLIB.requireIsset (typeof unknown_variable));</code> */
    isset : RSLIB_isset,
    /** <title>ON-COMPLETE EVENT.</title> 
      * <p>Event employee for pass an external <i>callback</i> function to be executed upon completion of the loaded library.<br />
      * This can be useful if, for example, you want to immediately use a resource (variable, object, method, ...) defined in the library.</p>
      * <p>This should be done within the ' <i>callback</i> ' function as incorporating the various scripts in <b>HTML-Head</b> of a synchronous 
      * load page ' <cite>modifies the flow execution javascript</cite> ' occurs due to the <b>DOM</b> modification, it can (depending on the 
      * browser) run the last library scripts.</p> */
    onComplete: RSLIB_onComplete
  }
})();

//document.normalize();