diff --git a/README.md b/README.md index f67e6a1..93e55a1 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,9 @@ $defer = new \shinsenter\Defer(); $defer->append_defer_js = false; $defer->default_defer_time = 10; +// Add custom splash screen +$defer->custom_splash_screen = '
Loading
'; + // Page optimizations $defer->enable_preloading = true; $defer->enable_dns_prefetch = true; diff --git a/assets/helpers.js b/assets/helpers.js index 4f04310..22d862e 100644 --- a/assets/helpers.js +++ b/assets/helpers.js @@ -38,10 +38,14 @@ var PROJECT_NAME = 'defer.js'; var CLASS_PREFIX = 'defer-'; var CLASS_SUFFIX = 'deferjs'; - var DATA_PREFIX = 'data-'; + + var COPY_COMMON = 'font-size:14px;color:#fff;'+'padding:2px;border-radius:'; + var COPY_TEXT = '%c ' + PROJECT_NAME + ' '; + var COPY_STYLE = COPY_COMMON + '4px;background:#2a313c'; var JQUERY = 'jQuery'; var NOOP = Function(); + var DATA_PREFIX = 'data-'; var GET_ATTRIBUTE = 'getAttribute'; var IS_CHROME = typeof window.chrome == 'object' && window.navigator.userAgent.indexOf('Trident/') == -1; @@ -73,30 +77,22 @@ t: 10 }; - var log = (console.log || NOOP) - .bind(console); - var defer = window.defer || NOOP; - var deferimg = window.deferimg || NOOP; + var log = (console.log || NOOP).bind(console); + var defer = window.defer || NOOP; + var deferimg = window.deferimg || NOOP; var deferiframe = window.deferiframe || NOOP; var old_ready; function copyright() { - var text = '%c shinsenter %c ' + PROJECT_NAME + ' '; - var common = 'font-size:16px;color:#fff;padding:4px;border-radius:'; - var style1 = common + '4px 0 0 4px;background:#2a313c'; - var style2 = common + '0 4px 4px 0;background:#e61e25'; - if (IS_CHROME) { - log(text, style1, style2); + log(COPY_TEXT, COPY_STYLE); } log([ - 'This page was optimized with ' + PROJECT_NAME, + 'Optimized by ' + PROJECT_NAME, '(c) 2019 Mai Nhut Tan ', - '', 'Github: ' + PROJECT_URL + PROJECT_NAME, - 'PHP lib: ' + PROJECT_URL + 'defer.php', - 'WordPress: https://wordpress.org/plugins/shins-pageload-magic/' + 'PHP lib: ' + PROJECT_URL + 'defer.php' ].join('\n')); } diff --git a/composer.lock b/composer.lock index a5f548a..935afca 100644 --- a/composer.lock +++ b/composer.lock @@ -8,20 +8,21 @@ "packages": [ { "name": "symfony/http-foundation", - "version": "v4.2.7", + "version": "v4.3.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "6ebbe61f48069033225c9d3fa7eb5ed116d766d6" + "reference": "b7e4945dd9b277cd24e93566e4da0a87956392a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6ebbe61f48069033225c9d3fa7eb5ed116d766d6", - "reference": "6ebbe61f48069033225c9d3fa7eb5ed116d766d6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b7e4945dd9b277cd24e93566e4da0a87956392a9", + "reference": "b7e4945dd9b277cd24e93566e4da0a87956392a9", "shasum": "" }, "require": { "php": "^7.1.3", + "symfony/mime": "^4.3", "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { @@ -31,7 +32,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -58,7 +59,128 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-04-17T14:56:00+00:00" + "time": "2019-06-06T10:05:02+00:00" + }, + { + "name": "symfony/mime", + "version": "v4.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "ec2c5565de60e03f33d4296a655e3273f0ad1f8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/ec2c5565de60e03f33d4296a655e3273f0ad1f8b", + "reference": "ec2c5565de60e03f33d4296a655e3273f0ad1f8b", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "egulias/email-validator": "^2.0", + "symfony/dependency-injection": "~3.4|^4.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A library to manipulate MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "time": "2019-06-04T09:22:54+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c766e95bec706cdd89903b1eda8afab7d7a6b7af", + "reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php72": "^1.9" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "time": "2019-03-04T13:44:35+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -118,6 +240,61 @@ "shim" ], "time": "2019-02-06T07:57:58+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/ab50dcf166d5f577978419edd37aa2bb8eabce0c", + "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2019-02-06T07:57:58+00:00" } ], "packages-dev": [], diff --git a/public/helpers.min.js b/public/helpers.min.js index 10f5a76..f5a03ae 100644 --- a/public/helpers.min.js +++ b/public/helpers.min.js @@ -1 +1 @@ -!function(e,n,t,i){var o,r='https://github.com/shinsenter/',a='defer.js',s='defer-',u='deferjs',c='data-',d='jQuery',f=Function(),l='getAttribute',p='object'==typeof e.chrome&&-1==e.navigator.userAgent.indexOf('Trident/'),m=':not(['+c+'lazied]):not(['+c+'ignore])',g='['+c+'src]'+m,h='addEventListener',b='load',y=['img'+g,'picture'+m,'[data-style]'+m].join(','),v=['iframe'+g,'frame'+g,'audio'+m,'video'+m].join(','),j={c:s+'lazied',l:s+'loading',d:s+'loaded',h:n.getElementsByTagName('html').item(0),t:10},x=(t.log||f).bind(t),T=e.defer||f,z=e.deferimg||f,N=e.deferiframe||f;function k(){p&&x('%c shinsenter %c defer.js ','font-size:16px;color:#fff;padding:4px;border-radius:4px 0 0 4px;background:#2a313c','font-size:16px;color:#fff;padding:4px;border-radius:0 4px 4px 0;background:#e61e25'),x(['This page was optimized with '+a,'(c) 2019 Mai Nhut Tan ','','Github: '+r+a,'PHP lib: '+r+'defer.php','WordPress: https://wordpress.org/plugins/shins-pageload-magic/'].join('\n'))}function A(e,n){return e.split(' ').filter(function(e){return''!=e&&e!=n})}function w(e,n){var t=A(e.className,n);t.push(n),e.className=t.join(' ')}function M(e,n){e.className=A(e.className,n).join(' ')}function P(e){var n,t,i=e[l](c+'src');function o(){n&&(clearTimeout(n),n=null),M(e,j.l),w(e,j.d)}w(e,j.l),null!==(t=/(?:youtube(?:-nocookie)?\.com\/(?:[^/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/.exec(i))&&(e.style.background='transparent url(https://img.youtube.com/vi/'+t[1]+'/hqdefault.jpg) 50% 50% / cover no-repeat'),e.hasAttribute(c+'ignore')||i&&e.src==i||!i&&e[l](c+'style')?o():(e[h](b,o),n=setTimeout(o,3e3))}function C(){z(y,j.t,j.c,P,{rootMargin:'150%'}),N(v,j.t,j.c,P,{rootMargin:'200%'})}j.copyright=k,j.debounce=function(t,i,o,r){return function(){var e=this,n=arguments;o||clearTimeout(r),o&&r||(r=setTimeout(function(){r=null,t.apply(e,n)},i))}},j.defermedia=C,j.addClass=w,(j.removeClass=M)(j.h,'no-'+u),w(j.h,u),e.defer_helper=j,e[h](b,function(){!o&&d in e&&'fn'in e[d]&&(o=e[d].fn.ready,e[d].fn.ready=function(e){return T(function(){o(e)}),this})}),C(),k()}(this,document,console); \ No newline at end of file +!function(e,n,t,i){var o,r='https://github.com/shinsenter/',a='defer.js',u='defer-',c='deferjs',d='jQuery',s=Function(),f='data-',l='getAttribute',m='object'==typeof e.chrome&&-1==e.navigator.userAgent.indexOf('Trident/'),h=':not(['+f+'lazied]):not(['+f+'ignore])',p='['+f+'src]'+h,g='addEventListener',b='load',y=['img'+p,'picture'+h,'[data-style]'+h].join(','),v=['iframe'+p,'frame'+p,'audio'+h,'video'+h].join(','),j={c:u+'lazied',l:u+'loading',d:u+'loaded',h:n.getElementsByTagName('html').item(0),t:10},T=(t.log||s).bind(t),N=e.defer||s,x=e.deferimg||s,z=e.deferiframe||s;function A(){m&&T('%c defer.js ','font-size:14px;color:#fff;padding:2px;border-radius:4px;background:#2a313c'),T(['Optimized by '+a,'(c) 2019 Mai Nhut Tan ','Github: '+r+a,'PHP lib: '+r+'defer.php'].join('\n'))}function k(e,n){return e.split(' ').filter(function(e){return''!=e&&e!=n})}function M(e,n){var t=k(e.className,n);t.push(n),e.className=t.join(' ')}function C(e,n){e.className=k(e.className,n).join(' ')}function E(e){var n,t,i=e[l](f+'src');function o(){n&&(clearTimeout(n),n=null),C(e,j.l),M(e,j.d)}M(e,j.l),null!==(t=/(?:youtube(?:-nocookie)?\.com\/(?:[^/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/.exec(i))&&(e.style.background='transparent url(https://img.youtube.com/vi/'+t[1]+'/hqdefault.jpg) 50% 50% / cover no-repeat'),e.hasAttribute(f+'ignore')||i&&e.src==i||!i&&e[l](f+'style')?o():(e[g](b,o),n=setTimeout(o,3e3))}function O(){x(y,j.t,j.c,E,{rootMargin:'150%'}),z(v,j.t,j.c,E,{rootMargin:'200%'})}j.copyright=A,j.debounce=function(t,i,o,r){return function(){var e=this,n=arguments;o||clearTimeout(r),o&&r||(r=setTimeout(function(){r=null,t.apply(e,n)},i))}},j.defermedia=O,j.addClass=M,(j.removeClass=C)(j.h,'no-'+c),M(j.h,c),e.defer_helper=j,e[g](b,function(){!o&&d in e&&'fn'in e[d]&&(o=e[d].fn.ready,e[d].fn.ready=function(e){return N(function(){o(e)}),this})}),O(),A()}(this,document,console); \ No newline at end of file diff --git a/src/DeferInterface.php b/src/DeferInterface.php index ee27779..c0b6967 100644 --- a/src/DeferInterface.php +++ b/src/DeferInterface.php @@ -58,6 +58,18 @@ ])); define('DEFER_MINIFY_HTML_IGNORE', 'not(parent::*[self::textarea or self::code or self::pre or self::script])'); + + // Splash screen + + define('DEFER_SLASH_TEMPLATE', implode('', [ + '', + '
%s
', + ])); + + define('DEFER_SLASH_HIDE_SCRIPT', implode('', [ + '', + '', + ])); } abstract class DeferInterface @@ -68,6 +80,10 @@ abstract class DeferInterface const HELPERS_URL = DEFER_JS_ROOT . '/public/helpers.min.js'; const INLINE_CSS_URL = DEFER_JS_ROOT . '/public/styles.min.css'; + // For splash screen + const SLASH_TEMPLATE = DEFER_SLASH_TEMPLATE; + const SLASH_HIDE_SCRIPT = DEFER_SLASH_HIDE_SCRIPT; + // Polyfill & library's fingerprint const POLYFILL_URL = 'https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver'; const FINGERPRINT_URL = 'https://raw.githubusercontent.com/shinsenter/defer.php/footprint/copyright.txt'; diff --git a/src/DeferOptimizer.php b/src/DeferOptimizer.php index ea80552..44c1c09 100644 --- a/src/DeferOptimizer.php +++ b/src/DeferOptimizer.php @@ -51,6 +51,9 @@ protected function optimize() $this->addMissingMeta(); $this->addFingerprint(); + // Add custom splash screen + $this->addCustomSplashScreen(); + // Minify $this->minifyOutputHTML(); } @@ -612,6 +615,23 @@ protected function optimizeBackgroundTags() } } + /** + * Added splash screen + * + * @since 1.0.15 + */ + protected function addCustomSplashScreen() + { + $splash = $this->custom_splash_screen; + + if (empty($splash)) { + return; + } + + $this->prependHtml($this->body, sprintf(static::SLASH_TEMPLATE, $splash)); + $this->appendHtml($this->body, static::SLASH_HIDE_SCRIPT); + } + /* |-------------------------------------------------------------------------- | Other helper functions @@ -755,9 +775,15 @@ protected function getPreloadType($node) if (is_a($node, DOMElement::class)) { switch ($node->nodeName) { case static::LINK_TAG: + if ($node->hasAttribute(static::ATTR_AS)) { + $as = $node->getAttribute(static::ATTR_AS); + break; + } + if (in_array($node->getAttribute(static::ATTR_REL), [static::REL_DNSPREFETCH, static::REL_PRECONNECT])) { break; } + $as = static::PRELOAD_STYLE; break; case static::STYLE_TAG: diff --git a/src/DeferOptions.php b/src/DeferOptions.php index 0f1bf24..259c707 100644 --- a/src/DeferOptions.php +++ b/src/DeferOptions.php @@ -30,6 +30,9 @@ trait DeferOptions 'append_defer_js' => false, 'default_defer_time' => 10, + // Splash screen + 'custom_splash_screen' => '', + // Page optimizations 'enable_preloading' => true, 'enable_dns_prefetch' => true, diff --git a/src/DeferParser.php b/src/DeferParser.php index 1b2fa2f..fcb5869 100644 --- a/src/DeferParser.php +++ b/src/DeferParser.php @@ -496,7 +496,7 @@ protected function createNode($tag, $content = null, $attributes = []) if (is_array($content)) { $attributes = $content; $content = null; - } elseif (is_string($content)) { + } elseif (in_array($tag, [static::SCRIPT_TAG]) && is_string($content)) { $content = htmlspecialchars($content); } @@ -528,6 +528,43 @@ protected function removeNode(&$node) return $this; } + /** + * Prepend HTML + * + * @since 1.0.15 + * @param DOMNode $node + * @param string $html + * @return self + */ + protected function prependHtml(&$node, $html) + { + $tpl = $this->dom->createDocumentFragment(); + $tpl->appendXML($html); + + $the_anchor = $node->childNodes->item(0); + $node->insertBefore($tpl, $the_anchor); + + return $node; + } + + /** + * Append HTML + * + * @since 1.0.15 + * @param DOMNode $node + * @param string $html + * @return self + */ + protected function appendHtml(&$node, $html) + { + $tpl = $this->dom->createDocumentFragment(); + $tpl->appendXML($html); + + $node->appendChild($tpl); + + return $node; + } + /** * Add the resouce URL to the preload list *