Asset CleanUp: Page Speed Booster - Version 1.3.2

Version Description

  • Minify/Combine JS files & HEAD CleanUp features are now available in the Lite version
Download this release

Release Info

Developer gabelivan
Plugin Icon 128x128 Asset CleanUp: Page Speed Booster
Version 1.3.2
Comparing to
See all releases

Code changes from version 1.3.1 to 1.3.2

assets/script.min.js CHANGED
@@ -1 +1 @@
1
- function wpacuTabOpenSettingsArea(a,b){a.preventDefault();var c,d,e;for(d=document.getElementsByClassName("wpacu-settings-tab-content"),c=0;c<d.length;c++)d[c].style.display="none";for(e=document.getElementsByClassName("wpacu-settings-tab-link"),c=0;c<e.length;c++)e[c].className=e[c].className.replace(" active","");document.getElementById(b).style.display="table-cell",jQuery('a[href="#'+b+'"]').addClass("active"),jQuery("#wpacu-selected-tab-area").val(b),history.pushState(null,null,"#"+b)}if(jQuery(document).ready(function(a){function b(){var b={};"direct"===wpacu_object.dom_get_type?(b[wpacu_object.plugin_name+"_load"]=1,b[wpacu_object.plugin_name+"_time_r"]=(new Date).getTime(),a.ajax({method:"GET",url:wpacu_object.page_url,data:b,cache:!1,complete:function(b,c){if("error"===b.statusText){var d=wpacu_object.ajax_direct_fetch_error;d=d.replace(/\{wpacu_output\}/,b.responseText),d=d.replace(/\{wpacu_status_code_error\}/,b.status),a("#wpacu_meta_box_content").html(d)}}}).done(function(b){var c=b.substring(b.lastIndexOf(wpacu_object.start_del)+wpacu_object.start_del.length,b.lastIndexOf(wpacu_object.end_del)),d={action:wpacu_object.plugin_name+"_get_loaded_assets",wpacu_list:c,post_id:wpacu_object.post_id,page_url:wpacu_object.page_url,tag_id:wpacu_object.tag_id,time_r:(new Date).getTime()};a.post(wpacu_object.ajax_url,d,function(b){if(!b)return!1;a("#wpacu_meta_box_content").html(b),a("#wpacu_home_page_form").length>0&&a("#submit").show(),setTimeout(function(){e.load()},200)})})):"wp_remote_post"===wpacu_object.dom_get_type&&(b={action:wpacu_object.plugin_name+"_get_loaded_assets",post_id:wpacu_object.post_id,page_url:wpacu_object.page_url,tag_id:wpacu_object.tag_id,time_r:(new Date).getTime()},a.post(wpacu_object.ajax_url,b,function(b){if(!b)return!1;a("#wpacu_meta_box_content").html(b),a("#wpacu_home_page_form").length>0&&a("#submit").show(),setTimeout(function(){e.load()},200)}))}a("#wpacu-mark-license-valid-button").click(function(){return confirm(wpacu_object.mark_license_valid_confirm)});var c,d;a("#wpacu-reset-drop-down").on("change keyup keydown mouseup mousedown click",function(){""===a(this).val()?(a("#wpacu-warning-read").removeClass("wpacu-visible"),a("#wpacu-reset-submit-btn").attr("disabled","disabled").removeClass("button-primary").addClass("button-secondary")):("reset_everything"===a(this).val()?a("#wpacu-license-data-remove-area").addClass("wpacu-visible"):a("#wpacu-license-data-remove-area").removeClass("wpacu-visible"),a("#wpacu-warning-read").addClass("wpacu-visible"),a("#wpacu-reset-submit-btn").removeAttr("disabled").removeClass("button-secondary").addClass("button-primary")),a(".wpacu-tools-area .wpacu-warning").hide(),c=a(this).find("option:selected"),a("#"+c.attr("data-id")).show()}),a("#wpacu-reset-submit-btn").on("click",function(){if("reset_settings"===a("#wpacu-reset-drop-down").val()?d=wpacu_object.reset_settings_confirm_msg:"reset_everything"===a("#wpacu-reset-drop-down").val()&&(d=wpacu_object.reset_everything_confirm_msg),!confirm(d))return!1;a("#wpacu-action-confirmed").val("yes"),setTimeout(function(){"yes"===a("#wpacu-action-confirmed").val()&&a("#wpacu-tools-form").submit()},1e3)});var e={load:function(){var b;a(".input-unload-on-this-page").on("click change",function(){a(this).prop("checked")?a(this).closest("tr").addClass("wpacu_not_load"):a(this).closest("tr").removeClass("wpacu_not_load")}),a(".wpacu_global_unload").click(function(){b=a(this).attr("data-handle"),a(this).prop("checked")?a(this).parent("label").addClass("wpacu_global_checked"):a(this).parent("label").removeClass("wpacu_global_checked")}),a(".wpacu_keep_bulk_rule").click(function(){a(this).prop("checked")&&a(this).parents("li").next().removeClass("remove_rule")}),a(".wpacu_remove_bulk_rule").click(function(){a(this).prop("checked")&&a(this).parents("li").addClass("remove_rule")}),a(".wpacu_bulk_unload").click(function(){a(this).prop("checked")?a(this).parent("label").addClass("wpacu_bulk_unload_active"):a(this).parent("label").removeClass("wpacu_bulk_unload_active")}),a(".wpacu_load_it_option").click(function(){var b=a(this).attr("data-handle");if(a(this).prop("checked")){a(this).parent("label").addClass("wpacu_global_unload_exception");var c="";a(this).hasClass("wpacu_style")?c="style":a(this).hasClass("wpacu_script")&&(c="script"),a("#"+c+"_"+b).prop("checked",!1).trigger("change")}else a(this).parent("label").removeClass("wpacu_global_unload_exception")}),a(".wpacu-external-file-size").on("click",function(b){b.preventDefault();var c,d=a(this);d.hide(),c=d.next(),c.show(),a.post(wpacu_object.ajax_url,{action:"get_external_file_size",wpacu_remote_file:d.attr("data-src")},function(a){c.html(a)})})}};a(".wpacu-dom-get-type-selection").change(function(){a(this).is(":checked")&&(a(".wpacu-dom-get-type-info").hide(),a("#"+a(this).attr("data-target")).fadeIn("fast"))}),a("#wpacu_post_type_select").change(function(){a("#wpacu_post_type_form").submit()}),a("#wpacu_taxonomy_select").change(function(){a("#wpacu_taxonomy_form").submit()}),a("#wpacu_dashboard").click(function(){a(this).prop("checked")?a("#wpacu-settings-assets-retrieval-mode").fadeIn("fast"):a("#wpacu-settings-assets-retrieval-mode").fadeOut("fast")}),a("#wpacu_disable_jquery_migrate").on("click",function(){return!a(this).is(":checked")||(!(!a(this).is(":checked")||!confirm(wpacu_object.jquery_migration_disable_confirm_msg))||(a(this).prop("checked",!1),!1))}),a("#wpacu_disable_comment_reply").on("click",function(){return!a(this).is(":checked")||(!(!a(this).is(":checked")||!confirm(wpacu_object.comment_reply_disable_confirm_msg))||(a(this).prop("checked",!1),!1))}),a("#wpacu_combine_loaded_css_enable").click(function(){a(this).prop("checked")?a("#combine_loaded_css_info_area").css({opacity:1}):a("#combine_loaded_css_info_area").css({opacity:.4})}),a("#wpacu_combine_loaded_js_enable").click(function(){a(this).prop("checked")?a("#combine_loaded_js_info_area").css({opacity:1}):a("#combine_loaded_js_info_area").css({opacity:.4})}),a("#wpacu_minify_css_enable").click(function(){a(this).prop("checked")?a("#wpacu_minify_css_exceptions_area").css({opacity:1}):a("#wpacu_minify_css_exceptions_area").css({opacity:.4})}),a("#wpacu_minify_js_enable").click(function(){a(this).prop("checked")?a("#wpacu_minify_js_exceptions_area").css({opacity:1}):a("#wpacu_minify_js_exceptions_area").css({opacity:.4})}),a(".wpacu-combine-loaded-js-level").change(function(){a(this).is(":checked")&&(a(".wpacu_combine_loaded_js_level_area").removeClass("wpacu_active"),a("#"+a(this).attr("data-target")).addClass("wpacu_active"))});var f=a('#wpacu-update-button-area input[type="submit"]');if(f.click(function(){a("#wpacu-updating-settings").show()}),a("form#wpacu-settings-form").submit(function(){return f.attr("disabled","disabled"),!0}),a(".wpacu_remove_rule").click(function(){var b=a(this).parents(".wpacu_global_rule_row");a(this).prop("checked")?b.addClass("selected"):b.removeClass("selected")}),a("#wpacu_wrap_assets").length>0&&setTimeout(function(){e.load()},200),"undefined"==typeof wpacu_object||a("#wpacu_meta_box_content").length<1)return!1;b(),a(document).on("click","body.branch-5 .edit-post-header__settings button.is-primary",function(){var c=function(){0===a(".edit-post-header__settings .is-saving").length&&(b(),clearInterval(d))},d=setInterval(c,900)})}),-1!==location.href.indexOf("#")){var hashFromUrl=location.href.substr(location.href.indexOf("#"));jQuery('a[href="'+hashFromUrl+'"]').trigger("click")}
1
+ function wpacuTabOpenSettingsArea(a,b){a.preventDefault();var c,d,e;for(d=document.getElementsByClassName("wpacu-settings-tab-content"),c=0;c<d.length;c++)d[c].style.display="none";for(e=document.getElementsByClassName("wpacu-settings-tab-link"),c=0;c<e.length;c++)e[c].className=e[c].className.replace(" active","");document.getElementById(b).style.display="table-cell",jQuery('a[href="#'+b+'"]').addClass("active"),jQuery("#wpacu-selected-tab-area").val(b),history.pushState(null,null,"#"+b)}if(jQuery(document).ready(function(a){function b(){if(!a("#wpacu_ajax_fetch_assets_list_dashboard_view").length)return!1;var b={};"direct"===wpacu_object.dom_get_type?(b[wpacu_object.plugin_name+"_load"]=1,b[wpacu_object.plugin_name+"_time_r"]=(new Date).getTime(),a.ajax({method:"GET",url:wpacu_object.page_url,data:b,cache:!1,complete:function(b,c){if("error"===b.statusText){var d=wpacu_object.ajax_direct_fetch_error;d=d.replace(/\{wpacu_output\}/,b.responseText),d=d.replace(/\{wpacu_status_code_error\}/,b.status),a("#wpacu_meta_box_content").html(d)}}}).done(function(b){var c=b.substring(b.lastIndexOf(wpacu_object.start_del)+wpacu_object.start_del.length,b.lastIndexOf(wpacu_object.end_del)),d={action:wpacu_object.plugin_name+"_get_loaded_assets",wpacu_list:c,post_id:wpacu_object.post_id,page_url:wpacu_object.page_url,tag_id:wpacu_object.tag_id,time_r:(new Date).getTime()};a.post(wpacu_object.ajax_url,d,function(b){if(!b)return!1;a("#wpacu_meta_box_content").html(b),a("#wpacu_home_page_form").length>0&&a("#submit").show(),setTimeout(function(){e.load()},200)})})):"wp_remote_post"===wpacu_object.dom_get_type&&(b={action:wpacu_object.plugin_name+"_get_loaded_assets",post_id:wpacu_object.post_id,page_url:wpacu_object.page_url,tag_id:wpacu_object.tag_id,time_r:(new Date).getTime()},a.post(wpacu_object.ajax_url,b,function(b){if(!b)return!1;a("#wpacu_meta_box_content").html(b),a("#wpacu_home_page_form").length>0&&a("#submit").show(),setTimeout(function(){e.load()},200)}))}a("#wpacu-mark-license-valid-button").click(function(){return confirm(wpacu_object.mark_license_valid_confirm)});var c,d;a("#wpacu-reset-drop-down").on("change keyup keydown mouseup mousedown click",function(){""===a(this).val()?(a("#wpacu-warning-read").removeClass("wpacu-visible"),a("#wpacu-reset-submit-btn").attr("disabled","disabled").removeClass("button-primary").addClass("button-secondary")):("reset_everything"===a(this).val()?a("#wpacu-license-data-remove-area").addClass("wpacu-visible"):a("#wpacu-license-data-remove-area").removeClass("wpacu-visible"),a("#wpacu-warning-read").addClass("wpacu-visible"),a("#wpacu-reset-submit-btn").removeAttr("disabled").removeClass("button-secondary").addClass("button-primary")),a(".wpacu-tools-area .wpacu-warning").hide(),c=a(this).find("option:selected"),a("#"+c.attr("data-id")).show()}),a("#wpacu-reset-submit-btn").on("click",function(){if("reset_settings"===a("#wpacu-reset-drop-down").val()?d=wpacu_object.reset_settings_confirm_msg:"reset_everything"===a("#wpacu-reset-drop-down").val()&&(d=wpacu_object.reset_everything_confirm_msg),!confirm(d))return!1;a("#wpacu-action-confirmed").val("yes"),setTimeout(function(){"yes"===a("#wpacu-action-confirmed").val()&&a("#wpacu-tools-form").submit()},1e3)});var e={load:function(){var b;a(".input-unload-on-this-page").on("click change",function(){a(this).prop("checked")?a(this).closest("tr").addClass("wpacu_not_load"):a(this).closest("tr").removeClass("wpacu_not_load")}),a(".wpacu_global_unload").click(function(){b=a(this).attr("data-handle"),a(this).prop("checked")?a(this).parent("label").addClass("wpacu_global_checked"):a(this).parent("label").removeClass("wpacu_global_checked")}),a(".wpacu_keep_bulk_rule").click(function(){a(this).prop("checked")&&a(this).parents("li").next().removeClass("remove_rule")}),a(".wpacu_remove_bulk_rule").click(function(){a(this).prop("checked")&&a(this).parents("li").addClass("remove_rule")}),a(".wpacu_bulk_unload").click(function(){a(this).prop("checked")?a(this).parent("label").addClass("wpacu_bulk_unload_active"):a(this).parent("label").removeClass("wpacu_bulk_unload_active")}),a(".wpacu_load_it_option").click(function(){var b=a(this).attr("data-handle");if(a(this).prop("checked")){a(this).parent("label").addClass("wpacu_global_unload_exception");var c="";a(this).hasClass("wpacu_style")?c="style":a(this).hasClass("wpacu_script")&&(c="script"),a("#"+c+"_"+b).prop("checked",!1).trigger("change")}else a(this).parent("label").removeClass("wpacu_global_unload_exception")}),a(".wpacu-external-file-size").on("click",function(b){b.preventDefault();var c,d=a(this);d.hide(),c=d.next(),c.show(),a.post(wpacu_object.ajax_url,{action:"get_external_file_size",wpacu_remote_file:d.attr("data-src")},function(a){c.html(a)})})}};a(".wpacu-dom-get-type-selection").change(function(){a(this).is(":checked")&&(a(".wpacu-dom-get-type-info").hide(),a("#"+a(this).attr("data-target")).fadeIn("fast"))}),a("#wpacu_post_type_select").change(function(){a("#wpacu_post_type_form").submit()}),a("#wpacu_taxonomy_select").change(function(){a("#wpacu_taxonomy_form").submit()}),a("#wpacu_dashboard").click(function(){a(this).prop("checked")?a("#wpacu-settings-assets-retrieval-mode").fadeIn("fast"):a("#wpacu-settings-assets-retrieval-mode").fadeOut("fast")}),a("#wpacu_disable_jquery_migrate").on("click",function(){return!a(this).is(":checked")||(!(!a(this).is(":checked")||!confirm(wpacu_object.jquery_migration_disable_confirm_msg))||(a(this).prop("checked",!1),!1))}),a("#wpacu_disable_comment_reply").on("click",function(){return!a(this).is(":checked")||(!(!a(this).is(":checked")||!confirm(wpacu_object.comment_reply_disable_confirm_msg))||(a(this).prop("checked",!1),!1))}),a("#wpacu_combine_loaded_css_enable").click(function(){a(this).prop("checked")?a("#combine_loaded_css_info_area").css({opacity:1}):a("#combine_loaded_css_info_area").css({opacity:.4})}),a("#wpacu_combine_loaded_js_enable").click(function(){a(this).prop("checked")?a("#combine_loaded_js_info_area").css({opacity:1}):a("#combine_loaded_js_info_area").css({opacity:.4})}),a("#wpacu_minify_css_enable").click(function(){a(this).prop("checked")?a("#wpacu_minify_css_exceptions_area").css({opacity:1}):a("#wpacu_minify_css_exceptions_area").css({opacity:.4})}),a("#wpacu_minify_js_enable").click(function(){a(this).prop("checked")?a("#wpacu_minify_js_exceptions_area").css({opacity:1}):a("#wpacu_minify_js_exceptions_area").css({opacity:.4})}),a(".wpacu-combine-loaded-js-level").change(function(){a(this).is(":checked")&&(a(".wpacu_combine_loaded_js_level_area").removeClass("wpacu_active"),a("#"+a(this).attr("data-target")).addClass("wpacu_active"))});var f=a('#wpacu-update-button-area input[type="submit"]');if(f.click(function(){a("#wpacu-updating-settings").show()}),a("form#wpacu-settings-form").submit(function(){return f.attr("disabled","disabled"),!0}),a(".wpacu_remove_rule").click(function(){var b=a(this).parents(".wpacu_global_rule_row");a(this).prop("checked")?b.addClass("selected"):b.removeClass("selected")}),a("#wpacu_wrap_assets").length>0&&setTimeout(function(){e.load()},200),"undefined"==typeof wpacu_object||a("#wpacu_meta_box_content").length<1)return!1;b(),a(document).on("click",".wp-admin.wp-editor .edit-post-header__settings button.is-primary",function(){var c=function(){0===a(".edit-post-header__settings .is-saving").length&&(b(),clearInterval(d))},d=setInterval(c,900)})}),-1!==location.href.indexOf("#")){var hashFromUrl=location.href.substr(location.href.indexOf("#"));jQuery('a[href="'+hashFromUrl+'"]').trigger("click")}
classes/AdminBar.php CHANGED
@@ -12,7 +12,7 @@ class AdminBar
12
*/
13
public function __construct()
14
{
15
- add_action( 'plugins_loaded', array( $this, 'topBar' ) );
16
}
17
18
/**
12
*/
13
public function __construct()
14
{
15
+ add_action( 'init', array( $this, 'topBar' ) );
16
}
17
18
/**
classes/AssetsPagesManager.php CHANGED
@@ -14,8 +14,6 @@ class AssetsPagesManager
14
15
/**
16
* AssetsPagesManager constructor.
17
- *
18
- * @param $data
19
*/
20
public function __construct()
21
{
14
15
/**
16
* AssetsPagesManager constructor.
17
*/
18
public function __construct()
19
{
classes/CleanUp.php ADDED
@@ -0,0 +1,227 @@
1
+ <?php
2
+ namespace WpAssetCleanUp;
3
+
4
+ /**
5
+ * Class CleanUp
6
+ * @package WpAssetCleanUpPro
7
+ */
8
+ class CleanUp
9
+ {
10
+ /**
11
+ *
12
+ */
13
+ public function init()
14
+ {
15
+ // Is "Test Mode" is enabled and the page is viewed by a regular visitor (not administrator with plugin activation privileges)?
16
+ // Stop here as the script will NOT PREVENT any of the elements below to load
17
+ // They will load as they used to for the regular visitor while the admin debugs the website
18
+ add_action('init', function() {
19
+ if ( Main::instance()->preventUnloadAssets() ) {
20
+ return;
21
+ }
22
+
23
+ CleanUp::doClean();
24
+ });
25
+ }
26
+
27
+ /**
28
+ *
29
+ */
30
+ public function doClean()
31
+ {
32
+ $settings = Main::instance()->settings;
33
+
34
+ // Remove "Really Simple Discovery (RSD)" link?
35
+ if ($settings['remove_rsd_link'] == 1) {
36
+ // <link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://yourwebsite.com/xmlrpc.php?rsd" />
37
+ remove_action('wp_head', 'rsd_link');
38
+ }
39
+
40
+ // Remove "Windows Live Writer" link?
41
+ if ($settings['remove_wlw_link'] == 1) {
42
+ // <link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://yourwebsite.com/wp-includes/wlwmanifest.xml">
43
+ remove_action('wp_head', 'wlwmanifest_link');
44
+ }
45
+
46
+ // Remove "REST API" link?
47
+ if ($settings['remove_rest_api_link'] == 1) {
48
+ // <link rel='https://api.w.org/' href='https://yourwebsite.com/wp-json/' />
49
+ remove_action('wp_head', 'rest_output_link_wp_head');
50
+ }
51
+
52
+ // Remove "Shortlink"?
53
+ if ($settings['remove_shortlink'] == 1) {
54
+ // <link rel='shortlink' href="https://yourdomain.com/?p=1">
55
+ remove_action('wp_head', 'wp_shortlink_wp_head');
56
+ }
57
+
58
+ // Remove "Post's Relational Links"?
59
+ if ($settings['remove_posts_rel_links'] == 1) {
60
+ // <link rel='prev' title='Title of adjacent post' href='https://yourdomain.com/adjacent-post-slug-here/' />
61
+ remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
62
+ }
63
+
64
+ // Remove "WordPress version" tag?
65
+ if ($settings['remove_wp_version']) {
66
+ // <meta name="generator" content="WordPress 4.9.8" />
67
+ remove_action('wp_head', 'wp_generator');
68
+
69
+ // also hide it from RSS
70
+ add_filter('the_generator', '__return_false');
71
+ }
72
+
73
+ // Remove Main RSS Feed Link?
74
+ if ($settings['remove_main_feed_link']) {
75
+ add_filter('feed_links_show_posts_feed', '__return_false');
76
+ remove_action('wp_head', 'feed_links_extra', 3);
77
+ }
78
+
79
+ // Remove Comment RSS Feed Link?
80
+ if ($settings['remove_comment_feed_link']) {
81
+ add_filter('feed_links_show_comments_feed', '__return_false');
82
+ }
83
+
84
+ // Remove "WordPress version" and all other "generator" meta tags?
85
+ if ($settings['remove_generator_tag']) {
86
+ add_action('wp_loaded', function () {
87
+ ob_start(function ($htmlSource) {
88
+ return self::removeMetaGenerators($htmlSource);
89
+ });
90
+ } );
91
+ }
92
+
93
+ // Disable XML-RPC protocol support (partially or completely)
94
+ if (in_array($settings['disable_xmlrpc'], array('disable_all', 'disable_pingback'))) {
95
+ // Partially or Completely Options / Pingback will be disabled
96
+ $this->disableXmlRpcPingback();
97
+
98
+ // Complete disable the service
99
+ if ($settings['disable_xmlrpc'] === 'disable_all') {
100
+ add_filter('xmlrpc_enabled', '__return_false');
101
+ }
102
+
103
+ // Also clean it up from the <head>
104
+ add_action('wp_loaded', function() {
105
+ ob_start(function ($htmlSource) {
106
+ $pingBackUrl = get_bloginfo('pingback_url');
107
+
108
+ $matchRegExps = array(
109
+ '#<link rel=("|\')pingback("|\') href=("|\')'.$pingBackUrl.'("|\')( /|)>#',
110
+ '#<link href=("|\')'.$pingBackUrl.'("|\') rel=("|\')pingback("|\')( /|)>#'
111
+ );
112
+
113
+ foreach ($matchRegExps as $matchRegExp) {
114
+ $htmlSource = preg_replace($matchRegExp, '', $htmlSource);
115
+ }
116
+
117
+ return $htmlSource;
118
+ });
119
+ });
120
+ }
121
+ }
122
+
123
+ /**
124
+ *
125
+ */
126
+ public function disableXmlRpcPingback()
127
+ {
128
+ // Disable Pingback method
129
+ add_filter('xmlrpc_methods', function ($methods) {
130
+ unset($methods['pingback.ping'], $methods['pingback.extensions.getPingbacks']);
131
+ return $methods;
132
+ } );
133
+
134
+ // Remove X-Pingback HTTP header
135
+ add_filter('wp_headers', function ($headers) {
136
+ unset($headers['X-Pingback']);
137
+ return $headers;
138
+ });
139
+ }
140
+
141
+ /**
142
+ * @param $htmlSource
143
+ *
144
+ * @return mixed
145
+ */
146
+ public static function removeMetaGenerators($htmlSource)
147
+ {
148
+ if (stripos($htmlSource, '<meta') === false) {
149
+ return $htmlSource;
150
+ }
151
+
152
+ // Use DOMDocument to alter the HTML Source and Remove the tags
153
+ $htmlSourceOriginal = $htmlSource;
154
+
155
+ if (function_exists('libxml_use_internal_errors')
156
+ && function_exists('libxml_clear_errors')
157
+ && class_exists('DOMDocument'))
158
+ {
159
+ $document = new \DOMDocument();
160
+ libxml_use_internal_errors(true);
161
+
162
+ $document->loadHTML($htmlSource);
163
+
164
+ $domUpdated = false;
165
+
166
+ foreach ($document->getElementsByTagName('meta') as $tagObject) {
167
+ $nameAttrValue = $tagObject->getAttribute('name');
168
+
169
+ if ($nameAttrValue === 'generator') {
170
+ $outerTag = $outerTagRegExp = trim(self::getOuterHTML($tagObject));
171
+ $last2Chars = substr($outerTag, -2);
172
+
173
+ if ($last2Chars === '">' || $last2Chars === "'>") {
174
+ $tagWithoutLastChar = substr($outerTag, 0, -1);
175
+ $outerTagRegExp = $tagWithoutLastChar.'(.*?)>';
176
+ }
177
+
178
+ if (strpos($outerTagRegExp, '<meta') !== false) {
179
+ preg_match_all('#' . $outerTagRegExp . '#si', $htmlSource, $matches);
180
+
181
+ if (isset($matches[0][0]) && ! empty($matches[0][0]) && strip_tags($matches[0][0]) === '') {
182
+ $htmlSource = str_replace( $matches[0][0], '', $htmlSource );
183
+ }
184
+
185
+ if ($htmlSource !== $htmlSourceOriginal) {
186
+ $domUpdated = true;
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ libxml_clear_errors();
193
+
194
+ if ($domUpdated) {
195
+ return $htmlSource;
196
+ }
197
+ }
198
+
199
+ // DOMDocument is not enabled. Use the RegExp instead (not as smooth, but does its job)!
200
+ preg_match_all('#<meta(.*?)>#si', $htmlSource, $matches);
201
+
202
+ if (isset($matches[0]) && ! empty($matches[0])) {
203
+ foreach ($matches[0] as $metaTag) {
204
+ if (strip_tags($metaTag) === ''
205
+ && (stripos($metaTag, 'name="generator"') !== false || stripos($metaTag, 'name=\'generator\'') !== false)
206
+ ) {
207
+ $htmlSource = str_replace($metaTag, '', $htmlSource);
208
+ }
209
+ }
210
+ }
211
+
212
+ return $htmlSource;
213
+ }
214
+
215
+ /**
216
+ * @param $e
217
+ *
218
+ * @return mixed
219
+ */
220
+ public static function getOuterHTML($e)
221
+ {
222
+ $doc = new \DOMDocument();
223
+ $doc->appendChild( $doc->importNode( $e, true ) );
224
+
225
+ return $doc->saveHTML();
226
+ }
227
+ }
classes/Main.php CHANGED
@@ -75,10 +75,15 @@ class Main
75
*/
76
public $isFrontendEditView = false;
77
78
/**
79
* @var array
80
*/
81
- public $assetsInFooter = array();
82
83
/**
84
* @var array
@@ -116,8 +121,8 @@ class Main
116
public $isAjaxCall = false;
117
118
/**
119
- * Populated in the Parser constructor
120
- *
121
* @var array
122
*/
123
public $skipAssets = array('styles' => array(), 'scripts' => array());
@@ -125,7 +130,7 @@ class Main
125
/**
126
* @var Main|null
127
*/
128
- private static $singleton = null;
129
130
/**
131
* @return null|Main
@@ -144,20 +149,20 @@ class Main
144
*/
145
public function __construct()
146
{
147
- $this->skipAssets['styles'] = array(
148
- WPACU_PLUGIN_ID . '-style', // Asset CleanUp Styling (for admin use only)
149
- 'admin-bar', // The top admin bar
150
- 'yoast-seo-adminbar', // Yoast "WordPress SEO" plugin
151
- 'autoptimize-toolbar',
152
- 'query-monitor'
153
- );
154
155
$this->skipAssets['scripts'] = array(
156
WPACU_PLUGIN_ID . '-script', // Asset CleanUp Script (for admin use only)
157
'admin-bar', // The top admin bar
158
'autoptimize-toolbar',
159
'query-monitor'
160
- );
161
162
if (array_key_exists(WPACU_LOAD_ASSETS_REQ_KEY, $_REQUEST)) {
163
add_filter('w3tc_minify_enable', '__return_false');
@@ -177,7 +182,7 @@ class Main
177
178
// This is triggered AFTER "saveSettings" from 'Settings' class
179
// In case the settings were just updated, the script will get the latest values
180
- add_action('plugins_loaded', array($this, 'initAfterPluginsLoaded'), 10);
181
182
// Front-end View - Unload the assets
183
// If there are reasons to prevent the unloading in case 'test mode' is enabled,
@@ -196,7 +201,7 @@ class Main
196
add_action( 'wp_print_footer_scripts', array( $this, 'filterScripts' ), 1 );
197
add_action( 'wp_print_footer_scripts', array( $this, 'filterStyles' ), 1 );
198
}
199
-
200
// This is recommended to keep active for Lite users as it helps spread the word about the plugin
201
// To remove the notice, consider upgrading to PRO
202
@@ -207,7 +212,7 @@ class Main
207
$this->wpacuUsageNotice();
208
}
209
210
- add_action( 'admin_footer', array( $this, 'ajaxFetchActivePluginsJsFooterCode' ) );
211
add_action( 'wp_ajax_' . WPACU_PLUGIN_ID . '_fetch_active_plugins_icons',
212
array( $this, 'ajaxFetchActivePluginsIcons' ) );
213
@@ -217,30 +222,37 @@ class Main
217
/**
218
*
219
*/
220
- public function initAfterPluginsLoaded()
221
- {
222
- $wpacuSettingsClass = new Settings();
223
- $this->settings = $wpacuSettingsClass->getAll();
224
225
- if ($this->settings['dashboard_show'] && $this->settings['dom_get_type']) {
226
- self::$domGetType = $this->settings['dom_get_type'];
227
- }
228
229
- // Fetch the page in the background to see what scripts/styles are already loading
230
- if (isset($_REQUEST[WPACU_LOAD_ASSETS_REQ_KEY]) || $this->settings['frontend_show']) {
231
- if (isset($_REQUEST[WPACU_LOAD_ASSETS_REQ_KEY])) {
232
- Misc::noAdminBarLoad();
233
- }
234
235
- add_action('wp_head', array($this, 'saveFooterAssets'), 100000000);
236
- add_action('wp_footer', array($this, 'printScriptsStyles'), PHP_INT_MAX);
237
- }
238
239
- $metaboxes = new MetaBoxes;
240
241
- if ( is_admin() ) {
242
- // Do not load the meta box nor do any AJAX calls
243
- // if the asset management is not enabled for the Dashboard
244
if ($this->settings['dashboard_show'] == 1) {
245
// Send an AJAX request to get the list of loaded scripts and styles and print it nicely
246
add_action(
@@ -252,17 +264,15 @@ class Main
252
}
253
254
// Side Meta Box: Asset CleanUp Options
255
- $metaboxes->initCustomOptionsMetaBox();
256
- }
257
258
- if ($this->settings['disable_emojis'] == 1) {
259
- add_action('init', array($this, 'doDisableEmojis'));
260
- }
261
- }
262
263
/**
264
- * This has to be triggered after 'plugins_loaded' (e.g. in 'wp')
265
- *
266
* Priority: 8 (earliest)
267
*/
268
public function setVarsBeforeUpdate()
@@ -330,7 +340,7 @@ class Main
330
$globalUnload = $this->globalUnloaded;
331
332
// [wpacu_lite]
333
- if (! empty($globalUnload['scripts']) && $nonAssetConfigPage) {
334
$list = $globalUnload['scripts'];
335
} else { // [/wpacu_lite]
336
// Post, Page or Front-page?
@@ -453,7 +463,7 @@ class Main
453
$globalUnload = $this->globalUnloaded;
454
455
// [wpacu_lite]
456
- if (! empty($globalUnload['styles']) && $nonAssetConfigPage) {
457
$list = $globalUnload['styles'];
458
} else { // [/wpacu_lite]
459
// Post, Page, Front-page
@@ -505,11 +515,11 @@ class Main
505
}
506
}
507
508
- global $wp_styles;
509
510
$allStyles = $wp_styles;
511
512
- if ($allStyles !== null && isset($allStyles->registered)) {
513
$i = $this->lastStylePos;
514
515
foreach ($allStyles->registered as $handle => $value) {
@@ -650,15 +660,37 @@ class Main
650
return $existingList;
651
}
652
653
/**
654
*
655
*/
656
- public function saveFooterAssets()
657
{
658
global $wp_scripts;
659
660
- $this->assetsInFooter = $wp_scripts->in_footer;
661
- }
662
663
/**
664
* This output will be extracted and the JSON will be processed
@@ -736,11 +768,11 @@ class Main
736
$manageScripts = $wp_scripts->done;
737
738
if ($isFrontEndEditView) {
739
- if (isset($this->wpAllStyles['queue']) && ! empty($this->wpAllStyles)) {
740
$manageStyles = $this->wpAllStyles['queue'];
741
}
742
743
- if (isset($this->wpAllScripts['queue']) && ! empty($this->wpAllScripts)) {
744
$manageScripts = $this->wpAllScripts['queue'];
745
}
746
@@ -793,12 +825,12 @@ class Main
793
if (! empty($stylesList)) {
794
/* These styles below are used by this plugin (except admin-bar) and they should not show in the list
795
as they are loaded only when you (or other admin) manage the assets, never for your website visitors */
796
- if (is_admin_bar_showing() && is_admin()) {
797
- $this->skipAssets['styles'][] = 'dashicons';
798
- }
799
800
foreach ($manageStyles as $handle) {
801
- if (in_array($handle, $this->skipAssets['styles']) || (! isset($stylesList[$handle]))) {
802
continue;
803
}
804
@@ -814,7 +846,7 @@ class Main
814
}
815
816
// Append unloaded ones (if any)
817
- if (! empty($currentUnloadedAll['styles']) && !empty($stylesBeforeUnload)) {
818
foreach ($currentUnloadedAll['styles'] as $sbuHandle) {
819
if (! in_array($sbuHandle, $manageStyles)) {
820
// Could be an old style that is not loaded anymore
@@ -847,7 +879,7 @@ class Main
847
/* These scripts below are used by this plugin (except admin-bar) and they should not show in the list
848
as they are loaded only when you (or other admin) manage the assets, never for your website visitors */
849
foreach ($manageScripts as $handle) {
850
- if (in_array($handle, $this->skipAssets['scripts']) || (! isset($scriptsList[$handle]))) {
851
continue;
852
}
853
@@ -863,7 +895,7 @@ class Main
863
}
864
865
// Append unloaded ones (if any)
866
- if (! empty($currentUnloadedAll['scripts']) && !empty($scriptsBeforeUnload)) {
867
foreach ($currentUnloadedAll['scripts'] as $sbuHandle) {
868
if (! in_array($sbuHandle, $manageScripts)) {
869
// Could be an old script that is not loaded anymore
@@ -993,7 +1025,7 @@ class Main
993
);
994
995
if (! file_exists($templateFile)) {
996
- return 'Template '.$name.' not found.';
997
}
998
999
ob_start();
@@ -1185,13 +1217,19 @@ class Main
1185
if (! empty($data['all']['styles'])) {
1186
$data['core_styles_loaded'] = false;
1187
1188
- foreach ($data['all']['styles'] as $key => $obj) {
1189
if (! isset($obj->handle)) {
1190
unset($data['all']['styles']['']);
1191
continue;
1192
}
1193
1194
- if (isset($obj->src, $data['all']['styles'][$key]) && $obj->src) {
1195
$part = str_replace(
1196
array(
1197
'http://',
@@ -1210,10 +1248,10 @@ class Main
1210
$data['core_styles_loaded'] = true;
1211
}
1212
1213
- // Determine source href
1214
- if (substr($obj->src, 0, 1) === '/' && substr($obj->src, 0, 2) !== '//') {
1215
$obj->srcHref = $siteUrl . $obj->src;
1216
- } else {
1217
$obj->srcHref = $obj->src;
1218
}
1219
}
@@ -1223,18 +1261,6 @@ class Main
1223
if (! empty($data['all']['scripts'])) {
1224
$data['core_scripts_loaded'] = false;
1225
1226
- $headPart = $bodyPart = '';
1227
-
1228
- if (isset($data['contents'])) {
1229
- // Extract 'HEAD' part
1230
- $headPart = Misc::extractBetween($data['contents'], '<head', '</head>');
1231
-
1232
- // Extract 'BODY' part
1233
- $contentsAltered = str_replace($headPart, '', $data['contents']);
1234
- $bodyDel = '<body'; // Get everything after $bodyDel
1235
- $bodyPart = substr($data['contents'], stripos($contentsAltered, $bodyDel) + strlen($bodyDel));
1236
- }
1237
-
1238
foreach ($data['all']['scripts'] as $key => $obj) {
1239
if (! isset($obj->handle)) {
1240
unset($data['all']['scripts']['']);
@@ -1244,20 +1270,9 @@ class Main
1244
// From WordPress directories (false by default)
1245
$data['all']['scripts'][$key]->wp = false;
1246
1247
- $toCheck = $obj->src;
1248
- $toCheckExtra = str_replace(
1249
- array(',', '&'),
1250
- array('%2C', '&#038;'),
1251
- $obj->src
1252
- );
1253
1254
- if (isset($data['contents'])) {
1255
- if (stripos($headPart, $toCheck) !== false || stripos($headPart, $toCheckExtra) !== false) {
1256
- $data['all']['scripts'][$key]->position = 'head';
1257
- } elseif (stripos($bodyPart, $toCheck) !== false || stripos($bodyPart, $toCheckExtra) !== false) {
1258
- $data['all']['scripts'][$key]->position = 'body';
1259
- }
1260
- } elseif (in_array($obj->handle, $this->assetsInFooter)) {
1261
$data['all']['scripts'][$key]->position = 'body';
1262
} else {
1263
$data['all']['scripts'][$key]->position = 'head';
@@ -1295,7 +1310,8 @@ class Main
1295
$data['all']['scripts'][$key]->wp = true;
1296
$data['core_scripts_loaded'] = true;
1297
}
1298
- }
1299
}
1300
}
1301
@@ -1313,7 +1329,7 @@ class Main
1313
{
1314
// Post Type (Overwrites 'front' - home page - if we are in a singular post)
1315
if ($postId == 0) {
1316
- $postId = $this->getCurrentPostId();
1317
}
1318
1319
$isInAdminPageViaAjax = (is_admin() && defined('DOING_AJAX') && DOING_AJAX);
@@ -1367,16 +1383,14 @@ class Main
1367
$this->vars['is_woo_shop_page'] = true;
1368
}
1369
} else {
1370
- if ($wooCommerceShopPageId > 0 && Misc::isHomePage()) {
1371
- if (strpos(get_site_url(), '://') !== false) {
1372
- list($siteUrlAfterProtocol) = explode('://', get_site_url());
1373
- $currentPageUrlAfterProtocol = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
1374
-
1375
- if ($siteUrlAfterProtocol != $currentPageUrlAfterProtocol && (strpos($siteUrlAfterProtocol,
1376
- '/shop') !== false)
1377
- ) {
1378
- $this->vars['woo_url_not_match'] = true;
1379
- }
1380
}
1381
}
1382
}
@@ -1387,7 +1401,7 @@ class Main
1387
}
1388
1389
// It has to be a single page (no "Posts page")
1390
- if (is_singular() && ($this->currentPostId < 1)) {
1391
global $post;
1392
$this->currentPostId = isset($post->ID) ? $post->ID : 0;
1393
}
75
*/
76
public $isFrontendEditView = false;
77
78
+ /**
79
+ * @var array
80
+ */
81
+ public $stylesInHead = array();
82
+
83
/**
84
* @var array
85
*/
86
+ public $assetsInFooter = array('styles' => array(), 'scripts' => array());
87
88
/**
89
* @var array
121
public $isAjaxCall = false;
122
123
/**
124
+ * Populated in the Parser constructor
125
+ *
126
* @var array
127
*/
128
public $skipAssets = array('styles' => array(), 'scripts' => array());
130
/**
131
* @var Main|null
132
*/
133
+ private static $singleton;
134
135
/**
136
* @return null|Main
149
*/
150
public function __construct()
151
{
152
+ $this->skipAssets['styles'] = array(
153
+ WPACU_PLUGIN_ID . '-style', // Asset CleanUp Styling (for admin use only)
154
+ 'admin-bar', // The top admin bar
155
+ 'yoast-seo-adminbar', // Yoast "WordPress SEO" plugin
156
+ 'autoptimize-toolbar',
157
+ 'query-monitor'
158
+ );
159
160
$this->skipAssets['scripts'] = array(
161
WPACU_PLUGIN_ID . '-script', // Asset CleanUp Script (for admin use only)
162
'admin-bar', // The top admin bar
163
'autoptimize-toolbar',
164
'query-monitor'
165
+ );
166
167
if (array_key_exists(WPACU_LOAD_ASSETS_REQ_KEY, $_REQUEST)) {
168
add_filter('w3tc_minify_enable', '__return_false');
182
183
// This is triggered AFTER "saveSettings" from 'Settings' class
184
// In case the settings were just updated, the script will get the latest values
185
+ add_action('init', array($this, 'triggersAfterInit'), 10);
186
187
// Front-end View - Unload the assets
188
// If there are reasons to prevent the unloading in case 'test mode' is enabled,
201
add_action( 'wp_print_footer_scripts', array( $this, 'filterScripts' ), 1 );
202
add_action( 'wp_print_footer_scripts', array( $this, 'filterStyles' ), 1 );
203
}
204
+
205
// This is recommended to keep active for Lite users as it helps spread the word about the plugin
206
// To remove the notice, consider upgrading to PRO
207
212
$this->wpacuUsageNotice();
213
}
214
215
+ add_action( 'admin_footer', array( $this, 'ajaxFetchActivePluginsJsFooterCode' ) );
216
add_action( 'wp_ajax_' . WPACU_PLUGIN_ID . '_fetch_active_plugins_icons',
217
array( $this, 'ajaxFetchActivePluginsIcons' ) );
218
222
/**
223
*
224
*/
225
+ public function triggersAfterInit()
226
+ {
227
+ $wpacuSettingsClass = new Settings();
228
+ $this->settings = $wpacuSettingsClass->getAll();
229
230
+ if ($this->settings['dashboard_show'] && $this->settings['dom_get_type']) {
231
+ self::$domGetType = $this->settings['dom_get_type'];
232
+ }
233
234
+ $calledFromDashboard = isset($_REQUEST[WPACU_LOAD_ASSETS_REQ_KEY]);
235
236
+ // Fetch the page in the background to see what scripts/styles are already loading
237
+ if ($calledFromDashboard || $this->settings['frontend_show']) {
238
+ if ($calledFromDashboard) {
239
+ Misc::noAdminBarLoad();
240
+ }
241
+
242
+ // Save CSS list that is printed in the <HEAD>
243
+ add_action('wp_head', array($this, 'saveFooterStyles'), PHP_INT_MAX - 1);
244
245
+ // Save CSS/JS list that is printed in the <BODY>
246
+ add_action('wp_footer', array($this, 'saveFooterScripts'), 100000000);
247
248
+ add_action('wp_footer', array($this, 'printScriptsStyles'), PHP_INT_MAX);
249
+ }
250
+
251
+ $metaboxes = new MetaBoxes;
252
+
253
+ if ( is_admin() ) {
254
+ // Do not load the meta box nor do any AJAX calls
255
+ // if the asset management is not enabled for the Dashboard
256
if ($this->settings['dashboard_show'] == 1) {
257
// Send an AJAX request to get the list of loaded scripts and styles and print it nicely
258
add_action(
264
}
265
266
// Side Meta Box: Asset CleanUp Options
267
+ $metaboxes->initCustomOptionsMetaBox();
268
+ }
269
270
+ if ($this->settings['disable_emojis'] == 1) {
271
+ add_action('init', array($this, 'doDisableEmojis'));
272
+ }
273
+ }
274
275
/**
276
* Priority: 8 (earliest)
277
*/
278
public function setVarsBeforeUpdate()
340
$globalUnload = $this->globalUnloaded;
341
342
// [wpacu_lite]
343
+ if ($nonAssetConfigPage && ! empty($globalUnload['scripts'])) {
344
$list = $globalUnload['scripts'];
345
} else { // [/wpacu_lite]
346
// Post, Page or Front-page?
463
$globalUnload = $this->globalUnloaded;
464
465
// [wpacu_lite]
466
+ if ($nonAssetConfigPage && ! empty($globalUnload['styles'])) {
467
$list = $globalUnload['styles'];
468
} else { // [/wpacu_lite]
469
// Post, Page, Front-page
515
}
516
}
517
518
+ global $wp_styles;
519
520
$allStyles = $wp_styles;
521
522
+ if ($allStyles !== null && ! empty($allStyles->registered)) {
523
$i = $this->lastStylePos;
524
525
foreach ($allStyles->registered as $handle => $value) {
660
return $existingList;
661
}
662
663
+ /**
664
+ *
665
+ */
666
+ public function saveFooterStyles()
667
+ {
668
+ if (isset($this->wpAllStyles['queue']) && ! empty($this->wpAllStyles['queue'])) {
669
+ $this->stylesInHead = $this->wpAllStyles['queue'];
670
+ }
671
+ }
672
+
673
/**
674
*
675
*/
676
+ public function saveFooterScripts()
677
{
678
global $wp_scripts;
679
+ $this->assetsInFooter['scripts'] = (isset($wp_scripts->in_footer) && ! empty($wp_scripts->in_footer)) ? $wp_scripts->in_footer : array();
680
681
+ $footerStyles = array();
682
+
683
+ if (isset($this->wpAllStyles['queue']) && ! empty($this->wpAllStyles['queue'])) {
684
+ foreach ( $this->wpAllStyles['queue'] as $handle ) {
685
+ if ( ! in_array( $handle, $this->stylesInHead ) ) {
686
+ $footerStyles[] = $handle;
687
+ }
688
+ }
689
+ }
690
+
691
+ $this->assetsInFooter['styles'] = $footerStyles;
692
+
693
+ }
694
695
/**
696
* This output will be extracted and the JSON will be processed
768
$manageScripts = $wp_scripts->done;
769
770
if ($isFrontEndEditView) {
771
+ if (! empty($this->wpAllStyles) && isset($this->wpAllStyles['queue'])) {
772
$manageStyles = $this->wpAllStyles['queue'];
773
}
774
775
+ if (! empty($this->wpAllScripts) && isset($this->wpAllScripts['queue'])) {
776
$manageScripts = $this->wpAllScripts['queue'];
777
}
778
825
if (! empty($stylesList)) {
826
/* These styles below are used by this plugin (except admin-bar) and they should not show in the list
827
as they are loaded only when you (or other admin) manage the assets, never for your website visitors */
828
+ if (is_admin_bar_showing() && is_admin()) {
829
+ $this->skipAssets['styles'][] = 'dashicons';
830
+ }
831
832
foreach ($manageStyles as $handle) {
833
+ if (! isset($stylesList[$handle]) || in_array($handle, $this->skipAssets['styles'])) {
834
continue;
835
}
836
846
}
847
848
// Append unloaded ones (if any)
849
+ if (!empty($stylesBeforeUnload) && ! empty($currentUnloadedAll['styles'])) {
850
foreach ($currentUnloadedAll['styles'] as $sbuHandle) {
851
if (! in_array($sbuHandle, $manageStyles)) {
852
// Could be an old style that is not loaded anymore
879
/* These scripts below are used by this plugin (except admin-bar) and they should not show in the list
880
as they are loaded only when you (or other admin) manage the assets, never for your website visitors */
881
foreach ($manageScripts as $handle) {
882
+ if (! isset($scriptsList[$handle]) || in_array($handle, $this->skipAssets['scripts'])) {
883
continue;
884
}
885
895
}
896
897
// Append unloaded ones (if any)
898
+ if (!empty($scriptsBeforeUnload) && ! empty($currentUnloadedAll['scripts'])) {
899
foreach ($currentUnloadedAll['scripts'] as $sbuHandle) {
900
if (! in_array($sbuHandle, $manageScripts)) {
901
// Could be an old script that is not loaded anymore
1025
);
1026
1027
if (! file_exists($templateFile)) {
1028
+ return 'Template '.$templateFile.' not found.';
1029
}
1030
1031
ob_start();
1217
if (! empty($data['all']['styles'])) {
1218
$data['core_styles_loaded'] = false;
1219
1220
+ foreach ($data['all']['styles'] as $key => $obj) {
1221
if (! isset($obj->handle)) {
1222
unset($data['all']['styles']['']);
1223
continue;
1224
}
1225
1226
+ if (in_array($obj->handle, $this->assetsInFooter['styles'])) {
1227
+ $data['all']['styles'][$key]->position = 'body';
1228
+ } else {
1229
+ $data['all']['styles'][$key]->position = 'head';
1230
+ }
1231
+
1232
+ if (isset($data['all']['styles'][$key], $obj->src) && $obj->src) {
1233
$part = str_replace(
1234
array(
1235
'http://',
1248
$data['core_styles_loaded'] = true;
1249
}
1250
1251
+ // Determine source href (starting with '/' but not starting with '//')
1252
+ if (strpos($obj->src, '/') === 0 && strpos($obj->src, '//') !== 0) {
1253
$obj->srcHref = $siteUrl . $obj->src;
1254
+ } else{
1255
$obj->srcHref = $obj->src;
1256
}
1257
}
1261
if (! empty($data['all']['scripts'])) {
1262
$data['core_scripts_loaded'] = false;
1263
1264
foreach ($data['all']['scripts'] as $key => $obj) {
1265
if (! isset($obj->handle)) {
1266
unset($data['all']['scripts']['']);
1270
// From WordPress directories (false by default)
1271
$data['all']['scripts'][$key]->wp = false;
1272
1273
+ $initialScriptPos = wp_cache_get($obj->handle, 'wpacu_scripts_initial_positions');
1274
1275
+ if ($initialScriptPos === 'body' || in_array($obj->handle, $this->assetsInFooter['scripts'])) {
1276
$data['all']['scripts'][$key]->position = 'body';
1277
} else {
1278
$data['all']['scripts'][$key]->position = 'head';
1310
$data['all']['scripts'][$key]->wp = true;
1311
$data['core_scripts_loaded'] = true;
1312
}
1313
+
1314
+ }
1315
}
1316
}
1317
1329
{
1330
// Post Type (Overwrites 'front' - home page - if we are in a singular post)
1331
if ($postId == 0) {
1332
+ $postId = (int)$this->getCurrentPostId();
1333
}
1334
1335
$isInAdminPageViaAjax = (is_admin() && defined('DOING_AJAX') && DOING_AJAX);
1383
$this->vars['is_woo_shop_page'] = true;
1384
}
1385
} else {
1386
+ if ($wooCommerceShopPageId > 0 && Misc::isHomePage() && strpos(get_site_url(), '://') !== false) {
1387
+ list($siteUrlAfterProtocol) = explode('://', get_site_url());
1388
+ $currentPageUrlAfterProtocol = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
1389
+
1390
+ if ($siteUrlAfterProtocol != $currentPageUrlAfterProtocol && (strpos($siteUrlAfterProtocol,
1391
+ '/shop') !== false)
1392
+ ) {
1393
+ $this->vars['woo_url_not_match'] = true;
1394
}
1395
}
1396
}
1401
}
1402
1403
// It has to be a single page (no "Posts page")
1404
+ if (($this->currentPostId < 1) && is_singular()) {
1405
global $post;
1406
$this->currentPostId = isset($post->ID) ? $post->ID : 0;
1407
}
classes/MetaBoxes.php CHANGED
@@ -53,23 +53,21 @@ class MetaBoxes
53
return;
54
}
55
56
- $postId = $post->ID;
57
58
$getAssets = true;
59
60
- if (get_post_status($postId) !== 'publish') {
61
$getAssets = false;
62
}
63
64
- if ($getAssets) {
65
- // Add an nonce field so we can check for it later.
66
- wp_nonce_field( WPACU_PLUGIN_ID . '_meta_box', WPACU_PLUGIN_ID . '_nonce');
67
- }
68
-
69
$data = array();
70
71
$data['get_assets'] = $getAssets;
72
- $data['fetch_url'] = Misc::getPageUrl($postId);
73
74
Main::instance()->parseTemplate('meta-box', $data, true);
75
}
53
return;
54
}
55
56
+ $postId = (isset($post->ID) && $post->ID > 0) ? $post->ID : 0;
57
58
$getAssets = true;
59
60
+ if ($postId < 1 || get_post_status($postId) !== 'publish') {
61
$getAssets = false;
62
}
63
64
$data = array();
65
66
$data['get_assets'] = $getAssets;
67
+
68
+ if ($getAssets) {
69
+ $data['fetch_url'] = Misc::getPageUrl( $postId );
70
+ }
71
72
Main::instance()->parseTemplate('meta-box', $data, true);
73
}
classes/OptimiseAssets/MinifyCss.php CHANGED
@@ -18,7 +18,8 @@ class MinifyCss
18
public function __construct()
19
{
20
add_action('wp_footer', function() {
21
- if ( is_admin() || // not for Dashboard view
22
(! Main::instance()->settings['minify_loaded_css']) || // Minify CSS has to be Enabled
23
(Main::instance()->settings['test_mode'] && ! Menu::userCanManageAssets()) ) { // Does not trigger if "Test Mode" is Enabled
24
return;
@@ -108,9 +109,10 @@ class MinifyCss
108
}
109
110
/**
111
- *
112
* @param $value
113
- */
114
public function maybeMinifyIt($value)
115
{
116
global $wp_version;
18
public function __construct()
19
{
20
add_action('wp_footer', function() {
21
+ if ( array_key_exists('wpacu_no_css_minify', $_GET) || // not on query string request (debugging purposes)
22
+ is_admin() || // not for Dashboard view
23
(! Main::instance()->settings['minify_loaded_css']) || // Minify CSS has to be Enabled
24
(Main::instance()->settings['test_mode'] && ! Menu::userCanManageAssets()) ) { // Does not trigger if "Test Mode" is Enabled
25
return;
109
}
110
111
/**
112
* @param $value
113
+ *
114
+ * @return array
115
+ */
116
public function maybeMinifyIt($value)
117
{
118
global $wp_version;
classes/OptimiseAssets/MinifyJs.php ADDED
@@ -0,0 +1,430 @@
1
+ <?php
2
+ namespace WpAssetCleanUp\OptimiseAssets;
3
+
4
+ use WpAssetCleanUp\Main;
5
+ use WpAssetCleanUp\Menu;
6
+ use WpAssetCleanUp\Misc;
7
+ use WpAssetCleanUp\MetaBoxes;
8
+
9
+ /**
10
+ * Class MinifyJs
11
+ * @package WpAssetCleanUp\OptimiseAssets
12
+ */
13
+ class MinifyJs
14
+ {
15
+ /**
16
+ * MinifyJs constructor.
17
+ */
18
+ public function __construct()
19
+ {
20
+ /*
21
+ * #minifying
22
+ * STEP 1: Prepare minify-able caching list
23
+ */
24
+ add_action('wp_print_footer_scripts', function() {
25
+ if ( array_key_exists('wpacu_no_js_minify', $_GET) || // not on query string request (debugging purposes)
26
+ is_admin() || // not for Dashboard view
27
+ (! Main::instance()->settings['minify_loaded_js']) || // Minify JS has to be Enabled
28
+ (Main::instance()->settings['test_mode'] && ! Menu::userCanManageAssets()) ) { // Does not trigger if "Test Mode" is Enabled
29
+ return;
30
+ }
31
+
32
+ if (defined('WPACU_CURRENT_PAGE_ID') && WPACU_CURRENT_PAGE_ID > 0 && is_singular()) {
33
+ // If "Do not minify JS on this page" is checked in "Asset CleanUp: Options" side meta box
34
+ $pageOptions = MetaBoxes::getPageOptions( WPACU_CURRENT_PAGE_ID );
35
+
36
+ if ( isset( $pageOptions['no_js_minify'] ) && $pageOptions['no_js_minify'] ) {
37
+ return;
38
+ }
39
+ }
40
+
41
+ global $wp_scripts;
42
+
43
+ $jsMinifyList = array();
44
+
45
+ $wpScriptsList = array_unique(array_merge($wp_scripts->done, $wp_scripts->queue));
46
+
47
+ // [Start] Collect for caching
48
+ foreach ($wpScriptsList as $handle) {
49
+ if (isset($wp_scripts->registered[$handle])) {
50
+ $value = $wp_scripts->registered[$handle];
51
+ $minifyValues = $this->maybeMinifyIt( $value );
52
+
53
+ if ( ! empty( $minifyValues ) ) {
54
+ $jsMinifyList[] = $minifyValues;
55
+ }
56
+ }
57
+ }
58
+
59
+ wp_cache_add('wpacu_js_minify_list', $jsMinifyList);
60
+ // [End] Collect for caching
61
+ }, PHP_INT_MAX);
62
+ }
63
+
64
+ /**
65
+ * @param $htmlSource
66
+ *
67
+ * @return mixed
68
+ */
69
+ public static function updateHtmlSourceOriginalToMinJs($htmlSource)
70
+ {
71
+ $jsMinifyList = wp_cache_get('wpacu_js_minify_list');
72
+
73
+ if (empty($jsMinifyList)) {
74
+ return $htmlSource;
75
+ }
76
+
77
+ $regExpPattern = '#<script[^>]*src(|\s+)=(|\s+)[^>]*(>)#Usmi';
78
+
79
+ preg_match_all($regExpPattern, OptimizeCommon::cleanerHtmlSource($htmlSource), $matchesSourcesFromTags, PREG_SET_ORDER);
80
+
81
+ foreach ($matchesSourcesFromTags as $matches) {
82
+ $scriptSourceTag = $matches[0];
83
+
84
+ if (strip_tags($scriptSourceTag) !== '') {
85
+ // Hmm? Not a valid tag... Skip it...
86
+ continue;
87
+ }
88
+
89
+ foreach ($jsMinifyList as $listValues) {
90
+ $sourceUrl = site_url() . $listValues[0];
91
+ $minUrl = site_url() . $listValues[1];
92
+
93
+ $newScriptSourceTag = str_ireplace($sourceUrl, $minUrl, $scriptSourceTag);
94
+
95
+ if ($scriptSourceTag !== $newScriptSourceTag) {
96
+ // Strip ?ver=
97
+ $toStrip = Misc::extractBetween($newScriptSourceTag, '?ver=', '>');
98
+
99
+ if (in_array(substr($toStrip, -1), array('"', "'"))) {
100
+ $toStrip = '?ver='. trim(trim($toStrip, '"'), "'");
101
+ $newScriptSourceTag = str_replace($toStrip, '', $newScriptSourceTag);
102
+ }
103
+
104
+ $htmlSource = str_replace($scriptSourceTag, $newScriptSourceTag, $htmlSource);
105
+ break;
106
+ }
107
+ }
108
+ }
109
+
110
+ return $htmlSource;
111
+ }
112
+
113
+ /**
114
+ * @param $value
115
+ *
116
+ * @return array
117
+ */
118
+ public function maybeMinifyIt($value)
119
+ {
120
+ global $wp_version;
121
+
122
+ $src = isset($value->src) ? $value->src : false;
123
+
124
+ if (! $src || $this->skipMinify($src)) {
125
+ return array();
126
+ }
127
+
128
+ $handleDbStr = md5($value->handle);
129
+
130
+ $transientName = 'wpacu_js_minify_'.$handleDbStr;
131
+
132
+ $savedValues = get_transient( $transientName );
133
+
134
+ if ( $savedValues ) {
135
+ $savedValuesArray = json_decode( $savedValues, ARRAY_A );
136
+
137
+ if ( $savedValuesArray['ver'] !== $value->ver ) {
138
+ // New File Version? Delete transient as it will be re-added to the database with the new version
139
+ delete_transient( $transientName );
140
+ } else {
141
+ $localPathToJsMin = str_replace( '//', '/', ABSPATH . $savedValuesArray['min_uri'] );
142
+
143
+ // Do not load any minified JS file (from the database transient cache) if it doesn't exist
144
+ // It will fallback to the original JS file
145
+ if ( isset( $savedValuesArray['source_uri'] ) && file_exists( $localPathToJsMin ) ) {
146
+ return array(
147
+ $savedValuesArray['source_uri'],
148
+ $savedValuesArray['min_uri'],
149
+ );
150
+ }
151
+ }
152
+ }
153
+
154
+ if (strpos($src, '/wp-includes/') === 0) {
155
+ $src = site_url() . $src;
156
+ }
157
+
158
+ $localAssetPath = OptimizeCommon::getLocalAssetPath($src, 'js');
159
+
160
+ if (! file_exists($localAssetPath)) {
161
+ return array();
162
+ }
163
+
164
+ $assetHref = $value->src;
165
+
166
+ $posLastSlash = strrpos($assetHref, '/');
167
+ $pathToAssetDir = substr($assetHref, 0, $posLastSlash);
168
+
169
+ $parseUrl = parse_url($pathToAssetDir);
170
+
171
+ if (isset($parseUrl['scheme']) && $parseUrl['scheme'] !== '') {
172
+ $pathToAssetDir = str_replace(
173
+ array('http://'.$parseUrl['host'], 'https://'.$parseUrl['host']),
174
+ '',
175
+ $pathToAssetDir
176
+ );
177
+ } elseif (strpos($pathToAssetDir, '//') === 0) {
178
+ $pathToAssetDir = str_replace(
179
+ array('//'.$parseUrl['host'], '//'.$parseUrl['host']),
180
+ '',
181
+ $pathToAssetDir
182
+ );
183
+ }
184
+
185
+ $jsContent = @file_get_contents($localAssetPath);
186
+ $jsContent = OptimizeJs::maybeDoJsFixes($jsContent, $pathToAssetDir . '/'); // Minify it and save it to /wp-content/cache/js/min/
187
+
188
+ $jsContent = self::applyMinification($jsContent);
189
+
190
+ // Relative path to the new file
191
+ $ver = (isset($value->ver) && $value->ver) ? $value->ver : $wp_version;
192
+
193
+ $newFilePathUri = OptimizeJs::$relPathJsCacheDir . 'min/' . $value->handle . '-v' . $ver . '.js';
194
+
195
+ $newLocalPath = WP_CONTENT_DIR . $newFilePathUri; // Ful Local path
196
+ $newLocalPathUrl = WP_CONTENT_URL . $newFilePathUri; // Full URL path
197
+
198
+ $saveFile = @file_put_contents($newLocalPath, $jsContent);
199
+
200
+ if (! $saveFile || ! $jsContent) {
201
+ // Fallback to the original JS if the minified version can't be created or updated
202
+ return array();
203
+ }
204
+
205
+ $saveValues = array(
206
+ 'source_uri' => OptimizeCommon::getHrefRelPath($value->src),
207
+ 'min_uri' => OptimizeCommon::getHrefRelPath($newLocalPathUrl),
208
+ 'ver' => $ver
209
+ );
210
+
211
+ // Add / Re-add (with new version) transient
212
+ set_transient($transientName, json_encode($saveValues));
213
+
214
+ return array(
215
+ OptimizeCommon::getHrefRelPath($value->src),
216
+ OptimizeCommon::getHrefRelPath($newLocalPathUrl),
217
+ );
218
+ }
219
+
220
+ /**
221
+ * @param $jsContent
222
+ *
223
+ * @return string|string[]|null
224
+ */
225
+ public static function applyMinification($jsContent)
226
+ {
227
+ $jsContent = preg_replace(array("/\s+\n/", "/\n\s+/", '/ +/'), array("\n", "\n ", ' '), $jsContent);
228
+
229
+ // Going line by line
230
+ $jsContentsLines = explode( "\n", $jsContent );
231
+
232
+ $jsContent = '';
233
+
234
+ foreach ( $jsContentsLines as $jsLineIndex => $jsContentLine ) {
235
+ $jsContentLine = trim( $jsContentLine );
236
+
237
+ if (strpos(trim($jsContentLine), '//') === 0) {
238
+ continue;
239
+ }
240
+
241
+ $appendNewLine = true;
242
+ $mergeDelimiter = '';
243
+
244
+ if (strpos($jsContentLine, '//') !== false) {
245
+ $appendNewLine = true;
246
+ }
247
+
248
+ // When to keep the new line
249
+ elseif ( strpos( $jsContentLine, '/*' ) !== false
250
+ || strpos( $jsContentLine, '*/' ) !== false
251
+ || strpos( $jsContentLine, '*' ) === 0
252
+ || in_array(substr( trim( $jsContentLine ),
253
+ - 1 ), array('}', ')')) // Later, consider a solution to skip this from having a new line added
254
+ ) {
255
+ $appendNewLine = true;
256
+ } else {
257
+ $mergeDelimiter = in_array(
258
+ substr( trim( $jsContentLine ), - 1 ),
259
+ array( '{', '}', ';', ',' )
260
+ ) ? '' : ' ';
261
+ }
262
+
263
+ $jsContent .= self::basicReplacementOnLine($jsContentLine) . ($appendNewLine ? "\n" : $mergeDelimiter);
264
+ }
265
+
266
+ //return $jsContent;
267
+
268
+ /*
269
+ * Step 1: Make sure content between quotes (could be message alerts, plain text) is not replaced
270
+ * It will be replaced later on
271
+ */
272
+ preg_match_all("/(\"(.*?)\")|('(.*?)')/", $jsContent,$matchesBetweenQuotes);
273
+
274
+ $wpacuSpaceDel = '@[wpacu-plugin-space-del]@';
275
+
276
+ if (isset($matchesBetweenQuotes[0]) && ! empty($matchesBetweenQuotes[0])) {
277
+ foreach ($matchesBetweenQuotes[0] as $matchBetweenQuotes) {
278
+ if (strpos($matchBetweenQuotes, ' ') !== false) {
279
+ $newMatch = str_replace( ' ', $wpacuSpaceDel, $matchBetweenQuotes );
280
+ $jsContent = str_replace( $matchBetweenQuotes, $newMatch, $jsContent );
281
+ }
282
+ }
283
+ }
284
+
285
+ // Source: https://github.com/Letractively/samstyle-php-framework/blob/master/sp.php
286
+ $regex = array(
287
+ "`^([\t\s]+)`ism" => '',
288
+ "`^\/\*(.+?)\*\/`ism" => '',
289
+ "`([\n\A;]+)\/\*(.+?)\*\/`ism" => '$1',
290
+ "`([\n\A;\s]+)//(.+?)[\n\r]`ism" => "$1\n",
291
+ "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism" => "\n",
292
+
293
+ "/}\);\n}\)/" => '});})',
294
+
295
+ "/([{|;|,]+)(\n+)('|\"|var|if|else|for|this|return|ready|jQuery|\\$|})/i" => '$1 $3',
296
+
297
+ '/} else {/i' => '}else{',
298
+ '/if \(/i' => 'if('
299
+ );
300
+
301
+ $jsContent = preg_replace(array_keys($regex), array_values($regex), $jsContent);
302
+
303
+ $newReps = array(
304
+ ";\n" => ';',
305
+ //",\n" => ',',
306
+ "}\n}" => '}}'
307
+ );
308
+
309
+ $jsContent = str_replace(array_keys($newReps), array_values($newReps), $jsContent);
310
+
311
+ /*
312
+ * Step: Make sure content between quotes (could be message alerts, plain text) is not replaced
313
+ * Restore the spacing between quotes
314
+ */
315
+ $jsContent = str_replace($wpacuSpaceDel, ' ', $jsContent);
316
+
317
+ // Remove whitespaces before and after the content
318
+ return trim($jsContent);
319
+ }
320
+
321
+ /**
322
+ * @param $jsContentLine
323
+ *
324
+ * @return mixed
325
+ */
326
+ public static function basicReplacementOnLine($jsContentLine)
327
+ {
328
+ // Regular Expression in the line? Don't make any changes
329
+ if ( strpos($jsContentLine, 'RegExp') !== false
330
+ || preg_match('/\=\s\//', $jsContentLine)) {
331
+ return $jsContentLine;
332
+ }
333
+
334
+ $repsOne = array(
335
+ // Remove space before & after colons
336
+ ' :' => ':',
337
+ ': ' => ':',
338
+
339
+ // Remove space before & after equal signs
340
+ ' =' => '=',
341
+ '= ' => '=',
342
+
343
+ "' ? '" => "'?'",
344
+ ') {' => '){',
345
+ ') !' => ')!'
346
+ );
347
+ $jsContentLine = str_replace(array_keys($repsOne), array_values($repsOne), $jsContentLine);
348
+
349
+ $repsTwo = array(
350
+ "{ '" => "{'",
351
+ "' }" => "'}",
352
+ ", '" => ",'",
353
+ ' || ' => '||',
354
+
355
+ '=true;' => '=!0;',
356
+ ':true;' => ':!0;',
357
+ '(true)' => '(!0)',
358
+ '(true,' => '(!0,',
359
+ 'return true;' => 'return !0;',
360
+ 'return true}' => 'return !0}',
361
+
362
+ '=false;' => '=!1;',
363
+ ':false;' => ':!1;',
364
+ '(false)' => '(!1)',
365
+ '(false,' => '(!1,',
366
+ 'return false;' => 'return !1;',
367
+ 'return false}' => 'return !1}',
368
+
369
+ );
370
+
371
+ $jsContentLine = str_ireplace(array_keys($repsTwo), array_values($repsTwo), $jsContentLine);
372
+
373
+ $repsThree = array(
374
+ '; ' => ';',
375
+ '{ ' => '{',
376
+ '} ' => '}',
377
+ '( ' => '(',
378
+ ' (' => '(',
379
+ ' )' => ')',
380
+ ') ' => ')',
381
+ ', ' => ',',
382
+ ' + ' => '+'
383
+ );
384
+
385
+ $jsContentLine = str_ireplace(array_keys($repsThree), array_values($repsThree), $jsContentLine);
386
+
387
+ return $jsContentLine;
388
+ }
389
+
390
+ /**
391
+ * @param $src
392
+ *
393
+ * @return bool
394
+ */
395
+ public function skipMinify($src)
396
+ {
397
+ $regExps = array(
398
+ '#/wp-content/plugins/wp-asset-clean-up(.*?).min.js#',
399
+
400
+ // Other libraries from the core that end in .min.js
401
+ '#/wp-includes/(.*?).min.js#',
402
+
403
+ // jQuery library
404
+ '#/wp-includes/js/jquery/jquery.js#'
405
+
406
+ );
407
+
408
+ if (Main::instance()->settings['minify_loaded_js_exceptions'] !== '') {
409
+ $loadedJsExceptionsPatterns = trim(Main::instance()->settings['minify_loaded_js_exceptions']);
410
+
411
+ if (strpos($loadedJsExceptionsPatterns, "\n")) {
412
+ // Multiple values (one per line)
413
+ foreach ($loadedJsExceptionsPatterns as $loadedJsExceptionPattern) {
414
+ $regExps[] = '#'.$loadedJsExceptionPattern.'#';
415
+ }
416
+ } else {
417
+ // Only one value?
418
+ $regExps[] = '#'.$loadedJsExceptionsPatterns.'#';
419
+ }
420
+ }
421
+
422
+ foreach ($regExps as $regExp) {
423
+ if ( preg_match( $regExp, $src ) ) {
424
+ return true;
425
+ }
426
+ }
427
+
428
+ return false;
429
+ }
430
+ }
classes/OptimiseAssets/OptimizeCommon.php CHANGED
@@ -139,17 +139,29 @@ class OptimizeCommon
139
*/
140
public static function getLocalAssetPath($href, $assetType)
141
{
142
/*
143
* Validate it first
144
*/
145
- // Asset's Host
146
$assetHost = strtolower(parse_url($href, PHP_URL_HOST));
147
148
- // First check the host name
149
- $siteDbUrl = get_option('siteurl');
150
- $siteUrlHost = strtolower(parse_url($siteDbUrl, PHP_URL_HOST));
151
152
- // Different host name (most likely 3rd party one such as fonts.googleapis.com or a CDN)
153
// Do not add it to the combine list
154
if ($assetHost !== $siteUrlHost) {
155
return false;
@@ -226,7 +238,7 @@ class OptimizeCommon
226
227
$requestUriPart = $requestUri;
228
229
- if ($requestUri === '/') {
230
$requestUriPart = '';
231
}
232
@@ -242,11 +254,13 @@ class OptimizeCommon
242
return array();
243
}
244
245
- //if ($assetType === 'css') {
246
$cachedAssetsFileExpiresIn = OptimizeCss::$cachedCssAssetsFileExpiresIn;
247
- //} else {
248
- // return array();
249
- //}
250
251
// Delete cached file after it expired as it will be regenerated
252
if (filemtime($assetsFile) < (time() - 1 * $cachedAssetsFileExpiresIn)) {
@@ -259,10 +273,14 @@ class OptimizeCommon
259
if ($optionValue) {
260
$optionValueArray = @json_decode($optionValue, ARRAY_A);
261
262
- if (! empty( $optionValueArray) && (isset($optionValueArray['head']['link_hrefs']) || isset($optionValueArray['body']['link_hrefs']))) {
263
- return $optionValueArray;
264
- }
265
- }
266
267
// File exists, but it's invalid or outdated; Delete it as it has to be re-generated
268
self::clearAssetCachedData($jsonStorageFile);
@@ -285,7 +303,7 @@ class OptimizeCommon
285
286
$requestUriPart = $requestUri;
287
288
- if ($requestUri === '/') {
289
$requestUriPart = '';
290
}
291
@@ -301,7 +319,7 @@ class OptimizeCommon
301
302
$assetsFile = $dirToFilename . self::filterStorageFileName($jsonStorageFile);
303
304
- // CSS JSON FILE DATA
305
$assetsValue = $list;
306
307
@file_put_contents($assetsFile, $assetsValue);
@@ -321,7 +339,7 @@ class OptimizeCommon
321
322
$requestUriPart = $requestUri;
323
324
- if ($requestUri === '/') {
325
$requestUriPart = '';
326
}
327
@@ -355,9 +373,10 @@ class OptimizeCommon
355
*/
356
$fileExtToRemove = array('.json');
357
358
- // Also delete .css
359
if (! $keepAssetFiles) {
360
$fileExtToRemove[] = '.css';
361
}
362
363
$assetCleanUpCacheDir = WP_CONTENT_DIR . self::$relPathPluginCacheDir;
@@ -373,10 +392,8 @@ class OptimizeCommon
373
374
if ( is_file( $item ) && in_array( strrchr( $fileBaseName, '.' ), $fileExtToRemove ) ) {
375
@unlink( $item );
376
- } else {
377
- if ( strpos( $item, $storageDir ) !== false && $item != $storageDir ) {
378
- $storageEmptyDirs[] = $item;
379
- }
380
}
381
}
382
@@ -425,17 +442,23 @@ class OptimizeCommon
425
*/
426
public static function filterStorageFileName($fileName)
427
{
428
$current_user = wp_get_current_user();
429
430
if (isset($current_user->ID) && $current_user->ID > 0) {
431
$fileName = str_replace(
432
'{maybe-extra-info}',
433
- '-logged-in-'.$current_user->ID,
434
$fileName
435
);
436
} else {
437
// Just clear {maybe-extra-info}
438
- $fileName = str_replace('{maybe-extra-info}', '', $fileName);
439
}
440
441
return $fileName;
139
*/
140
public static function getLocalAssetPath($href, $assetType)
141
{
142
+ // Check the host name
143
+ $siteDbUrl = get_option('siteurl');
144
+ $siteUrlHost = strtolower(parse_url($siteDbUrl, PHP_URL_HOST));
145
+
146
+ if (strpos($href, '//') === 0) {
147
+ list ($urlPrefix) = explode('//', $siteDbUrl);
148
+ $href = $urlPrefix . $href;
149
+ }
150
+
151
+ $externalHostsList = array(
152
+ 'fonts.googleapis.com'
153
+ );
154
+
155
/*
156
* Validate it first
157
*/
158
$assetHost = strtolower(parse_url($href, PHP_URL_HOST));
159
160
+ if (in_array($assetHost, $externalHostsList)) {
161
+ return false;
162
+ }
163
164
+ // Different host name (most likely 3rd party one such as fonts.googleapis.com or an external CDN)
165
// Do not add it to the combine list
166
if ($assetHost !== $siteUrlHost) {
167
return false;
238
239
$requestUriPart = $requestUri;
240
241
+ if ($requestUri === '/' || is_404()) {
242
$requestUriPart = '';
243
}
244
254
return array();
255
}
256
257
+ if ($assetType === 'css') {
258
$cachedAssetsFileExpiresIn = OptimizeCss::$cachedCssAssetsFileExpiresIn;
259
+ } elseif ($assetType === 'js') {
260
+ $cachedAssetsFileExpiresIn = OptimizeJs::$cachedJsAssetsFileExpiresIn;
261
+ } else {
262
+ return array();
263
+ }
264
265
// Delete cached file after it expired as it will be regenerated
266
if (filemtime($assetsFile) < (time() - 1 * $cachedAssetsFileExpiresIn)) {
273
if ($optionValue) {
274
$optionValueArray = @json_decode($optionValue, ARRAY_A);
275
276
+ if ($assetType === 'css' && ! empty( $optionValueArray) && (isset($optionValueArray['head']['link_hrefs']) || isset($optionValueArray['body']['link_hrefs']))) {
277
+ return $optionValueArray;
278
+ }
279
+
280
+ if ($assetType === 'js' && ! empty($optionValueArray)) {
281
+ return $optionValueArray;
282
+ }
283
+ }
284
285
// File exists, but it's invalid or outdated; Delete it as it has to be re-generated
286
self::clearAssetCachedData($jsonStorageFile);
303
304
$requestUriPart = $requestUri;
305
306
+ if ($requestUri === '/' || is_404()) {
307
$requestUriPart = '';
308
}
309
319
320
$assetsFile = $dirToFilename . self::filterStorageFileName($jsonStorageFile);
321
322
+ // CSS/JS JSON FILE DATA
323
$assetsValue = $list;
324
325
@file_put_contents($assetsFile, $assetsValue);
339
340
$requestUriPart = $requestUri;
341
342
+ if ($requestUri === '/' || is_404()) {
343
$requestUriPart = '';
344
}
345
373
*/
374
$fileExtToRemove = array('.json');
375
376
+ // Also delete .css & .js
377
if (! $keepAssetFiles) {
378
$fileExtToRemove[] = '.css';
379
+ $fileExtToRemove[] = '.js';
380
}
381
382
$assetCleanUpCacheDir = WP_CONTENT_DIR . self::$relPathPluginCacheDir;
392
393
if ( is_file( $item ) && in_array( strrchr( $fileBaseName, '.' ), $fileExtToRemove ) ) {
394
@unlink( $item );
395
+ } elseif ( strpos( $item, $storageDir ) !== false && $item != $storageDir ) {
396
+ $storageEmptyDirs[] = $item;
397
}
398
}
399
442
*/
443
public static function filterStorageFileName($fileName)
444
{
445
+ $filterString = '';
446
+
447
+ if (is_404()) {
448
+ $filterString = '-404-not-found';
449
+ }
450
+
451
$current_user = wp_get_current_user();
452
453
if (isset($current_user->ID) && $current_user->ID > 0) {
454
$fileName = str_replace(
455
'{maybe-extra-info}',
456
+ $filterString.'-logged-in-'.$current_user->ID,
457
$fileName
458
);
459
} else {
460
// Just clear {maybe-extra-info}
461
+ $fileName = str_replace('{maybe-extra-info}', $filterString, $fileName);
462
}
463
464
return $fileName;
classes/OptimiseAssets/OptimizeCss.php CHANGED
@@ -32,9 +32,13 @@ class OptimizeCss
32
public function init()
33
{
34
add_action('wp_loaded', function() {
35
ob_start(function ($htmlSource) {
36
// Do not do any optimization if "Test Mode" is Enabled
37
- if (Main::instance()->settings['test_mode'] && ! Menu::userCanManageAssets()) {
38
return $htmlSource;
39
}
40
@@ -86,8 +90,8 @@ class OptimizeCss
86
OptimizeCommon::clearAssetCachedData( $this->jsonStorageFile );
87
88
// Fetch the DOM, and then set a new transient
89
- $document = new \DOMDocument();
90
- $document->loadHTML( $htmlSource );
91
libxml_use_internal_errors( true );
92
93
$storageJsonContents = array();
@@ -95,7 +99,7 @@ class OptimizeCss
95
foreach ( array( 'head', 'body' ) as $docLocationTag ) {
96
$combinedUriPaths = $hrefUriNotCombinableList = $localAssetsPaths = $linkHrefs = array();
97
98
- $docLocationElements = $document->getElementsByTagName( $docLocationTag )->item( 0 );
99
$linkTags = $docLocationElements->getElementsByTagName( 'link' );
100
101
if ( $linkTags === null ) {
@@ -194,7 +198,7 @@ class OptimizeCss
194
}
195
196
$maybeDoCssCombine = $this->maybeDoCssCombine( sha1( implode( '', $combinedUriPaths ) ),
197
- $localAssetsPaths );
198
199
// Local path to combined CSS file
200
$localFinalCssFile = $maybeDoCssCombine['local_final_css_file'];
@@ -202,6 +206,9 @@ class OptimizeCss
202
// URI (e.g. /wp-content/cache/asset-cleanup/[file-name-here.css]) to the combined CSS file
203
$uriToFinalCssFile = $maybeDoCssCombine['uri_final_css_file'];
204
205
if ( file_exists( $localFinalCssFile ) ) {
206
$storageJsonContents[$docLocationTag] = array(
207
'uri_to_final_css_file' => $uriToFinalCssFile,
@@ -286,10 +293,11 @@ HTML;
286
/**
287
* @param $shaOneCombinedUriPaths
288
* @param $localAssetsPaths
289
*
290
* @return array
291
*/
292
- public function maybeDoCssCombine($shaOneCombinedUriPaths, $localAssetsPaths)
293
{
294
$current_user = wp_get_current_user();
295
$dirToUserCachedFile = ((isset($current_user->ID) && $current_user->ID > 0) ? 'logged-in/'.$current_user->ID.'/' : '');
@@ -303,7 +311,9 @@ HTML;
303
// If "?ver" value changes on any of the assets or the asset list changes in any way
304
// then $shaOneCombinedUriPaths will change too and a new CSS file will be generated and loaded
305
306
- if (! file_exists($localFinalCssFile)) {
307
// Change $assetsContents as paths to fonts and images that are relative (e.g. ../, ../../) have to be updated
308
$finalAssetsContents = '';
309
@@ -330,6 +340,14 @@ HTML;
330
$assetContent = @file_get_contents($localAssetsPath);
331
332
if ($assetContent) {
333
$finalAssetsContents .= self::maybeFixCssBackgroundUrls($assetContent, $pathToAssetDir . '/') . "\n\n";
334
}
335
}
@@ -348,7 +366,8 @@ HTML;
348
349
return array(
350
'uri_final_css_file' => $uriToFinalCssFile,
351
- 'local_final_css_file' => $localFinalCssFile
352
);
353
}
354
32
public function init()
33
{
34
add_action('wp_loaded', function() {
35
+ if (is_admin()) { // don't apply any changes if not in the front-end view (e.g. Dashboard view)
36
+ return;
37
+ }
38
+
39
ob_start(function ($htmlSource) {
40
// Do not do any optimization if "Test Mode" is Enabled
41
+ if (! Menu::userCanManageAssets() && Main::instance()->settings['test_mode']) {
42
return $htmlSource;
43
}
44
90
OptimizeCommon::clearAssetCachedData( $this->jsonStorageFile );
91
92
// Fetch the DOM, and then set a new transient
93
+ $documentForCSS = new \DOMDocument();
94
+ $documentForCSS->loadHTML( $htmlSource );
95
libxml_use_internal_errors( true );
96
97
$storageJsonContents = array();
99
foreach ( array( 'head', 'body' ) as $docLocationTag ) {
100
$combinedUriPaths = $hrefUriNotCombinableList = $localAssetsPaths = $linkHrefs = array();
101
102
+ $docLocationElements = $documentForCSS->getElementsByTagName( $docLocationTag )->item( 0 );
103
$linkTags = $docLocationElements->getElementsByTagName( 'link' );
104
105
if ( $linkTags === null ) {
198
}
199
200
$maybeDoCssCombine = $this->maybeDoCssCombine( sha1( implode( '', $combinedUriPaths ) ),
201
+ $localAssetsPaths, $linkHrefs );
202
203
// Local path to combined CSS file
204
$localFinalCssFile = $maybeDoCssCombine['local_final_css_file'];
206
// URI (e.g. /wp-content/cache/asset-cleanup/[file-name-here.css]) to the combined CSS file
207
$uriToFinalCssFile = $maybeDoCssCombine['uri_final_css_file'];
208
209
+ // Any link hrefs removed perhaps if the file wasn't combined?
210
+ $linkHrefs = $maybeDoCssCombine['link_hrefs'];
211
+
212
if ( file_exists( $localFinalCssFile ) ) {
213
$storageJsonContents[$docLocationTag] = array(
214
'uri_to_final_css_file' => $uriToFinalCssFile,
293
/**
294
* @param $shaOneCombinedUriPaths
295
* @param $localAssetsPaths
296
+ * @param $linkHrefs
297
*
298
* @return array
299
*/
300
+ public function maybeDoCssCombine($shaOneCombinedUriPaths, $localAssetsPaths, $linkHrefs)
301
{
302
$current_user = wp_get_current_user();
303
$dirToUserCachedFile = ((isset($current_user->ID) && $current_user->ID > 0) ? 'logged-in/'.$current_user->ID.'/' : '');
311
// If "?ver" value changes on any of the assets or the asset list changes in any way
312
// then $shaOneCombinedUriPaths will change too and a new CSS file will be generated and loaded
313
314
+ $skipIfFileExists = true;
315
+
316
+ if ($skipIfFileExists || ! file_exists($localFinalCssFile)) {
317
// Change $assetsContents as paths to fonts and images that are relative (e.g. ../, ../../) have to be updated
318
$finalAssetsContents = '';
319
340
$assetContent = @file_get_contents($localAssetsPath);
341
342
if ($assetContent) {
343
+ // Do not combine it if it contains "@import"
344
+ if (stripos($assetContent, '@import') !== false) {
345
+ unset($localAssetsPaths[$assetHref]);
346
+ $linkHrefKey = array_search($assetHref, $linkHrefs);
347
+ unset($linkHrefs[$linkHrefKey]);
348
+ continue;
349
+ }
350
+
351
$finalAssetsContents .= self::maybeFixCssBackgroundUrls($assetContent, $pathToAssetDir . '/') . "\n\n";
352
}
353
}
366
367
return array(
368
'uri_final_css_file' => $uriToFinalCssFile,
369
+ 'local_final_css_file' => $localFinalCssFile,
370
+ 'link_hrefs' => $linkHrefs
371
);
372
}
373
classes/OptimiseAssets/OptimizeJs.php ADDED
@@ -0,0 +1,605 @@
1
+ <?php
2
+ namespace WpAssetCleanUp\OptimiseAssets;
3
+
4
+ use WpAssetCleanUp\Main;
5
+ use WpAssetCleanUp\Menu;
6
+ use WpAssetCleanUp\MetaBoxes;
7
+ use WpAssetCleanUp\Misc;
8
+
9
+ /**
10
+ * Class CombineJs
11
+ * @package WpAssetCleanUp
12
+ */
13
+ class OptimizeJs
14
+ {
15
+ /**
16
+ * @var string
17
+ */
18
+ public static $relPathJsCacheDir = '/cache/asset-cleanup/js/'; // keep trailing slash at the end
19
+
20
+ /**
21
+ * @var float|int
22
+ */
23
+ public static $cachedJsAssetsFileExpiresIn = 28800; // 8 hours in seconds (60 * 60 * 8)
24
+
25
+ /**
26
+ * @var string
27
+ */
28
+ public $jsonStorageFile = 'js-combined{maybe-extra-info}.json';
29
+
30
+ /**
31
+ *
32
+ */
33
+ public function init()
34
+ {
35
+ add_action('wp_loaded', function() {
36
+ if (is_admin()) { // don't apply any changes if not in the front-end view (e.g. Dashboard view)
37
+ return;
38
+ }
39
+
40
+ ob_start(function($htmlSource) {
41
+ // Do not do any optimization if "Test Mode" is Enabled
42
+ if (! Menu::userCanManageAssets() && Main::instance()->settings['test_mode']) {
43
+ return $htmlSource;
44
+ }
45
+
46
+ // There has to be at least one "<script", otherwise, it could be a feed request or something similar (not page, post, homepage etc.)
47
+ if (stripos($htmlSource, '<script') === false) {
48
+ return $htmlSource;
49
+ }
50
+
51
+ /*
52
+ * #minifying
53
+ * STEP 2: Load minify-able caching list and replace the original source URLs with the new cached ones
54
+ */
55
+ if (Main::instance()->settings['minify_loaded_js']) {
56
+ // 'wpacu_js_minify_list' caching list is also checked; if it's empty, no minification is made
57
+ $htmlSource = MinifyJs::updateHtmlSourceOriginalToMinJs( $htmlSource );
58
+ }
59
+
60
+ if ( array_key_exists('wpacu_no_js_combine', $_GET) || // not on query string request (debugging purposes)
61
+ ! $this->doJsCombine() ) {
62
+ return $htmlSource;
63
+ }
64
+
65
+ // If "Do not combine CSS on this page" is checked in "Asset CleanUp Options" side meta box
66
+ // Works for posts, pages and custom post types
67
+ if (defined('WPACU_CURRENT_PAGE_ID') && WPACU_CURRENT_PAGE_ID > 0) {
68
+ $pageOptions = MetaBoxes::getPageOptions( WPACU_CURRENT_PAGE_ID );
69
+
70
+ if ( isset( $pageOptions['no_js_optimize'] ) && $pageOptions['no_js_optimize'] ) {
71
+ return $htmlSource;
72
+ }
73
+ }
74
+
75
+ $useDom = function_exists('libxml_use_internal_errors') && function_exists('libxml_clear_errors') && class_exists('DOMDocument');
76
+
77
+ if (! $useDom) {
78
+ return $htmlSource;
79
+ }
80
+
81
+ $combineLevel = 2;
82
+
83
+ // Speed up processing by getting the already existing final CSS file URI
84
+ // This will avoid parsing the HTML DOM and determine the combined URI paths for all the CSS files
85
+ $finalCacheList = OptimizeCommon::getAssetCachedData($this->jsonStorageFile, self::$relPathJsCacheDir, 'js');
86
+
87
+ // $uriToFinalJsFile will always be relative ONLY within WP_CONTENT_DIR . self::$relPathJsCacheDir
88
+ // which is usually "wp-content/cache/asset-cleanup/js/"
89
+
90
+ // "false" would make it avoid checking the cache and always use the DOM Parser / RegExp
91
+ // for DEV purposes ONLY as it uses more resources
92
+ if (empty($finalCacheList)) {
93
+ /*
94
+ * NO CACHING TRANSIENT; Parse the DOM
95
+ */
96
+ // Nothing in the database records or the retrieved cached file does not exist?
97
+ OptimizeCommon::clearAssetCachedData($this->jsonStorageFile);
98
+
99
+ $regExpPattern = '#<script[^>]*>.*?</script>#is';
100
+
101
+ preg_match_all($regExpPattern, OptimizeCommon::cleanerHtmlSource($htmlSource), $matchesSourcesFromTags, PREG_SET_ORDER);
102
+
103
+ // No <script> tag found? Do not continue
104
+ if (empty($matchesSourcesFromTags)) {
105
+ return $htmlSource;
106
+ }
107
+
108
+ if ($combineLevel === 2) {
109
+ $matchesSourcesFromTags = $this->clearInlineScriptTags($matchesSourcesFromTags);
110
+ }
111
+
112
+ if (empty($matchesSourcesFromTags)) {
113
+ return $htmlSource;
114
+ }
115
+
116
+ $combinableList = $bodyGroupIndexes = array();
117
+
118
+ $groupIndex = 1;
119
+ $jQueryAndMigrateGroup = 0;
120
+
121
+ $jQueryGroupIndex = $loadsLocaljQuery = $loadsLocaljQueryMigrate = false;
122
+
123
+ $lastScriptSrcFromHead = $this->lastScriptSrcFromHead($htmlSource);
124
+
125
+ $reachedBody = false;
126
+
127
+ // Only keep combinable JS files
128
+ foreach ($matchesSourcesFromTags as $matchSourceFromTag) {
129
+ $matchedSourceFromTag = trim( $matchSourceFromTag[0] );
130
+
131
+ $domTag = new \DOMDocument();
132
+ $domTag->loadHTML($matchedSourceFromTag);
133
+
134
+ $hasSrc = $src = false;
135
+
136
+ foreach ($domTag->getElementsByTagName( 'script' ) as $tagObject) {
137
+ if (! $tagObject->hasAttributes()) {
138
+ continue;
139
+ }
140
+
141
+ foreach ( $tagObject->attributes as $attrObj ) {
142
+ if ($attrObj->nodeName === 'src' && $attrObj->nodeValue) {
143
+ $hasSrc = true;
144
+ $src = (string) $attrObj->nodeValue;
145
+
146
+ if ($this->skipCombine($src)) {
147
+ $hasSrc = false;
148
+ break;
149
+ }
150
+ }
151
+
152
+ // Do not add it to the combination list if it has "async" or "defer" attributes
153
+ if (in_array($attrObj->nodeName, array('async', 'defer'))) {
154
+ $hasSrc = false;
155
+ break;
156
+ }
157
+ }
158
+
159
+ }
160
+
161
+ if ( $hasSrc ) {
162
+ $localAssetPath = OptimizeCommon::getLocalAssetPath( $src, 'js' );
163
+
164
+ if ( $localAssetPath ) {
165
+ $combinableList[ $groupIndex ][] = array(
166
+ 'src' => $src,
167
+ 'local' => $localAssetPath,
168
+ 'html' => $matchedSourceFromTag
169
+ );
170
+
171
+ if ( strpos( $localAssetPath, '/wp-includes/js/jquery/jquery.js' ) !== false ) {
172
+ $loadsLocaljQuery = true;
173
+ $jQueryGroupIndex = $groupIndex;
174
+
175
+ $jQueryArrayGroupKeys = array_keys( $combinableList[ $groupIndex ] );
176
+ $jQueryScriptIndex = array_pop( $jQueryArrayGroupKeys );
177
+
178
+ $jQueryAndMigrateGroup ++;
179
+ } elseif ( strpos( $localAssetPath,
180
+ '/wp-includes/js/jquery/jquery-migrate.' ) !== false ) {
181
+ $loadsLocaljQueryMigrate = true;
182
+ $jQueryAndMigrateGroup ++;
183
+ }
184
+ }
185
+
186
+ // We'll check the current group
187
+ // If we have jQuery and jQuery migrate, we will consider the group completed
188
+ // and we will move on to the next group
189
+ if ( $jQueryAndMigrateGroup > 1 ) {
190
+ $groupIndex ++;
191
+ $jQueryAndMigrateGroup = 0; // reset it to avoid having one file per group!
192
+ }
193
+
194
+ // Have we passed <head> and stumbled upon the first script tag from the <body>
195
+ // Then consider the group completed
196
+ if ($lastScriptSrcFromHead && ($src === $lastScriptSrcFromHead)) {
197
+ $groupIndex++;
198
+ $reachedBody = true;
199
+ }
200
+ } else {
201
+ $groupIndex ++;
202
+ }
203
+
204
+ if ($reachedBody && Main::instance()->settings['combine_loaded_js_defer_body']) {
205
+ $bodyGroupIndexes[] = $groupIndex;
206
+ }
207
+ }
208
+
209
+ // Is the page loading local jQuery but not local jQuery Migrate?
210
+ // Keep jQuery as standalone file (not in the combinable list)
211
+ if ( $loadsLocaljQuery && ! $loadsLocaljQueryMigrate && isset($jQueryScriptIndex) ) {
212
+ unset($combinableList[$jQueryGroupIndex][$jQueryScriptIndex]);
213
+ }
214
+
215
+ // Could be pages such as maintenance mode with no external JavaScript files
216
+ if (empty($combinableList)) {
217
+ return $htmlSource;
218
+ }
219
+
220
+ $groupNo = 1;
221
+
222
+ $finalCacheList = array();
223
+
224
+ foreach ($combinableList as $groupIndex => $groupFiles) {
225
+ // Any groups having one file? Then it's not really a group and the file should load on its own
226
+ // Could be one extra file besides the jQuery & jQuery Migrate group or the only JS file called within the HEAD
227
+ if (count($groupFiles) < 2) {
228
+ continue;
229
+ }
230
+
231
+ $combinedUriPaths = $localAssetsPaths = $groupScriptTags = $groupScriptSrcs = array();
232
+
233
+ foreach ( $groupFiles as $groupFileData ) {
234
+ $src = $groupFileData['src'];
235
+ $groupScriptSrcs[] = $src;
236
+ $combinedUriPaths[] = OptimizeCommon::getHrefRelPath( $src );
237
+ $localAssetsPaths[ $src ] = $groupFileData['local'];
238
+ $groupScriptTags[] = $groupFileData['html'];
239
+ }
240
+
241
+ $maybeDoJsCombine = $this->maybeDoJsCombine(
242
+ sha1( implode( '', $combinedUriPaths ) ) . '-' . $groupNo,
243
+ $localAssetsPaths
244
+ );
245
+
246
+ // Local path to combined CSS file
247
+ $localFinalJsFile = $maybeDoJsCombine['local_final_js_file'];
248
+
249
+ // URI (e.g. /wp-content/cache/asset-cleanup/[file-name-here.js]) to the combined JS file
250
+ $uriToFinalJsFile = $maybeDoJsCombine['uri_final_js_file'];
251
+
252
+ if ( ! file_exists( $localFinalJsFile ) ) {
253
+ return $htmlSource; // something is not right as the file wasn't created, we will return the original HTML source
254
+ }
255
+
256
+ $groupScriptSrcsFilter = array_map( function ( $src ) {
257
+ return str_replace( site_url(), '{site_url}', $src );
258
+ }, $groupScriptSrcs );
259
+
260
+ $groupScriptTagsFilter = array_map( function ( $scriptTag ) {
261
+ return str_replace( site_url(), '{site_url}', $scriptTag );
262
+ }, $groupScriptTags );
263
+
264
+ $finalCacheList[ $groupNo ] = array(
265
+ 'uri_to_final_js_file' => $uriToFinalJsFile,
266
+ 'script_srcs' => $groupScriptSrcsFilter,
267
+ 'script_tags' => $groupScriptTagsFilter
268
+ );
269
+
270
+ if (in_array($groupIndex, $bodyGroupIndexes)) {
271
+ $finalCacheList[ $groupNo ]['extras'][] = 'defer';
272
+ }
273
+
274
+ $groupNo++;
275
+ }
276
+
277
+ OptimizeCommon::setAssetCachedData($this->jsonStorageFile, self::$relPathJsCacheDir, json_encode($finalCacheList));
278
+ }
279
+
280
+ if (! empty($finalCacheList)) {
281
+ foreach ( $finalCacheList as $groupNo => $cachedValues ) {
282
+ $htmlSourceBeforeGroupReplacement = $htmlSource;
283
+
284
+ $uriToFinalJsFile = $cachedValues['uri_to_final_js_file'];
285
+
286
+ // Basic Combining (1) -> replace "first" tag with the final combination tag (there would be most likely multiple groups)
287
+ // Enhanced Combining (2) -> replace "last" tag with the final combination tag (most likely one group)
288
+ $indexReplacement = ($combineLevel === 2) ? (count($cachedValues['script_tags']) - 1) : 0;
289
+
290
+ $finalTagUrl = WP_CONTENT_URL . self::$relPathJsCacheDir . $uriToFinalJsFile;
291
+
292
+ $deferAttr = (isset($cachedValues['extras']) && in_array('defer', $cachedValues['extras'])) ? 'defer="defer"' : '';
293
+
294
+ $finalJsTag = <<<HTML
295
+ <script {$deferAttr} id='asset-cleanup-combined-js-group-{$groupNo}' type='text/javascript' src='{$finalTagUrl}'></script>
296
+ HTML;
297
+ $tagsStripped = 0;
298
+
299
+ foreach ( $cachedValues['script_tags'] as $groupScriptTagIndex => $scriptTag ) {
300
+ $scriptTag = str_replace( '{site_url}', site_url(), $scriptTag );
301
+
302
+ if ( $groupScriptTagIndex === $indexReplacement ) {
303
+ $htmlSourceBeforeTagReplacement = $htmlSource;
304
+ $htmlSource = $this->strReplaceOnce( $scriptTag, $finalJsTag, $htmlSource );
305
+ } else {
306
+ $htmlSourceBeforeTagReplacement = $htmlSource;
307
+ $htmlSource = $this->strReplaceOnce( $scriptTag, '', $htmlSource );
308
+ }
309
+
310
+ if ($htmlSource !== $htmlSourceBeforeTagReplacement) {
311
+ $tagsStripped++;
312
+ }
313
+ }
314
+
315
+ // At least two tags has have be stripped from the group to consider doing the group replacement
316
+ // If the tags weren't replaced it's likely there were changes to their structure after they were cached for the group merging
317
+ if ($tagsStripped < 2) {
318
+ $htmlSource = $htmlSourceBeforeGroupReplacement;
319
+ }
320
+ }
321
+ }
322
+
323
+ return $htmlSource;
324
+ });
325
+ }, 1);
326
+ }
327
+
328
+ /**
329
+ * @param $matchesSourcesFromTags
330
+ *
331
+ * @return mixed
332
+ */
333
+ public function clearInlineScriptTags($matchesSourcesFromTags)
334
+ {
335
+ foreach ($matchesSourcesFromTags as $scriptTagIndex => $matchSourceFromTag) {
336
+ $matchedSourceFromTag = trim( $matchSourceFromTag[0] );
337
+
338
+ $domTag = new \DOMDocument();
339
+ $domTag->loadHTML( $matchedSourceFromTag );
340
+
341
+ foreach ( $domTag->getElementsByTagName( 'script' ) as $tagObject ) {
342
+ $hasSrc = false;
343
+
344
+ if ( ! $tagObject->hasAttributes() ) {
345
+ $hasSrc = false;
346
+ } else {
347
+ // Has attributes? Check them
348
+ foreach ( $tagObject->attributes as $attrObj ) {
349
+ if ( $attrObj->nodeName === 'src' && $attrObj->nodeValue ) {
350
+ $hasSrc = true;
351
+ }
352
+ }
353
+ }
354
+
355
+ if (! $hasSrc) {
356
+ unset($matchesSourcesFromTags[$scriptTagIndex]);
357
+ }
358
+ }
359
+ }
360
+
361
+ return $matchesSourcesFromTags;
362
+ }
363
+
364
+ /**
365
+ * @param $htmlSource
366
+ *
367
+ * @return string
368
+ */
369
+ public function lastScriptSrcFromHead($htmlSource)
370
+ {
371
+ $bodyHtml = Misc::extractBetween( $htmlSource, '<head', '</head>' );
372
+
373
+ $regExpPattern = '#<script[^>]*>.*?</script>#is';
374
+
375
+ preg_match_all( $regExpPattern, $bodyHtml, $matchesSourcesFromTags, PREG_SET_ORDER );
376
+
377
+ // Only keep combinable JS files
378
+ foreach ( array_reverse($matchesSourcesFromTags) as $matchSourceFromTag ) {
379
+ $matchedSourceFromTag = trim( $matchSourceFromTag[0] );
380
+
381
+ $domTag = new \DOMDocument();
382
+ $domTag->loadHTML( $matchedSourceFromTag );
383
+
384
+ foreach ( $domTag->getElementsByTagName( 'script' ) as $tagObject ) {
385
+ if ( ! $tagObject->hasAttributes() ) {
386
+ continue;
387
+ }
388
+