aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Lange <code@nerdmind.de>2016-06-23 13:10:24 +0200
committerThomas Lange <code@nerdmind.de>2016-06-23 13:16:30 +0200
commitc5637489e603c588fca41e2b7bd4345b67914f33 (patch)
tree5a1e8cc8f9558105422c24ddf8c1b8067df0a081
parent102d2dc24505f4ce7fe6601b21a7e2e414f613cb (diff)
downloadbigpipe-c5637489e603c588fca41e2b7bd4345b67914f33.tar.gz
bigpipe-c5637489e603c588fca41e2b7bd4345b67914f33.tar.xz
bigpipe-c5637489e603c588fca41e2b7bd4345b67914f33.zip
Pagelets executed immediately; Dependency feature; Several improvements
+ All pagelets are now executed immediately on arrive. + A "Display Dependency" feature was added. If you give a pagelet an dependency, it will first be executed if all pagelets, who registered as dependency, are displayed. + If BigPipe.reset() is called, the function loops through each Resource and executes Resource.abortLoading(). This removes the <link> or <script> element from DOM and removes the onload callbacks. This is to prevent that an onload callback from a previous page are executed while the user has already changes the page asynchronously and a resource from the previous page wasn't already loaded.
-rwxr-xr-xasync.php29
-rwxr-xr-xinclude/classes/BigPipe/BigPipe.php2
-rwxr-xr-xinclude/classes/BigPipe/DemoPagelet.php4
-rwxr-xr-xinclude/classes/BigPipe/Pagelet.php11
-rw-r--r--include/pagelets.php50
-rwxr-xr-xindex.php28
-rwxr-xr-xstatic/bigpipe.js199
7 files changed, 180 insertions, 143 deletions
diff --git a/async.php b/async.php
index 9093c87..bd67865 100755
--- a/async.php
+++ b/async.php
@@ -31,32 +31,8 @@ if(isset($_GET['bigpipe']) AND (int) $_GET['bigpipe'] === 0) {
BigPipe\BigPipe::enablePipeline(FALSE);
}
-#===============================================================================
-# Pagelet with red background color
-#===============================================================================
-$PageletRed = new BigPipe\DemoPagelet();
-$PageletRed->addHTML('<section id="red" class="text">['.time().'] I AM A PAGELET WITH RED BACKGROUND</section>');
-$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('<section id="blue" class="text">['.time().'] I AM A PAGELET WITH BLUE BACKGROUND</section>');
-$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('<section id="green" class="text">['.time().'] I AM A PAGELET WITH GREEN BACKGROUND</section>');
-$PageletGreen->addCSS('static/green.php');
-$PageletGreen->addJS('static/delayJS.php');
-$PageletGreen->addJSCode("document.getElementById('green').innerHTML += ' [JS executed]';document.getElementById('green').style.borderRadius = '30px';");
+// Outsourced to avoid duplicate code in index.php and async.php
+require_once 'include/pagelets.php';
?>
<!DOCTYPE html>
<html lang="de">
@@ -134,7 +110,6 @@ $PageletGreen->addJSCode("document.getElementById('green').innerHTML += ' [JS ex
if(isAsyncRequest()) {
$BUFFER = removeLineBreaksAndTabs(ob_get_clean());
echo '<script>["Application","BigPipe"].forEach(function(name){window[name] = parent[name];});</script>'."\n";
- echo '<script>["BigPipe", "Application"].forEach(function(name){window[name] = parent[name];});</script>'."\n";
echo '<script>Application.placeholderHTML('.json_encode($BUFFER).');</script>'."\n\n";
}
diff --git a/include/classes/BigPipe/BigPipe.php b/include/classes/BigPipe/BigPipe.php
index 8cdda0b..1af4d8a 100755
--- a/include/classes/BigPipe/BigPipe.php
+++ b/include/classes/BigPipe/BigPipe.php
@@ -34,7 +34,7 @@ class BigPipe {
#===============================================================================
private static function singleResponse(Pagelet $Pagelet, $last = FALSE) {
$pageletJSON = [
- 'ID' => $Pagelet->getID(),
+ 'ID' => $Pagelet->getID(), 'NEED' => $Pagelet->getDependencies(),
'RESOURCES' => ['CSS' => $Pagelet->getCSSFiles(), 'JS' => $Pagelet->getJSFiles(), 'JS_CODE' => removeLineBreaksAndTabs($Pagelet->getJSCode())],
'PHASES' => (object) $Pagelet->getPhaseDoneJS()
];
diff --git a/include/classes/BigPipe/DemoPagelet.php b/include/classes/BigPipe/DemoPagelet.php
index 83689ba..adb9f89 100755
--- a/include/classes/BigPipe/DemoPagelet.php
+++ b/include/classes/BigPipe/DemoPagelet.php
@@ -3,8 +3,8 @@ namespace BigPipe;
class DemoPagelet extends Pagelet {
- public function __construct($customID = NULL, $priority = Pagelet::PRIORITY_NORMAL) {
- parent::__construct($customID, $priority);
+ public function __construct($customID = NULL, $priority = Pagelet::PRIORITY_NORMAL, array $dependencies = []) {
+ parent::__construct($customID, $priority, $dependencies);
$message = '%s: PhaseDoneJS for phase %s';
diff --git a/include/classes/BigPipe/Pagelet.php b/include/classes/BigPipe/Pagelet.php
index 22aadce..436e12a 100755
--- a/include/classes/BigPipe/Pagelet.php
+++ b/include/classes/BigPipe/Pagelet.php
@@ -8,6 +8,7 @@ class Pagelet {
private $JSFiles = [];
private $CSSFiles = [];
private $phaseDoneJS = [];
+ private $dependencies = [];
private $tagname = 'div';
private static $count = 0;
@@ -29,8 +30,9 @@ class Pagelet {
const PHASE_LOADJS = 3; # After all the JS resources have been loaded
const PHASE_EXECJS = 4; # After the static JS code has been executed
- public function __construct($customID = NULL, $priority = self::PRIORITY_NORMAL) {
+ public function __construct($customID = NULL, $priority = self::PRIORITY_NORMAL, array $dependencies = []) {
$this->phaseDoneJS = array_pad($this->phaseDoneJS, 5, []);
+ $this->dependencies = $dependencies;
$this->ID = is_string($customID) ? $customID : 'P'.++self::$count;
BigPipe::addPagelet($this, $priority);
@@ -114,6 +116,13 @@ class Pagelet {
}
#===============================================================================
+ # Return all display dependencies
+ #===============================================================================
+ public function getDependencies(): array {
+ return $this->dependencies;
+ }
+
+ #===============================================================================
# Set custom placeholder tagname
#===============================================================================
public function setTagname($tagname) {
diff --git a/include/pagelets.php b/include/pagelets.php
new file mode 100644
index 0000000..176bc74
--- /dev/null
+++ b/include/pagelets.php
@@ -0,0 +1,50 @@
+<?php
+#===============================================================================
+# Pagelet with red background color
+#===============================================================================
+$PageletRed = new BigPipe\DemoPagelet('redPL');
+$PageletRed->addHTML('<section id="red" class="text">I AM A PAGELET WITH RED BACKGROUND</section>');
+$PageletRed->addCSS('static/red.php');
+$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('bluePL', BigPipe\Pagelet::PRIORITY_HIGH);
+$PageletBlue->addHTML('<section id="blue" class="text">I AM A PAGELET WITH BLUE BACKGROUND</section>');
+$PageletBlue->addCSS('static/blue.php');
+$PageletRed->addCSS('static/red.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('greenPL');
+
+{
+ #===============================================================================
+ # Pagelet within $PageletGreen
+ #===============================================================================
+ // The third parameter is required to ensure that the $InnerPagelet will only be
+ // executed if the HTML from the $PageletGreen has ALREADY DISPLAYED. Otherwise,
+ // $InnerPagelet would not find his placeholder tag which is defined WITHIN the
+ // HTML on $PageletGreen. Of course, you can still add other pagelets as
+ // dependency. Then will $InnerPagelet only displayed if all dependencies are
+ // already displayed!
+ //
+ // NOTE: PRIORITY_HIGHEST is only set so that you can see, that this pagelet is
+ // the first which arrives, but it will first be displayed if his dependency
+ // pagelets are already displayed.
+
+ $InnerPagelet = new BigPipe\DemoPagelet('innerPL', BigPipe\Pagelet::PRIORITY_HIGHEST, [$PageletGreen->getID()]);
+ $InnerPagelet->addHTML('<section sytle="background:#FFF;padding:5px;">Inner Pagelet \(o_o)/</section>');
+}
+
+$PageletGreen->addHTML('<section id="green" class="text">I AM A PAGELET WITH GREEN BACKGROUND'.$InnerPagelet.'</section>');
+$PageletGreen->addCSS('static/green.php');
+$PageletGreen->addJS('static/delayJS.php');
+$PageletGreen->addJSCode("document.getElementById('green').innerHTML += ' [JS executed]';document.getElementById('green').style.borderRadius = '30px';");
+?> \ No newline at end of file
diff --git a/index.php b/index.php
index 1eae51e..5f12fd6 100755
--- a/index.php
+++ b/index.php
@@ -20,32 +20,8 @@ if(isset($_GET['bigpipe']) AND (int) $_GET['bigpipe'] === 0) {
BigPipe\BigPipe::enablePipeline(FALSE);
}
-#===============================================================================
-# Pagelet with red background color
-#===============================================================================
-$PageletRed = new BigPipe\DemoPagelet();
-$PageletRed->addHTML('<section id="red" class="text">I AM A PAGELET WITH RED BACKGROUND</section>');
-$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('<section id="blue" class="text">I AM A PAGELET WITH BLUE BACKGROUND</section>');
-$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('<section id="green" class="text">I AM A PAGELET WITH GREEN BACKGROUND</section>');
-$PageletGreen->addCSS('static/green.php');
-$PageletGreen->addJS('static/delayJS.php');
-$PageletGreen->addJSCode("document.getElementById('green').innerHTML += ' [JS executed]';document.getElementById('green').style.borderRadius = '30px';");
+// Outsourced to avoid duplicate code in index.php and async.php
+require_once 'include/pagelets.php';
?>
<!DOCTYPE html>
<html lang="de">
diff --git a/static/bigpipe.js b/static/bigpipe.js
index 30d1f55..5139483 100755
--- a/static/bigpipe.js
+++ b/static/bigpipe.js
@@ -8,56 +8,71 @@ var BigPipe = (function() {
function Resource(resourceURL, type) {
this.resourceURL = resourceURL;
this.callbacks = [];
+ this.node = false;
this.done = false;
this.type = type;
}
//==============================================================================
+ // 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();
+ });
+ }
+ };
+
+ //==============================================================================
// Resource: Loading the resource
//==============================================================================
- Resource.prototype.start = function() {
+ Resource.prototype.execute = function() {
if(this.type === 0) {
- var element = document.createElement('link');
- element.setAttribute('rel', 'stylesheet');
- element.setAttribute('href', this.resourceURL);
+ this.node = document.createElement('link');
+ this.node.setAttribute('rel', 'stylesheet');
+ this.node.setAttribute('href', this.resourceURL);
}
else {
- var element = document.createElement('script');
- element.setAttribute('src', this.resourceURL);
- element.async = true;
+ this.node = document.createElement('script');
+ this.node.setAttribute('src', this.resourceURL);
+ this.node.async = true;
}
- element.setAttribute('class', 'bigpipe');
+ this.node.setAttribute('class', 'bigpipe');
- document.head.appendChild(element);
+ document.head.appendChild(this.node);
- element.onload = function() {
+ this.node.onload = function() {
this.executeCallbacks();
}.bind(this);
- element.onerror = function() {
+ this.node.onerror = function() {
this.executeCallbacks();
}.bind(this);
};
//==============================================================================
- // Resource: Register a new callback
+ // Resource: Remove callbacks after abort of loading the resource
//==============================================================================
- Resource.prototype.registerCallback = function(callback) {
- return this.callbacks.push(callback);
- };
+ Resource.prototype.abortLoading = function() {
+ if(this.node) {
+ this.node.onload = function(){};
+ this.node.onerror = function(){};
- //==============================================================================
- // Resource: Executes all registered callbacks
- //==============================================================================
- Resource.prototype.executeCallbacks = function() {
- if(!this.done) {
- this.done = true;
-
- this.callbacks.forEach(function(callback) {
- callback();
- });
+ // Remove element from DOM
+ var parentNode = this.node.parentNode;
+ return parentNode.removeChild(this.node);
}
};
@@ -66,16 +81,19 @@ var BigPipe = (function() {
//==============================================================================
function Pagelet(data, HTML) {
this.pageletID = data.ID;
- this.HTML = HTML || "";
+ this.HTML = HTML;
this.CSSFiles = data.RESOURCES.CSS;
this.JSFiles = data.RESOURCES.JS;
this.JSCode = data.RESOURCES.JS_CODE;
+ this.NEED = data.NEED;
this.phase = 0;
this.CSSResources = [];
this.JSResources = [];
this.phaseDoneJS = data.PHASES;
+
+ this.phaseDoneHandler(0);
}
//==============================================================================
@@ -103,9 +121,9 @@ var BigPipe = (function() {
};
//==============================================================================
- // Pagelet: Initialize and start the CSS resources
+ // Pagelet: Initialize and execute the CSS resources
//==============================================================================
- Pagelet.prototype.start = function() {
+ Pagelet.prototype.execute = function() {
var isStarted = false;
this.CSSFiles.forEach(function(resourceURL) {
@@ -118,11 +136,11 @@ var BigPipe = (function() {
this.CSSResources.forEach(function(resource) {
isStarted = true;
- resource.start();
+ resource.execute();
}.bind(this));
// If no CSS resource was started (= no external CSS resources exists), then begin to inject the HTML
- !isStarted && this.injectHTML();
+ !isStarted && this.replaceHTML();
};
//==============================================================================
@@ -152,6 +170,7 @@ var BigPipe = (function() {
console.error(this.pageletID + ": " + e);
}
});
+ this.phaseDoneHandler(4);
};
//==============================================================================
@@ -159,11 +178,10 @@ var BigPipe = (function() {
//==============================================================================
Pagelet.prototype.onloadJS = function() {
if(this.phase === 3 && this.JSResources.every(function(resource){
- return resource.done;
- })) {
+ return resource.done;
+ })) {
this.phaseDoneHandler(3);
this.executeJSCode();
- this.phaseDoneHandler(4);
}
};
@@ -172,28 +190,22 @@ var BigPipe = (function() {
//==============================================================================
Pagelet.prototype.onloadCSS = function() {
if(this.CSSResources.every(function(resource){
- return resource.done;
- })) {
- this.injectHTML();
+ return resource.done;
+ })) {
+ this.phaseDoneHandler(1);
+ this.replaceHTML();
}
};
//==============================================================================
// Pagelet: Injects the HTML content into the DOM
//==============================================================================
- Pagelet.prototype.injectHTML = function() {
- this.phaseDoneHandler(1);
-
+ Pagelet.prototype.replaceHTML = function() {
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.pageletHTMLreplaced(this.pageletID);
};
//==============================================================================
@@ -202,17 +214,8 @@ var BigPipe = (function() {
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);
- }
- },
+ done: [],
+ wait: [],
onPageletArrive: function(data, codeContainer) {
var pageletHTML = codeContainer.innerHTML;
@@ -220,43 +223,64 @@ var BigPipe = (function() {
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.pagelets.push(pagelet);
+
+ if(this.phase = 0) {
this.phase = 1;
- this.executeNextPagelet();
}
- else if(data.IS_LAST) {
+ if(data.IS_LAST) {
this.phase = 2;
- if(this.pagelets.length === 1) {
- this.executeNextPagelet();
+ }
+
+ if(pagelet.NEED.length === 0 || pagelet.NEED.every(function(needID) {
+ return BigPipe.done.indexOf(needID) !== -1;
+ })) {
+ pagelet.execute();
+ }
+
+ else {
+ this.wait.push(pagelet);
+ }
+ },
+
+ pageletHTMLreplaced: function(pageletID) {
+ BigPipe.done.push(pageletID);
+
+ for(var i = 0; i < this.wait.length; ++i) {
+ var 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();
}
}
+
+ // Check if this was the last pagelet and then execute loading of the external JS resources
+ if(BigPipe.phase === 2 && BigPipe.done.length === BigPipe.pagelets.length ) {
+ BigPipe.executeJSResources();
+ }
},
- loadJSResources: function() {
+ executeJSResources: function() {
this.phase = 3;
- var isLoading = false;
- this.pagelets.forEach(function(Pagelet) {
- if(Pagelet.JSResources.length === 0) {
- Pagelet.onloadJS();
+ 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;
- });
+ else {
+ pagelet.JSResources.forEach(function(resource) {
+ resource.execute();
+ });
+ }
});
-
- if(!isLoading) {
- this.pagelets.forEach(function(Pagelet) {
- Pagelet.onloadJS();
- });
- }
}
};
@@ -269,15 +293,18 @@ var BigPipe = (function() {
},
reset: function() {
- BigPipe.pagelets = [];
- BigPipe.offset = 0;
- BigPipe.phase = 0;
+ BigPipe.pagelets.forEach(function(pagelet) {
+ pagelet.CSSResources.concat(pagelet.JSResources).forEach(function(resource) {
+ resource.abortLoading();
+ });
+ });
- var resources = document.head.getElementsByClassName('bigpipe');
+ window.stop() || document.execCommand("Stop");
- while(resources[0]) {
- resources[0].parentNode.removeChild(resources[0]);
- }
+ BigPipe.pagelets = [];
+ BigPipe.phase = 0;
+ BigPipe.wait = [];
+ BigPipe.done = [];
}
};
})(); \ No newline at end of file