diff --git a/app/main/controller/appcontroller.php b/app/main/controller/appcontroller.php
index d730a82e..305b2c10 100644
--- a/app/main/controller/appcontroller.php
+++ b/app/main/controller/appcontroller.php
@@ -10,6 +10,7 @@ namespace Controller;
use Controller\Ccp as Ccp;
use lib\Config;
+use lib\Resource;
class AppController extends Controller {
@@ -58,7 +59,13 @@ class AppController extends Controller {
* @param \Base $f3
*/
public function init(\Base $f3) {
-
+ $resource = Resource::instance();
+ $resource->register('script', 'app/login');
+ $resource->register('script', 'app/mappage', 'prefetch');
+ $resource->register('image', 'pf-bg.jpg');
+ $resource->register('image', 'pf-header-bg.jpg');
+ $resource->register('image', 'landing/eve_sso_login_buttons_large_black.png');
+ $resource->register('image', 'landing/eve_sso_login_buttons_large_black_hover.png');
}
}
\ No newline at end of file
diff --git a/app/main/controller/controller.php b/app/main/controller/controller.php
index acfb5e76..0f785c74 100644
--- a/app/main/controller/controller.php
+++ b/app/main/controller/controller.php
@@ -7,8 +7,10 @@
*/
namespace Controller;
+
use Controller\Api as Api;
use lib\Config;
+use lib\Resource;
use lib\Monolog;
use lib\Socket;
use lib\Util;
@@ -76,8 +78,7 @@ class Controller {
if($f3->get('AJAX')){
header('Content-type: application/json');
}else{
- // js path (build/minified or raw uncompressed files)
- $f3->set('tplPathJs', 'public/js/' . Config::getPathfinderData('version') );
+ $this->initResource($f3);
$this->setTemplate( Config::getPathfinderData('view.index') );
}
@@ -91,6 +92,12 @@ class Controller {
* @param \Base $f3
*/
public function afterroute(\Base $f3){
+ // send preload/prefetch headers
+ $resource = Resource::instance();
+ if($resource->getOption('output') === 'header'){
+ header($resource->buildHeader(), false);
+ }
+
if($this->getTemplate()){
// Ajax calls don“t need a page render..
// this happens on client side
@@ -145,6 +152,34 @@ class Controller {
}
+ /**
+ * init new Resource handler
+ * @param \Base $f3
+ * @throws \Exception\PathfinderException
+ */
+ protected function initResource(\Base $f3){
+ $resource = Resource::instance();
+ $resource->setOption('filePath', [
+ 'style' => $f3->get('BASE') . '/public/css/' . Config::getPathfinderData('version'),
+ 'script' => $f3->get('BASE') . '/public/js/' . Config::getPathfinderData('version'),
+ 'font' => $f3->get('BASE') . '/public/fonts',
+ 'image' => $f3->get('BASE') . '/public/img'
+ ]);
+
+ $resource->register('style', 'pathfinder');
+
+ $resource->register('script', 'lib/require');
+ $resource->register('script', 'app');
+
+ $resource->register('font', 'oxygen-regular-webfont');
+ $resource->register('font', 'oxygen-bold-webfont');
+ $resource->register('font', 'fa-regular-400');
+ $resource->register('font', 'fa-solid-900');
+ $resource->register('font', 'fa-brands-400');
+
+ $f3->set('tplResource', $resource);
+ }
+
/**
* get cookies "state" information
* -> whether user accepts cookies
diff --git a/app/main/controller/setup.php b/app/main/controller/setup.php
index ec08ce9d..db954059 100644
--- a/app/main/controller/setup.php
+++ b/app/main/controller/setup.php
@@ -140,6 +140,8 @@ class Setup extends Controller {
* @throws \Exception\PathfinderException
*/
function beforeroute(\Base $f3, $params): bool {
+ $this->initResource($f3);
+
// init dbLib class. Manages all DB connections
$this->dbLib = DB\Database::instance();
@@ -152,9 +154,6 @@ class Setup extends Controller {
// body element class
$f3->set('tplBodyClass', 'pf-landing');
- // js path (build/minified or raw uncompressed files)
- $f3->set('tplPathJs', 'public/js/' . Config::getPathfinderData('version') );
-
return true;
}
diff --git a/app/main/lib/config.php b/app/main/lib/config.php
index 02ebe7e7..5479883c 100644
--- a/app/main/lib/config.php
+++ b/app/main/lib/config.php
@@ -8,7 +8,6 @@
namespace lib;
-
use controller\LogController;
use Exception;
diff --git a/app/main/lib/resource.php b/app/main/lib/resource.php
new file mode 100644
index 00000000..1187f563
--- /dev/null
+++ b/app/main/lib/resource.php
@@ -0,0 +1,240 @@
+ 'style',
+ 'script' => 'script',
+ 'font' => 'font',
+ 'image' => 'image'
+ ];
+
+ /**
+ * default link "type" attributes
+ */
+ const ATTR_TYPE = [
+ 'font' => 'font/woff2'
+ ];
+
+ /**
+ * default additional attributes by $group
+ */
+ const ATTR_ADD = [
+ 'font' => ['crossorigin' => 'anonymous']
+ ];
+
+ /**
+ * absolute file path -> use setOption() for update
+ * @var array
+ */
+ private $filePath = [
+ 'style' => '',
+ 'script' => '',
+ 'font' => '',
+ 'image' => ''
+ ];
+
+ /**
+ * default file extensions by $group
+ * -> used if no fileExtension found in $file
+ * @var array
+ */
+ private $fileExt = [
+ 'style' => 'css',
+ 'script' => 'js',
+ 'font' => 'woff2'
+ ];
+
+ /**
+ * output type
+ * -> 'inline' -> render inline HTML tags
+ * -> 'header' -> send "Link" HTTP Header with request
+ * @see buildLinks()
+ * @see buildHeader()
+ * @var string
+ */
+ private $output = 'inline';
+
+ /**
+ * resource file cache
+ * @var array
+ */
+ private $resources = [];
+
+ /**
+ * set option
+ * @param string $option
+ * @param $value
+ */
+ public function setOption(string $option, $value){
+ $this->$option = $value;
+ }
+
+ /**
+ * get option
+ * @param string $option
+ * @return mixed|null
+ */
+ public function getOption(string $option){
+ return isset($this->$option) ? $this->$option : null;
+ }
+
+ /**
+ * register new resource $file
+ * @param string $group
+ * @param string $file
+ * @param string $rel
+ */
+ public function register(string $group, string $file, string $rel = self::ATTR_REL){
+ $this->resources[$group][$file] = ['options' => ['rel' => $rel]];
+ }
+
+ /**
+ * get resource path/file.ext
+ * @param string $group
+ * @param string $file
+ * @return string
+ */
+ public function getLink(string $group, string $file) : string {
+ $link = $this->getPath($group) . '/' . $file;
+ // add extension if not already part of the file
+ // -> allows switching between extensions (e.g. .jpg, .png) for the same image
+ $link .= empty(pathinfo($file, PATHINFO_EXTENSION)) ? '.' . $this->getFileExtension($group) : '';
+ return $link;
+ }
+
+ /**
+ * get resource path
+ * @param string $group
+ * @return string
+ */
+ public function getPath(string $group) : string {
+ return $this->filePath[$group];
+ }
+
+ /**
+ * build inline HTML tags for resources
+ * @return string
+ */
+ public function buildLinks(){
+ $this->build();
+ $links = [];
+ foreach($this->resources as $group => $resources){
+ foreach($resources as $file => $conf){
+ $resourceHeader = ' $value){
+ $resourceHeader .= ' ' . $attr . '="' . $value . '"';
+ // insert href attr after rel attr -> better readability
+ if($attr == 'rel'){
+ $resourceHeader .= ' href="' . $conf['link'] . '"';
+ }
+ }
+ $links[] = $resourceHeader . '>';
+ }
+ }
+ return "\n\t" . implode("\n\t", $links);
+ }
+
+ /**
+ * build HTTP header for resource preload
+ * -> all registered resources combined in a single header
+ * @link https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/#automatic-push
+ * @return string
+ */
+ public function buildHeader() : string {
+ $this->build();
+ $headers = [];
+ foreach($this->resources as $group => $resources){
+ foreach($resources as $file => $conf){
+ $resourceHeader = '<' . $conf['link'] . '>';
+ foreach($conf['options'] as $attr => $value){
+ $resourceHeader .= '; ' . $attr . '="' . $value . '"';
+ }
+ $headers[] = $resourceHeader;
+ }
+ }
+ return 'Link: ' . implode(', ', $headers);
+ }
+
+ /**
+ * build resource data
+ * -> add missing attributes to resources
+ */
+ protected function build(){
+ foreach($this->resources as $group => &$resources){
+ foreach($resources as $file => &$conf){
+ if(empty($conf['link'])){
+ $conf['link'] = $this->getLink($group, $file);
+ }
+
+ if( empty($conf['options']['rel']) ){
+ $conf['options']['rel'] = self::ATTR_REL;
+ }
+ if( empty($conf['options']['as']) ){
+ $conf['options']['as'] = $group;
+ }
+ if( empty($conf['options']['type']) && !empty($attrType = $this->getLinkAttrType($group)) ){
+ $conf['options']['type'] = $attrType;
+ }
+
+ if( !empty($additionalAttr = $this->getAdditionalAttrs($group)) ){
+ $conf['options'] = $conf['options'] + $additionalAttr;
+ }
+ }
+ unset($options); // unset ref
+ }
+ unset($resources); // unset ref
+ }
+
+ /**
+ * get 'as' attribute (potential destination) by resource $group
+ * @link https://w3c.github.io/preload/#as-attribute
+ * @param string $group
+ * @return string
+ */
+ protected function getLinkAttrAs(string $group) : string {
+ return isset(self::ATTR_AS[$group]) ? self::ATTR_AS[$group] : '';
+ }
+
+ /**
+ * get 'type' attribute by resource $group
+ * @link https://w3c.github.io/preload/#early-fetch-of-critical-resources
+ * @param string $group
+ * @return string
+ */
+ protected function getLinkAttrType(string $group) : string {
+ return isset(self::ATTR_TYPE[$group]) ? self::ATTR_TYPE[$group] : '';
+ }
+
+ /**
+ * get additional attributes by $group
+ * -> e.g. or fonts
+ * @param string $group
+ * @return array
+ */
+ protected function getAdditionalAttrs(string $group) : array {
+ return isset(self::ATTR_ADD[$group]) ? self::ATTR_ADD[$group] : [];
+ }
+
+ protected function getFileExtension(string $group) : string {
+ return isset($this->fileExt[$group]) ? $this->fileExt[$group] : '';
+ }
+}
\ No newline at end of file
diff --git a/public/templates/view/index.html b/public/templates/view/index.html
index 35b5004d..91bc8712 100644
--- a/public/templates/view/index.html
+++ b/public/templates/view/index.html
@@ -43,45 +43,28 @@
-
{* Chrome, Firefox OS and Opera *}
{* Windows Phone *}
{* iOS Safari *}
{* Youtube verification code *}
-