123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- //==============================================================================
- // BigPipe Module
- //==============================================================================
- BigPipe = (function() {
- "use strict";
- //==============================================================================
- // PhaseDoneJS: Responsible for Pagelet and Resource
- //==============================================================================
- const PhaseDoneJS = {
- //==============================================================================
- // Increase phase and execute callbacks
- //==============================================================================
- handler(context, phase) {
- for(let currentPhase = context.phase; currentPhase <= phase; ++currentPhase) {
- this.execute(context, currentPhase);
- }
- return context.phase = ++phase;
- },
- //==============================================================================
- // Execute callbacks of the given phase
- //==============================================================================
- execute(context, phase) {
- context.phaseDoneJS[phase].forEach(function(code) {
- try {
- window.eval.call(window, code);
- } catch(e) {
- console.error("PhaseDoneJS: " + e);
- }
- });
- }
- };
- //==============================================================================
- // Resource: Represents a resource
- //==============================================================================
- class Resource {
- constructor(data, type) {
- this.ID = data.ID;
- this.HREF = data.HREF;
- this.callbacks = [];
- this.node = false;
- this.done = false;
- this.type = type;
- this.phaseDoneJS = data.PHASE;
- this.phase = 0;
- PhaseDoneJS.handler(this, Resource.PHASE_INIT);
- }
- //==============================================================================
- // Resource types
- //==============================================================================
- static get TYPE_STYLESHEET() { return 0; }
- static get TYPE_JAVASCRIPT() { return 1; }
- //==============================================================================
- // Phase numbers for PhaseDoneJS
- //==============================================================================
- static get PHASE_INIT() { return 0; }
- static get PHASE_LOAD() { return 1; }
- static get PHASE_DONE() { return 2; }
- //==============================================================================
- // Loading the resource
- //==============================================================================
- execute() {
- switch(this.type) {
- case Resource.TYPE_STYLESHEET:
- this.node = document.createElement("link");
- this.node.setAttribute("rel", "stylesheet");
- this.node.setAttribute("href", this.HREF);
- break;
- case Resource.TYPE_JAVASCRIPT:
- this.node = document.createElement("script");
- this.node.setAttribute("src", this.HREF);
- this.node.setAttribute("async", "async");
- break;
- default:
- return false;
- }
- const callback = () => {
- PhaseDoneJS.handler(this, Resource.PHASE_DONE);
- this.executeCallbacks();
- };
- this.node.onload = callback;
- this.node.onerror = callback;
- document.head.appendChild(this.node);
- PhaseDoneJS.handler(this, Resource.PHASE_LOAD);
- }
- //==============================================================================
- // Register a new callback
- //==============================================================================
- registerCallback(callback) {
- return this.callbacks.push(callback);
- }
- //==============================================================================
- // Executes all registered callbacks
- //==============================================================================
- executeCallbacks() {
- if(!this.done && (this.done = true)) {
- this.callbacks.forEach(function(callback) {
- callback();
- });
- }
- }
- //==============================================================================
- // Remove callbacks after abort of loading the resource
- //==============================================================================
- abortLoading() {
- if(this.node) {
- this.node.onload = null;
- this.node.onerror = null;
- // Remove element from DOM
- let parentNode = this.node.parentNode;
- return parentNode.removeChild(this.node);
- }
- }
- }
- //==============================================================================
- // Pagelet: Represents a pagelet
- //==============================================================================
- class Pagelet {
- constructor(data, HTML) {
- this.ID = data.ID;
- this.NEED = data.NEED;
- this.HTML = HTML;
- this.JSCode = data.CODE;
- this.phaseDoneJS = data.PHASE;
- this.stylesheets = data.RSRC[Resource.TYPE_STYLESHEET];
- this.javascripts = data.RSRC[Resource.TYPE_JAVASCRIPT];
- this.phase = 0;
- this.resources = [[], []];
- PhaseDoneJS.handler(this, Pagelet.PHASE_INIT);
- }
- //==============================================================================
- // Phase numbers for PhaseDoneJS
- //==============================================================================
- static get PHASE_INIT() { return 0; }
- static get PHASE_LOADCSS() { return 1; }
- static get PHASE_HTML() { return 2; }
- static get PHASE_LOADJS() { return 3; }
- static get PHASE_DONE() { return 4; }
- //==============================================================================
- // Initialize and execute the CSS resources
- //==============================================================================
- execute() {
- this.initializeResources();
- if(!this.executeResources(Resource.TYPE_STYLESHEET)) {
- this.replaceHTML();
- }
- }
- //==============================================================================
- // Initialize the pagelet resources
- //==============================================================================
- initializeResources() {
- this.stylesheets.forEach(data => {
- this.attachResource(new Resource(data, Resource.TYPE_STYLESHEET));
- });
- this.javascripts.forEach(data => {
- this.attachResource(new Resource(data, Resource.TYPE_JAVASCRIPT));
- });
- }
- //==============================================================================
- // Executes all resources of the specific type
- //==============================================================================
- executeResources(type) {
- let somethingExecuted = false;
- this.resources[type].forEach(function(resource) {
- somethingExecuted = true;
- resource.execute();
- });
- return somethingExecuted;
- }
- //==============================================================================
- // Attach a new resource to the pagelet
- //==============================================================================
- attachResource(resource) {
- switch(resource.type) {
- case Resource.TYPE_STYLESHEET:
- resource.registerCallback(() => this.onStylesheetLoaded());
- break;
- case Resource.TYPE_JAVASCRIPT:
- resource.registerCallback(() => this.onJavascriptLoaded());
- break;
- }
- return this.resources[resource.type].push(resource);
- }
- //==============================================================================
- // Replaces the placeholder node HTML
- //==============================================================================
- replaceHTML() {
- document.getElementById(this.ID).innerHTML = this.HTML;
- PhaseDoneJS.handler(this, Pagelet.PHASE_HTML);
- BigPipe.onPageletHTMLreplaced(this.ID);
- }
- //==============================================================================
- // Executes the inline javascript code of the pagelet
- //==============================================================================
- executeInlineJavascript() {
- this.JSCode.forEach(code => {
- try {
- window.eval.call(window, code);
- } catch(e) {
- console.error(this.ID + ": " + e);
- }
- });
- PhaseDoneJS.handler(this, Pagelet.PHASE_DONE);
- }
- //==============================================================================
- // Executed each time when a stylesheet resource has been loaded
- //==============================================================================
- onStylesheetLoaded() {
- if(this.resources[Resource.TYPE_STYLESHEET].every(function(resource){
- return resource.done;
- })) {
- PhaseDoneJS.handler(this, Pagelet.PHASE_LOADCSS);
- this.replaceHTML();
- }
- }
- //==============================================================================
- // Executed each time when a javascript resource has been loaded
- //==============================================================================
- onJavascriptLoaded() {
- if(this.resources[Resource.TYPE_JAVASCRIPT].every(function(resource){
- return resource.done;
- })) {
- PhaseDoneJS.handler(this, Pagelet.PHASE_LOADJS);
- this.executeInlineJavascript();
- }
- }
- }
- //==============================================================================
- // BigPipe
- //==============================================================================
- const BigPipe = {
- pagelets: [],
- phase: 0,
- done: [],
- wait: [],
- interval: null,
- onPageletArrive(data, codeContainer) {
- let pageletHTML = codeContainer.innerHTML;
- pageletHTML = pageletHTML.substring(5, pageletHTML.length - 4);
- codeContainer.parentNode.removeChild(codeContainer);
- let pagelet = new Pagelet(data, pageletHTML);
- this.pagelets.push(pagelet);
- if(this.phase === 0) {
- this.phase = 1;
- }
- if(pagelet.NEED.length === 0 || pagelet.NEED.every(function(needID) {
- return BigPipe.done.indexOf(needID) !== -1;
- })) {
- pagelet.execute();
- }
- else {
- this.wait.push(pagelet);
- }
- },
- onLastPageletArrived() {
- this.phase = 2;
- this.interval = setInterval(() => {
- if(this.done.length === this.pagelets.length) {
- clearInterval(this.interval);
- this.executeJavascriptResources();
- }
- }, 50);
- },
- onPageletHTMLreplaced(pageletID) {
- BigPipe.done.push(pageletID);
- for(let i = 0; i < this.wait.length; ++i) {
- let pagelet = this.wait[i];
- // Check if all IDs from NEED exists within BigPipe.done
- // If this is true, then all required dependencies are satisfied.
- if(pagelet.NEED.every(function(needID){
- return BigPipe.done.indexOf(needID) !== -1;
- })) {
- BigPipe.wait.splice(i--, 1); // remove THIS pagelet from wait list
- pagelet.execute();
- }
- }
- },
- executeJavascriptResources() {
- this.phase = 3;
- this.pagelets.forEach(function(pagelet) {
- if(!pagelet.executeResources(Resource.TYPE_JAVASCRIPT)) {
- pagelet.onJavascriptLoaded();
- }
- });
- }
- };
- //==============================================================================
- // Public-Access
- //==============================================================================
- return {
- onPageletArrive(data, codeContainer) {
- BigPipe.onPageletArrive(data, codeContainer);
- },
- onLastPageletArrived() {
- BigPipe.onLastPageletArrived();
- },
- reset() {
- BigPipe.pagelets.forEach(function(pagelet) {
- pagelet.resources[Resource.TYPE_STYLESHEET].forEach(function(resource) {
- resource.abortLoading();
- });
- pagelet.resources[Resource.TYPE_JAVASCRIPT].forEach(function(resource) {
- resource.abortLoading();
- });
- });
- try {
- window.stop();
- } catch(e) {
- document.execCommand('Stop');
- }
- clearInterval(BigPipe.interval);
- BigPipe.pagelets = [];
- BigPipe.phase = 0;
- BigPipe.wait = [];
- BigPipe.done = [];
- }
- };
- })();
|