diff options
-rw-r--r-- | async.php | 252 | ||||
-rw-r--r-- | include/functions.php | 14 | ||||
-rw-r--r-- | index.php | 126 | ||||
-rw-r--r-- | output-flushing-test.php | 22 | ||||
-rw-r--r-- | static/bigpipe.js | 730 | ||||
-rw-r--r-- | static/blue.php | 32 | ||||
-rw-r--r-- | static/delayJS.php | 36 | ||||
-rw-r--r-- | static/green.php | 32 | ||||
-rw-r--r-- | static/red.php | 32 |
9 files changed, 638 insertions, 638 deletions
@@ -1,127 +1,127 @@ -<?php
-# >>> [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
-#===============================================================================
-spl_autoload_register(function($classname) {
- $classpath = 'include/classes/%s.php';
- $classname = str_replace('\\', '/', $classname);
-
- require_once sprintf($classpath, $classname);
-});
-
-require_once 'include/functions.php';
-
-#===============================================================================
-# Check if BigPipe should be disabled
-#===============================================================================
-if(isset($_GET['bigpipe'])) {
-
- # You can use this method also to disable pipeline for Googlebot or something.
- BigPipe\BigPipe::enabled($_GET['bigpipe']);
-}
-
-// Outsourced to avoid duplicate code in index.php and async.php
-require_once 'include/pagelets.php';
-?>
-<!DOCTYPE html>
-<html lang="de">
-<?php if(!isAsyncRequest()): ?>
-<head>
- <meta charset="UTF-8" />
- <meta name="robots" content="noindex, nofollow" />
- <style>
- html{margin:0;padding:0;background:#B9C3D2;font-family:Calibri,Sans-Serif;}
- body{max-width:1200px;margin:0 auto;}
- .text{color:white;margin-bottom:30px;padding:40px;border-radius:4px;font-weight:600;text-align:center;border:4px solid black;}
- .hidden{display:none;}
- </style>
- <script src="static/bigpipe.js"></script>
- <title>BigPipe Demo</title>
- <!-- >>> [Additional code for the async function] -->
- <script>
- var Application = {
- bigPipeEnabled: <?=json_encode(BigPipe\BigPipe::enabled())?>,
-
- placeholderHTML: function(HTML) {
- document.getElementById('placeholder_container').innerHTML = HTML;
- }
- };
-
- function fireAsyncRequest(href) {
- if(Application.bigPipeEnabled === false) {
- alert("Note: Pipelining is disabled and page will be loaded quite normal.");
- return;
- }
-
- console.info('ASYNC REQUEST FIRED!');
-
- Application.placeholderHTML("");
- BigPipe.reset();
- var transport_frame;
-
- if(transport_frame = document.getElementById('transport_frame')) {
- document.body.removeChild(transport_frame);
- }
-
- var iframe = document.createElement('iframe');
- iframe.setAttribute('id', 'transport_frame');
- iframe.setAttribute('class', 'hidden');
- iframe.setAttribute('src', href + '?response=async');
-
- document.body.appendChild(iframe);
-
- iframe.onload = function() {
- document.body.removeChild(iframe);
- }.bind(this);
-
- return false;
- }
- </script>
- <!-- <<< -->
-</head>
-<?php endif; ?>
-<body>
-<?php if(!isAsyncRequest()): ?>
-<h1>BigPipe Async Demo</h1>
-
-<p><a href="async.php" onclick="return fireAsyncRequest(this)">LOAD CONTENT VIA TRANSPORT FRAME</a> [Current Time: <?=time();?> – So you can see, that the page does not get completely reloaded]</p>
-<p><em>Look at the developer console of your browser to see the debug messages and how the async response from server looks.</em></p>
-
-<section id="placeholder_container">
- <?php else: ob_start(); endif; ?>
- <?php
- echo $PageletRed;
- echo $PageletBlue;
- echo $PageletGreen;
- ?>
- <?php if(!isAsyncRequest()):?>
-</section>
-<footer><strong>The footer of the page.</strong></footer>
-
-<?php endif;
-if(isAsyncRequest()) {
- $BUFFER = removeLineBreaksAndTabs(ob_get_clean());
- echo '<script>["Application","BigPipe"].forEach(function(name){window[name] = parent[name];});</script>'."\n";
- echo '<script>Application.placeholderHTML('.json_encode($BUFFER).');</script>'."\n\n";
-}
-
-BigPipe\BigPipe::render();
-?>
-</body>
+<?php +# >>> [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 +#=============================================================================== +spl_autoload_register(function($classname) { + $classpath = 'include/classes/%s.php'; + $classname = str_replace('\\', '/', $classname); + + require_once sprintf($classpath, $classname); +}); + +require_once 'include/functions.php'; + +#=============================================================================== +# Check if BigPipe should be disabled +#=============================================================================== +if(isset($_GET['bigpipe'])) { + + # You can use this method also to disable pipeline for Googlebot or something. + BigPipe\BigPipe::enabled($_GET['bigpipe']); +} + +// Outsourced to avoid duplicate code in index.php and async.php +require_once 'include/pagelets.php'; +?> +<!DOCTYPE html> +<html lang="de"> +<?php if(!isAsyncRequest()): ?> +<head> + <meta charset="UTF-8" /> + <meta name="robots" content="noindex, nofollow" /> + <style> + html{margin:0;padding:0;background:#B9C3D2;font-family:Calibri,Sans-Serif;} + body{max-width:1200px;margin:0 auto;} + .text{color:white;margin-bottom:30px;padding:40px;border-radius:4px;font-weight:600;text-align:center;border:4px solid black;} + .hidden{display:none;} + </style> + <script src="static/bigpipe.js"></script> + <title>BigPipe Demo</title> + <!-- >>> [Additional code for the async function] --> + <script> + var Application = { + bigPipeEnabled: <?=json_encode(BigPipe\BigPipe::enabled())?>, + + placeholderHTML: function(HTML) { + document.getElementById('placeholder_container').innerHTML = HTML; + } + }; + + function fireAsyncRequest(href) { + if(Application.bigPipeEnabled === false) { + alert("Note: Pipelining is disabled and page will be loaded quite normal."); + return; + } + + console.info('ASYNC REQUEST FIRED!'); + + Application.placeholderHTML(""); + BigPipe.reset(); + var transport_frame; + + if(transport_frame = document.getElementById('transport_frame')) { + document.body.removeChild(transport_frame); + } + + var iframe = document.createElement('iframe'); + iframe.setAttribute('id', 'transport_frame'); + iframe.setAttribute('class', 'hidden'); + iframe.setAttribute('src', href + '?response=async'); + + document.body.appendChild(iframe); + + iframe.onload = function() { + document.body.removeChild(iframe); + }.bind(this); + + return false; + } + </script> + <!-- <<< --> +</head> +<?php endif; ?> +<body> +<?php if(!isAsyncRequest()): ?> +<h1>BigPipe Async Demo</h1> + +<p><a href="async.php" onclick="return fireAsyncRequest(this)">LOAD CONTENT VIA TRANSPORT FRAME</a> [Current Time: <?=time();?> – So you can see, that the page does not get completely reloaded]</p> +<p><em>Look at the developer console of your browser to see the debug messages and how the async response from server looks.</em></p> + +<section id="placeholder_container"> + <?php else: ob_start(); endif; ?> + <?php + echo $PageletRed; + echo $PageletBlue; + echo $PageletGreen; + ?> + <?php if(!isAsyncRequest()):?> +</section> +<footer><strong>The footer of the page.</strong></footer> + +<?php endif; +if(isAsyncRequest()) { + $BUFFER = removeLineBreaksAndTabs(ob_get_clean()); + echo '<script>["Application","BigPipe"].forEach(function(name){window[name] = parent[name];});</script>'."\n"; + echo '<script>Application.placeholderHTML('.json_encode($BUFFER).');</script>'."\n\n"; +} + +BigPipe\BigPipe::render(); +?> +</body> </html>
\ No newline at end of file diff --git a/include/functions.php b/include/functions.php index fd46c63..b866994 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1,8 +1,8 @@ -<?php
-#===============================================================================
-# Remove line breaks and tabs from a string
-#===============================================================================
-function removeLineBreaksAndTabs($string) {
- return str_replace(["\r\n", "\r", "\n", "\t"], NULL, $string);
-}
+<?php +#=============================================================================== +# Remove line breaks and tabs from a string +#=============================================================================== +function removeLineBreaksAndTabs($string) { + return str_replace(["\r\n", "\r", "\n", "\t"], NULL, $string); +} ?>
\ No newline at end of file @@ -1,64 +1,64 @@ -<?php
-#===============================================================================
-# Deactivate caching
-#===============================================================================
-header('Cache-Control: no-cache, no-store, must-revalidate');
-
-#===============================================================================
-# Include classes and functions
-#===============================================================================
-spl_autoload_register(function($classname) {
- $classpath = 'include/classes/%s.php';
- $classname = str_replace('\\', '/', $classname);
-
- require_once sprintf($classpath, $classname);
-});
-
-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::enabled(FALSE);
-}
-
-// Outsourced to avoid duplicate code in index.php and async.php
-require_once 'include/pagelets.php';
-?>
-<!DOCTYPE html>
-<html lang="de">
-<head>
- <meta charset="UTF-8" />
- <meta name="robots" content="noindex, nofollow" />
- <style>
- html{margin:0;padding:0;background:#B9C3D2;font-family:Calibri,Sans-Serif;}
- body{max-width:1200px;margin:0 auto;}
- .text{color:white;margin-bottom:30px;padding:40px;border-radius:4px;font-weight:600;text-align:center;border:4px solid black;}
- </style>
- <script src="static/bigpipe.js"></script>
- <title>BigPipe Demo</title>
-</head>
-<body>
-<h1>BigPipe Demo</h1>
-<p>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 replace the content on the placeholder HTML with the Pagelet HTML 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.</p>
-<p>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 ("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.</p>
-
-<p><strong>Notice:</strong> BigPipe may support the features of the new <em>PHP 7</em> in the future and you may also benefit from the performance boost with <em>PHP 7</em>.</p>
-
-<p><b>Check if output flushing works on your server:</b><br /><a href="output-flushing-test.php">output-flushing-test.php</a></p>
-
-<?php
-echo $PageletRed;
-echo $PageletBlue;
-echo $PageletGreen;
-?>
-
-<footer><strong>The footer of the page.</strong></footer>
-
-<?php
-BigPipe\BigPipe::render();
-?>
-</body>
+<?php +#=============================================================================== +# Deactivate caching +#=============================================================================== +header('Cache-Control: no-cache, no-store, must-revalidate'); + +#=============================================================================== +# Include classes and functions +#=============================================================================== +spl_autoload_register(function($classname) { + $classpath = 'include/classes/%s.php'; + $classname = str_replace('\\', '/', $classname); + + require_once sprintf($classpath, $classname); +}); + +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::enabled(FALSE); +} + +// Outsourced to avoid duplicate code in index.php and async.php +require_once 'include/pagelets.php'; +?> +<!DOCTYPE html> +<html lang="de"> +<head> + <meta charset="UTF-8" /> + <meta name="robots" content="noindex, nofollow" /> + <style> + html{margin:0;padding:0;background:#B9C3D2;font-family:Calibri,Sans-Serif;} + body{max-width:1200px;margin:0 auto;} + .text{color:white;margin-bottom:30px;padding:40px;border-radius:4px;font-weight:600;text-align:center;border:4px solid black;} + </style> + <script src="static/bigpipe.js"></script> + <title>BigPipe Demo</title> +</head> +<body> +<h1>BigPipe Demo</h1> +<p>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 replace the content on the placeholder HTML with the Pagelet HTML 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.</p> +<p>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 ("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.</p> + +<p><strong>Notice:</strong> BigPipe may support the features of the new <em>PHP 7</em> in the future and you may also benefit from the performance boost with <em>PHP 7</em>.</p> + +<p><b>Check if output flushing works on your server:</b><br /><a href="output-flushing-test.php">output-flushing-test.php</a></p> + +<?php +echo $PageletRed; +echo $PageletBlue; +echo $PageletGreen; +?> + +<footer><strong>The footer of the page.</strong></footer> + +<?php +BigPipe\BigPipe::render(); +?> +</body> </html>
\ No newline at end of file diff --git a/output-flushing-test.php b/output-flushing-test.php index faaf9bc..410208f 100644 --- a/output-flushing-test.php +++ b/output-flushing-test.php @@ -1,12 +1,12 @@ -<?php
-header('Content-Type: text/plain; charset=UTF-8');
-
-echo "[INFO: If you see the single blocks successively, then the output flushing on your server works. If not, and you see it all at once, your server need to be configured to use output flushing.]\n\n";
-
-for($i = 1; $i <= 8; $i++) {
- echo '[BLOCK: '.$i.']'."\n".str_repeat('[0]', 1500)."\n\n\n";
- flush(); usleep(750000);
-}
-
-echo '[ALL BLOCKS LOADED]';
+<?php +header('Content-Type: text/plain; charset=UTF-8'); + +echo "[INFO: If you see the single blocks successively, then the output flushing on your server works. If not, and you see it all at once, your server need to be configured to use output flushing.]\n\n"; + +for($i = 1; $i <= 8; $i++) { + echo '[BLOCK: '.$i.']'."\n".str_repeat('[0]', 1500)."\n\n\n"; + flush(); usleep(750000); +} + +echo '[ALL BLOCKS LOADED]'; ?>
\ No newline at end of file diff --git a/static/bigpipe.js b/static/bigpipe.js index 43189a4..ae15935 100644 --- a/static/bigpipe.js +++ b/static/bigpipe.js @@ -1,366 +1,366 @@ -//==============================================================================
-// 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(dataJSON, type) {
- this.ID = dataJSON.ID;
- this.HREF = dataJSON.HREF;
- this.callbacks = [];
- this.node = false;
- this.done = false;
- this.type = type;
-
- this.phaseDoneJS = dataJSON.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(dataJSON, HTML) {
- this.ID = dataJSON.ID;
- this.NEED = dataJSON.NEED;
- this.HTML = HTML;
- this.JSCode = dataJSON.CODE;
- this.phaseDoneJS = dataJSON.PHASE;
- this.stylesheets = dataJSON.RSRC[Resource.TYPE_STYLESHEET];
- this.javascripts = dataJSON.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: [],
-
- 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(data.IS_LAST) {
- this.phase = 2;
- }
-
- if(pagelet.NEED.length === 0 || pagelet.NEED.every(function(needID) {
- return BigPipe.done.indexOf(needID) !== -1;
- })) {
- pagelet.execute();
- }
-
- else {
- this.wait.push(pagelet);
- }
- },
-
- 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();
- }
- }
-
- // 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.executeJavascriptResources();
- }
- },
-
- 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);
- },
-
- 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');
- }
-
- BigPipe.pagelets = [];
- BigPipe.phase = 0;
- BigPipe.wait = [];
- BigPipe.done = [];
- }
- };
+//============================================================================== +// 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(dataJSON, type) { + this.ID = dataJSON.ID; + this.HREF = dataJSON.HREF; + this.callbacks = []; + this.node = false; + this.done = false; + this.type = type; + + this.phaseDoneJS = dataJSON.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(dataJSON, HTML) { + this.ID = dataJSON.ID; + this.NEED = dataJSON.NEED; + this.HTML = HTML; + this.JSCode = dataJSON.CODE; + this.phaseDoneJS = dataJSON.PHASE; + this.stylesheets = dataJSON.RSRC[Resource.TYPE_STYLESHEET]; + this.javascripts = dataJSON.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: [], + + 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(data.IS_LAST) { + this.phase = 2; + } + + if(pagelet.NEED.length === 0 || pagelet.NEED.every(function(needID) { + return BigPipe.done.indexOf(needID) !== -1; + })) { + pagelet.execute(); + } + + else { + this.wait.push(pagelet); + } + }, + + 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(); + } + } + + // 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.executeJavascriptResources(); + } + }, + + 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); + }, + + 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'); + } + + BigPipe.pagelets = []; + BigPipe.phase = 0; + BigPipe.wait = []; + BigPipe.done = []; + } + }; })();
\ No newline at end of file diff --git a/static/blue.php b/static/blue.php index 5f9bacb..c5e7de8 100644 --- a/static/blue.php +++ b/static/blue.php @@ -1,17 +1,17 @@ -<?php
-#===============================================================================
-# Disable cache
-#===============================================================================
-header('Cache-Control: no-cache, no-store, must-revalidate');
-
-#===============================================================================
-# Set Content-Type
-#===============================================================================
-header('Content-Type: text/css');
-
-#===============================================================================
-# Simulate long loading time
-#===============================================================================
-usleep(intval(rand(60, 100).'0000'));
-?>
+<?php +#=============================================================================== +# Disable cache +#=============================================================================== +header('Cache-Control: no-cache, no-store, must-revalidate'); + +#=============================================================================== +# Set Content-Type +#=============================================================================== +header('Content-Type: text/css'); + +#=============================================================================== +# Simulate long loading time +#=============================================================================== +usleep(intval(rand(60, 100).'0000')); +?> #blue{background:blue;}
\ No newline at end of file diff --git a/static/delayJS.php b/static/delayJS.php index b4af48d..51252b0 100644 --- a/static/delayJS.php +++ b/static/delayJS.php @@ -1,18 +1,18 @@ -<?php
-#===============================================================================
-# Disable cache
-#===============================================================================
-header('Cache-Control: no-cache, no-store, must-revalidate');
-
-#===============================================================================
-# Set Content-Type
-#===============================================================================
-header('Content-Type: text/javascript');
-
-#===============================================================================
-# Simulate long loading time
-#===============================================================================
-usleep(intval(rand(10, 40).'0000'));
-?>
-
-console.log("EXTERNAL: Delayed javascript resource loaded");
+<?php +#=============================================================================== +# Disable cache +#=============================================================================== +header('Cache-Control: no-cache, no-store, must-revalidate'); + +#=============================================================================== +# Set Content-Type +#=============================================================================== +header('Content-Type: text/javascript'); + +#=============================================================================== +# Simulate long loading time +#=============================================================================== +usleep(intval(rand(10, 40).'0000')); +?> + +console.log("EXTERNAL: Delayed javascript resource loaded"); diff --git a/static/green.php b/static/green.php index f1c5f1b..3a9959d 100644 --- a/static/green.php +++ b/static/green.php @@ -1,17 +1,17 @@ -<?php
-#===============================================================================
-# Disable cache
-#===============================================================================
-header('Cache-Control: no-cache, no-store, must-revalidate');
-
-#===============================================================================
-# Set Content-Type
-#===============================================================================
-header('Content-Type: text/css');
-
-#===============================================================================
-# Simulate long loading time
-#===============================================================================
-usleep(intval(rand(60, 100).'0000'));
-?>
+<?php +#=============================================================================== +# Disable cache +#=============================================================================== +header('Cache-Control: no-cache, no-store, must-revalidate'); + +#=============================================================================== +# Set Content-Type +#=============================================================================== +header('Content-Type: text/css'); + +#=============================================================================== +# Simulate long loading time +#=============================================================================== +usleep(intval(rand(60, 100).'0000')); +?> #green{background:green;}
\ No newline at end of file diff --git a/static/red.php b/static/red.php index b54abb3..7eda0da 100644 --- a/static/red.php +++ b/static/red.php @@ -1,17 +1,17 @@ -<?php
-#===============================================================================
-# Disable cache
-#===============================================================================
-header('Cache-Control: no-cache, no-store, must-revalidate');
-
-#===============================================================================
-# Set Content-Type
-#===============================================================================
-header('Content-Type: text/css');
-
-#===============================================================================
-# Simulate long loading time
-#===============================================================================
-usleep(intval(rand(60, 100).'0000'));
-?>
+<?php +#=============================================================================== +# Disable cache +#=============================================================================== +header('Cache-Control: no-cache, no-store, must-revalidate'); + +#=============================================================================== +# Set Content-Type +#=============================================================================== +header('Content-Type: text/css'); + +#=============================================================================== +# Simulate long loading time +#=============================================================================== +usleep(intval(rand(60, 100).'0000')); +?> #red{background:red;}
\ No newline at end of file |