import { DynamicCSSInclude } from "./DynamicCSSInclude.js";
import { DynamicJSInclude } from "./DynamicJSInclude.js";
/**
* @class DynamicDepsLoader
*
* @description
* Responsibilities:
* > looks at the DOM and find the component's dependencies
* > loads the dependencies and caches them in the LocalStorage
*
*
* HOW IT WORKS
* ------------
*
* The dependecies are inserted into the component's markup
* as data- attribute
*
* single JS dependency
* --------------------
*
*
*
* double (and nested) JS dependencies
* -----------------------------------
* in this case the second js is loaded and launched after the
* execution of the first one
*
*
*
* deferred JS dependencies
* ------------------------
*
*
* they work the same as the other dependencies but they are
* loaded just after the first scroll.
*
*
* single CSS dependency
* ---------------------
*
*
* multiple CSS dependencies: in this case add more than one dependency
* in priority order. ex:
*
*
*
* NOTE: we could add other feature like cache expiration times
*
* @param {dictionary} args { jsdir, cssdir }
*/
export class DynamicDepsLoader {
constructor( args = {
// set defaults
jsdir : '/js/components/',
cssdir : '/css/components/'
}) {
this.base_url = `${window.location.protocol}//${window.location.host}`;
/// write the properties
this.js_dir = args.jsdir;
this.css_dir = `${this.base_url}${args.cssdir}`;
this.js_dep_selector = '[data-js-dependency]';
this.def_js_dep_selector = '[data-js-deferred-dependency]';
this.css_dep_selector = '[data-css-dependency]';
/// feature detection IntersectionObserver
this.fallback = ( typeof window.IntersectionObserver == 'undefined' );
}
scanDependencies() {
// load dependecies
if(this.fallback)
this.FallbackLoadDeps();
else
this.getDeps();
}
scanJS() {
return this.scan( this.js_dep_selector, 'jsDependency', this.js_dir );
};
scanCSS() {
return this.scan( this.css_dep_selector, 'cssDependency', this.css_dir );
}
/**
* Scan the markup to find js deferred deps
*/
scanDeferredJS() {
return this.scan( this.def_js_dep_selector, 'jsDeferredDependency', this.js_dir );
}
/**
* Takes the dependencies
* and istance them all
*/
getDeps() {
// scan components and find dependencies
this.js_deps = this.scanJS();
this.def_deps = this.scanDeferredJS();
this.css_deps = this.scanCSS();
// defer dependencies loading after the first scroll
this.firstScrollTrigger( () => {
new DynamicJSInclude( this.def_deps );
} );
// appends js and styles
return {
jsinc : new DynamicJSInclude( this.js_deps ),
cssinc : new DynamicCSSInclude( this.css_deps)
};
}
/**
* Scan the markup to find deps
*/
scan( selector, datafield, prefix) {
// find the elements with dependencies
let elements = [].map.call(document.querySelectorAll( selector ), i => i );
// extract the dependencies
return elements.reduce( ( dependencies, element ) => {
let deps = element.dataset[ datafield ].split(',')
/// remove empty spaces
.map( dep => dep.trim() )
/// remove empty deps
.filter( dep => dep )
/// add prefix
.map( dep => prefix + dep )
/// remove already taken deps
.filter( dep => !dependencies.flat().includes( dep ) );
// remove dependency attribute
delete element.dataset[ datafield ];
if( deps.length ) dependencies.push( deps );
return dependencies;
}, [] );
}
/**
* set the trigger to
* sense the first scroll and executes the callback
*/
firstScrollTrigger( callback ) {
/// create the trigger element
var trigger = document.createElement('span');
trigger.style.position = 'absolute';
trigger.style.display = 'block';
trigger.style.top = ( window.innerHeight + 10 ) + 'px';
trigger.style.left = '0';
trigger.style.width = '1px';
trigger.style.height = '1px';
trigger.style.backgroundColor = 'transparent';
/// put the trigger on the page
document.body.style.position = 'relative';
document.body.appendChild(trigger);
/// setup the observer
var observer = new IntersectionObserver( function (entries, observer) {
entries.forEach( function( entry ) {
if( entry.isIntersecting ) {
/// call the callback
callback();
/// stop observing
observer.unobserve(trigger);
}
}.bind(this));
}.bind(this));
/// start observing
observer.observe(trigger);
}
}