This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,57 @@
jQuery(function($) {
var autoCompleteEl = '#wpacu-search-form-assets-manager .search-field';
$(autoCompleteEl).autocomplete({
source: function(request, response) {
var wpacu_post_type;
if ($('#wpacu-custom-post-type-choice').length > 0) {
// Custom Post Type
wpacu_post_type = $('#wpacu-custom-post-type-choice').val();
} else {
// Post, Page or Attachment
wpacu_post_type = wpacu_autocomplete_search_obj.post_type;
}
$.ajax({
dataType: 'json',
url: wpacu_autocomplete_search_obj.ajax_url,
cache: false,
data: {
wpacu_term: request.term,
wpacu_post_type: wpacu_post_type,
action: wpacu_autocomplete_search_obj.ajax_action,
wpacu_security: wpacu_autocomplete_search_obj.ajax_nonce,
wpacu_time: new Date().getTime()
},
success: function(data) {
$('#wpacu-search-form-assets-manager-no-results').hide(); // in case it was ever shown
response(data);
console.log(data);
},
complete: function(jqXHR, textStatus) {
if (jqXHR.responseText == 'no_results') {
var noResultsArray = new Object();
$('#wpacu-search-form-assets-manager-no-results').show();
response(noResultsArray);
//$(autoCompleteEl).val('');
}
}
});
},
select: function(event, ui) {
$('#wpacu-search-form-assets-manager').hide();
$('#wpacu-post-chosen-loading-assets').show();
//console.log(ui.item.id);
window.location.href = wpacu_autocomplete_search_obj.redirect_to.replace('=post_id_here', '=' + ui.item.id);
},
close: function(el) {
el.target.value = '';
}
}).data("ui-autocomplete")._renderItem = function (ul, item) {
return $( "<li>" )
.append( "<div>" + item.label + "<span style='display:block;color:green;font-size:11px;'>"+ item.link +"</span></div>" )
.appendTo( ul );
};
});

View File

@ -0,0 +1 @@
jQuery(function(e){e("#wpacu-search-form-assets-manager .search-field").autocomplete({source:function(a,o){var t;t=e("#wpacu-custom-post-type-choice").length>0?e("#wpacu-custom-post-type-choice").val():wpacu_autocomplete_search_obj.post_type,e.ajax({dataType:"json",url:wpacu_autocomplete_search_obj.ajax_url,cache:!1,data:{wpacu_term:a.term,wpacu_post_type:t,action:wpacu_autocomplete_search_obj.ajax_action,wpacu_security:wpacu_autocomplete_search_obj.ajax_nonce,wpacu_time:(new Date).getTime()},success:function(a){e("#wpacu-search-form-assets-manager-no-results").hide(),o(a),console.log(a)},complete:function(a,t){if("no_results"==a.responseText){var c=new Object;e("#wpacu-search-form-assets-manager-no-results").show(),o(c)}}})},select:function(a,o){e("#wpacu-search-form-assets-manager").hide(),e("#wpacu-post-chosen-loading-assets").show(),window.location.href=wpacu_autocomplete_search_obj.redirect_to.replace("=post_id_here","="+o.item.id)},close:function(e){e.target.value=""}}).data("ui-autocomplete")._renderItem=function(a,o){return e("<li>").append("<div>"+o.label+"<span style='display:block;color:green;font-size:11px;'>"+o.link+"</span></div>").appendTo(a)}});

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

View File

@ -0,0 +1,496 @@
/*!
Chosen, a Select Box Enhancer for jQuery and Prototype
by Patrick Filler for Harvest, http://getharvest.com
Version 1.8.7
Full source at https://github.com/harvesthq/chosen
Copyright (c) 2011-2018 Harvest http://getharvest.com
MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
This file is generated by `grunt build`, do not edit it by hand.
*/
/* @group Base */
.chosen-container {
position: relative;
display: inline-block;
vertical-align: middle;
font-size: 13px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.chosen-container * {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.chosen-container .chosen-drop {
position: absolute;
top: 100%;
z-index: 1010;
width: 100%;
border: 1px solid #aaa;
border-top: 0;
background: #fff;
-webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15);
box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15);
clip: rect(0, 0, 0, 0);
-webkit-clip-path: inset(100% 100%);
clip-path: inset(100% 100%);
}
.chosen-container.chosen-with-drop .chosen-drop {
clip: auto;
-webkit-clip-path: none;
clip-path: none;
}
.chosen-container a {
cursor: pointer;
}
.chosen-container .search-choice .group-name, .chosen-container .chosen-single .group-name {
margin-right: 4px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-weight: normal;
color: #999999;
}
.chosen-container .search-choice .group-name:after, .chosen-container .chosen-single .group-name:after {
content: ":";
padding-left: 2px;
vertical-align: top;
}
/* @end */
/* @group Single Chosen */
.chosen-container-single .chosen-single {
position: relative;
display: block;
overflow: hidden;
padding: 0 0 0 8px;
height: 25px;
border: 1px solid #aaa;
border-radius: 5px;
background-color: #fff;
background: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #fff), color-stop(50%, #f6f6f6), color-stop(52%, #eee), to(#f4f4f4));
background: linear-gradient(#fff 20%, #f6f6f6 50%, #eee 52%, #f4f4f4 100%);
background-clip: padding-box;
-webkit-box-shadow: 0 0 3px #fff inset, 0 1px 1px rgba(0, 0, 0, 0.1);
box-shadow: 0 0 3px #fff inset, 0 1px 1px rgba(0, 0, 0, 0.1);
color: #444;
text-decoration: none;
white-space: nowrap;
line-height: 24px;
}
.chosen-container-single .chosen-default {
color: #999;
}
.chosen-container-single .chosen-single span {
display: block;
overflow: hidden;
margin-right: 26px;
text-overflow: ellipsis;
white-space: nowrap;
}
.chosen-container-single .chosen-single-with-deselect span {
margin-right: 38px;
}
.chosen-container-single .chosen-single abbr {
position: absolute;
top: 6px;
right: 26px;
display: block;
width: 12px;
height: 12px;
background: url("chosen-sprite.png") -42px 1px no-repeat;
font-size: 1px;
}
.chosen-container-single .chosen-single abbr:hover {
background-position: -42px -10px;
}
.chosen-container-single.chosen-disabled .chosen-single abbr:hover {
background-position: -42px -10px;
}
.chosen-container-single .chosen-single div {
position: absolute;
top: 0;
right: 0;
display: block;
width: 18px;
height: 100%;
}
.chosen-container-single .chosen-single div b {
display: block;
width: 100%;
height: 100%;
background: url("chosen-sprite.png") no-repeat 0px 2px;
}
.chosen-container-single .chosen-search {
position: relative;
z-index: 1010;
margin: 0;
padding: 3px 4px;
white-space: nowrap;
}
.chosen-container-single .chosen-search input[type="text"] {
margin: 1px 0;
padding: 4px 20px 4px 5px;
width: 100%;
height: auto;
outline: 0;
border: 1px solid #aaa;
background: url("chosen-sprite.png") no-repeat 100% -20px;
font-size: 1em;
font-family: sans-serif;
line-height: normal;
border-radius: 0;
}
.chosen-container-single .chosen-drop {
margin-top: -1px;
border-radius: 0 0 4px 4px;
background-clip: padding-box;
}
.chosen-container-single.chosen-container-single-nosearch .chosen-search {
position: absolute;
clip: rect(0, 0, 0, 0);
-webkit-clip-path: inset(100% 100%);
clip-path: inset(100% 100%);
}
/* @end */
/* @group Results */
.chosen-container .chosen-results {
color: #444;
position: relative;
overflow-x: hidden;
overflow-y: auto;
margin: 0 4px 4px 0;
padding: 0 0 0 4px;
max-height: 240px;
-webkit-overflow-scrolling: touch;
}
.chosen-container .chosen-results li {
display: none;
margin: 0;
padding: 5px 6px;
list-style: none;
line-height: 15px;
word-wrap: break-word;
-webkit-touch-callout: none;
}
.chosen-container .chosen-results li.active-result {
display: list-item;
cursor: pointer;
}
.chosen-container .chosen-results li.disabled-result {
display: list-item;
color: #ccc;
cursor: default;
}
.chosen-container .chosen-results li.highlighted {
background-color: #3875d7;
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
background-image: linear-gradient(#3875d7 20%, #2a62bc 90%);
color: #fff;
}
.chosen-container .chosen-results li.no-results {
color: #777;
display: list-item;
background: #f4f4f4;
}
.chosen-container .chosen-results li.group-result {
display: list-item;
font-weight: bold;
cursor: default;
}
.chosen-container .chosen-results li.group-option {
padding-left: 15px;
}
.chosen-container .chosen-results li em {
font-style: normal;
text-decoration: underline;
}
/* @end */
/* @group Multi Chosen */
.chosen-container-multi .chosen-choices {
position: relative;
overflow: hidden;
margin: 0;
padding: 0 5px;
width: 100%;
height: auto;
border: 1px solid #aaa;
background-color: #fff;
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(1%, #eee), color-stop(15%, #fff));
background-image: linear-gradient(#eee 1%, #fff 15%);
cursor: text;
}
.chosen-container-multi .chosen-choices li {
float: left;
list-style: none;
}
.chosen-container-multi .chosen-choices li.search-field {
margin: 0;
padding: 0;
white-space: nowrap;
}
.chosen-container-multi .chosen-choices li.search-field input[type="text"] {
margin: 1px 0;
padding: 0;
height: 25px;
outline: 0;
border: 0 !important;
background: transparent !important;
-webkit-box-shadow: none;
box-shadow: none;
color: #999;
font-size: 100%;
font-family: sans-serif;
line-height: normal;
border-radius: 0;
width: 25px;
}
.chosen-container-multi .chosen-choices li.search-choice {
position: relative;
margin: 3px 5px 3px 0;
padding: 3px 20px 3px 5px;
border: 1px solid #aaa;
max-width: 100%;
border-radius: 3px;
background-color: #eeeeee;
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), to(#eee));
background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
background-size: 100% 19px;
background-repeat: repeat-x;
background-clip: padding-box;
-webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
color: #333;
line-height: 13px;
cursor: default;
}
.chosen-container-multi .chosen-choices li.search-choice span {
word-wrap: break-word;
}
.chosen-container-multi .chosen-choices li.search-choice .search-choice-close {
position: absolute;
top: 4px;
right: 3px;
display: block;
width: 12px;
height: 12px;
background: url("chosen-sprite.png") -42px 1px no-repeat;
font-size: 1px;
}
.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover {
background-position: -42px -10px;
}
.chosen-container-multi .chosen-choices li.search-choice-disabled {
padding-right: 5px;
border: 1px solid #ccc;
background-color: #e4e4e4;
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), to(#eee));
background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
color: #666;
}
.chosen-container-multi .chosen-choices li.search-choice-focus {
background: #d4d4d4;
}
.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close {
background-position: -42px -10px;
}
.chosen-container-multi .chosen-results {
margin: 0;
padding: 0;
}
.chosen-container-multi .chosen-drop .result-selected {
display: list-item;
color: #ccc;
cursor: default;
}
/* @end */
/* @group Active */
.chosen-container-active .chosen-single {
border: 1px solid #5897fb;
-webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}
.chosen-container-active.chosen-with-drop .chosen-single {
border: 1px solid #aaa;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #eee), color-stop(80%, #fff));
background-image: linear-gradient(#eee 20%, #fff 80%);
-webkit-box-shadow: 0 1px 0 #fff inset;
box-shadow: 0 1px 0 #fff inset;
}
.chosen-container-active.chosen-with-drop .chosen-single div {
border-left: none;
background: transparent;
}
.chosen-container-active.chosen-with-drop .chosen-single div b {
background-position: -18px 2px;
}
.chosen-container-active .chosen-choices {
border: 1px solid #5897fb;
-webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}
.chosen-container-active .chosen-choices li.search-field input[type="text"] {
color: #222 !important;
}
/* @end */
/* @group Disabled Support */
.chosen-disabled {
opacity: 0.5 !important;
cursor: default;
}
.chosen-disabled .chosen-single {
cursor: default;
}
.chosen-disabled .chosen-choices .search-choice .search-choice-close {
cursor: default;
}
/* @end */
/* @group Right to Left */
.chosen-rtl {
text-align: right;
}
.chosen-rtl .chosen-single {
overflow: visible;
padding: 0 8px 0 0;
}
.chosen-rtl .chosen-single span {
margin-right: 0;
margin-left: 26px;
direction: rtl;
}
.chosen-rtl .chosen-single-with-deselect span {
margin-left: 38px;
}
.chosen-rtl .chosen-single div {
right: auto;
left: 3px;
}
.chosen-rtl .chosen-single abbr {
right: auto;
left: 26px;
}
.chosen-rtl .chosen-choices li {
float: right;
}
.chosen-rtl .chosen-choices li.search-field input[type="text"] {
direction: rtl;
}
.chosen-rtl .chosen-choices li.search-choice {
margin: 3px 5px 3px 0;
padding: 3px 5px 3px 19px;
}
.chosen-rtl .chosen-choices li.search-choice .search-choice-close {
right: auto;
left: 4px;
}
.chosen-rtl.chosen-container-single .chosen-results {
margin: 0 0 4px 4px;
padding: 0 4px 0 0;
}
.chosen-rtl .chosen-results li.group-option {
padding-right: 15px;
padding-left: 0;
}
.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div {
border-right: none;
}
.chosen-rtl .chosen-search input[type="text"] {
padding: 4px 5px 4px 20px;
background: url("chosen-sprite.png") no-repeat -30px -20px;
direction: rtl;
}
.chosen-rtl.chosen-container-single .chosen-single div b {
background-position: 6px 2px;
}
.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b {
background-position: -12px 2px;
}
/* @end */
/* @group Retina compatibility */
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) {
.chosen-rtl .chosen-search input[type="text"],
.chosen-container-single .chosen-single abbr,
.chosen-container-single .chosen-single div b,
.chosen-container-single .chosen-search input[type="text"],
.chosen-container-multi .chosen-choices .search-choice .search-choice-close,
.chosen-container .chosen-results-scroll-down span,
.chosen-container .chosen-results-scroll-up span {
background-image: url("chosen-sprite@2x.png") !important;
background-size: 52px 37px !important;
background-repeat: no-repeat !important;
}
}
/* @end */

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,49 @@
<svg class="lds-spinner" width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" style="background: none;"><g transform="rotate(0 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(30 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(60 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.75s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(90 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(120 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(150 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(180 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(210 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(240 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.25s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(270 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(300 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(330 50 50)">
<rect x="47" y="24" rx="9.4" ry="4.8" width="6" height="12" fill="#007eb1">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animate>
</rect>
</g></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> <image id="image0" width="16" height="16" x="0" y="0"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAmVBMVEX////+/Pn71LP5wI/7
zqj+9/L+8+r2mEf1jjX959X85dH83cL4qWb2lUL+/f372bv827L947/+/Pr+9vD4rW37vWT++fP1
ii781Jr4qGT8w3P7zaf5voz6x5z94Lb81Z394731giD3o1v7v2r7rkD2kz/3pmH4qWf4rWz4sHL6
wJD+9/H94778yH37s032lUL96cz7tE7////3kCn2AAAAIXRSTlMACFiCZhAa1OgyNkiy2gRQkFgI
FKrQEvCItLxohoaGhlp839kJAAAAAWJLR0QAiAUdSAAAAAd0SU1FB+QJDA8xJhnL648AAABeSURB
VBjTY2CgGmBkYmZhReKzsSsqKnJwwrhc3DyKIMDLB+HzK8KAgKAQkC8sAuMrKauIijFwiSsqqiqp
qWtoamnr6OpJMEjCNegbGKqoSDFIy8CArJy8vLwCNTwBAOyoCt8+ouIQAAAAJXRFWHRkYXRlOmNy
ZWF0ZQAyMDIwLTA5LTEyVDE1OjQ5OjM4KzAzOjAwseD5SAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAy
MC0wOS0xMlQxNTo0OTozOCswMzowMMC9QfQAAAAASUVORK5CYII=" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="256px"
height="256px" viewBox="0 0 256 256" style="enable-background:new 0 0 256 256;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);stroke:#2064A9;stroke-miterlimit:10;}
.st1{fill:url(#SVGID_2_);fill-opacity:0.95;}
.st2{fill:#FFB700;fill-opacity:0.95;}
.st3{filter:url(#AI_GaussianBlur_4);}
.st4{fill:#FFFFA6;}
.st5{fill:url(#SVGID_3_);fill-opacity:0.95;}
</style>
<filter id="AI_GaussianBlur_4">
<feGaussianBlur stdDeviation="4"></feGaussianBlur>
</filter>
<g id="the_e">
<radialGradient id="SVGID_1_" cx="143.5159" cy="115.7612" r="104.4259" fx="138.9545" fy="113.5903" gradientTransform="matrix(0.6237 0.7816 -0.7859 0.6271 144.9713 -69.0107)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#89EAFE"/>
<stop offset="0.2624" style="stop-color:#86E8FD"/>
<stop offset="0.4357" style="stop-color:#7DDFF9"/>
<stop offset="0.5832" style="stop-color:#6ED2F2"/>
<stop offset="0.7163" style="stop-color:#59BEE9"/>
<stop offset="0.8397" style="stop-color:#3EA5DD"/>
<stop offset="0.9544" style="stop-color:#1C87CF"/>
<stop offset="1" style="stop-color:#0D79C8"/>
</radialGradient>
<path class="st0" d="M100.5,117.8c2.6-23.3,21-42,43.9-42c25.7,0,43.4,18,45,42H100.5z M184.7,167c-6.5,13.4-20,22.1-40.4,22.6
c-28.3,0.7-43.2-23.4-43.9-44.1h84h4.9h56.9c0.5-4.2,0.8-8.4,0.8-12.8c0-56.4-45.7-102.1-102.1-102.1
c-56.4,0-102.1,45.7-102.1,102.1s45.7,102.1,102.1,102.1c44.4,0,82.1-28.3,96.2-67.8H184.7z"/>
</g>
<g id="halo">
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="237.8817" y1="64.2799" x2="234.5647" y2="79.6944">
<stop offset="0" style="stop-color:#FFB700"/>
<stop offset="0.8599" style="stop-color:#FFB700;stop-opacity:0"/>
</linearGradient>
<path class="st1" d="M235.5,63.7c-0.8,3.1-1.6,5.8-2.1,8.1c-0.4,1.8-0.6,7.1-0.6,7.1s3.8-2.4,4.7-3.6c0.8-1.1,1.9-5.8,2.9-10.5
L235.5,63.7z"/>
<path class="st2" d="M240.6,65c0.7-3.1,1.3-6.3,1.6-8.5c1.3-8.1,4-51.3-53.7-39C111.1,33.9,68.3,94.6,61.5,103
c-12.9,16-51.3,77.8-41.2,115.8c5.9,22.2,29.5,22.1,46.7,18.6l-1.2-6.1c-29.2,4.7-33.6-17.5-29.6-34.8
c4.9-21.6,20.8-57.1,72.2-107.1c45.6-44.4,94-69.2,119.2-56.9c13.4,6.5,10.8,20.6,7.9,31.4L240.6,65z"/>
<g class="st3">
<path class="st4" d="M188.5,17.5C111.1,33.9,68.3,94.6,61.5,103c-8.1,10-26.1,38-36,66.8c-0.6,2.3-1.3,4.6-2.1,6.6c0,0,0,0,0,0
C55.2,94.9,134.2,37.2,191.6,16.9C190.6,17.1,189.5,17.3,188.5,17.5z"/>
</g>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="66.431" y1="235.1271" x2="87.9329" y2="226.4992">
<stop offset="0" style="stop-color:#FFB700"/>
<stop offset="1" style="stop-color:#FFB700;stop-opacity:0"/>
</linearGradient>
<path class="st5" d="M67,237.3c9.4-2,17.1-5,18.8-5.9c1.6-0.9,7.1-3.9,12.1-6.9l-4.4-2.5c-2.4,0.9-5.2,2-8.3,3.3
c-7.3,3-13.9,4.9-19.4,5.7L67,237.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><svg height="32px" version="1.1" viewBox="0 0 32 32" width="32px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><desc/><defs/><g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1"><g fill="#157EFB" id="icon-114-lock"><path d="M16,21.9146472 L16,24.5089948 C16,24.7801695 16.2319336,25 16.5,25 C16.7761424,25 17,24.7721195 17,24.5089948 L17,21.9146472 C17.5825962,21.708729 18,21.1531095 18,20.5 C18,19.6715728 17.3284272,19 16.5,19 C15.6715728,19 15,19.6715728 15,20.5 C15,21.1531095 15.4174038,21.708729 16,21.9146472 L16,21.9146472 Z M9,14.0000125 L9,10.499235 C9,6.35670485 12.3578644,3 16.5,3 C20.6337072,3 24,6.35752188 24,10.499235 L24,14.0000125 C25.6591471,14.0047488 27,15.3503174 27,17.0094776 L27,26.9905224 C27,28.6633689 25.6529197,30 23.991212,30 L9.00878799,30 C7.34559019,30 6,28.652611 6,26.9905224 L6,17.0094776 C6,15.339581 7.34233349,14.0047152 9,14.0000125 L9,14.0000125 L9,14.0000125 Z M12,14 L12,10.5008537 C12,8.0092478 14.0147186,6 16.5,6 C18.9802243,6 21,8.01510082 21,10.5008537 L21,14 L12,14 L12,14 L12,14 Z" id="lock"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 50 50" height="50px" id="Layer_1" version="1.1" viewBox="0 0 50 50" width="50px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect fill="none" height="50" width="50"/><path d="M18,47c-1.104,0-2-0.896-2-2 V23c0-1.104,0.896-2,2-2h28c1.104,0,2,0.896,2,2v22c0,1.104-0.896,2-2,2H18z" fill="none" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2"/><path d="M42,21c0-1.045,0-5.955,0-7 c0-5.523-4.477-10-10-10S22,8.477,22,14c0,1.045,0,5.955,0,7" fill="none" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2"/><path d="M35,32c0-1.657-1.343-3-3-3s-3,1.343-3,3c0,0.885,0.391,1.673,1,2.222V37c0,1.104,0.896,2,2,2s2-0.896,2-2v-2.778 C34.609,33.673,35,32.885,35,32z"/></svg>

After

Width:  |  Height:  |  Size: 932 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#DD2E44" d="M21.533 18.002L33.768 5.768c.976-.976.976-2.559 0-3.535-.977-.977-2.559-.977-3.535 0L17.998 14.467 5.764 2.233c-.976-.977-2.56-.977-3.535 0-.977.976-.977 2.559 0 3.535l12.234 12.234L2.201 30.265c-.977.977-.977 2.559 0 3.535.488.488 1.128.732 1.768.732s1.28-.244 1.768-.732l12.262-12.263 12.234 12.234c.488.488 1.128.732 1.768.732.64 0 1.279-.244 1.768-.732.976-.977.976-2.559 0-3.535L21.533 18.002z"/></svg>

After

Width:  |  Height:  |  Size: 491 B

View File

@ -0,0 +1 @@
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M14.83 4.89l1.34.94-5.81 8.38H9.02L5.78 9.67l1.34-1.25 2.57 2.4z' fill='#cc0000'/></svg>

After

Width:  |  Height:  |  Size: 157 B

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: transparent; display: block; shape-rendering: auto;" width="180px" height="40px" viewBox="40 41 20 18" preserveAspectRatio="xMidYMid">
<g transform="translate(20 50)">
<circle cx="0" cy="0" r="6" fill="#064565">
<animateTransform attributeName="transform" type="scale" begin="-0.375s" calcMode="spline" keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s" repeatCount="indefinite"></animateTransform>
</circle>
</g><g transform="translate(40 50)">
<circle cx="0" cy="0" r="6" fill="#0f6481">
<animateTransform attributeName="transform" type="scale" begin="-0.25s" calcMode="spline" keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s" repeatCount="indefinite"></animateTransform>
</circle>
</g><g transform="translate(60 50)">
<circle cx="0" cy="0" r="6" fill="#1f8d9b">
<animateTransform attributeName="transform" type="scale" begin="-0.125s" calcMode="spline" keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s" repeatCount="indefinite"></animateTransform>
</circle>
</g><g transform="translate(80 50)">
<circle cx="0" cy="0" r="6" fill="#4fa5a6">
<animateTransform attributeName="transform" type="scale" begin="0s" calcMode="spline" keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s" repeatCount="indefinite"></animateTransform>
</circle>
</g>
<!-- [ldio] generated by https://loading.io/ --></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256"><defs><style>.cls-1{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="3.74" y1="252.26" x2="252.26" y2="3.74" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2e2975"/><stop offset="1" stop-color="#f2295b"/></linearGradient></defs><title>elementor-gradient-256px</title><path class="cls-1" d="M243.24,0H12.76A12.76,12.76,0,0,0,0,12.76V243.24A12.76,12.76,0,0,0,12.76,256H243.24A12.76,12.76,0,0,0,256,243.24V12.76A12.76,12.76,0,0,0,243.24,0ZM93.87,184.89H71.11V71.11H93.87V184.89Zm91,0H116.62V162.13h68.27v22.76Zm0-45.51H116.62V116.62h68.27v22.76Zm0-45.51H116.62V71.11h68.27V93.87Z" transform="translate(0 0)"/></svg>

After

Width:  |  Height:  |  Size: 792 B

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="248px" height="279px" viewBox="0 0 248 279" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.3 (51167) - http://www.bohemiancoding.com/sketch -->
<title>GF Blue Fill Logo</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Dark" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="GF-Stroke-Logo" transform="translate(-727.000000, -134.000000)">
<g id="GF-Blue-Fill-Logo" transform="translate(727.000000, 134.000000)">
<path d="M223.492735,58.6304269 L147.95484,14.9305838 C141.399754,11.1848206 132.659848,9 123.919942,9 C115.179724,9 106.439817,11.1848206 99.8850437,14.9305838 L24.0345862,58.6304269 C10.6127878,66.7458313 0,84.5378947 0,100.145137 L0,187.856763 C0,203.463693 10.3005369,221.568008 24.0345862,229.371473 L99.8850437,273.071316 C106.439817,276.816767 115.179724,279.0019 123.919942,279.0019 C132.659848,279.0019 141.399754,276.816767 147.95484,273.071316 L223.804985,229.371473 C237.227096,221.255757 247.839884,203.463693 247.839884,187.856763 L247.839884,100.145137 C247.839884,84.2259557 237.539035,66.4338923 223.492735,58.6304269 Z" id="stroke-F-Copy" fill="#375767"></path>
<path d="M95.6646347,129.3158 L191.026345,129.3158 L191.026345,105 L95.8647747,105 C83.5568047,105 73.2501347,109.3028 65.2449547,117.6082 C46.4327747,137.7212 49.1345247,175.5457 49.1345247,177.1467 L50.0351147,188.354 L190.025745,188.354 L190.025745,151.1299 L165.609945,151.1299 L165.609945,164.1383 L73.7504547,164.1383 C74.2507847,156.1331 76.2520747,141.9239 83.2566147,134.5191 C86.3586147,130.9168 90.2611447,129.3158 95.6646347,129.3158 Z" id="Fill-21" fill="#E7E9EB"></path>
<path d="M225.670882,16.3598934 L200.770997,1.95492412 C198.610216,0.720191908 195.729242,0 192.848269,0 C189.967193,0 187.08622,0.720191908 184.925541,1.95492412 L159.922625,16.3598934 C155.498339,19.0350094 152,24.8998846 152,30.0445679 L152,58.9573321 C152,64.1019126 155.395411,70.0697164 159.922625,72.6420066 L184.925541,87.0469759 C187.08622,88.2816053 189.967193,89.0019 192.848269,89.0019 C195.729242,89.0019 198.610216,88.2816053 200.770997,87.0469759 L225.77381,72.6420066 C230.198199,69.9667878 233.696538,64.1019126 233.696538,58.9573321 L233.696538,30.0445679 C233.696538,24.7970588 230.301025,18.9321836 225.670882,16.3598934 Z" id="stroke-F" fill="#E7E9EB"></path>
<path d="M178.179283,56.2747335 C178.079741,57.3692984 177.383146,57.8669093 176.288581,57.8669093 L172.407833,57.8669093 C171.313268,57.8669093 170.915199,57.3692984 171.014741,56.2747335 L173.701482,39.0600231 C174.49752,34.08471 177.084619,31 182.955612,31 L214.200781,31 C215.295345,31 215.693414,31.4975114 215.593872,32.5920763 L215.096361,35.6767863 C214.996819,36.7713512 214.300323,37.2689621 213.205758,37.2689621 L183.851192,37.2689621 C181.960489,37.2689621 180.865925,38.2639849 180.567398,40.0551453 L180.368413,41.8462063 L208.628414,41.8462063 C209.722979,41.8462063 210.120948,42.3438172 209.921964,43.4383821 L209.424452,46.5230921 C209.32491,47.617657 208.628414,48.1151684 207.533849,48.1151684 L179.273848,48.1151684 L178.179283,56.2747335 Z" id="Path" fill="#375767"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

View File

@ -0,0 +1,30 @@
<svg width="78" height="78" viewBox="0 0 78 78" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>logo</title>
<desc>Created using Figma</desc>
<g id="Canvas" transform="translate(9211 -1154)">
<g id="logo">
<g id="Dark">
<g id="M/opacity">
<g id="Vector">
<use xlink:href="#path0_fill" transform="translate(-9211 1154)" fill="url(#paint0_linear)"/>
</g>
<g id="Vector" opacity="0.5">
<use xlink:href="#path1_fill" transform="translate(-9172 1154)" fill="url(#paint1_linear)"/>
</g>
</g>
</g>
</g>
</g>
<defs>
<linearGradient id="paint0_linear" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2.38804e-15 77.998 -38.9997 4.776e-15 38.9997 -4.776e-15)">
<stop offset="0.00552486" stop-color="#BC7FFF"/>
<stop offset="1" stop-color="#6CA6D7"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2.38804e-15 77.998 -38.9997 4.776e-15 38.9997 -4.776e-15)">
<stop offset="0.00552486" stop-color="#BC7FFF"/>
<stop offset="1" stop-color="#6CA6D7"/>
</linearGradient>
<path id="path0_fill" d="M 38.9997 77.998L 38.9997 67.0614L 30.3201 58.3819L 38.9997 49.702L 38.9997 38.7653L 24.8514 52.9137L 10.9369 38.999L 38.9997 10.9363L 38.9997 -9.16485e-12L 0 38.999L 38.9997 77.998Z"/>
<path id="path1_fill" d="M 6.00638e-07 10.9367L 28.0628 38.9994L 14.1483 52.9137L 6.00638e-07 38.7661L 6.00638e-07 49.7024L 8.68004 58.3823L 6.00638e-07 67.0618L 6.00638e-07 77.998L 19.6165 58.3823L 38.9997 38.9994L 6.00638e-07 0L 6.00638e-07 10.9367Z"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,10 @@
<svg viewBox="0 0 314 314" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill-rule="evenodd">
<g transform="translate(-465.000000, -660.000000)">
<g transform="translate(465.000000, 660.000000)">
<polygon points="92.2385914 87 123.226879 87 157.525701 139.018555 192.196599 87.0859375 221.951847 87.0859375 174.820623 156.598633 223.402654 227.489258 191.967905 227.489258 157.755193 175.689453 122.392888 227.351562 91 227.351562 140.283513 156.523438"></polygon>
<path d="M157,314 C70.2912943,314 0,243.708706 0,157 C0,70.2912943 70.2912943,0 157,0 C243.708706,0 314,70.2912943 314,157 C314,243.708706 243.708706,314 157,314 Z M157,291 C231.006156,291 291,231.006156 291,157 C291,82.9938435 231.006156,23 157,23 C82.9938435,23 23,82.9938435 23,157 C23,231.006156 82.9938435,291 157,291 Z"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 981 B

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="1200px" height="1200px" viewBox="0 0 1200 1200" enable-background="new 0 0 1200 1200" xml:space="preserve">
<path fill="#9B5C8F" d="M131.729,257.729h936.074c59.242,0,107.197,47.954,107.197,107.197v357.313
c0,59.243-47.955,107.196-107.197,107.196H732.115l46.077,112.836l-202.64-112.836H132.196
c-59.238,0-107.193-47.953-107.193-107.196V364.926C24.531,306.155,72.481,257.729,131.729,257.729z"/>
<path fill="#FFFFFF" d="M90.486,355.434c6.545-8.886,16.359-13.558,29.451-14.491c23.845-1.874,37.401,9.344,40.672,33.654
c14.492,97.705,30.385,180.452,47.213,248.234l102.377-194.939c9.353-17.767,21.041-27.114,35.064-28.049
c20.57-1.4,33.188,11.686,38.332,39.267c11.689,62.172,26.648,115,44.41,159.877c12.156-118.737,32.727-204.287,61.709-257.11
c7.008-13.09,17.295-19.635,30.854-20.574c10.754-0.93,20.568,2.34,29.449,9.354c8.882,7.012,13.559,15.893,14.492,26.646
c0.467,8.414-0.934,15.426-4.672,22.438c-18.234,33.66-33.197,90.226-45.349,168.758c-11.686,76.201-15.895,135.575-13.09,178.116
c0.938,11.679-0.935,21.967-5.606,30.852c-5.615,10.283-14.025,15.895-24.779,16.828c-12.154,0.935-24.773-4.672-36.93-17.295
c-43.475-44.414-78.07-110.795-103.313-199.148c-30.39,59.837-52.827,104.714-67.319,134.632
c-27.582,52.827-50.955,79.941-70.59,81.345c-12.623,0.934-23.373-9.82-32.722-32.254c-23.84-61.242-49.553-179.519-77.131-354.821
C81.137,374.598,83.939,363.843,90.486,355.434L90.486,355.434z M1095.102,428.827c-16.827-29.451-41.605-47.213-74.795-54.229
c-8.886-1.869-17.295-2.803-25.246-2.803c-44.877,0-81.341,23.373-109.856,70.123c-24.312,39.733-36.463,83.676-36.463,131.828
c0,35.996,7.479,66.848,22.438,92.561c16.827,29.451,41.605,47.215,74.795,54.227c8.881,1.874,17.294,2.808,25.245,2.808
c45.345,0,81.809-23.373,109.857-70.123c24.307-40.205,36.463-84.146,36.463-132.295
C1118.007,484.458,1110.061,454.069,1095.102,428.827z M1036.2,558.319c-6.546,30.852-18.234,53.758-35.534,69.185
c-13.552,12.155-26.176,17.295-37.859,14.959c-11.222-2.336-20.574-12.151-27.582-30.386c-5.615-14.491-8.414-28.983-8.414-42.541
c0-11.686,0.936-23.373,3.271-34.123c4.209-19.168,12.155-37.869,24.773-55.631c15.431-22.911,31.791-32.254,48.619-28.984
c11.223,2.336,20.574,12.156,27.582,30.385c5.61,14.492,8.413,28.984,8.413,42.541c0,12.156-0.935,23.841-3.27,34.591V558.319z
M802.458,428.827c-16.828-29.45-42.074-47.213-74.795-54.229c-8.881-1.869-17.295-2.803-25.246-2.803
c-44.877,0-81.34,23.373-109.857,70.123c-24.307,39.733-36.463,83.676-36.463,131.828c0,35.996,7.48,66.848,22.439,92.561
c16.828,29.451,41.606,47.215,74.795,54.227c8.885,1.874,17.294,2.808,25.246,2.808c45.344,0,81.807-23.373,109.855-70.123
c24.313-40.205,36.463-84.146,36.463-132.295C824.896,484.458,817.417,454.069,802.458,428.827L802.458,428.827z M743.089,558.319
c-6.545,30.852-18.233,53.758-35.528,69.185c-13.558,12.155-26.181,17.295-37.868,14.959c-11.218-2.336-20.565-12.151-27.582-30.386
c-5.606-14.491-8.409-28.983-8.409-42.541c0-11.686,0.935-23.373,3.27-34.123c4.21-19.168,12.156-37.869,24.779-55.631
c15.427-22.911,31.787-32.254,48.614-28.984c11.222,2.336,20.573,12.156,27.582,30.385c5.614,14.492,8.414,28.984,8.414,42.541
c0.472,12.156-0.935,23.841-3.271,34.591V558.319z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014 Tristan Edwards & Limon Monte
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,263 @@
<p align="center">
<a href="https://github.com/sponsors/limonte">[= Become the :trophy: Ultimate Sponsor of SweetAlert2 and place your banner here (100K+ unique visitors per month) =]</a>
</p>
<p align="center">
<a href="https://sweetalert2.github.io/">
<img src="/assets/swal2-logo.png" alt="SweetAlert2">
</a>
</p>
<p align="center">
A beautiful, responsive, customizable, accessible (WAI-ARIA) replacement for JavaScript's popup boxes. Zero dependencies.
</p>
<p align="center">
<a href="https://sweetalert2.github.io/">
<img src="https://raw.github.com/sweetalert2/sweetalert2/master/assets/sweetalert2.gif" width="562"><br>
See SweetAlert2 in action ↗
</a>
</p>
<p align="center">
<a href="https://github.com/sweetalert2/sweetalert2/actions"><img alt="Build Status" src="https://github.com/sweetalert2/sweetalert2/workflows/build/badge.svg"></a>
<a href="https://codeclimate.com/github/sweetalert2/sweetalert2/test_coverage"><img alt="Coverage Status" src="https://api.codeclimate.com/v1/badges/eba34bb80477933854d4/test_coverage"></a>
<a href="https://www.npmjs.com/package/sweetalert2"><img alt="Version" src="https://img.shields.io/npm/v/sweetalert2.svg"></a>
<a href="https://www.jsdelivr.com/package/npm/sweetalert2"><img alt="jsdelivr" src="https://data.jsdelivr.com/v1/package/npm/sweetalert2/badge?style=rounded"></a>
<a href="#support-and-donations"><img alt="Support Donate" src="https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=ea4aaa"></a>
</p>
---
:shipit: The author of SweetAlert2 ([@limonte](https://github.com/limonte/)) is looking for short-term to medium-term working contracts in front-end, preferably OSS.
---
:point_right: **Upgrading from v9.x to v10.x?** [Read the release notes!](https://github.com/sweetalert2/sweetalert2/releases/tag/v10.0.0)
<br>If you're upgrading from v8.x, please [upgrade from v8 to v9](https://github.com/sweetalert2/sweetalert2/releases/tag/v10.0.0) first!
<br>If you're upgrading from v7.x, please [upgrade from v7 to v8](https://github.com/sweetalert2/sweetalert2/releases/tag/v8.0.0) first!
<br>If you're upgrading from v6.x, please [upgrade from v6 to v7](https://github.com/sweetalert2/sweetalert2/releases/tag/v7.0.0) first!
:point_right: **Migrating from [SweetAlert](https://github.com/t4t5/sweetalert)?** [SweetAlert 1.x to SweetAlert2 migration guide](https://github.com/sweetalert2/sweetalert2/wiki/Migration-from-SweetAlert-to-SweetAlert2)
---
Installation
------------
```sh
npm install --save sweetalert2
```
Or grab from [jsdelivr CDN](https://www.jsdelivr.com/package/npm/sweetalert2)
:
```html
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
```
Usage
-----
```html
<script src="sweetalert2/dist/sweetalert2.all.min.js"></script>
<!-- Include a polyfill for ES6 Promises (optional) for IE11 -->
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.js"></script>
```
You can also include the stylesheet separately if desired:
```html
<script src="sweetalert2/dist/sweetalert2.min.js"></script>
<link rel="stylesheet" href="sweetalert2/dist/sweetalert2.min.css">
```
Or:
```js
// ES6 Modules or TypeScript
import Swal from 'sweetalert2'
// CommonJS
const Swal = require('sweetalert2')
```
Or with JS modules:
```html
<link rel="stylesheet" href="sweetalert2/dist/sweetalert2.css">
<script type="module">
import Swal from 'sweetalert2/src/sweetalert2.js'
</script>
```
It's possible to import JS and CSS separately, e.g. if you need to customize styles:
```js
import Swal from 'sweetalert2/dist/sweetalert2.js'
import 'sweetalert2/src/sweetalert2.scss'
```
Please note that [TypeScript is well-supported](https://github.com/sweetalert2/sweetalert2/blob/master/sweetalert2.d.ts), so you don't have to install a third-party declaration file.
Examples
--------
The most basic message:
```js
Swal.fire('Hello world!')
```
A message signaling an error:
```js
Swal.fire('Oops...', 'Something went wrong!', 'error')
```
Handling the result of SweetAlert2 modal:
```js
Swal.fire({
title: 'Are you sure?',
text: 'You will not be able to recover this imaginary file!',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'No, keep it'
}).then((result) => {
if (result.value) {
Swal.fire(
'Deleted!',
'Your imaginary file has been deleted.',
'success'
)
// For more information about handling dismissals please visit
// https://sweetalert2.github.io/#handling-dismissals
} else if (result.dismiss === Swal.DismissReason.cancel) {
Swal.fire(
'Cancelled',
'Your imaginary file is safe :)',
'error'
)
}
})
```
## [Go here to see the docs and more examples ↗](https://sweetalert2.github.io/)
Browser compatibility
---------------------
IE11* | Edge | Chrome | Firefox | Safari | Opera | UC Browser
-------|------|--------|---------|--------|-------|------------
:heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
\* ES6 Promise polyfill should be included, see [usage example](#usage).
Note that SweetAlert2 **does not** and **will not** provide support or functionality of any kind on IE10 and lower.
Themes ([`sweetalert2-themes ↗`](https://github.com/sweetalert2/sweetalert2-themes))
------
- [`Dark`](https://github.com/sweetalert2/sweetalert2-themes/tree/master/dark)
- [`Minimal`](https://github.com/sweetalert2/sweetalert2-themes/tree/master/minimal)
- [`Borderless`](https://github.com/sweetalert2/sweetalert2-themes/tree/master/borderless)
- [`Bootstrap 4`](https://github.com/sweetalert2/sweetalert2-themes/tree/master/bootstrap-4)
- [`Material UI`](https://github.com/sweetalert2/sweetalert2-themes/tree/master/material-ui)
- [`WordPress Admin`](https://github.com/sweetalert2/sweetalert2-themes/tree/master/wordpress-admin)
- [`Bulma`](https://github.com/sweetalert2/sweetalert2-themes/tree/master/bulma)
- [`Default`](https://github.com/sweetalert2/sweetalert2-themes/tree/master/default)
Related projects
-------------------------
- [ngx-sweetalert2](https://github.com/sweetalert2/ngx-sweetalert2) - Angular 4+ integration
- [sweetalert2-react-content](https://github.com/sweetalert2/sweetalert2-react-content) - React integration
- [sweetalert2-webpack-demo](https://github.com/sweetalert2/sweetalert2-webpack-demo) - webpack demo
- [sweetalert2-parcel-demo](https://github.com/sweetalert2/sweetalert2-parcel-demo) - overriding SCSS variables demo
Related community projects
-------------------------
- [avil13/vue-sweetalert2](https://github.com/avil13/vue-sweetalert2) - Vue.js wrapper
- [realrashid/sweet-alert](https://github.com/realrashid/sweet-alert) - Laravel 5 Package
- [Basaingeal/Razor.SweetAlert2](https://github.com/Basaingeal/Razor.SweetAlert2) - Blazor Wrapper
- [ElectronAlert](https://electron.guide/electron-alert/) - SweetAlert2 for Electron applications (main process)
Collaborators
-------------
[![](https://avatars3.githubusercontent.com/u/17089396?v=4&s=80)](https://github.com/gverni) | [![](https://avatars3.githubusercontent.com/u/3198597?v=4&s=80)](https://github.com/zenflow) | [![](https://avatars1.githubusercontent.com/u/1343250?v=4&s=80)](https://github.com/toverux)
-|-|-
[@gverni](https://github.com/gverni) | [@zenflow](https://github.com/zenflow) | [@toverux](https://github.com/toverux)
Contributing
------------
[![Maintainability](https://api.codeclimate.com/v1/badges/eba34bb80477933854d4/maintainability)](https://codeclimate.com/github/sweetalert2/sweetalert2/maintainability)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/sweetalert2/sweetalert2/blob/master/CHANGELOG.md)
If you would like to contribute enhancements or fixes, please do the following:
1. Fork the `sweetalert2` repository and clone it locally.
2. Make sure you have [npm](https://www.npmjs.com/) or [yarn](https://yarnpkg.com/) installed.
3. When in the SweetAlert2 directory, run `npm install` or `yarn install` to install dependencies.
4. To begin active development, run `npm start` or `yarn start`. This does several things for you:
- Builds the `dist` folder
- Serves sandbox.html @ http://localhost:8080/ (browser-sync ui: http://localhost:8081/)
- Re-builds and re-loads as necessary when files change
Big Thanks
----------
- [Serena Verni (@serenaperora)](https://serena.verni.xyz) for creating the amazing project logo
- [Sauce Labs](https://saucelabs.com/) for providing the reliable cross-browser testing platform
Sponsors
--------
[<img src="https://sweetalert2.github.io/images/sponsors/flowcrypt-banner.png">](https://flowcrypt.com/?utm_source=sweetalert2&utm_medium=banner)
[<img src="https://sweetalert2.github.io/images/plus.png" width="80">](SPONSORS.md) | [<img src="https://avatars2.githubusercontent.com/u/28631236?s=80&v=4" width="80">](https://flowcrypt.com/?utm_source=sweetalert2&utm_medium=logo) | [<img src="https://sweetalert2.github.io/images/sponsors/brainbi.png" width="80">](https://www.brainbi.dev/) | [<img src="https://avatars0.githubusercontent.com/u/3986989?s=80&v=4" width="80">](https://github.com/tiagostutz)
-|-|-|-
[Become a sponsor](SPONSORS.md) | [FlowCrypt](https://flowcrypt.com/?utm_source=sweetalert2&utm_medium=logo) | [brainbi price monitoring](https://www.brainbi.dev/) | [Tiago de Oliveira Stutz](https://github.com/tiagostutz)
[<img src="https://sweetalert2.github.io/images/sponsors/zillathemes.jpg" width="80">](https://zillathemes.com/) | [<img src="https://sweetalert2.github.io/images/sponsors/sebaebc.png" width="80">](https://github.com/sebaebc) | [<img src="https://sweetalert2.github.io/images/sponsors/wp-reset.png" width="80">](https://wpreset.com/?utm_source=sweetalert2&utm_medium=logo)
-|-|-
[Zilla Themes](https://zillathemes.com/) | [SebaEBC](https://github.com/sebaebc) | [WP Reset](https://wpreset.com/?utm_source=sweetalert2&utm_medium=logo)
NSFW Sponsors
-------------
[<img src="https://sweetalert2.github.io/images/sponsors/twerkingbutt.jpg" width="80">](https://twerkingbutt.com/?utm_source=sweetalert2&utm_medium=logo) | [<img src="https://sweetalert2.github.io/images/sponsors/sex-toy-education.png" width="80">](https://sextoyeducation.com/?utm_source=sweetalert2&utm_medium=logo) | [<img src="https://sweetalert2.github.io/images/sponsors/sextopedia.png" width="80">](https://sextopedia.com/?utm_source=sweetalert2&utm_medium=logo) | [<img src="https://sweetalert2.github.io/images/sponsors/my-sex-toy-guide.jpg" width="80">](https://www.mysextoyguide.com/?utm_source=sweetalert2&utm_medium=logo) | [<img src="https://sweetalert2.github.io/images/sponsors/best-blowjob-machines.jpg" width="80">](https://www.bestblowjobmachines.com/?utm_source=sweetalert2&utm_medium=logo) | [<img src="https://sweetalert2.github.io/images/sponsors/yourdoll.jpg" width="80">](https://www.yourdoll.com/?utm_source=sweetalert2&utm_medium=logo)
-|-|-|-|-|-
[Twerking Butt](https://twerkingbutt.com/?utm_source=sweetalert2&utm_medium=logo) | [STED](https://sextoyeducation.com/?utm_source=sweetalert2&utm_medium=logo) | [Sextopedia](https://sextopedia.com/?utm_source=sweetalert2&utm_medium=logo) | [My Sex Toy Guide](https://www.mysextoyguide.com/?utm_source=sweetalert2&utm_medium=logo) | [Best Blowjob Machines](https://www.bestblowjobmachines.com/?utm_source=sweetalert2&utm_medium=logo) | [YourDoll](https://www.yourdoll.com/?utm_source=sweetalert2&utm_medium=logo)
[<img src="https://sweetalert2.github.io/images/sponsors/celebjihad.png" width="80">](https://celebjihad.ltd/?utm_source=sweetalert2&utm_medium=logo) | [<img src="https://sweetalert2.github.io/images/sponsors/sextoycollective.jpg" width="80">](https://sextoycollective.com/?utm_source=sweetalert2&utm_medium=logo) | [<img src="https://sweetalert2.github.io/images/sponsors/bingato.png" width="80">](https://bingato.com/?utm_source=sweetalert2&utm_medium=logo) | [<img src="https://sweetalert2.github.io/images/sponsors/realsexdoll.png" width="80">](https://realsexdoll.com/?utm_source=sweetalert2&utm_medium=logo)| [<img src="https://sweetalert2.github.io/images/sponsors/doctorclimax.png" width="80">](https://doctorclimax.com/)
-|-|-|-|-
[Celebjihad](https://celebjihad.ltd/?utm_source=sweetalert2&utm_medium=logo) | [STC](https://sextoycollective.com/?utm_source=sweetalert2&utm_medium=logo) | [Bingato](https://bingato.com/?utm_source=sweetalert2&utm_medium=logo) | [RealSexDoll](https://realsexdoll.com/?utm_source=sweetalert2&utm_medium=logo) | [DoctorClimax](https://doctorclimax.com/)
Support and Donations
---------------------
Has SweetAlert2 helped you create an amazing application? You can show your support via [GitHub Sponsors](https://github.com/sponsors/limonte)
Alternative ways for donations (PayPal, cryptocurrencies, etc.) are listed here: https://sweetalert2.github.io/#donations
### [Hall of Donators :trophy:](DONATIONS.md)

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,162 @@
{
"_from": "sweetalert2",
"_id": "sweetalert2@10.6.1",
"_inBundle": false,
"_integrity": "sha512-5cIPtYldmZ+EljMVRd5ml9xT72STU+N4kk57nO0oECQmjkCsJypAcEdL+h045otoAd3t9962FtewJYSKyJxOFQ==",
"_location": "/sweetalert2",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "sweetalert2",
"name": "sweetalert2",
"escapedName": "sweetalert2",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-10.6.1.tgz",
"_shasum": "343d980858494bd0073ac8de9be57bf2365df316",
"_spec": "sweetalert2",
"_where": "/Applications/MAMP/htdocs/asset-optimizer",
"author": {
"name": "Limon Monte",
"email": "limon.monte@gmail.com",
"url": "https://limonte.github.io"
},
"browser": "dist/sweetalert2.all.js",
"bugs": {
"url": "https://github.com/sweetalert2/sweetalert2/issues"
},
"bundleDependencies": false,
"bundlewatch": {
"files": [
{
"path": "dist/sweetalert2.all.min.js",
"maxSize": "20kB"
}
]
},
"contributors": [
{
"name": "Giuseppe Verni",
"url": "https://github.com/gverni"
},
{
"name": "Matthew Francis Brunetti",
"email": "zenflow87@gmail.com",
"url": "https://github.com/zenflow"
},
{
"name": "Morgan Touverey-Quilling",
"email": "mtouverey@alembic-dev.com",
"url": "https://github.com/toverux"
},
{
"name": "Sam Turrell",
"email": "sam@samturrell.co.uk",
"url": "https://github.com/samturrell"
},
{
"name": "Joseph Schultz",
"url": "https://github.com/acupajoe"
},
{
"name": "Johan Fagerberg",
"url": "https://github.com/birjolaxew"
}
],
"deprecated": false,
"description": "A beautiful, responsive, customizable and accessible (WAI-ARIA) replacement for JavaScript's popup boxes, supported fork of sweetalert",
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-transform-object-assign": "^7.2.0",
"@babel/preset-env": "^7.3.1",
"@rollup/plugin-json": "^4.0.2",
"@sweetalert2/eslint-config": "^1.0.0",
"@sweetalert2/execute": "^1.0.0",
"@sweetalert2/stylelint-config": "^1.1.5",
"babel-loader": "^8.0.4",
"babel-plugin-array-includes": "^2.0.3",
"browser-sync": "^2.26.3",
"custom-event-polyfill": "^1.0.6",
"eslint": "^7.0.0",
"eslint-plugin-cypress": "^2.8.1",
"gulp": "^4.0.0",
"gulp-autoprefixer": "^7.0.0",
"gulp-clean-css": "^4.0.0",
"gulp-concat": "^2.6.1",
"gulp-css2js": "^1.1.2",
"gulp-if": "^3.0.0",
"gulp-rename": "^2.0.0",
"gulp-rollup": "^2.16.2",
"gulp-uglify": "^3.0.0",
"istanbul-lib-coverage": "^3.0.0",
"jquery": "^3.3.1",
"karma": "^5.0.0",
"karma-chrome-launcher": "^3.0.0",
"karma-firefox-launcher": "^1.1.0",
"karma-ie-launcher": "^1.0.0",
"karma-qunit": "^4.0.0",
"karma-sauce-launcher": "^4.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "^0.0.32",
"karma-webpack": "^4.0.0",
"merge2": "^1.2.3",
"nyc": "^15.0.0",
"promise-polyfill": "^8.1.0",
"qunit": "^2.8.0",
"replace-in-file": "^6.0.0",
"rollup": "^2.0.0",
"rollup-plugin-babel": "^4.3.2",
"sass": "^1.22.1",
"sinon": "^9.0.0",
"stylelint": "^13.0.0",
"typescript": "^4.0.0",
"webpack": "^4.29.0"
},
"files": [
"dist",
"src",
"sweetalert2.d.ts"
],
"funding": {
"url": "https://sweetalert2.github.io/#donations"
},
"homepage": "https://sweetalert2.github.io/",
"keywords": [
"sweetalert",
"sweetalert2",
"alert",
"modal",
"popup",
"prompt",
"confirm",
"toast",
"accessible"
],
"license": "MIT",
"main": "dist/sweetalert2.all.js",
"module": "src/sweetalert2.js",
"name": "sweetalert2",
"repository": {
"type": "git",
"url": "git+https://github.com/sweetalert2/sweetalert2.git"
},
"scripts": {
"build": "gulp build",
"cy:run": "cypress run --browser chrome --config supportFile=cypress/support/ci.js",
"cy:run:headless": "cypress run --headless --browser chrome --config supportFile=cypress/support/ci.js",
"cy:start": "cypress open",
"lint": "stylelint src && eslint src test cypress tools *.js *.ts",
"nyc:instrument": "nyc instrument --compact=false src src_instrumented && rm -rf src && mv src_instrumented src",
"start": "gulp develop --continue-on-error --skip-minification --skip-standalone",
"test": "karma start karma.conf.js --single-run"
},
"types": "sweetalert2.d.ts",
"version": "10.6.1"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,165 @@
#wpacu-debug-options {
background: white;
width: 90%;
margin: 10px;
border: 1px solid #cdcdcd;
border-radius: 5px;
padding: 12px;
}
#wpacu-debug-options p {
margin-bottom: 15px;
}
#wpacu-debug-options ul.wpacu-options {
list-style: none;
padding-left: 0;
margin-top: 0;
margin-left: 8px;
}
#wpacu-debug-options ul.wpacu-options li {
line-height: normal;
font-size: inherit;
}
#wpacu-debug-options ul.wpacu-options li label {
cursor: pointer;
font-size: inherit;
}
#wpacu-debug-options table td {
padding: 20px;
}
ul#wpacu-debug-timing {
margin-left: 0;
padding-left: 0;
}
ul#wpacu-debug-timing > li {
list-style: none;
padding-left: 20px;
}
ul#wpacu-debug-timing li > ul > li {
list-style: disc;
padding-left: 0;
}
tr.wpacu_plugin_row_debug_unload.wpacu_plugin_row_debug_unload_marked td {
background: #ffe1e1 !important;
}
/* The container */
.wpacu_plugin_unload_debug_container {
display: block;
position: relative;
cursor: pointer;
font-size: 22px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Hide the browser's default checkbox */
.wpacu_plugin_unload_debug_container input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Create a custom checkbox */
.wpacu_plugin_unload_debug_checkbox_checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
background-color: #eee;
}
/* On mouse-over, add a grey background color */
.wpacu_plugin_unload_debug_container:hover input ~ .wpacu_plugin_unload_debug_checkbox_checkmark {
background-color: #ccc;
}
/* When the checkbox is checked, add a blue background */
.wpacu_plugin_unload_debug_container input:checked ~ .wpacu_plugin_unload_debug_checkbox_checkmark {
background-color: #cc0000;
}
/* Create the checkmark/indicator (hidden when not checked) */
.wpacu_plugin_unload_debug_container .wpacu_plugin_unload_debug_checkbox_checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the checkmark when checked */
.wpacu_plugin_unload_debug_container input:checked ~ .wpacu_plugin_unload_debug_checkbox_checkmark:after {
display: block;
}
/* Style the checkmark/indicator */
.wpacu_plugin_unload_debug_container .wpacu_plugin_unload_debug_checkbox_checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
/*
* [Dashboard]
*/
#wpacu-debug-admin-area {
line-height: 20px;
background: white;
padding: 15px 15px 30px;
bottom: 0;
z-index: 100000000;
width: 100%;
border-top: 1px solid #e7e7e7;
}
#wpacu-debug-plugins-list .wpacu_plugin_icon > img {
-webkit-border-radius: 3px;
/* Chrome, Safari, Opera */
-moz-border-radius: 3px;
/* Firefox */
border-radius: 3px;
}
#wpacu-debug-plugins-list .wpacu_plugin_icon > div {
background: #efefef;
border: #cdcdcd;
border-radius: 3px;
width: 20px;
height: 20px;
vertical-align: middle;
position: relative;
text-align: center;
}
#wpacu-debug-plugins-list .wpacu_plugin_icon > div > span {
font-size: 20px;
color: #b3b3b3;
top: 50%;
vertical-align: middle;
left: 50%;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
position: absolute;
}
/*
* [/Dashboard]
*/

View File

@ -0,0 +1,39 @@
/* Dashboard */
function wpacuChangeDebugAdminArea()
{
var $adminMenuWrap = jQuery('#adminmenuwrap'),
$wpacuDebugAdminArea = jQuery('#wpacu-debug-admin-area');
if ($adminMenuWrap.length > 0 && $adminMenuWrap.is(':visible')) {
$wpacuDebugAdminArea.css('margin-left', $adminMenuWrap.width() + 'px');
} else {
$wpacuDebugAdminArea.css('margin-left', '0');
}
}
if (jQuery('body.wp-admin').length > 0) {
window.addEventListener('resize', function () {
wpacuChangeDebugAdminArea();
});
jQuery(document).ready(function ($) {
wpacuChangeDebugAdminArea();
});
}
/* Front-end */
jQuery(document).ready(function($) {
var wpacuCheckboxDebugInput = '.wpacu_plugin_unload_debug_checkbox';
if ($(wpacuCheckboxDebugInput).length < 1) {
return;
}
$(wpacuCheckboxDebugInput).change(function() {
if ($(this).prop('checked')) {
$(this).parents('tr.wpacu_plugin_row_debug_unload').addClass('wpacu_plugin_row_debug_unload_marked');
} else {
$(this).parents('tr.wpacu_plugin_row_debug_unload').removeClass('wpacu_plugin_row_debug_unload_marked');
}
});
});

View File

@ -0,0 +1,257 @@
<?php
namespace WpAssetCleanUp;
/**
* Class AdminBar
* @package WpAssetCleanUp
*/
class AdminBar
{
/**
*
*/
public function __construct()
{
add_action( 'init', array( $this, 'topBar' ) );
// Hide top WordPress admin bar on request for debugging purposes and a cleared view of the tested page
// This is done in /early-triggers.php within assetCleanUpNoLoad() function
}
/**
*
*/
public function topBar()
{
if (Menu::userCanManageAssets() && (! Main::instance()->settings['hide_from_admin_bar'])) {
add_action( 'admin_bar_menu', array( $this, 'topBarInfo' ), 81 );
}
}
/**
* @param $wp_admin_bar
*/
public function topBarInfo($wp_admin_bar)
{
$topTitle = WPACU_PLUGIN_TITLE;
$wpacuUnloadedAssetsStatus = false;
if (! is_admin()) {
$markedCssListForUnload = isset(Main::instance()->allUnloadedAssets['styles']) ? array_unique(Main::instance()->allUnloadedAssets['styles']) : array();
$markedJsListForUnload = isset(Main::instance()->allUnloadedAssets['scripts']) ? array_unique(Main::instance()->allUnloadedAssets['scripts']) : array();
// [wpacu_lite]
// Do not print any irrelevant data from the Pro version such as hardcoded CSS/JS
$markedCssListForUnload = array_filter($markedCssListForUnload, function($value) {
if (strpos($value, 'wpacu_hardcoded_style_') === 0) {
return false;
}
return $value;
});
$markedJsListForUnload = array_filter($markedJsListForUnload, function($value) {
if (strpos($value, 'wpacu_hardcoded_script_') === 0) {
return false;
}
return $value;
});
// [/wpacu_lite]
$wpacuUnloadedAssetsStatus = (count($markedCssListForUnload) + count($markedJsListForUnload)) > 0;
}
if ($wpacuUnloadedAssetsStatus) {
$styleAttrType = Misc::getStyleTypeAttribute();
$cssStyle = <<<HTML
<style {$styleAttrType}>
#wpadminbar .wpacu-alert-sign-top-admin-bar {
font-size: 20px;
color: lightyellow;
vertical-align: top;
margin: -7px 0 0;
display: inline-block;
box-sizing: border-box;
}
#wp-admin-bar-assetcleanup-plugin-unload-rules-notice-default .ab-item {
min-width: 250px !important;
}
#wp-admin-bar-assetcleanup-plugin-unload-rules-notice .ab-item > .dashicons-admin-plugins {
width: 20px;
height: 20px;
font-size: 20px;
line-height: normal;
vertical-align: middle;
margin-top: -2px;
}
</style>
HTML;
$topTitle .= $cssStyle . '&nbsp;<span class="wpacu-alert-sign-top-admin-bar dashicons dashicons-filter"></span>';
}
if (Main::instance()->settings['test_mode']) {
$topTitle .= '&nbsp; <span class="dashicons dashicons-admin-tools"></span> <strong>TEST MODE</strong> is <strong>ON</strong>';
}
$goBackToCurrentUrl = '&_wp_http_referer=' . urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) );
$wp_admin_bar->add_menu(array(
'id' => 'assetcleanup-parent',
'title' => $topTitle,
'href' => esc_url(admin_url('admin.php?page=' . WPACU_PLUGIN_ID . '_settings'))
));
$wp_admin_bar->add_menu(array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-settings',
'title' => __('Settings', 'wp-asset-clean-up'),
'href' => esc_url(admin_url( 'admin.php?page=' . WPACU_PLUGIN_ID . '_settings'))
));
$wp_admin_bar->add_menu( array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-clear-css-js-files-cache',
'title' => __('Clear CSS/JS Files Cache', 'wp-asset-clean-up'),
'href' => esc_url(wp_nonce_url( admin_url( 'admin-post.php?action=assetcleanup_clear_assets_cache' . $goBackToCurrentUrl ), 'assetcleanup_clear_assets_cache' ))
) );
// Only trigger in the front-end view
if (! is_admin()) {
if ( ! Misc::isHomePage() ) {
// Not on the home page
$homepageManageAssetsHref = Main::instance()->frontendShow()
? get_site_url().'#wpacu_wrap_assets'
: esc_url(admin_url( 'admin.php?page=' . WPACU_PLUGIN_ID . '_assets_manager&wpacu_for=homepage' ));
$wp_admin_bar->add_menu(array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-homepage',
'title' => esc_html__('Manage Homepage Assets', 'wp-asset-clean-up'),
'href' => $homepageManageAssetsHref
));
} else {
// On the home page
// Front-end view is disabled! Go to Dashboard link
if ( ! Main::instance()->frontendShow() ) {
$wp_admin_bar->add_menu( array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-homepage',
'title' => esc_html__('Manage Homepage Assets', 'wp-asset-clean-up'),
'href' => esc_url(admin_url('admin.php?page=' . WPACU_PLUGIN_ID . '_assets_manager&wpacu_for=homepage')),
'meta' => array('target' => '_blank')
) );
}
}
}
if (! is_admin()) {
if (Main::instance()->frontendShow()) {
$wp_admin_bar->add_menu( array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-jump-to-assets-list',
// language: alias of 'Manage Page Assets'
'title' => esc_html__( 'Manage Current Page Assets', 'wp-asset-clean-up' ) . '&nbsp;<span style="vertical-align: sub;" class="dashicons dashicons-arrow-down-alt"></span>',
'href' => '#wpacu_wrap_assets'
) );
} elseif (is_singular()) {
global $post;
if (isset($post->ID)) {
$wp_admin_bar->add_menu( array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-manage-page-assets-dashboard',
// language: alias of 'Manage Page Assets'
'title' => esc_html__('Manage Current Page Assets', 'wp-asset-clean-up'),
'href' => esc_url(admin_url('admin.php?page=' . WPACU_PLUGIN_ID . '_assets_manager&wpacu_post_id='.$post->ID)),
'meta' => array('target' => '_blank')
) );
}
}
}
$wp_admin_bar->add_menu(array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-bulk-unloaded',
'title' => esc_html__('Bulk Changes', 'wp-asset-clean-up'),
'href' => esc_url(admin_url( 'admin.php?page=' . WPACU_PLUGIN_ID . '_bulk_unloads'))
));
$wp_admin_bar->add_menu( array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-overview',
'title' => esc_html__('Overview', 'wp-asset-clean-up'),
'href' => esc_url(admin_url( 'admin.php?page=' . WPACU_PLUGIN_ID . '_overview'))
) );
$wp_admin_bar->add_menu(array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-support-forum',
'title' => esc_html__('Support Forum', 'wp-asset-clean-up'),
'href' => 'https://wordpress.org/support/plugin/wp-asset-clean-up',
'meta' => array('target' => '_blank')
));
// [START LISTING UNLOADED ASSETS]
if (! is_admin()) { // Frontend view (show any unloaded handles)
$totalUnloadedAssets = count($markedCssListForUnload) + count($markedJsListForUnload);
if ($totalUnloadedAssets > 0) {
$titleUnloadText = sprintf( _n( '%d unload asset rules took effect on this frontend page',
'%d unload asset rules took effect on this frontend page', $totalUnloadedAssets, 'wp-asset-clean-up' ),
$totalUnloadedAssets );
$wp_admin_bar->add_menu( array(
'parent' => 'assetcleanup-parent',
'id' => 'assetcleanup-asset-unload-rules-notice',
'title' => '<span style="margin: -10px 0 0;" class="wpacu-alert-sign-top-admin-bar dashicons dashicons-filter"></span> &nbsp; '. $titleUnloadText,
'href' => '#'
) );
if ( count( $markedCssListForUnload ) > 0 ) {
$wp_admin_bar->add_menu(array(
'parent' => 'assetcleanup-asset-unload-rules-notice',
'id' => 'assetcleanup-asset-unload-rules-css',
'title' => esc_html__('CSS', 'wp-asset-clean-up'). ' ('.count( $markedCssListForUnload ).')',
'href' => '#'
));
sort($markedCssListForUnload);
foreach ($markedCssListForUnload as $cssHandle) {
$wp_admin_bar->add_menu(array(
'parent' => 'assetcleanup-asset-unload-rules-css',
'id' => 'assetcleanup-asset-unload-rules-css-'.$cssHandle,
'title' => $cssHandle,
'href' => esc_url(admin_url('admin.php?page=wpassetcleanup_overview#wpacu-overview-css-'.$cssHandle))
));
}
}
if ( count( $markedJsListForUnload ) > 0 ) {
$wp_admin_bar->add_menu(array(
'parent' => 'assetcleanup-asset-unload-rules-notice',
'id' => 'assetcleanup-asset-unload-rules-js',
'title' => esc_html__('JavaScript', 'wp-asset-clean-up'). ' ('.count( $markedJsListForUnload ).')',
'href' => '#'
));
sort($markedJsListForUnload);
foreach ($markedJsListForUnload as $jsHandle) {
$wp_admin_bar->add_menu(array(
'parent' => 'assetcleanup-asset-unload-rules-js',
'id' => 'assetcleanup-asset-unload-rules-js-'.$jsHandle,
'title' => $jsHandle,
'href' => esc_url(admin_url('admin.php?page=wpassetcleanup_overview#wpacu-overview-js-'.$jsHandle))
));
}
}
}
}
// [END LISTING UNLOADED ASSETS]
}
}

View File

@ -0,0 +1,240 @@
<?php
namespace WpAssetCleanUp;
/**
* Class AjaxSearchAutocomplete
* @package WpAssetCleanUp
*/
class AjaxSearchAutocomplete
{
/**
* AjaxSearchAutocomplete constructor.
*/
public function __construct()
{
add_action('admin_enqueue_scripts', array($this, 'enqueueScripts'));
add_action('wp_ajax_' . WPACU_PLUGIN_ID . '_autocomplete_search', array($this, 'wpAjaxSearch'));
self::maybePreventWpmlPluginFromFiltering();
}
/**
* "WPML Multilingual CMS" prevents the AJAX loader from "Load assets manager for:" from loading the results as they are
* If a specific ID is put there, the post with that ID should be returned and not one of its translated posts with a different ID
*
* @return void
*/
public static function maybePreventWpmlPluginFromFiltering()
{
if ( ! (isset($_REQUEST['action'], $_REQUEST['wpacu_term'], $GLOBALS['sitepress']) &&
$_REQUEST['action'] === WPACU_PLUGIN_ID . '_autocomplete_search' &&
$_REQUEST['wpacu_term'] &&
Misc::isPluginActive('sitepress-multilingual-cms/sitepress.php')) ) {
return;
}
// This is called before "WPML Multilingual CMS" loads as we need to avoid any filtering of the search results
// to avoid confusing the admin when managing the assets within "CSS & JS Manager" -- "Manage CSS/JS"
// Avoid retrieving the wrong (language related) post ID and title
global $sitepress;
remove_action( 'parse_query', array( $sitepress, 'parse_query' ) );
// Avoid retrieving the wrong (language related) permalink
global $wp_filter;
if ( ! isset( $wp_filter['page_link']->callbacks ) ) {
return;
}
foreach ( $wp_filter['page_link']->callbacks as $key => $values ) {
if ( ! empty( $wp_filter['page_link']->callbacks ) ) {
foreach ( $values as $values2 ) {
if ( isset( $values2['function'][0] ) && $values2['function'][0] instanceof \WPML_URL_Filters ) {
unset( $wp_filter['page_link']->callbacks[ $key ] );
}
}
}
}
}
/**
* Only valid for "CSS & JS Manager" -- "Manage CSS/JS" -- ("Posts" | "Pages" | "Custom Post Types" | "Media")
*/
public function enqueueScripts()
{
if (! isset($_REQUEST['wpacu_for'])) {
return;
}
$isManageCssJsDash = isset($_GET['page']) && $_GET['page'] === WPACU_PLUGIN_ID.'_assets_manager';
$subPage = isset($_GET['wpacu_sub_page']) ? $_GET['wpacu_sub_page'] : 'manage_css_js';
$loadAutoCompleteOnManageCssJsDash = ($isManageCssJsDash && $subPage === 'manage_css_js') &&
in_array($_REQUEST['wpacu_for'], array('posts', 'pages', 'media-attachment', 'custom-post-types'));
if ( ! $loadAutoCompleteOnManageCssJsDash ) {
return;
}
$wpacuFor = sanitize_text_field($_REQUEST['wpacu_for']);
switch ($wpacuFor) {
case 'posts':
$forPostType = 'post';
break;
case 'pages':
$forPostType = 'page';
break;
case 'media-attachment':
$forPostType = 'attachment';
break;
case 'custom-post-types':
$forPostType = 'wpacu-custom-post-types';
break;
default:
$forPostType = '';
}
if ( ! $forPostType ) {
return;
}
wp_enqueue_script(
OwnAssets::$ownAssets['scripts']['autocomplete_search']['handle'],
plugins_url(OwnAssets::$ownAssets['scripts']['autocomplete_search']['rel_path'], WPACU_PLUGIN_FILE),
array('jquery', 'jquery-ui-autocomplete'),
OwnAssets::assetVer(OwnAssets::$ownAssets['scripts']['autocomplete_search']['rel_path']),
true
);
wp_localize_script(OwnAssets::$ownAssets['scripts']['autocomplete_search']['handle'], 'wpacu_autocomplete_search_obj', array(
'ajax_url' => esc_url(admin_url('admin-ajax.php')),
'ajax_nonce' => wp_create_nonce('wpacu_autocomplete_search_nonce'),
'ajax_action' => WPACU_PLUGIN_ID . '_autocomplete_search',
'post_type' => $forPostType,
'redirect_to' => esc_url(admin_url('admin.php?page=wpassetcleanup_assets_manager&wpacu_for='.$wpacuFor.'&wpacu_post_id=post_id_here'))
));
wp_enqueue_style(
OwnAssets::$ownAssets['styles']['autocomplete_search_jquery_ui_custom']['handle'],
plugins_url(OwnAssets::$ownAssets['styles']['autocomplete_search_jquery_ui_custom']['rel_path'], WPACU_PLUGIN_FILE),
false, null, false
);
$jqueryUiCustom = <<<CSS
#wpacu-search-form-assets-manager input[type=text].ui-autocomplete-loading {
background-position: 99% 6px;
}
CSS;
wp_add_inline_style(OwnAssets::$ownAssets['styles']['autocomplete_search_jquery_ui_custom']['handle'], $jqueryUiCustom);
}
/**
*
*/
public function wpAjaxSearch()
{
check_ajax_referer('wpacu_autocomplete_search_nonce', 'wpacu_security');
global $wpdb;
$search_term = isset($_REQUEST['wpacu_term']) ? sanitize_text_field($_REQUEST['wpacu_term']) : '';
$post_type = isset($_REQUEST['wpacu_post_type']) ? sanitize_text_field($_REQUEST['wpacu_post_type']) : '';
if ( $search_term === '' ) {
echo wp_json_encode(array());
}
$results = array();
if ($post_type !== 'attachment') {
// 'post', 'page', custom post types
$queryDataByKeyword = array(
'post_type' => $post_type,
's' => $search_term,
'post_status' => array( 'publish', 'private' ),
'posts_per_page' => -1,
'suppress_filters' => true
);
} else {
// 'attachment'
$search = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT ID FROM {$wpdb->posts} WHERE post_title = '%s'", $search_term ) );
$queryDataByKeyword = array(
'post_type' => 'attachment',
'post_status' => 'inherit',
'orderby' => 'date',
'order' => 'DESC',
'post__in' => $search,
'suppress_filters' => true
);
}
// Standard search
$query = new \WP_Query($queryDataByKeyword);
// No results? Search by ID in case the admin put the post/page ID in the search box
if (! $query->have_posts()) {
// This one works for any post type, including 'attachment'
$queryDataByID = array(
'post_type' => $post_type,
'p' => $search_term,
'post_status' => array( 'publish', 'private' ),
'posts_per_page' => -1,
'post__in' => $search_term,
'suppress_filters' => true
);
$query = new \WP_Query($queryDataByID);
}
if ($query->have_posts()) {
$pageOnFront = $pageForPosts = false;
if ($post_type === 'page' && get_option('show_on_front') === 'page') {
$pageOnFront = (int)get_option('page_on_front');
$pageForPosts = (int)get_option('page_for_posts');
}
while ($query->have_posts()) {
$query->the_post();
$resultPostId = get_the_ID();
$resultPostStatus = get_post_status($resultPostId);
$resultToShow = get_the_title() . ' / ID: '.$resultPostId;
if ($resultPostStatus === 'private') {
$iconPrivate = '<span class="dashicons dashicons-lock"></span>';
$resultToShow .= ' / '.$iconPrivate.' Private';
}
// This is a page, and it was set as the homepage (point this out)
if ($pageOnFront === $resultPostId) {
$iconHome = '<span class="dashicons dashicons-admin-home"></span>';
$resultToShow .= ' / '.$iconHome.' Homepage';
}
if ($pageForPosts === $resultPostId) {
$iconPost = '<span class="dashicons dashicons-admin-post"></span>';
$resultToShow .= ' / '.$iconPost.' Posts page';
}
$results[] = array(
'id' => $resultPostId,
'label' => $resultToShow,
'link' => get_the_permalink()
);
}
wp_reset_postdata();
}
if (empty($results)) {
echo 'no_results';
wp_die();
}
echo wp_json_encode($results);
wp_die();
}
}

View File

@ -0,0 +1,178 @@
<?php
namespace WpAssetCleanUp;
/**
* Class AssetsPagesManager
* @package WpAssetCleanUp
*
* Actions taken within the Dashboard, inside the plugin area: "CSS & JS MANAGER" (main top menu) -- "MANAGE CSS/JS" (main tab)
*/
class AssetsPagesManager
{
/**
* @var array
*/
public $data = array();
/**
* AssetsPagesManager constructor.
*/
public function __construct()
{
if ( Misc::getVar('get', 'page') !== WPACU_PLUGIN_ID . '_assets_manager' ) {
return;
}
$wpacuSubPage = (isset($_GET['wpacu_sub_page']) && $_GET['wpacu_sub_page']) ? $_GET['wpacu_sub_page'] : 'manage_css_js';
$this->data = array(
'for' => 'homepage', // default
'nonce_action' => WPACU_PLUGIN_ID . '_dash_assets_page_update_nonce_action',
'nonce_name' => WPACU_PLUGIN_ID . '_dash_assets_page_update_nonce_name'
);
$this->data['site_url'] = get_site_url();
if (isset($_GET['wpacu_for']) && $_GET['wpacu_for'] !== '') {
$this->data['for'] = sanitize_text_field($_GET['wpacu_for']);
}
$this->data['wpacu_post_id'] = (isset($_GET['wpacu_post_id']) && $_GET['wpacu_post_id']) ? (int)$_GET['wpacu_post_id'] : false;
if ($this->data['wpacu_post_id'] && $this->data['for'] === 'homepage') {
// URI is like: /wp-admin/admin.php?page=wpassetcleanup_assets_manager&wpacu_post_id=POST_ID_HERE (without any "wpacu_for")
// Proceed to detect the post type
global $wpdb;
$query = $wpdb->prepare("SELECT `post_type` FROM `{$wpdb->posts}` WHERE `ID`='%d'", $this->data['wpacu_post_id']);
$requestedPostType = $wpdb->get_var($query);
if ($requestedPostType === 'post') {
$this->data['for'] = 'posts';
} elseif ($requestedPostType === 'page') {
$this->data['for'] = 'posts';
} elseif ($requestedPostType === 'attachment') {
$this->data['for'] = 'media-attachment';
} elseif ($requestedPostType !== '') {
$this->data['for'] = 'custom-post-types';
}
}
if (Menu::isPluginPage()) {
$this->data['page'] = sanitize_text_field($_GET['page']);
}
$wpacuSettings = new Settings;
$this->data['wpacu_settings'] = $wpacuSettings->getAll();
$this->data['show_on_front'] = Misc::getShowOnFront();
if ($wpacuSubPage === 'manage_css_js' && in_array($this->data['for'], array('homepage', 'pages', 'posts', 'custom-post-types', 'media-attachment'))) {
Misc::w3TotalCacheFlushObjectCache();
// Front page displays: A Static Page
if ($this->data['for'] === 'homepage' && $this->data['show_on_front'] === 'page') {
$this->data['page_on_front'] = get_option('page_on_front');
if ($this->data['page_on_front']) {
$this->data['page_on_front_title'] = get_the_title($this->data['page_on_front']);
}
$this->data['page_for_posts'] = get_option('page_for_posts');
if ($this->data['page_for_posts']) {
$this->data['page_for_posts_title'] = get_the_title($this->data['page_for_posts']);
}
}
// e.g. It could be the homepage tab loading a singular page set as the homepage in "Settings" -> "Reading"
$anyPostId = (int)Misc::getVar('post', 'wpacu_manage_singular_page_id');
if ($this->data['for'] === 'homepage' && ! $anyPostId) {
// "CSS & JS MANAGER" -- "Homepage" (e.g. "Your homepage displays" set as "Your latest posts")
$this->homepageActions();
} else {
// "CSS & JS MANAGER" --> "MANAGE CSS/JS"
// Case 1: "Homepage", if singular page set as the homepage in "Settings" -> "Reading")
// Case 2: "Posts"
// Case 3: "Pages"
// Case 4: "Custom Post Types" (e.g. WooCommerce product)
// Case 5: "Media" (attachment pages, rarely used)
$this->singularPageActions();
}
}
}
/**
*
*/
public function homepageActions()
{
// Only continue if we are on the plugin's homepage edit mode
if ( ! ( $this->data['for'] === 'homepage' && Misc::getVar('get', 'page') === WPACU_PLUGIN_ID . '_assets_manager' ) ) {
return;
}
// Update action?
if (! empty($_POST) && Misc::getVar( 'post', 'wpacu_manage_home_page_assets', false ) ) {
$wpacuNoLoadAssets = Misc::getVar( 'post', WPACU_PLUGIN_ID, array() );
$wpacuUpdate = new Update;
if ( ! (isset($_REQUEST[$this->data['nonce_name']])
&& wp_verify_nonce($_REQUEST[$this->data['nonce_name']], $this->data['nonce_action'])) ) {
add_action('wpacu_admin_notices', array($wpacuUpdate, 'changesNotMadeInvalidNonce'));
return;
}
// All good with the nonce? Do the changes!
$wpacuUpdate->updateFrontPage( $wpacuNoLoadAssets );
}
}
/**
* Any post type, including the custom ones
*/
public function singularPageActions()
{
$postId = (int)Misc::getVar('post', 'wpacu_manage_singular_page_id');
$isSingularPageEdit = $postId > 0 &&
( Misc::getVar('get', 'page') === WPACU_PLUGIN_ID . '_assets_manager' &&
in_array( $this->data['for'], array('homepage', 'pages', 'posts', 'custom-post-types', 'media-attachment' ) ) );
// Only continue if the form was submitted for a singular page
// e.g. a post, a page (could be the homepage), a WooCommerce product page, any public custom post type
if (! $isSingularPageEdit) {
return;
}
if (! empty($_POST)) {
// Update action?
$wpacuNoLoadAssets = Misc::getVar( 'post', WPACU_PLUGIN_ID, array() );
$wpacuSingularPageUpdate = Misc::getVar( 'post', 'wpacu_manage_singular_page_assets', false );
// Could Be an Empty Array as Well so just is_array() is enough to use
if ( is_array( $wpacuNoLoadAssets ) && $wpacuSingularPageUpdate ) {
$wpacuUpdate = new Update;
if ( ! (isset($_REQUEST[$this->data['nonce_name']])
&& wp_verify_nonce($_REQUEST[$this->data['nonce_name']], $this->data['nonce_action'])) ) {
add_action('wpacu_admin_notices', array($wpacuUpdate, 'changesNotMadeInvalidNonce'));
return;
}
if ($postId > 0) {
$wpacuUpdate = new Update;
$wpacuUpdate->savePosts($postId);
}
}
}
}
/**
* Called in Menu.php (within "admin_menu" hook via "activeMenu" method)
*/
public function renderPage()
{
Main::instance()->parseTemplate('admin-page-assets-manager', $this->data, true);
}
}

View File

@ -0,0 +1,218 @@
<?php
namespace WpAssetCleanUp;
/**
*
* Class BulkChanges
* @package WpAssetCleanUp
*/
class BulkChanges
{
/**
* @var string
*/
public $wpacuFor = 'everywhere';
/**
* @var string
*/
public $wpacuPostType = 'post';
/**
* @var array
*/
public $data = array();
/**
* Includes bulk unload rules, RegEx unloads & load exceptions
*
* BulkChanges constructor.
*/
public function __construct()
{
$this->wpacuFor = sanitize_text_field(Misc::getVar('request', 'wpacu_for', $this->wpacuFor));
$this->wpacuPostType = sanitize_text_field(Misc::getVar('request', 'wpacu_post_type', $this->wpacuPostType));
if (Misc::getVar('request', 'wpacu_update') == 1) {
$this->update();
}
}
/**
* @return array
*/
public function getCount()
{
$values = array();
if ($this->wpacuFor === 'everywhere') {
$values = Main::instance()->getGlobalUnload();
} elseif ($this->wpacuFor === 'post_types') {
$values = Main::instance()->getBulkUnload('post_type', $this->wpacuPostType);
}
if (isset($values['styles']) && ! empty($values['styles'])) {
sort($values['styles']);
}
if (isset($values['scripts']) && ! empty($values['scripts'])) {
sort($values['scripts']);
}
return $values;
}
/**
*
*/
public function pageBulkUnloads()
{
$this->data['assets_info'] = Main::getHandlesInfo();
$this->data['for'] = $this->wpacuFor;
if ($this->wpacuFor === 'post_types') {
$this->data['post_type'] = $this->wpacuPostType;
// Get All Post Types
$postTypes = get_post_types(array('public' => true));
$this->data['post_types_list'] = Misc::filterPostTypesList( $postTypes );
}
$this->data['values'] = $this->getCount();
$this->data['nonce_name'] = Update::NONCE_FIELD_NAME;
$this->data['nonce_action'] = Update::NONCE_ACTION_NAME;
$this->data['plugin_settings'] = Main::instance()->settings;
Main::instance()->parseTemplate('admin-page-settings-bulk-changes', $this->data, true);
}
/**
* @param $postTypes
*
* @return mixed
*/
public function filterPostTypesList($postTypes)
{
foreach ($postTypes as $postTypeKey => $postTypeValue) {
// Exclude irrelevant custom post types
if (in_array($postTypeKey, MetaBoxes::$noMetaBoxesForPostTypes)) {
unset($postTypes[$postTypeKey]);
}
// Polish existing values
if ($postTypeKey === 'product' && Misc::isPluginActive('woocommerce/woocommerce.php')) {
$postTypes[$postTypeKey] = 'product &#10230; WooCommerce';
}
}
return $postTypes;
}
/**
* @param $postTypesList
* @param $currentPostType
*/
public static function buildPostTypesListDd($postTypesList, $currentPostType)
{
$ddList = array();
foreach ($postTypesList as $postTypeKey => $postTypeValue) {
if (in_array($postTypeKey, array('post', 'page', 'attachment'))) {
$ddList['WordPress (default)'][$postTypeKey] = $postTypeValue;
} else {
$ddList['Custom Post Types (Singular pages)'][$postTypeKey] = $postTypeValue;
$list = Main::instance()->getBulkUnload('custom_post_type_archive_'.$postTypeKey);
// At least one of the buckets ('styles' or 'scripts') needs to contain something
if (! empty($list['styles']) || ! empty($list['scripts'])) {
$ddList['Custom Post Types (Archive pages)'][ 'wpacu_custom_post_type_archive_'.$postTypeKey ] = $postTypeValue. ' (archive page)';
}
}
}
?>
<select id="wpacu_post_type_select" name="wpacu_post_type">
<?php
foreach ($ddList as $groupLabel => $groupPostTypesList) {
echo '<optgroup label="'.$groupLabel.'">';
foreach ($groupPostTypesList as $postTypeKey => $postTypeValue) {
?>
<option <?php if ($currentPostType === $postTypeKey) { echo 'selected="selected"'; } ?> value="<?php echo esc_attr($postTypeKey); ?>"><?php echo esc_html($postTypeValue); ?></option>
<?php
}
echo '</optgroup>';
}
?>
</select>
<?php
}
/**
*
*/
public function update()
{
if ( ! Misc::getVar('post', 'wpacu_bulk_unloads_update_nonce') ) {
return;
}
check_admin_referer('wpacu_bulk_unloads_update', 'wpacu_bulk_unloads_update_nonce');
$wpacuUpdate = new Update;
if ($this->wpacuFor === 'everywhere') {
$removed = $wpacuUpdate->removeEverywhereUnloads(array(), array(), 'post');
if ($removed) {
add_action('wpacu_admin_notices', array($this, 'noticeGlobalsRemoved'));
}
}
if ($this->wpacuFor === 'post_types') {
$removed = $wpacuUpdate->removeBulkUnloads($this->wpacuPostType);
if ($removed) {
add_action('wpacu_admin_notices', array($this, 'noticePostTypesRemoved'));
}
}
}
/**
*
*/
public function noticeGlobalsRemoved()
{
?>
<div class="updated notice wpacu-notice is-dismissible">
<p><span class="dashicons dashicons-yes"></span>
<?php
_e('The selected styles/scripts were removed from the global unload list and they will now load in the pages/posts, unless you have other rules that would prevent them from loading.', 'wp-asset-clean-up');
?>
</p>
</div>
<?php
}
/**
*
*/
public function noticePostTypesRemoved()
{
?>
<div class="updated notice wpacu-notice is-dismissible">
<p><span class="dashicons dashicons-yes"></span>
<?php
echo sprintf(
__('The selected styles/scripts were removed from the unload list for <strong><u>%s</u></strong> post type and they will now load in the pages/posts, unless you have other rules that would prevent them from loading.', 'wp-asset-clean-up'),
$this->wpacuPostType
);
?>
</p>
</div>
<?php
}
}

View File

@ -0,0 +1,629 @@
<?php
namespace WpAssetCleanUp;
use WpAssetCleanUp\OptimiseAssets\OptimizeCommon;
/**
* Class CleanUp
* @package WpAssetCleanUp
*/
class CleanUp
{
/**
*
*/
public function init()
{
// Is "Test Mode" is enabled and the page is viewed by a regular visitor (not administrator with plugin activation privileges)?
// Stop here as the script will NOT PREVENT any of the elements below to load
// They will load as they used to for the regular visitor while the admin debugs the website
add_action('init', array($this, 'doClean'), 12);
}
/**
*
*/
public function doClean()
{
if ( ! method_exists( '\WpAssetCleanUp\Plugin', 'preventAnyFrontendOptimization' ) ) {
return; // something's funny as, for some reason, on very rare occasions, the class is not found, so don't continue
}
if ( (defined('WPACU_ALLOW_ONLY_UNLOAD_RULES') && WPACU_ALLOW_ONLY_UNLOAD_RULES) || Main::instance()->preventAssetsSettings() || Plugin::preventAnyFrontendOptimization() ) {
return;
}
$settings = Main::instance()->settings;
// Remove "Really Simple Discovery (RSD)" link?
if ($settings['remove_rsd_link'] == 1 || $settings['disable_xmlrpc'] === 'disable_all') {
// <link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://yourwebsite.com/xmlrpc.php?rsd" />
remove_action('wp_head', 'rsd_link');
}
// Remove "Windows Live Writer" link?
if ($settings['remove_wlw_link'] == 1) {
// <link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://yourwebsite.com/wp-includes/wlwmanifest.xml">
remove_action('wp_head', 'wlwmanifest_link');
}
// Remove "REST API" link?
if ($settings['remove_rest_api_link'] == 1) {
// <link rel='https://api.w.org/' href='https://yourwebsite.com/wp-json/' />
remove_action('wp_head', 'rest_output_link_wp_head');
// Removes the following printed within "Response headers":
// <https://yourwebsite.com/wp-json/>; rel="https://api.w.org/"
remove_action( 'template_redirect', 'rest_output_link_header', 11 );
}
// Remove "Shortlink"?
if ($settings['remove_shortlink'] == 1) {
// <link rel='shortlink' href="https://yourdomain.com/?p=1">
remove_action('wp_head', 'wp_shortlink_wp_head');
// link: <https://yourdomainname.com/wp-json/>; rel="https://api.w.org/", <https://yourdomainname.com/?p=[post_id_here]>; rel=shortlink
remove_action('template_redirect', 'wp_shortlink_header', 11);
}
// Remove "Post's Relational Links"?
if ($settings['remove_posts_rel_links'] == 1) {
// <link rel='prev' title='Title of adjacent post' href='https://yourdomain.com/adjacent-post-slug-here/' />
remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
}
// Remove "WordPress version" tag?
if ($settings['remove_wp_version']) {
// <meta name="generator" content="WordPress 4.9.8" />
remove_action('wp_head', 'wp_generator');
// also hide it from RSS
add_filter('the_generator', '__return_false');
}
if ($settings['disable_rss_feed']) {
$this->doDisableRssFeed();
}
// Remove Main RSS Feed Link?
if ($settings['remove_main_feed_link']) {
add_filter('feed_links_show_posts_feed', '__return_false');
remove_action('wp_head', 'feed_links_extra', 3);
}
// Remove Comment RSS Feed Link?
if ($settings['remove_comment_feed_link']) {
add_filter('feed_links_show_comments_feed', '__return_false');
}
// Disable XML-RPC protocol support (partially or completely)
if (in_array($settings['disable_xmlrpc'], array('disable_all', 'disable_pingback'))) {
// Partially or Completely Options / Pingback will be disabled
$this->disableXmlRpcPingback();
// Complete disable the service
if ($settings['disable_xmlrpc'] === 'disable_all') {
add_filter('xmlrpc_enabled', '__return_false');
}
}
}
/**
* Called in OptimiseAssets/OptimizeCommon.php
*
* @param $htmlSource
*
* @return string|string[]|null
*/
public static function cleanPingbackLinkRel($htmlSource)
{
$pingBackUrl = get_bloginfo('pingback_url');
$matchRegExps = array(
'#<link rel=("|\')pingback("|\') href=("|\')'.$pingBackUrl.'("|\')( /|)>#',
'#<link href=("|\')'.$pingBackUrl.'("|\') rel=("|\')pingback("|\')( /|)>#'
);
foreach ($matchRegExps as $matchRegExp) {
$htmlSource = preg_replace($matchRegExp, '', $htmlSource);
}
return $htmlSource;
}
/**
*
*/
public function disableXmlRpcPingback()
{
// Disable Pingback method
add_filter('xmlrpc_methods', static function ($methods) {
unset($methods['pingback.ping'], $methods['pingback.extensions.getPingbacks']);
return $methods;
} );
// Remove X-Pingback HTTP header
add_filter('wp_headers', static function ($headers) {
unset($headers['X-Pingback']);
return $headers;
});
}
/**
* @param $htmlSource
*
* @return mixed
*/
public static function removeMetaGenerators($htmlSource)
{
$fetchMethod = 'dom'; // 'regex' or 'dom'
if ( $fetchMethod === 'dom' && Misc::isDOMDocumentOn() && $htmlSource ) {
$domTag = OptimizeCommon::getDomLoadedTag($htmlSource, 'removeMetaGenerators');
$metaTagsToStrip = array();
foreach ( $domTag->getElementsByTagName( 'meta' ) as $tagObject ) {
$nameAttrValue = $tagObject->getAttribute( 'name' );
if ( $nameAttrValue === 'generator' ) {
$outerTag = $outerTagRegExp = trim( Misc::getOuterHTML( $tagObject ) );
// As DOMDocument doesn't retrieve the exact string, some alterations to the RegEx have to be made
// Leave no room for errors as all sort of characters can be within the "content" attribute
$last2Chars = substr( $outerTag, - 2 );
if ( $last2Chars === '">' || $last2Chars === "'>" ) {
$tagWithoutLastChar = substr( $outerTag, 0, - 1 );
$outerTagRegExp = preg_quote( $tagWithoutLastChar, '/' ) . '(.*?)>';
}
$outerTagRegExp = str_replace(
array( '"', '&lt;', '&gt;' ),
array( '("|\'|)', '(<|&lt;)', '(>|&gt;)' ),
$outerTagRegExp
);
if ( strpos( $outerTagRegExp, '<meta' ) !== false ) {
$outerTagRegExp = str_replace('#', '\#', $outerTagRegExp);
preg_match_all( '#' . $outerTagRegExp . '#si', $htmlSource, $matches );
if ( isset( $matches[0][0] ) && ! empty( $matches[0][0] ) && strip_tags( $matches[0][0] ) === '' ) {
$metaTagsToStrip[$matches[0][0]] = '';
}
}
}
}
$htmlSource = strtr($htmlSource, $metaTagsToStrip);
libxml_clear_errors();
}
/* [wpacu_timing] */ Misc::scriptExecTimer( 'alter_html_source_for_remove_meta_generators', 'end' ); /* [/wpacu_timing] */
return $htmlSource;
}
/**
* @param $htmlSource
* @param bool $ignoreExceptions
* @param $for
*
* @return string|string[]
*/
public static function removeHtmlComments($htmlSource, $ignoreExceptions = false)
{
if ( strpos($htmlSource, '<!--') === false || ! Misc::isDOMDocumentOn() ) {
return $htmlSource;
}
$domTag = OptimizeCommon::getDomLoadedTag($htmlSource, 'removeHtmlComments');
if (! $domTag) {
return $htmlSource;
}
$xpathComments = new \DOMXPath($domTag);
$comments = $xpathComments->query('//comment()');
libxml_clear_errors();
if ($comments === null) {
return $htmlSource;
}
preg_match_all('#<!--(.*?)-->#s', $htmlSource, $matchesRegExpComments);
// "comments" within tag attributes or script tags?
// e.g. <script>var type='<!-- A comment here -->';</script>
// e.g. <div data-info="This is just a <!-- comment --> text">Content here</div>
$commentsWithinQuotes = array();
if (isset($matchesRegExpComments[1]) && count($matchesRegExpComments[1]) !== count($comments)) {
preg_match_all('#=(|\s+)([\'"])(|\s+)<!--(.*?)-->(|\s+)([\'"])#s', $htmlSource, $matchesCommentsWithinQuotes);
if (isset($matchesCommentsWithinQuotes[0]) && ! empty($matchesCommentsWithinQuotes[0])) {
foreach ($matchesCommentsWithinQuotes[0] as $matchedDataOriginal) {
$matchedDataUpdated = str_replace(
array('', '<!--', '-->'),
array('--wpacu-space-del--', '--wpacu-start-comm--', '--wpacu-end-comm--'),
$matchedDataOriginal
);
$htmlSource = str_replace($matchedDataOriginal, $matchedDataUpdated, $htmlSource);
$commentsWithinQuotes[] = array(
'original' => $matchedDataOriginal,
'updated' => $matchedDataUpdated
);
}
}
}
$stripCommentsList = array();
foreach ($comments as $comment) {
$entireComment = '<!--' . $comment->nodeValue . '-->';
// Do not strip MSIE conditional comments
if ( strpos( $entireComment, '<!--<![endif]-->' ) !== false ||
preg_match( '#<!--\[if(.*?)]>(.*?)<!-->#si', $entireComment ) ||
preg_match( '#<!--\[if(.*?)\[endif]-->#si', $entireComment ) ) {
continue;
}
// Any exceptions set in "Strip HTML comments?" textarea?
// $ignoreExceptions has to be set to false (as it is by default)
if (! $ignoreExceptions && Main::instance()->settings['remove_html_comments_exceptions']) {
$removeHtmlCommentsExceptions = trim(Main::instance()->settings['remove_html_comments_exceptions']);
if (strpos($removeHtmlCommentsExceptions, "\n") !== false) {
foreach (explode("\n", $removeHtmlCommentsExceptions) as $removeCommExceptionPattern) {
$removeCommExceptionPattern = trim($removeCommExceptionPattern);
if (stripos($entireComment, $removeCommExceptionPattern) !== false) {
continue 2;
}
}
} elseif (stripos($entireComment, $removeHtmlCommentsExceptions) !== false) {
continue;
}
}
if (strlen($entireComment) < 200) {
$stripCommentsList[ $entireComment ] = '';
} else {
$htmlSource = str_replace($entireComment, '', $htmlSource);
}
}
if (! empty($stripCommentsList)) {
$htmlSource = strtr( $htmlSource, $stripCommentsList );
}
if (! empty($commentsWithinQuotes)) {
foreach ($commentsWithinQuotes as $commentQuote) {
$htmlSource = str_replace($commentQuote['updated'], $commentQuote['original'], $htmlSource);
}
}
return $htmlSource;
}
/**
* @param $strContains
* @param $htmlSource
*
* @return mixed
*/
public static function cleanLinkTagFromHtmlSource($strContains, $htmlSource)
{
if (! is_array($strContains)) {
$strContains = array($strContains);
}
$strContains = array_map(function($value) {
if (strpos($value, 'data-wpacu-style-handle') !== false) {
return $value; // no need to use preg-quote
}
return preg_quote($value, '/');
}, $strContains);
preg_match_all(
'#<link[^>]*('.implode('|', $strContains).')[^>].*(>)#Usmi',
$htmlSource,
$matchesSourcesFromTags
);
if (empty($matchesSourcesFromTags)) {
return $htmlSource;
}
foreach ($matchesSourcesFromTags as $matchesFromTag) {
if (! (isset($matchesFromTag[0]) && $matchesFromTag[0])) {
continue;
}
$linkTag = $matchesFromTag[0];
if (stripos($linkTag, '<link') === 0 && substr($linkTag, -1) === '>' && strip_tags($linkTag) === '') {
$htmlSource = str_replace($linkTag, '', $htmlSource);
}
}
return $htmlSource;
}
/**
* @param $strContains
* @param $htmlSource
*
* @return mixed
*/
public static function cleanScriptTagFromHtmlSource($strContains, $htmlSource)
{
if (! is_array($strContains)) {
$strContains = array($strContains);
}
$strContains = array_map(function($value) {
if (strpos($value, 'data-wpacu-script-handle') !== false) {
return $value; // no need to use preg-quote
}
return preg_quote($value, '/');
}, $strContains);
preg_match_all(
'#<script[^>]*('.implode('|', $strContains).')[^>].*(>)#Usmi',
$htmlSource,
$matchesSourcesFromTags
);
if (empty($matchesSourcesFromTags)) {
return $htmlSource;
}
foreach ($matchesSourcesFromTags as $matchesFromTag) {
if (isset($matchesFromTag[0]) && $matchesFromTag[0] && strip_tags($matchesFromTag[0]) === '') {
$htmlSource = str_replace($matchesFromTag[0] . '</script>', '', $htmlSource);
}
}
return $htmlSource;
}
/**
*
*/
public function doDisableEmojis()
{
// Emojis Actions and Filters
remove_action('admin_print_styles', 'print_emoji_styles');
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('admin_print_scripts', 'print_emoji_detection_script');
remove_action('wp_print_styles', 'print_emoji_styles');
remove_filter('wp_mail', 'wp_staticize_emoji_for_email');
remove_filter('the_content_feed', 'wp_staticize_emoji');
remove_filter('comment_text_rss', 'wp_staticize_emoji');
// TinyMCE Emojis
add_filter('tiny_mce_plugins', array($this, 'removeEmojisTinymce'));
add_filter('emoji_svg_url', '__return_false');
}
/**
* @param $plugins
*
* @return array
*/
public function removeEmojisTinymce($plugins)
{
if (is_array($plugins)) {
return array_diff($plugins, array('wpemoji'));
}
return array();
}
/**
*
*/
public function doDisableOembed()
{
add_action('init', static function() {
// Remove the REST API endpoint.
remove_action('rest_api_init', 'wp_oembed_register_route');
// Turn off oEmbed auto discovery.
add_filter('embed_oembed_discover', '__return_false');
// Don't filter oEmbed results.
remove_filter('oembed_dataparse', 'wp_filter_oembed_result');
// Remove oEmbed discovery links.
remove_action('wp_head', 'wp_oembed_add_discovery_links');
// Remove oEmbed-specific JavaScript from the front-end and back-end.
remove_action('wp_head', 'wp_oembed_add_host_js');
add_filter('tiny_mce_plugins', static function ($plugins) {
return array_diff($plugins, array('wpembed'));
});
// Remove all embeds rewrite rules.
add_filter('rewrite_rules_array', static function ($rules) {
if ( ! empty($rules) ) {
foreach ( $rules as $rule => $rewrite ) {
if ( is_string($rewrite) && false !== strpos( $rewrite, 'embed=true' ) ) {
unset( $rules[ $rule ] );
}
}
}
return $rules;
});
// Remove filter of the oEmbed result before any HTTP requests are made.
remove_filter('pre_oembed_result', 'wp_filter_pre_oembed_result');
}, 9999 );
}
/**
*
*/
public function doDisableRssFeed()
{
add_action('do_feed', array($this, 'disabledRssFeedMsg'), 1);
add_action('do_feed_rdf', array($this, 'disabledRssFeedMsg'), 1);
add_action('do_feed_rss', array($this, 'disabledRssFeedMsg'), 1);
add_action('do_feed_rss2', array($this, 'disabledRssFeedMsg'), 1);
add_action('do_feed_atom', array($this, 'disabledRssFeedMsg'), 1);
add_action('do_feed_rss2_comments', array($this, 'disabledRssFeedMsg'), 1);
add_action('do_feed_atom_comments', array($this, 'disabledRssFeedMsg'), 1);
}
/**
*
*/
public function disabledRssFeedMsg()
{
$wpacuSettings = new Settings();
$settingsArray = $wpacuSettings->getAll();
$feedDisableMsg = isset($settingsArray['disable_rss_feed_message']) ? $settingsArray['disable_rss_feed_message'] : '';
wp_die( __($feedDisableMsg, 'wp-asset-clean-up') );
}
/**
*
*/
public function cleanUpHtmlOutputForAssetsCall()
{
if (isset($_GET['wpacu_clean_load'])) {
// No Admin Bar
add_filter('show_admin_bar', '__return_false', PHP_INT_MAX);
}
// No Autoptimize
if (! defined('AUTOPTIMIZE_NOBUFFER_OPTIMIZE')) {
define( 'AUTOPTIMIZE_NOBUFFER_OPTIMIZE', true );
}
add_filter('autoptimize_filter_noptimize', '__return_false');
// Use less resources during CSS/JS fetching by preventing other plugins to interfere with the HTML output as it's completely unnecessary in this instance
if (Misc::isPluginActive('autoptimize/autoptimize.php')) {
foreach (array('autoptimize_html', 'autoptimize_css', 'autoptimize_js', 'autoptimize_cdn_url', 'autoptimize_optimize_logged') as $aoOption) {
add_filter('pre_option_'.$aoOption, static function($value) { return ''; });
}
}
// No Fast Velocity Minify
add_action('plugins_loaded', static function() {
remove_action( 'setup_theme', 'fastvelocity_process_frontend' );
}, PHP_INT_MAX);
// No WP Rocket (Minify / Concatenate)
add_filter( 'get_rocket_option_minify_css', '__return_false' );
add_filter( 'get_rocket_option_minify_concatenate_css', '__return_false' );
add_filter( 'get_rocket_option_minify_js', '__return_false' );
add_filter( 'get_rocket_option_minify_concatenate_js', '__return_false' );
// No W3 Total Cache: Minify
add_filter('w3tc_minify_enable', '__return_false');
// [NO SG Optimiser]
self::filterSGOptions();
// Emulate page builder param to view page with no SG Optimiser on request
// Extra params to be used in case 'SG Optimiser' is called before Asset CleanUp: 'fl_builder', 'vcv-action', 'et_fb', 'ct_builder', 'tve'
add_filter('sgo_pb_params', static function($pbParams) {
$pbParams[] = WPACU_LOAD_ASSETS_REQ_KEY; // fetching assets
$pbParams[] = 'wpacu_clean_load'; // loading the page unoptimized for debugging purposes
return $pbParams;
});
// Fallback in case SG Optimizer is triggered BEFORE Asset CleanUp and the filter above will not work
add_filter('sgo_css_combine_exclude', array($this, 'allCssHandles'));
add_filter('sgo_css_minify_exclude', array($this, 'allCssHandles'));
add_filter('sgo_js_minify_exclude', array($this, 'allJsHandles'));
add_filter('sgo_js_async_exclude', array($this, 'allJsHandles'));
add_filter('sgo_html_minify_exclude_params', static function ($excludeParams) {
$excludeParams[] = WPACU_LOAD_ASSETS_REQ_KEY; // fetching assets
$excludeParams[] = 'wpacu_clean_load'; // loading the page unoptimized for debugging purposes
return $excludeParams;
});
// [/NO SG Optimiser]
}
/**
*
*/
public static function filterSGOptions()
{
// SG Optimizer Plugin
$sgOptimizerMapping = array(
'autoflush' => 'siteground_optimizer_autoflush_cache',
'dynamic-cache' => 'siteground_optimizer_enable_cache',
'memcache' => 'siteground_optimizer_enable_memcached',
'ssl-fix' => 'siteground_optimizer_fix_insecure_content',
'html' => 'siteground_optimizer_optimize_html',
'js' => 'siteground_optimizer_optimize_javascript',
'js-async' => 'siteground_optimizer_optimize_javascript_async',
'css' => 'siteground_optimizer_optimize_css',
'combine-css' => 'siteground_optimizer_combine_css',
'querystring' => 'siteground_optimizer_remove_query_strings',
'emojis' => 'siteground_optimizer_disable_emojis',
'images' => 'siteground_optimizer_optimize_images',
'lazyload_images' => 'siteground_optimizer_lazyload_images',
'lazyload_gravatars' => 'siteground_optimizer_lazyload_gravatars',
'lazyload_thumbnails' => 'siteground_optimizer_lazyload_thumbnails',
'lazyload_responsive' => 'siteground_optimizer_lazyload_responsive',
'lazyload_textwidgets' => 'siteground_optimizer_lazyload_textwidgets',
'ssl' => 'siteground_optimizer_ssl_enabled',
'gzip' => 'siteground_optimizer_enable_gzip_compression',
'browser-caching' => 'siteground_optimizer_enable_browser_caching',
);
foreach ($sgOptimizerMapping as $optionName) {
add_filter('pre_option_'.$optionName, '__return_false');
}
}
/**
* @return array
*/
public function allCssHandles()
{
global $wp_styles;
$allCssHandles = array();
if (isset($wp_styles->registered) && ! empty($wp_styles->registered)) {
$allCssHandles = array_keys($wp_styles->registered);
}
return $allCssHandles;
}
/**
* @return array
*/
public function allJsHandles()
{
global $wp_scripts;
$allJsHandles = array();
if (isset($wp_scripts->registered) && ! empty($wp_scripts->registered)) {
$allJsHandles = array_keys($wp_scripts->registered);
}
return $allJsHandles;
}
}

View File

@ -0,0 +1,273 @@
<?php
namespace WpAssetCleanUp;
use WpAssetCleanUp\OptimiseAssets\OptimizeCommon;
/**
* Class Debug
* @package WpAssetCleanUp
*/
class Debug
{
/**
* Debug constructor.
*/
public function __construct()
{
if (isset($_GET['wpacu_debug'])) {
add_action('wp_footer', array($this, 'showDebugOptionsFront'), PHP_INT_MAX);
}
foreach(array('wp', 'admin_init') as $wpacuActionHook) {
add_action( $wpacuActionHook, static function() {
if (isset( $_GET['wpacu_get_cache_dir_size'] ) && Menu::userCanManageAssets()) {
self::printCacheDirInfo();
}
// For debugging purposes
if (isset($_GET['wpacu_get_already_minified']) && Menu::userCanManageAssets()) {
echo '<pre>'; print_r(OptimizeCommon::getAlreadyMarkedAsMinified()); echo '</pre>';
exit();
}
if (isset($_GET['wpacu_remove_already_minified']) && Menu::userCanManageAssets()) {
echo '<pre>'; print_r(OptimizeCommon::removeAlreadyMarkedAsMinified()); echo '</pre>';
exit();
}
if (isset($_GET['wpacu_limit_already_minified']) && Menu::userCanManageAssets()) {
OptimizeCommon::limitAlreadyMarkedAsMinified();
echo '<pre>'; print_r(OptimizeCommon::getAlreadyMarkedAsMinified()); echo '</pre>';
exit();
}
} );
}
}
/**
*
*/
public function showDebugOptionsFront()
{
if (! Menu::userCanManageAssets()) {
return;
}
$markedCssListForUnload = array_unique(Main::instance()->allUnloadedAssets['styles']);
$markedJsListForUnload = array_unique(Main::instance()->allUnloadedAssets['scripts']);
$allDebugOptions = array(
// [For CSS]
'wpacu_no_css_unload' => 'Do not apply any CSS unload rules',
'wpacu_no_css_minify' => 'Do not minify any CSS',
'wpacu_no_css_combine' => 'Do not combine any CSS',
'wpacu_no_css_preload_basic' => 'Do not preload any CSS (Basic)',
// [/For CSS]
// [For JS]
'wpacu_no_js_unload' => 'Do not apply any JavaScript unload rules',
'wpacu_no_js_minify' => 'Do not minify any JavaScript',
'wpacu_no_js_combine' => 'Do not combine any JavaScript',
'wpacu_no_js_preload_basic' => 'Do not preload any JS (Basic)',
// [/For JS]
// Others
'wpacu_no_frontend_show' => 'Do not show the bottom CSS/JS managing list',
'wpacu_no_admin_bar' => 'Do not show the admin bar',
'wpacu_no_html_changes' => 'Do not alter the HTML DOM (this will also load all assets non-minified and non-combined)',
);
?>
<style <?php echo Misc::getStyleTypeAttribute(); ?>>
<?php echo file_get_contents(WPACU_PLUGIN_DIR.'/assets/wpacu-debug.css'); ?>
</style>
<script <?php echo Misc::getScriptTypeAttribute(); ?>>
<?php echo file_get_contents(WPACU_PLUGIN_DIR.'/assets/wpacu-debug.js'); ?>
</script>
<div id="wpacu-debug-options">
<table>
<tr>
<td style="vertical-align: top;">
<p>View the page with the following options <strong>disabled</strong> (for debugging purposes):</p>
<form method="post">
<ul class="wpacu-options">
<?php
foreach ($allDebugOptions as $debugKey => $debugText) {
?>
<li>
<label><input type="checkbox"
name="<?php echo esc_attr($debugKey); ?>"
<?php if ( ! empty($_GET) && array_key_exists($debugKey, $_GET) ) { echo 'checked="checked"'; } ?> /> &nbsp;<?php echo esc_html($debugText); ?></label>
</li>
<?php
}
?>
</ul>
<div>
<input type="submit"
value="Preview this page with the changes made above" />
</div>
<input type="hidden" name="wpacu_debug" value="on" />
</form>
</td>
<td>
<div style="margin: 0 0 10px; padding: 10px 0;">
<strong>CSS handles marked for unload:</strong>&nbsp;
<?php
if (! empty($markedCssListForUnload)) {
sort($markedCssListForUnload);
$markedCssListForUnloadFiltered = array_map(static function($handle) {
return '<span style="color: darkred;">'.esc_html($handle).'</span>';
}, $markedCssListForUnload);
echo implode(' &nbsp;/&nbsp; ', $markedCssListForUnloadFiltered);
} else {
echo 'None';
}
?>
</div>
<div style="margin: 0 0 10px; padding: 10px 0;">
<strong>JS handles marked for unload:</strong>&nbsp;
<?php
if (! empty($markedJsListForUnload)) {
sort($markedJsListForUnload);
$markedJsListForUnloadFiltered = array_map(static function($handle) {
return '<span style="color: darkred;">'.esc_html($handle).'</span>';
}, $markedJsListForUnload);
echo implode(' &nbsp;/&nbsp; ', $markedJsListForUnloadFiltered);
} else {
echo 'None';
}
?>
</div>
<hr />
<div style="margin: 0 0 10px; padding: 10px 0;">
<ul style="list-style: none; padding-left: 0;">
<li style="margin-bottom: 10px;">Dequeue any chosen styles (.css): <?php echo Misc::printTimingFor('filter_dequeue_styles', '{wpacu_filter_dequeue_styles_exec_time} ({wpacu_filter_dequeue_styles_exec_time_sec})'); ?></li>
<li style="margin-bottom: 20px;">Dequeue any chosen scripts (.js): <?php echo Misc::printTimingFor('filter_dequeue_scripts', '{wpacu_filter_dequeue_scripts_exec_time} ({wpacu_filter_dequeue_scripts_exec_time_sec})'); ?></li>
<li style="margin-bottom: 10px;">Prepare CSS files to optimize: {wpacu_prepare_optimize_files_css_exec_time} ({wpacu_prepare_optimize_files_css_exec_time_sec})</li>
<li style="margin-bottom: 20px;">Prepare JS files to optimize: {wpacu_prepare_optimize_files_js_exec_time} ({wpacu_prepare_optimize_files_js_exec_time_sec})</li>
<li style="margin-bottom: 10px;">OptimizeCommon - HTML alteration via <em>wp_loaded</em>: {wpacu_alter_html_source_exec_time} ({wpacu_alter_html_source_exec_time_sec})
<ul id="wpacu-debug-timing">
<li style="margin-top: 10px; margin-bottom: 10px;">&nbsp;OptimizeCSS: {wpacu_alter_html_source_for_optimize_css_exec_time} ({wpacu_alter_html_source_for_optimize_css_exec_time_sec})
<ul>
<li>Google Fonts Optimization/Removal: {wpacu_alter_html_source_for_google_fonts_optimization_removal_exec_time}</li>
<li>From CSS file to Inline: {wpacu_alter_html_source_for_inline_css_exec_time}</li>
<li>Update Original to Optimized: {wpacu_alter_html_source_original_to_optimized_css_exec_time}</li>
<li>Preloads: {wpacu_alter_html_source_for_preload_css_exec_time}</li>
<!-- -->
<li>Combine: {wpacu_alter_html_source_for_combine_css_exec_time}</li>
<li>Minify Inline Tags: {wpacu_alter_html_source_for_minify_inline_style_tags_exec_time}</li>
<li>Unload (ignore dependencies): {wpacu_alter_html_source_unload_ignore_deps_css_exec_time}</li>
</ul>
</li>
<li>OptimizeJs: {wpacu_alter_html_source_for_optimize_js_exec_time} ({wpacu_alter_html_source_for_optimize_js_exec_time_sec})
<ul>
<li>Update Original to Optimized: {wpacu_alter_html_source_original_to_optimized_js_exec_time}</li>
<li>Preloads: {wpacu_alter_html_source_for_preload_js_exec_time}</li>
<!-- -->
<li>Combine: {wpacu_alter_html_source_for_combine_js_exec_time}</li>
<li>Unload (ignore dependencies): {wpacu_alter_html_source_unload_ignore_deps_js_exec_time}</li>
<li>Move any inline wih jQuery code after jQuery library: {wpacu_alter_html_source_move_inline_jquery_after_src_tag_exec_time}</li>
</ul>
</li>
<li>HTML CleanUp: {wpacu_alter_html_source_cleanup_exec_time}
<ul>
<li>Strip HTML Comments: {wpacu_alter_html_source_for_remove_html_comments_exec_time}</li>
<li>Remove Generator Meta Tags: {wpacu_alter_html_source_for_remove_meta_generators_exec_time}</li>
</ul>
</li>
</ul>
</li>
<li style="margin-bottom: 10px;">Output CSS &amp; JS Management List: {wpacu_output_css_js_manager_exec_time} ({wpacu_output_css_js_manager_exec_time_sec})</li>
<!-- -->
</ul>
</div>
</td>
</tr>
</table>
</div>
<?php
}
/**
*
*/
public static function printCacheDirInfo()
{
$assetCleanUpCacheDirRel = OptimizeCommon::getRelPathPluginCacheDir();
$assetCleanUpCacheDir = WP_CONTENT_DIR . $assetCleanUpCacheDirRel;
echo '<h3>'.WPACU_PLUGIN_TITLE.': Caching Directory Stats</h3>';
if (is_dir($assetCleanUpCacheDir)) {
$printCacheDirOutput = '<em>'.str_replace($assetCleanUpCacheDirRel, '<strong>'.$assetCleanUpCacheDirRel.'</strong>', $assetCleanUpCacheDir).'</em>';
if (! is_writable($assetCleanUpCacheDir)) {
echo '<span style="color: red;">'.
'The '.wp_kses($printCacheDirOutput, array('em' => array(), 'strong' => array())).' directory is <em>not writable</em>.</span>'.
'<br /><br />';
} else {
echo '<span style="color: green;">The '.wp_kses($printCacheDirOutput, array('em' => array(), 'strong' => array())).' directory is <em>writable</em>.</span>' . '<br /><br />';
}
$dirItems = new \RecursiveDirectoryIterator( $assetCleanUpCacheDir,
\RecursiveDirectoryIterator::SKIP_DOTS );
$totalFiles = 0;
$totalSize = 0;
foreach (
new \RecursiveIteratorIterator( $dirItems, \RecursiveIteratorIterator::SELF_FIRST,
\RecursiveIteratorIterator::CATCH_GET_CHILD ) as $item
) {
if ($item->isDir()) {
echo '<br />';
$appendAfter = ' - ';
if (is_writable($item)) {
$appendAfter .= ' <em><strong>writable</strong> directory</em>';
} else {
$appendAfter .= ' <em><strong style="color: red;">not writable</strong> directory</em>';
}
} elseif ($item->isFile()) {
$appendAfter = '(<em>'.Misc::formatBytes($item->getSize()).'</em>)';
echo '&nbsp;-&nbsp;';
}
echo wp_kses($item.' '.$appendAfter, array(
'em' => array(),
'strong' => array('style' => array()),
'br' => array(),
'span' => array('style' => array())
))
.'<br />';
if ( $item->isFile() ) {
$totalSize += $item->getSize();
$totalFiles ++;
}
}
echo '<br />'.'Total Files: <strong>'.$totalFiles.'</strong> / Total Size: <strong>'.Misc::formatBytes($totalSize).'</strong>';
} else {
echo 'The directory does not exists.';
}
exit();
}
}

View File

@ -0,0 +1,104 @@
<?php
namespace WpAssetCleanUp;
use WpAssetCleanUp\OptimiseAssets\OptimizeCss;
use WpAssetCleanUp\OptimiseAssets\OptimizeJs;
/**
* Class FileSystem
* @package WpAssetCleanUp
*/
class FileSystem
{
/**
* @return bool|\WP_Filesystem_Direct
*/
public static function init()
{
// Set the permission constants if not already set.
if ( ! defined('FS_CHMOD_DIR') ) {
define('FS_CHMOD_DIR', fileperms(ABSPATH) & 0777 | 0755);
}
if ( ! defined('FS_CHMOD_FILE') ) {
define('FS_CHMOD_FILE', fileperms(ABSPATH . 'index.php') & 0777 | 0644);
}
if (! defined('WPACU_FS_USED') && ! class_exists('\WP_Filesystem_Base') && ! class_exists('\WP_Filesystem_Direct')) {
$wpFileSystemBase = ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
$wpFileSystemDirect = ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
if (is_file($wpFileSystemBase) && is_file($wpFileSystemDirect)) {
// Make sure to use the 'direct' method as it's the most effective in this scenario
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
define('WPACU_FS_USED', true);
} else {
// Do not use WordPress FileSystem Direct (fallback to default PHP functions)
define('WPACU_FS_USED', false);
}
}
if (defined('WPACU_FS_USED') && WPACU_FS_USED === true) {
return new \WP_Filesystem_Direct( new \StdClass() );
}
return false;
}
/**
* @param $localPathToFile
* @param string $alter
*
* @return false|string
*/
public static function fileGetContents($localPathToFile, $alter = '')
{
// ONLY relevant for CSS files
if ($alter === 'combine_css_imports') {
// This custom class does not minify as it's custom-made for combining @import
$optimizer = new \WpAssetCleanUp\OptimiseAssets\CombineCssImports($localPathToFile);
return $optimizer->minify();
}
// Fallback
if (! self::init()) {
return @file_get_contents($localPathToFile);
}
return self::init()->get_contents($localPathToFile);
}
/**
* @param $localPathToFile
* @param $contents
*
* @return bool
*/
public static function filePutContents($localPathToFile, $contents)
{
if ( (strpos($localPathToFile, WP_CONTENT_DIR . OptimizeCss::getRelPathCssCacheDir()) !== false && ! is_dir(dirname($localPathToFile)))
|| (strpos($localPathToFile, WP_CONTENT_DIR . OptimizeJs::getRelPathJsCacheDir()) !== false && ! is_dir(dirname($localPathToFile)))
) {
$dirToCreate = dirname( $localPathToFile );
try {
mkdir( $dirToCreate, FS_CHMOD_DIR, true );
} catch (\Exception $e) {
error_log( WPACU_PLUGIN_TITLE . ': Could not make directory ' . $dirToCreate . ' / Error: '.$e->getMessage() );
}
}
// Fallback
try {
if ( ! self::init() ) {
$return = file_put_contents( $localPathToFile, $contents );
} else {
$return = self::init()->put_contents( $localPathToFile, $contents, FS_CHMOD_FILE );
}
} catch ( \Exception $e ) {
error_log( WPACU_PLUGIN_TITLE . ': Could not write to ' . $localPathToFile . ' / Error: '.$e->getMessage() );
}
return $return;
}
}

View File

@ -0,0 +1,883 @@
<?php
namespace WpAssetCleanUp;
use WpAssetCleanUp\OptimiseAssets\OptimizeCommon;
use WpAssetCleanUp\OptimiseAssets\OptimizeJs;
/**
* Class HardcodedAssets
* @package WpAssetCleanUp
*/
class HardcodedAssets
{
/**
*
*/
public static function init()
{
add_action( 'init', static function() {
if (Main::instance()->isGetAssetsCall) {
// Case 1: An AJAX call is made from the Dashboard
self::initBufferingForAjaxCallFromTheDashboard();
} elseif (self::useBufferingForEditFrontEndView()) {
// Case 2: The logged-in admin manages the assets from the front-end view
self::initBufferingForFrontendManagement();
}
});
}
/**
*
*/
public static function initBufferingForAjaxCallFromTheDashboard()
{
ob_start();
add_action('shutdown', static function() {
$htmlSource = '';
// We'll need to get the number of ob levels we're in, so that we can iterate over each, collecting
// that buffer's output into the final output.
$htmlSourceLevel = ob_get_level();
for ($wpacuI = 0; $wpacuI < $htmlSourceLevel; $wpacuI++) {
$htmlSource .= ob_get_clean();
}
$anyHardCodedAssets = HardcodedAssets::getAll($htmlSource); // Fetch all for this type of request
echo str_replace('{wpacu_hardcoded_assets}', $anyHardCodedAssets, $htmlSource);
}, 0);
}
/**
*
*/
public static function initBufferingForFrontendManagement()
{
// Used to print the hardcoded CSS/JS
ob_start();
add_action('shutdown', static function() {
if (! defined('NEXTEND_SMARTSLIDER_3_URL_PATH')) {
ob_flush();
}
$htmlSource = '';
// We'll need to get the number of ob levels we're in, so that we can iterate over each, collecting
// that buffer's output into the final output.
$htmlSourceLevel = ob_get_level();
for ($wpacuI = 0; $wpacuI < $htmlSourceLevel; $wpacuI++) {
$htmlSource .= ob_get_clean();
}
echo OptimizeCommon::alterHtmlSource($htmlSource);
}, 0);
}
/**
* @return bool
*/
public static function useBufferingForEditFrontEndView()
{
// The logged-in admin needs to be outside the Dashboard (in the front-end view)
// "Manage in the Front-end" is enabled in "Settings" -> "Plugin Usage Preferences"
return (Main::instance()->frontendShow() && ! is_admin() && Menu::userCanManageAssets() && ! Main::instance()->isGetAssetsCall);
}
/**
* @param $htmlSource
* @param bool $encodeIt - if set to "false", it's mostly for testing purposes
*
* @return string|array
*/
public static function getAll($htmlSource, $encodeIt = true)
{
$htmlSourceAlt = CleanUp::removeHtmlComments($htmlSource, true);
$collectLinkStyles = true; // default
$collectScripts = true; // default
$hardCodedAssets = array(
'link_and_style_tags' => array(), // LINK (rel="stylesheet") & STYLE (inline)
'script_nosrc_and_inline_tags' => array(), // SCRIPT (with "src" attribute) & SCRIPT (inline)
);
$matchesSourcesFromTags = array();
$stickToRegEx = true;
if ( $collectLinkStyles ) {
if ( ! $stickToRegEx && Misc::isDOMDocumentOn() ) {
$domDoc = Misc::initDOMDocument();
$domDoc->loadHTML($htmlSourceAlt);
$selector = new \DOMXPath($domDoc);
$domTagQuery = $selector->query('//link[@rel="stylesheet"]|//style|//script|//noscript');
if (count($domTagQuery) > 1) {
foreach($domTagQuery as $tagFound) {
$tagType = in_array($tagFound->nodeName, array('link', 'style')) ? 'css' : 'js';
if (self::skipTagIfNotRelevant( Misc::getOuterHTML( $tagFound ), 'whole_tag', $tagType)) {
continue; // no point in wasting more resources as the tag will never be shown, since it's irrelevant
}
if ( $tagFound->hasAttributes() ) {
foreach ( $tagFound->attributes as $attr ) {
if ( self::skipTagIfNotRelevant( $attr->nodeName, 'attribute', $tagType ) ) {
continue 2;
}
}
}
if ($tagFound->nodeName === 'link') {
if ( ! $tagFound->hasAttributes() ) {
continue;
}
$linkTagParts = array();
$linkTagParts[] = '<link ';
foreach ($tagFound->attributes as $attr) {
$attrName = $attr->nodeName;
$attrValue = $attr->nodeValue;
if ($attrName) {
if ($attrValue !== '') {
$linkTagParts[] = '(\s+|)' . preg_quote($attrName, '/') . '(\s+|)=(\s+|)(|"|\')' . preg_quote($attrValue, '/') . '(|"|\')(|\s+)';
} else {
$linkTagParts[] = '(\s+|)' . preg_quote($attrName, '/') . '(|((\s+|)=(\s+|)(|"|\')(|"|\')))';
}
}
}
$linkTagParts[] = '(|\s+)(|/)>';
$linkTagFinalRegExPart = implode('', $linkTagParts);
preg_match_all(
'#'.$linkTagFinalRegExPart.'#Umi',
$htmlSource,
$matchSourceFromTag,
PREG_SET_ORDER
);
// It always has to be a match from the DOM generated tag
// Otherwise, default it to RegEx
if ( empty($matchSourceFromTag) || ! (isset($matchSourceFromTag[0][0]) && ! empty($matchSourceFromTag[0][0])) ) {
$stickToRegEx = true;
break;
}
$matchesSourcesFromTags[] = array('link_tag' => $matchSourceFromTag[0][0]);
}
}
if (! $stickToRegEx) {
$shaOneToOriginal = array();
$htmlSourceAltEncoded = $htmlSourceAlt;
foreach($domTagQuery as $tagFound) {
if ( $tagFound->nodeValue && in_array( $tagFound->nodeName, array( 'style', 'script', 'noscript' ) ) ) {
if (strpos($htmlSourceAlt, $tagFound->nodeValue) === false) {
$stickToRegEx = true;
break;
}
$shaOneToOriginal[sha1($tagFound->nodeValue)] = $tagFound->nodeValue;
$htmlSourceAltEncoded = str_replace(
$tagFound->nodeValue,
'/*[wpacu]*/' . sha1($tagFound->nodeValue) . '/*[/wpacu]*/',
$htmlSourceAltEncoded
);
}
}
$domDocForTwo = Misc::initDOMDocument();
$domDocForTwo->loadHTML($htmlSourceAltEncoded);
$selectorTwo = new \DOMXPath($domDocForTwo);
$domTagQueryTwo = $selectorTwo->query('//style|//script|//noscript');
foreach($domTagQueryTwo as $tagFoundTwo) {
$tagType = in_array($tagFoundTwo->nodeName, array('link', 'style')) ? 'css' : 'js';
if ( $tagFoundTwo->hasAttributes() ) {
foreach ( $tagFoundTwo->attributes as $attr ) {
if ( self::skipTagIfNotRelevant( $attr->nodeName, 'attribute', $tagType ) ) {
continue 2;
}
}
}
$tagParts = array();
$tagParts[] = '<'.$tagFoundTwo->nodeName;
foreach ($tagFoundTwo->attributes as $attr) {
$attrName = $attr->nodeName;
$attrValue = $attr->nodeValue;
if ($attrName) {
if ($attrValue !== '') {
$tagParts[] = '(\s+|)' . preg_quote($attrName, '/') . '(\s+|)=(\s+|)(|"|\')' . preg_quote($attrValue, '/') . '(|"|\')(|\s+)';
} else {
$tagParts[] = '(\s+|)' . preg_quote($attrName, '/') . '(|((\s+|)=(\s+|)(|"|\')(|"|\')))';
}
}
}
$tagParts[] = '(|\s+)>';
if ($tagFoundTwo->nodeValue) {
$tagParts[] = preg_quote($tagFoundTwo->nodeValue, '/');
}
$tagParts[] = '</'.$tagFoundTwo->nodeName.'>';
$tagFinalRegExPart = implode('', $tagParts);
preg_match_all(
'#'.$tagFinalRegExPart.'#Umi',
$htmlSourceAltEncoded,
$matchSourceFromTagTwo,
PREG_SET_ORDER
);
// It always has to be a match from the DOM generated tag
// Otherwise, default it to RegEx
if ( empty($matchSourceFromTagTwo) || ! (isset($matchSourceFromTagTwo[0][0]) && ! empty($matchSourceFromTagTwo[0][0])) ) {
$stickToRegEx = true;
break;
}
$encodedNodeValue = Misc::extractBetween($matchSourceFromTagTwo[0][0], '/*[wpacu]*/', '/*[/wpacu]*/');
$matchedTag = str_replace('/*[wpacu]*/'.$encodedNodeValue.'/*[/wpacu]*/', $shaOneToOriginal[$encodedNodeValue], $matchSourceFromTagTwo[0][0]);
$tagTypeForReference = ($tagFoundTwo->nodeName === 'style') ? 'style_tag' : 'script_noscript_tag';
$matchesSourcesFromTags[] = array($tagTypeForReference => $matchedTag);
}
}
}
}
/*
* [START] Collect Hardcoded LINK (stylesheet) & STYLE tags
*/
if ($stickToRegEx || ! Misc::isDOMDocumentOn()) {
preg_match_all(
'#(?=(?P<link_tag><link[^>]*stylesheet[^>]*(>)))|(?=(?P<style_tag><style[^>]*?>.*</style>))#Umsi',
$htmlSourceAlt,
$matchesSourcesFromTags,
PREG_SET_ORDER
);
}
if ( ! empty( $matchesSourcesFromTags ) ) {
// Only the hashes are set
// For instance, 'd1eae32c4e99d24573042dfbb71f5258a86e2a8e' is the hash for the following script:
/*
* <style media="print">#wpadminbar { display:none; }</style>
*/
$stripsSpecificStylesHashes = array(
'5ead5f033961f3b8db362d2ede500051f659dd6d',
'25bd090513716c34b48b0495c834d2070088ad24'
);
// Sometimes, the hash checking might fail (if there's a small change to the JS content)
// Consider using a fallback verification by checking the actual content
$stripsSpecificStylesContaining = array(
'<style media="print">#wpadminbar { display:none; }</style>',
'id="edd-store-menu-styling"',
'#wp-admin-bar-gform-forms'
);
foreach ( $matchesSourcesFromTags as $matchedTag ) {
// LINK "stylesheet" tags (if any)
if ( isset( $matchedTag['link_tag'] ) && trim( $matchedTag['link_tag'] ) !== '' && ( trim( strip_tags( $matchedTag['link_tag'] ) ) === '' ) ) {
$matchedTagOutput = trim( $matchedTag['link_tag'] );
// Own plugin assets and enqueued ones since they aren't hardcoded
if (self::skipTagIfNotRelevant($matchedTagOutput)) {
continue;
}
$hardCodedAssets['link_and_style_tags'][] = $matchedTagOutput;
}
// STYLE inline tags (if any)
if ( isset( $matchedTag['style_tag'] ) && trim( $matchedTag['style_tag'] ) !== '' ) {
$matchedTagOutput = trim( $matchedTag['style_tag'] );
/*
* Strip certain STYLE tags irrelevant for the list (e.g. related to the WordPress Admin Bar, etc.)
*/
if ( in_array( self::determineHardcodedAssetSha1( $matchedTagOutput ), $stripsSpecificStylesHashes ) ) {
continue;
}
foreach ( $stripsSpecificStylesContaining as $cssContentTargeted ) {
if ( strpos( $matchedTagOutput, $cssContentTargeted ) !== false ) {
continue 2; // applies for this "foreach": ($matchesSourcesFromTags as $matchedTag)
}
}
// Own plugin assets and enqueued ones since they aren't hardcoded
if (self::skipTagIfNotRelevant($matchedTagOutput)) {
continue;
}
foreach ( wp_styles()->done as $cssHandle ) {
if ( strpos( $matchedTagOutput,
'<style id=\'' . trim( $cssHandle ) . '-inline-css\'' ) !== false ) {
// Do not consider the STYLE added via WordPress with wp_add_inline_style() as it's not hardcoded
continue 2;
}
}
$hardCodedAssets['link_and_style_tags'][] = $matchedTagOutput;
}
}
}
/*
* [END] Collect Hardcoded LINK (stylesheet) & STYLE tags
*/
}
if ($collectScripts) {
/*
* [START] Collect Hardcoded SCRIPT (src/inline)
*/
if ($stickToRegEx || ! Misc::isDOMDocumentOn()) {
preg_match_all( '@<script[^>]*?>.*?</script>|<noscript[^>]*?>.*?</noscript>@si', $htmlSourceAlt, $matchesScriptTags, PREG_SET_ORDER );
} else {
$matchesScriptTags = array();
if (! empty($matchesSourcesFromTags)) {
foreach ($matchesSourcesFromTags as $matchedTag) {
if (isset($matchedTag['script_noscript_tag']) && $matchedTag['script_noscript_tag']) {
$matchesScriptTags[][0] = $matchedTag['script_noscript_tag'];
}
}
}
}
$allInlineAssocWithJsHandle = array();
if ( isset( wp_scripts()->done ) && ! empty( wp_scripts()->done ) ) {
foreach ( wp_scripts()->done as $assetHandle ) {
// Now, go through the list of inline SCRIPTs associated with an enqueued SCRIPT (with "src" attribute)
// And make sure they do not show to the hardcoded list, since they are related to the handle, and they are stripped when the handle is dequeued
$anyInlineAssocWithJsHandle = OptimizeJs::getInlineAssociatedWithScriptHandle( $assetHandle, wp_scripts()->registered, 'handle' );
if ( ! empty( $anyInlineAssocWithJsHandle ) ) {
foreach ( $anyInlineAssocWithJsHandle as $jsInlineTagContent ) {
if ( trim( $jsInlineTagContent ) === '' ) {
continue;
}
$allInlineAssocWithJsHandle[] = trim($jsInlineTagContent);
}
}
}
$allInlineAssocWithJsHandle = array_unique($allInlineAssocWithJsHandle);
}
// Go through the hardcoded SCRIPT tags
if ( isset( $matchesScriptTags ) && ! empty( $matchesScriptTags ) ) {
// Only the hashes are set
// For instance, 'd1eae32c4e99d24573042dfbb71f5258a86e2a8e' is the hash for the following script:
/*
* <script>
(function() {
var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
request = true;
b[c] = b[c].replace( rcs, ' ' );
// The customizer requires postMessage and CORS (if the site is cross domain)
b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
}());
</script>
*/
$stripsSpecificScriptsHashes = array(
'd1eae32c4e99d24573042dfbb71f5258a86e2a8e',
'1a8f46f9f33e5d95919620df54781acbfa9efff7'
);
// Sometimes, the hash checking might fail (if there's a small change to the JS content)
// Consider using a fallback verification by checking the actual content
$stripsSpecificScriptsContaining = array(
'// The customizer requires postMessage and CORS (if the site is cross domain)',
'b[c] += ( window.postMessage && request ? \' \' : \' no-\' ) + cs;',
"(function(){var request,b=document.body,c='className',cs='customize-support',rcs=new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');request=!0;b[c]=b[c].replace(rcs,' ');b[c]+=(window.postMessage&&request?' ':' no-')+cs}())",
'document.body.className = document.body.className.replace( /(^|\s)(no-)?customize-support(?=\s|$)/, \'\' ) + \' no-customize-support\'',
"c = c.replace(/woocommerce-no-js/, 'woocommerce-js');" // WooCommerce related
);
foreach ( $matchesScriptTags as $matchedTag ) {
if ( isset( $matchedTag[0] ) && $matchedTag[0]
&& (stripos( $matchedTag[0], '<script' ) === 0 || stripos( $matchedTag[0], '<noscript' ) === 0) ) {
$matchedTagOutput = trim( $matchedTag[0] );
// Own plugin assets and enqueued ones since they aren't hardcoded
if (self::skipTagIfNotRelevant($matchedTagOutput, 'whole_tag', 'js', array('all_inline_assoc_with_js_handle' => $allInlineAssocWithJsHandle))) {
continue;
}
/*
* Strip certain SCRIPT tags irrelevant for the list (e.g. related to WordPress Customiser, Admin Bar, etc.)
*/
if ( in_array( self::determineHardcodedAssetSha1( $matchedTagOutput ), $stripsSpecificScriptsHashes ) ) {
continue;
}
foreach ( $stripsSpecificScriptsContaining as $jsContentTargeted ) {
if ( strpos( $matchedTagOutput, $jsContentTargeted ) !== false ) {
continue 2; // applies for this "foreach": ($matchesScriptTags as $matchedTag)
}
}
$hardCodedAssets['script_src_or_inline_and_noscript_inline_tags'][] = trim( $matchedTag[0] );
}
}
}
/*
* [END] Collect Hardcoded SCRIPT (src/inline)
*/
}
if ($stickToRegEx && ! empty($hardCodedAssets['link_and_style_tags']) && ! empty($hardCodedAssets['script_src_or_inline_and_noscript_inline_tags'])) {
$hardCodedAssets = self::removeAnyLinkTagsThatMightBeDetectedWithinScriptTags( $hardCodedAssets );
}
$tagsWithinConditionalComments = self::extractHtmlFromConditionalComments( $htmlSourceAlt );
if (Main::instance()->isGetAssetsCall) {
// AJAX call within the Dashboard
$hardCodedAssets['within_conditional_comments'] = $tagsWithinConditionalComments;
}
if ($encodeIt) {
return base64_encode( wp_json_encode( $hardCodedAssets ) );
}
return $hardCodedAssets;
}
/**
* @param $value
* @param $via ('whole_tag', 'attribute')
* @param string $type ('css', 'js')
* @param array $extras ('all_inline_assoc_with_js_handle')
*
* @return bool
*/
public static function skipTagIfNotRelevant($value, $via = 'whole_tag', $type = 'css', $extras = array())
{
if ($via === 'whole_tag') {
if ($type === 'css') {
if ( strpos( $value, 'data-wpacu-style-handle=' ) !== false ) {
// skip the SCRIPT with src that was enqueued properly and keep the hardcoded ones
return true;
}
if ( ( strpos( $value, 'data-wpacu-own-inline-style=' ) !== false ) ||
( strpos( $value, 'data-wpacu-inline-css-file=' ) !== false ) ) {
// remove plugin's own STYLE tags as they are not relevant in this context
return true;
}
// Do not add to the list elements such as Emojis (not relevant for hard-coded tags)
if ( strpos( $value, 'img.wp-smiley' ) !== false
&& strpos( $value, 'img.emoji' ) !== false
&& strpos( $value, '!important;' ) !== false ) {
return true;
}
}
if ($type === 'js') {
if ( strpos( $value, 'data-wpacu-script-handle=' ) !== false ) {
// skip the SCRIPT with src that was enqueued properly and keep the hardcoded ones
return true;
}
if ( ( strpos( $value, 'data-wpacu-own-inline-script=' ) !== false ) ||
( strpos( $value, 'data-wpacu-inline-js-file=' ) !== false ) ) {
// skip plugin's own SCRIPT tags as they are not relevant in this context
return true;
}
if ( strpos( $value, 'wpacu-preload-async-css-fallback' ) !== false ) {
// skip plugin's own SCRIPT tags as they are not relevant in this context
return true;
}
if ( strpos( $value, 'window._wpemojiSettings' ) !== false
&& strpos( $value, 'twemoji' ) !== false ) {
return true;
}
// Check the type and only allow SCRIPT tags with type='text/javascript' or no type at all (it will default to 'text/javascript')
$matchedTagInner = strip_tags( $value );
$matchedTagOnlyTags = str_replace( $matchedTagInner, '', $value );
$scriptType = Misc::getValueFromTag($matchedTagOnlyTags, 'type') ?: 'text/javascript';
if ( strpos( $scriptType, 'text/javascript' ) === false ) {
return true;
}
$allInlineAssocWithJsHandle = isset($extras['all_inline_assoc_with_js_handle']) ? $extras['all_inline_assoc_with_js_handle'] : array();
$hasSrc = false;
if (strpos($matchedTagOnlyTags, ' src=') !== false) {
$hasSrc = true;
}
if ( ! $hasSrc && ! empty( $allInlineAssocWithJsHandle ) ) {
preg_match_all("'<script[^>]*?>(.*?)</script>'si", $value, $matchesFromTagOutput);
$matchedTagOutputInner = isset($matchesFromTagOutput[1][0]) && trim($matchesFromTagOutput[1][0])
? trim($matchesFromTagOutput[1][0]) : false;
$matchedTagOutputInnerCleaner = $matchedTagOutputInner;
$stripStrStart = '/* <![CDATA[ */';
$stripStrEnd = '/* ]]> */';
if (strpos($matchedTagOutputInnerCleaner, $stripStrStart) === 0
&& Misc::endsWith($matchedTagOutputInnerCleaner, '/* ]]> */')) {
$matchedTagOutputInnerCleaner = substr($matchedTagOutputInnerCleaner, strlen($stripStrStart));
$matchedTagOutputInnerCleaner = substr($matchedTagOutputInnerCleaner, 0, -strlen($stripStrEnd));
$matchedTagOutputInnerCleaner = trim($matchedTagOutputInnerCleaner);
}
if (in_array($matchedTagOutputInnerCleaner, $allInlineAssocWithJsHandle)) {
return true;
}
}
}
}
if ($via === 'attribute') {
if ( $type === 'css' ) {
$possibleSignatures = array(
'data-wpacu-style-handle',
'data-wpacu-own-inline-style',
'data-wpacu-inline-css-file'
);
} else {
$possibleSignatures = array(
'data-wpacu-script-handle',
'data-wpacu-own-inline-script',
'data-wpacu-inline-js-file',
'wpacu-preload-async-css-fallback'
);
}
if (in_array($value, $possibleSignatures)) {
return true;
}
}
return false; // default
}
/**
*
* @param $hardcodedAssets
*
* @return mixed
*/
public static function removeAnyLinkTagsThatMightBeDetectedWithinScriptTags($hardcodedAssets)
{
foreach ($hardcodedAssets['link_and_style_tags'] as $cssTagIndex => $cssTag) {
if ($cssTag) {
foreach ($hardcodedAssets['script_src_or_inline_and_noscript_inline_tags'] as $scriptTag) {
if (strpos($scriptTag, $cssTag) !== false) {
// e.g. could be '<script>var linkToCss="<link href='[path_to_custom_css_file_here]'>";</script>'
unset($hardcodedAssets['link_and_style_tags'][$cssTagIndex]);
}
}
}
}
return $hardcodedAssets;
}
/**
* @param $htmlSource
*
* @return array
*/
public static function extractHtmlFromConditionalComments($htmlSource)
{
preg_match_all('#<!--\[if(.*?)]>(<!-->|-->|\s|)(.*?)(<!--<!|<!)\[endif]-->#si', $htmlSource, $matchedContent);
if (isset($matchedContent[1], $matchedContent[3]) && ! empty($matchedContent[1]) && ! empty($matchedContent[3])) {
$conditions = array_map('trim', $matchedContent[1]);
$tags = array_map('trim', $matchedContent[3]);
return array(
'conditions' => $conditions,
'tags' => $tags,
);
}
return array();
}
/**
* @param $targetedTag
* @param $contentWithinConditionalComments
*
* @return bool
*/
public static function isWithinConditionalComment($targetedTag, $contentWithinConditionalComments)
{
if (empty($contentWithinConditionalComments)) {
return false;
}
$targetedTag = trim($targetedTag);
foreach ($contentWithinConditionalComments['tags'] as $tagIndex => $tagFromList) {
$tagFromList = trim($tagFromList);
if ($targetedTag === $tagFromList || strpos($targetedTag, $tagFromList) !== false) {
return $contentWithinConditionalComments['conditions'][$tagIndex]; // Stops here and returns the condition
}
}
return false; // Not within a conditional comment (most cases)
}
/**
* @param $htmlTag
*
* @return bool|string
*/
public static function belongsTo($htmlTag)
{
$belongList = array(
'wpcf7recaptcha.' => '"Contact Form 7" plugin',
'c = c.replace(/woocommerce-no-js/, \'woocommerce-js\');' => '"WooCommerce" plugin',
'.woocommerce-product-gallery{ opacity: 1 !important; }' => '"WooCommerce" plugin',
'-ss-slider-3' => '"Smart Slider 3" plugin',
'N2R(["nextend-frontend","smartslider-frontend","smartslider-simple-type-frontend"]' => '"Smart Slider 3" plugin',
'function setREVStartSize' => '"Slider Revolution" plugin',
'jQuery(\'.rev_slider_wrapper\')' => '"Slider Revolution" plugin',
'jQuery(\'#wp-admin-bar-revslider-default' => '"Slider Revolution" plugin'
);
foreach ($belongList as $ifContains => $isFromSource) {
if ( strpos( $htmlTag, $ifContains) !== false ) {
return $isFromSource;
}
}
return false;
}
/**
* @param $tagOutput
*
* @return string
*/
public static function determineHardcodedAssetSha1($tagOutput)
{
// Look if the "href" or "src" ends with '.css' or '.js'
// Only hash the actual path to the file
// In case the tag changes (e.g. an attribute will be added), the tag will be considered the same for the plugin rules
// To avoid the rules from not working / e.g. the file could have a dynamic "?ver=" at the end
if ( ! (stripos($tagOutput, '<link') !== false || stripos($tagOutput, '<style') !== false
|| stripos($tagOutput, '<script') !== false || stripos($tagOutput, '<noscript') !== false) ) {
return sha1( $tagOutput ); // default
}
$isLinkWithHref = (stripos($tagOutput, '<link') !== false) && preg_match('#href(\s+|)=(\s+|)(["\'])(.*)(["\'])#Usmi', $tagOutput);
$isScriptWithSrc = (stripos($tagOutput, '<script') !== false) && preg_match('#src(\s+|)=(\s+|)(["\'])(.*)(["\'])|src(\s+|)=(\s+|)(.*)(\s+)#Usmi', $tagOutput);
if ($isLinkWithHref || $isScriptWithSrc) {
return self::determineHardcodedAssetSha1ForAssetsWithSource($tagOutput);
}
if (stripos($tagOutput, '<style') !== false || stripos($tagOutput, '<script') !== false || stripos($tagOutput, '<noscript') !== false) {
return self::determineHardcodedAssetSha1ForAssetsWithoutSource($tagOutput);
}
return sha1( $tagOutput ); // default
}
/**
// In case there are STYLE tags and SCRIPT tags without any SRC, make sure to consider only the content of the tag as a reference
// e.g. if the user updates <style type="text/css"> to <style> the tag should be considered the same if the content is the same
// also, do not consider any whitespaces from the beginning and ending of the tag's content
*
* @param $tagOutput
*
* @return string
*/
public static function determineHardcodedAssetSha1ForAssetsWithoutSource($tagOutput)
{
if (stripos($tagOutput, '<style') !== false) {
preg_match_all('@(<style[^>]*?>)(.*?)</style>@si', $tagOutput, $matches);
if (isset($matches[0][0], $matches[2][0]) && strlen($tagOutput) === strlen($matches[0][0])) {
return sha1( trim($matches[2][0]) ); // the hashed content of the STYLE tag
}
} elseif (stripos($tagOutput, '<script') !== false) {
preg_match_all('@(<script[^>]*?>)(.*?)</script>@si', $tagOutput, $matches);
if (isset($matches[0][0], $matches[2][0]) && strlen($tagOutput) === strlen($matches[0][0])) {
return sha1( trim($matches[2][0]) ); // the hashed content of the SCRIPT tag
}
} elseif (stripos($tagOutput, '<noscript') !== false) {
preg_match_all('@(<noscript[^>]*?>)(.*?)</noscript>@si', $tagOutput, $matches);
if (isset($matches[0][0], $matches[2][0]) && strlen($tagOutput) === strlen($matches[0][0])) {
return sha1( trim($matches[2][0]) ); // the hashed content of the NOSCRIPT tag
}
}
return sha1($tagOutput);
}
/**
* Only the LINK tags and SCRIPT tags with the "href" and "src" attributes would be considered
*
* @param $tagOutput
*
* @return string
*/
public static function determineHardcodedAssetSha1ForAssetsWithSource($tagOutput)
{
if ($finalCleanSource = self::getRelSourceFromTagOutputForReference($tagOutput)) {
return sha1($finalCleanSource);
}
return sha1( $tagOutput ); // default
}
/**
* @param $tagOutput
*
* @return array|false|string|string[]
*/
public static function getRelSourceFromTagOutputForReference($tagOutput)
{
if (stripos($tagOutput, 'href') !== false && stripos($tagOutput, 'stylesheet') !== false && stripos(trim($tagOutput), '<link') === 0) {
$attrToGet = 'href';
$extToCheck = 'css';
} else {
$attrToGet = 'src';
$extToCheck = 'js';
}
$sourceValue = Misc::getValueFromTag($tagOutput, $attrToGet);
if (! $sourceValue) {
return false;
}
if ( stripos( $sourceValue, '.' . $extToCheck . '?' ) !== false ) {
list( $cleanSource ) = explode( '.' . $extToCheck . '?', $sourceValue );
$finalCleanSource = $cleanSource . '.' . $extToCheck;
} else {
$finalCleanSource = $sourceValue;
}
if ( $finalCleanSource ) {
$localAssetPath = OptimizeCommon::getLocalAssetPath( $finalCleanSource, $extToCheck );
if ( $localAssetPath ) {
$sourceRelPath = OptimizeCommon::getSourceRelPath( $finalCleanSource );
if ( $sourceRelPath ) {
return $finalCleanSource;
}
} else {
$finalCleanSource = str_ireplace( array( 'http://', 'https://' ), '', $finalCleanSource );
$finalCleanSource = ( strpos( $finalCleanSource, '//' ) === 0 ) ? substr( $finalCleanSource, 2 ) : $finalCleanSource; // if the string starts with '//', remove it
}
}
return $finalCleanSource;
}
/**
* @param $data
*
* @return string
*/
public static function getHardCodedManageAreaForFrontEndView($data)
{
$dataSettingsFrontEnd = ObjectCache::wpacu_cache_get('wpacu_settings_frontend_data') ?: array();
$dataSettingsFrontEnd['page_unload_text'] = esc_html($data['page_unload_text']);
// The following string will be replaced by the values got from the AJAX call to /?wpassetcleanup_load=1&wpacu_just_hardcoded
$dataWpacuSettingsFrontend = base64_encode(wp_json_encode($dataSettingsFrontEnd));
$currentHardcodedAssetRules = '';
// When the form is submitted it will clear some values if they are not sent anymore which can happen with a failed AJAX call to retrieve the list of hardcoded assets
// Place the current values to the area in case the AJAX call fails, and it won't print the list
// If the user presses "Update", it won't clear any existing rules
// If the list is printed, obviously it will be with all the fields in place as they should be
foreach (array('current_unloaded_page_level', 'load_exceptions', 'handle_unload_regex', 'handle_load_regex', 'handle_load_logged_in') as $ruleKey) {
foreach ( array( 'styles', 'scripts' ) as $assetType ) {
if ( isset( $dataSettingsFrontEnd[$ruleKey][ $assetType ] ) && ! empty( $dataSettingsFrontEnd[$ruleKey][$assetType] ) ) {
// Go through the values, depending on how the array is structured
// handle_unload_regex, handle_load_regex
if (in_array($ruleKey, array('handle_unload_regex', 'handle_load_regex'))) {
foreach ( $dataSettingsFrontEnd[ $ruleKey ][ $assetType ] as $assetHandle => $assetValues ) {
if ( strpos( $assetHandle, 'wpacu_hardcoded_' ) !== false ) {
if ($ruleKey === 'handle_unload_regex') {
$enableValue = isset( $assetValues['enable'] ) ? $assetValues['enable'] : '';
$regExValue = isset( $assetValues['value'] ) ? $assetValues['value'] : '';
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_handle_unload_regex[' . $assetType . '][' . $assetHandle . '][enable]" value="' . $enableValue . '" />';
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_handle_unload_regex[' . $assetType . '][' . $assetHandle . '][value]" value="' . esc_attr( $regExValue ) . '" />';
} elseif ($ruleKey === 'handle_load_regex') {
$enableValue = isset( $assetValues['enable'] ) ? $assetValues['enable'] : '';
$regExValue = isset( $assetValues['value'] ) ? $assetValues['value'] : '';
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_handle_load_regex[' . $assetType . '][' . $assetHandle . '][enable]" value="' . $enableValue . '" />';
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_handle_load_regex[' . $assetType . '][' . $assetHandle . '][value]" value="' . esc_attr( $regExValue ) . '" />';
}
}
}
} else {
// current unloaded on a page level, load_exceptions, handle_load_logged_in
foreach ( $dataSettingsFrontEnd[ $ruleKey ][ $assetType ] as $assetHandle ) {
if ( strpos( $assetHandle, 'wpacu_hardcoded_' ) !== false ) {
if ( $ruleKey === 'current_unloaded_page_level' ) {
$currentHardcodedAssetRules .= '<input type="hidden" name="wpassetcleanup[' . $assetType . '][]" value="' . $assetHandle . '" />';
} elseif ( $ruleKey === 'load_exceptions' ) {
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_styles_load_it[]" value="' . $assetHandle . '" />';
} elseif ($ruleKey === 'handle_load_logged_in') {
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_load_it_logged_in['.$assetType.']['.$assetHandle.']" value="1" />';
}
}
}
}
}
}
}
return '<div class="wpacu-assets-collapsible-wrap wpacu-wrap-area wpacu-hardcoded" id="wpacu-assets-collapsible-wrap-hardcoded-list" data-wpacu-settings-frontend="'.esc_attr($dataWpacuSettingsFrontend).'">
<a class="wpacu-assets-collapsible wpacu-assets-collapsible-active" href="#" style="padding: 15px 15px 15px 44px;"><span class="dashicons dashicons-code-standards"></span> Hardcoded (non-enqueued) Styles &amp; Scripts</a>
<div class="wpacu-assets-collapsible-content" style="max-height: inherit;">
<div style="padding: 20px 0; margin: 0;"><img src="'.esc_url(admin_url('images/spinner.gif')).'" align="top" width="20" height="20" alt="" /> The list of hardcoded assets is fetched... Please wait...</div>
'.wp_kses($currentHardcodedAssetRules, array('input' => array('type' => array(), 'name' => array(), 'value' => array()))).'
</div>
</div>';
}
}

View File

@ -0,0 +1,326 @@
<?php
namespace WpAssetCleanUp;
use WpAssetCleanUp\OptimiseAssets\OptimizeCommon;
/**
* Class ImportExport
* @package WpAssetCleanUp
*/
class ImportExport
{
/***** BEGIN EXPORT ******/
/**
* @return string
*/
public function jsonSettings()
{
$wpacuSettings = new Settings();
$settingsArray = $wpacuSettings->getAll();
// Some "Site-wide Common Unloads" values are fetched outside the "Settings" option values
// e.g. jQuery Migrate, Comment Reply
$globalUnloadList = Main::instance()->getGlobalUnload();
// CSS
$settingsArray['disable_dashicons_for_guests'] = in_array( 'dashicons', $globalUnloadList['styles'] );
$settingsArray['disable_wp_block_library'] = in_array( 'wp-block-library', $globalUnloadList['styles'] );
// JS
$settingsArray['disable_jquery_migrate'] = in_array( 'jquery-migrate', $globalUnloadList['scripts'] );
$settingsArray['disable_comment_reply'] = in_array( 'comment-reply', $globalUnloadList['scripts'] );
return wp_json_encode($settingsArray);
}
/**
* Was the "Export" button clicked? Do verifications and send the right headers
*/
public function doExport()
{
if (! Menu::userCanManageAssets()) {
return;
}
if (! Misc::getVar('post', 'wpacu_do_export_nonce')) {
return;
}
$wpacuExportFor = Misc::getVar('post', 'wpacu_export_for');
if (! $wpacuExportFor) {
return;
}
// Last important check
\check_admin_referer('wpacu_do_export', 'wpacu_do_export_nonce');
$exportComment = 'Exported [exported_text] via '.WPACU_PLUGIN_TITLE.' (v'.WPACU_PLUGIN_VERSION.') - Timestamp: '.time();
// "Settings" values (could be just default ones if none are found in the database)
if ($wpacuExportFor === 'settings') {
$exportComment = str_replace('[exported_text]', 'Settings', $exportComment);
$settingsJson = $this->jsonSettings();
$valuesArray = array(
'__comment' => $exportComment,
'settings' => json_decode($settingsJson, ARRAY_A)
);
}
if ($wpacuExportFor === 'everything') {
$exportComment = str_replace('[exported_text]', 'Everything', $exportComment);
// "Settings"
$settingsJson = $this->jsonSettings();
// "Homepage"
$frontPageNoLoad = get_option(WPACU_PLUGIN_ID . '_front_page_no_load');
$frontPageNoLoadArray = json_decode($frontPageNoLoad, ARRAY_A);
$frontPageExceptionsListJson = get_option(WPACU_PLUGIN_ID . '_front_page_load_exceptions');
$frontPageExceptionsListArray = json_decode($frontPageExceptionsListJson, ARRAY_A);
// "Site-wide" Unloads
$globalUnloadListJson = get_option(WPACU_PLUGIN_ID . '_global_unload');
$globalUnloadArray = json_decode($globalUnloadListJson, ARRAY_A);
// "Bulk" unloads (for all pages, posts, custom post type)
$bulkUnloadListJson = get_option(WPACU_PLUGIN_ID . '_bulk_unload');
$bulkUnloadArray = json_decode($bulkUnloadListJson, ARRAY_A);
// Post type: load exceptions
$postTypeLoadExceptionsJson = get_option(WPACU_PLUGIN_ID . '_post_type_load_exceptions');
$postTypeLoadExceptionsArray = json_decode($postTypeLoadExceptionsJson, ARRAY_A);
$globalDataListJson = get_option(WPACU_PLUGIN_ID . '_global_data');
$globalDataListArray = json_decode($globalDataListJson, ARRAY_A);
// [wpacu_pro]
// Load exceptions for extras: is_archive(), author, search, 404 pages
$extrasLoadExceptionsListJson = get_option(WPACU_PLUGIN_ID . '_extras_load_exceptions');
$extrasLoadExceptionsArray = json_decode($extrasLoadExceptionsListJson, ARRAY_A);
// Load exceptions for pages that have certain taxonomies set
$postTypeViaTaxLoadExceptionsJson = get_option(WPACU_PLUGIN_ID . '_post_type_via_tax_load_exceptions');
$postTypeViaTaxLoadExceptionsArray = json_decode($postTypeViaTaxLoadExceptionsJson, ARRAY_A);
// [/wpacu_pro]
global $wpdb;
$allMetaResults = array();
$metaKeyLike = '_' . WPACU_PLUGIN_ID . '_%';
$tableList = array($wpdb->postmeta);
foreach ($tableList as $tableName) {
if ( $tableName === $wpdb->postmeta ) {
$sqlFetchPostsMetas = <<<SQL
SELECT post_id, meta_key, meta_value FROM `{$wpdb->postmeta}` WHERE meta_key LIKE '{$metaKeyLike}'
SQL;
$allMetaResults['postmeta'] = $wpdb->get_results( $sqlFetchPostsMetas, ARRAY_A );
}
}
// Export Field Names should be kept as they are and in case
// they are changed later on, a fallback should be in place
$valuesArray = array(
'__comment' => $exportComment,
'settings' => json_decode($settingsJson, ARRAY_A),
'homepage' => array(
'unloads' => $frontPageNoLoadArray,
'load_exceptions' => $frontPageExceptionsListArray
),
'global_unload' => $globalUnloadArray,
'bulk_unload' => $bulkUnloadArray,
'post_type_exceptions' => $postTypeLoadExceptionsArray,
// [wpacu_pro]
'post_type_via_tax_exceptions' => $postTypeViaTaxLoadExceptionsArray,
// [/wpacu_pro]
'global_data' => $globalDataListArray,
// [wpacu_pro]
'extras_exceptions' => $extrasLoadExceptionsArray,
// [/wpacu_pro]
'posts_metas' => $allMetaResults['postmeta'],
);
} else {
return; // has to be "Settings" or "Everything"
}
// Was the right selection made? Continue
$date = date('j-M-Y-H.i');
$host = parse_url(site_url(), PHP_URL_HOST);
$wpacuExportForPartOfFileName = str_replace('_', '-', $wpacuExportFor);
header('Content-Type: application/json');
header('Content-Disposition: attachment; filename="asset-cleanup-lite-exported-'.$wpacuExportForPartOfFileName.'-from-'.$host.'-'.$date.'.json"');
echo wp_json_encode($valuesArray);
exit();
}
/***** END EXPORT ******/
/***** BEGIN IMPORT ******/
/**
*
*/
public function doImport()
{
if (! Menu::userCanManageAssets()) {
return;
}
if (! Misc::getVar('post', 'wpacu_do_import_nonce')) {
return;
}
$jsonTmpName = isset($_FILES['wpacu_import_file']['tmp_name']) ? $_FILES['wpacu_import_file']['tmp_name'] : false;
if (! $jsonTmpName) {
return;
}
// Last important check
\check_admin_referer('wpacu_do_import', 'wpacu_do_import_nonce');
if (! is_file($jsonTmpName)) {
return;
}
$valuesJson = FileSystem::fileGetContents($jsonTmpName);
$valuesArray = json_decode($valuesJson, ARRAY_A);
if ( ! (JSON_ERROR_NONE === Misc::jsonLastError())) {
return;
}
$importedList = array();
// NOTE: The values are not replaced, but added to the existing ones (if any)
// "Settings" (Replace)
if (isset($valuesArray['settings']) && ! empty($valuesArray['settings'])) {
$wpacuSettings = new Settings();
// "Site-wide Common Unloads" - apply settings
// JS
$disableJQueryMigrate = isset( $valuesArray['settings']['disable_jquery_migrate'] ) ? $valuesArray['settings']['disable_jquery_migrate'] : false;
$disableCommentReply = isset( $valuesArray['settings']['disable_comment_reply'] ) ? $valuesArray['settings']['disable_comment_reply'] : false;
// CSS
$disableGutenbergCssBlockLibrary = isset( $valuesArray['settings']['disable_wp_block_library'] ) ? $valuesArray['settings']['disable_wp_block_library'] : false;
$disableDashiconsForGuests = isset( $valuesArray['settings']['disable_dashicons_for_guests'] ) ? $valuesArray['settings']['disable_dashicons_for_guests'] : false;
$wpacuSettings->updateSiteWideRuleForCommonAssets(
array(
// JS
'jquery_migrate' => $disableJQueryMigrate,
'comment_reply' => $disableCommentReply,
// CSS
'wp_block_library' => $disableGutenbergCssBlockLibrary,
'dashicons' => $disableDashiconsForGuests,
)
);
Misc::addUpdateOption(WPACU_PLUGIN_ID . '_settings', wp_json_encode($valuesArray['settings']));
$importedList[] = 'settings';
}
// "Homepage" Unloads
if (isset($valuesArray['homepage']['unloads']['scripts'])
|| isset($valuesArray['homepage']['unloads']['styles'])) {
Misc::addUpdateOption(WPACU_PLUGIN_ID . '_front_page_no_load', wp_json_encode($valuesArray['homepage']['unloads']));
$importedList[] = 'homepage_unloads';
}
// "Homepage" Load Exceptions
if (isset($valuesArray['homepage']['load_exceptions']['scripts'])
|| isset($valuesArray['homepage']['load_exceptions']['styles'])) {
Misc::addUpdateOption(WPACU_PLUGIN_ID . '_front_page_load_exceptions', wp_json_encode($valuesArray['homepage']['load_exceptions']));
$importedList[] = 'homepage_exceptions';
}
// "Site-Wide" (Everywhere) Unloads
if (isset($valuesArray['global_unload']['scripts'])
|| isset($valuesArray['global_unload']['styles'])) {
Misc::addUpdateOption(WPACU_PLUGIN_ID . '_global_unload', wp_json_encode($valuesArray['global_unload']));
$importedList[] = 'sitewide_unloads';
}
// Bulk Unloads (e.g. Unload on all pages of product post type)
if (isset($valuesArray['bulk_unload']['scripts'])
|| isset($valuesArray['bulk_unload']['styles'])) {
Misc::addUpdateOption(WPACU_PLUGIN_ID . '_bulk_unload', wp_json_encode($valuesArray['bulk_unload']));
$importedList[] = 'bulk_unload';
}
// Post type: load exception
if (isset($valuesArray['post_type_exceptions']) && ! empty($valuesArray['post_type_exceptions'])) {
Misc::addUpdateOption(WPACU_PLUGIN_ID . '_post_type_load_exceptions', wp_json_encode($valuesArray['post_type_exceptions']));
$importedList[] = 'post_type_load_exceptions';
}
// [wpacu_pro]
// Post type: load exception via taxonomy
if (isset($valuesArray['post_type_via_tax_exceptions']) && ! empty($valuesArray['post_type_via_tax_exceptions'])) {
Misc::addUpdateOption(WPACU_PLUGIN_ID . '_post_type_via_tax_exceptions', wp_json_encode($valuesArray['post_type_via_tax_exceptions']));
$importedList[] = 'post_type_via_tax_exceptions';
}
// [/wpacu_pro]
// Global Data
if (isset($valuesArray['global_data']['scripts'])
|| isset($valuesArray['global_data']['styles'])) {
Misc::addUpdateOption(WPACU_PLUGIN_ID . '_global_data', wp_json_encode($valuesArray['global_data']));
$importedList[] = 'global_data';
}
// [START] All Posts Metas (per page unloads, load exceptions, page options from side meta box)
$targetKey = 'posts_metas';
if (isset($valuesArray[$targetKey]) && ! empty($valuesArray[$targetKey])) {
foreach ($valuesArray[$targetKey] as $metaValues) {
// It needs to have a post ID and meta key starting with _' . WPACU_PLUGIN_ID . '
if ( ! (isset($metaValues['post_id'], $metaValues['meta_key'])
&& strpos($metaValues['meta_key'], '_' . WPACU_PLUGIN_ID) === 0) ) {
continue;
}
$postId = $metaValues['post_id'];
$metaKey = $metaValues['meta_key'];
$metaValue = $metaValues['meta_value']; // already JSON encoded
if (! add_post_meta($postId, $metaKey, $metaValue, true)) {
update_post_meta($postId, $metaKey, $metaValue);
}
}
$importedList[] = 'posts_metas';
}
// [END] All Posts Metas (per page unloads, load exceptions, page options from side meta box)
if (! empty($importedList)) {
// After import was completed, clear all CSS/JS cache
OptimizeCommon::clearCache();
set_transient('wpacu_import_done', $importedList, 30);
wp_redirect(admin_url('admin.php?page=wpassetcleanup_tools&wpacu_for=import_export&wpacu_import_done=1&wpacu_time=' . time()));
exit();
}
}
/***** END IMPORT ******/
}

View File

@ -0,0 +1,136 @@
<?php
namespace WpAssetCleanUp;
/**
* Gets information pages such as "Getting Started", "Help" and "Info"
* Retrieves specific information about a plugin or a theme
*
* Class Info
* @package WpAssetCleanUp
*/
class Info
{
/**
* Info constructor.
*/
public function __construct()
{
add_action('wpacu_assets_plugin_notice_table_row', array($this, 'pluginNotice'));
}
/**
*
*/
public function gettingStarted()
{
$data = array('for' => 'how-it-works');
if (isset($_GET['wpacu_for'])) {
$data['for'] = sanitize_text_field($_GET['wpacu_for']);
}
Main::instance()->parseTemplate('admin-page-getting-started', $data, true);
}
/**
*
*/
public function help()
{
Main::instance()->parseTemplate('admin-page-get-help', array(), true);
}
/**
*
*/
public function pagesInfo()
{
Main::instance()->parseTemplate('admin-page-pages-info', array(), true);
}
/**
*
*/
public function license()
{
Main::instance()->parseTemplate('admin-page-license', array(), true);
}
/**
* @param $locationChild
* @param $allPlugins
* @param $allActivePluginsIcons
*
* @return string
*/
public static function getPluginInfo($locationChild, $allPlugins, $allActivePluginsIcons)
{
foreach (array_keys($allPlugins) as $pluginFile) {
if (strpos($pluginFile, $locationChild.'/') === 0) {
$imageIconStyle = $classIconStyle = '';
if (isset($allActivePluginsIcons[$locationChild]) && $allActivePluginsIcons[$locationChild]) {
$classIconStyle = 'has-icon';
$imageIconStyle = 'style="background: transparent url(\''.$allActivePluginsIcons[$locationChild].'\') no-repeat 0 0; background-size: cover;"';
}
return '<div class="icon-plugin-default '.$classIconStyle.'"><div class="icon-area" '.$imageIconStyle.'></div></div> &nbsp; <span class="wpacu-child-location-name">'.$allPlugins[$pluginFile]['Name'].'</span>' . ' <span class="wpacu-child-location-version">v'.$allPlugins[$pluginFile]['Version'].'</span>';
}
}
return $locationChild;
}
/**
* @param $locationChild
* @param $allThemes
*
* @return array
*/
public static function getThemeInfo($locationChild, $allThemes)
{
foreach (array_keys($allThemes) as $themeDir) {
if ($locationChild === $themeDir) {
$themeInfo = wp_get_theme($themeDir);
$themeIconUrl = Misc::getThemeIcon($themeInfo->get('Name'));
$themeIconHtml = '';
$hasIcon = false;
if ($themeIconUrl) {
$hasIcon = true;
$imageIconStyle = 'style="background: transparent url(\''.$themeIconUrl.'\') no-repeat 0 0; background-size: cover;"';
$themeIconHtml = '<div class="icon-theme has-icon"><div class="icon-area" '.$imageIconStyle.'></div></div>';
}
$output = $themeIconHtml . $themeInfo->get('Name') . ' <span class="wpacu-child-location-version">v'.$themeInfo->get('Version').'</span>';
return array('has_icon' => $hasIcon, 'output' => $output);
}
}
return array('has_icon' => false, 'output' => $locationChild);
}
/**
* Notices about consequences in unloading assets from specific plugins
*
* @param $plugin
*/
public function pluginNotice($plugin)
{
// Elementor, Elementor Pro
if (in_array($plugin, array('elementor', 'elementor-pro'))) {
$wpacuPluginTitle = WPACU_PLUGIN_TITLE;
?>
<tr class="wpacu_asset_row wpacu_notice_row">
<td valign="top">
<div class="wpacu-warning">
<p style="margin: 0 0 4px !important;"><small><span class="dashicons dashicons-warning"></span> Most (if not all) of this plugin's files are linked (child &amp; parent) for maximum compatibility. Unloading one Elementor CSS/JS will likely trigger the unloading of other "children" associated with it. <strong>To avoid breaking the Elementor editor, <?php echo esc_html($wpacuPluginTitle); ?> is deactivated in the page builder's edit &amp; preview mode. If this page is not edited via Elementor and you don't need any of the plugin's functionality (widgets, templates etc.) here, you can unload the files below making sure to test the page after you updated it.</strong></small></p>
</div>
</td>
</tr>
<?php
}
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace WpAssetCleanUp;
/**
* The code is triggered only in Asset CleanUp Lite
*
* Class Lite
* @package WpAssetCleanUp
*/
class Lite
{
/**
* Lite constructor.
*/
public function __construct()
{
add_action('current_screen', array($this, 'currentScreen'));
}
/**
*
*/
public function currentScreen()
{
$current_screen = \get_current_screen();
if ($current_screen->base === 'term' && isset($current_screen->taxonomy) && $current_screen->taxonomy != '') {
add_action ($current_screen->taxonomy . '_edit_form_fields', static function ($tag) {
?>
<tr class="form-field">
<th scope="row" valign="top"><label for="wpassetcleanup_list"><?php echo WPACU_PLUGIN_TITLE; ?>: <?php _e('CSS &amp; JavaScript Load Manager', 'wp-asset-clean-up'); ?></label></th>
<td data-wpacu-taxonomy="<?php echo esc_attr($tag->taxonomy); ?>">
<img width="20" height="20" src="<?php echo esc_url(WPACU_PLUGIN_URL); ?>/assets/icons/icon-lock.svg" valign="top" alt="" /> &nbsp;
<?php
echo sprintf(
__('Managing the loading of the styles &amp; scripts files for this <strong>%s</strong> taxonomy is %savailable in the Pro version%s', 'wp-asset-clean-up'),
$tag->taxonomy,
'<a href="'.apply_filters('wpacu_go_pro_affiliate_link', WPACU_PLUGIN_GO_PRO_URL.'?utm_source=taxonomy_edit_page&utm_medium=upgrade_link').'" target="_blank">',
'</a>'
);
?>
</td>
</tr>
<?php
});
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,696 @@
<?php
namespace WpAssetCleanUp;
use WpAssetCleanUp\OptimiseAssets\OptimizeCommon;
/**
* Class Maintenance
* @package WpAssetCleanUp
*/
class Maintenance
{
/**
* Maintenance constructor.
*/
public function __construct()
{
// Schedule cron events
add_action('wp', array($this, 'scheduleEvents'));
add_action('init', array($this, 'scheduleTrigger'));
if (is_admin() && Menu::isPluginPage()) {
add_action('admin_init', static function() {
Maintenance::cleanUnusedAssetsFromInfoArea();
Maintenance::combineNewOptionUpdate(); // Since v1.1.7.3 (Pro) & v1.3.6.4 (Lite)
OptimizeCommon::limitAlreadyMarkedAsMinified(); // Since v1.1.7.4 (Pro) & v1.3.6.6 (Lite)
});
}
add_action('init', static function() {
if ( is_user_logged_in() && Menu::userCanManageAssets() ) {
Maintenance::combineNewOptionUpdate(); // Since v1.1.7.3 (Pro) & v1.3.6.4 (Lite)
}
});
}
/**
* Schedule events
*
* @access private
* @since 1.6
* @return void
*/
public function scheduleEvents()
{
// Daily events
if (! wp_next_scheduled('wpacu_daily_scheduled_events')) {
wp_schedule_event(current_time('timestamp', true), 'daily', 'wpacu_daily_scheduled_events');
}
}
/**
* Trigger scheduled events
*
* @return void
*/
public function scheduleTrigger()
{
// Debugging purposes: trigger directly the code meant to be scheduled
if (current_user_can('administrator')) {
if (isset($_GET['wpacu_toggle_inline_code_to_combined_assets'])) {
self::updateAppendOrNotInlineCodeToCombinedAssets(true);
}
if (isset($_GET['wpacu_clear_cache_conditionally'])) {
self::updateAppendOrNotInlineCodeToCombinedAssets(true);
}
}
if (Misc::doingCron()) {
add_action('wpacu_daily_scheduled_events', array($this, 'triggerDailyScheduleEvents'));
}
}
/**
*
*/
public static function triggerDailyScheduleEvents()
{
self::updateAppendOrNotInlineCodeToCombinedAssets();
self::clearCacheConditionally();
}
/**
* @param false $isDebug
*/
public static function updateAppendOrNotInlineCodeToCombinedAssets($isDebug = false)
{
// WordPress Version below 5.5? Skip appending CSS/JS code to the combine CSS/JS list
if ( ! Misc::isWpVersionAtLeast('5.5') ) {
$settingsClass = new Settings();
$optionsToUpdate = array(
'_combine_loaded_css_append_handle_extra' => '',
'_combine_loaded_js_append_handle_extra' => ''
);
$settingsClass->updateOption(array_keys($optionsToUpdate), array_values($optionsToUpdate));
if ($isDebug) {
echo 'The WordPress version is below 5.5, thus there is no appending of the inline CSS/JS code to the combine CSS/JS list';
}
} else {
// Check if there are too many .css /.js combined files in the caching directory and change settings
// to prevent the appending of the inline CSS/JS code that is likely the culprit of so many files
$settingsClass = new Settings();
$settings = $settingsClass->getAll( true );
$settingsClass::toggleAppendInlineAssocCodeHiddenSettings( $settings, true, $isDebug );
}
if ($isDebug) {
exit();
}
}
/**
* @param false $isDebug
*/
public static function clearCacheConditionally($isDebug = false)
{
// Clear caching if it wasn't cleared in the past 24 hours (e.g. the admin hasn't used the plugin for a while)
$wpacuLastClearCache = get_transient('wpacu_last_clear_cache');
if ($isDebug) {
echo 'Cache cleared last time: '.$wpacuLastClearCache.'<br />';
}
if ($wpacuLastClearCache && (strtotime( '-1 days' ) > $wpacuLastClearCache)) {
OptimizeCommon::clearCache();
if ($isDebug) {
echo 'The cache was just cleared as it was not cleared in the past 24 hours.<br />';
}
}
}
/**
*
*/
public static function cleanUnusedAssetsFromInfoArea()
{
$allAssetsWithAtLeastOneRule = Overview::handlesWithAtLeastOneRule();
if (empty($allAssetsWithAtLeastOneRule)) {
return;
}
// Stored in the "assets_info" key from "wpassetcleanup_global_data" option name (from `{$wpdb->prefix}options` table)
$allAssetsFromInfoArea = Main::getHandlesInfo();
$handlesToClearFromInfo = array('styles' => array(), 'scripts' => array());
foreach (array('styles', 'scripts') as $assetType) {
if ( isset( $allAssetsFromInfoArea[$assetType] ) && ! empty( $allAssetsFromInfoArea[$assetType] ) ) {
foreach ( array_keys( $allAssetsFromInfoArea[$assetType] ) as $assetHandle ) {
if ( ! isset($allAssetsWithAtLeastOneRule[$assetType][$assetHandle]) ) { // not found in $allAssetsWithAtLeastOneRule? Add it to the clear list
$handlesToClearFromInfo[$assetType][] = $assetHandle;
}
}
}
}
if (! empty($handlesToClearFromInfo['styles']) || ! empty($handlesToClearFromInfo['scripts'])) {
self::removeHandlesInfoFromGlobalDataOption($handlesToClearFromInfo);
}
}
/**
*
*/
public static function combineNewOptionUpdate()
{
$settingsClass = new Settings();
$pluginSettings = $settingsClass->getAll();
if ( ($pluginSettings['combine_loaded_css'] === 'for_admin' ||
(isset($pluginSettings['combine_loaded_css_for_admin_only']) && $pluginSettings['combine_loaded_css_for_admin_only'] == 1) )
&& Menu::userCanManageAssets() ) {
$settingsClass->updateOption('combine_loaded_css', '');
$settingsClass->updateOption('combine_loaded_css_for_admin_only', '');
}
if ( ($pluginSettings['combine_loaded_js'] === 'for_admin' ||
(isset($pluginSettings['combine_loaded_js_for_admin_only']) && $pluginSettings['combine_loaded_js_for_admin_only'] == 1) )
&& Menu::userCanManageAssets() ) {
$settingsClass->updateOption('combine_loaded_js', '');
$settingsClass->updateOption('combine_loaded_js_for_admin_only', '');
}
}
/**
* @param $handlesToClearFromInfo
*/
public static function removeHandlesInfoFromGlobalDataOption($handlesToClearFromInfo)
{
$optionToUpdate = WPACU_PLUGIN_ID . '_global_data';
$globalKey = 'assets_info';
$existingListEmpty = array('styles' => array($globalKey => array()), 'scripts' => array($globalKey => array()));
$existingListJson = get_option($optionToUpdate);
$existingListData = Main::instance()->existingList($existingListJson, $existingListEmpty);
$existingList = $existingListData['list'];
// $assetType could be 'styles' or 'scripts'
foreach ($handlesToClearFromInfo as $assetType => $handles) {
foreach ($handles as $handle) {
if ( isset( $existingList[ $assetType ][ $globalKey ][ $handle ] ) ) {
unset( $existingList[ $assetType ][ $globalKey ][ $handle ] );
}
}
}
Misc::addUpdateOption($optionToUpdate, wp_json_encode(Misc::filterList($existingList)));
}
/**
* When is this needed? Sometimes, you have rules such as "Unload site-wide (everywhere)" and load exceptions associated to it
* However, if you remove the unload rule, then the load exceptions associated with it will become useless as they worth together
* If no unload rule is added, the file is loaded anyway, it doesn't need any load exception obviously
*
* @param $assetHandle
* @param $assetType | it belongs to "styles" or "scripts"
*/
public static function removeAllLoadExceptionsFor($assetHandle, $assetType)
{
/*
* Any in the front-page?
*/
$wpacuFrontPageLoadExceptions = get_option(WPACU_PLUGIN_ID . '_front_page_load_exceptions');
if ($wpacuFrontPageLoadExceptions) {
$wpacuFrontPageLoadExceptionsArray = @json_decode( $wpacuFrontPageLoadExceptions, ARRAY_A );
$targetArray = isset($wpacuFrontPageLoadExceptionsArray[$assetType]) && is_array($wpacuFrontPageLoadExceptionsArray[$assetType])
? $wpacuFrontPageLoadExceptionsArray[$assetType]
: array();
if ( in_array($assetHandle, $targetArray) ) {
$targetKey = array_search($assetHandle, $targetArray);
unset($wpacuFrontPageLoadExceptionsArray[$assetType][$targetKey]); // clear the exception
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_front_page_load_exceptions',
wp_json_encode(Misc::filterList($wpacuFrontPageLoadExceptionsArray))
);
}
}
/*
* Any for all pages of a certain post type? (e.g. in all WooCommerce "product" pages)
*/
$wpacuPostTypeLoadExceptions = get_option(WPACU_PLUGIN_ID . '_post_type_load_exceptions');
if ($wpacuPostTypeLoadExceptions) {
$wpacuPostTypeLoadExceptionsArray = @json_decode( $wpacuPostTypeLoadExceptions, ARRAY_A );
if (! empty($wpacuPostTypeLoadExceptionsArray)) {
foreach ($wpacuPostTypeLoadExceptionsArray as $dbPostType => $dbList) {
foreach ($dbList as $dbAssetType => $dbValues) {
if ($assetType === $dbAssetType && array_key_exists( $assetHandle, $dbValues ) ) {
unset($wpacuPostTypeLoadExceptionsArray[$dbPostType][$dbAssetType][$assetHandle]);
}
}
}
}
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_post_type_load_exceptions',
wp_json_encode(Misc::filterList($wpacuPostTypeLoadExceptionsArray))
);
}
global $wpdb;
$wpacuPluginId = WPACU_PLUGIN_ID;
/*
* Any for posts (any kind), pages, taxonomies or users?
*/
foreach (array($wpdb->postmeta, $wpdb->termmeta, $wpdb->usermeta) as $tableName) {
$wpacuGetValuesQuery = <<<SQL
SELECT * FROM `{$tableName}` WHERE meta_key='_{$wpacuPluginId}_load_exceptions'
SQL;
$wpacuMetaData = $wpdb->get_results( $wpacuGetValuesQuery, ARRAY_A );
foreach ( $wpacuMetaData as $wpacuValues ) {
$decodedValues = @json_decode( $wpacuValues['meta_value'], ARRAY_A );
if ( empty( $decodedValues ) ) {
continue;
}
if ( isset( $decodedValues[ $assetType ] ) &&
is_array( $decodedValues[ $assetType ] ) &&
in_array( $assetHandle, $decodedValues[ $assetType ] ) ) {
$targetKey = array_search( $assetHandle, $decodedValues[ $assetType ] );
unset( $decodedValues[ $assetType ][ $targetKey ] ); // clear the exception
} else {
continue; // no point in re-updating the database with the same values
}
$newList = wp_json_encode(Misc::filterList($decodedValues));
if ( $tableName === $wpdb->postmeta ) {
update_post_meta($wpacuValues['post_id'], '_'.$wpacuPluginId.'_load_exceptions', $newList);
} elseif ( $tableName === $wpdb->termmeta ) {
update_term_meta($wpacuValues['term_id'], '_'.$wpacuPluginId.'_load_exceptions', $newList);
} else {
update_user_meta($wpacuValues['user_id'], '_'.$wpacuPluginId.'_load_exceptions', $newList);
}
}
}
/*
* Any load exceptions for 404, search, date pages?
*/
$wpacuExtrasLoadExceptions = get_option(WPACU_PLUGIN_ID . '_extras_load_exceptions');
if ($wpacuExtrasLoadExceptions) {
$wpacuExtrasLoadExceptionsArray = @json_decode( $wpacuExtrasLoadExceptions, ARRAY_A );
// $forKey could be '404', 'search', 'date', etc.
foreach ($wpacuExtrasLoadExceptionsArray as $forKey => $values) {
$targetArray = isset( $values[ $assetType ] ) && is_array( $values[ $assetType ] ) ? $values[ $assetType ] : array();
if ( in_array( $assetHandle, $targetArray ) ) {
$targetKey = array_search( $assetHandle, $targetArray );
unset( $wpacuExtrasLoadExceptionsArray[ $forKey ][ $assetType ][ $targetKey ] ); // clear the exception
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_extras_load_exceptions',
wp_json_encode( Misc::filterList( $wpacuExtrasLoadExceptionsArray ) )
);
}
}
}
/*
* Any (RegEx / Logged-in User) load exceptions?
*/
$dbListJson = get_option(WPACU_PLUGIN_ID . '_global_data');
$globalKeys = array('load_regex', 'load_it_logged_in');
foreach ($globalKeys as $globalKey) {
if ( $dbListJson ) {
$dbList = @json_decode( $dbListJson, ARRAY_A );
$targetArray = isset( $dbList[ $assetType ][ $globalKey ] ) &&
is_array( $dbList[ $assetType ][ $globalKey ] )
? $dbList[ $assetType ][ $globalKey ] : array();
if ( array_key_exists( $assetHandle, $targetArray ) ) {
unset( $dbList[ $assetType ][ $globalKey ][ $assetHandle ] ); // clear the exception
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_global_data',
wp_json_encode( Misc::filterList( $dbList ) )
);
}
}
}
}
/**
* @param $assetHandle
* @param $assetType
*/
public static function removeAllRulesFor($assetHandle, $assetType)
{
// Clear any load exception rules
self::removeAllLoadExceptionsFor($assetHandle, $assetType);
/*
* Table: WPACU_PLUGIN_ID . '_global_data'
* Global (Site-wide) Rules: Preloading, Position changing, Unload via RegEx, etc.
*/
$wpacuGlobalData = get_option(WPACU_PLUGIN_ID . '_global_data');
$wpacuGlobalDataArray = @json_decode($wpacuGlobalData, ARRAY_A);
foreach ( array(
'404',
'assets_info',
'date',
'everywhere',
'ignore_child',
'load_it_logged_in',
'load_regex',
'notes',
'positions',
'preloads',
'search',
'unload_regex' ) as $dataType ) {
if ( isset( $wpacuGlobalDataArray[ $assetType ][ $dataType ] ) && ! empty( $wpacuGlobalDataArray[ $assetType ][ $dataType ] ) && array_key_exists($assetHandle, $wpacuGlobalDataArray[ $assetType ][ $dataType ]) ) {
unset( $wpacuGlobalDataArray[ $assetType ][ $dataType ][ $assetHandle ]);
}
}
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_global_data',
wp_json_encode( Misc::filterList( $wpacuGlobalDataArray ) )
);
/*
* Table: WPACU_PLUGIN_ID . '_global_unload'
* Unload Site-Wide (Everywhere)
*/
$wpacuGlobalUnloadData = get_option(WPACU_PLUGIN_ID . '_global_unload');
$wpacuGlobalUnloadDataArray = @json_decode($wpacuGlobalUnloadData, ARRAY_A);
if (isset($wpacuGlobalUnloadDataArray[$assetType]) && ! empty($wpacuGlobalUnloadDataArray[$assetType]) && in_array($assetHandle, $wpacuGlobalUnloadDataArray[$assetType])) {
$targetKey = array_search($assetHandle, $wpacuGlobalUnloadDataArray[$assetType]);
unset($wpacuGlobalUnloadDataArray[$assetType][$targetKey]);
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_global_unload',
wp_json_encode( Misc::filterList( $wpacuGlobalUnloadDataArray ) )
);
}
/*
* Table: WPACU_PLUGIN_ID . '_bulk_unload'
* Bulk Unload
*/
$wpacuBulkUnloadData = get_option(WPACU_PLUGIN_ID . '_bulk_unload');
$wpacuBulkUnloadDataArray = @json_decode($wpacuBulkUnloadData, ARRAY_A);
if (isset($wpacuBulkUnloadDataArray[$assetType]) && ! empty($wpacuBulkUnloadDataArray[$assetType])) {
foreach ($wpacuBulkUnloadDataArray[$assetType] as $unloadBulkType => $unloadBulkValues) {
// $unloadBulkType could be 'post_type', 'date', '404', 'taxonomy', 'search', 'custom_post_type_archive_[custom_post_type]'
if ($unloadBulkType === 'post_type') {
foreach ($unloadBulkValues as $postType => $assetHandles) {
if (in_array($assetHandle, $assetHandles)) {
$targetKey = array_search($assetHandle, $assetHandles);
unset($wpacuBulkUnloadDataArray[$assetType][$unloadBulkType][$postType][$targetKey]);
}
}
} elseif (in_array($unloadBulkType, array('date', '404', 'search')) || (strpos($unloadBulkType, 'custom_post_type_archive_') !== false)) {
if (in_array($assetHandle, $unloadBulkValues)) {
$targetKey = array_search($assetHandle, $unloadBulkValues);
unset($wpacuBulkUnloadDataArray[$assetType][$unloadBulkType][$targetKey]);
}
} elseif ($unloadBulkType === 'taxonomy') {
foreach ($unloadBulkValues as $taxonomyType => $assetHandles) {
if (in_array($assetHandle, $assetHandles)) {
$targetKey = array_search($assetHandle, $assetHandles);
unset($wpacuBulkUnloadDataArray[$assetType][$unloadBulkType][$taxonomyType][$targetKey]);
}
}
} elseif ($unloadBulkType === 'author' && isset($unloadBulkValues['all']) && ! empty($unloadBulkValues['all'])) {
foreach ($unloadBulkValues['all'] as $assetHandles) {
if (in_array($assetHandle, $assetHandles)) {
$targetKey = array_search($assetHandle, $assetHandles);
unset($wpacuBulkUnloadDataArray[$assetType][$unloadBulkType]['all'][$targetKey]);
}
}
}
}
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_bulk_unload',
wp_json_encode( Misc::filterList( $wpacuBulkUnloadDataArray ) )
);
}
/*
* Table: WPACU_PLUGIN_ID . '_front_page_no_load'
* Homepage (Unloads)
*/
$wpacuFrontPageUnloads = get_option(WPACU_PLUGIN_ID . '_front_page_no_load');
if ($wpacuFrontPageUnloads) {
$wpacuFrontPageUnloadsArray = @json_decode( $wpacuFrontPageUnloads, ARRAY_A );
if ( isset( $wpacuFrontPageUnloadsArray[$assetType] ) && ! empty( $wpacuFrontPageUnloadsArray[$assetType] ) && in_array( $assetHandle, $wpacuFrontPageUnloadsArray[$assetType] ) ) {
$targetKey = array_search($assetHandle, $wpacuFrontPageUnloadsArray[$assetType]);
unset($wpacuFrontPageUnloadsArray[$assetType][$targetKey]);
}
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_front_page_no_load',
wp_json_encode( Misc::filterList( $wpacuFrontPageUnloadsArray ) )
);
}
/*
* Table: WPACU_PLUGIN_ID . '_front_page_data'
* Homepage (async, defer)
*/
$wpacuFrontPageData = ($assetType === 'scripts') && get_option(WPACU_PLUGIN_ID . '_front_page_data');
if ($wpacuFrontPageData) {
$wpacuFrontPageDataArray = @json_decode( $wpacuFrontPageData, ARRAY_A );
if ( isset( $wpacuFrontPageDataArray[$assetType][$assetHandle] ) ) {
unset( $wpacuFrontPageDataArray[$assetType][$assetHandle] );
}
if ( isset( $wpacuFrontPageDataArray['scripts_attributes_no_load'][$assetHandle] ) ) {
unset( $wpacuFrontPageDataArray['scripts_attributes_no_load'][$assetHandle] );
}
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_front_page_data',
wp_json_encode( Misc::filterList( $wpacuFrontPageDataArray ) )
);
}
/*
* Tables: $wpdb->postmeta, $wpdb->termmeta, $wpdb->usermeta (all part of the standard WordPress tables)
* Plugin meta keys: _{$wpacuPluginId}_no_load', _{$wpacuPluginId}_data
* Get all Asset CleanUp (Pro) meta keys from all WordPress meta tables where it can be possibly used
*
*/
global $wpdb;
$wpacuPluginId = WPACU_PLUGIN_ID;
foreach (array($wpdb->postmeta, $wpdb->termmeta, $wpdb->usermeta) as $tableName) {
$wpacuGetValuesQuery = <<<SQL
SELECT * FROM `{$tableName}`
WHERE meta_key IN('_{$wpacuPluginId}_no_load', '_{$wpacuPluginId}_data')
SQL;
$wpacuMetaData = $wpdb->get_results( $wpacuGetValuesQuery, ARRAY_A );
foreach ( $wpacuMetaData as $wpacuValues ) {
$decodedValues = @json_decode( $wpacuValues['meta_value'], ARRAY_A );
// No load rules
if ( $wpacuValues['meta_key'] === '_' . $wpacuPluginId . '_no_load' && isset( $decodedValues[$assetType] ) && in_array($assetHandle, $decodedValues[$assetType]) ) {
$targetKey = array_search($assetHandle, $decodedValues[$assetType]);
unset($decodedValues[$assetType][$targetKey]);
if ($tableName === $wpdb->postmeta) {
update_post_meta($wpacuValues['post_id'], '_' . $wpacuPluginId . '_no_load', wp_json_encode( Misc::filterList( $decodedValues ) ) );
} elseif ($tableName === $wpdb->termmeta) {
update_term_meta($wpacuValues['term_id'], '_' . $wpacuPluginId . '_no_load', wp_json_encode( Misc::filterList( $decodedValues ) ) );
} elseif ($tableName === $wpdb->usermeta) {
update_user_meta($wpacuValues['user_id'], '_' . $wpacuPluginId . '_no_load', wp_json_encode( Misc::filterList( $decodedValues ) ) );
}
}
// Other rules such as script attribute (e.g. async, defer)
if ( $wpacuValues['meta_key'] === '_' . $wpacuPluginId . '_data' ) {
if ( isset( $decodedValues[$assetType][$assetHandle] ) ) {
unset( $decodedValues[ $assetType ][ $assetHandle ] );
}
// Load exceptions for script attributes
if ( $assetType === 'scripts' && isset( $decodedValues['scripts_attributes_no_load'][$assetHandle] ) ) {
unset($decodedValues['scripts_attributes_no_load'][$assetHandle]);
}
if ($tableName === $wpdb->postmeta) {
update_post_meta($wpacuValues['post_id'], '_' . $wpacuPluginId . '_data', wp_json_encode( Misc::filterList( $decodedValues ) ) );
} elseif ($tableName === $wpdb->termmeta) {
update_term_meta($wpacuValues['term_id'], '_' . $wpacuPluginId . '_data', wp_json_encode( Misc::filterList( $decodedValues ) ) );
} elseif ($tableName === $wpdb->usermeta) {
update_user_meta($wpacuValues['user_id'], '_' . $wpacuPluginId . '_data', wp_json_encode( Misc::filterList( $decodedValues ) ) );
}
}
}
}
}
/**
* Remove all unload rules apart from the site-wide one
*
* @param $assetHandle
* @param $assetType
*/
public static function removeAllRedundantUnloadRulesFor($assetHandle, $assetType)
{
/*
* Table: WPACU_PLUGIN_ID . '_front_page_no_load'
* Homepage (Unloads)
*/
$wpacuFrontPageUnloads = get_option(WPACU_PLUGIN_ID . '_front_page_no_load');
if ($wpacuFrontPageUnloads) {
$wpacuFrontPageUnloadsArray = @json_decode( $wpacuFrontPageUnloads, ARRAY_A );
if ( isset( $wpacuFrontPageUnloadsArray[$assetType] ) && ! empty( $wpacuFrontPageUnloadsArray[$assetType] ) && in_array( $assetHandle, $wpacuFrontPageUnloadsArray[$assetType] ) ) {
$targetKey = array_search($assetHandle, $wpacuFrontPageUnloadsArray[$assetType]);
unset($wpacuFrontPageUnloadsArray[$assetType][$targetKey]);
}
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_front_page_no_load',
wp_json_encode( Misc::filterList( $wpacuFrontPageUnloadsArray ) )
);
}
/*
* Table: WPACU_PLUGIN_ID . '_bulk_unload'
* Bulk Unload
*/
$wpacuBulkUnloadData = get_option(WPACU_PLUGIN_ID . '_bulk_unload');
$wpacuBulkUnloadDataArray = @json_decode($wpacuBulkUnloadData, ARRAY_A);
if (isset($wpacuBulkUnloadDataArray[$assetType]) && ! empty($wpacuBulkUnloadDataArray[$assetType])) {
foreach ($wpacuBulkUnloadDataArray[$assetType] as $unloadBulkType => $unloadBulkValues) {
// $unloadBulkType could be 'post_type', 'date', '404', 'taxonomy', 'search', 'custom_post_type_archive_[custom_post_type]'
if ($unloadBulkType === 'post_type') {
foreach ($unloadBulkValues as $postType => $assetHandles) {
if (in_array($assetHandle, $assetHandles)) {
$targetKey = array_search($assetHandle, $assetHandles);
unset($wpacuBulkUnloadDataArray[$assetType][$unloadBulkType][$postType][$targetKey]);
}
}
// [Any Pro left overs]
} elseif ($unloadBulkType === 'post_type_via_tax') {
foreach ($unloadBulkValues as $postType => $assetHandlesValues) {
if (isset($assetHandlesValues[$assetHandle]) && is_array($assetHandlesValues[$assetHandle])) {
unset($wpacuBulkUnloadDataArray[$assetType][$unloadBulkType][$postType][$assetHandle]);
}
}
} elseif (in_array($unloadBulkType, array('date', '404', 'search')) || (strpos($unloadBulkType, 'custom_post_type_archive_') !== false)) {
if (in_array($assetHandle, $unloadBulkValues)) {
$targetKey = array_search($assetHandle, $unloadBulkValues);
unset($wpacuBulkUnloadDataArray[$assetType][$unloadBulkType][$targetKey]);
}
} elseif ($unloadBulkType === 'taxonomy') {
foreach ($unloadBulkValues as $taxonomyType => $assetHandles) {
if (in_array($assetHandle, $assetHandles)) {
$targetKey = array_search($assetHandle, $assetHandles);
unset($wpacuBulkUnloadDataArray[$assetType][$unloadBulkType][$taxonomyType][$targetKey]);
}
}
} elseif ($unloadBulkType === 'author' && isset($unloadBulkValues['all']) && ! empty($unloadBulkValues['all'])) {
foreach ($unloadBulkValues['all'] as $assetHandles) {
if (in_array($assetHandle, $assetHandles)) {
$targetKey = array_search($assetHandle, $assetHandles);
unset($wpacuBulkUnloadDataArray[$assetType][$unloadBulkType]['all'][$targetKey]);
}
}
}
// [/Any Pro left overs]
}
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_bulk_unload',
wp_json_encode( Misc::filterList( $wpacuBulkUnloadDataArray ) )
);
}
/*
* Tables: $wpdb->postmeta, $wpdb->termmeta, $wpdb->usermeta (all part of the standard WordPress tables)
* Plugin meta key: _{$wpacuPluginId}_no_load'
*
*/
global $wpdb;
$wpacuPluginId = WPACU_PLUGIN_ID;
foreach (array($wpdb->postmeta, $wpdb->termmeta, $wpdb->usermeta) as $tableName) {
$wpacuGetValuesQuery = <<<SQL
SELECT * FROM `{$tableName}` WHERE meta_key='_{$wpacuPluginId}_no_load'
SQL;
$wpacuMetaData = $wpdb->get_results( $wpacuGetValuesQuery, ARRAY_A );
foreach ( $wpacuMetaData as $wpacuValues ) {
$decodedValues = @json_decode( $wpacuValues['meta_value'], ARRAY_A );
// No load rules
if ( isset( $decodedValues[$assetType] ) && in_array($assetHandle, $decodedValues[$assetType]) ) {
$targetKey = array_search($assetHandle, $decodedValues[$assetType]);
unset($decodedValues[$assetType][$targetKey]);
if ($tableName === $wpdb->postmeta) {
update_post_meta($wpacuValues['post_id'], '_' . $wpacuPluginId . '_no_load', wp_json_encode( Misc::filterList( $decodedValues ) ) );
} elseif ($tableName === $wpdb->termmeta) {
update_term_meta($wpacuValues['term_id'], '_' . $wpacuPluginId . '_no_load', wp_json_encode( Misc::filterList( $decodedValues ) ) );
} elseif ($tableName === $wpdb->usermeta) {
update_user_meta($wpacuValues['user_id'], '_' . $wpacuPluginId . '_no_load', wp_json_encode( Misc::filterList( $decodedValues ) ) );
}
}
}
}
/*
* Table: WPACU_PLUGIN_ID . '_global_data'
* Global (Site-wide) Rules: Unload via RegEx
*/
$wpacuGlobalData = get_option(WPACU_PLUGIN_ID . '_global_data');
$wpacuGlobalDataArray = @json_decode($wpacuGlobalData, ARRAY_A);
foreach ( array( 'unload_regex' ) as $dataType ) {
if ( isset( $wpacuGlobalDataArray[ $assetType ][ $dataType ] ) && ! empty( $wpacuGlobalDataArray[ $assetType ][ $dataType ] ) && array_key_exists($assetHandle, $wpacuGlobalDataArray[ $assetType ][ $dataType ]) ) {
unset( $wpacuGlobalDataArray[ $assetType ][ $dataType ][ $assetHandle ]);
}
}
Misc::addUpdateOption(
WPACU_PLUGIN_ID . '_global_data',
wp_json_encode( Misc::filterList( $wpacuGlobalDataArray ) )
);
}
}

View File

@ -0,0 +1,299 @@
<?php
namespace WpAssetCleanUp;
/**
* Class Menu
* @package WpAssetCleanUp
*/
class Menu
{
/**
* @var array|string[]
*/
public static $allMenuPages = array();
/**
* @var string
*/
private static $_capability = 'administrator';
/**
* @var string
*/
private static $_slug;
/**
* Menu constructor.
*/
public function __construct()
{
self::$allMenuPages = array(
WPACU_PLUGIN_ID . '_getting_started',
WPACU_PLUGIN_ID . '_settings',
WPACU_PLUGIN_ID . '_assets_manager',
WPACU_PLUGIN_ID . '_plugins_manager',
WPACU_PLUGIN_ID . '_bulk_unloads',
WPACU_PLUGIN_ID . '_overview',
WPACU_PLUGIN_ID . '_tools',
WPACU_PLUGIN_ID . '_license',
WPACU_PLUGIN_ID . '_get_help',
WPACU_PLUGIN_ID . '_go_pro'
);
self::$_slug = WPACU_PLUGIN_ID . '_getting_started';
add_action('admin_menu', array($this, 'activeMenu'));
if (isset($_GET['page']) && $_GET['page'] === WPACU_PLUGIN_ID . '_go_pro') {
header('Location: '.apply_filters('wpacu_go_pro_affiliate_link', WPACU_PLUGIN_GO_PRO_URL.'?utm_source=plugin_go_pro'));
exit();
}
add_filter( 'post_row_actions', array($this, 'editPostRowActions'), 10, 2 );
add_filter( 'page_row_actions', array($this, 'editPostRowActions'), 10, 2 );
add_action('admin_page_access_denied', array($this, 'pluginPagesAccessDenied'));
}
/**
*
*/
public function activeMenu()
{
// User should be of 'administrator' role and allowed to activate plugins
if (! self::userCanManageAssets()) {
return;
}
add_menu_page(
WPACU_PLUGIN_TITLE,
WPACU_PLUGIN_TITLE,
self::getAccessCapability(),
self::$_slug,
array(new Info, 'gettingStarted'),
WPACU_PLUGIN_URL.'/assets/icons/icon-asset-cleanup.png'
);
add_submenu_page(
self::$_slug,
__('Settings', 'wp-asset-clean-up'),
__('Settings', 'wp-asset-clean-up'),
self::getAccessCapability(),
WPACU_PLUGIN_ID . '_settings',
array(new Settings, 'settingsPage')
);
add_submenu_page(
self::$_slug,
__('CSS/JS Manager', 'wp-asset-clean-up'),
__('CSS/JS Manager', 'wp-asset-clean-up'),
self::getAccessCapability(),
WPACU_PLUGIN_ID . '_assets_manager',
array(new AssetsPagesManager, 'renderPage')
);
add_submenu_page(
self::$_slug,
__('Plugins Manager', 'wp-asset-clean-up'),
__('Plugins Manager', 'wp-asset-clean-up'),
self::getAccessCapability(),
WPACU_PLUGIN_ID . '_plugins_manager',
array(new PluginsManager, 'page')
);
add_submenu_page(
self::$_slug,
__('Bulk Changes', 'wp-asset-clean-up'),
__('Bulk Changes', 'wp-asset-clean-up'),
self::getAccessCapability(),
WPACU_PLUGIN_ID . '_bulk_unloads',
array(new BulkChanges, 'pageBulkUnloads')
);
add_submenu_page(
self::$_slug,
__('Overview', 'wp-asset-clean-up'),
__('Overview', 'wp-asset-clean-up'),
self::getAccessCapability(),
WPACU_PLUGIN_ID . '_overview',
array(new Overview, 'pageOverview')
);
add_submenu_page(
self::$_slug,
__('Tools', 'wp-asset-clean-up'),
__('Tools', 'wp-asset-clean-up'),
self::getAccessCapability(),
WPACU_PLUGIN_ID . '_tools',
array(new Tools, 'toolsPage')
);
// License Page
add_submenu_page(
self::$_slug,
__('License', 'wp-asset-clean-up'),
__('License', 'wp-asset-clean-up'),
self::getAccessCapability(),
WPACU_PLUGIN_ID . '_license',
array(new Info, 'license')
);
// Get Help | Support Page
add_submenu_page(
self::$_slug,
__('Help', 'wp-asset-clean-up'),
__('Help', 'wp-asset-clean-up'),
self::getAccessCapability(),
WPACU_PLUGIN_ID . '_get_help',
array(new Info, 'help')
);
// [wpacu_lite]
// Upgrade to "Go Pro" | Redirects to sale page
add_submenu_page(
self::$_slug,
__('Go Pro', 'wp-asset-clean-up'),
__('Go Pro', 'wp-asset-clean-up') . ' <span style="font-size: 16px; color: inherit;" class="dashicons dashicons-star-filled"></span>',
self::getAccessCapability(),
WPACU_PLUGIN_ID . '_go_pro',
function() {}
);
// [/wpacu_lite]
// Add "Asset CleanUp Pro" Settings Link to the main "Settings" menu within the Dashboard
// For easier navigation
$GLOBALS['submenu']['options-general.php'][] = array(
WPACU_PLUGIN_TITLE,
self::getAccessCapability(),
esc_url(admin_url( 'admin.php?page=' . WPACU_PLUGIN_ID . '_settings')),
WPACU_PLUGIN_TITLE,
);
// Rename first item from the menu which has the same title as the menu page
$GLOBALS['submenu'][self::$_slug][0][0] = esc_attr__('Getting Started', 'wp-asset-clean-up');
}
/**
* @return bool
*/
public static function userCanManageAssets()
{
if (is_super_admin()) {
return true; // For security reasons, super admins will always be able to access the plugin's settings
}
// Has self::$_capability been changed? Just user current_user_can()
if (self::getAccessCapability() !== self::$_capability) {
return current_user_can(self::getAccessCapability());
}
// self::$_capability default value: "administrator"
return current_user_can(self::getAccessCapability());
}
/**
* @return bool
*/
public static function isPluginPage()
{
return isset($_GET['page']) && in_array($_GET['page'], self::$allMenuPages);
}
/**
* Here self::$_capability can be overridden
*
* @return mixed|void
*/
public static function getAccessCapability()
{
return apply_filters('wpacu_access_role', self::$_capability);
}
/**
* @param $actions
* @param $post
*
* @return mixed
*/
public function editPostRowActions($actions, $post)
{
// Check for your post type.
if ( $post->post_type === 'post' ) {
$wpacuFor = 'posts';
} elseif ( $post->post_type === 'page' ) {
$wpacuFor = 'pages';
} elseif ( $post->post_type === 'attachment' ) {
$wpacuFor = 'media-attachment';
} else {
$wpacuFor = 'custom-post-types';
}
$postTypeObject = get_post_type_object($post->post_type);
if ( ! (isset($postTypeObject->public) && $postTypeObject->public == 1) ) {
return $actions;
}
if ( ! in_array(get_post_status($post), array('publish', 'private')) ) {
return $actions;
}
// Do not show the management link to specific post types that are marked as "public", but not relevant such as "ct_template" from Oxygen Builder
if (in_array($post->post_type, MetaBoxes::$noMetaBoxesForPostTypes)) {
return $actions;
}
// Build your links URL.
$url = esc_url(admin_url( 'admin.php?page=wpassetcleanup_assets_manager' ));
// Maybe put in some extra arguments based on the post status.
$edit_link = add_query_arg(
array(
'wpacu_for' => $wpacuFor,
'wpacu_post_id' => $post->ID
), $url
);
// Only show it to the user that has "administrator" access, and it's in the following list (if a certain list of admins is provided)
// "Settings" -> "Plugin Usage Preferences" -> "Allow managing assets to:"
if (self::userCanManageAssets() && Main::currentUserCanViewAssetsList()) {
/*
* You can reset the default $actions with your own array, or simply merge them
* here I want to rewrite my Edit link, remove the Quick-link, and introduce a
* new link 'Copy'
*/
$actions['wpacu_manage_assets'] = sprintf( '<a href="%1$s">%2$s</a>',
esc_url( $edit_link ),
esc_html( __( 'Manage CSS &amp; JS', 'wp-asset-clean-up' ) )
);
}
return $actions;
}
/**
* Message to show if the user does not have self::$_capability role and tries to access a plugin's page
*/
public function pluginPagesAccessDenied()
{
if ( ! self::isPluginPage() ) {
// Not an Asset CleanUp page
return;
}
$userMeta = get_userdata(get_current_user_id());
$userRoles = $userMeta->roles;
wp_die(
__('Sorry, you are not allowed to access this page.').'<br /><br />'.
sprintf(__('Asset CleanUp requires "%s" role and the ability to activate plugins in order to access its pages.', 'wp-asset-clean-up'), '<span style="color: green; font-weight: bold;">'.self::getAccessCapability().'</span>').'<br />'.
sprintf(__('Your current role(s): <strong>%s</strong>', 'wp-asset-clean-up'), implode(', ', $userRoles)).'<br /><br />'.
__('The value (in green color) can be changed if you use the following snippet in functions.php (within your theme/child theme or a custom plugin):').'<br />'.
'<p style="margin: -10px 0 0;"><code style="background: #f2f3ea; padding: 5px;">add_filter(\'wpacu_access_role\', function($role) { return \'your_role_here\'; });</code></p>'.
'<p>If the snippet is not used, it will default to "administrator".</p>'.
'<p>Possible values: <strong>manage_options</strong>, <strong>activate_plugins</strong>, <strong>manager</strong> etc.</p>'.
'<p>Read more: <a target="_blank" href="https://wordpress.org/support/article/roles-and-capabilities/#summary-of-roles">https://wordpress.org/support/article/roles-and-capabilities/#summary-of-roles</a></p>',
403
);
}
}

View File

@ -0,0 +1,348 @@
<?php
namespace WpAssetCleanUp;
/**
* Class MetaBoxes
* @package WpAssetCleanUp
*/
class MetaBoxes
{
/**
* @var array
*/
public static $noMetaBoxesForPostTypes = array(
// Oxygen Page Builder
'ct_template',
'oxy_user_library',
// Themify Page Builder (Layout & Layout Part)
'tbuilder_layout',
'tbuilder_layout_part',
// "Popup Maker" plugin
'popup',
'popup_theme',
// "Popup Builder" plugin
'popupbuilder',
// "Datafeedr Product Sets" plugin
'datafeedr-productset',
// Elementor
'elementor_library'
);
/**
*
*/
public function initMetaBox($type)
{
if ( ! Menu::userCanManageAssets() ) {
return;
}
if ( Main::instance()->settings['allow_manage_assets_to'] === 'chosen' && ! empty(Main::instance()->settings['allow_manage_assets_to_list']) ) {
$wpacuCurrentUserId = get_current_user_id();
if ( ! in_array( $wpacuCurrentUserId, Main::instance()->settings['allow_manage_assets_to_list'] ) ) {
return; // the current logged-in admin is not in the list of "Allow managing assets to:"
}
}
if ($type === 'manage_page_assets') {
add_action( 'add_meta_boxes', array( $this, 'addAssetManagerMetaBox' ), 11 );
add_action( 'add_meta_boxes', array( $this, 'keepAssetManagerMetaBoxOnTheLeftSide' ), 1 );
}
}
/**
* @param $postType
*/
public function addAssetManagerMetaBox($postType)
{
$obj = $this->showMetaBoxes($postType);
if (isset($obj->public) && $obj->public > 0) {
add_meta_box(
WPACU_PLUGIN_ID . '_asset_list',
WPACU_PLUGIN_TITLE.': '.__('CSS &amp; JavaScript Manager / Page Options', 'wp-asset-clean-up'),
array($this, 'renderAssetManagerMetaBoxContent'),
$postType,
apply_filters('wpacu_asset_list_meta_box_context', 'normal'),
apply_filters('wpacu_asset_list_meta_box_priority', 'high')
);
}
}
/**
* Sometimes, users are moving by mistake the meta box to the right side which is not desirable
* and have difficulties moving it back, thus, this method moves it back to the left (normal) side
*
* @param $postType
*/
public function keepAssetManagerMetaBoxOnTheLeftSide($postType)
{
$user = wp_get_current_user();
if (isset($user->ID) && $user->ID) {
$userMetaBoxOption = get_user_option('meta-box-order_'.$postType, $user->ID );
if (isset($userMetaBoxOption['side'], $userMetaBoxOption['normal']) && strpos($userMetaBoxOption['side'], WPACU_PLUGIN_ID . '_asset_list') !== false) {
// Remove it from the side list
if (strpos($userMetaBoxOption['side'], ',') !== false) {
$allSideMetaBoxes = explode(',', $userMetaBoxOption['side']);
foreach ($allSideMetaBoxes as $sideMetaBoxIndex => $sideMetaBoxName) {
if ($sideMetaBoxName === WPACU_PLUGIN_ID . '_asset_list') {
unset($allSideMetaBoxes[$sideMetaBoxIndex]);
}
}
$userMetaBoxOption['side'] = implode(',', array_unique($allSideMetaBoxes));
} else {
$userMetaBoxOption['side'] = str_replace(WPACU_PLUGIN_ID . '_asset_list', '', $userMetaBoxOption['side']);
}
// Move it back to the normal one
if (strpos($userMetaBoxOption['normal'], ',') !== false) {
$allNormalMetaBoxes = explode( ',', $userMetaBoxOption['normal'] );
$allNormalMetaBoxes[] = WPACU_PLUGIN_ID . '_asset_list';
$userMetaBoxOption['normal'] = implode(',', array_unique($allNormalMetaBoxes));
} elseif ($userMetaBoxOption['normal'] !== '') {
$userMetaBoxOption['normal'] .= ','.WPACU_PLUGIN_ID . '_asset_list';
} elseif ($userMetaBoxOption['normal'] === '') {
$userMetaBoxOption['normal'] .= WPACU_PLUGIN_ID . '_asset_list';
}
update_user_option($user->ID, 'meta-box-order_'.$postType, $userMetaBoxOption, true);
}
}
}
/**
* @param $postType
*/
public function addPageOptionsMetaBox($postType)
{
global $post;
if (self::isMediaWithPermalinkDeactivated($post)) {
return;
}
$obj = $this->showMetaBoxes($postType);
if (isset($obj->public) && $obj->public > 0) {
add_meta_box(
WPACU_PLUGIN_ID . '_page_options',
WPACU_PLUGIN_TITLE.': '.__('Options', 'wp-asset-clean-up'),
array($this, 'renderPageOptionsMetaBoxContent'),
$postType,
apply_filters('wpacu_page_options_meta_box_context', 'side'),
apply_filters('wpacu_page_options_meta_box_priority', 'high')
);
}
}
/**
* This is triggered only in the Edit Mode Dashboard View
*/
public function renderAssetManagerMetaBoxContent()
{
global $post;
if ($post->ID === null) {
return;
}
$data = array('status' => 1);
$postId = (isset($post->ID) && $post->ID > 0) ? $post->ID : 0;
$isListFetchable = true;
if (! Main::instance()->settings['dashboard_show']) {
$isListFetchable = false;
$data['status'] = 2; // "Manage within Dashboard" is disabled in plugin's settings
} elseif ($postId < 1 || ! in_array(get_post_status($postId), array('publish', 'private'))) {
$data['status'] = 3; // "draft", "auto-draft" post (it has to be published)
$isListFetchable = false;
}
if (self::isMediaWithPermalinkDeactivated($post)) {
$isListFetchable = false;
$data['status'] = 4; // "Redirect attachment URLs to the attachment itself?" is enabled in "Yoast SEO" -> "Media"
}
if ($isListFetchable) {
$data['fetch_url'] = Misc::getPageUrl($postId);
switch (assetCleanUpHasNoLoadMatches($data['fetch_url'])) {
case 'is_set_in_settings':
// The rules from "Settings" -> "Plugin Usage Preferences" -> "Do not load the plugin on certain pages" will be checked
$data['status'] = 5;
$isListFetchable = false;
break;
case 'is_set_in_page':
// The following option from "Page Options" (within the CSS/JS manager of the targeted page) is set: "Do not load Asset CleanUp Pro on this page (this will disable any functionality of the plugin)"
$data['status'] = 6;
$isListFetchable = false;
break;
}
}
$data['is_list_fetchable'] = $isListFetchable;
$data['fetch_assets_on_click'] = false;
if ($isListFetchable) {
if (Main::instance()->settings['assets_list_show_status'] === 'fetch_on_click') {
$data['fetch_assets_on_click'] = true;
}
$data['dom_get_type'] = Main::instance()->settings['dom_get_type'];
}
$data['post_id'] = $postId;
Main::instance()->parseTemplate('meta-box', $data, true);
}
/**
* This is triggered only in the Edit Mode Dashboard View
* Valid for posts, pages (a page can also be set as the homepage), custom post types
*/
public function renderPageOptionsMetaBoxContent()
{
$data = array('page_options' => self::getPageOptions());
Main::instance()->parseTemplate('meta-box-side-page-options', $data, true);
}
/**
* @param int $postId
* @param string $type
* @return array|mixed|object
*/
public static function getPageOptions($postId = 0, $type = 'post')
{
if ($type === 'post' || $postId > 0) {
if ( $postId < 1 ) {
global $post;
$postId = (int) $post->ID;
}
if ( $postId > 1 ) {
$metaPageOptionsJson = get_post_meta( $postId, '_' . WPACU_PLUGIN_ID . '_page_options', true );
return @json_decode( $metaPageOptionsJson, ARRAY_A );
}
} elseif ($type === 'front_page') { // e.g. the latest posts, not a chosen page ID (that's when $type as 'post' is used)
$globalPageOptions = get_option(WPACU_PLUGIN_ID . '_global_data');
if ($globalPageOptions) {
$globalPageOptionsList = @json_decode( $globalPageOptions, true );
if ( isset( $globalPageOptionsList['page_options']['homepage'] ) && ! empty( $globalPageOptionsList['page_options']['homepage'] ) ) {
return $globalPageOptionsList['page_options']['homepage'];
}
}
}
return array();
}
/**
* @return bool
*/
public static function hasNoFrontendOptimizationPageOption()
{
$isSingularPage = defined('WPACU_CURRENT_PAGE_ID') && WPACU_CURRENT_PAGE_ID > 0 && is_singular();
if ($isSingularPage || Misc::isHomePage()) {
if ($isSingularPage) {
$pageOptions = self::getPageOptions( WPACU_CURRENT_PAGE_ID ); // Singular page
} else {
$pageOptions = self::getPageOptions(0, 'front_page'); // Home page
}
if (isset($pageOptions['no_assets_settings']) && $pageOptions['no_assets_settings']) {
return true;
}
}
return false;
}
/**
* @return array
*/
public static function hideMetaBoxesForPostTypes()
{
$allValues = self::$noMetaBoxesForPostTypes;
$hideForChosenPostTypes = Main::instance()->settings['hide_meta_boxes_for_post_types'];
if (! empty($hideForChosenPostTypes)) {
foreach ($hideForChosenPostTypes as $chosenPostType) {
$allValues[] = trim($chosenPostType);
}
}
return $allValues;
}
/**
* Determine whether to show any Asset CleanUp (Pro) meta boxes, depending on the post type
*
* @param $postType
* @return bool|object
*/
public function showMetaBoxes($postType)
{
$obj = get_post_type_object($postType);
// These are not public pages that are loading CSS/JS
// e.g. URI request ending in '/ct_template/inner-content/'
if (isset($obj->name) && $obj->name && in_array($obj->name, self::hideMetaBoxesForPostTypes())) {
return false;
}
if (isset($_GET['post'], $obj->name) && $_GET['post'] && $obj->name) {
$permalinkStructure = get_option( 'permalink_structure' );
$postPermalink = get_permalink( $_GET['post'] );
if (strpos($permalinkStructure, '%postname%') !== false && strpos($postPermalink, '/?'.$obj->name.'=')) {
// Doesn't have the right permalink; Showing any Asset CleanUp (Lite or Pro) options is not relevant
return false;
}
}
return $obj;
}
/**
* @param string $post
*
* @return bool
*/
public static function isMediaWithPermalinkDeactivated($post = '')
{
if ($post === '') {
$postTypeToCheck = 'attachment';
} else {
$postTypeToCheck = get_post_type($post->ID);
}
if ('attachment' === $postTypeToCheck && method_exists('WPSEO_Options', 'get')) {
try {
if (\WPSEO_Options::get( 'disable-attachment' ) === true) {
return true;
}
} catch (\Exception $e) {}
}
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,775 @@
<?php
namespace WpAssetCleanUp;
/**
* NOTE: This is from the original core file located /wp-includes/class-wp-object-cache.php
* Avoid the WordPress core $wp_object_cache global variable which is sometimes altered by 3rd party plugins
* This would make this plugin compatible with plugins such as "Redis Object Cache"
*
* Object Cache API: ObjectCache class
*
* @package WordPress
* @subpackage Cache
* @since 5.4.0
*/
/**
* Core class that implements an object cache.
*
* The WordPress Object Cache is used to save on trips to the database. The
* Object Cache stores all the cache data to memory and makes the cache
* contents available by using a key, which is used to name and later retrieve
* the cache contents.
*
* The Object Cache can be replaced by other caching mechanisms by placing files
* in the wp-content folder which is looked at in wp-settings. If that file
* exists, then this file will not be included.
*
* @since 2.0.0
*/
class ObjectCache {
/**
* Holds the cached objects.
*
* @since 2.0.0
* @var array
*/
private $cache = array();
/**
* The amount of times the cache data was already stored in the cache.
*
* @since 2.5.0
* @var int
*/
public $cache_hits = 0;
/**
* Amount of times the cache did not have the request in cache.
*
* @since 2.0.0
* @var int
*/
public $cache_misses = 0;
/**
* List of global cache groups.
*
* @since 3.0.0
* @var array
*/
protected $global_groups = array();
/**
* The blog prefix to prepend to keys in non-global groups.
*
* @since 3.5.0
* @var string
*/
private $blog_prefix;
/**
* Holds the value of is_multisite().
*
* @since 3.5.0
* @var bool
*/
private $multisite;
/**
* @var string|void
*/
public static $objNotInitErrorMsg;
/**
* Sets up object properties; PHP 5 style constructor.
*
* @since 2.0.8
*/
public function __construct() {
self::$objNotInitErrorMsg = __('Asset CleanUp\'s object cache is not valid (from method "[method]").', 'wp-asset-clean-up');
$this->multisite = is_multisite();
$this->blog_prefix = $this->multisite ? get_current_blog_id() . ':' : '';
}
/**
* Makes private properties readable for backward compatibility.
*
* @since 4.0.0
*
* @param string $name Property to get.
* @return mixed Property.
*/
public function __get( $name ) {
return $this->$name;
}
/**
* Makes private properties settable for backward compatibility.
*
* @since 4.0.0
*
* @param string $name Property to set.
* @param mixed $value Property value.
* @return mixed Newly-set property.
*/
public function __set( $name, $value ) {
return $this->$name = $value;
}
/**
* Makes private properties checkable for backward compatibility.
*
* @since 4.0.0
*
* @param string $name Property to check if set.
* @return bool Whether the property is set.
*/
public function __isset( $name ) {
return isset( $this->$name );
}
/**
* Makes private properties un-settable for backward compatibility.
*
* @since 4.0.0
*
* @param string $name Property to unset.
*/
public function __unset( $name ) {
unset( $this->$name );
}
/**
* Adds data to the cache if it doesn't already exist.
*
* @since 2.0.0
*
* @uses ObjectCache::_exists() Checks to see if the cache already has data.
* @uses ObjectCache::set() Sets the data after the checking the cache
* contents existence.
*
* @param int|string $key What to call the contents in the cache.
* @param mixed $data The contents to store in the cache.
* @param string $group Optional. Where to group the cache contents. Default 'default'.
* @param int $expire Optional. When to expire the cache contents. Default 0 (no expiration).
* @return bool True on success, false if cache key and group already exist.
*/
public function add( $key, $data, $group = 'default', $expire = 0 ) {
if ( wp_suspend_cache_addition() ) {
return false;
}
if ( empty( $group ) ) {
$group = 'default';
}
$id = $key;
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
$id = $this->blog_prefix . $key;
}
if ( $this->_exists( $id, $group ) ) {
return false;
}
return $this->set( $key, $data, $group, (int) $expire );
}
/**
* Sets the list of global cache groups.
*
* @since 3.0.0
*
* @param array $groups List of groups that are global.
*/
public function add_global_groups( $groups ) {
$groups = (array) $groups;
$groups = array_fill_keys( $groups, true );
$this->global_groups = array_merge( $this->global_groups, $groups );
}
/**
* Decrements numeric cache item's value.
*
* @since 3.3.0
*
* @param int|string $key The cache key to decrement.
* @param int $offset Optional. The amount by which to decrement the item's value. Default 1.
* @param string $group Optional. The group the key is in. Default 'default'.
* @return int|false The item's new value on success, false on failure.
*/
public function decr( $key, $offset = 1, $group = 'default' ) {
if ( empty( $group ) ) {
$group = 'default';
}
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
$key = $this->blog_prefix . $key;
}
if ( ! $this->_exists( $key, $group ) ) {
return false;
}
if ( ! is_numeric( $this->cache[ $group ][ $key ] ) ) {
$this->cache[ $group ][ $key ] = 0;
}
$offset = (int) $offset;
$this->cache[ $group ][ $key ] -= $offset;
if ( $this->cache[ $group ][ $key ] < 0 ) {
$this->cache[ $group ][ $key ] = 0;
}
return $this->cache[ $group ][ $key ];
}
/**
* Removes the contents of the cache key in the group.
*
* If the cache key does not exist in the group, then nothing will happen.
*
* @since 2.0.0
*
* @param int|string $key What the contents in the cache are called.
* @param string $group Optional. Where the cache contents are grouped. Default 'default'.
* @param bool $deprecated Optional. Unused. Default false.
* @return bool False if the contents weren't deleted and true on success.
*/
public function delete( $key, $group = 'default', $deprecated = false ) {
if ( empty( $group ) ) {
$group = 'default';
}
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
$key = $this->blog_prefix . $key;
}
if ( ! $this->_exists( $key, $group ) ) {
return false;
}
unset( $this->cache[ $group ][ $key ] );
return true;
}
/**
* Clears the object cache of all data.
*
* @since 2.0.0
*
* @return true Always returns true.
*/
public function flush() {
$this->cache = array();
return true;
}
/**
* Retrieves the cache contents, if it exists.
*
* The contents will be first attempted to be retrieved by searching by the
* key in the cache group. If the cache is hit (success) then the contents
* are returned.
*
* On failure, the number of cache misses will be incremented.
*
* @since 2.0.0
*
* @param int|string $key What the contents in the cache are called.
* @param string $group Optional. Where the cache contents are grouped. Default 'default'.
* @param bool $force Optional. Unused. Whether to force a refetch rather than relying on the local
* cache. Default false.
* @param bool $found Optional. Whether the key was found in the cache (passed by reference).
* Disambiguates a return of false, a storable value. Default null.
* @return mixed|false The cache contents on success, false on failure to retrieve contents.
*/
public function get( $key, $group = 'default', $force = false, &$found = null ) {
if ( empty( $group ) ) {
$group = 'default';
}
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
$key = $this->blog_prefix . $key;
}
if ( $this->_exists( $key, $group ) ) {
$found = true;
$this->cache_hits += 1;
if ( is_object( $this->cache[ $group ][ $key ] ) ) {
return clone $this->cache[ $group ][ $key ];
} else {
return $this->cache[ $group ][ $key ];
}
}
$found = false;
$this->cache_misses += 1;
return false;
}
/**
* Increments numeric cache item's value.
*
* @since 3.3.0
*
* @param int|string $key The cache key to increment
* @param int $offset Optional. The amount by which to increment the item's value. Default 1.
* @param string $group Optional. The group the key is in. Default 'default'.
* @return int|false The item's new value on success, false on failure.
*/
public function incr( $key, $offset = 1, $group = 'default' ) {
if ( empty( $group ) ) {
$group = 'default';
}
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
$key = $this->blog_prefix . $key;
}
if ( ! $this->_exists( $key, $group ) ) {
return false;
}
if ( ! is_numeric( $this->cache[ $group ][ $key ] ) ) {
$this->cache[ $group ][ $key ] = 0;
}
$offset = (int) $offset;
$this->cache[ $group ][ $key ] += $offset;
if ( $this->cache[ $group ][ $key ] < 0 ) {
$this->cache[ $group ][ $key ] = 0;
}
return $this->cache[ $group ][ $key ];
}
/**
* Replaces the contents in the cache, if contents already exist.
*
* @since 2.0.0
*
* @see ObjectCache::set()
*
* @param int|string $key What to call the contents in the cache.
* @param mixed $data The contents to store in the cache.
* @param string $group Optional. Where to group the cache contents. Default 'default'.
* @param int $expire Optional. When to expire the cache contents. Default 0 (no expiration).
* @return bool False if not exists, true if contents were replaced.
*/
public function replace( $key, $data, $group = 'default', $expire = 0 ) {
if ( empty( $group ) ) {
$group = 'default';
}
$id = $key;
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
$id = $this->blog_prefix . $key;
}
if ( ! $this->_exists( $id, $group ) ) {
return false;
}
return $this->set( $key, $data, $group, (int) $expire );
}
/**
* Resets cache keys.
*
* @since 3.0.0
*
* @deprecated 3.5.0 Use switch_to_blog()
* @see switch_to_blog()
*/
public function reset() {
_deprecated_function( __FUNCTION__, '3.5.0', 'switch_to_blog()' );
// Clear out non-global caches since the blog ID has changed.
foreach ( array_keys( $this->cache ) as $group ) {
if ( ! isset( $this->global_groups[ $group ] ) ) {
unset( $this->cache[ $group ] );
}
}
}
/**
* Sets the data contents into the cache.
*
* The cache contents are grouped by the $group parameter followed by the
* $key. This allows for duplicate ids in unique groups. Therefore, naming of
* the group should be used with care and should follow normal function
* naming guidelines outside of core WordPress usage.
*
* The $expire parameter is not used, because the cache will automatically
* expire for each time a page is accessed and PHP finishes. The method is
* more for cache plugins which use files.
*
* @since 2.0.0
*
* @param int|string $key What to call the contents in the cache.
* @param mixed $data The contents to store in the cache.
* @param string $group Optional. Where to group the cache contents. Default 'default'.
* @param int $expire Not Used.
* @return true Always returns true.
*/
public function set( $key, $data, $group = 'default', $expire = 0 ) {
if ( empty( $group ) ) {
$group = 'default';
}
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
$key = $this->blog_prefix . $key;
}
if ( is_object( $data ) ) {
$data = clone $data;
}
$this->cache[ $group ][ $key ] = $data;
return true;
}
/**
* Echoes the stats of the caching.
*
* Gives the cache hits, and cache misses. Also prints every cached group,
* key and the data.
*
* @since 2.0.0
*/
public function stats() {
echo '<p>';
echo "<strong>Cache Hits:</strong> {$this->cache_hits}<br />";
echo "<strong>Cache Misses:</strong> {$this->cache_misses}<br />";
echo '</p>';
echo '<ul>';
foreach ( $this->cache as $group => $cache ) {
echo "<li><strong>Group:</strong> $group - ( " . number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )</li>';
}
echo '</ul>';
}
/**
* Switches the internal blog ID.
*
* This changes the blog ID used to create keys in blog specific groups.
*
* @since 3.5.0
*
* @param int $blog_id Blog ID.
*/
public function switch_to_blog( $blog_id ) {
$blog_id = (int) $blog_id;
$this->blog_prefix = $this->multisite ? $blog_id . ':' : '';
}
/**
* Serves as a utility function to determine whether a key exists in the cache.
*
* @since 3.4.0
*
* @param int|string $key Cache key to check for existence.
* @param string $group Cache group for the key existence check.
* @return bool Whether the key exists in the cache for the given group.
*/
protected function _exists( $key, $group ) {
return isset( $this->cache[ $group ] ) && ( isset( $this->cache[ $group ][ $key ] ) || array_key_exists( $key, $this->cache[ $group ] ) );
}
/**
* [START] Functions to call (reference: /wp-includes/cache.php)
*/
/**
* Adds data to the cache, if the cache key doesn't already exist.
*
* @since 2.0.0
*
* @see ObjectCache::add()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @param int|string $key The cache key to use for retrieval later.
* @param mixed $data The data to add to the cache.
* @param string $group Optional. The group to add the cache to. Enables the same key
* to be used across groups. Default empty.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool True on success, false if cache key and group already exist.
*/
public static function wpacu_cache_add( $key, $data, $group = '', $expire = 0 ) {
global $wpacu_object_cache;
return $wpacu_object_cache->add( $key, $data, $group, (int) $expire );
}
/**
* Closes the cache.
*
* This function has ceased to do anything since WordPress 2.5. The
* functionality was removed along with the rest of the persistent cache.
*
* This does not mean that plugins can't implement this function when they need
* to make sure that the cache is cleaned up after WordPress no longer needs it.
*
* @since 2.0.0
*
* @return true Always returns true.
*/
public static function wpacu_cache_close() {
return true;
}
/**
* Decrements numeric cache item's value.
*
* @since 3.3.0
*
* @see ObjectCache::decr()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @param int|string $key The cache key to decrement.
* @param int $offset Optional. The amount by which to decrement the item's value. Default 1.
* @param string $group Optional. The group the key is in. Default empty.
* @return int|false The item's new value on success, false on failure.
*/
public static function wpacu_cache_decr( $key, $offset = 1, $group = '' ) {
if ( ! self::isValidObjectCache() ) { error_log(str_replace('[method]', __METHOD__, self::$objNotInitErrorMsg)); return; }
global $wpacu_object_cache;
return $wpacu_object_cache->decr( $key, $offset, $group );
}
/**
* Removes the cache contents matching key and group.
*
* @since 2.0.0
*
* @see ObjectCache::delete()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @param int|string $key What the contents in the cache are called.
* @param string $group Optional. Where the cache contents are grouped. Default empty.
* @return bool True on successful removal, false on failure.
*/
public static function wpacu_cache_delete( $key, $group = '' ) {
if ( ! self::isValidObjectCache() ) { error_log(str_replace('[method]', __METHOD__, self::$objNotInitErrorMsg)); return; }
global $wpacu_object_cache;
return $wpacu_object_cache->delete( $key, $group );
}
/**
* Removes all cache items.
*
* @since 2.0.0
*
* @see ObjectCache::flush()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @return bool True on success, false on failure.
*/
public static function wpacu_cache_flush() {
if ( ! self::isValidObjectCache() ) { error_log(str_replace('[method]', __METHOD__, self::$objNotInitErrorMsg)); return; }
global $wpacu_object_cache;
return $wpacu_object_cache->flush();
}
/**
* Retrieves the cache contents from the cache by key and group.
*
* @since 2.0.0
*
* @see ObjectCache::get()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @param int|string $key The key under which the cache contents are stored.
* @param string $group Optional. Where the cache contents are grouped. Default empty.
* @param bool $force Optional. Whether to force an update of the local cache from the persistent
* cache. Default false.
* @param bool $found Optional. Whether the key was found in the cache (passed by reference).
* Disambiguates a return of false, a storable value. Default null.
* @return bool|mixed False on failure to retrieve contents or the cache
* contents on success
*/
public static function wpacu_cache_get( $key, $group = '', $force = false, &$found = null ) {
if ( ! self::isValidObjectCache() ) { error_log(str_replace('[method]', __METHOD__, self::$objNotInitErrorMsg)); return; }
global $wpacu_object_cache;
return $wpacu_object_cache->get( $key, $group, $force, $found );
}
/**
* Increment numeric cache item's value
*
* @since 3.3.0
*
* @see ObjectCache::incr()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @param int|string $key The key for the cache contents that should be incremented.
* @param int $offset Optional. The amount by which to increment the item's value. Default 1.
* @param string $group Optional. The group the key is in. Default empty.
* @return int|false The item's new value on success, false on failure.
*/
public static function wpacu_cache_incr( $key, $offset = 1, $group = '' ) {
if ( ! self::isValidObjectCache() ) { error_log(str_replace('[method]', __METHOD__, self::$objNotInitErrorMsg)); return; }
global $wpacu_object_cache;
return $wpacu_object_cache->incr( $key, $offset, $group );
}
/**
* Sets up Object Cache Global and assigns it.
*
* @since 2.0.0
*
* @global ObjectCache $wpacu_object_cache
*/
public static function wpacu_cache_init() {
$GLOBALS['wpacu_object_cache'] = new self();
}
/**
* Replaces the contents of the cache with new data.
*
* @since 2.0.0
*
* @see ObjectCache::replace()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @param int|string $key The key for the cache data that should be replaced.
* @param mixed $data The new data to store in the cache.
* @param string $group Optional. The group for the cache data that should be replaced.
* Default empty.
* @param int $expire Optional. When to expire the cache contents, in seconds.
* Default 0 (no expiration).
* @return bool False if original value does not exist, true if contents were replaced
*/
public static function wpacu_cache_replace( $key, $data, $group = '', $expire = 0 ) {
if ( ! self::isValidObjectCache() ) { error_log(str_replace('[method]', __METHOD__, self::$objNotInitErrorMsg)); return; }
global $wpacu_object_cache;
return $wpacu_object_cache->replace( $key, $data, $group, (int) $expire );
}
/**
* Saves the data to the cache.
*
* Differs from ObjectCache::wpacu_cache_add() and wp_cache_replace() in that it will always write data.
*
* @since 2.0.0
*
* @see ObjectCache::set()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @param int|string $key The cache key to use for retrieval later.
* @param mixed $data The contents to store in the cache.
* @param string $group Optional. Where to group the cache contents. Enables the same key
* to be used across groups. Default empty.
* @param int $expire Optional. When to expire the cache contents, in seconds.
* Default 0 (no expiration).
* @return bool True on success, false on failure.
*/
public static function wpacu_cache_set( $key, $data, $group = '', $expire = 0 ) {
global $wpacu_object_cache;
return $wpacu_object_cache->set( $key, $data, $group, (int) $expire );
}
/**
* Switches the internal blog ID.
*
* This changes the blog id used to create keys in blog specific groups.
*
* @since 3.5.0
*
* @see ObjectCache::switch_to_blog()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @param int $blog_id Site ID.
*/
public static function wpacu_cache_switch_to_blog( $blog_id ) {
global $wpacu_object_cache;
$wpacu_object_cache->switch_to_blog( $blog_id );
}
/**
* Adds a group or set of groups to the list of global groups.
*
* @since 2.6.0
*
* @see ObjectCache::add_global_groups()
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*
* @param string|array $groups A group or an array of groups to add.
*/
public static function wpacu_cache_add_global_groups( $groups ) {
global $wpacu_object_cache;
$wpacu_object_cache->add_global_groups( $groups );
}
/**
* Adds a group or set of groups to the list of non-persistent groups.
*
* @since 2.6.0
*
* @param string|array $groups A group or an array of groups to add.
*/
public static function wpacu_cache_add_non_persistent_groups( $groups ) {
// Default cache doesn't persist so nothing to do here.
}
/**
* Reset internal cache keys and structures.
*
* If the cache back end uses global blog or site IDs as part of its cache keys,
* this function instructs the back end to reset those keys and perform any cleanup
* since blog or site IDs have changed since cache init.
*
* This function is deprecated. Use wp_cache_switch_to_blog() instead of this
* function when preparing the cache for a blog switch. For clearing the cache
* during unit tests, consider using wp_cache_init(). wp_cache_init() is not
* recommended outside of unit tests as the performance penalty for using it is
* high.
*
* @since 2.6.0
* @deprecated 3.5.0 ObjectCache::reset()
* @see ObjectCache::reset()
*
* @global ObjectCache $wpacu_object_cache Object cache global instance.
*/
public static function wpacu_cache_reset() {
_deprecated_function( __FUNCTION__, '3.5.0', 'ObjectCache::reset()' );
if ( ! self::isValidObjectCache() ) { error_log(str_replace('[method]', __METHOD__, self::$objNotInitErrorMsg)); return; }
global $wpacu_object_cache;
$wpacu_object_cache->reset();
}
/**
* Main purpose: Avoid errors such as "PHP Fatal error: Uncaught Error: Call to a member function get() on null"
*
* @return bool
*/
public static function isValidObjectCache()
{
global $wpacu_object_cache;
return isset( $wpacu_object_cache ) && ! is_null( $wpacu_object_cache );
}
/**
* [END] Functions to call (reference: /wp-includes/cache.php)
*/
}

View File

@ -0,0 +1,627 @@
<?php
namespace WpAssetCleanUp\OptimiseAssets;
use WpAssetCleanUp\Main;
use WpAssetCleanUp\Menu;
use WpAssetCleanUp\FileSystem;
use WpAssetCleanUp\Misc;
use WpAssetCleanUp\ObjectCache;
use WpAssetCleanUp\Preloads;
/**
* Class CombineCss
* @package WpAssetCleanUp\OptimiseAssets
*/
class CombineCss
{
/**
* @var string
*/
public static $jsonStorageFile = 'css-combined{maybe-extra-info}.json';
/**
* @param $htmlSource
*
* @return mixed
*/
public static function doCombine($htmlSource)
{
if ( ! Misc::isDOMDocumentOn() ) {
return $htmlSource;
}
if ( ! self::proceedWithCssCombine() ) {
return $htmlSource;
}
global $wp_styles;
$wpacuRegisteredStyles = $wp_styles->registered;
$storageJsonContents = array();
$skipCache = false; // default
if (isset($_GET['wpacu_no_cache']) || (defined('WPACU_NO_CACHE') && WPACU_NO_CACHE === true)) {
$skipCache = true;
}
// If cache is not skipped, read the information from the cache as it's much faster
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
$storageJsonContents = OptimizeCommon::getAssetCachedData( self::$jsonStorageFile, OptimizeCss::getRelPathCssCacheDir(), 'css' );
}
// $uriToFinalCssFile will always be relative ONLY within WP_CONTENT_DIR . self::getRelPathCssCacheDir()
// which is usually "wp-content/cache/asset-cleanup/css/"
if ( $skipCache || empty($storageJsonContents) ) {
$storageJsonContentsToSave = array();
/*
* NO CACHING? Parse the DOM
*/
// Nothing in the database records or the retrieved cached file does not exist?
OptimizeCommon::clearAssetCachedData(self::$jsonStorageFile);
$storageJsonContents = array();
$domTag = OptimizeCommon::getDomLoadedTag($htmlSource, 'combineCss');
foreach (array('head', 'body') as $docLocationTag) {
$combinedUriPathsGroup = $localAssetsPathsGroup = $linkHrefsGroup = array();
$localAssetsExtraGroup = array();
$docLocationElements = $domTag->getElementsByTagName($docLocationTag)->item(0);
if ($docLocationElements === null) { continue; }
$xpath = new \DOMXpath($domTag);
$linkTags = $xpath->query('/html/'.$docLocationTag.'/link[@rel="stylesheet"] | /html/'.$docLocationTag.'/link[@rel="preload"]');
if ($linkTags === null) { continue; }
foreach ($linkTags as $tagObject) {
$linkAttributes = array();
foreach ($tagObject->attributes as $attrObj) { $linkAttributes[$attrObj->nodeName] = trim($attrObj->nodeValue); }
// Only rel="stylesheet" (with no rel="preload" associated with it) gets prepared for combining as links with rel="preload" (if any) are never combined into a standard render-blocking CSS file
// rel="preload" is there for a reason to make sure the CSS code is made available earlier prior to the one from rel="stylesheet" which is render-blocking
if (isset($linkAttributes['rel'], $linkAttributes['href']) && $linkAttributes['href']) {
$href = (string) $linkAttributes['href'];
if (self::skipCombine($linkAttributes['href'])) {
continue;
}
// e.g. for 'admin-bar' (keep it as standalone when critical CSS is used)
if (isset($linkAttributes['data-wpacu-skip-preload']) && has_filter('wpacu_critical_css')) {
continue;
}
// Check if the CSS file has any 'data-wpacu-skip' attribute; if it does, do not alter it
if (isset($linkAttributes['data-wpacu-skip']) || isset($scriptAttributes['data-wpacu-apply-media-query'])) {
continue;
}
// Separate each combined group by the "media" attribute; e.g. we don't want "all" and "print" mixed
$mediaValue = (array_key_exists('media', $linkAttributes) && $linkAttributes['media']) ? $linkAttributes['media'] : 'all';
// Check if there is any rel="preload" (Basic) connected to the rel="stylesheet"
// making sure the file is not added to the final CSS combined file
if (isset($linkAttributes['data-wpacu-style-handle']) &&
$linkAttributes['data-wpacu-style-handle'] &&
ObjectCache::wpacu_cache_get($linkAttributes['data-wpacu-style-handle'], 'wpacu_basic_preload_handles')) {
$mediaValue = 'wpacu_preload_basic_' . $mediaValue;
}
// Make the right reference for later use
if ($linkAttributes['rel'] === 'preload') {
if (isset($linkAttributes['data-wpacu-preload-css-basic'])) {
$mediaValue = 'wpacu_preload_basic_' . $mediaValue;
} else {
continue;
}
}
// Was it optimized and has the URL updated? Check the Source URL to determine if it should be skipped from combining
if (isset($linkAttributes['data-wpacu-link-rel-href-before']) && $linkAttributes['data-wpacu-link-rel-href-before'] && self::skipCombine($linkAttributes['data-wpacu-link-rel-href-before'])) {
continue;
}
// Avoid combining own plugin's CSS (irrelevant) as it takes extra useless space in the caching directory
if (isset($linkAttributes['id']) && $linkAttributes['id'] === WPACU_PLUGIN_ID.'-style-css') {
continue;
}
$localAssetPath = OptimizeCommon::getLocalAssetPath($href, 'css');
// It will skip external stylesheets (from a different domain)
if ( $localAssetPath ) {
$styleExtra = array();
if (isset($linkAttributes['data-wpacu-style-handle'], $wpacuRegisteredStyles[$linkAttributes['data-wpacu-style-handle']]->extra) && OptimizeCommon::appendInlineCodeToCombineAssetType('css')) {
$styleExtra = $wpacuRegisteredStyles[$linkAttributes['data-wpacu-style-handle']]->extra;
}
$sourceRelPath = OptimizeCommon::getSourceRelPath($href);
$alreadyAddedSourceRelPath = isset($combinedUriPathsGroup[$mediaValue]) && in_array($sourceRelPath, $combinedUriPathsGroup[$mediaValue]);
if (! $alreadyAddedSourceRelPath) {
$combinedUriPathsGroup[$mediaValue][] = $sourceRelPath;
}
$localAssetsPathsGroup[$mediaValue][$href] = $localAssetPath;
$alreadyAddedHref = isset($linkHrefsGroup[$mediaValue]) && in_array($href, $linkHrefsGroup[$mediaValue]);
if (! $alreadyAddedHref) {
$linkHrefsGroup[$mediaValue][] = $href;
}
$localAssetsExtraGroup[$mediaValue][$href] = $styleExtra;
}
}
}
// No Link Tags or only one tag in the combined group? Do not proceed with any combining
if ( empty( $combinedUriPathsGroup ) ) {
continue;
}
foreach ($combinedUriPathsGroup as $mediaValue => $combinedUriPaths) {
// There have to be at least two CSS files to create a combined CSS file
if (count($combinedUriPaths) < 2) {
continue;
}
$localAssetsPaths = $localAssetsPathsGroup[$mediaValue];
$linkHrefs = $linkHrefsGroup[$mediaValue];
$localAssetsExtra = array_filter($localAssetsExtraGroup[$mediaValue]);
$maybeDoCssCombine = self::maybeDoCssCombine(
$localAssetsPaths,
$linkHrefs,
$localAssetsExtra,
$docLocationTag
);
// Local path to combined CSS file
$localFinalCssFile = $maybeDoCssCombine['local_final_css_file'];
// URI (e.g. /wp-content/cache/asset-cleanup/[file-name-here.css]) to the combined CSS file
$uriToFinalCssFile = $maybeDoCssCombine['uri_final_css_file'];
// Any link hrefs removed perhaps if the file wasn't combined?
$linkHrefs = $maybeDoCssCombine['link_hrefs'];
if (is_file($localFinalCssFile)) {
$storageJsonContents[$docLocationTag][$mediaValue] = array(
'uri_to_final_css_file' => $uriToFinalCssFile,
'link_hrefs' => array_map(static function($href) {
return str_replace('{site_url}', '', OptimizeCommon::getSourceRelPath($href));
}, $linkHrefs)
);
$storageJsonContentsToSave[$docLocationTag][$mediaValue] = array(
'uri_to_final_css_file' => $uriToFinalCssFile,
'link_hrefs' => array_map(static function($href) {
return OptimizeCommon::getSourceRelPath($href);
}, $linkHrefs)
);
}
}
}
libxml_clear_errors();
OptimizeCommon::setAssetCachedData(
self::$jsonStorageFile,
OptimizeCss::getRelPathCssCacheDir(),
wp_json_encode($storageJsonContentsToSave)
);
}
$cdnUrls = OptimizeCommon::getAnyCdnUrls();
$cdnUrlForCss = isset($cdnUrls['css']) ? $cdnUrls['css'] : false;
if ( ! empty($storageJsonContents) ) {
foreach ($storageJsonContents as $docLocationTag => $mediaValues) {
$groupLocation = 1;
foreach ($mediaValues as $mediaValue => $storageJsonContentLocation) {
if (! isset($storageJsonContentLocation['link_hrefs'][0])) {
continue;
}
// Irrelevant to have only one CSS file in a combine CSS group
if (count($storageJsonContentLocation['link_hrefs']) < 2) {
continue;
}
$storageJsonContentLocation['link_hrefs'] = array_map(static function($href) {
return str_replace('{site_url}', '', $href);
}, $storageJsonContentLocation['link_hrefs']);
$finalTagUrl = OptimizeCommon::filterWpContentUrl($cdnUrlForCss) . OptimizeCss::getRelPathCssCacheDir() . $storageJsonContentLocation['uri_to_final_css_file'];
$finalCssTagAttrs = array();
if (strpos($mediaValue, 'wpacu_preload_basic_') === 0) {
// Put the right "media" value after cleaning the reference
$mediaValueClean = str_replace('wpacu_preload_basic_', '', $mediaValue);
// Basic Preload
$finalCssTag = <<<HTML
<link rel='stylesheet' data-wpacu-to-be-preloaded-basic='1' id='wpacu-combined-css-{$docLocationTag}-{$groupLocation}-preload-it-basic' href='{$finalTagUrl}' type='text/css' media='{$mediaValueClean}' />
HTML;
$finalCssTagRelPreload = <<<HTML
<link rel='preload' as='style' data-wpacu-preload-it-basic='1' id='wpacu-combined-css-{$docLocationTag}-{$groupLocation}-preload-it-basic' href='{$finalTagUrl}' type='text/css' media='{$mediaValueClean}' />
HTML;
$finalCssTagAttrs['rel'] = 'preload';
$finalCssTagAttrs['media'] = $mediaValueClean;
$htmlSource = str_replace(Preloads::DEL_STYLES_PRELOADS, $finalCssTagRelPreload."\n" . Preloads::DEL_STYLES_PRELOADS, $htmlSource);
} else {
// Render-blocking CSS
$finalCssTag = <<<HTML
<link rel='stylesheet' id='wpacu-combined-css-{$docLocationTag}-{$groupLocation}' href='{$finalTagUrl}' type='text/css' media='{$mediaValue}' />
HTML;
$finalCssTagAttrs['rel'] = 'stylesheet';
$finalCssTagAttrs['media'] = $mediaValue;
}
// In case one (e.g. usually a developer) needs to alter it
$finalCssTag = apply_filters(
'wpacu_combined_css_tag',
$finalCssTag,
array(
'attrs' => $finalCssTagAttrs,
'doc_location' => $docLocationTag,
'group_no' => $groupLocation,
'href' => $finalTagUrl
)
);
// Reference: https://stackoverflow.com/questions/2368539/php-replacing-multiple-spaces-with-a-single-space
$finalCssTag = preg_replace('!\s+!', ' ', $finalCssTag);
$htmlSourceBeforeAnyLinkTagReplacement = $htmlSource;
// Detect first LINK tag from the <$locationTag> and replace it with the final combined LINK tag
$firstLinkTag = OptimizeCss::getFirstLinkTag($storageJsonContentLocation['link_hrefs'][0], $htmlSource);
if ($firstLinkTag) {
// 1) Strip inline code before/after it (if any)
// 2) Finally, strip the actual tag
$htmlSource = self::stripTagAndAnyInlineAssocCode( $firstLinkTag, $wpacuRegisteredStyles, $finalCssTag, $htmlSource );
}
if ($htmlSource !== $htmlSourceBeforeAnyLinkTagReplacement) {
$htmlSource = self::stripJustCombinedLinkTags(
$storageJsonContentLocation['link_hrefs'],
$wpacuRegisteredStyles,
$htmlSource
); // Strip the combined files to avoid duplicate code
// There should be at least two replacements made AND all the tags should have been replaced
// Leave no room for errors, otherwise the page could end up with extra files loaded, leading to a slower website
if ($htmlSource === 'do_not_combine') {
$htmlSource = $htmlSourceBeforeAnyLinkTagReplacement;
} else {
$groupLocation++;
}
}
}
}
}
return $htmlSource;
}
/**
* @param $filesSources
* @param $wpacuRegisteredStyles
* @param $htmlSource
*
* @return mixed
*/
public static function stripJustCombinedLinkTags($filesSources, $wpacuRegisteredStyles, $htmlSource)
{
preg_match_all('#<link[^>]*(stylesheet|preload)[^>]*(>)#Umi', $htmlSource, $matchesSourcesFromTags, PREG_SET_ORDER);
$linkTagsStrippedNo = 0;
foreach ($matchesSourcesFromTags as $matchSourceFromTag) {
$matchedSourceFromTag = (isset($matchSourceFromTag[0]) && strip_tags($matchSourceFromTag[0]) === '') ? trim($matchSourceFromTag[0]) : '';
if (! $matchSourceFromTag) {
continue;
}
// The DOMDocument is already checked if it's enabled in doCombine()
$domTag = Misc::initDOMDocument();
$domTag->loadHTML($matchedSourceFromTag);
foreach ($domTag->getElementsByTagName('link') as $tagObject) {
if (empty($tagObject->attributes)) { continue; }
foreach ($tagObject->attributes as $tagAttrs) {
if ($tagAttrs->nodeName === 'href') {
$relNodeValue = trim(OptimizeCommon::getSourceRelPath($tagAttrs->nodeValue));
if (in_array($relNodeValue, $filesSources)) {
$htmlSourceBeforeLinkTagReplacement = $htmlSource;
// 1) Strip inline code before/after it (if any)
// 2) Finally, strip the actual tag
$htmlSource = self::stripTagAndAnyInlineAssocCode( $matchedSourceFromTag, $wpacuRegisteredStyles, '', $htmlSource );
if ($htmlSource !== $htmlSourceBeforeLinkTagReplacement) {
$linkTagsStrippedNo++;
}
}
}
}
}
libxml_clear_errors();
}
// Aren't all the LINK tags stripped? They should be, otherwise, do not proceed with the HTML alteration (no combining will take place)
// Minus the already combined tag
if (($linkTagsStrippedNo < 2) && (count($filesSources) !== $linkTagsStrippedNo)) {
return 'do_not_combine';
}
return $htmlSource;
}
/**
* @param $href
*
* @return bool
*/
public static function skipCombine($href)
{
$regExps = array(
'#/wp-content/bs-booster-cache/#'
);
if (Main::instance()->settings['combine_loaded_css_exceptions'] !== '') {
$loadedCssExceptionsPatterns = trim(Main::instance()->settings['combine_loaded_css_exceptions']);
if (strpos($loadedCssExceptionsPatterns, "\n")) {
// Multiple values (one per line)
foreach (explode("\n", $loadedCssExceptionsPatterns) as $loadedCssExceptionPattern) {
$regExps[] = '#'.trim($loadedCssExceptionPattern).'#';
}
} else {
// Only one value?
$regExps[] = '#'.trim($loadedCssExceptionsPatterns).'#';
}
}
// No exceptions set? Do not skip combination
if (empty($regExps)) {
return false;
}
foreach ($regExps as $regExp) {
$regExp = Misc::purifyRegexValue($regExp);
if ( @preg_match( $regExp, $href ) || ( strpos($href, $regExp) !== false ) ) {
// Skip combination
return true;
}
}
return false;
}
/**
* @param $localAssetsPaths
* @param $linkHrefs
* @param $localAssetsExtra
* @param $docLocationTag
*
* @return array
*/
public static function maybeDoCssCombine($localAssetsPaths, $linkHrefs, $localAssetsExtra, $docLocationTag)
{
// Only combine if $shaOneCombinedUriPaths.css 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 CSS file will be generated and loaded
// Change $finalCombinedCssContent as paths to fonts and images that are relative (e.g. ../, ../../) have to be updated + other optimization changes
$uriToFinalCssFile = $localFinalCssFile = $finalCombinedCssContent = '';
foreach ($localAssetsPaths as $assetHref => $localAssetsPath) {
if ($cssContent = trim(FileSystem::fileGetContents($localAssetsPath, 'combine_css_imports'))) {
$pathToAssetDir = OptimizeCommon::getPathToAssetDir($assetHref);
// Does it have a source map? Strip it
if (strpos($cssContent, '/*# sourceMappingURL=') !== false) {
$cssContent = OptimizeCommon::stripSourceMap($cssContent, 'css');
}
if (apply_filters('wpacu_print_info_comments_in_cached_assets', true)) {
$finalCombinedCssContent .= '/*!' . str_replace( Misc::getWpRootDirPath(), '/', $localAssetsPath ) . "*/\n";
}
$finalCombinedCssContent .= OptimizeCss::maybeFixCssContent($cssContent, $pathToAssetDir . '/') . "\n";
$finalCombinedCssContent = self::appendToCombineCss($localAssetsExtra, $assetHref, $pathToAssetDir, $finalCombinedCssContent);
}
}
// Move any @imports to top; This also strips any @imports to Google Fonts if the option is chosen
$finalCombinedCssContent = trim(OptimizeCss::importsUpdate($finalCombinedCssContent));
if (Main::instance()->settings['google_fonts_remove']) {
$finalCombinedCssContent = FontsGoogleRemove::cleanFontFaceReferences($finalCombinedCssContent);
}
$finalCombinedCssContent = apply_filters('wpacu_local_fonts_display_css_output', $finalCombinedCssContent, Main::instance()->settings['local_fonts_display']);
if ($finalCombinedCssContent) {
$finalCombinedCssContent = trim($finalCombinedCssContent);
$shaOneForCombinedCss = sha1($finalCombinedCssContent);
$uriToFinalCssFile = $docLocationTag . '-' .$shaOneForCombinedCss . '.css';
$localFinalCssFile = WP_CONTENT_DIR . OptimizeCss::getRelPathCssCacheDir() . $uriToFinalCssFile;
if (! is_file($localFinalCssFile)) {
FileSystem::filePutContents($localFinalCssFile, $finalCombinedCssContent);
}
}
return array(
'uri_final_css_file' => $uriToFinalCssFile,
'local_final_css_file' => $localFinalCssFile,
'link_hrefs' => $linkHrefs
);
}
/**
* @param $localAssetsExtra
* @param $assetHref
* @param $pathToAssetDir
* @param $finalAssetsContents
*
* @return string
*/
public static function appendToCombineCss($localAssetsExtra, $assetHref, $pathToAssetDir, $finalAssetsContents)
{
if (isset($localAssetsExtra[$assetHref]['after']) && ! empty($localAssetsExtra[$assetHref]['after'])) {
$afterCssContent = '';
foreach ($localAssetsExtra[$assetHref]['after'] as $afterData) {
if (! is_bool($afterData)) {
$afterCssContent .= $afterData."\n";
}
}
if (trim($afterCssContent)) {
if (MinifyCss::isMinifyCssEnabled() && in_array(Main::instance()->settings['minify_loaded_css_for'], array('inline', 'all'))) {
$afterCssContent = MinifyCss::applyMinification( $afterCssContent );
}
$afterCssContent = OptimizeCss::maybeFixCssContent( $afterCssContent, $pathToAssetDir . '/' );
$finalAssetsContents .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [inline: after] */' : '';
$finalAssetsContents .= $afterCssContent;
$finalAssetsContents .= apply_filters('wpacu_print_info_comments_in_cached_assets', true) ? '/* [/inline: after] */' : '';
$finalAssetsContents .= "\n";
}
}
return $finalAssetsContents;
}
/**
* The targeted LINK tag (which was enqueued and has a handle) is replaced with $replaceWith
* along with any inline content that was added after it via wp_add_inline_style()
*
* @param $targetedLinkTag
* @param $wpacuRegisteredStyles
* @param $replaceWith
* @param $htmlSource
*
* @return mixed
*/
public static function stripTagAndAnyInlineAssocCode($targetedLinkTag, $wpacuRegisteredStyles, $replaceWith, $htmlSource)
{
if (OptimizeCommon::appendInlineCodeToCombineAssetType('css')) {
$scriptExtrasHtml = OptimizeCss::getInlineAssociatedWithLinkHandle($targetedLinkTag, $wpacuRegisteredStyles, 'tag', 'html');
$scriptExtraAfterHtml = (isset($scriptExtrasHtml['after']) && $scriptExtrasHtml['after']) ? "\n".$scriptExtrasHtml['after'] : '';
$htmlSource = str_replace(
array(
$targetedLinkTag . $scriptExtraAfterHtml,
$targetedLinkTag . trim($scriptExtraAfterHtml)
),
$replaceWith,
$htmlSource
);
}
return str_replace(
array(
$targetedLinkTag."\n",
$targetedLinkTag
),
$replaceWith."\n",
$htmlSource
);
}
/**
* @return bool
*/
public static function proceedWithCssCombine()
{
// Not on query string request (debugging purposes)
if ( ! empty($_REQUEST) && array_key_exists('wpacu_no_css_combine', $_REQUEST) ) {
return false;
}
// No CSS 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 CSS file larger
if (! empty($_POST) || is_admin()) {
return false; // Do not combine
}
// Only clean request URIs allowed (with Exceptions)
// Exceptions
if ((strpos($_SERVER['REQUEST_URI'], '?') !== false) && ! 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 and the user is in guest mode (not logged-in)
}
if ($pluginSettings['combine_loaded_css'] === '') {
return false; // Do not combine
}
if (OptimizeCss::isOptimizeCssEnabledByOtherParty('if_enabled')) {
return false; // Do not combine (it's already enabled in other plugin)
}
// "Minify HTML" from WP Rocket is sometimes stripping combined LINK tags
// Better uncombined then missing essential CSS files
if (Misc::isWpRocketMinifyHtmlEnabled()) {
return false;
}
/*
// The option is no longer used since v1.1.7.3 (Pro) & v1.3.6.4 (Lite)
if ( ($pluginSettings['combine_loaded_css'] === 'for_admin'
|| $pluginSettings['combine_loaded_css_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_css_for'] === 'guests' && is_user_logged_in() ) {
return false;
}
if (in_array($pluginSettings['combine_loaded_css'], array('for_all', 1)) ) {
return true; // Do combine
}
// Finally, return false as none of the verification above matched
return false;
}
}

View File

@ -0,0 +1,514 @@
<?php
namespace WpAssetCleanUp\OptimiseAssets;
use MatthiasMullie\Minify\Minify;
use MatthiasMullie\PathConverter\ConverterInterface;
use MatthiasMullie\PathConverter\Converter;
/**
* Combine CSS Imports extended from CSS minifier
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @package Minify
* @author Matthias Mullie <minify@mullie.eu>
* @author Tijs Verkoyen <minify@verkoyen.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
class CombineCssImports extends Minify
{
/**
* @var int maximum import size in kB
*/
protected $maxImportSize = 5;
/**
* @var string[] valid import extensions
*/
protected $importExtensions = array(
'gif' => 'data:image/gif',
'png' => 'data:image/png',
'jpe' => 'data:image/jpeg',
'jpg' => 'data:image/jpeg',
'jpeg' => 'data:image/jpeg',
'svg' => 'data:image/svg+xml',
'woff' => 'data:application/x-font-woff',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'xbm' => 'image/x-xbitmap',
);
/**
* Set the maximum size if files to be imported.
*
* Files larger than this size (in kB) will not be imported into the CSS.
* Importing files into the CSS as data-uri will save you some connections,
* but we should only import relatively small decorative images so that our
* CSS file doesn't get too bulky.
*
* @param int $size Size in kB
*/
public function setMaxImportSize($size)
{
$this->maxImportSize = $size;
}
/**
* Set the type of extensions to be imported into the CSS (to save network
* connections).
* Keys of the array should be the file extensions & respective values
* should be the data type.
*
* @param string[] $extensions Array of file extensions
*/
public function setImportExtensions(array $extensions)
{
$this->importExtensions = $extensions;
}
/**
* Move any import statements to the top.
*
* @param string $content Nearly finished CSS content
*
* @return string
*/
protected function moveImportsToTop($content)
{
if (preg_match_all('/(;?)(@import (?<url>url\()?(?P<quotes>["\']?).+?(?P=quotes)(?(url)\)));?/', $content, $matches)) {
// remove from content
foreach ($matches[0] as $import) {
$content = str_replace($import, '', $content);
}
// add to top
$content = implode(';', $matches[2]).';'.trim($content, ';');
}
return $content;
}
/**
* Combine CSS from import statements.
*
* @import's will be loaded and their content merged into the original file,
* to save HTTP requests.
*
* @param string $source The file to combine imports for
* @param string $content The CSS content to combine imports for
* @param string[] $parents Parent paths, for circular reference checks
*
* @return string
*
*/
protected function combineImports($source, $content, $parents)
{
$importRegexes = array(
// @import url(xxx)
'/
# import statement
@import
# whitespace
\s+
# open url()
url\(
# (optional) open path enclosure
(?P<quotes>["\']?)
# fetch path
(?P<path>.+?)
# (optional) close path enclosure
(?P=quotes)
# close url()
\)
# (optional) trailing whitespace
\s*
# (optional) media statement(s)
(?P<media>[^;]*)
# (optional) trailing whitespace
\s*
# (optional) closing semi-colon
;?
/ix',
// @import 'xxx'
'/
# import statement
@import
# whitespace
\s+
# open path enclosure
(?P<quotes>["\'])
# fetch path
(?P<path>.+?)
# close path enclosure
(?P=quotes)
# (optional) trailing whitespace
\s*
# (optional) media statement(s)
(?P<media>[^;]*)
# (optional) trailing whitespace
\s*
# (optional) closing semi-colon
;?
/ix',
);
// find all relative imports in css
$matches = array();
foreach ($importRegexes as $importRegex) {
if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) {
$matches = array_merge($matches, $regexMatches);
}
}
$search = array();
$replace = array();
// loop the matches
foreach ($matches as $match) {
// get the path for the file that will be imported
$importPath = dirname($source).'/'.$match['path'];
// only replace the import with the content if we can grab the
// content of the file
if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
continue;
}
// check if current file was not imported previously in the same
// import chain.
if (in_array($importPath, $parents)) {
// No need to have and endless loop (the same file imported again and again)
$search[] = $match[0];
$replace[] = '';
continue;
}
// grab referenced file & optimize it (which may include importing
// yet other @import statements recursively)
$minifier = new static($importPath);
$minifier->setMaxImportSize($this->maxImportSize);
$minifier->setImportExtensions($this->importExtensions);
$importContent = $minifier->execute($source, $parents);
// check if this is only valid for certain media
if (!empty($match['media'])) {
$importContent = '@media '.$match['media'].'{'.$importContent.'}';
}
// add to replacement array
$search[] = $match[0];
$replace[] = $importContent;
}
// replace the import statements
return str_replace($search, $replace, $content);
}
/**
* @param $css
*
* @return mixed
*/
protected function alterImportsBetweenComments($css)
{
// RegEx Source: https://blog.ostermiller.org/finding-comments-in-source-code-using-regular-expressions/
preg_match_all('#/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/#', $css, $commentsMatches);
if (isset($commentsMatches[0]) && ! empty($commentsMatches[0])) {
foreach ($commentsMatches[0] as $commentMatch) {
if (strpos($commentMatch, '@import') === false) {
continue; // the comment needs to have @import
}
$newComment = str_replace('@import', '(wpacu)(at)import', $commentMatch);
$css = str_replace($commentMatch, $newComment, $css);
}
}
return $css;
}
/**
* Import files into the CSS, base64-sized.
*
* @url(image.jpg) images will be loaded and their content merged into the
* original file, to save HTTP requests.
*
* @param string $source The file to import files for
* @param string $content The CSS content to import files for
*
* @return string
*/
protected function importFiles($source, $content)
{
$regex = '/url\((["\']?)(.+?)\\1\)/i';
if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
$search = array();
$replace = array();
// loop the matches
foreach ($matches as $match) {
$extension = substr(strrchr($match[2], '.'), 1);
if ($extension && !array_key_exists($extension, $this->importExtensions)) {
continue;
}
// get the path for the file that will be imported
$path = $match[2];
$path = dirname($source).'/'.$path;
// only replace the import with the content if we're able to get
// the content of the file, and it's relatively small
if ($this->canImportFile($path) && $this->canImportBySize($path)) {
// grab content && base64-ize
$importContent = $this->load($path);
$importContent = base64_encode($importContent);
// build replacement
$search[] = $match[0];
$replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
}
}
// replace the import statements
$content = str_replace($search, $replace, $content);
}
return $content;
}
/**
* Perform CSS optimizations.
*
* @param string[optional] $path Path to write the data to
* @param string[] $parents Parent paths, for circular reference checks
*
* @return string The minified data
*/
public function execute($path = null, $parents = array())
{
$content = '';
// loop CSS data (raw data and files)
foreach ($this->data as $source => $css) {
// Some developers might have wrapped @import between comments
// No import for those
$css = $this->alterImportsBetweenComments($css);
$source = is_int($source) ? '' : $source;
$parents = $source ? array_merge($parents, array($source)) : $parents;
$css = $this->combineImports($source, $css, $parents);
$css = $this->importFiles($source, $css);
/*
* If we'll save to a new path, we'll have to fix the relative paths
* to be relative no longer to the source file, but to the new path.
* If we don't write to a file, fall back to same path so no
* conversion happens (because we still want it to go through most
* of the move code, which also addresses url() & @import syntax...)
*/
$converter = $this->getPathConverter($source, $path ?: $source);
$css = $this->move($converter, $css);
// combine css
$content .= $css;
}
$content = $this->moveImportsToTop($content);
return $content;
}
/**
* Moving a css file should update all relative urls.
* Relative references (e.g. ../images/image.gif) in a certain css file,
* will have to be updated when a file is being saved at another location
* (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
*
* @param ConverterInterface $converter Relative path converter
* @param string $content The CSS content to update relative urls for
*
* @return string
*/
protected function move(ConverterInterface $converter, $content)
{
/*
* Relative path references will usually be enclosed by url(). @import
* is an exception, where url() is not necessary around the path (but is
* allowed).
* This *could* be 1 regular expression, where both regular expressions
* in this array are on different sides of a |. But we're using named
* patterns in both regexes, the same name on both regexes. This is only
* possible with a (?J) modifier, but that only works after a fairly
* recent PCRE version. That's why I'm doing 2 separate regular
* expressions & combining the matches after executing of both.
*/
$relativeRegexes = array(
// url(xxx)
'/
# open url()
url\(
\s*
# open path enclosure
(?P<quotes>["\'])?
# fetch path
(?P<path>.+?)
# close path enclosure
(?(quotes)(?P=quotes))
\s*
# close url()
\)
/ix',
// @import "xxx"
'/
# import statement
@import
# whitespace
\s+
# we don\'t have to check for @import url(), because the
# condition above will already catch these
# open path enclosure
(?P<quotes>["\'])
# fetch path
(?P<path>.+?)
# close path enclosure
(?P=quotes)
/ix',
);
// find all relative urls in css
$matches = array();
foreach ($relativeRegexes as $relativeRegex) {
if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
$matches = array_merge($matches, $regexMatches);
}
}
$search = array();
$replace = array();
// loop all urls
foreach ($matches as $match) {
// determine if it's an url() or an @import match
$type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
$url = $match['path'];
if ($this->canImportByPath($url)) {
// attempting to interpret GET-params makes no sense, so let's discard them for a while
$params = strrchr($url, '?');
$url = $params ? substr($url, 0, -strlen($params)) : $url;
// fix relative url
$url = $converter->convert($url);
// now that the path has been converted, re-apply GET-params
$url .= $params;
}
/*
* Urls with control characters above 0x7e should be quoted.
* According to Mozilla's parser, whitespace is only allowed at the
* end of unquoted urls.
* Urls with `)` (as could happen with data: uris) should also be
* quoted to avoid being confused for the url() closing parentheses.
* And urls with a # have also been reported to cause issues.
* Urls with quotes inside should also remain escaped.
*
* @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
* @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
* @see https://github.com/matthiasmullie/minify/issues/193
*/
$url = trim($url);
if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
$url = $match['quotes'] . $url . $match['quotes'];
}
// build replacement
$search[] = $match[0];
if ($type === 'url') {
$replace[] = 'url('.$url.')';
} elseif ($type === 'import') {
$replace[] = '@import "'.$url.'"';
}
}
// replace urls
return str_replace($search, $replace, $content);
}
/**
* Check if file is small enough to be imported.
*
* @param string $path The path to the file
*
* @return bool
*/
protected function canImportBySize($path)
{
return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
}
/**
* Check if file a file can be imported, going by the path.
*
* @param string $path
*
* @return bool
*/
protected function canImportByPath($path)
{
return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
}
/**
* Return a converter to update relative paths to be relative to the new
* destination.
*
* @param string $source
* @param string $target
*
* @return ConverterInterface
*/
protected function getPathConverter($source, $target)
{
return new Converter($source, $target);
}
}

View File

@ -0,0 +1,920 @@
<?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;
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace WpAssetCleanUp\OptimiseAssets;
/**
* Class DynamicLoadedAssets
* @package WpAssetCleanUp
*/
class DynamicLoadedAssets
{
/**
* @param $from
* @param $value
*
* @return bool|mixed|string
*/
public static function getAssetContentFrom($from, $value)
{
$assetContent = '';
if ($from === 'simple-custom-css') {
/*
* Special Case: "Simple Custom CSS" Plugin
*
* /?sccss=1
*
* As it is (no minification or optimization), it adds extra load time to the page
* as the CSS is read via PHP and all the WP environment is loading
*/
if (! $assetContent = self::getSimpleCustomCss()) {
return false;
}
}
if ($from === 'dynamic') { // /? .php? etc.
if (! OptimizeCommon::isSourceFromSameHost($value->src)) {
return array();
}
$response = wp_remote_get(
$value->src
);
if (wp_remote_retrieve_response_code($response) !== 200) {
return false;
}
if (! $assetContent = wp_remote_retrieve_body($response)) {
return false;
}
}
return $assetContent;
}
/**
* "Simple Custom CSS" (better retrieval, especially for localhost and password-protected sites)
*
* @return string
*/
public static function getSimpleCustomCss()
{
$sccssOptions = get_option('sccss_settings');
$sccssRawContent = isset($sccssOptions['sccss-content']) ? $sccssOptions['sccss-content'] : '';
$cssContent = wp_kses($sccssRawContent, array('\'', '\"'));
$cssContent = str_replace('&gt;', '>', $cssContent);
return trim($cssContent);
}
}

View File

@ -0,0 +1,764 @@
<?php
namespace WpAssetCleanUp\OptimiseAssets;
use WpAssetCleanUp\Main;
use WpAssetCleanUp\Misc;
use WpAssetCleanUp\Plugin;
/**
* Class FontsGoogle
* @package WpAssetCleanUp\OptimiseAssets
*/
class FontsGoogle
{
/**
* @var string
*/
public static $containsStr = '//fonts.googleapis.com/';
/**
* @var string
*/
public static $matchesStr = '//fonts.googleapis.com/(css|icon)\?';
/**
*
*/
const NOSCRIPT_WEB_FONT_LOADER = '<span style="display: none;" data-name=wpacu-delimiter content="ASSET CLEANUP NOSCRIPT WEB FONT LOADER"></span>';
/**
*
*/
const COMBINED_LINK_DEL = '<span style="display: none;" data-name=wpacu-delimiter content="ASSET CLEANUP COMBINED LINK LOCATION"></span>';
/**
*
*/
public function init()
{
if (self::preventAnyChange()) {
return;
}
add_filter('wp_resource_hints', array($this, 'resourceHints'), PHP_INT_MAX, 2);
add_action('wp_head', array($this, 'preloadFontFiles'), 1);
add_action('wp_footer', static function() {
if ( Plugin::preventAnyFrontendOptimization() || Main::isTestModeActive() || Main::instance()->settings['google_fonts_remove'] ) {
return;
}
echo self::NOSCRIPT_WEB_FONT_LOADER;
}, PHP_INT_MAX);
add_filter('wpacu_html_source_after_optimization', static function($htmlSource) {
// Is the mark still there and wasn't replaced? Strip it
return str_replace(FontsGoogle::NOSCRIPT_WEB_FONT_LOADER, '', $htmlSource);
});
add_action('init', function() {
// don't apply any changes if not in the front-end view (e.g. Dashboard view)
// or test mode is enabled and a guest user is accessing the page
if ( Plugin::preventAnyFrontendOptimization() || Main::isTestModeActive() || Main::instance()->settings['google_fonts_remove'] ) {
return;
}
add_filter('style_loader_src', array($this, 'alterGoogleFontLink'));
}, 20);
}
/**
* @param $urls
* @param $relationType
*
* @return array
*/
public function resourceHints($urls, $relationType)
{
// don't apply any changes if not in the front-end view (e.g. Dashboard view)
// or test mode is enabled and a guest user is accessing the page
if (is_admin() || Main::isTestModeActive() || Plugin::preventAnyFrontendOptimization()) {
return $urls;
}
// Are the Google Fonts removed? Do not add it and strip any existing ones
if (! empty($urls) && Main::instance()->settings['google_fonts_remove']) {
foreach ($urls as $urlKey => $urlValue) {
if (is_string($urlValue) && ((stripos($urlValue, 'fonts.googleapis.com') !== false) || (stripos($urlValue, 'fonts.gstatic.com') !== false))) {
unset($urls[$urlKey]);
}
}
return $urls; // Finally, return the list after any removals
}
// Google Fonts "preconnect"
if ('preconnect' === $relationType
&& ! Main::instance()->settings['google_fonts_remove'] // "Remove Google Fonts" has to be turned off
&& Main::instance()->settings['google_fonts_preconnect']) { // Needs to be enabled within "Plugin Usage Preferences" in "Settings"
$urls[] = array(
'href' => 'https://fonts.gstatic.com/',
'crossorigin'
);
}
return $urls;
}
/**
*
*/
public function preloadFontFiles()
{
// don't apply any changes if not in the front-end view (e.g. Dashboard view)
// or test mode is enabled and a guest user is accessing the page
if ( Plugin::preventAnyFrontendOptimization() || Main::isTestModeActive() ) {
return;
}
if (! $preloadFontFiles = trim(Main::instance()->settings['google_fonts_preload_files'])) {
return;
}
$preloadFontFilesArray = array();
if (strpos($preloadFontFiles, "\n") !== false) {
foreach (explode("\n", $preloadFontFiles) as $preloadFontFile) {
$preloadFontFile = trim($preloadFontFile);
if (! $preloadFontFile) {
continue;
}
$preloadFontFilesArray[] = $preloadFontFile;
}
} else {
$preloadFontFilesArray[] = $preloadFontFiles;
}
$preloadFontFilesArray = array_unique($preloadFontFilesArray);
$preloadFontFilesOutput = '';
// Finally, go through the list
foreach ($preloadFontFilesArray as $preloadFontFile) {
$preloadFontFilesOutput .= '<link rel="preload" as="font" href="'.esc_attr($preloadFontFile).'" data-wpacu-preload-font="1" crossorigin>'."\n";
}
echo apply_filters('wpacu_preload_google_font_files_output', $preloadFontFilesOutput);
}
/**
* @param $htmlSource
*
* @return false|mixed|string|void
*/
public static function alterHtmlSource($htmlSource)
{
// don't apply any changes if not in the front-end view (e.g. Dashboard view)
// or test mode is enabled and a guest user is accessing the page
// or an AMP page is accessed
if ( Plugin::preventAnyFrontendOptimization() || Main::isTestModeActive()) {
return $htmlSource;
}
/*
* Remove Google Fonts? Stop here as optimization is no longer relevant
*/
if (Main::instance()->settings['google_fonts_remove']) {
return FontsGoogleRemove::cleanHtmlSource($htmlSource);
}
/*
* Optimize Google Fonts
*/
if (stripos($htmlSource, self::$containsStr) !== false) {
// Cleaner HTML Source
$altHtmlSource = preg_replace( '@<(script|style|noscript)[^>]*?>.*?</\\1>@si', '', $htmlSource ); // strip irrelevant tags for the collection
$altHtmlSource = preg_replace( '/<!--[^>]*' . preg_quote( self::$containsStr, '/' ) . '.*?-->/', '', $altHtmlSource ); // strip any comments containing the string
// Get all valid LINKs that have the $string within them
preg_match_all( '#<link[^>]*' . self::$matchesStr . '.*(>)#Usmi', $altHtmlSource, $matchesFromLinkTags, PREG_SET_ORDER );
// Needs to match at least one to carry on with the replacements
if ( isset( $matchesFromLinkTags[0] ) && ! empty( $matchesFromLinkTags[0] ) ) {
if ( Main::instance()->settings['google_fonts_combine'] ) {
/*
* "Combine Google Fonts" IS enabled
*/
$finalCombinableLinks = $preloadedLinks = array();
foreach ( $matchesFromLinkTags as $linkTagArray ) {
$linkTag = $finalLinkTag = trim( trim( $linkTagArray[0], '"\'' ) );
// Extra checks to make sure it's a valid LINK tag
if ( ( strpos( $linkTag, "'" ) !== false && ( substr_count( $linkTag, "'" ) % 2 ) )
|| ( strpos( $linkTag, '"' ) !== false && ( substr_count( $linkTag, '"' ) % 2 ) )
|| ( trim( strip_tags( $linkTag ) ) !== '' ) ) {
continue;
}
// Check if the CSS has any 'data-wpacu-skip' attribute; if it does, do not continue and leave it as it is (non-combined)
if ( preg_match( '#data-wpacu-skip([=>/ ])#i', $linkTag ) ) {
continue;
}
$finalLinkHref = $linkHrefOriginal = Misc::getValueFromTag($linkTag);
// [START] Remove invalid requests with no font family
$urlParse = parse_url( str_replace( '&amp;', '&', $linkHrefOriginal ), PHP_URL_QUERY );
parse_str( $urlParse, $qStr );
if ( isset( $qStr['family'] ) && ! $qStr['family'] ) {
$htmlSource = str_replace( $linkTag, '', $htmlSource );
continue;
}
// [END] Remove invalid requests with no font family
// If anything is set apart from '[none set]', proceed
if ( Main::instance()->settings['google_fonts_display'] ) {
$finalLinkHref = self::alterGoogleFontLink( $linkHrefOriginal );
if ( $finalLinkHref !== $linkHrefOriginal ) {
$finalLinkTag = str_replace( $linkHrefOriginal, $finalLinkHref, $linkTag );
// Finally, alter the HTML source
$htmlSource = str_replace( $linkTag, $finalLinkTag, $htmlSource );
}
}
if ( preg_match( '/rel=(["\'])preload(["\'])/i', $finalLinkTag )
|| strpos( $finalLinkTag, 'data-wpacu-to-be-preloaded-basic' ) ) {
$preloadedLinks[] = $finalLinkHref;
}
$finalCombinableLinks[] = array( 'href' => $finalLinkHref, 'tag' => $finalLinkTag );
}
$preloadedLinks = array_unique( $preloadedLinks );
// Remove data for preloaded LINKs
if ( ! empty( $preloadedLinks ) ) {
foreach ( $finalCombinableLinks as $fclIndex => $combinableLinkData ) {
if ( in_array( $combinableLinkData['href'], $preloadedLinks ) ) {
unset( $finalCombinableLinks[ $fclIndex ] );
}
}
}
$finalCombinableLinks = array_values( $finalCombinableLinks );
// Only proceed with the optimization/combine if there's obviously at least 2 combinable URL requests to Google Fonts
// OR the loading type is different from render-blocking
if ( Main::instance()->settings['google_fonts_combine_type'] || count( $finalCombinableLinks ) > 1 ) {
$htmlSource = self::combineGoogleFontLinks( $finalCombinableLinks, $htmlSource );
}
} elseif (Main::instance()->settings['google_fonts_display']) {
/*
* "Combine Google Fonts" IS NOT enabled
* Go through the links and apply any "font-display"
*/
foreach ( $matchesFromLinkTags as $linkTagArray ) {
$linkTag = trim( trim( $linkTagArray[0], '"\'' ) );
// Extra checks to make sure it's a valid LINK tag
if ( ( strpos( $linkTag, "'" ) !== false && ( substr_count( $linkTag, "'" ) % 2 ) )
|| ( strpos( $linkTag, '"' ) !== false && ( substr_count( $linkTag, '"' ) % 2 ) )
|| ( trim( strip_tags( $linkTag ) ) !== '' ) ) {
continue;
}
// Check if the CSS has any 'data-wpacu-skip' attribute; if it does, do not continue and leave it as it is (non-altered)
if ( preg_match( '#data-wpacu-skip([=>/ ])#i', $linkTag ) ) {
continue;
}
$linkHrefOriginal = Misc::getValueFromTag($linkTag);
// [START] Remove invalid requests with no font family
$urlParse = parse_url( str_replace( '&amp;', '&', $linkHrefOriginal ), PHP_URL_QUERY );
parse_str( $urlParse, $qStr );
if ( isset( $qStr['family'] ) && ! $qStr['family'] ) {
$htmlSource = str_replace( $linkTag, '', $htmlSource );
continue;
}
// [END] Remove invalid requests with no font family
// If anything is set apart from '[none set]', proceed
$newLinkHref = self::alterGoogleFontLink( $linkHrefOriginal );
if ( $newLinkHref !== $linkHrefOriginal ) {
$finalLinkTag = str_replace( $linkHrefOriginal, $newLinkHref, $linkTag );
// Finally, alter the HTML source
$htmlSource = str_replace( $linkTag, $finalLinkTag, $htmlSource );
}
}
}
}
}
// "font-display: swap;" if enabled
$htmlSource = self::alterGoogleFontUrlFromInlineStyleTags($htmlSource);
// Clear any traces
return str_replace(self::NOSCRIPT_WEB_FONT_LOADER, '', $htmlSource);
}
/**
* @param $linkHrefOriginal
* @param bool $escHtml
* @param $alterFor
*
* @return string
*/
public static function alterGoogleFontLink($linkHrefOriginal, $escHtml = true, $alterFor = 'css')
{
$isInVar = false; // The link is inside a variable with a JSON format
// Some special filtering here as some hosting environments (at least staging) behave funny with // inside SCRIPT tags
if ($alterFor === 'js') {
$containsStrNoSlashes = str_replace('/', '', self::$containsStr);
$conditionOne = stripos($linkHrefOriginal, $containsStrNoSlashes) === false;
if (strpos($linkHrefOriginal, '\/') !== false) {
$isInVar = true;
}
} else { // css (default)
$conditionOne = stripos($linkHrefOriginal, self::$containsStr) === false;
}
// Do not continue if it doesn't contain the right string, or it contains 'display=' or it does not contain 'family=' or there is no value set for "font-display"
if ($conditionOne ||
stripos($linkHrefOriginal, 'display=') !== false ||
stripos($linkHrefOriginal, 'family=') === false ||
! Main::instance()->settings['google_fonts_display']) {
// Return original source
return $linkHrefOriginal;
}
$altLinkHref = str_replace('&#038;', '&', $linkHrefOriginal);
if ($isInVar) {
$altLinkHref = str_replace('\/', '/', $altLinkHref);
}
$urlQuery = parse_url($altLinkHref, PHP_URL_QUERY);
parse_str($urlQuery, $outputStr);
// Is there no "display" or there is, but it has an empty value? Append the one we have in the "Settings" - "Google Fonts"
if ( ! isset($outputStr['display']) || (isset($outputStr['display']) && $outputStr['display'] === '') ) {
$outputStr['display'] = Main::instance()->settings['google_fonts_display'];
list($linkHrefFirstPart) = explode('?', $linkHrefOriginal);
// Returned the updated source with the 'display' parameter appended to it
$afterQuestionMark = http_build_query($outputStr);
if ($escHtml) {
$afterQuestionMark = esc_attr($afterQuestionMark);
}
return $linkHrefFirstPart . '?' . $afterQuestionMark;
}
// Return original source
return $linkHrefOriginal;
}
/**
* @param $htmlSource
*
* @return mixed
*/
public static function alterGoogleFontUrlFromInlineStyleTags($htmlSource)
{
if (! preg_match('/@import(\s+)url\(/i', $htmlSource)) {
return $htmlSource;
}
preg_match_all('#<\s*?style\b[^>]*>(.*?)</style\b[^>]*>#s', $htmlSource, $styleMatches, PREG_SET_ORDER);
if (empty($styleMatches)) {
return $htmlSource;
}
// Go through each STYLE tag
foreach ($styleMatches as $styleInlineArray) {
list($styleInlineTag, $styleInlineContent) = $styleInlineArray;
// Check if the STYLE tag has any 'data-wpacu-skip' attribute; if it does, do not continue
if (preg_match('#data-wpacu-skip([=>/ ])#i', $styleInlineTag)) {
continue;
}
// Is the content relevant?
if (! preg_match('/@import(\s+|)(url|\(|\'|")/i', $styleInlineContent)
|| stripos($styleInlineContent, 'fonts.googleapis.com') === false) {
continue;
}
// Do any alteration to the URL of the Google Font
$newCssOutput = self::alterGoogleFontUrlFromCssContent($styleInlineTag);
$htmlSource = str_replace($styleInlineTag, $newCssOutput, $htmlSource);
}
return $htmlSource;
}
/**
* @param $cssContent
*
* @return mixed
*/
public static function alterGoogleFontUrlFromCssContent($cssContent)
{
if (stripos($cssContent, 'fonts.googleapis.com') === false || ! Main::instance()->settings['google_fonts_display']) {
return $cssContent;
}
$regExps = array('/@import(\s+)url\((.*?)\)(|\s+)\;/i', '/@import(\s+|)(\(|\'|")(.*?)(\'|"|\))\;/i');
$newCssOutput = $cssContent;
foreach ($regExps as $regExpIndex => $regExpPattern) {
preg_match_all($regExpPattern, $cssContent, $matchesFromInlineCode, PREG_SET_ORDER);
if (! empty($matchesFromInlineCode)) {
foreach ($matchesFromInlineCode as $matchesFromInlineCodeArray) {
$cssImportRule = $matchesFromInlineCodeArray[0];
if ($regExpIndex === 0) {
$googleApisUrl = trim($matchesFromInlineCodeArray[2], '"\' ');
} else {
$googleApisUrl = trim($matchesFromInlineCodeArray[3], '"\' ');
}
// It has to be a Google Fonts API link
if (stripos($googleApisUrl, 'fonts.googleapis.com') === false) {
continue;
}
$newGoogleApisUrl = self::alterGoogleFontLink($googleApisUrl, false);
if ($newGoogleApisUrl !== $googleApisUrl) {
$newCssImportRule = str_replace($googleApisUrl, $newGoogleApisUrl, $cssImportRule);
$newCssOutput = str_replace($cssImportRule, $newCssImportRule, $newCssOutput);
}
}
}
}
return $newCssOutput;
}
/**
* @param $jsContent
*
* @return mixed
*/
public static function alterGoogleFontUrlFromJsContent($jsContent)
{
if (stripos($jsContent, 'fonts.googleapis.com') === false) {
return $jsContent;
}
$newJsOutput = $jsContent;
preg_match_all('#fonts.googleapis.com(.*?)(["\'])#si', $jsContent, $matchesFromJsCode);
if (isset($matchesFromJsCode[0]) && ! empty($matchesFromJsCode)) {
foreach ($matchesFromJsCode[0] as $match) {
$matchRule = $match;
$googleApisUrl = trim($match, '"\' ');
$newGoogleApisUrl = self::alterGoogleFontLink($googleApisUrl, false, 'js');
if ($newGoogleApisUrl !== $googleApisUrl) {
$newJsMatchOutput = str_replace($googleApisUrl, $newGoogleApisUrl, $matchRule);
$newJsOutput = str_replace($matchRule, $newJsMatchOutput, $newJsOutput);
}
}
}
// Look for any "WebFontConfig = { google: { families: ['font-one', 'font-two'] } }" patterns
if ( stripos( $jsContent, 'WebFontConfig' ) !== false
&& preg_match_all( '#WebFontConfig(.*?)google(\s+|):(\s+|){(\s+|)families(\s+|):(?<families>.*?)]#s', $jsContent, $webFontConfigMatches )
&& isset( $webFontConfigMatches['families'] ) && ! empty( $webFontConfigMatches['families'] )
) {
foreach ($webFontConfigMatches['families'] as $webFontConfigKey => $webFontConfigMatch) {
$originalWholeMatch = $webFontConfigMatches[0][$webFontConfigKey];
$familiesMatchOutput = trim($webFontConfigMatch);
// NO match or existing "display" parameter was found? Do not continue
if (! $familiesMatchOutput || strpos($familiesMatchOutput, 'display=')) {
continue;
}
// Alter the matched string
$familiesNewOutput = preg_replace('/([\'"])$/', '&display='.Main::instance()->settings['google_fonts_display'].'\\1', $familiesMatchOutput);
$newWebFontConfigOutput = str_replace($familiesMatchOutput, $familiesNewOutput, $originalWholeMatch);
// Finally, do the replacement
$newJsOutput = str_replace($originalWholeMatch, $newWebFontConfigOutput, $newJsOutput);
}
}
return $newJsOutput;
}
/**
* @param $finalLinks
* @param $htmlSource
*
* @return false|mixed|string|void
*/
public static function combineGoogleFontLinks($finalLinks, $htmlSource)
{
$fontsArray = array();
foreach ($finalLinks as $finalLinkIndex => $finalLinkData) {
$finalLinkHref = $finalLinkData['href'];
$finalLinkHref = str_replace('&#038;', '&', $finalLinkHref);
$queries = parse_url($finalLinkHref, PHP_URL_QUERY);
parse_str($queries, $fontQueries);
if (! array_key_exists('family', $fontQueries) || array_key_exists('text', $fontQueries)) {
continue;
}
// Strip the existing tag, leave a mark where the final combined LINK will be placed
$stripTagWith = ($finalLinkIndex === 0) ? self::COMBINED_LINK_DEL : '';
$finalLinkTag = $finalLinkData['tag'];
$htmlSource = str_ireplace(array($finalLinkTag."\n", $finalLinkTag), $stripTagWith, $htmlSource);
$family = trim($fontQueries['family']);
$family = trim($family, '|');
if (! $family) {
continue;
}
if (strpos($family, '|') !== false) {
// More than one family per request?
foreach (explode('|', $family) as $familyOne) {
if (strpos($familyOne, ':') !== false) {
// They have types
list ($familyRaw, $familyTypes) = explode(':', $familyOne);
$fontsArray['families'][$familyRaw]['types'] = self::buildSortTypesList($familyTypes);
} else {
// They do not have types
$familyRaw = $familyOne;
$fontsArray['families'][$familyRaw]['types'] = false;
}
}
} elseif (strpos($family, ':') !== false) {
list ($familyRaw, $familyTypes) = explode(':', $family);
$fontsArray['families'][$familyRaw]['types'] = self::buildSortTypesList($familyTypes);
} else {
$familyRaw = $family;
$fontsArray['families'][$familyRaw]['types'] = false;
}
if (array_key_exists('subset', $fontQueries)) {
// More than one subset per request?
if (strpos($fontQueries['subset'], ',') !== false) {
$multipleSubsets = explode(',', trim($fontQueries['subset'], ','));
foreach ($multipleSubsets as $subset) {
$fontsArray['subsets'][] = trim($subset);
}
} else {
// Only one subset
$fontsArray['subsets'][] = $fontQueries['subset'];
}
}
if (array_key_exists('effect', $fontQueries)) {
// More than one subset per request?
if (strpos($fontQueries['effect'], '|') !== false) {
$multipleSubsets = explode('|', trim($fontQueries['effect'], '|'));
foreach ($multipleSubsets as $subset) {
$fontsArray['effects'][] = trim($subset);
}
} else {
// Only one subset
$fontsArray['effects'][] = $fontQueries['effect'];
}
}
}
if (! empty($fontsArray)) {
$finalCombinedParameters = '';
ksort($fontsArray['families']);
// Families
foreach ($fontsArray['families'] as $familyRaw => $fontValues) {
$finalCombinedParameters .= str_replace(' ', '+', $familyRaw);
// Any types? e.g. 400, 400italic, bold, etc.
if (isset($fontValues['types']) && $fontValues['types'] !== false) {
$finalCombinedParameters .= ':' . $fontValues['types'];
}
$finalCombinedParameters .= '|';
}
$finalCombinedParameters = trim($finalCombinedParameters, '|');
// Subsets
if (isset($fontsArray['subsets']) && ! empty($fontsArray['subsets'])) {
sort($fontsArray['subsets']);
$finalCombinedParameters .= '&subset=' . implode(',', array_unique($fontsArray['subsets']));
}
// Effects
if (isset($fontsArray['effects']) && ! empty($fontsArray['effects'])) {
sort($fontsArray['effects']);
$finalCombinedParameters .= '&effect=' . implode('|', array_unique($fontsArray['effects']));
}
if ($fontDisplay = Main::instance()->settings['google_fonts_display']) {
$finalCombinedParameters .= '&display=' . $fontDisplay;
}
$finalCombinedParameters = esc_attr($finalCombinedParameters);
// This is needed for both render-blocking and async (within NOSCRIPT tag as a fallback)
$finalCombinedLink = <<<LINK
<link rel='stylesheet' id='wpacu-combined-google-fonts-css' href='https://fonts.googleapis.com/css?family={$finalCombinedParameters}' type='text/css' media='all' />
LINK;
/*
* Loading Type: Render-Blocking (Default)
*/
if (! Main::instance()->settings['google_fonts_combine_type']) {
$finalCombinedLink .= "\n";
$htmlSource = str_replace(self::COMBINED_LINK_DEL, apply_filters('wpacu_combined_google_fonts_link_tag', $finalCombinedLink), $htmlSource);
}
/*
* Loading Type: Asynchronous via LINK preload with fallback
*/
if (Main::instance()->settings['google_fonts_combine_type'] === 'async_preload') {
$finalPreloadCombinedLink = <<<LINK
<link rel='preload' as="style" onload="this.onload=null;this.rel='stylesheet'" data-wpacu-preload-it-async='1' id='wpacu-combined-google-fonts-css-async-preload' href='https://fonts.googleapis.com/css?family={$finalCombinedParameters}' type='text/css' media='all' />
LINK;
$finalPreloadCombinedLink .= "\n".Misc::preloadAsyncCssFallbackOutput();
$htmlSource = str_replace(self::COMBINED_LINK_DEL, apply_filters('wpacu_combined_google_fonts_async_preload_link_tag', $finalPreloadCombinedLink), $htmlSource);
}
/*
* Loading Type: Asynchronous via Web Font Loader (webfont.js) with fallback
*/
if (Main::instance()->settings['google_fonts_combine_type'] === 'async') { // Async via Web Font Loader
$subSetsStr = '';
if (isset($fontsArray['subsets']) && ! empty($fontsArray['subsets'])) {
sort($fontsArray['subsets']);
$subSetsStr = implode(',', array_unique($fontsArray['subsets']));
}
$wfConfigGoogleFamilies = array();
// Families
$iCount = 0;
foreach ($fontsArray['families'] as $familyRaw => $fontValues) {
$wfConfigGoogleFamily = str_replace(' ', '+', $familyRaw);
// Any types? e.g. 400, 400italic, bold, etc.
$hasTypes = false;
if (isset($fontValues['types']) && $fontValues['types']) {
$wfConfigGoogleFamily .= ':'.$fontValues['types'];
$hasTypes = true;
}
if ($subSetsStr) {
// If there are types, continue to use the comma delimiter
$wfConfigGoogleFamily .= ($hasTypes ? ',' : ':') . $subSetsStr;
}
// Append extra parameters to the last family from the list
if ($iCount === count($fontsArray['families']) - 1) {
// Effects
if (isset($fontsArray['effects']) && ! empty($fontsArray['effects'])) {
sort($fontsArray['effects']);
$wfConfigGoogleFamily .= '&effect=' . implode('|', array_unique($fontsArray['effects']));
}
if ($fontDisplay = Main::instance()->settings['google_fonts_display']) {
$wfConfigGoogleFamily .= '&display=' . $fontDisplay;
}
}
$wfConfigGoogleFamilies[] = "'".$wfConfigGoogleFamily."'";
$iCount++;
}
$wfConfigGoogleFamiliesStr = '['.implode(',', $wfConfigGoogleFamilies).']';
$finalInlineTagWebFontConfig = '<script id=\'wpacu-google-fonts-async-load\' type=\'text/javascript\'>'."\n".'WebFontConfig={google:{families:'.$wfConfigGoogleFamiliesStr.'}};(function(wpacuD){var wpacuWf=wpacuD.createElement(\'script\'),wpacuS=wpacuD.scripts[0];wpacuWf.src=(\'https:\'===document.location.protocol?\'https\':\'http\')+\'://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js\';wpacuWf.async=!0;wpacuS.parentNode.insertBefore(wpacuWf,wpacuS)})(document);'."\n".'</script>';
$htmlSource = str_replace(
array(
self::COMBINED_LINK_DEL,
self::NOSCRIPT_WEB_FONT_LOADER
),
array(
apply_filters( 'wpacu_combined_google_fonts_inline_script_tag', $finalInlineTagWebFontConfig ),
'<noscript>' . apply_filters( 'wpacu_combined_google_fonts_link_tag', $finalCombinedLink ) . '</noscript>' . "\n"
),
$htmlSource
);
}
}
return $htmlSource;
}
/**
* e.g. 300, 400, 400italic, bold, etc.
*
* @param $types
*
* @return string
*/
public static function buildSortTypesList($types)
{
$newTypes = array();
// More than one type per family?
if (strpos($types, ',') !== false) {
$multipleTypes = explode(',', trim($types, ','));
foreach ($multipleTypes as $type) {
if (trim($type)) {
$newTypes[] = trim($type);
}
}
} else {
// Only one type per family
$newTypes[] = $types;
}
$newTypes = array_unique($newTypes);
sort($newTypes);
return implode(',', $newTypes);
}
/**
* @return bool
*/
public static function preventAnyChange()
{
return defined( 'WPACU_ALLOW_ONLY_UNLOAD_RULES' ) && WPACU_ALLOW_ONLY_UNLOAD_RULES;
}
}

View File

@ -0,0 +1,234 @@
<?php
namespace WpAssetCleanUp\OptimiseAssets;
/**
* Class FontsGoogle
* @package WpAssetCleanUp\OptimiseAssets
*/
class FontsGoogleRemove
{
/**
* @var array
*/
public static $stringsToCheck = array(
'//fonts.googleapis.com',
'//fonts.gstatic.com'
);
/**
* @var array
*/
public static $possibleWebFontConfigCdnPatterns = array(
'//ajax.googleapis.com/ajax/libs/webfont/(.*?)', // Google Apis
'//cdnjs.cloudflare.com/ajax/libs/webfont/(.*?)', // Cloudflare
'//cdn.jsdelivr.net/npm/webfontloader@(.*?)' // jsDELIVR
);
/**
* Called late from OptimizeCss after all other optimizations are done (e.g. minify, combine)
*
* @param $htmlSource
*
* @return mixed
*/
public static function cleanHtmlSource($htmlSource)
{
$htmlSource = self::cleanLinkTags($htmlSource);
$htmlSource = self::cleanFromInlineStyleTags($htmlSource);
return str_replace(FontsGoogle::NOSCRIPT_WEB_FONT_LOADER, '', $htmlSource);
}
/**
* @param $htmlSource
*
* @return mixed
*/
public static function cleanLinkTags($htmlSource)
{
// Do not continue if there is no single reference to the string we look for in the clean HTML source
if (stripos($htmlSource, FontsGoogle::$containsStr) === false) {
return $htmlSource;
}
// Get all valid LINKs that have the self::$stringsToCheck within them
$strContainsArray = array_map(static function($containsStr) {
return preg_quote($containsStr, '/');
}, self::$stringsToCheck);
$strContainsFormat = implode('|', $strContainsArray);
preg_match_all('#<link[^>]*(' . $strContainsFormat . ').*(>)#Usmi', $htmlSource, $matchesFromLinkTags, PREG_SET_ORDER);
$stripLinksList = array();
// Needs to match at least one to carry on with the replacements
if (isset($matchesFromLinkTags[0]) && ! empty($matchesFromLinkTags[0])) {
foreach ($matchesFromLinkTags as $linkTagArray) {
$linkTag = trim(trim($linkTagArray[0], '"\''));
if (strip_tags($linkTag) !== '') {
continue; // Something might be funny there, make sure the tag is valid
}
// Check if the Google Fonts CSS has any 'data-wpacu-skip' attribute; if it does, do not remove it
if (preg_match('#data-wpacu-skip([=>/ ])#i', $linkTag)) {
continue;
}
$stripLinksList[$linkTag] = '';
}
$htmlSource = strtr($htmlSource, $stripLinksList);
}
return $htmlSource;
}
/**
* @param $htmlSource
*
* @return mixed
*/
public static function cleanFromInlineStyleTags($htmlSource)
{
if (! preg_match('/(;?)(@import (?<url>url\(|\()?(?P<quotes>["\'()]?).+?(?P=quotes)(?(url)\)));?/', $htmlSource)) {
return $htmlSource;
}
preg_match_all('#<\s*?style\b[^>]*>(.*?)</style\b[^>]*>#s', $htmlSource, $styleMatches, PREG_SET_ORDER);
if (empty($styleMatches)) {
return $htmlSource;
}
// Go through each STYLE tag
foreach ($styleMatches as $styleInlineArray) {
list($styleInlineTag, $styleInlineContent) = $styleInlineArray;
// Check if the STYLE tag has any 'data-wpacu-skip' attribute; if it does, do not continue
if (preg_match('#data-wpacu-skip([=>/ ])#i', $styleInlineTag)) {
continue;
}
$newStyleInlineTag = $styleInlineTag;
$newStyleInlineContent = $styleInlineContent;
// Is the content relevant?
preg_match_all('/(;?)(@import (?<url>url\(|\()?(?P<quotes>["\'()]?).+?(?P=quotes)(?(url)\)));?/', $styleInlineContent, $matches);
if (isset($matches[0]) && ! empty($matches[0])) {
foreach ($matches[0] as $matchedImport) {
$newStyleInlineContent = str_replace($matchedImport, '', $newStyleInlineContent);
}
$newStyleInlineContent = trim($newStyleInlineContent);
// Is the STYLE tag empty after the @imports are removed? It happens on some websites; strip the tag, no point of having it empty
if ($newStyleInlineContent === '') {
$htmlSource = str_replace($styleInlineTag, '', $htmlSource);
} else {
$newStyleInlineTag = str_replace($styleInlineContent, $newStyleInlineContent, $styleInlineTag);
$htmlSource = str_replace($styleInlineTag, $newStyleInlineTag, $htmlSource);
}
}
$styleTagAfterImportsCleaned = $newStyleInlineTag;
$styleTagAfterFontFaceCleaned = trim(self::cleanFontFaceReferences($newStyleInlineContent));
$newStyleInlineTag = str_replace($newStyleInlineContent, $styleTagAfterFontFaceCleaned, $newStyleInlineTag);
$htmlSource = str_replace($styleTagAfterImportsCleaned, $newStyleInlineTag, $htmlSource);
}
return $htmlSource;
}
/**
* @param $importsAddToTop
*
* @return mixed
*/
public static function stripGoogleApisImport($importsAddToTop)
{
// Remove any Google Fonts imports
foreach ($importsAddToTop as $importKey => $importToPrepend) {
if (stripos($importToPrepend, FontsGoogle::$containsStr) !== false) {
unset($importsAddToTop[$importKey]);
}
}
return $importsAddToTop;
}
/**
* If "Google Font Remove" is active, strip its references from JavaScript code as well
*
* @param $jsContent
*
* @return string|string[]|null
*/
public static function stripReferencesFromJsCode($jsContent)
{
if (self::preventAnyChange()) {
return $jsContent;
}
$webFontConfigReferenceOne = "#src(\s+|)=(\s+|)(?<startDel>'|\")(\s+|)((http:|https:|)(".implode('|', self::$possibleWebFontConfigCdnPatterns).")(\s+|))(?<endDel>'|\")#si";
if (stripos($jsContent, 'WebFontConfig') !== false
&& preg_match('/(WebFontConfig\.|\'|"|)google(\s+|)([\'":=])/i', $jsContent)
&& preg_match_all($webFontConfigReferenceOne, $jsContent, $matches) && ! empty($matches)
) {
foreach ($matches[0] as $matchIndex => $matchRow) {
$jsContent = str_replace(
$matchRow,
'src=' . $matches['startDel'][$matchIndex] . $matches['endDel'][$matchIndex] . ';/* Stripped by ' . WPACU_PLUGIN_TITLE . ' */',
$jsContent
);
}
}
$webFontConfigReferenceTwo = '#("|\')((http:|https:|)//fonts.googleapis.com/(.*?))("|\')#si';
if (preg_match($webFontConfigReferenceTwo, $jsContent)) {
$jsContent = preg_replace($webFontConfigReferenceTwo, '\\1\\5', $jsContent);
}
return $jsContent;
}
/**
* @param $cssContent
*
* @return mixed
*/
public static function cleanFontFaceReferences($cssContent)
{
if (self::preventAnyChange()) {
return $cssContent;
}
preg_match_all('#@font-face(|\s+){(.*?)}#si', $cssContent, $matchesFromCssCode, PREG_SET_ORDER);
if (! empty($matchesFromCssCode)) {
foreach ($matchesFromCssCode as $matches) {
$fontFaceSyntax = $matches[0];
preg_match_all('/url(\s+|)\((?![\'"]?(?:data):)[\'"]?([^\'")]*)[\'"]?\)/i', $matches[0], $matchesFromUrlSyntax);
if (! empty($matchesFromUrlSyntax) && stripos(implode('', $matchesFromUrlSyntax[0]), '//fonts.gstatic.com/') !== false) {
$cssContent = str_replace($fontFaceSyntax, '', $cssContent);
}
}
}
return $cssContent;
}
/**
* @return bool
*/
public static function preventAnyChange()
{
return defined( 'WPACU_ALLOW_ONLY_UNLOAD_RULES' ) && WPACU_ALLOW_ONLY_UNLOAD_RULES;
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace WpAssetCleanUp\OptimiseAssets;
use WpAssetCleanUp\Main;
use WpAssetCleanUp\Plugin;
/**
* Class LocalFonts
* @package WpAssetCleanUp\OptimiseAssets
*/
class FontsLocal
{
/**
*
*/
public function init()
{
if (self::preventAnyChange()) {
return;
}
add_action('wp_head', array($this, 'preloadFontFiles'), 1);
}
/**
*
*/
public function preloadFontFiles()
{
// AMP page or Test Mode? Do not print anything
if ( Plugin::preventAnyFrontendOptimization() || Main::isTestModeActive() ) {
return;
}
if (! $preloadFontFiles = trim(Main::instance()->settings['local_fonts_preload_files'])) {
return;
}
$preloadFontFilesArray = array();
if (strpos($preloadFontFiles, "\n") !== false) {
foreach (explode("\n", $preloadFontFiles) as $preloadFontFile) {
$preloadFontFile = trim($preloadFontFile);
if (! $preloadFontFile) {
continue;
}
$preloadFontFilesArray[] = $preloadFontFile;
}
} else {
$preloadFontFilesArray[] = $preloadFontFiles;
}
$preloadFontFilesArray = array_unique($preloadFontFilesArray);
$preloadFontFilesOutput = '';
// Finally, go through the list
foreach ($preloadFontFilesArray as $preloadFontFile) {
$preloadFontFilesOutput .= '<link rel="preload" as="font" href="'.esc_attr($preloadFontFile).'" data-wpacu-preload-font="1" crossorigin>'."\n";
}
echo apply_filters('wpacu_preload_local_font_files_output', $preloadFontFilesOutput);
}
/**
* @return bool
*/
public static function preventAnyChange()
{
if (defined('WPACU_ALLOW_ONLY_UNLOAD_RULES') && WPACU_ALLOW_ONLY_UNLOAD_RULES) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,340 @@
<?php
namespace WpAssetCleanUp\OptimiseAssets;
use WpAssetCleanUp\Main;
use WpAssetCleanUp\Menu;
use WpAssetCleanUp\MetaBoxes;
use WpAssetCleanUp\Misc;
/**
* Class MinifyCss
* @package WpAssetCleanUp\OptimiseAssets
*/
class MinifyCss
{
/**
* @param $cssContent
* @param bool $forInlineStyle
*
* @return string
*/
public static function applyMinification($cssContent, $forInlineStyle = false
)
{
if (class_exists('\MatthiasMullie\Minify\CSS')) {
$sha1OriginalContent = sha1($cssContent);
$checkForAlreadyMinifiedShaOne = mb_strlen($cssContent) > 40000;
// Let's check if the content is already minified
// Save resources as the minify process can take time if the content is very large
// Limit the total number of entries tp 100: if it's more than that, it's likely because there's dynamic JS altering on every page load
if ($checkForAlreadyMinifiedShaOne && OptimizeCommon::originalContentIsAlreadyMarkedAsMinified($sha1OriginalContent, 'styles')) {
return $cssContent;
}
$cssContentBeforeAnyBugChanges = $cssContent;
// [CUSTOM BUG FIX]
// Encode the special matched content to avoid any wrong minification from the minifier
$hasVarWithZeroUnit = false;
preg_match_all('#--([a-zA-Z0-9_-]+):(\s+)0(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)#', $cssContent, $cssVariablesMatches);
if (isset($cssVariablesMatches[0]) && ! empty($cssVariablesMatches[0])) {
$hasVarWithZeroUnit = true;
foreach ($cssVariablesMatches[0] as $zeroUnitMatch) {
$cssContent = str_replace( $zeroUnitMatch, '[wpacu]' . base64_encode( $zeroUnitMatch ) . '[/wpacu]', $cssContent );
}
}
// Fix: If the content is something like "calc(50% - 22px) calc(50% - 22px);" then leave it as it is
preg_match_all('#calc(|\s+)\((.*?)(;|})#si', $cssContent, $cssCalcMatches);
$multipleOrSpecificCalcMatches = array(); // with multiple calc() or with at least one calc() that contains new lines
if (isset($cssCalcMatches[0]) && ! empty($cssCalcMatches[0])) {
foreach ($cssCalcMatches[0] as $cssCalcMatch) {
if (substr_count($cssCalcMatch, 'calc') > 1 || strpos($cssCalcMatch, "\n") !== false) {
$cssContent = str_replace( $cssCalcMatch, '[wpacu]' . base64_encode( $cssCalcMatch ) . '[/wpacu]', $cssContent );
$multipleOrSpecificCalcMatches[] = $cssCalcMatch;
}
}
}
// [/CUSTOM BUG FIX]
$minifier = new \MatthiasMullie\Minify\CSS( $cssContent );
if ( $forInlineStyle ) {
// If the minification is applied for inlined CSS (within STYLE) leave the background URLs unchanged as it sometimes lead to issues
$minifier->setImportExtensions( array() );
}
$minifiedContent = trim( $minifier->minify() );
// [CUSTOM BUG FIX]
// Restore the original content
if ($hasVarWithZeroUnit) {
foreach ( $cssVariablesMatches[0] as $zeroUnitMatch ) {
$zeroUnitMatchAlt = str_replace(': 0', ':0', $zeroUnitMatch); // remove the space
$minifiedContent = str_replace( '[wpacu]' . base64_encode( $zeroUnitMatch ) . '[/wpacu]', $zeroUnitMatchAlt, $minifiedContent );
}
}
if ( ! empty($multipleOrSpecificCalcMatches) ) {
foreach ( $multipleOrSpecificCalcMatches as $cssCalcMatch ) {
$originalCssCalcMatch = $cssCalcMatch;
$cssCalcMatch = preg_replace(array('#calc\(\s+#', '#\s+\);#'), array('calc(', ');'), $originalCssCalcMatch);
$cssCalcMatch = str_replace(' ) calc(', ') calc(', $cssCalcMatch);
$minifiedContent = str_replace( '[wpacu]' . base64_encode( $originalCssCalcMatch ) . '[/wpacu]', $cssCalcMatch, $minifiedContent );
}
}
// [/CUSTOM BUG FIX]
// Is there any [wpacu] left? Hmm, the replacement wasn't alright. Make sure to use the original minified version
if (strpos($minifiedContent, '[wpacu]') !== false && strpos($minifiedContent, '[/wpacu]') !== false) {
$minifier = new \MatthiasMullie\Minify\CSS( $cssContentBeforeAnyBugChanges );
if ( $forInlineStyle ) {
// If the minification is applied for inlined CSS (within STYLE) leave the background URLs unchanged as it sometimes leads to issues
$minifier->setImportExtensions( array() );
}
$minifiedContent = trim( $minifier->minify() );
}
if ($checkForAlreadyMinifiedShaOne && $minifiedContent === $cssContent) {
// If the resulting content is the same, mark it as minified to avoid the minify process next time
OptimizeCommon::originalContentMarkAsAlreadyMinified( $sha1OriginalContent, 'styles' );
}
return $minifiedContent;
}
return $cssContent;
}
/**
* @param $href
* @param string $handle
*
* @return bool
*/
public static function skipMinify($href, $handle = '')
{
// Things like WP Fastest Cache Toolbar CSS shouldn't be minified and take up space on the server
if ($handle !== '' && in_array($handle, Main::instance()->skipAssets['styles'])) {
return true;
}
// Some of these files (e.g. from Oxygen, WooCommerce) are already minified
$regExps = array(
'#/wp-content/plugins/wp-asset-clean-up(.*?).min.css#',
// Formidable Forms
'#/wp-content/plugins/formidable/css/formidableforms.css#',
// Oxygen
//'#/wp-content/plugins/oxygen/component-framework/oxygen.css#',
// WooCommerce
'#/wp-content/plugins/woocommerce/assets/css/woocommerce-layout.css#',
'#/wp-content/plugins/woocommerce/assets/css/woocommerce.css#',
'#/wp-content/plugins/woocommerce/assets/css/woocommerce-smallscreen.css#',
'#/wp-content/plugins/woocommerce/assets/css/blocks/style.css#',
'#/wp-content/plugins/woocommerce/packages/woocommerce-blocks/build/style.css#',
// Google Site Kit: the files are already optimized
'#/wp-content/plugins/google-site-kit/#',
// Other libraries from the core that end in .min.css
'#/wp-includes/css/(.*?).min.css#',
// Files within /wp-content/uploads/ or /wp-content/cache/
// Could belong to plugins such as "Elementor", "Oxygen" etc.
'#/wp-content/uploads/elementor/(.*?).css#',
'#/wp-content/uploads/oxygen/css/(.*?)-(.*?).css#',
'#/wp-content/cache/(.*?).css#',
// Already minified, and it also has a random name making the cache folder make bigger
'#/wp-content/bs-booster-cache/#',
);
$regExps = Misc::replaceRelPluginPath($regExps);
if (Main::instance()->settings['minify_loaded_css_exceptions'] !== '') {
$loadedCssExceptionsPatterns = trim(Main::instance()->settings['minify_loaded_css_exceptions']);
if (strpos($loadedCssExceptionsPatterns, "\n")) {
// Multiple values (one per line)
foreach (explode("\n", $loadedCssExceptionsPatterns) as $loadedCssExceptionPattern) {
$regExps[] = '#'.trim($loadedCssExceptionPattern).'#';
}
} else {
// Only one value?
$regExps[] = '#'.trim($loadedCssExceptionsPatterns).'#';
}
}
foreach ($regExps as $regExp) {
if ( preg_match( $regExp, $href ) || ( strpos($href, $regExp) !== false ) ) {
return true;
}
}
return false;
}
/**
* @param $htmlSource
*
* @return mixed|string
*/
public static function minifyInlineStyleTags($htmlSource)
{
if (stripos($htmlSource, '<style') === false) {
return $htmlSource; // no STYLE tags
}
$skipTagsContaining = array(
'data-wpacu-skip',
'astra-theme-css-inline-css',
'astra-edd-inline-css',
'et-builder-module-design-cached-inline-styles',
'fusion-stylesheet-inline-css',
'woocommerce-general-inline-css',
'woocommerce-inline-inline-css',
'data-wpacu-own-inline-style',
// Only shown to the admin, irrelevant for any optimization (save resources)
'data-wpacu-inline-css-file'
// already minified/optimized since the INLINE was generated from the cached file
);
$fetchType = 'regex';
if ( $fetchType === 'regex' ) {
preg_match_all( '@(<style[^>]*?>).*?</style>@si', $htmlSource, $matchesStyleTags, PREG_SET_ORDER );
if ( $matchesStyleTags === null ) {
return $htmlSource;
}
foreach ($matchesStyleTags as $matchedStyle) {
if ( ! (isset($matchedStyle[0]) && $matchedStyle[0]) ) {
continue;
}
$originalTag = $matchedStyle[0];
if (substr($originalTag, -strlen('></style>')) === strtolower('></style>')) {
// No empty STYLE tags
continue;
}
// No need to use extra resources as the tag is already minified
if ( preg_match( '(' . implode( '|', $skipTagsContaining ) . ')', $originalTag ) ) {
continue;
}
$tagOpen = $matchedStyle[1];
$withTagOpenStripped = substr($originalTag, strlen($tagOpen));
$originalTagContents = substr($withTagOpenStripped, 0, -strlen('</style>'));
if ( $originalTagContents ) {
$newTagContents = OptimizeCss::maybeAlterContentForInlineStyleTag( $originalTagContents, true, array( 'just_minify' ) );
// Only comments or no content added to the inline STYLE tag? Strip it completely to reduce the number of DOM elements
if ( $newTagContents === '/**/' || ! $newTagContents ) {
$htmlSource = str_replace( '>' . $originalTagContents . '</', '></', $htmlSource );
preg_match( '#<style.*?>#si', $originalTag, $matchFromStyle );
if ( isset( $matchFromStyle[0] ) && $styleTagWithoutContent = $matchFromStyle[0] ) {
$styleTagWithoutContentAlt = str_ireplace( '"', '\'', $styleTagWithoutContent );
$htmlSource = str_ireplace( array(
$styleTagWithoutContent . '</style>',
$styleTagWithoutContentAlt . '</style>'
), '', $htmlSource );
}
} else {
// It has content; do the replacement
$htmlSource = str_replace(
'>' . $originalTagContents . '</style>',
'>' . $newTagContents . '</style>',
$htmlSource
);
}
}
}
}
return $htmlSource;
}
/**
* @return bool
*/
public static function isMinifyCssEnabled()
{
if (defined('WPACU_IS_MINIFY_CSS_ENABLED')) {
return WPACU_IS_MINIFY_CSS_ENABLED;
}
// Request Minify On The Fly
// It will preview the page with CSS minified
// Only if the admin is logged-in as it uses more resources (CPU / Memory)
if ( isset($_GET['wpacu_css_minify']) && Menu::userCanManageAssets() ) {
self::isMinifyCssEnabledChecked('true');
return true;
}
if ( isset($_REQUEST['wpacu_no_css_minify']) || // not on query string request (debugging purposes)
is_admin() || // not for Dashboard view
(! Main::instance()->settings['minify_loaded_css']) || // Minify CSS has to be Enabled
(Main::instance()->settings['test_mode'] && ! Menu::userCanManageAssets()) ) { // Does not trigger if "Test Mode" is Enabled
self::isMinifyCssEnabledChecked('false');
return false;
}
$isSingularPage = defined('WPACU_CURRENT_PAGE_ID') && WPACU_CURRENT_PAGE_ID > 0 && is_singular();
if ($isSingularPage || Misc::isHomePage()) {
// If "Do not minify CSS on this page" is checked in "Asset CleanUp: Options" side meta box
if ($isSingularPage) {
$pageOptions = MetaBoxes::getPageOptions( WPACU_CURRENT_PAGE_ID ); // Singular page
} else {
$pageOptions = MetaBoxes::getPageOptions(0, 'front_page'); // Home page
}
if ( isset( $pageOptions['no_css_minify'] ) && $pageOptions['no_css_minify'] ) {
self::isMinifyCssEnabledChecked('false');
return false;
}
}
if (OptimizeCss::isOptimizeCssEnabledByOtherParty('if_enabled')) {
self::isMinifyCssEnabledChecked('false');
return false;
}
self::isMinifyCssEnabledChecked('true');
return true;
}
/**
* @param $value
*/
public static function isMinifyCssEnabledChecked($value)
{
if (! defined('WPACU_IS_MINIFY_CSS_ENABLED')) {
if ($value === 'true') {
define( 'WPACU_IS_MINIFY_CSS_ENABLED', true );
} elseif ($value === 'false') {
define( 'WPACU_IS_MINIFY_CSS_ENABLED', false );
}
}
}
}

View File

@ -0,0 +1,268 @@
<?php
namespace WpAssetCleanUp\OptimiseAssets;
use WpAssetCleanUp\Main;
use WpAssetCleanUp\Menu;
use WpAssetCleanUp\MetaBoxes;
use WpAssetCleanUp\Misc;
/**
* Class MinifyJs
* @package WpAssetCleanUp\OptimiseAssets
*/
class MinifyJs
{
/**
* @param $jsContent
*
* @return string|string[]|null
*/
public static function applyMinification($jsContent)
{
if (class_exists('\MatthiasMullie\Minify\JS')) {
$sha1OriginalContent = sha1($jsContent);
$checkForAlreadyMinifiedShaOne = mb_strlen($jsContent) > 40000;
// Let's check if the content is already minified
// Save resources as the minify process can take time if the content is very large
// Limit the total number of entries to 100: if it's more than that, it's likely because there's dynamic JS altering on every page load
if ($checkForAlreadyMinifiedShaOne && OptimizeCommon::originalContentIsAlreadyMarkedAsMinified($sha1OriginalContent, 'scripts')) {
return $jsContent;
}
// Minify it
$alreadyMinified = false; // default
$minifier = new \MatthiasMullie\Minify\JS($jsContent);
$minifiedContent = trim($minifier->minify());
if (trim($minifiedContent) === trim(trim($jsContent, ';'))) {
$minifiedContent = $jsContent; // consider them the same if only the ';' at the end was stripped (it doesn't worth the resources that would be used)
$alreadyMinified = true;
}
// If the resulting content is the same, mark it as minified to avoid the minify process next time
if ($checkForAlreadyMinifiedShaOne && $alreadyMinified) {
// If the resulting content is the same, mark it as minified to avoid the minify process next time
OptimizeCommon::originalContentMarkAsAlreadyMinified( $sha1OriginalContent, 'scripts' );
}
return $minifiedContent;
}
return $jsContent;
}
/**
* @param $src
* @param string $handle
*
* @return bool
*/
public static function skipMinify($src, $handle = '')
{
// Things like WP Fastest Cache Toolbar JS shouldn't be minified and take up space on the server
if ($handle !== '' && in_array($handle, Main::instance()->skipAssets['scripts'])) {
return true;
}
$regExps = array(
'#/wp-content/plugins/wp-asset-clean-up(.*?).js#',
// Other libraries from the core that end in .min.js
'#/wp-includes/(.*?).min.js#',
// jQuery & jQuery Migrate
'#/wp-includes/js/jquery/jquery.js#',
'#/wp-includes/js/jquery/jquery-migrate.js#',
// Files within /wp-content/uploads/
// Files within /wp-content/uploads/ or /wp-content/cache/
// Could belong to plugins such as "Elementor, "Oxygen" etc.
//'#/wp-content/uploads/(.*?).js#',
'#/wp-content/cache/(.*?).js#',
// Already minified, and it also has a random name making the cache folder make bigger
'#/wp-content/bs-booster-cache/#',
// Elementor .min.js
'#/wp-content/plugins/elementor/assets/(.*?).min.js#',
// WooCommerce Assets
'#/wp-content/plugins/woocommerce/assets/js/(.*?).min.js#',
// Google Site Kit
// The files are already optimized (they just have comments once in a while)
// Minifying them causes some errors, so better to leave them load as they are
'#/wp-content/plugins/google-site-kit/#',
// TranslatePress Multilingual
'#/translatepress-multilingual/assets/js/trp-editor.js#',
);
$regExps = Misc::replaceRelPluginPath($regExps);
if (Main::instance()->settings['minify_loaded_js_exceptions'] !== '') {
$loadedJsExceptionsPatterns = trim(Main::instance()->settings['minify_loaded_js_exceptions']);
if (strpos($loadedJsExceptionsPatterns, "\n")) {
// Multiple values (one per line)
foreach (explode("\n", $loadedJsExceptionsPatterns) as $loadedJsExceptionPattern) {
$regExps[] = '#'.trim($loadedJsExceptionPattern).'#';
}
} else {
// Only one value?
$regExps[] = '#'.trim($loadedJsExceptionsPatterns).'#';
}
}
foreach ($regExps as $regExp) {
if ( preg_match( $regExp, $src ) || ( strpos($src, $regExp) !== false ) ) {
return true;
}
}
return false;
}
/**
* @param $htmlSource
*
* @return mixed|string
*/
public static function minifyInlineScriptTags($htmlSource)
{
if (stripos($htmlSource, '<script') === false) {
return $htmlSource; // no SCRIPT tags, hmm
}
$skipTagsContaining = array_map( static function ( $toMatch ) {
return preg_quote($toMatch, '/');
}, array(
'data-wpacu-skip',
'/* <![CDATA[ */', // added via wp_localize_script()
'wpacu-google-fonts-async-load',
'wpacu-preload-async-css-fallback',
/* [wpacu_pro] */'data-wpacu-inline-js-file',/* [/wpacu_pro] */
'document.body.prepend(wpacuLinkTag',
'var wc_product_block_data = JSON.parse( decodeURIComponent(',
'/(^|\s)(no-)?customize-support(?=\s|$)/', // WP Core
'b[c] += ( window.postMessage && request ? \' \' : \' no-\' ) + cs;', // WP Core
'data-wpacu-own-inline-script', // Only shown to the admin, irrelevant for any optimization (save resources)
// [wpacu_pro]
'data-wpacu-inline-js-file', // already minified/optimized since the INLINE was generated from the cached file
// [/wpacu_pro]
));
// Do not perform another \DOMDocument call if it was done already somewhere else (e.g. CombineJs)
$fetchType = 'regex'; // 'regex' or 'dom'
if ($fetchType === 'regex') {
preg_match_all( '@(<script[^>]*?>).*?</script>@si', $htmlSource, $matchesScriptTags, PREG_SET_ORDER );
if ( $matchesScriptTags === null ) {
return $htmlSource;
}
foreach ($matchesScriptTags as $matchedScript) {
if (isset($matchedScript[0]) && $matchedScript[0]) {
$originalTag = $matchedScript[0];
if (strpos($originalTag, 'src=') && strtolower(substr($originalTag, -strlen('></script>'))) === strtolower('></script>')) {
// Only inline SCRIPT tags allowed
continue;
}
// No need to use extra resources as the tag is already minified
if ( preg_match( '/(' . implode( '|', $skipTagsContaining ) . ')/', $originalTag ) ) {
continue;
}
// Only 'text/javascript' type is allowed for minification
$scriptType = Misc::getValueFromTag($originalTag, 'type') ?: 'text/javascript'; // default
if ($scriptType !== 'text/javascript') {
continue;
}
$tagOpen = $matchedScript[1];
$withTagOpenStripped = substr($originalTag, strlen($tagOpen));
$originalTagContents = substr($withTagOpenStripped, 0, -strlen('</script>'));
$newTagContents = OptimizeJs::maybeAlterContentForInlineScriptTag( $originalTagContents, true );
if ( $newTagContents !== $originalTagContents ) {
$htmlSource = str_ireplace( '>' . $originalTagContents . '</script', '>' . $newTagContents . '</script', $htmlSource );
}
}
}
}
return $htmlSource;
}
/**
* @return bool
*/
public static function isMinifyJsEnabled()
{
if (defined('WPACU_IS_MINIFY_JS_ENABLED')) {
return WPACU_IS_MINIFY_JS_ENABLED;
}
// Request Minify On The Fly
// It will preview the page with JS minified
// Only if the admin is logged-in as it uses more resources (CPU / Memory)
if ( isset($_GET['wpacu_js_minify']) && Menu::userCanManageAssets()) {
self::isMinifyJsEnabledChecked('true');
return true;
}
if ( isset($_REQUEST['wpacu_no_js_minify']) || // not on query string request (debugging purposes)
is_admin() || // not for Dashboard view
(! Main::instance()->settings['minify_loaded_js']) || // Minify JS has to be Enabled
(Main::instance()->settings['test_mode'] && ! Menu::userCanManageAssets()) ) { // Does not trigger if "Test Mode" is Enabled
self::isMinifyJsEnabledChecked('false');
return false;
}
$isSingularPage = defined('WPACU_CURRENT_PAGE_ID') && WPACU_CURRENT_PAGE_ID > 0 && is_singular();
if ($isSingularPage || Misc::isHomePage()) {
// If "Do not minify JS on this page" is checked in "Asset CleanUp: Options" side meta box
if ($isSingularPage) {
$pageOptions = MetaBoxes::getPageOptions( WPACU_CURRENT_PAGE_ID ); // Singular page
} else {
$pageOptions = MetaBoxes::getPageOptions(0, 'front_page'); // Home page
}
if ( isset( $pageOptions['no_js_minify'] ) && $pageOptions['no_js_minify'] ) {
self::isMinifyJsEnabledChecked('false');
return false;
}
}
if (OptimizeJs::isOptimizeJsEnabledByOtherParty('if_enabled')) {
self::isMinifyJsEnabledChecked('false');
return false;
}
self::isMinifyJsEnabledChecked('true');
return true;
}
/**
* @param $value
*/
public static function isMinifyJsEnabledChecked($value)
{
if (! defined('WPACU_IS_MINIFY_JS_ENABLED')) {
if ($value === 'true') {
define( 'WPACU_IS_MINIFY_JS_ENABLED', true );
} elseif ($value === 'false') {
define( 'WPACU_IS_MINIFY_JS_ENABLED', false );
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More