<?php
/*
 * Copyright (c) 2017-2024 Aimy Extensions, Netzum Sorglos Software GmbH
 * Copyright (c) 2015-2017 Aimy Extensions, Lingua-Systems Software GmbH
 *
 * https://www.aimy-extensions.com/
 *
 * License: GNU GPLv2, see LICENSE.txt within distribution and/or
 *          https://www.aimy-extensions.com/software-license.html
 */
 defined( '_JEXEC' ) or die(); jimport( 'joomla.plugin.plugin' ); use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Table; use Joomla\CMS\Uri\Uri; class plgSystemAimySpeedOptimization extends CMSPlugin { protected $autoloadLanguage = true; private $html5 = null; private $dom = null; private $app = null; private $queue = null; private $ctx = null; public function __construct( &$subject, $config ) { parent::__construct( $subject, $config ); $this->app = Factory::getApplication(); $this->queue = array( 'js' => array( 'links' => array(), 'code' => array() ), 'css' => array() ); $this->ctx = $this->app->input->getCmd( 'option', 'unknown' ) . '.' . $this->app->input->getCmd( 'view', 'unknown' ); } public function onBeforeRender() { if ( ! $this->in_frontend() ) { $this->update_plugin_ordering(); return $this->handle_htaccess_config_change(); } } public function onAfterRender() { if ( ! $this->in_frontend() or $this->is_unsupported_ctx() or $this->is_frontend_editing() or ( $this->params->get( 'exclude_pages', false ) && $this->is_excluded_page() ) ) { return; } $doc = Factory::getDocument(); if ( $doc->getType() !== 'html' ) { return; } $html = $this->app->getBody(); if ( ! is_string( $html ) or empty( $html ) ) { return false; } require_once( dirname( __FILE__ ) . '/html5-bundle.php' ); try { $this->html5 = new AimySpeedOptimization\Masterminds\HTML5(); $this->dom = @$this->html5->loadHTML( $html ); } catch ( Exception $e ) { error_log( "Aimy Speed Optimization: $e" ); return; } if ( $this->params->get( 'eliminate_render_blocking', false ) && ! $this->page_should_not_be_unblocked() ) { $this->handle_js(); $this->handle_css(); } if ( $this->params->get( 'image_action', false ) ) { $this->handle_images(); } $this->add_js_loader(); $html = ''; if ( $this->params->get( 'minify_html', false ) ) { $html = $this->get_minified_html(); } else { $html = $this->html5->saveHTML( $this->dom ); $html = preg_replace( '#(<!xml:)(lang="\w{2}(?:-\w{2})?")\s+lang="\w{2}(?:-\w{2})?"#', '$1', $html ); } $this->app->setBody( $html ); } private function update_htaccess( $state ) { require_once( dirname( __FILE__ ) . '/HtaccessHelper.php' ); if ( ! AimySpeedOptimizationHtaccessHelper::htaccess_exists() ) { $this->app->enqueueMessage( 'Aimy Speed Optimization: ' . Text::_( 'AIMY_SO_ERR_NO_HTACCESS_FILE' ), 'error' ); return; } try { AimySpeedOptimizationHtaccessHelper::cleanup(); AimySpeedOptimizationHtaccessHelper::modify( $state ); } catch ( Exception $e ) { $this->app->enqueueMessage( 'Aimy Speed Optimization: ' . $e->getMessage(), 'error' ); return false; } $this->app->enqueueMessage( 'Aimy Speed Optimization: ' . Text::_( 'AIMY_SO_MSG_HTACCESS_UPDATED' ), 'notice' ); return true; } private function handle_htaccess_config_change() { if ( $this->ctx != 'com_plugins.plugin' && $this->ctx != 'com_plugins.plugins' ) { return; } $old_state = $this->app->getUserState( 'htaccess_state', false, 'aimyspeedoptimization' ); if ( $old_state === false ) { $old_state = $this->params->get( 'htaccess_state', '' ); } $on = array(); if ( $this->params->get( 'enable_browser_caching', 0 ) ) { $on[] = 'browsercache'; } if ( $this->params->get( 'enable_compression', 0 ) ) { $on[] = 'compress'; } $new_state = implode( '+', $on ); if ( $old_state == $new_state ) { return; } if ( ! $this->update_htaccess( $new_state ) ) { return false; } $this->app->setUserState( 'htaccess_state', $new_state, 'aimyspeedoptimization' ); $this->params->set( 'htaccess_state', $new_state ); $tbl = Table::getInstance( 'extension' ); $tbl->load( array( 'element' => 'aimyspeedoptimization' ) ); $tbl->set( 'params', $this->params->toString() ); $tbl->store(); return; } private function update_plugin_ordering() { if ( ! $this->params->get( 'order_as_last', true ) ) { return; } if ( $this->ctx != 'com_plugins.plugin' && $this->ctx != 'com_plugins.plugins' ) { return; } $exts = Table::getInstance( 'extension' ); $exts->find( array( 'type' => 'plugin', 'folder' => 'system' ) ); $next = $exts->getNextOrder(); $plg = Table::getInstance( 'extension' ); $plg->load( array( 'element' => 'aimyspeedoptimization' ) ); if ( $plg->get( 'ordering', 0 ) + 1 < $next ) { $plg->set( 'ordering', $next ); $plg->store(); $this->app->enqueueMessage( 'Aimy Speed Optimization: ' . Text::_( 'AIMY_SO_PLG_ORDERED_AS_LAST' ) ); } } private function handle_images() { $els = self::DomNodeList_to_array( $this->dom->getElementsByTagName( 'img' ) ); $action = $this->params->get( 'image_action', 'defer' ); $select = $this->params->get( 'image_selection', 'uploaded' ); $wrap = $this->params->get( 'wrap_in_link', false ); $images_base_dir = Uri::root( true ) . '/images/'; $cache_base_dir = Uri::root( true ) . '/cache/'; $embed_images = $this->params->get( 'embed_images', false ); $embed_image_maxsize = intVal( $this->params->get( 'embed_image_max_size', 5 ) ) * 1024; foreach ( $this->dom->getElementsByTagName( 'picture' ) as $pic ) { foreach ( $pic->childNodes as $el ) { if ( isset( $el->nodeName ) && $el->nodeName == 'source' ) { $els[] = $el; } } } foreach ( $els as $i => $el ) { $isA = strtolower( $el->nodeName ); if ( ( $isA == 'img' && ( ! $el->hasAttribute( 'src' ) && ! $el->hasAttribute( 'srcset' ) ) ) || ( $isA == 'source' && ! $el->hasAttribute( 'srcset' ) ) ) { unset( $els[ $i ] ); continue; } $src = false; if ( $isA == 'source' ) { $v = $el->getAttribute( 'srcset' ); if ( ! empty( $v ) ) { $src = self::get_first_srcset_path( $v ); } } else { $src = $el->getAttribute( 'src' ); } if ( empty( $src ) or strpos( $src, 'data:' ) === 0 ) { unset( $els[ $i ] ); continue; } switch ( $select ) { case 'class_defer': if ( ! $el->hasAttribute( 'class' ) ) { unset( $els[ $i ] ); break; } $class = $el->getAttribute( 'class' ); if ( ! preg_match( '#\bdefer\b#', $class ) ) { unset( $els[ $i ] ); break; } break; case 'uploaded': default: if ( ! self::is_local_link( $src ) || ( strpos( $src, $images_base_dir ) === false && strpos( $src, 'images/' ) !== 0 && strpos( $src, $cache_base_dir ) === false && strpos( $src, 'cache/' ) !== 0 ) ) { unset( $els[ $i ] ); break; } break; } } $els = array_values( $els ); if ( ( $n = intVal( $this->params->get( 'skip_first_n', 0 ) ) ) > 0 ) { while ( $n > 0 && count( $els ) > 0 ) { array_shift( $els ); $n--; } } if ( count( $els ) === 0 ) { return; } foreach ( $els as $i => $el ) { $isA = strtolower( $el->nodeName ); $src = false; if ( $isA == 'img' ) { $src = trim( $el->getAttribute( 'src' ) ); } elseif ( $isA == 'source' ) { $src = self::get_first_srcset_path( $el->getAttribute( 'srcset' ) ); } if ( ! $src ) { continue; } $path = JPATH_ROOT . '/' . preg_replace( '/\s*[#\?].*$/', '', preg_replace( '#^' . Uri::root( true ) . '/?#', '/', preg_replace( '#https?://[^/]+/#i', '/', $src ) ) ); if ( preg_match( '#/$#', $path ) ) { continue; } if ( self::is_local_link( $src ) && preg_match( '#\.(jpe?g|png|gif|tiff?|webp)$#i', $path ) && is_file( $path ) ) { if ( $isA == 'img' && ( ! $el->hasAttribute( 'width' ) or ! $el->hasAttribute( 'height' ) ) ) { $ii = getimagesize( $path ); if ( is_array( $ii ) ) { $el->setAttribute( 'width', $ii[ 0 ] ); $el->setAttribute( 'height', $ii[ 1 ] ); } } if ( $embed_images ) { $fsize = filesize( $path ); if ( $fsize && $fsize <= $embed_image_maxsize ) { $data = self::get_image_data_uri( $path ); if ( $data ) { $attr = ( $isA == 'source' ) ? 'srcset' : 'src'; $el->setAttribute( $attr, $data ); continue; } } } } if ( $action == 'dimensions' ) { continue; } if ( $el->hasAttribute( 'loading' ) && $el->getAttribute( 'loading' ) == 'lazy' ) { continue; } if ( $action == 'lazy-native' ) { if ( $isA == 'img' ) { $el->setAttribute( 'loading', 'lazy' ); } continue; } if ( $el->hasAttribute( 'srcset' ) ) { $el->setAttribute( 'data-srcset', $el->getAttribute( 'srcset' ) ); $el->removeAttribute( 'srcset' ); } if ( $isA == 'img' && $el->hasAttribute( 'src' ) ) { $el->setAttribute( 'data-src', $src ); $el->setAttribute( 'src', self::get_inline_image() ); } $class = $el->hasAttribute( 'class' ) ? $el->getAttribute( 'class' ) : ''; if ( ! preg_match( '#\bdefer\b#', $class ) ) { $class .= ' defer'; $el->setAttribute( 'class', trim( $class ) ); } if ( $isA == 'img' ) { if ( $wrap && ! self::is_wrapped_in_link( $el ) && ! self::is_descendant_of_class( $el, 'AimyVideoEmbedderVideoPlaceholder' ) ) { $a = $this->dom->createElement( 'a' ); $href = $this->dom->createAttribute( 'href' ); $cls = $this->dom->createAttribute( 'class' ); $href->nodeValue = $src; $cls->nodeValue = 'defer-wrap'; $a->appendChild( $href ); $a->appendChild( $cls ); $el->parentNode->replaceChild( $a, $el ); $a->appendChild( $el ); } } } if ( $action == 'defer' or $action == 'lazy' ) { array_unshift( $this->queue[ 'js' ][ 'links' ], sprintf( '%s/media/plg_aimyspeedoptimization/%s', Uri::root( true ), ( $action == 'defer' ? 'loadDeferredImages.js' : 'blazy.js' ) ) ); if ( $wrap ) { $h = $this->dom->getElementsByTagName( 'head' )->item( 0 ); if ( $h ) { $s = $this->dom->createElement( 'style' ); $s->nodeValue = '.defer-wrap{cursor:default;}'; $h->appendChild( $s ); } } } } private function handle_js() { $scripts = self::DomNodeList_to_array( $this->dom->getElementsByTagName( 'script' ) ); if ( empty( $scripts ) ) { return; } $auto_preload = $this->params->get( 'auto_preload', false ); if ( ! self::is_joomla3() ) { foreach ( $scripts as $script ) { if ( ! self::is_js_script_element( $script ) || ! $script->hasAttribute( 'src' ) ) { continue; } $src = $script->getAttribute( 'src' ); if ( strpos( $src, 'media/system/js/' ) === false && strpos( $src, 'media/com_finder/js' ) === false ) { continue; } if ( preg_match( '#^(.*)-es5\.min\.js#', $src, $m ) ) { $es6_name = $m[ 1 ] . '.min.js'; foreach ( $scripts as $_s ) { if ( ! self::is_js_script_element( $_s ) || ! $_s->hasAttribute( 'src' ) ) { continue; } $_src = preg_replace( '#\?.*$#', '', $_s->getAttribute( 'src' ) ); if ( $_src == $es6_name ) { $script->parentNode->removeChild( $script ); } } } } } foreach ( $scripts as $script ) { if ( ! $script->parentNode ) { continue; } if ( ! self::is_js_script_element( $script ) || $script->hasAttribute( 'async' ) ) { continue; } if ( $script->hasAttribute( 'src' ) ) { $link = $script->getAttribute( 'src' ); if ( ! empty( $link ) && ! self::is_unsupported_js( $link ) ) { $script->parentNode->removeChild( $script ); $type = strtolower( $script->getAttribute( 'type' ) ?: '' ); if ( $type == 'module' ) { $this->queue[ 'js' ][ 'links' ][] = 'module:' . $link; if ( $auto_preload ) { $this->add_js_module_preload_link( $link ); } } else { $this->queue[ 'js' ][ 'links' ][] = $link; if ( $auto_preload ) { $this->add_preload_link( $link, 'script' ); } } } } else { require_once( dirname( __FILE__ ) . '/JShrinkMinifier.php' ); $code = trim( $script->nodeValue ); try { $code = JShrink\Minifier::minify( $code ); } catch ( Exception $e ) { error_log( 'Aimy Speed Optimization: ' . $e->getMessage() ); } if ( ! empty( $code ) && ! self::is_unsupported_js_code( $code ) ) { $this->queue[ 'js' ][ 'code' ][] = $code; $script->parentNode->removeChild( $script ); } } } } private function handle_css() { $head = $this->dom->getElementsByTagName( 'head' )->item( 0 ); $els = self::DomNodeList_to_array( $head->childNodes ); $num = 0; $auto_preload = $this->params->get( 'auto_preload', false ); foreach ( $els as $el ) { if ( ! isset( $el->nodeName ) ) { continue; } if ( $el->nodeName == 'link' && $el->hasAttribute( 'rel' ) && $el->getAttribute( 'rel' ) == 'stylesheet' && $el->hasAttribute( 'href' ) ) { $href = $el->getAttribute( 'href' ); $media = $el->hasAttribute( 'media' ) ? $el->getAttribute( 'media' ) : ''; if ( self::is_local_link( $href ) && ! self::requires_http_access( $href ) ) { require_once( dirname( __FILE__ ) . '/MinifyCSS.php' ); $path = self::get_link_path( $href ); $code = null; try { $cssmin = new AimySpeedOptimizationMinifyCSS( JPATH_ROOT, Uri::root( true ), AimySpeedOptimizationMinifyCSS::KEEP_HEADER + AimySpeedOptimizationMinifyCSS::SHORTEN_HEADER ); $fs_path = preg_replace( '#^' . Uri::root( true ) . '/?#', '', $path ); $code = $cssmin->minify_file( $fs_path ); } catch ( Exception $e ) { error_log( 'Aimy Speed Optimization: ' . $fs_path . ': ' . $e->getMessage() ); } if ( empty( $code ) ) { continue; } if ( ! empty( $media ) ) { $code = '@media ' . $media . '{' . $code . '};'; } $nel = $this->dom->createElement( 'style' ); $nid = $this->dom->createAttribute( 'data-asoid' ); $nid->nodeValue = ++$num; $nel->appendChild( $nid ); $nel->appendChild( $this->dom->createTextNode( $code ) ); $head->replaceChild( $nel, $el ); } else { $this->queue[ 'css' ][] = array( 'value' => $href, 'media' => $media, 'id' => ++$num ); $noscript = $this->dom->createElement( 'noscript' ); $noscript->appendChild( $this->dom->createTextNode( $this->dom->saveXML( $el ) ) ); $head->replaceChild( $noscript, $el ); if ( $auto_preload ) { $this->add_preload_link( $href, 'style' ); } } } else if ( $el->nodeName == 'style' ) { $media = $el->hasAttribute( 'media' ) ? $el->getAttribute( 'media' ) : ''; $code = null; require_once( dirname( __FILE__ ) . '/MinifyCSS.php' ); try { $cssmin = new AimySpeedOptimizationMinifyCSS( JPATH_ROOT, Uri::root( true ), AimySpeedOptimizationMinifyCSS::KEEP_HEADER + AimySpeedOptimizationMinifyCSS::SHORTEN_HEADER ); $code = $cssmin->minify( $el->nodeValue, Uri::root( true ) ); } catch ( Exception $e ) { error_log( 'Aimy Speed Optimization: ' . $e->getMessage() ); } if ( empty( $code ) ) { continue; } if ( ! empty( $media ) ) { $code = '@media ' . $media . '{' . $code . '};'; } $el->nodeValue = trim( $code ); $nid = $this->dom->createAttribute( 'data-asoid' ); $nid->nodeValue = ++$num; $el->appendChild( $nid ); } } } private function get_minified_html() { require_once( dirname( __FILE__ ) . '/MinifyHTML.php' ); return AimySpeedOptimizationMinifyHTML::minify_by_dom( $this->dom ); } private function add_js_loader() { if ( empty( $this->queue[ 'js' ][ 'links' ] ) && empty( $this->queue[ 'js' ][ 'code' ] ) && empty( $this->queue[ 'css' ] ) ) { return; } $loader = ''; foreach ( $this->queue[ 'css' ] as $css ) { $loader .= 'AimySpeedOptimization.loadCssLink("' . $css[ 'value' ] . '","' . $css[ 'media' ] . '","' . $css[ 'id' ] . '");'; } $held_dom_ready = 0; foreach ( $this->queue[ 'js' ][ 'links' ] as $src ) { $loader .= '$script("' . $src . '",' . 'function(){'; if ( strpos( $src, 'jquery.js' ) !== false || strpos( $src, 'jquery.min.js' ) !== false || strpos( $src, 'jquery.min' ) !== false || strpos( $src, 'jquery.noconflict.js' ) !== false || strpos( $src, 'jquery-noconflict.js' ) !== false ) { $loader .= 'AimySpeedOptimization.holdDomReadyEvent();'; $held_dom_ready++; } } if ( ! empty( $this->queue[ 'js' ][ 'code' ] ) ) { $loader .= 'AimySpeedOptimization.addInlineScript("' . rawurlencode( implode( ';', $this->queue[ 'js' ][ 'code' ] ) ) . '");'; } while ( $held_dom_ready-- > 0 ) { $loader .= 'AimySpeedOptimization.releaseDomReadyEvent();'; } if ( ! self::is_joomla3() ) { $loader .= 'AimySpeedOptimization.triggerDOMContentLoaded();'; } $loader .= 'AimySpeedOptimization.triggerWindowLoad();'; for ( $i = 0; $i < count( $this->queue[ 'js' ][ 'links' ] ); $i++ ) { $loader .= ';})'; } $loader .= ';'; $frameworks = trim( @file_get_contents( JPATH_ROOT . '/' . 'media' . '/' . 'plg_aimyspeedoptimization' . '/' . 'scriptjs.js' ) ) . ';' . trim( @file_get_contents( JPATH_ROOT . '/' . 'media' . '/' . 'plg_aimyspeedoptimization' . '/' . 'aimyspeedoptimization.js' ) ); $js = $this->dom->createElement( 'script' ); $js->appendChild( $this->dom->createTextNode( $frameworks . $loader ) ); return $this->add_js_element( $js ); } private function add_js_element( &$js ) { $to = null; try { $nodes = self::DomNodeList_to_array( $this->dom->documentElement->childNodes ); foreach ( $nodes as $n ) { if ( $n->nodeType === XML_ELEMENT_NODE && isset( $n->nodeName ) && ( $n->nodeName != 'style' && $n->nodeName != 'script' && $n->nodeName != 'link' && $n->nodeName != 'meta' ) ) { $to = $n; } } } catch ( Exception $e ){ } if ( empty( $to ) ) { return $this->add_head_element( $js ); } return $to->appendChild( $js ); } private function add_head_element( &$el ) { return $this->dom->getElementsByTagName( 'head' ) ->item( 0 ) ->appendChild( $el ); } private function add_preload_link( $link, $as ) { $link = self::encode_ampersand( $link ); $nel = $this->dom->createElement( 'link' ); $relAttr = $this->dom->createAttribute( 'rel' ); $relAttr->nodeValue = 'preload'; $nel->appendChild( $relAttr ); $hrefAttr = $this->dom->createAttribute( 'href' ); $hrefAttr->nodeValue = $link; $nel->appendChild( $hrefAttr ); $asAttr = $this->dom->createAttribute( 'as' ); $asAttr->nodeValue = $as; $nel->appendChild( $asAttr ); if ( ! self::is_local_link( $link ) ) { $xoAttr = $this->dom->createAttribute( 'crossorigin' ); $xoAttr->nodeValue = ''; $nel->appendChild( $xoAttr ); } return $this->add_head_element( $nel ); } private function add_js_module_preload_link( $link ) { $link = self::encode_ampersand( $link ); $nel = $this->dom->createElement( 'link' ); $relAttr = $this->dom->createAttribute( 'rel' ); $relAttr->nodeValue = 'modulepreload'; $nel->appendChild( $relAttr ); $hrefAttr = $this->dom->createAttribute( 'href' ); $hrefAttr->nodeValue = $link; $nel->appendChild( $hrefAttr ); if ( ! self::is_local_link( $link ) ) { $xoAttr = $this->dom->createAttribute( 'crossorigin' ); $xoAttr->nodeValue = ''; $nel->appendChild( $xoAttr ); } return $this->add_head_element( $nel ); } private function is_excluded_page( $url = null ) { $pep_res = self::extract_patterns( $this->params->get( 'page_exclude_patterns', '' ) ); if ( empty( $pep_res ) ) { return false; } if ( is_null( $url ) ) { $url = strVal( Uri::getInstance() ); } $juri = new Uri( $url ); $cur = $juri->getPath(); if ( ( $q = $juri->getQuery() ) != '' ) { $cur .= '?' . $q; } foreach ( $pep_res as $pep_re ) { if ( preg_match( '/' . $pep_re . '/', $cur ) ) { return true; } } return false; } private function in_frontend() { if ( self::is_joomla3() ) { return $this->app->isSite(); } return $this->app->isClient( 'site' ); } private function is_unsupported_ctx() { return ( $this->ctx == 'com_sppagebuilder.dashboard' or $this->ctx == 'com_sppagebuilder.form' ); } private function is_frontend_editing() { if ( $this->ctx == 'unknown.unknown' ) { return ( Factory::getUser()->id > 0 or $this->app->input->get( 'jooa11y', 0, 'int' ) > 0 ); } return false; } static private function DomNodeList_to_array( $dnl ) { $a = array(); foreach ( $dnl as $n ) { $a[] = $n; } return $a; } static private function is_wrapped_in_link( &$e ) { if ( ! $e || ! $e->nodeName ) { return false; } if ( $e->nodeName == 'a' ) { return true; } if ( $e->parentNode ) { return self::is_wrapped_in_link( $e->parentNode ); } return false; } static private function is_descendant_of_class( &$el, $cls ) { if ( ! $el || ! $el->nodeName || $el->nodeName == '#document' ) { return false; } if ( $el->hasAttribute( 'class' ) ) { $class = $el->getAttribute( 'class' ); if ( preg_match( '#\b\Q' . $cls . '\E\b#', $class ) ) { return true; } } return self::is_descendant_of_class( $el->parentNode, $cls ); } static private function get_link_path( $l ) { $u = new Uri( $l ); return $u->getPath(); } static private function is_local_link( $l ) { if ( stripos( $l, 'http://' ) === 0 || stripos( $l, 'https://' ) === 0 ) { $site = new Uri( Uri::root() ); $link = new Uri( $l ); return ( $site->getHost() == $link->getHost() && strpos( $link->getPath(), $site->getPath() ) === 0 ); } if ( strpos( '/', $l ) !== 0 ) { return true; } $root = Uri::root( true ); $root = empty( $root ) ? '/' : $root; return ( strpos( $l, '//' ) !== 0 && strpos( $l, $root ) === 0 ); } static private function is_unsupported_js( $l ) { return ( strpos( $l, 'require.js' ) !== false || strpos( $l, 'mod_horizontal_scrolling_slideshow.js' ) !== false ); } static private function is_unsupported_js_code( &$c ) { if ( strpos( $c, 'function utmx_section()' ) === 0 || strpos( $c, 'utmx(' ) === 0 ) { return true; } if ( strpos( $c, '/gtm.js?id=' ) !== false && strpos( $c, 'gtm.start' ) !== false ) { return true; } if ( strpos( $c, '/fbevents.js' ) !== false || strpos( $c, 'fbq(' ) === 0 ) { return true; } if ( strpos( $c, 'window.ezb=window.eb=' ) === 0 ) { return true; } if ( strpos( $c, 'var IHRSS_WIDTH' ) !== false ) { return true; } if ( strpos( $c, '/reDimCookieHint/' ) !== false ) { return true; } if ( strpos( $c, 'var gdprConfigurationOptions' ) === 0 ) { return true; } return false; } private function page_should_not_be_unblocked() { $scripts = self::DomNodeList_to_array( $this->dom->getElementsByTagName( 'script' ) ); if ( empty( $scripts ) ) { return false; } foreach ( $scripts as $script ) { if ( ! self::is_js_script_element( $script ) || ! $script->hasAttribute( 'src' ) ) { continue; } $src = $script->getAttribute( 'src' ); if ( strpos( $src, 'cdn.consentmanager.net/' ) !== false ) { return true; } if ( strpos( $src, 'jooa11y.' ) !== false ) { return true; } } return false; } static private function requires_http_access( $l ) { return preg_match( '#\.php\d*$#i', preg_replace( '#[\?\#].*$#', '', $l ) ); } static private function get_inline_image() { return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAA' . 'fFcSJAAAABmJLR0QAmQAAAAAlVf4eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA' . 'B3RJTUUH3wwJBwcE2ifiigAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR' . '0lNUFeBDhcAAAANSURBVAjXY2BgYGAAAAAFAAFe8yo6AAAAAElFTkSuQmCC'; } static private function extract_patterns( $str ) { $str = trim( $str ); if ( empty( $str ) ) { return array(); } $r = array(); $in = explode( "\r\n", $str ); foreach ( $in as $i => $s ) { $s = trim( $s ); if ( empty( $s ) ) { continue; } $r[] = '^' . str_replace( '*', '.*?', preg_replace( '#([./?\[\]{}()$\^])#', '\\\\${1}', $s ) ) . '$'; } return $r; } static private function get_first_srcset_path( $s ) { $ps = preg_split( '#,?\s+#', trim( $s ) ); if ( is_array( $ps ) && count( $ps ) ) { return $ps[ 0 ]; } return false; } static private function is_js_script_element( & $el ) { if ( $el->hasAttribute( 'type' ) && ( $el->getAttribute( 'type' ) != 'text/javascript' && $el->getAttribute( 'type' ) != 'module' ) ) { return false; } return true; } static private function get_image_data_uri( $path, $ext = false ) { $data = @file_get_contents( $path ); if ( $data === false ) { return false; } if ( ! $ext ) { $ext = pathinfo( $path, PATHINFO_EXTENSION ); } if ( ! $ext ) { return false; } $mime = false; switch ( strtolower( $ext ) ) { case 'jpg': case 'jpeg': $mime = 'image/jpeg'; break; case 'png': $mime = 'image/png'; break; case 'gif': $mime = 'image/gif'; break; case 'tif': case 'tiff': $mime = 'image/tiff'; break; case 'webp': $mime = 'image/webp'; break; } if ( ! $mime ) { return false; } return sprintf( 'data:%s;base64,%s', $mime, base64_encode( $data ) ); } static private function is_joomla3() { return ( strpos( strVal( JVERSION ), '3.' ) === 0 ); } static private function encode_ampersand( $s ) { return preg_replace( '#&(?!amp;)#', '&amp;', $s ); } public function onExtensionAfterSave( $context, $table, $isNew, $data = array() ) { if ( strpos( JVERSION, '3.' ) !== 0 ) { return; } if ( $context != 'com_plugins.plugin' or ! is_object( $table ) or ! property_exists( $table, 'element' ) or $table->element != 'aimyspeedoptimization' ) { return; } if ( ! is_array( $data ) or empty( $data ) or ! isset( $data[ 'params' ] ) or ! is_array( $data[ 'params' ] ) or ! isset( $data[ 'params' ][ 'dl_key' ] ) ) { return; } $key = trim( $data[ 'params' ][ 'dl_key' ] ); require_once( __DIR__ . '/helpers/DownloadKeyHelper.php' ); try { AimySpeedOptimization\Helpers\DownloadKeyHelper::passKeyToUpdatesites( $key ); } catch ( Exception $e ) { Factory::getApplication( 'Failed to pass download key to update sites: ' . $e->getMessage(), 'error' ); } } public function onInstallerBeforePackageDownload( &$url, &$headers ) { if ( ! strpos( $url, 'aimy-extensions.com' ) or ! strpos( $url, '/plg_AimySpeedOptimization/' ) ) { return; } require_once( __DIR__ . '/helpers/DownloadKeyHelper.php' ); $rv = AimySpeedOptimization\Helpers\DownloadKeyHelper::validateKey(); if ( is_object( $rv ) && ! $rv->valid && isset( $rv->msg ) ) { Factory::getApplication()->enqueueMessage( '<b>Aimy Speed Optimization: ' . $rv->msg . '</b>', 'error' ); } } } 
