921 lines
38 KiB
PHP
921 lines
38 KiB
PHP
|
<?php
|
||
|
namespace WpAssetCleanUp\OptimiseAssets;
|
||
|
|
||
|
use WpAssetCleanUp\Main;
|
||
|
use WpAssetCleanUp\Menu;
|
||
|
use WpAssetCleanUp\FileSystem;
|
||
|
use WpAssetCleanUp\Misc;
|
||
|
use WpAssetCleanUp\ObjectCache;
|
||
|
|
||
|
/**
|
||
|
* Class CombineJs
|
||
|
* @package WpAssetCleanUp\OptimiseAssets
|
||
|
*/
|
||
|
class CombineJs
|
||
|
{
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
public static $jsonStorageFile = 'js-combined{maybe-extra-info}.json';
|
||
|
|
||
|
/**
|
||
|
* @param $htmlSource
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public static function doCombine($htmlSource)
|
||
|
{
|
||
|
if ( ! Misc::isDOMDocumentOn() ) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
if ( ! self::proceedWithJsCombine() ) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
global $wp_scripts;
|
||
|
$wpacuRegisteredScripts = $wp_scripts->registered;
|
||
|
|
||
|
$combineLevel = 2;
|
||
|
|
||
|
$isDeferAppliedOnBodyCombineGroupNo = false;
|
||
|
|
||
|
// $uriToFinalJsFile will always be relative ONLY within WP_CONTENT_DIR . self::getRelPathJsCacheDir()
|
||
|
// which is usually "wp-content/cache/asset-cleanup/js/"
|
||
|
|
||
|
// "true" would make it avoid checking the cache and always use the DOM Parser / RegExp
|
||
|
// for DEV purposes ONLY as it uses more resources
|
||
|
$finalCacheList = array();
|
||
|
$skipCache = false;
|
||
|
|
||
|
if (isset($_GET['wpacu_no_cache']) || (defined('WPACU_NO_CACHE') && WPACU_NO_CACHE === true)) {
|
||
|
$skipCache = true;
|
||
|
}
|
||
|
|
||
|
if (! $skipCache) {
|
||
|
// Speed up processing by getting the already existing final CSS file URI
|
||
|
// This will avoid parsing the HTML DOM and determine the combined URI paths for all the CSS files
|
||
|
$finalCacheList = OptimizeCommon::getAssetCachedData( self::$jsonStorageFile, OptimizeJs::getRelPathJsCacheDir(), 'js' );
|
||
|
}
|
||
|
|
||
|
if ( $skipCache || empty($finalCacheList) ) {
|
||
|
/*
|
||
|
* NO CACHING TRANSIENT; Parse the DOM
|
||
|
*/
|
||
|
// Nothing in the database records or the retrieved cached file does not exist?
|
||
|
OptimizeCommon::clearAssetCachedData(self::$jsonStorageFile);
|
||
|
|
||
|
$combinableList = array();
|
||
|
|
||
|
$jQueryMigrateInBody = false;
|
||
|
$jQueryLibInBodyCount = 0;
|
||
|
|
||
|
$minifyJsInlineTagsIsNotEnabled = ! (MinifyJs::isMinifyJsEnabled() && in_array(Main::instance()->settings['minify_loaded_js_for'], array('inline', 'all')));
|
||
|
|
||
|
if ($minifyJsInlineTagsIsNotEnabled) {
|
||
|
$domTag = Misc::initDOMDocument();
|
||
|
|
||
|
// Strip irrelevant tags to boost the speed of the parser (e.g. NOSCRIPT / SCRIPT(inline) / STYLE)
|
||
|
// Sometimes, inline CODE can be too large, and it takes extra time for loadHTML() to parse
|
||
|
$htmlSourceAlt = preg_replace( '@<script(| (type=(\'|"|)text/(javascript|template|html)(\'|"|)))>.*?</script>@si', '', $htmlSource );
|
||
|
$htmlSourceAlt = preg_replace( '@<(style|noscript)[^>]*?>.*?</\\1>@si', '', $htmlSourceAlt );
|
||
|
$htmlSourceAlt = preg_replace( '#<link([^<>]+)/?>#iU', '', $htmlSourceAlt );
|
||
|
|
||
|
if (Main::instance()->isFrontendEditView) {
|
||
|
$htmlSourceAlt = preg_replace( '@<form action="#wpacu_wrap_assets" method="post">.*?</form>@si', '', $htmlSourceAlt );
|
||
|
}
|
||
|
|
||
|
if ($htmlSourceAlt === '') {
|
||
|
$htmlSourceAlt = $htmlSource;
|
||
|
}
|
||
|
|
||
|
$domTag->loadHTML( $htmlSourceAlt );
|
||
|
} else {
|
||
|
$domTag = OptimizeCommon::getDomLoadedTag($htmlSource, 'combineJs');
|
||
|
}
|
||
|
|
||
|
// Only keep combinable JS files
|
||
|
foreach ( array( 'head', 'body' ) as $docLocationScript ) {
|
||
|
$groupIndex = 1;
|
||
|
|
||
|
$docLocationElements = $domTag->getElementsByTagName($docLocationScript)->item(0);
|
||
|
if ($docLocationElements === null) { continue; }
|
||
|
|
||
|
// High accuracy (e.g. it ignores tags inside HTML comments, conditional or not)
|
||
|
$scriptTags = $docLocationElements->getElementsByTagName('script');
|
||
|
if ($scriptTags === null) { continue; }
|
||
|
|
||
|
if ($docLocationScript && Main::instance()->settings['combine_loaded_js_defer_body']) {
|
||
|
ObjectCache::wpacu_cache_set('wpacu_html_dom_body_tag_for_js', $docLocationElements);
|
||
|
}
|
||
|
|
||
|
foreach ($scriptTags as $tagObject) {
|
||
|
$scriptAttributes = array();
|
||
|
|
||
|
if ( isset($tagObject->attributes) && ! empty($tagObject->attributes) ) {
|
||
|
foreach ( $tagObject->attributes as $attrObj ) {
|
||
|
$scriptAttributes[ $attrObj->nodeName ] = trim( $attrObj->nodeValue );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$scriptNotCombinable = false; // default (usually, most of the SCRIPT tags can be optimized)
|
||
|
|
||
|
// Check if the CSS file has any 'data-wpacu-skip' attribute; if it does, do not alter it
|
||
|
if (isset($scriptAttributes['data-wpacu-skip'])) {
|
||
|
$scriptNotCombinable = true;
|
||
|
}
|
||
|
|
||
|
$handleToCheck = isset($scriptAttributes['data-wpacu-script-handle']) ? $scriptAttributes['data-wpacu-script-handle'] : ''; // Maybe: JS Inline (Before, After)
|
||
|
$hasSrc = isset($scriptAttributes['src']) && trim($scriptAttributes['src']); // No valid SRC attribute? It's not combinable (e.g. an inline tag)
|
||
|
$isPluginScript = isset($scriptAttributes['data-wpacu-plugin-script']); // Only of the user is logged-in (skip it as it belongs to the Asset CleanUp (Pro) plugin)
|
||
|
|
||
|
if (! $scriptNotCombinable && (! $hasSrc || $isPluginScript)) {
|
||
|
// Inline tag? Skip it in the BODY
|
||
|
if ($docLocationScript === 'body') {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Because of jQuery, we will not have the list of all inline scripts and then the combined files as it is in BODY
|
||
|
if ($docLocationScript === 'head') {
|
||
|
if ($handleToCheck === '' && isset($scriptAttributes['id'])) {
|
||
|
$replaceToGetHandle = '';
|
||
|
if (strpos($scriptAttributes['id'], '-js-extra') !== false) { $replaceToGetHandle = '-js-extra'; }
|
||
|
if (strpos($scriptAttributes['id'], '-js-before') !== false) { $replaceToGetHandle = '-js-before'; }
|
||
|
if (strpos($scriptAttributes['id'], '-js-after') !== false) { $replaceToGetHandle = '-js-after'; }
|
||
|
if (strpos($scriptAttributes['id'], '-js-translations') !== false) { $replaceToGetHandle = '-js-translations'; }
|
||
|
|
||
|
if ($replaceToGetHandle) {
|
||
|
$handleToCheck = str_replace( $replaceToGetHandle, '', $scriptAttributes['id'] ); // Maybe: JS Inline (Data)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Once an inline SCRIPT (with few exceptions below), except the ones associated with an enqueued script tag (with "src") is stumbled upon, a new combined group in the HEAD tag will be formed
|
||
|
if ($handleToCheck && OptimizeCommon::appendInlineCodeToCombineAssetType('js')) {
|
||
|
$getInlineAssociatedWithHandle = OptimizeJs::getInlineAssociatedWithScriptHandle($handleToCheck, $wpacuRegisteredScripts, 'handle');
|
||
|
|
||
|
if ( ($getInlineAssociatedWithHandle['data'] || $getInlineAssociatedWithHandle['before'] || $getInlineAssociatedWithHandle['after'])
|
||
|
|| in_array(trim($tagObject->nodeValue), array($getInlineAssociatedWithHandle['data'], $getInlineAssociatedWithHandle['before'], $getInlineAssociatedWithHandle['after']))
|
||
|
|| (strpos(trim($tagObject->nodeValue), '/* <![CDATA[ */') === 0 && Misc::endsWith(trim($tagObject->nodeValue), '/* ]]> */')) ) {
|
||
|
|
||
|
// It's associated with the enqueued scripts, or it's a (standalone) CDATA inline tag added via wp_localize_script()
|
||
|
// Skip it instead and if the CDATA is not standalone (e.g. not associated with any script tag), the loop will "stay" in the same combined group
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$scriptNotCombinable = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$isInGroupType = 'standard';
|
||
|
$isJQueryLib = $isJQueryMigrate = false;
|
||
|
|
||
|
// Has SRC and $isPluginScript is set to false OR it does not have "data-wpacu-skip" attribute
|
||
|
if (! $scriptNotCombinable) {
|
||
|
$src = (string)$scriptAttributes['src'];
|
||
|
|
||
|
if (self::skipCombine($src, $handleToCheck)) {
|
||
|
$scriptNotCombinable = true;
|
||
|
}
|
||
|
|
||
|
// Avoid any errors when code like the following one is used:
|
||
|
// wp.i18n.setLocaleData( localeData, domain );
|
||
|
// Because the inline JS is not appended to the combined JS, /wp-includes/js/dist/i18n.(min).js has to be called earlier (outside the combined JS file)
|
||
|
if ( ! OptimizeCommon::appendInlineCodeToCombineAssetType('js') && (strpos($src, '/wp-includes/js/dist/i18n.') !== false) ) {
|
||
|
$scriptNotCombinable = true;
|
||
|
}
|
||
|
|
||
|
if (isset($scriptAttributes['data-wpacu-to-be-preloaded-basic']) && $scriptAttributes['data-wpacu-to-be-preloaded-basic']) {
|
||
|
$scriptNotCombinable = true;
|
||
|
}
|
||
|
|
||
|
// Was it optimized and has the URL updated? Check the Source URL
|
||
|
if (! $scriptNotCombinable && isset($scriptAttributes['data-wpacu-script-rel-src-before']) && $scriptAttributes['data-wpacu-script-rel-src-before'] && self::skipCombine($scriptAttributes['data-wpacu-script-rel-src-before'], $handleToCheck)) {
|
||
|
$scriptNotCombinable = true;
|
||
|
}
|
||
|
|
||
|
$isJQueryLib = isset($scriptAttributes['data-wpacu-jquery-core-handle']);
|
||
|
$isJQueryMigrate = isset($scriptAttributes['data-wpacu-jquery-migrate-handle']);
|
||
|
|
||
|
if (isset($scriptAttributes['async'], $scriptAttributes['defer'])) { // Has both "async" and "defer"
|
||
|
$isInGroupType = 'async_defer';
|
||
|
} elseif (isset($scriptAttributes['async'])) { // Has only "async"
|
||
|
$isInGroupType = 'async';
|
||
|
} elseif (isset($scriptAttributes['defer'])) { // Has only "defer"
|
||
|
// Does it have "defer" attribute, it's combinable (all checks were already done), loads in the BODY tag and "combine_loaded_js_defer_body" is ON? Keep it to the combination list
|
||
|
$isCombinableWithBodyDefer = (! $scriptNotCombinable && $docLocationScript === 'body' && Main::instance()->settings['combine_loaded_js_defer_body']);
|
||
|
|
||
|
if (! $isCombinableWithBodyDefer) {
|
||
|
$isInGroupType = 'defer'; // Otherwise, add it to the "defer" group type
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ! $scriptNotCombinable ) {
|
||
|
// It also checks the domain name to make sure no external scripts would be added to the list
|
||
|
if ( $localAssetPath = OptimizeCommon::getLocalAssetPath( $src, 'js' ) ) {
|
||
|
$scriptExtra = array();
|
||
|
|
||
|
if ( isset( $scriptAttributes['data-wpacu-script-handle'], $wpacuRegisteredScripts[ $scriptAttributes['data-wpacu-script-handle'] ]->extra ) && OptimizeCommon::appendInlineCodeToCombineAssetType('js') ) {
|
||
|
$scriptExtra = $wpacuRegisteredScripts[ $scriptAttributes['data-wpacu-script-handle'] ]->extra;
|
||
|
|
||
|
$anyScriptTranslations = method_exists('wp_scripts', 'print_translations')
|
||
|
? wp_scripts()->print_translations( $scriptAttributes['data-wpacu-script-handle'], false )
|
||
|
: false;
|
||
|
|
||
|
if ( $anyScriptTranslations ) {
|
||
|
$scriptExtra['translations'] = $anyScriptTranslations;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Standard (could be multiple groups per $docLocationScript), Async & Defer, Async, Defer
|
||
|
$groupByType = ($isInGroupType === 'standard') ? $groupIndex : $isInGroupType;
|
||
|
|
||
|
if ($docLocationScript === 'body') {
|
||
|
if ($isJQueryLib || strpos($localAssetPath, '/wp-includes/js/jquery/jquery.js') !== false) {
|
||
|
$jQueryLibInBodyCount++;
|
||
|
}
|
||
|
|
||
|
if ($isJQueryMigrate || strpos($localAssetPath, '/wp-includes/js/jquery/jquery-migrate') !== false) {
|
||
|
$jQueryLibInBodyCount++;
|
||
|
$jQueryMigrateInBody = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$combinableList[$docLocationScript][$groupByType][] = array(
|
||
|
'src' => $src,
|
||
|
'local' => $localAssetPath,
|
||
|
'info' => array(
|
||
|
'is_jquery' => $isJQueryLib,
|
||
|
'is_jquery_migrate' => $isJQueryMigrate
|
||
|
),
|
||
|
'extra' => $scriptExtra
|
||
|
);
|
||
|
|
||
|
if ($docLocationScript === 'body' && $jQueryLibInBodyCount === 2) {
|
||
|
$jQueryLibInBodyCount = 0; // reset it
|
||
|
$groupIndex ++; // a new JS group will be created if jQuery & jQuery Migrate are combined in the BODY
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
$groupIndex ++; // a new JS group will be created (applies to "standard" ones only)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Could be pages such as maintenance mode with no external JavaScript files
|
||
|
if (empty($combinableList)) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
$finalCacheList = array();
|
||
|
|
||
|
foreach ($combinableList as $docLocationScript => $combinableListGroups) {
|
||
|
$groupNo = 1;
|
||
|
|
||
|
foreach ($combinableListGroups as $groupType => $groupFiles) {
|
||
|
// Any groups having one file? Then it's not really a group and the file should load on its own
|
||
|
// Could be one extra file besides the jQuery & jQuery Migrate group or the only JS file called within the HEAD
|
||
|
if (count($groupFiles) < 2) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$localAssetsPaths = $groupScriptSrcs = array();
|
||
|
$localAssetsExtra = array();
|
||
|
$jQueryIsIncludedInGroup = false;
|
||
|
|
||
|
foreach ($groupFiles as $groupFileData) {
|
||
|
if ($groupFileData['info']['is_jquery'] || strpos($groupFileData['local'], '/wp-includes/js/jquery/jquery.js') !== false) {
|
||
|
$jQueryIsIncludedInGroup = true;
|
||
|
|
||
|
// Is jQuery in the BODY without jQuery Migrate loaded?
|
||
|
// Isolate it as it needs to be the first to load in case there are inline scripts calling it before the combined group(s)
|
||
|
if ($docLocationScript === 'body' && ! $jQueryMigrateInBody) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$src = $groupFileData['src'];
|
||
|
$groupScriptSrcs[] = $src;
|
||
|
$localAssetsPaths[$src] = $groupFileData['local'];
|
||
|
$localAssetsExtra[$src] = $groupFileData['extra'];
|
||
|
}
|
||
|
|
||
|
$maybeDoJsCombine = self::maybeDoJsCombine(
|
||
|
$localAssetsPaths,
|
||
|
$localAssetsExtra,
|
||
|
$docLocationScript
|
||
|
);
|
||
|
|
||
|
// Local path to combined CSS file
|
||
|
$localFinalJsFile = $maybeDoJsCombine['local_final_js_file'];
|
||
|
|
||
|
// URI (e.g. /wp-content/cache/asset-cleanup/[file-name-here.js]) to the combined JS file
|
||
|
$uriToFinalJsFile = $maybeDoJsCombine['uri_final_js_file'];
|
||
|
|
||
|
if (! is_file($localFinalJsFile)) {
|
||
|
return $htmlSource; // something is not right as the file wasn't created, we will return the original HTML source
|
||
|
}
|
||
|
|
||
|
$groupScriptSrcsFilter = array_map(static function($src) {
|
||
|
$src = str_replace(site_url(), '', $src);
|
||
|
// Starts with // (protocol is missing) - the replacement above wasn't made
|
||
|
if (strpos($src, '//') === 0) {
|
||
|
$siteUrlNoProtocol = str_replace(array('http:', 'https:'), '', site_url());
|
||
|
return str_replace($siteUrlNoProtocol, '', $src);
|
||
|
}
|
||
|
return $src;
|
||
|
}, $groupScriptSrcs);
|
||
|
|
||
|
$finalCacheList[$docLocationScript][$groupNo] = array(
|
||
|
'uri_to_final_js_file' => $uriToFinalJsFile,
|
||
|
'script_srcs' => $groupScriptSrcsFilter
|
||
|
);
|
||
|
|
||
|
if (in_array($groupType, array('async_defer', 'async', 'defer'))) {
|
||
|
if ($groupType === 'async_defer') {
|
||
|
$finalCacheList[$docLocationScript][$groupNo]['extra_attributes'][] = 'async';
|
||
|
$finalCacheList[$docLocationScript][$groupNo]['extra_attributes'][] = 'defer';
|
||
|
} else {
|
||
|
$finalCacheList[$docLocationScript][$groupNo]['extra_attributes'][] = $groupType;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Apply 'defer="defer"' to combined JS files from the BODY tag (if enabled), except the combined jQuery & jQuery Migrate Group
|
||
|
if ($docLocationScript === 'body' && ! $jQueryIsIncludedInGroup && Main::instance()->settings['combine_loaded_js_defer_body']) {
|
||
|
if ($isDeferAppliedOnBodyCombineGroupNo === false) {
|
||
|
// Only record the first one
|
||
|
$isDeferAppliedOnBodyCombineGroupNo = $groupNo;
|
||
|
}
|
||
|
|
||
|
$finalCacheList[$docLocationScript][$groupNo]['extra_attributes'][] = 'defer';
|
||
|
}
|
||
|
|
||
|
$groupNo ++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
OptimizeCommon::setAssetCachedData(self::$jsonStorageFile, OptimizeJs::getRelPathJsCacheDir(), wp_json_encode($finalCacheList));
|
||
|
}
|
||
|
|
||
|
if (! empty($finalCacheList)) {
|
||
|
$cdnUrls = OptimizeCommon::getAnyCdnUrls();
|
||
|
$cdnUrlForJs = isset($cdnUrls['js']) ? $cdnUrls['js'] : false;
|
||
|
|
||
|
foreach ( $finalCacheList as $docLocationScript => $cachedGroupsList ) {
|
||
|
foreach ($cachedGroupsList as $groupNo => $cachedValues) {
|
||
|
$htmlSourceBeforeGroupReplacement = $htmlSource;
|
||
|
|
||
|
$uriToFinalJsFile = $cachedValues['uri_to_final_js_file'];
|
||
|
$filesSources = $cachedValues['script_srcs'];
|
||
|
|
||
|
// Basic Combining (1) -> replace "first" tag with the final combination tag (there would be most likely multiple groups)
|
||
|
// Enhanced Combining (2) -> replace "last" tag with the final combination tag (most likely one group)
|
||
|
$indexReplacement = ($combineLevel === 2) ? (count($filesSources) - 1) : 0;
|
||
|
|
||
|
$finalTagUrl = OptimizeCommon::filterWpContentUrl($cdnUrlForJs) . OptimizeJs::getRelPathJsCacheDir() . $uriToFinalJsFile;
|
||
|
|
||
|
$finalJsTagAttrsOutput = '';
|
||
|
$extraAttrs = array();
|
||
|
|
||
|
if (isset($cachedValues['extra_attributes']) && ! empty($cachedValues['extra_attributes'])) {
|
||
|
$extraAttrs = $cachedValues['extra_attributes'];
|
||
|
foreach ($extraAttrs as $finalJsTagAttr) {
|
||
|
$finalJsTagAttrsOutput .= ' '.$finalJsTagAttr.'=\''.$finalJsTagAttr.'\' ';
|
||
|
}
|
||
|
$finalJsTagAttrsOutput = trim($finalJsTagAttrsOutput);
|
||
|
}
|
||
|
|
||
|
// No async or defer? Add the preloading for the combined JS from the BODY
|
||
|
if ( ! $finalJsTagAttrsOutput && $docLocationScript === 'body' ) {
|
||
|
$finalJsTagAttrsOutput = ' data-wpacu-to-be-preloaded-basic=\'1\' ';
|
||
|
if ( ! defined('WPACU_REAPPLY_PRELOADING_FOR_COMBINED_JS') ) { define('WPACU_REAPPLY_PRELOADING_FOR_COMBINED_JS', true); }
|
||
|
}
|
||
|
|
||
|
// e.g. For developers that might want to add custom attributes such as data-cfasync="false"
|
||
|
$finalJsTag = apply_filters(
|
||
|
'wpacu_combined_js_tag',
|
||
|
'<script '.$finalJsTagAttrsOutput.' '.Misc::getScriptTypeAttribute().' id=\'wpacu-combined-js-'.$docLocationScript.'-group-'.$groupNo.'\' src=\''.$finalTagUrl.'\'></script>',
|
||
|
array(
|
||
|
'attrs' => $extraAttrs,
|
||
|
'doc_location' => $docLocationScript,
|
||
|
'group_no' => $groupNo,
|
||
|
'src' => $finalTagUrl
|
||
|
)
|
||
|
);
|
||
|
|
||
|
// Reference: https://stackoverflow.com/questions/2368539/php-replacing-multiple-spaces-with-a-single-space
|
||
|
$finalJsTag = preg_replace('!\s+!', ' ', $finalJsTag);
|
||
|
|
||
|
$scriptTagsStrippedNo = 0;
|
||
|
|
||
|
$scriptTags = OptimizeJs::getScriptTagsFromSrcs($filesSources, $htmlSource);
|
||
|
|
||
|
foreach ($scriptTags as $groupScriptTagIndex => $scriptTag) {
|
||
|
$replaceWith = ($groupScriptTagIndex === $indexReplacement) ? $finalJsTag : '';
|
||
|
$htmlSourceBeforeTagReplacement = $htmlSource;
|
||
|
|
||
|
// 1) Strip any inline code associated with the tag
|
||
|
// 2) Finally, strip the actual tag
|
||
|
$htmlSource = self::stripTagAndAnyInlineAssocCode( $scriptTag, $wpacuRegisteredScripts, $replaceWith, $htmlSource );
|
||
|
|
||
|
if ($htmlSource !== $htmlSourceBeforeTagReplacement) {
|
||
|
$scriptTagsStrippedNo ++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// At least two tags have to be stripped from the group to consider doing the group replacement
|
||
|
// If the tags weren't replaced it's likely there were changes to their structure after they were cached for the group merging
|
||
|
if (count($filesSources) !== $scriptTagsStrippedNo) {
|
||
|
$htmlSource = $htmlSourceBeforeGroupReplacement;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Only relevant if "Defer loading JavaScript combined files from <body>" in "Settings" - "Combine CSS & JS Files" - "Combine loaded JS (JavaScript) into fewer files"
|
||
|
// and there is at least one combined deferred tag
|
||
|
|
||
|
if (isset($finalCacheList['body']) && (! empty($finalCacheList['body'])) && Main::instance()->settings['combine_loaded_js_defer_body']) {
|
||
|
// CACHE RE-BUILT
|
||
|
if ($isDeferAppliedOnBodyCombineGroupNo > 0 && $domTag = ObjectCache::wpacu_cache_get('wpacu_html_dom_body_tag_for_js')) {
|
||
|
$strPart = "id='wpacu-combined-js-body-group-".$isDeferAppliedOnBodyCombineGroupNo."' ";
|
||
|
|
||
|
if (strpos($htmlSource, $strPart) === false) {
|
||
|
return $htmlSource; // something is funny, do not continue
|
||
|
}
|
||
|
|
||
|
list(,$htmlAfterFirstCombinedDeferScript) = explode($strPart, $htmlSource);
|
||
|
$htmlAfterFirstCombinedDeferScriptMaybeChanged = $htmlAfterFirstCombinedDeferScript;
|
||
|
$scriptTags = $domTag->getElementsByTagName('script');
|
||
|
} else {
|
||
|
// FROM THE CACHE
|
||
|
foreach ($finalCacheList['body'] as $bodyCombineGroupNo => $values) {
|
||
|
if (isset($values['extra_attributes']) && in_array('defer', $values['extra_attributes'])) {
|
||
|
$isDeferAppliedOnBodyCombineGroupNo = $bodyCombineGroupNo;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (! $isDeferAppliedOnBodyCombineGroupNo) {
|
||
|
// Not applicable to any combined group
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
$strPart = 'id=\'wpacu-combined-js-body-group-'.$isDeferAppliedOnBodyCombineGroupNo.'\'';
|
||
|
|
||
|
$htmlAfterFirstCombinedDeferScriptMaybeChanged = false;
|
||
|
|
||
|
if (strpos($htmlSource, $strPart) !== false) {
|
||
|
list( , $htmlAfterFirstCombinedDeferScript ) = explode( $strPart, $htmlSource );
|
||
|
$htmlAfterFirstCombinedDeferScriptMaybeChanged = $htmlAfterFirstCombinedDeferScript;
|
||
|
}
|
||
|
|
||
|
// It means to combine took place for any reason (e.g. only one JS file loaded in the HEAD and one in the BODY)
|
||
|
if (! isset($htmlAfterFirstCombinedDeferScript)) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
$domTag = Misc::initDOMDocument();
|
||
|
|
||
|
// Strip irrelevant tags to boost the speed of the parser (e.g. NOSCRIPT / SCRIPT(inline) / STYLE)
|
||
|
// Sometimes, inline CODE can be too large, and it takes extra time for loadHTML() to parse
|
||
|
$htmlSourceAlt = preg_replace( '@<script(| type=\'text/javascript\'| type="text/javascript")>.*?</script>@si', '', $htmlAfterFirstCombinedDeferScript );
|
||
|
$htmlSourceAlt = preg_replace( '@<(style|noscript)[^>]*?>.*?</\\1>@si', '', $htmlSourceAlt );
|
||
|
$htmlSourceAlt = preg_replace( '#<link([^<>]+)/?>#iU', '', $htmlSourceAlt );
|
||
|
|
||
|
if (Main::instance()->isFrontendEditView) {
|
||
|
$htmlSourceAlt = preg_replace( '@<form action="#wpacu_wrap_assets" method="post">.*?</form>@si', '', $htmlSourceAlt );
|
||
|
}
|
||
|
|
||
|
// No other SCRIPT left, stop here in this case
|
||
|
if (strpos($htmlSourceAlt, '<script') === false) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
$domTag->loadHTML( $htmlSourceAlt );
|
||
|
$scriptTags = $domTag->getElementsByTagName('script');
|
||
|
}
|
||
|
|
||
|
if ( $scriptTags === null ) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
foreach ($scriptTags as $tagObject) {
|
||
|
if (empty($tagObject->attributes)) { continue; }
|
||
|
|
||
|
$scriptAttributes = array();
|
||
|
|
||
|
foreach ( $tagObject->attributes as $attrObj ) {
|
||
|
$scriptAttributes[ $attrObj->nodeName ] = trim( $attrObj->nodeValue );
|
||
|
}
|
||
|
|
||
|
// No "src" attribute? Skip it (most likely an inline script tag)
|
||
|
if (! (isset($scriptAttributes['src']) && $scriptAttributes['src'])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Skip it as "defer" is already set
|
||
|
if (isset($scriptAttributes['defer'])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Has "src" attribute and "defer" is not applied? Add it
|
||
|
if ($htmlAfterFirstCombinedDeferScriptMaybeChanged !== false) {
|
||
|
$htmlAfterFirstCombinedDeferScriptMaybeChanged = trim( preg_replace(
|
||
|
'#\ssrc(\s+|)=(\s+|)(|"|\'|\s+)(' . preg_quote( $scriptAttributes['src'], '/' ) . ')(\3)#si',
|
||
|
' src=\3\4\3 defer=\'defer\'',
|
||
|
$htmlAfterFirstCombinedDeferScriptMaybeChanged
|
||
|
) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($htmlAfterFirstCombinedDeferScriptMaybeChanged && $htmlAfterFirstCombinedDeferScriptMaybeChanged !== $htmlAfterFirstCombinedDeferScript) {
|
||
|
$htmlSource = str_replace($htmlAfterFirstCombinedDeferScript, $htmlAfterFirstCombinedDeferScriptMaybeChanged, $htmlSource);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
libxml_clear_errors();
|
||
|
|
||
|
// Finally, return the HTML source
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $src
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public static function skipCombine($src, $handle = '')
|
||
|
{
|
||
|
// In case the handle was appended
|
||
|
if ($handle !== '' && in_array($handle, Main::instance()->skipAssets['scripts'])) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
$regExps = array(
|
||
|
'#/wp-content/bs-booster-cache/#'
|
||
|
);
|
||
|
|
||
|
if (Main::instance()->settings['combine_loaded_js_exceptions'] !== '') {
|
||
|
$loadedJsExceptionsPatterns = trim(Main::instance()->settings['combine_loaded_js_exceptions']);
|
||
|
|
||
|
if (strpos($loadedJsExceptionsPatterns, "\n")) {
|
||
|
// Multiple values (one per line)
|
||
|
foreach (explode("\n", $loadedJsExceptionsPatterns) as $loadedJsExceptionsPattern) {
|
||
|
$regExps[] = '#'.trim($loadedJsExceptionsPattern).'#';
|
||
|
}
|
||
|
} else {
|
||
|
// Only one value?
|
||
|
$regExps[] = '#'.trim($loadedJsExceptionsPatterns).'#';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// No exceptions set? Do not skip combination
|
||
|
if (empty($regExps)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
foreach ($regExps as $regExp) {
|
||
|
$regExp = Misc::purifyRegexValue($regExp);
|
||
|
|
||
|
if ( @preg_match( $regExp, $src ) || ( strpos($src, $regExp) !== false ) ) {
|
||
|
// Skip combination
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $localAssetsPaths
|
||
|
* @param $localAssetsExtra
|
||
|
* @param $docLocationScript
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public static function maybeDoJsCombine($localAssetsPaths, $localAssetsExtra, $docLocationScript)
|
||
|
{
|
||
|
// Only combine if $shaOneCombinedUriPaths.js does not exist
|
||
|
// If "?ver" value changes on any of the assets or the asset list changes in any way
|
||
|
// then $shaOneCombinedUriPaths will change too and a new JS file will be generated and loaded
|
||
|
|
||
|
// Change $assetsContents as paths to fonts and images that are relative (e.g. ../, ../../) have to be updated
|
||
|
$uriToFinalJsFile = $localFinalJsFile = $finalJsContents = '';
|
||
|
|
||
|
foreach ($localAssetsPaths as $assetHref => $localAssetsPath) {
|
||
|
if ($jsContent = trim(FileSystem::fileGetContents($localAssetsPath))) {
|
||
|
// Does it have a source map? Strip it
|
||
|
if (strpos($jsContent, '//# sourceMappingURL=') !== false) {
|
||
|
$jsContent = OptimizeCommon::stripSourceMap($jsContent, 'js');
|
||
|
}
|
||
|
|
||
|
$pathToAssetDir = OptimizeCommon::getPathToAssetDir($assetHref);
|
||
|
|
||
|
$contentToAddToCombinedFile = '';
|
||
|
|
||
|
if (apply_filters('wpacu_print_info_comments_in_cached_assets', true)) {
|
||
|
$contentToAddToCombinedFile = '/*!' . str_replace( Misc::getWpRootDirPath(), '/', $localAssetsPath ) . "*/\n";
|
||
|
}
|
||
|
|
||
|
// This includes the extra from 'data' (CDATA added via wp_localize_script()) & 'before' as they are both printed BEFORE the SCRIPT tag
|
||
|
$contentToAddToCombinedFile .= self::maybeWrapBetweenTryCatch(self::appendToCombineJs('translations', $localAssetsExtra, $assetHref, $pathToAssetDir), $assetHref);
|
||
|
$contentToAddToCombinedFile .= self::maybeWrapBetweenTryCatch(self::appendToCombineJs('before', $localAssetsExtra, $assetHref, $pathToAssetDir), $assetHref);
|
||
|
$contentToAddToCombinedFile .= self::maybeWrapBetweenTryCatch(OptimizeJs::maybeDoJsFixes($jsContent, $pathToAssetDir . '/'), $assetHref) . "\n";
|
||
|
// This includes the inline 'after' the SCRIPT tag
|
||
|
$contentToAddToCombinedFile .= self::maybeWrapBetweenTryCatch(self::appendToCombineJs('after', $localAssetsExtra, $assetHref, $pathToAssetDir), $assetHref);
|
||
|
|
||
|
$finalJsContents .= $contentToAddToCombinedFile;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($finalJsContents !== '') {
|
||
|
$finalJsContents = trim($finalJsContents);
|
||
|
$shaOneForCombinedJs = sha1($finalJsContents);
|
||
|
|
||
|
$uriToFinalJsFile = $docLocationScript . '-' . $shaOneForCombinedJs . '.js';
|
||
|
$localFinalJsFile = WP_CONTENT_DIR . OptimizeJs::getRelPathJsCacheDir() . $uriToFinalJsFile;
|
||
|
|
||
|
if (! is_file($localFinalJsFile)) {
|
||
|
FileSystem::filePutContents( $localFinalJsFile, $finalJsContents );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return array(
|
||
|
'uri_final_js_file' => $uriToFinalJsFile,
|
||
|
'local_final_js_file' => $localFinalJsFile
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $addItLocation
|
||
|
* @param $localAssetsExtra
|
||
|
* @param $assetHref
|
||
|
* @param $pathToAssetDir
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function appendToCombineJs($addItLocation, $localAssetsExtra, $assetHref, $pathToAssetDir)
|
||
|
{
|
||
|
$extraContentToAppend = '';
|
||
|
$doJsMinifyInline = MinifyJs::isMinifyJsEnabled() && in_array(Main::instance()->settings['minify_loaded_js_for'], array('inline', 'all'));
|
||
|
|
||
|
if ($addItLocation === 'before') {
|
||
|
// [Before JS Content]
|
||
|
if (isset($localAssetsExtra[$assetHref]['data']) && ($dataValue = $localAssetsExtra[$assetHref]['data'])) {
|
||
|
$extraContentToAppend = '';
|
||
|
if (self::isInlineJsCombineable($dataValue) && trim($dataValue) !== '') {
|
||
|
$cData = $doJsMinifyInline ? MinifyJs::applyMinification( $dataValue ) : $dataValue;
|
||
|
$cData = OptimizeJs::maybeDoJsFixes( $cData, $pathToAssetDir . '/' );
|
||
|
$extraContentToAppend .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [inline: cdata] */' : '';
|
||
|
$extraContentToAppend .= $cData;
|
||
|
$extraContentToAppend .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [/inline: cdata] */' : '';
|
||
|
$extraContentToAppend .= "\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (isset($localAssetsExtra[$assetHref]['before']) && ! empty($localAssetsExtra[$assetHref]['before'])) {
|
||
|
$inlineBeforeJsData = '';
|
||
|
|
||
|
foreach ($localAssetsExtra[$assetHref]['before'] as $beforeData) {
|
||
|
if (! is_bool($beforeData) && self::isInlineJsCombineable($beforeData)) {
|
||
|
$inlineBeforeJsData .= $beforeData . "\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (trim($inlineBeforeJsData)) {
|
||
|
$inlineBeforeJsData = OptimizeJs::maybeAlterContentForInlineScriptTag( $inlineBeforeJsData, $doJsMinifyInline );
|
||
|
$inlineBeforeJsData = OptimizeJs::maybeDoJsFixes( $inlineBeforeJsData, $pathToAssetDir . '/' );
|
||
|
$extraContentToAppend .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [inline: before] */' : '';
|
||
|
$extraContentToAppend .= $inlineBeforeJsData;
|
||
|
$extraContentToAppend .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [/inline: before] */' : '';
|
||
|
$extraContentToAppend .= "\n";
|
||
|
}
|
||
|
}
|
||
|
// [/Before JS Content]
|
||
|
} elseif ($addItLocation === 'after') {
|
||
|
// [After JS Content]
|
||
|
if (isset($localAssetsExtra[$assetHref]['after']) && ! empty($localAssetsExtra[$assetHref]['after'])) {
|
||
|
$inlineAfterJsData = '';
|
||
|
|
||
|
foreach ($localAssetsExtra[$assetHref]['after'] as $afterData) {
|
||
|
if (! is_bool($afterData) && self::isInlineJsCombineable($afterData)) {
|
||
|
$inlineAfterJsData .= $afterData."\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( trim($inlineAfterJsData) ) {
|
||
|
$inlineAfterJsData = OptimizeJs::maybeAlterContentForInlineScriptTag( $inlineAfterJsData, $doJsMinifyInline );
|
||
|
$inlineAfterJsData = OptimizeJs::maybeDoJsFixes( $inlineAfterJsData, $pathToAssetDir . '/' );
|
||
|
$extraContentToAppend .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [inline: after] */' : '';
|
||
|
$extraContentToAppend .= $inlineAfterJsData;
|
||
|
$extraContentToAppend .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [/inline: after] */' : '';
|
||
|
$extraContentToAppend .= "\n";
|
||
|
}
|
||
|
}
|
||
|
// [/After JS Content]
|
||
|
} elseif ($addItLocation === 'translations' && isset($localAssetsExtra[$assetHref]['translations']) && $localAssetsExtra[$assetHref]['translations']) {
|
||
|
$inlineAfterJsData = OptimizeJs::maybeAlterContentForInlineScriptTag( $localAssetsExtra[$assetHref]['translations'], $doJsMinifyInline );
|
||
|
$inlineAfterJsData = OptimizeJs::maybeDoJsFixes( $inlineAfterJsData, $pathToAssetDir . '/' );
|
||
|
$extraContentToAppend .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [inline: translations] */' : '';
|
||
|
$extraContentToAppend .= $inlineAfterJsData;
|
||
|
$extraContentToAppend .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [/inline: translations] */' : '';
|
||
|
$extraContentToAppend .= "\n";
|
||
|
}
|
||
|
|
||
|
return $extraContentToAppend;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $jsCode
|
||
|
* @param $sourceUrl
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function maybeWrapBetweenTryCatch($jsCode, $sourceUrl)
|
||
|
{
|
||
|
if ($jsCode && Main::instance()->settings['combine_loaded_js_try_catch']) {
|
||
|
return <<<JS
|
||
|
try {
|
||
|
{$jsCode}
|
||
|
} catch (err) {
|
||
|
console.log("Asset CleanUp - There is a JavaScript error related to the following source: {$sourceUrl} - Error: " + err.message);
|
||
|
}
|
||
|
JS;
|
||
|
}
|
||
|
|
||
|
return $jsCode;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $scriptTag
|
||
|
* @param $wpacuRegisteredScripts
|
||
|
* @param $replaceWith
|
||
|
* @param $htmlSource
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public static function stripTagAndAnyInlineAssocCode($scriptTag, $wpacuRegisteredScripts, $replaceWith, $htmlSource)
|
||
|
{
|
||
|
if (OptimizeCommon::appendInlineCodeToCombineAssetType('js')) {
|
||
|
$scriptExtrasValue = OptimizeJs::getInlineAssociatedWithScriptHandle($scriptTag, $wpacuRegisteredScripts, 'tag', 'value');
|
||
|
|
||
|
$scriptExtraTranslationsValue = (isset($scriptExtrasValue['translations']) && $scriptExtrasValue['translations']) ? $scriptExtrasValue['translations'] : '';
|
||
|
$scriptExtraCdataValue = (isset($scriptExtrasValue['data']) && $scriptExtrasValue['data']) ? $scriptExtrasValue['data'] : '';
|
||
|
$scriptExtraBeforeValue = (isset($scriptExtrasValue['before']) && $scriptExtrasValue['before']) ? $scriptExtrasValue['before'] : '';
|
||
|
$scriptExtraAfterValue = (isset($scriptExtrasValue['after']) && $scriptExtrasValue['after']) ? $scriptExtrasValue['after'] : '';
|
||
|
|
||
|
$scriptExtrasHtml = OptimizeJs::getInlineAssociatedWithScriptHandle($scriptTag, $wpacuRegisteredScripts, 'tag', 'html');
|
||
|
preg_match_all('#data-wpacu-script-handle=([\'])' . '(.*)' . '(\1)#Usmi', $scriptTag, $outputMatches);
|
||
|
$scriptHandle = (isset($outputMatches[2][0]) && $outputMatches[2][0]) ? trim($outputMatches[2][0], '"\'') : '';
|
||
|
|
||
|
$scriptExtraTranslationsHtml = (isset($scriptExtrasHtml['translations']) && $scriptExtrasHtml['translations']) ? $scriptExtrasHtml['translations'] : '';
|
||
|
$scriptExtraCdataHtml = (isset($scriptExtrasHtml['data']) && $scriptExtrasHtml['data']) ? $scriptExtrasHtml['data'] : '';
|
||
|
$scriptExtraBeforeHtml = (isset($scriptExtrasHtml['before']) && $scriptExtrasHtml['before']) ? $scriptExtrasHtml['before'] : '';
|
||
|
$scriptExtraAfterHtml = (isset($scriptExtrasHtml['after']) && $scriptExtrasHtml['after']) ? $scriptExtrasHtml['after'] : '';
|
||
|
|
||
|
if ($scriptExtraTranslationsValue || $scriptExtraCdataValue || $scriptExtraBeforeValue || $scriptExtraAfterValue) {
|
||
|
if ( $scriptExtraCdataValue && self::isInlineJsCombineable($scriptExtraCdataValue) ) {
|
||
|
$htmlSource = str_replace($scriptExtraCdataHtml, '', $htmlSource );
|
||
|
}
|
||
|
|
||
|
if ($scriptExtraTranslationsValue) {
|
||
|
$repsBefore = array(
|
||
|
$scriptExtraTranslationsHtml => '',
|
||
|
str_replace( '<script ', '<script data-wpacu-script-handle=\'' . $scriptHandle . '\' ', $scriptExtraTranslationsHtml ) => '',
|
||
|
'>'."\n".$scriptExtraTranslationsValue."\n".'</script>' => '></script>',
|
||
|
$scriptExtraTranslationsValue."\n" => ''
|
||
|
);
|
||
|
$htmlSource = str_replace(array_keys($repsBefore), array_values($repsBefore), $htmlSource );
|
||
|
}
|
||
|
|
||
|
if ($scriptExtraBeforeValue && self::isInlineJsCombineable($scriptExtraBeforeValue)) {
|
||
|
$repsBefore = array(
|
||
|
$scriptExtraBeforeHtml => '',
|
||
|
str_replace( '<script ', '<script data-wpacu-script-handle=\'' . $scriptHandle . '\' ', $scriptExtraBeforeHtml ) => '',
|
||
|
'>'."\n".$scriptExtraBeforeValue."\n".'</script>' => '></script>',
|
||
|
$scriptExtraBeforeValue."\n" => ''
|
||
|
);
|
||
|
$htmlSource = str_replace(array_keys($repsBefore), array_values($repsBefore), $htmlSource );
|
||
|
}
|
||
|
|
||
|
if ($scriptExtraAfterValue && self::isInlineJsCombineable($scriptExtraAfterValue)) {
|
||
|
$repsBefore = array(
|
||
|
$scriptExtraAfterHtml => '',
|
||
|
str_replace( '<script ', '<script data-wpacu-script-handle=\'' . $scriptHandle . '\' ', $scriptExtraAfterHtml ) => '',
|
||
|
'>'."\n".$scriptExtraAfterValue."\n".'</script>' => '></script>',
|
||
|
$scriptExtraAfterValue."\n" => ''
|
||
|
);
|
||
|
$htmlSource = str_replace(array_keys($repsBefore), array_values($repsBefore), $htmlSource);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Finally, strip/replace the tag
|
||
|
return str_replace( array($scriptTag."\n", $scriptTag), $replaceWith, $htmlSource );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This is to prevent certain inline JS to be appended to the combined JS files in order to avoid lots of disk space (sometimes a few GB) of JS combined files
|
||
|
*
|
||
|
* @param $jsInlineValue
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public static function isInlineJsCombineable($jsInlineValue)
|
||
|
{
|
||
|
// The common WordPress nonce
|
||
|
if (strpos($jsInlineValue, 'nonce') !== false) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// WooCommerce Cart Fragments
|
||
|
if (strpos($jsInlineValue, 'wc_cart_hash_') !== false && strpos($jsInlineValue, 'cart_hash_key') !== false) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (substr(trim($jsInlineValue), 0, 1) === '{' && substr(trim($jsInlineValue), -1, 1) === '}') {
|
||
|
json_decode($jsInlineValue);
|
||
|
|
||
|
if (json_last_error() === JSON_ERROR_NONE) {
|
||
|
return false; // it's a JSON format (e.g. type="application/json" from "wordpress-popular-posts" plugin)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true; // default
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool
|
||
|
*/
|
||
|
public static function proceedWithJsCombine()
|
||
|
{
|
||
|
// not on query string request (debugging purposes)
|
||
|
if ( isset($_REQUEST['wpacu_no_js_combine']) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// No JS files are combined in the Dashboard
|
||
|
// Always in the front-end view
|
||
|
// Do not combine if there's a POST request as there could be assets loading conditionally
|
||
|
// that might not be needed when the page is accessed without POST, making the final JS file larger
|
||
|
if (! empty($_POST) || is_admin()) {
|
||
|
return false; // Do not combine
|
||
|
}
|
||
|
|
||
|
// Only clean request URIs allowed (with few exceptions)
|
||
|
if (strpos($_SERVER['REQUEST_URI'], '?') !== false) {
|
||
|
// Exceptions
|
||
|
if (! OptimizeCommon::loadOptimizedAssetsIfQueryStrings()) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (! OptimizeCommon::doCombineIsRegularPage()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$pluginSettings = Main::instance()->settings;
|
||
|
|
||
|
if ($pluginSettings['test_mode'] && ! Menu::userCanManageAssets()) {
|
||
|
return false; // Do not combine anything if "Test Mode" is ON
|
||
|
}
|
||
|
|
||
|
if ($pluginSettings['combine_loaded_js'] === '') {
|
||
|
return false; // Do not combine
|
||
|
}
|
||
|
|
||
|
if (OptimizeJs::isOptimizeJsEnabledByOtherParty('if_enabled')) {
|
||
|
return false; // Do not combine (it's already enabled in other plugin)
|
||
|
}
|
||
|
|
||
|
// "Minify HTML" from WP Rocket is sometimes stripping combined SCRIPT tags
|
||
|
// Better uncombined then missing essential SCRIPT files
|
||
|
if (Misc::isWpRocketMinifyHtmlEnabled()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
if ( ($pluginSettings['combine_loaded_js'] === 'for_admin'
|
||
|
|| $pluginSettings['combine_loaded_js_for_admin_only'] == 1)
|
||
|
&& Menu::userCanManageAssets() ) {
|
||
|
return true; // Do combine
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
// "Apply it only for guest visitors (default)" is set; Do not combine if the user is logged in
|
||
|
if ( $pluginSettings['combine_loaded_js_for'] === 'guests' && is_user_logged_in() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( in_array($pluginSettings['combine_loaded_js'], array('for_all', 1)) ) {
|
||
|
return true; // Do combine
|
||
|
}
|
||
|
|
||
|
// Finally, return false as none of the checks above matched
|
||
|
return false;
|
||
|
}
|
||
|
}
|