lockandload
Version: 
Minimalist AMD-compliant Javascript and CSS loader
244 lines (243 loc) • 9.03 kB
HTML
<html lang="en">
<head>
 <meta charset="utf-8" />
 <!-- The following <script> *must* stay at the top -->
 <!-- Cut BEGIN for lockandload_master.inc -->
 <script>			  // License: ISC OR GPL-3.0
((W,D,F,X,M,L,R)=>		 // lockandload v3.2: boilerplate AMD-loader
{ "use strict";			// Copyright (c) 2025 <srb@cuci.nl>
	   //
	  // Integrate GTM support to get the timing right
	 //
  W.dataLayer=[{"gtm.start":Date.now(),event:"gtm.js"}]; R[X]=R[M]=R;
       // DEBUG_START
  var reqcnt = 2;
     // DEBUG_END
    //
   // self-contained AMD define()
  //
  function E(n,d,f,j)
  { function c(k,i)
	//
       // Iterate through dependencies, count and postpone unresolved
      //
    { for (i of k||[]) if (L[i]||!R[i]) j++, (L[i]=L[i]||[]).push(c);
	       // DEBUG_START
      if (j > 1)
	console.log("Dependencies left(" + JSON.stringify(n) + ") = " + (j-1));
	    // DEBUG_END
      if (!--j)
	  //
	 // All dependencies have been resolved: in situ replacement
	//
      { R[X]=R[n]; for (i in d) d[i]=R[d[i]];
			     // DEBUG_START
	console.log("=============== Running(" + JSON.stringify(n) + ")");
			   // DEBUG_END
			  //
			 // Run factory exactly once.
			//  Support non-function factories.
		       //   Assume "exports" if the factory returns 0
		      //
	R[n]=(typeof f=="function"?f.apply(0,d):f)||R[X];
		    //
		   // Early release of factory function for garbage collection
		  //
	d=L[n]||[]; delete L[n];
		//
	       // Resolve dependents
	      //
	while (f=d.pop()) f();
      }
    }
	  //
	 // Restructure argument ordering
	//
    if (!f) if (f=d, d=0, !f) f=n, n=0; else if (Array.isArray(n)) d=n, n=0;
      //
     // Support anonymous definitions
    //
    if (!n)
    { n=D.currentScript.src.match(/([-\w_]+(?:\.[-\w_]+)*)\.js(?:[^\w]|$)/)[1];
      if (j=n.match(/(.*)\.min$/)) n=j[1];
    }
	    // DEBUG_START
    if (n === 1)
      n = reqcnt++;
    console.log("define(" + JSON.stringify(n) + "," + JSON.stringify(d) + ","
     + (typeof f == "function"?f.toString():JSON.stringify(f))
	.substr(0, 80).replace(/\s+/g, " ") + ")");
       // DEBUG_END
      //
     // Initialise exports, mark it as unresolved and off to the races
    //
    j=1; R[n]={}; L[n]=L[n]||[]; c(d=d||[F,X,M]);
  }
	    // AMD-loader compliance
  (W.define=E).amd={lockandload:E||"v3.2, Copyright (c) 2025 <srb@cuci.nl>"};
	  //
	 // Global require()
	//
  W[F]=(d,f)=>f?E(1,d,f):R[d];
      //
     // $$(on every SPA-page switch)
    //                            Run per definition if already triggered
  E.S=[0]; W.$$=(f)=>(E.S.push(f),E.S[0]&&f(1));
  //    $(on initial page load)
  E.Q=[]; W.$=(f)=>E.Q.push(f);
})(window,document,"require","exports","module",{},{});
 </script>
 <!-- Cut END for lockandload_master.inc -->
 <!-- All high priority Javascript files go here -->
 <script importance="high" src="main.js" async></script>
 <meta name="viewport" content="width=device-width,initial-scale=1" />
 <!-- All high priority CSS files go here -->
 <link rel="stylesheet" href="css/main.css" />
 <title>Lock and load</title>
 <meta name="description"
   value="Minimalist AMD-compliant Javascript and CSS loader" />
 <!-- This is where the you put all the other tags that go into <head> -->
 <!-- Cut BEGIN for lockandload_headready.inc -->
 <script>
   //
  // Provide local shortcuts to W=window, D=document, E=define
 // Declare a housekeeping variable P to store declared jsa() aliases.
//
((W,D,F,E,P)=>
{ "use strict";
     //
    // Add gtag() convenience function for gtag.js users.
   // Cannot use => function declaration syntax due to use of "arguments".
  //
  W.gtag=function(){ dataLayer.push(arguments); };
    //
   // Helper function to add new entries at the end of the <head>
  //
  function n(e,a,v,r,u,p,x,y,k)
  { k=D.createElement(e); if(p) k.onload=()=>E(p, 1);
    k.fetchPriority="low"; k[a]=v; k[r]=u; x||(k.crossOrigin="");
    for (x in y) k.setAttribute(x, y[x]);
    D.head.appendChild(k); }
      //
     // css(url): asynchronously load low priority CSS stylesheets
    //  css(url, id): same as above, but also fulfill dependency id after
   //		   the rules have been applied.
  //
  function css(u,p){ n("link","rel","stylesheet","href",u,p); }
         //
        // js(url): load Javascript files sequentially in the background
       //  js(url, "async"): load low priority Javascript files asynchronously
      //   js(url, "async", 1): like above, but without CORS (e.g. for GTM)
     //    js(url, "async", 0, {"data-attr1":"test"}): like the second, but
    //       with an object-parameter describing a number of attributes.
   //      We attempt to deduplicate against existing earlier entries.
  //
  function js(u,a,x,y)
  { if (!D.head.querySelector(`script[src="${u}"]`))
      n("script","async",a,"src",u,0,x,y); }
     //
    // jsa(alias, path); define aliases for javascript file paths
   //  to be referenced through require.load(alias) to load the file on demand
  //
  function jsa(a,p){ P[a]=p; }
  /* Cut END for lockandload_headready.inc */
  /*
  ** This is the start of your config area.  Customise to taste.
  **
  ** Define a bunch of pathname aliases to reference javascript files that
  ** shall be loaded through require.load(path) on demand.
  ** Specifying those here now avoids sprinkling pathnames throughout
  ** your other modules' source code:
  **jsa("lzstring", "js/lz-string.js");
  **
  ** Optional headready define, if you want it, uncomment the next line:
  **define("headready", 1);
  **
  ** Add low priority CSS files here.
  ** High priority CSS should use direct <link> entries instead if you
  ** do not need to fulfill a custom dependency for those files.
  ** CSS files you need to fulfill dependency 'id' for, must use scss(id, url)
  ** instead.
  **
  **var t="//cdn.remixml.org/css/";
  **css(t+"blue.css", "bluecssloaded");		// CSS fulfilling dependency
  **css(t+"sample.css");			// Low prio CSS
  **
  ** Add medium priority Javascript files here.
  ** Typically jQuery should be loaded here, if needed:
  **
  **require(["domready"], ()=> {
  **  js("//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js");
  **});
  **
  ** Executed when the DOM has been loaded completely.
  ** Typically the place to add remaining low priority Javascript files.
  ** High priority Javascript files should use direct script entries instead.
  ** If you do not use jQuery, take out the jQuery dependency from the line
  ** below:
  */
  require(["domready", "jQuery"], (domready)=> {
    /*var t="//cdn.remixml.org/js/";
    **js(t+"sequentialload.js");	// Legacy sequential low prio loads
    **js(t+"sequentialload2.js");	// Legacy sequential low prio loads
    **js(t+"sequentialload3.js");	// Legacy sequential low prio loads
    **js("lastjs.js");			// Fullfill lastjs dependency after
    **					// all js()s have been processed
    **js(t+"asynchronousload.js", "async");	// Low priority async
    */
    setTimeout(()=> {
	/*
	** Low priority non-interactive
	*/
	js("//www.googletagmanager.com/gtm.js?id=GTM-xxxxxx", "async", 1);
    }, 2048);
    /*
    ** Call domready(1) with a one as argument once from the startup scripts.
    ** Call domready() without arguments from your SPA after that on every
    ** page refresh.  Calling domready() before domready(1) has been called
    ** will silently ignore the domready() call.
    ** You can offer a limited scope for the domready() call by supplying
    ** an argument as in domready(scope).  The argument will be available
    ** to the scripts being run.  If you do not provide an argument, they
    ** will receive $(document) (the jQuery whole document object/scope).
    */
    domready(1);
  });
  /*
  ** This is where your config area ends.  Do not edit until after the
  ** end of the script
  */
  /* Cut BEGIN for lockandload_trailer.inc */
     //
    // Use the "jQuery" dependency instead of "jquery" to avoid a race
   // for an undefined W.$
  //
  E("jQuery", ["jquery"], ($, i)=>
  { W.jQuery=W.$=$; while (i=E.Q.shift()) $(i); return $; });
       //
      // Non-global require()
     //  require.undef(n) to unload a module prior to reloading it
    //   require.load(p, obj) to load a javascript file on demand
   //     asynchronously, with an optional obj describing extra attributes.
  //
  E(F, ["module"], (R)=>
   (W[F].undef=(n)=>R[n]=0, W[F].load=(p,y)=>js(P[p]||p, 1, 0, y), W[F]));
     //
    // Define "domready" dependency, it also returns the factory for running
   // the $$(...) scheduled functions: run it in the SPA after page switches.
  //
  W.onload=()=>E("domready", ["module"], (R)=> (t,i)=>
    { if(E.S[0]||t==1)
      { t=!(t==1?E.S.shift():t)&&R.jquery?$(D):t;
	for (i=0; i<E.S.length; E.S[i++](t)); }});
})(window,document,"require",define,{});
 </script>
 <!-- Cut END for lockandload_trailer.inc -->
 <!-- The preceding <script> *must* stay at the end of the <head> -->
</head>
<body>
 <h1>Minimalist AMD-compliant Javascript and CSS loader</h1>
 <p>Your glorious content</p>
</body>
</html>