From 7a49346f8069d814c51af2e0cefcc6e66fe9826e Mon Sep 17 00:00:00 2001 From: Thomas Lange Date: Fri, 22 Apr 2016 21:17:53 +0200 Subject: Multiple updates with async demo. --- async.php | 144 ++++++++++ include/classes/BigPipe/BigPipe.php | 26 +- include/classes/BigPipe/Pagelet.php | 56 ++-- include/functions.php | 22 +- index.php | 176 ++++++------ output-flushing-test.php | 22 +- static/bigpipe.js | 544 ++++++++++++++++++------------------ static/blue.php | 32 +-- static/delayJS.php | 36 +-- static/green.php | 32 +-- static/red.php | 32 +-- 11 files changed, 634 insertions(+), 488 deletions(-) create mode 100755 async.php diff --git a/async.php b/async.php new file mode 100755 index 0000000..9093c87 --- /dev/null +++ b/async.php @@ -0,0 +1,144 @@ +>> [Additional code for the async function] + +#=============================================================================== +# FUNCTION: Return TRUE if the request awaiting a async response +#=============================================================================== +function isAsyncRequest() { + return isset($_GET['response']) AND $_GET['response'] === 'async'; +} + +# <<< + +#=============================================================================== +# Deactivate caching +#=============================================================================== +header('Cache-Control: no-cache, no-store, must-revalidate'); + +#=============================================================================== +# Include classes and functions +#=============================================================================== +require_once 'include/classes/BigPipe/BigPipe.php'; +require_once 'include/classes/BigPipe/Pagelet.php'; +require_once 'include/classes/BigPipe/DemoPagelet.php'; +require_once 'include/functions.php'; + +#=============================================================================== +# Check if BigPipe should be disabled +#=============================================================================== +if(isset($_GET['bigpipe']) AND (int) $_GET['bigpipe'] === 0) { + // You can also check for search spiders and disable the pipeline + BigPipe\BigPipe::enablePipeline(FALSE); +} + +#=============================================================================== +# Pagelet with red background color +#=============================================================================== +$PageletRed = new BigPipe\DemoPagelet(); +$PageletRed->addHTML('
['.time().'] I AM A PAGELET WITH RED BACKGROUND
'); +$PageletRed->addCSS('static/red.php'); +$PageletRed->addJS('static/delayJS.php'); +$PageletRed->addJSCode("document.getElementById('red').innerHTML += ' [JS executed]';document.getElementById('red').style.borderRadius = '30px';"); + +#=============================================================================== +# Pagelet with blue background color +#=============================================================================== +$PageletBlue = new BigPipe\DemoPagelet('customPageletID', BigPipe\Pagelet::PRIORITY_HIGH); +$PageletBlue->addHTML('
['.time().'] I AM A PAGELET WITH BLUE BACKGROUND
'); +$PageletBlue->addCSS('static/blue.php'); +$PageletBlue->addJS('static/delayJS.php'); +$PageletBlue->addJSCode("document.getElementById('blue').innerHTML += ' [JS executed]';document.getElementById('blue').style.borderRadius = '30px';"); + +#=============================================================================== +# Pagelet with green background color +#=============================================================================== +$PageletGreen = new BigPipe\DemoPagelet(); +$PageletGreen->addHTML('
['.time().'] I AM A PAGELET WITH GREEN BACKGROUND
'); +$PageletGreen->addCSS('static/green.php'); +$PageletGreen->addJS('static/delayJS.php'); +$PageletGreen->addJSCode("document.getElementById('green').innerHTML += ' [JS executed]';document.getElementById('green').style.borderRadius = '30px';"); +?> + + + + + + + + + + BigPipe Demo + + + + + + + +

BigPipe Async Demo

+ +

LOAD CONTENT VIA TRANSPORT FRAME [Current Time: – So you can see, that the page does not get completely reloaded]

+

Look at the developer console of your browser to see the debug messages and how the async response from server looks.

+ +
+ + + +
+ + +["Application","BigPipe"].forEach(function(name){window[name] = parent[name];});'."\n"; + echo ''."\n"; + echo ''."\n\n"; +} + +BigPipe\BigPipe::render(); +?> + + \ No newline at end of file diff --git a/include/classes/BigPipe/BigPipe.php b/include/classes/BigPipe/BigPipe.php index 740d047..63cb711 100755 --- a/include/classes/BigPipe/BigPipe.php +++ b/include/classes/BigPipe/BigPipe.php @@ -7,31 +7,31 @@ class BigPipe { private static $pagelets = []; private static $count = 0; - #==================================================================================================== + #=============================================================================== # Return TRUE if the pipeline is enabled - #==================================================================================================== + #=============================================================================== public static function isEnabled() { return self::$enabled; } - #==================================================================================================== + #=============================================================================== # Enable or disable the pipeline mode - #==================================================================================================== + #=============================================================================== public static function enablePipeline($enabled = TRUE) { return self::$enabled = (bool) $enabled; } - #==================================================================================================== + #=============================================================================== # Add a new pagelet to pipeline - #==================================================================================================== + #=============================================================================== public static function addPagelet(Pagelet $Pagelet, $priority) { self::$pagelets[$priority][] = $Pagelet; return ++self::$count; } - #==================================================================================================== + #=============================================================================== # Prints a single pagelet response - #==================================================================================================== + #=============================================================================== private static function singleResponse(Pagelet $Pagelet, $last = FALSE) { $data = [ 'ID' => $Pagelet->getID(), @@ -47,19 +47,19 @@ class BigPipe { $pageletJSON = json_encode($data, (self::$debug ? JSON_PRETTY_PRINT : FALSE)); echo "getID()}\">\n"; - echo "\n\n"; + echo "\n\n"; } - #==================================================================================================== + #=============================================================================== # Sends output buffer so far as possible towards user - #==================================================================================================== + #=============================================================================== public static function flushOutputBuffer() { ob_flush(); flush(); } - #==================================================================================================== + #=============================================================================== # Render the pagelets - #==================================================================================================== + #=============================================================================== public static function render() { self::flushOutputBuffer(); diff --git a/include/classes/BigPipe/Pagelet.php b/include/classes/BigPipe/Pagelet.php index 3b5530c..3a6c64e 100755 --- a/include/classes/BigPipe/Pagelet.php +++ b/include/classes/BigPipe/Pagelet.php @@ -10,18 +10,18 @@ class Pagelet { private $phaseDoneJS = []; private static $count = 0; - #==================================================================================================== + #=============================================================================== # Priorities for sorting the pagelets - #==================================================================================================== + #=============================================================================== const PRIORITY_HIGHEST = 100; const PRIORITY_HIGH = 75; const PRIORITY_NORMAL = 50; const PRIORITY_LOW = 25; const PRIORITY_LOWEST = 0; - #==================================================================================================== + #=============================================================================== # Callback phase numbers for PhaseDoneJS - #==================================================================================================== + #=============================================================================== const PHASE_ARRIVE = 0; # After the pagelet reached BigPipe const PHASE_LOADCSS = 1; # After all the CSS resources have been loaded const PHASE_PUTHTML = 2; # After the HTML content has been injected into the placeholders @@ -35,86 +35,86 @@ class Pagelet { BigPipe::addPagelet($this, $priority); } - #==================================================================================================== + #=============================================================================== # Return the unique ID - #==================================================================================================== + #=============================================================================== public function getID() { return $this->ID; } - #==================================================================================================== + #=============================================================================== # Return the HTML content - #==================================================================================================== + #=============================================================================== public function getHTML() { return $this->HTML; } - #==================================================================================================== + #=============================================================================== # Return the CSS resources - #==================================================================================================== + #=============================================================================== public function getCSSFiles() { return $this->CSSFiles; } - #==================================================================================================== + #=============================================================================== # Return the JS resources - #==================================================================================================== + #=============================================================================== public function getJSFiles() { return $this->JSFiles; } - #==================================================================================================== + #=============================================================================== # Return the main JS code - #==================================================================================================== + #=============================================================================== public function getJSCode() { return $this->JSCode; } - #==================================================================================================== + #=============================================================================== # Add HTML or attach more - #==================================================================================================== + #=============================================================================== public function addHTML($HTML) { return $this->HTML .= $HTML; } - #==================================================================================================== + #=============================================================================== # Attach a CSS resource - #==================================================================================================== + #=============================================================================== public function addCSS($file) { return $this->CSSFiles[] = $file; } - #==================================================================================================== + #=============================================================================== # Attach a JS resource - #==================================================================================================== + #=============================================================================== public function addJS($file) { return $this->JSFiles[] = $file; } - #==================================================================================================== + #=============================================================================== # Add JS code or attach more - #==================================================================================================== + #=============================================================================== public function addJSCode($code) { return $this->JSCode .= $code; } - #==================================================================================================== + #=============================================================================== # Attach a PhaseDoneJS callback - #==================================================================================================== + #=============================================================================== public function addPhaseDoneJS($phase, $callback) { return $this->phaseDoneJS[$phase][] = removeLineBreaksAndTabs($callback); } - #==================================================================================================== + #=============================================================================== # Return all registered PhaseDoneJS callbacks - #==================================================================================================== + #=============================================================================== public function getPhaseDoneJS() { return $this->phaseDoneJS; } - #==================================================================================================== + #=============================================================================== # Magic method: __toString() - #==================================================================================================== + #=============================================================================== public function __toString() { return '
'.((!BigPipe::isEnabled()) ? $this->getHTML() : NULL).'
'; } diff --git a/include/functions.php b/include/functions.php index 21eb3f0..d06823d 100755 --- a/include/functions.php +++ b/include/functions.php @@ -1,12 +1,12 @@ - \ No newline at end of file diff --git a/index.php b/index.php index 3386d5e..1eae51e 100755 --- a/index.php +++ b/index.php @@ -1,88 +1,90 @@ -addHTML('
I AM A PAGELET WITH RED BACKGROUND
'); -$PageletRed->addCSS('static/red.php'); -$PageletRed->addJS('static/delayJS.php'); -$PageletRed->addJSCode("document.getElementById('red').innerHTML += ' [JS executed]';document.getElementById('red').style.borderRadius = '30px';"); - -#==================================================================================================== -# Pagelet with blue background color -#==================================================================================================== -$PageletBlue = new BigPipe\DemoPagelet('customPageletID', BigPipe\Pagelet::PRIORITY_HIGH); -$PageletBlue->addHTML('
I AM A PAGELET WITH BLUE BACKGROUND
'); -$PageletBlue->addCSS('static/blue.php'); -$PageletBlue->addJS('static/delayJS.php'); -$PageletBlue->addJSCode("document.getElementById('blue').innerHTML += ' [JS executed]';document.getElementById('blue').style.borderRadius = '30px';"); - -#==================================================================================================== -# Pagelet with green background color -#==================================================================================================== -$PageletGreen = new BigPipe\DemoPagelet(); -$PageletGreen->addHTML('
I AM A PAGELET WITH GREEN BACKGROUND
'); -$PageletGreen->addCSS('static/green.php'); -$PageletGreen->addJS('static/delayJS.php'); -$PageletGreen->addJSCode("document.getElementById('green').innerHTML += ' [JS executed]';document.getElementById('green').style.borderRadius = '30px';"); -?> - - - - - - - - - BigPipe Demo - - -

BigPipe Demo

-

You see on this page 3 pagelets are getting rendered. Each pagelet has his own CSS and JS resources. The CSS resources change the background color of the pagelet (so you can see the effect when the CSS is loaded). The next step is to load the JS resources and they change the border-radius of the pagelet. After the loading of CSS and JS resources the static JS callback get executed. Additionally, the PhaseDoneJS callbacks performed for each pagelet phase. See the javascript console for more debug informations.

-

PhaseDoneJS is a new feature of BigPipe which can execute JS callbacks for each pagelet phase. Each pagelet can have multiple PhaseDoneJS callbacks for each phase. The difference between a PhaseDoneJS callback and a static JS callback ("JS_CODE") is the following: The static JS callback always get executed (regardless of whether the pipeline is enabled or disabled) and can be a main part of the JS from the pagelet. But the PhaseDoneJS callbacks are only executed if the pipeline is enabled. They are suitable for application-specific stuff.

- -

More information [at this time only in german; sorry]:
https://blackphantom.de/artikel/bigpipe-website-pipelining-und-schnellerer-aufbau-durch-einzelne-pagelets/

- -

Check if output flushing works on your server:
output-flushing-test.php

- - - - - +addHTML('
I AM A PAGELET WITH RED BACKGROUND
'); +$PageletRed->addCSS('static/red.php'); +$PageletRed->addJS('static/delayJS.php'); +$PageletRed->addJSCode("document.getElementById('red').innerHTML += ' [JS executed]';document.getElementById('red').style.borderRadius = '30px';"); + +#=============================================================================== +# Pagelet with blue background color +#=============================================================================== +$PageletBlue = new BigPipe\DemoPagelet('customPageletID', BigPipe\Pagelet::PRIORITY_HIGH); +$PageletBlue->addHTML('
I AM A PAGELET WITH BLUE BACKGROUND
'); +$PageletBlue->addCSS('static/blue.php'); +$PageletBlue->addJS('static/delayJS.php'); +$PageletBlue->addJSCode("document.getElementById('blue').innerHTML += ' [JS executed]';document.getElementById('blue').style.borderRadius = '30px';"); + +#=============================================================================== +# Pagelet with green background color +#=============================================================================== +$PageletGreen = new BigPipe\DemoPagelet(); +$PageletGreen->addHTML('
I AM A PAGELET WITH GREEN BACKGROUND
'); +$PageletGreen->addCSS('static/green.php'); +$PageletGreen->addJS('static/delayJS.php'); +$PageletGreen->addJSCode("document.getElementById('green').innerHTML += ' [JS executed]';document.getElementById('green').style.borderRadius = '30px';"); +?> + + + + + + + + + BigPipe Demo + + +

BigPipe Demo

+

You see on this page 3 pagelets are getting rendered. Each pagelet has his own CSS and JS resources. The CSS resources change the background color of the pagelet (so you can see the effect when the CSS is loaded). The next step is to load the JS resources and they change the border-radius of the pagelet. After the loading of CSS and JS resources the static JS callback get executed. Additionally, the PhaseDoneJS callbacks performed for each pagelet phase. See the javascript console for more debug informations.

+

PhaseDoneJS is a new feature of BigPipe which can execute JS callbacks for each pagelet phase. Each pagelet can have multiple PhaseDoneJS callbacks for each phase. The difference between a PhaseDoneJS callback and a static JS callback ("JS_CODE") is the following: The static JS callback always get executed (regardless of whether the pipeline is enabled or disabled) and can be a main part of the JS from the pagelet. But the PhaseDoneJS callbacks are only executed if the pipeline is enabled. They are suitable for application-specific stuff.

+ +

Notice: BigPipe may support the features of the new PHP 7 in the future and you may also benefit from the performance boost with PHP 7.

+ +

Check if output flushing works on your server:
output-flushing-test.php

+ + + + + + + \ No newline at end of file diff --git a/output-flushing-test.php b/output-flushing-test.php index 410208f..faaf9bc 100755 --- a/output-flushing-test.php +++ b/output-flushing-test.php @@ -1,12 +1,12 @@ - \ No newline at end of file diff --git a/static/bigpipe.js b/static/bigpipe.js index 0fcf68f..fed6e39 100755 --- a/static/bigpipe.js +++ b/static/bigpipe.js @@ -1,273 +1,273 @@ -//=================================================================================================== -// Revealing Module Pattern -//=================================================================================================== -var BigPipe = (function() { - //=================================================================================================== - // Resource: Represents a single CSS or JS resource - //=================================================================================================== - function Resource(resourceURL, type) { - this.resourceURL = resourceURL; - this.callbacks = []; - this.done = false; - this.type = type; - } - - //=================================================================================================== - // Resource: Loading the resource - //=================================================================================================== - Resource.prototype.start = function() { - if(this.type === 0) { - var element = document.createElement('link'); - element.setAttribute('rel', 'stylesheet'); - element.setAttribute('href', this.resourceURL); - } - - else { - var element = document.createElement('script'); - element.setAttribute('src', this.resourceURL); - element.async = true; - } - - document.head.appendChild(element); - - element.onload = function() { - this.executeCallbacks(); - }.bind(this); - - element.onerror = function() { - this.executeCallbacks(); - }.bind(this); - }; - - //=================================================================================================== - // Resource: Register a new callback - //=================================================================================================== - Resource.prototype.registerCallback = function(callback) { - return this.callbacks.push(callback); - }; - - //=================================================================================================== - // Resource: Executes all registered callbacks - //=================================================================================================== - Resource.prototype.executeCallbacks = function() { - if(!this.done) { - this.done = true; - - this.callbacks.forEach(function(callback) { - callback(); - }); - } - }; - - //=================================================================================================== - // Pagelet: Represents a single pagelet - //=================================================================================================== - function Pagelet(data) { - this.pageletID = data.ID; - this.HTML = data.HTML || ""; - this.CSSFiles = data.RESOURCES.CSS; - this.JSFiles = data.RESOURCES.JS; - this.JSCode = data.RESOURCES.JS_CODE; - - this.phase = 0; - this.CSSResources = []; - this.JSResources = []; - - this.phaseDoneJS = data.PHASES; - } - - //=================================================================================================== - // Pagelet: Increases phase and executes PhaseDoneJS - //=================================================================================================== - Pagelet.prototype.phaseDoneHandler = function(phase) { - for(var currentPhase = this.phase; currentPhase <= phase; ++currentPhase) { - this.executePhaseDoneJS(currentPhase); - } - - return (this.phase = ++phase); - }; - - //=================================================================================================== - // Pagelet: Executes the callbacks of the specific phase - //=================================================================================================== - Pagelet.prototype.executePhaseDoneJS = function(phase) { - this.phaseDoneJS[phase].forEach(function(code) { - try { - globalExecution(code); - } catch(e) { - console.error("PhaseDoneJS: " + e); - } - }); - }; - - //=================================================================================================== - // Pagelet: Initialize and start the CSS resources - //=================================================================================================== - Pagelet.prototype.start = function() { - var isStarted = false; - - this.CSSFiles.forEach(function(resourceURL) { - this.attachCSSResource(new Resource(resourceURL, 0)); - }.bind(this)); - - this.JSFiles.forEach(function(resourceURL) { - this.attachJSResource(new Resource(resourceURL, 1)); - }.bind(this)); - - this.CSSResources.forEach(function(resource) { - isStarted = true; - resource.start(); - }.bind(this)); - - // If no CSS resource was started (= no external CSS resources exists), then begin to inject the HTML - !isStarted && this.injectHTML(); - }; - - //=================================================================================================== - // Pagelet: Attach a new CSS resource to the pagelet - //=================================================================================================== - Pagelet.prototype.attachCSSResource = function(resource) { - resource.registerCallback(this.onloadCSS.bind(this)); - return this.CSSResources.push(resource); - }; - - //=================================================================================================== - // Pagelet: Attach a new JS resource to the pagelet - //=================================================================================================== - Pagelet.prototype.attachJSResource = function(resource) { - resource.registerCallback(this.onloadJS.bind(this)); - return this.JSResources.push(resource); - }; - - //=================================================================================================== - // Pagelet: Executes the main JS code of the pagelet - //=================================================================================================== - Pagelet.prototype.executeJSCode = function() { - try { - globalExecution(this.JSCode); - } catch(e) { - console.error(this.pageletID + ": " + e); - } - }; - - //=================================================================================================== - // Pagelet: Get each time called if a single JS resource has been loaded - //=================================================================================================== - Pagelet.prototype.onloadJS = function() { - if(this.phase === 3 && this.JSResources.every(function(resource){ - return resource.done; - })) { - this.phaseDoneHandler(3); - this.executeJSCode(); - this.phaseDoneHandler(4); - } - }; - - //=================================================================================================== - // Pagelet: Get each time called if a single CSS resource has been loaded - //=================================================================================================== - Pagelet.prototype.onloadCSS = function() { - if(this.CSSResources.every(function(resource){ - return resource.done; - })) { - this.injectHTML(); - } - }; - - //=================================================================================================== - // Pagelet: Injects the HTML content into the DOM - //=================================================================================================== - Pagelet.prototype.injectHTML = function() { - this.phaseDoneHandler(1); - - if(placeholder = document.getElementById(this.pageletID)) { - var pageletHTML = document.getElementById('_' + this.pageletID); - placeholder.innerHTML = pageletHTML.innerHTML.substring(5, pageletHTML.innerHTML.length - 4); - document.body.removeChild(pageletHTML); - } - - this.phaseDoneHandler(2); - - BigPipe.executeNextPagelet(); - - // Check if this was the last pagelet and then start loading of the external JS resources - if(BigPipe.phase === 2 && BigPipe.pagelets[BigPipe.pagelets.length - 1].pageletID === this.pageletID) { - BigPipe.loadJSResources(); - } - }; - - //=================================================================================================== - // BigPipe - //=================================================================================================== - var BigPipe = { - pagelets: [], - phase: 0, - offset: 0, - - executeNextPagelet: function() { - if(this.pagelets[this.offset]) { - this.pagelets[this.offset++].start(); - } - - else if(this.phase < 2) { - setTimeout(this.executeNextPagelet.bind(this), 20); - } - }, - - onPageletArrive: function(data) { - var pagelet = new Pagelet(data); - pagelet.phaseDoneHandler(0); - - if(this.pagelets.push(pagelet) && this.phase === 0 && !data.IS_LAST) { - this.phase = 1; - this.executeNextPagelet(); - } - - else if(data.IS_LAST) { - this.phase = 2; - if(this.pagelets.length === 1) { - this.executeNextPagelet(); - } - } - }, - - loadJSResources: function() { - this.phase = 3; - var isLoading = false; - - this.pagelets.forEach(function(Pagelet) { - if(Pagelet.JSResources.length === 0) { - Pagelet.onloadJS(); - } - }); - - this.pagelets.forEach(function(Pagelet) { - Pagelet.JSResources.forEach(function(Resource) { - Resource.start(); - isLoading = true; - }); - }); - - if(!isLoading) { - this.pagelets.forEach(function(Pagelet) { - Pagelet.onloadJS(); - }); - } - } - }; - - //=================================================================================================== - // Public-Access - //=================================================================================================== - return { - onPageletArrive: function(data) { - BigPipe.onPageletArrive(data); - }, - - reset: function() { - BigPipe.pagelets = []; - BigPipe.offset = 0; - BigPipe.phase = 0; - } - }; +//============================================================================== +// Revealing Module Pattern +//============================================================================== +var BigPipe = (function() { + //============================================================================== + // Resource: Represents a single CSS or JS resource + //============================================================================== + function Resource(resourceURL, type) { + this.resourceURL = resourceURL; + this.callbacks = []; + this.done = false; + this.type = type; + } + + //============================================================================== + // Resource: Loading the resource + //============================================================================== + Resource.prototype.start = function() { + if(this.type === 0) { + var element = document.createElement('link'); + element.setAttribute('rel', 'stylesheet'); + element.setAttribute('href', this.resourceURL); + } + + else { + var element = document.createElement('script'); + element.setAttribute('src', this.resourceURL); + element.async = true; + } + + document.head.appendChild(element); + + element.onload = function() { + this.executeCallbacks(); + }.bind(this); + + element.onerror = function() { + this.executeCallbacks(); + }.bind(this); + }; + + //============================================================================== + // Resource: Register a new callback + //============================================================================== + Resource.prototype.registerCallback = function(callback) { + return this.callbacks.push(callback); + }; + + //============================================================================== + // Resource: Executes all registered callbacks + //============================================================================== + Resource.prototype.executeCallbacks = function() { + if(!this.done) { + this.done = true; + + this.callbacks.forEach(function(callback) { + callback(); + }); + } + }; + + //============================================================================== + // Pagelet: Represents a single pagelet + //============================================================================== + function Pagelet(data, HTML) { + this.pageletID = data.ID; + this.HTML = HTML || ""; + this.CSSFiles = data.RESOURCES.CSS; + this.JSFiles = data.RESOURCES.JS; + this.JSCode = data.RESOURCES.JS_CODE; + + this.phase = 0; + this.CSSResources = []; + this.JSResources = []; + + this.phaseDoneJS = data.PHASES; + } + + //============================================================================== + // Pagelet: Increases phase and executes PhaseDoneJS + //============================================================================== + Pagelet.prototype.phaseDoneHandler = function(phase) { + for(var currentPhase = this.phase; currentPhase <= phase; ++currentPhase) { + this.executePhaseDoneJS(currentPhase); + } + + return (this.phase = ++phase); + }; + + //============================================================================== + // Pagelet: Executes the callbacks of the specific phase + //============================================================================== + Pagelet.prototype.executePhaseDoneJS = function(phase) { + this.phaseDoneJS[phase].forEach(function(code) { + try { + globalExecution(code); + } catch(e) { + console.error("PhaseDoneJS: " + e); + } + }); + }; + + //============================================================================== + // Pagelet: Initialize and start the CSS resources + //============================================================================== + Pagelet.prototype.start = function() { + var isStarted = false; + + this.CSSFiles.forEach(function(resourceURL) { + this.attachCSSResource(new Resource(resourceURL, 0)); + }.bind(this)); + + this.JSFiles.forEach(function(resourceURL) { + this.attachJSResource(new Resource(resourceURL, 1)); + }.bind(this)); + + this.CSSResources.forEach(function(resource) { + isStarted = true; + resource.start(); + }.bind(this)); + + // If no CSS resource was started (= no external CSS resources exists), then begin to inject the HTML + !isStarted && this.injectHTML(); + }; + + //============================================================================== + // Pagelet: Attach a new CSS resource to the pagelet + //============================================================================== + Pagelet.prototype.attachCSSResource = function(resource) { + resource.registerCallback(this.onloadCSS.bind(this)); + return this.CSSResources.push(resource); + }; + + //============================================================================== + // Pagelet: Attach a new JS resource to the pagelet + //============================================================================== + Pagelet.prototype.attachJSResource = function(resource) { + resource.registerCallback(this.onloadJS.bind(this)); + return this.JSResources.push(resource); + }; + + //============================================================================== + // Pagelet: Executes the main JS code of the pagelet + //============================================================================== + Pagelet.prototype.executeJSCode = function() { + try { + globalExecution(this.JSCode); + } catch(e) { + console.error(this.pageletID + ": " + e); + } + }; + + //============================================================================== + // Pagelet: Get each time called if a single JS resource has been loaded + //============================================================================== + Pagelet.prototype.onloadJS = function() { + if(this.phase === 3 && this.JSResources.every(function(resource){ + return resource.done; + })) { + this.phaseDoneHandler(3); + this.executeJSCode(); + this.phaseDoneHandler(4); + } + }; + + //============================================================================== + // Pagelet: Get each time called if a single CSS resource has been loaded + //============================================================================== + Pagelet.prototype.onloadCSS = function() { + if(this.CSSResources.every(function(resource){ + return resource.done; + })) { + this.injectHTML(); + } + }; + + //============================================================================== + // Pagelet: Injects the HTML content into the DOM + //============================================================================== + Pagelet.prototype.injectHTML = function() { + this.phaseDoneHandler(1); + + document.getElementById(this.pageletID).innerHTML = this.HTML; + + this.phaseDoneHandler(2); + + BigPipe.executeNextPagelet(); + + // Check if this was the last pagelet and then start loading of the external JS resources + if(BigPipe.phase === 2 && BigPipe.pagelets[BigPipe.pagelets.length - 1].pageletID === this.pageletID) { + BigPipe.loadJSResources(); + } + }; + + //============================================================================== + // BigPipe + //============================================================================== + var BigPipe = { + pagelets: [], + phase: 0, + offset: 0, + + executeNextPagelet: function() { + if(this.pagelets[this.offset]) { + this.pagelets[this.offset++].start(); + } + + else if(this.phase < 2) { + setTimeout(this.executeNextPagelet.bind(this), 20); + } + }, + + onPageletArrive: function(data, codeContainer) { + var pageletHTML = codeContainer.innerHTML; + pageletHTML = pageletHTML.substring(5, pageletHTML.length - 4); + codeContainer.parentNode.removeChild(codeContainer); + + var pagelet = new Pagelet(data, pageletHTML); + pagelet.phaseDoneHandler(0); + + if(this.pagelets.push(pagelet) && this.phase === 0 && !data.IS_LAST) { + this.phase = 1; + this.executeNextPagelet(); + } + + else if(data.IS_LAST) { + this.phase = 2; + if(this.pagelets.length === 1) { + this.executeNextPagelet(); + } + } + }, + + loadJSResources: function() { + this.phase = 3; + var isLoading = false; + + this.pagelets.forEach(function(Pagelet) { + if(Pagelet.JSResources.length === 0) { + Pagelet.onloadJS(); + } + }); + + this.pagelets.forEach(function(Pagelet) { + Pagelet.JSResources.forEach(function(Resource) { + Resource.start(); + isLoading = true; + }); + }); + + if(!isLoading) { + this.pagelets.forEach(function(Pagelet) { + Pagelet.onloadJS(); + }); + } + } + }; + + //============================================================================== + // Public-Access + //============================================================================== + return { + onPageletArrive: function(data, codeContainer) { + BigPipe.onPageletArrive(data, codeContainer); + }, + + reset: function() { + BigPipe.pagelets = []; + BigPipe.offset = 0; + BigPipe.phase = 0; + } + }; })(); \ No newline at end of file diff --git a/static/blue.php b/static/blue.php index e715d83..5f9bacb 100755 --- a/static/blue.php +++ b/static/blue.php @@ -1,17 +1,17 @@ - + #blue{background:blue;} \ No newline at end of file diff --git a/static/delayJS.php b/static/delayJS.php index 96e46c5..b4af48d 100755 --- a/static/delayJS.php +++ b/static/delayJS.php @@ -1,18 +1,18 @@ - - -console.log("EXTERNAL: Delayed javascript resource loaded"); + + +console.log("EXTERNAL: Delayed javascript resource loaded"); diff --git a/static/green.php b/static/green.php index 65ded6f..f1c5f1b 100755 --- a/static/green.php +++ b/static/green.php @@ -1,17 +1,17 @@ - + #green{background:green;} \ No newline at end of file diff --git a/static/red.php b/static/red.php index e6936e8..b54abb3 100755 --- a/static/red.php +++ b/static/red.php @@ -1,17 +1,17 @@ - + #red{background:red;} \ No newline at end of file -- cgit v1.2.3