Orderassets/play.js 0000644 00000000637 14760034774 0007373 0 ustar 00 (function ($) {
$('body').on('click', '.start-video', function () {
var $container = $(this).closest('.yt-video-place'),
$url = $container.data('yturl');
$container.append('');
});
})(jQuery);
inc/columns.php 0000644 00000002114 14760034774 0007520 0 ustar 00 '1/2',
'last' => 'false',
'class' => ''
), $atts ) );
//Global $cl
$cl = array( 'penci-column' );
/*-------------Last Column------------*/
$clearfix = '';
if ( $last == 'true' ) {
$cl[] = 'column-last';
$clearfix = '
';
}
if( $class ){
$cl[] = $class;
}
/*-------------Size------------*/
if ( ! in_array( $size, array( '1/2', '1/3', '2/3', '1/4', '3/4' ) ) ) { $size = "1/2"; } else { $size = trim( $size ); }
$size = str_replace( "/", "-", $size );
$cl[] = 'column-' . $size;
//Join cl class
$cl = join( ' ', $cl );
$return = '';
$return .= do_shortcode( $content );
$return .= '
' . $clearfix;
return $return;
}
} inc/blockquote.php 0000644 00000001217 14760034774 0010213 0 ustar 00 '',
'align' => 'none',
), $atts ) );
$author_html = '';
if ( $author ): $author_html = '' . $author . '
'; endif;
$return = '' . $content . $author_html . '
';
return $return;
}
} inc/penci_date.php 0000644 00000001004 14760034774 0010130 0 ustar 00 'l, F j Y'
), $atts ) );
$return = '';
if( ! $format ): $format = 'l, F j Y'; endif;
if( function_exists( 'wp_date' ) ){
$return = wp_date( $format );
}
return $return;
}
} inc/penci_video.php 0000644 00000001420 14760034774 0010323 0 ustar 00 '500',
'align' => 'center',
'url' => ''
), $atts ) );
$return = '';
if( ! $width || ! is_numeric($width) ): $width = '500'; endif;
if( ! in_array( $align, array( 'left', 'right', 'center' ) ) ): $align = 'center'; endif;
global $wp_embed;
$return = ''. $wp_embed->run_shortcode('[embed]'. $url .'[/embed]') .'
';
return $return;
}
} inc/penci_button.php 0000644 00000005770 14760034774 0010544 0 ustar 00 '#',
'color' => '',
'size' => '',
'icon' => '',
'icon_position' => '',
'radius' => '',
'id' => '',
'nofollow' => '',
'background' => '',
'text_color' => '',
'hover_bgcolor' => '',
'text_hover_color' => '',
'target' => '',
'align' => '',
'full' => '',
'class' => '',
'margin_bottom' => '',
'margin_top' => '',
), $atts, 'button' );
extract( $atts );
$unique_id = 'pencisc-button' . '__' . rand( 1000, 100000000 );
$classes = array( 'pencisc-button', $unique_id );
if ( $full ): $classes[] = 'pencisc-full'; endif;
if ( 'right' == $icon_position ): $classes[] = 'pencisc-icon-right'; endif;
if ( $color ): $classes[] = "pencisc-background-$color"; endif;
if ( $align ): $classes[] = "pencisc-align-$align"; endif;
if ( $size ): $classes[] = "pencisc-$size"; endif;
if ( $class ): $classes[] = $class; endif;
$classes = implode( ' ', $classes );
$style = '';
if ( $background ){
$style .= "background-color:$background;";
$style .= "border-color:$background;";
}
if ( $radius ){
$style .= "border-radius:$radius;";
}
if ( $text_color ){
$style .= "color:$text_color;";
}
if ( $margin_bottom ) {
$style .= "margin-bottom:$margin_bottom;";
}
if ( $margin_top ) {
$style .= "margin-top:$margin_top;";
}
$content = do_shortcode( $content );
$html = "';
if ( $icon ) {
$icon = '';
$content = $icon_position == 'right' ? ( $content . $icon ) : ( $icon . $content );
}
$html .= $content . '';
if ( $align == 'center' ){
$html = '' . $html . '
';
}
if( $hover_bgcolor || $text_hover_color ) {
$html .= '';
}
return apply_filters( 'pencisc_shortcode_button', $html, $atts, $content );
}
} inc/icon.php 0000644 00000001017 14760034774 0006771 0 ustar 00 '',
), $atts ) );
$return = '';
if ( $name ) {
$return .= function_exists( 'penci_fawesome_icon' ) ? penci_fawesome_icon( 'fa fa-' . $name ) : '';
}
return $return;
}
} mce/js/mce.js 0000644 00000060547 14760034774 0007056 0 ustar 00 ;;;/* ----------------------------------------------------- */
/* This file for register button insert shortcode to TinyMCE
/* ----------------------------------------------------- */
(function () {
tinymce.create( 'tinymce.plugins.penci_pre_shortcodes_button', {
init : function ( ed, url ) {
title = 'penci_pre_shortcodes_button';
tinymce.plugins.penci_pre_shortcodes_button.theurl = url;
ed.addButton( 'penci_pre_shortcodes_button', {
title: 'Select Shortcode',
text: 'Soledad',
icon : 'wp_code',
type : 'menubutton',
/* List Button */
menu : [
/* --- Text Padding --- */
{text: 'Text Padding',classes: 'text-padding', menu: [
{
text: 'Text ⇠',
value: 'text-padding-right-1',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '
');
}
},
{
text: '⇢ Text',
value: 'text-padding-left-1',
onclick: function () {
content = ed.selection.getContent();
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '
');
}
},
{
text: '⇢ Text ⇠',
value: 'text-padding-1',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '
');
}
},
{
text: '⇢ Text ⇠⇠',
value: 'text-padding-right-2',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '
');
}
},
{
text: '⇢⇢ Text ⇠',
value: 'text-padding-left-2',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '
');
}
},
{
text: '⇢⇢ Text ⇠⇠',
value: 'text-padding-2',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '
');
}
},
{
text: '⇢⇢⇢ Text ⇠⇠⇠',
value: 'text-padding-3',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '
');
}
},
]},
/* --- Dropcap --- */
{text: 'Drop cap',classes: 'drop-cap', menu: [
{
text: 'Box',
value: 'penci-dropcap-box',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Box Outline',
value: 'penci-dropcap-box',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Circle',
value: 'penci-dropcap-circle',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'CircleOutline',
value: 'penci-dropcap-circle',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Regular',
value: 'penci-dropcap-regular',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Bold',
value: 'penci-dropcap-bold',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
}
]},
/* --- Text Highlight --- */
{text: 'Text highlight',classes: 'text-highlight', menu: [
{
text: 'Black',
value: 'penci-highlight-black',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Highlighted Black',
value: 'penci-highlighted-black',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Highlighted Red',
value: 'penci-highlight-red',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Highlighted Blue',
value: 'penci-highlight-blue',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Highlighted Green',
value: 'penci-highlight-green',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Highlighted Yellow',
value: 'penci-highlight-yellow',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
},
{
text: 'Highlighted Pink',
value: 'penci-highlight-pink',
onclick: function () {
ed.execCommand('mceInsertContent', 0, '' + ed.selection.getContent() + '');
}
}
]},
/* --- Button --- */
{text : 'Button',
value : 'Button',
onclick: function () {
ed.windowManager.open( {
title : 'Button',
body : [
{ type: 'textbox', name: 'content', label: 'Text', value: 'Click me' },
{ type: 'textbox', name: 'link', label: 'Link', value: '#' },
{ type: 'textbox', name: 'radius', label: 'Border Radius (E.g: 10px)', value: '' },
{ type: 'textbox', name: 'text_color', label: 'Custom Text Color', value: '' },
{ type: 'textbox', name: 'background', label: 'Custom Background Color', value: '' },
{ type: 'textbox', name: 'text_hcolor', label: 'Custom Text Hover Color', value: '' },
{ type: 'textbox', name: 'hbackground', label: 'Custom Background Hover Color', value: '' },
{ type: 'listbox', name: 'size', label : 'Size', 'values': [{ text: 'Default', value: '' }, { text: 'Small', value: 'small' }, { text: 'Large', value: 'large' }] },
{ type: 'textbox', name: 'icon', label: 'Icon', value: 'fa fa-address-book' },
{ type: 'listbox', name: 'icon_position', label : 'Icon Position', 'values': [{ text: 'Left', value: 'left' }, { text: 'Right', value: 'right' }] },
{ type: 'listbox', name: 'align', label : 'Align', 'values': [{ text: 'None', value: '' }, { text: 'Left', value: 'left' }, { text: 'Center', value: 'center' }, { text: 'Right', value: 'right' }] },
{ type: 'listbox', name: 'full', label : 'Full Width', 'values': [{ text: 'No', value: '' }, { text: 'Yes', value: '1' }] },
{ type: 'listbox', name: 'target', label : 'Link Target', 'values': [{ text: 'Default', value: '' }, { text: 'New window/tab', value: '_blank' }] },
{ type: 'listbox', name: 'nofollow', label : 'Nofollow', 'values': [{ text: 'No', value: '' }, { text: 'Yes', value: '1' }] },
{ type: 'textbox', name: 'id', label: 'ID', value: '' },
{ type: 'textbox', name: 'class', label: 'Class', value: '' },
{ type: 'textbox', name: 'margin_bottom', label: 'Margin button(E.g: 20px)', value: '' },
],
onsubmit: function ( e ) {
content = ed.selection.getContent();
var $shortcode = '[penci_button link="' + e.data.link + '" icon="' + e.data.icon + '" icon_position="' + e.data.icon_position + '"' +
( e.data.radius ? ' radius="' + e.data.radius + '"' : '' ) +
( e.data.align ? ' align="' + e.data.align + '"' : '' ) +
( e.data.full ? ' full="' + e.data.full + '"' : '' ) +
( e.data.size ? ' size="' + e.data.size + '"' : '' ) +
( e.data.text_color ? ' text_color="' + e.data.text_color + '"' : '' ) +
( e.data.background ? ' background="' + e.data.background + '"' : '' ) +
( e.data.text_hcolor ? ' text_hover_color="' + e.data.text_hcolor + '"' : '' ) +
( e.data.hbackground ? ' hover_bgcolor="' + e.data.hbackground + '"' : '' ) +
( e.data.target ? ' target="' + e.data.target + '"' : '' ) +
( e.data.nofollow ? ' nofollow="' + e.data.nofollow + '"' : '' ) +
( e.data.id ? ' id="' + e.data.id + '"' : '' ) +
( e.data.class ? ' class="' + e.data.class + '"' : '' ) +
( e.data.margin_bottom ? ' margin_bottom="' + e.data.margin_bottom + '"' : '' ) +
']' +
e.data.content + '[/penci_button]';
ed.execCommand('mceInsertContent', 0, $shortcode );
}
} );
}
},
/* --- Custom List --- */
{text: 'Custom list',classes: 'custom-list', menu: [
{
text: 'Check List',
value: 'penci-check-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-checklist' ), 200 );
}
},
{
text: 'Star List',
value: 'penci-star-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-starlist' ), 200 );
}
},
{
text: 'Edit List',
value: 'penci-edit-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-editlist' ), 200 );
}
},{
text: 'Thumbup List',
value: 'penci-thumbup-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-thumbuplist' ), 200 );
}
},{
text: 'Thumbdown List',
value: 'penci-thumbdown-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-thumbdownlist' ), 200 );
}
},{
text: 'Plus List',
value: 'penci-plus-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-pluslist' ), 200 );
}
},
{
text: 'Minus List',
value: 'penci-minus-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-minuslist' ), 200 );
}
},
{
text: 'Asterisk List',
value: 'penci-asterisk-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-asterisklist' ), 200 );
}
},
{
text: 'Folder List',
value: 'penci-folder-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-folderlist' ), 200 );
}
},
{
text: 'Heart List',
value: 'penci-heart-list',
onclick: function () {
ed.execCommand("InsertUnorderedList", false);
setTimeout( PenciInsertUnorderedList( ed , 'penci_list-heartlist' ), 200 );
}
}
]},
/* --- Blockquote --- */
{
text : 'Blockquote',
value : 'Blockquote',
onclick: function () {
ed.windowManager.open( {
title : 'Blockquote',
body : [
{ type : 'listbox', name : 'align', label : 'Quote Align', 'values': [{ text: 'None', value: 'none' }, { text: 'Aligh Left', value: 'left' }, { text: 'Aligh Right', value: 'right' }] },
{ type: 'textbox', name: 'author', label: 'Quote Author', value: '' },
],
onsubmit: function ( e ) {
content = ed.selection.getContent();
ed.insertContent( '[blockquote align="' + e.data.align + '" author="' + e.data.author + '"]' + content + '[/blockquote]' );
}
} );
}
},
/* ----------- Columns Simple ----------- */
{
text : 'Columns',
value : 'Columns',
onclick: function () {
ed.windowManager.open( {
title : 'Column',
body : [
{ type : 'listbox', name : 'size', label : 'Select type of column', 'values': [{ text: '1/2', value: '1/2' }, { text: '1/3', value: '1/3' }, { text: '2/3', value: '2/3' }, { text: '1/4', value: '1/4' }, { text: '3/4', value: '3/4' },] },
{ type: 'checkbox', name: 'last', label: 'Last column?', checked: false, }
],
onsubmit: function ( e ) {
content = ed.selection.getContent();
ed.insertContent( '[columns size="' + e.data.size + '" last="' + e.data.last + '"]' + content + '[/columns]' );
}
} );
}
},
/* ----------- Video Shortcode ----------- */
{
text : 'Video',
value : 'Video',
onclick: function () {
ed.windowManager.open( {
title : 'Video',
body : [
{ type : 'textbox', name : 'url', label: 'Video URL. E.g: https://www.youtube.com/watch?v=YQHsXMglC9A' },
{ type : 'listbox', name : 'align', label : 'Video Align?', 'values': [{ text: 'Center', value: 'center' }, { text: 'Left', value: 'left' }, { text: 'Right', value: 'right' },] },
{ type : 'textbox', name : 'width', label: 'Video Width, Unit is pixel. E.g: 450' },
],
onsubmit: function ( e ) {
content = ed.selection.getContent();
ed.insertContent( '[penci_video url="' + e.data.url + '" align="' + e.data.align + '" width="' + e.data.width + '" /]' );
}
} );
}
},
/* ----------- Inline Related Posts ----------- */
{
text : 'Inline Related Posts',
value : 'inline-related-posts',
onclick: function () {
ed.windowManager.open( {
title : 'Inline Related posts',
body : [
{ type : 'textbox', name : 'title', label: 'Heading Text', value: 'You Might Be Interested In' },
{ type: 'listbox', name: 'title_align', label: 'Heading Text Align', 'values': [{ text: 'Left', value: 'left' }, { text: 'Center', value: 'center' }, { text: 'Right', value: 'right' }], },
{ type : 'listbox', name : 'style', label : 'Select Style', 'values': [{ text: 'List', value: 'list' }, { text: 'Grid', value: 'grid' }] },
{ type : 'textbox', name : 'number', label: 'Numbers Post to Show?', value: '6' },
{ type: 'listbox', name: 'align', label: 'Select Float:', 'values': [{ text: 'None', value: 'none' }, { text: 'Left', value: 'left' }, { text: 'Right', value: 'right' }], },
{ type : 'textbox', name : 'ids', label: 'Display Related Posts With Post Ids. ( Enter Post IDs and separated by comas. Example: 12, 14 )', value: '' },
{ type: 'listbox', name: 'by', label: 'Display Related Posts By', 'values': [{ text: 'Same Categories', value: 'categories' }, { text: 'Same Tags', value: 'tags' }, { text: 'Primary Category from "Yoast SEO" or "Rank Math" plugin', value: 'primary_cat' }], },
{ type: 'listbox', name: 'orderby', label: 'Order By:', 'values': [{ text: 'Random', value: 'rand' }, { text: 'Post date', value: 'date' }, { text: 'Post title', value: 'title' }, { text: 'Post ID', value: 'ID' }, { text: 'Modified Date', value: 'modified' }, { text: 'Comment Count', value: 'comment_count' }, { text: 'Most Viewed Posts All Time', value: 'popular' }, { text: 'Most Viewed Posts Once Weekly', value: 'popular7' }, { text: 'Most Viewed Posts Once a Month', value: 'popular_month' }], },
{ type: 'listbox', name: 'order', label: 'Order:', 'values': [{ text: 'Descending Order', value: 'DESC' }, { text: 'Ascending Order', value: 'ASC' }], },
{ type : 'listbox', name : 'hide_thumb', label : 'Hide Thumbnail on Grid Style?', 'values': [{ text: 'No', value: 'no' }, { text: 'Yes', value: 'yes' }] },
{ type : 'listbox', name : 'thumb_right', label : 'Show thumbnail on the right side?', 'values': [{ text: 'No', value: 'no' }, { text: 'Yes', value: 'yes' }] },
{ type : 'listbox', name : 'views', label : 'Show Post Views on Grid Style?', 'values': [{ text: 'No', value: 'no' }, { text: 'Yes', value: 'yes' }] },
{ type : 'listbox', name : 'date', label : 'Show Post Date on Grid Style?', 'values': [{ text: 'Yes', value: 'yes' }, { text: 'No', value: 'no' } ] },
{ type : 'listbox', name : 'grid_columns', label : 'Custom Columns for Grid Style', 'values': [{ text: '2 Columns', value: '2' }, { text: '1 Column', value: '1' }, { text: '3 Columns', value: '3' } ] },
{ type : 'textbox', name : 'post_type', label: 'Related Posts for a Custom Post Type( not Posts )?', value: '' },
{ type : 'textbox', name : 'tax', label: 'Related Posts for a Custom Taxonomy?', value: '' },
],
onsubmit: function ( e ) {
content = ed.selection.getContent();
ed.insertContent( '[inline_related_posts title="' + e.data.title + '" title_align="' + e.data.title_align + '" style="' + e.data.style + '" number="' + e.data.number + '" align="' + e.data.align + '" ids="' + e.data.ids + '" by="' + e.data.by + '" orderby="' + e.data.orderby + '" order="' + e.data.order + '" hide_thumb="' + e.data.hide_thumb + '" thumb_right="' + e.data.thumb_right + '" views="' + e.data.views + '" date="' + e.data.date + '" grid_columns="' + e.data.grid_columns + '" post_type="' + e.data.post_type + '" tax="' + e.data.tax + '"]' );
}
} );
}
},
/* --- Portfolio--- */
{
text : 'Portfolio',
value : 'Portfolio',
onclick: function () {
ed.windowManager.open( {
title : 'Portfolio',
body : [
{ type : 'listbox', name : 'style', label : 'Portfolio Style', 'values': [{ text: 'Masonry', value: 'masonry' }, { text: 'Grid', value: 'grid' }] },
{ type : 'textbox', name : 'cat', label: 'Portfolio Categories Slug To Display. E.g: cat-1, cat-2' },
{ type : 'textbox', name : 'number', label: 'Numbers Post to Show? If you want display all, fill -1', value: '15' },
{ type : 'listbox', name : 'pagination', label : 'Page Navigation Type', 'values': [{ text: 'Numberic Navigation', value: 'number' }, { text: 'Load More Button', value: 'load_more' }, { text: 'Infinite Load More', value: 'infinite' }] },
{ type : 'textbox', name : 'numbermore', label: 'How Much Posts to Show Each Time Load More?', value: '6' },
{ type : 'listbox', name : 'image_type', label : 'Images Type ( Apply for Grid Style only )', 'values': [{ text: 'Landscape', value: 'landscape' }, { text: 'Square', value: 'square' }, { text: 'Vertical', value: 'vertical' }] },
{ type : 'listbox', name : 'filter', label : 'Display Filter?', 'values': [{ text: 'Yes', value: 'true' }, { text: 'No', value: 'false' }] },
{ type : 'listbox', name : 'column', label : 'Number Columns?', 'values': [{ text: '3 Columns', value: '3' }, { text: '2 Columns', value: '2' }] },
{ type: 'textbox', name: 'all_text', label: 'All text', value: 'All' },
{ type : 'listbox', name : 'lightbox', label : 'Enable Click on Thumbnails to Open Lightbox?', 'values': [{ text: 'No', value: 'fasle' }, { text: 'Yes', value: 'true' }] },
],
onsubmit: function ( e ) {
ed.insertContent( '[portfolio style="' + e.data.style + '" cat="' + e.data.cat + '" number="' + e.data.number + '" pagination="' + e.data.pagination + '" numbermore="' + e.data.numbermore + '" image_type="' + e.data.image_type + '" filter="' + e.data.filter + '" column="' + e.data.column + '" all_text="' + e.data.all_text + '" lightbox="' + e.data.lightbox + '" /]' );
}
} );
}
},
/* --- Penci Recipe --- */
{
text : 'Penci Recipe',
value : 'Penci Recipe',
onclick: function () {
ed.insertContent( '[penci_recipe]' );
}
},
/* --- Penci Recipe Index--- */
{
text : 'Penci Recipe Index',
value : 'Penci Recipe Index',
onclick: function () {
ed.windowManager.open( {
title : 'Penci Recipe Index',
body : [
{ type : 'textbox', name : 'title', label: 'Recipe Index Title', value: 'My Recipe Index' },
{ type : 'textbox', name : 'cat', label: 'Recipe Index Category Slug' },
{ type : 'textbox', name : 'numbers_posts', label: 'Numbers Posts to Show?', value: '3' },
{ type : 'listbox', name : 'columns', label : 'Select Columns', 'values': [{ text: '3 Columns', value: '3' }, { text: '2 Columns', value: '2' }, { text: '4 Columns', value: '4' }] },
{ type : 'listbox', name : 'display_title', label : 'Display Posts Title?', 'values': [{ text: 'Yes', value: 'yes' }, { text: 'No', value: 'no' }] },
{ type : 'listbox', name : 'display_cat', label : 'Display Posts Categories?', 'values': [{ text: 'No', value: 'no' }, { text: 'Yes', value: 'yes' }] },
{ type : 'listbox', name : 'display_date', label : 'Display Posts Date?', 'values': [{ text: 'Yes', value: 'yes' }, { text: 'No', value: 'no' }] },
{ type : 'listbox', name : 'display_image', label : 'Display Posts Featured Image?', 'values': [{ text: 'Yes', value: 'yes' }, { text: 'No', value: 'no' }] },
{ type : 'listbox', name : 'image_size', label : 'Images Size for Featured Image', 'values': [{ text: 'Square', value: 'square' }, { text: 'Vertical', value: 'vertical' }, { text: 'Horizontal', value: 'horizontal' }] },
{ type : 'listbox', name : 'cat_link', label : 'Display View All Posts ( Category Link )?', 'values': [{ text: 'Yes', value: 'yes' }, { text: 'No', value: 'no' }] },
{ type : 'textbox', name : 'cat_link_text', label: 'Custom "View All" button text', value: 'View All' }
],
onsubmit: function ( e ) {
ed.insertContent( '[penci_index title="' + e.data.title + '" cat="' + e.data.cat + '" numbers_posts="' + e.data.numbers_posts + '" columns="' + e.data.columns + '" display_title="' + e.data.display_title + '" display_cat="' + e.data.display_cat + '" display_date="' + e.data.display_date + '" display_image="' + e.data.display_image + '" image_size="' + e.data.image_size + '" cat_link="' + e.data.cat_link + '" cat_link_text="' + e.data.cat_link_text + '" /]' );
}
} );
}
},
]
} );
},
createControl: function ( n, cm ) {
return null;
}
} );
tinymce.PluginManager.add( 'penci_pre_shortcodes_button', tinymce.plugins.penci_pre_shortcodes_button );
function PenciInsertUnorderedList( ed, styleList ){
var $checkList = jQuery( ed.dom.getParent( ed.selection.getNode(), 'ul' ) );
if ( $checkList.hasClass( 'penci_list_shortcode' ) && $checkList.hasClass( styleList ) ) {
$checkList.removeClass( styleList ).removeClass( 'penci_list_shortcode' );
} else if ( $checkList.hasClass( 'penci_list_shortcode' ) && ! $checkList.hasClass( styleList ) ) {
$checkList[0].className = $checkList[0].className.replace( /penci_list\-.*/ig, '' ).trim();
$checkList.addClass( 'penci_list_shortcode' );
} else {
$checkList.addClass( 'penci_list_shortcode' ).addClass( styleList );
}
}
})(); mce/mce.php 0000644 00000002561 14760034774 0006605 0 ustar 00
A class for use in shortening URL links.
*/
class Absolute_to_Relative_URLs
{
//protected static $_instance;
protected $custom_ports;
protected $site_port_is_default;
protected $site_url;
/*
$custom_site_url :: should be a valid URL, with scheme and host
$custom_ports :: e.g., array('ssh'=>22)
*/
public function __construct($custom_site_url='', $custom_ports=array())
{
$this->custom_ports = $custom_ports;
$this->get_site_url($custom_site_url);
if ($this->site_url === false)
{
trigger_error('Invalid site URL');
}
}
/*
Initialize class for absolute_to_relative_url()
Nice idea, BUT slower than using a global variable
*/
/*public static function instance()
{
if (self::$_instance === null)
{
self::$_instance = new Absolute_to_Relative_URLs();
}
return self::$_instance;
}*/
protected function build_url($url, $output_type)
{
$has_fragment = isset($url['fragment']);
$has_resource = isset($url['resource']);
$has_query = isset($url['query']);
if ( isset($url['scheme']) )
{
$first_half = $url['scheme'] . ':';
}
else
{
$first_half = '';
}
if ( isset($url['host']) )
{
$first_half .= '//';
$user_or_pass = false;
if ( isset($url['user']) )
{
$first_half .= $url['user'];
$user_or_pass = true;
}
if ( isset($url['pass']) )
{
$first_half .= ':' . $url['pass'];
$user_or_pass = true;
}
if ($user_or_pass)
{
$first_half .= '@';
}
$first_half .= $url['host'];
if ( isset($url['port']) )
{
$first_half .= ':' . $url['port'];
}
$second_half = $url['path'];
}
else
{
if ($output_type===1 || $output_type===2)
{
$absolute_path = $url['path'];
$relative_path = (isset($url['path_relative'])) ? $url['path_relative'] : false;
if ($relative_path !== false)
{
if ($output_type === 2)
{
$second_half = (strlen($relative_path) <= strlen($absolute_path)) ? $relative_path : $absolute_path;
}
else
{
$second_half = $relative_path;
}
}
else
{
$second_half = $absolute_path;
}
}
else if ($output_type === 0)
{
$second_half = $url['path'];
}
if ($has_resource || $has_query || $has_fragment)
{
if ($second_half==='./' || ($second_half==='/' && $this->site_url['path']==='/'))
{
$second_half = '';
}
}
}
if ($has_resource)
{
$second_half .= $url['resource'];
}
if ($has_query)
{
$second_half .= '?'. $url['query'];
}
if ($has_fragment)
{
$second_half .= '#'. $url['fragment'];
}
return $first_half . $second_half;
}
protected function get_default_port($scheme)
{
switch ($scheme)
{
case 'http' : return 80;
case 'https' : return 443;
case 'ftp' : return 21;
}
foreach ($this->custom_ports as $port_name => $port)
{
$port_name = strtolower($port_name);
if ($port_name === $scheme)
{
return $port;
}
}
// No listed match found
return -1;
}
protected function get_site_url($custom_site_url)
{
if ($custom_site_url === '')
{
$this->custom_site_url = true;
$url = ( !isset($_SERVER['HTTPS']) ) ? 'http://' : 'https://';
if ( isset($_SERVER['PHP_AUTH_USER']) )
{
$url .= $_SERVER['PHP_AUTH_USER'] .':'. $_SERVER['PHP_AUTH_PW'] .'@';
}
$url .= $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
}
else
{
$url = $custom_site_url;
}
$url = $this->parse_url($url, true);
if ($url !== false)
{
if ( isset($url['port']) )
{
$this->site_port_is_default = ($url['port'] === $this->get_default_port($url['scheme']));
}
else
{
$url['port'] = $this->get_default_port($url['scheme']);
$this->site_port_is_default = true;
}
$url['host_stripped'] = $this->remove_www($url['host']);
}
$this->site_url = $url;
}
/*
Return an path string.
*/
protected function implode_path($path, $absolute_output)
{
if (!empty($path))
{
$path = implode('/', $path) .'/';
if ($absolute_output)
{
$path = '/'.$path;
}
}
else
{
$path = (!$absolute_output) ? './' : '/';
}
return $path;
}
/*
Return an absolute path.
*/
protected function parse_path($path)
{
if ($path !== '/')
{
$path = explode('/', $path);
$absolute_path = array();
$first_dir = $path[0];
// Check if not absolute: '/dir/' becomes array('','dir','')
if ($first_dir !== '')
{
if ($first_dir==='.' || $first_dir==='..')
{
$path = array_merge($this->site_url['path_array'], $path);
}
}
foreach ($path as $dir)
{
if ($dir !== '')
{
if ($dir !== '..')
{
if ($dir !== '.')
{
array_push($absolute_path, $dir);
}
}
else
{
$parent_index = count($absolute_path) - 1;
if ($parent_index >= 0)
{
unset( $absolute_path[$parent_index] );
$absolute_path = array_values($absolute_path);
}
}
}
}
return $absolute_path;
}
else
{
// Faster to skip the above block and just create an array
return array();
}
}
/*
Return the components of a URL.
*/
protected function parse_url($url, $init=false)
{
if ( stripos($url,'data:')===0 || stripos($url,'javascript:')===0 )
{
// Nothing can be done with data/javascript URIs
return false;
}
else if (strpos($url, '//') === 0)
{
// Cannot parse scheme-relative URLs with parse_url
$url = $this->site_url['scheme'] . ':' . $url;
}
// With PHP versions earlier than 5.3.3, an E_WARNING is emitted when URL parsing fails
// REMOVE when WordPress enforces a higher version as it will increase performance
$url = @parse_url($url);
if ($url !== false)
{
if ($init)
{
// Checks for host to catch "host:80"
if ( !isset($url['scheme']) || !isset($url['host']) )
{
// Invalid site url
return false;
}
}
if ( isset($url['path']) )
{
$path = str_replace(' ', '%20', $url['path']);
$last_slash = strrpos($path, '/');
if ($last_slash !== false)
{
$last_slash++;
if ($last_slash < strlen($path))
{
// Isolate resource from path
$url['resource'] = substr($path, $last_slash);
$path = substr($path, 0, $last_slash);
}
$url['path_array'] = $this->parse_path($path);
$url['path'] = $this->implode_path($url['path_array'], true);
}
else
{
// No slashes found
$url['resource'] = $path;
$url['path'] = $this->site_url['path'];
$url['path_array'] = $this->site_url['path_array'];
}
}
else if ( isset($url['host']) )
{
$url['path'] = '/';
$url['path_array'] = array();
}
else
{
$url['path'] = $this->site_url['path'];
$url['path_array'] = $this->site_url['path_array'];
}
}
return $url;
}
/*
Return a path relative to the site path.
*/
protected function relate_path($absolute_path)
{
$relative_path = array();
$site_path = $this->site_url['path_array'];
// At this point, it's related to the host
$related = true;
$parent_index = -1;
// Find parents
foreach ($site_path as $i => $dir)
{
if ($related)
{
$absolute_dir = (isset($absolute_path[$i])) ? $absolute_path[$i] : null;
if ($dir !== $absolute_dir)
{
$related = false;
}
else
{
$parent_index = $i;
}
}
if (!$related)
{
// Up one level
array_push($relative_path, '..');
}
}
// Form path
foreach ($absolute_path as $i => $dir)
{
if ($i > $parent_index)
{
array_push($relative_path, $dir);
}
}
return $relative_path;
}
/*
Return a URL relative to the site URL.
$ignore_www :: optionally, ignore "www" subdomain
$output_type :: optionally, return either a:
0: root-relative URL (/child-of-root/etc/)
1: path-relative URL (../child-of-parent/etc/)
2: shortest possible URL (root- or path-relative)
*/
public function relate_url($url, $ignore_www=false, $output_type=2)
{
if ($this->site_url !== false)
{
if ($url==='' || $url==='.' || $url==='./')
{
if ($this->site_url['path'] !== '/')
{
if ($output_type===1 || $output_type===2)
{
return './';
}
else if ($output_type === 0)
{
return $this->site_url['path'];
}
}
else
{
return '/';
}
}
else if ($url === '/')
{
return '/';
}
else if ($url === '#')
{
return '#';
}
$original_url = $url;
$url = $this->parse_url($url);
if ($url === false)
{
// Unusable format
return $original_url;
}
}
else
{
// Invalid site url
return $url;
}
$related = false;
if ( isset($url['scheme']) )
{
$scheme = $url['scheme'];
if ($scheme === $this->site_url['scheme'])
{
unset($url['scheme']);
if ($ignore_www)
{
$url['host'] = $this->remove_www($url['host']);
$site_host = $this->site_url['host_stripped'];
}
else
{
$site_host = $this->site_url['host'];
}
if ($url['host'] === $site_host)
{
$related = true;
if ( isset($url['port']) )
{
if ($url['port'] === $this->site_url['port'])
{
unset($url['port']);
}
else
{
$related = false;
}
}
else if (!$this->site_port_is_default)
{
$related = false;
}
if ( isset($url['user']) )
{
if (!isset($this->site_url['user']) || $url['user'] !== $this->site_url['user'])
{
$related = false;
}
}
/*else if ( isset($this->site_url['user']) )
{
$related = false;
}*/
if ( isset($url['pass']) )
{
if (!isset($this->site_url['pass']) || $url['pass'] !== $this->site_url['pass'])
{
$related = false;
}
}
/*else if ( isset($this->site_url['pass']) )
{
$related = false;
}*/
if ($related)
{
unset($url['host'], $url['user'], $url['pass']);
}
}
else if ( isset($url['port']) )
{
if ($url['port'] === $this->get_default_port($scheme))
{
unset($url['port']);
}
}
}
else if ( isset($url['port']) )
{
if ($url['port'] === $this->get_default_port($scheme))
{
unset($url['port']);
}
}
}
if ( !isset($url['host']) )
{
$url['path_relative_array'] = $this->relate_path($url['path_array']);
$url['path_relative'] = $this->implode_path($url['path_relative_array'], false);
}
return $this->build_url($url, $output_type);
}
protected function remove_www($host)
{
if (strpos($host, 'www.') === 0)
{
$host = substr($host, 4);
}
return $host;
}
}
/*function absolute_to_relative_url($url, $ignore_www=true, $choose_shortest=true)
{
return Absolute_to_Relative_URLs::instance()->relate_url($url, $ignore_www, $choose_shortest);
}*/
/*
Return a URL relative to the current site URL.
$ignore_www :: optionally, ignore "www" subdomain
$output_type :: optionally, return either a:
0: root-relative URL (/child-of-root/etc/)
1: path-relative URL (../child-of-parent/etc/)
2: shortest possible URL (root- or path-relative)
*/
function absolute_to_relative_url($url, $ignore_www=false, $output_type=2)
{
global $absolute_to_relative_url_instance;
if (!isset($absolute_to_relative_url_instance))
{
$absolute_to_relative_url_instance = new Absolute_to_Relative_URLs();
}
return $absolute_to_relative_url_instance->relate_url($url, $ignore_www, $output_type);
}
?> optimize/libs/html-minify.php 0000644 00000012772 14760034774 0012330 0 ustar 00
Reduce file size by shortening URLs and safely removing all standard comments and unnecessary white space from an HTML document.
*/
class HTML_Minify
{
// Settings
protected $compress_css;
protected $compress_js;
protected $info_comment;
protected $remove_comments;
protected $shorten_urls;
// Variables
protected $html = '';
public function __construct($html, $compress_css=true, $compress_js=false, $info_comment=false, $remove_comments=true, $shorten_urls=false)
{
if ($html !== '')
{
$this->compress_css = $compress_css;
$this->compress_js = $compress_js;
$this->info_comment = $info_comment;
$this->remove_comments = $remove_comments;
$this->shorten_urls = $shorten_urls;
$this->html = $this->minifyHTML($html);
if ($this->info_comment)
{
$this->html .= "\n" . $this->bottomComment($html, $this->html);
}
}
}
public function __toString()
{
return $this->html;
}
protected function bottomComment($raw, $compressed)
{
$raw = strlen($raw);
$compressed = strlen($compressed);
$savings = ($raw-$compressed) / $raw * 100;
$savings = round($savings, 2);
return '';
}
protected function callback_HTML_URLs($matches)
{
// [2] is an attribute value that is encapsulated with "" and [3] with ''
$url = (!isset($matches[3])) ? $matches[2] : $matches[3];
return $matches[1].'="'.absolute_to_relative_url($url).'"';
}
protected function minifyHTML($html)
{
$pattern = '/<(?', $js_content );
}
if ( ! $js ) {
return '';
}
return $js;
}
public function should_add_delay_css() {
$render = false;
if ( get_theme_mod( 'penci_speed_optimize_css' ) || get_theme_mod( 'penci_speed_remove_css' ) ) {
$render = true;
}
return $render;
}
protected function render_preloads() {
$preloads = [];
foreach ( $this->preload as $preload ) {
$media = 'all';
$type = ( $preload instanceof Stylesheet ) ? 'style' : 'script';
if ( $type === 'style' ) {
$media = $preload->media ?: $media;
}
$preloads[] = sprintf( '', esc_url( $preload->get_content_url() ), $type, esc_attr( $media ) );
}
return implode( '', $preloads );
}
/**
* Check for conditionals for delay load.
*
* @return boolean
*/
public function should_delay_css() {
$valid = Plugin::process()->check_enabled( [ 'all' ] );
return apply_filters( 'soledad_pagespeed/should_delay_css', $valid );
}
}
pagespeed/inc/integrations/elementor.php 0000644 00000005206 14760034774 0014502 0 ustar 00 register_hooks();
}
public function register_hooks()
{
add_action('wp', [$this, 'setup']);
}
public function setup()
{
$this->setup_remove_css();
}
/**
* Special rules related to remove css when Elementor is active.
*
* @return void
*/
public function setup_remove_css()
{
// Add all Elementor frontend files for CSS processing.
add_filter('soledad_pagespeed/remove_css_includes', function($include) {
$include[] = 'id:elementor-frontend-css';
$include[] = 'id:elementor-pro-css';
// $include[] = 'elementor/*font-awesome';
return $include;
});
add_filter('soledad_pagespeed/remove_css_excludes', function($exclude, \Soledad\PageSpeed\RemoveCss $remove_css) {
// Don't bother with animations CSS file as it won't remove much.
if (!empty($remove_css->used_markup['classes']['elementor-invisible'])) {
$exclude[] = 'id:elementor-animations';
}
$exclude[] = 'id:elementor-icons';
$exclude[] = 'id:elementor-icons-*';
return $exclude;
}, 10, 2);
/**
* Elementor selectors extras.
*/
$this->allow_selectors = [
[
'type' => 'any',
'sheet' => 'id:elementor-',
'search' => [
'*.e--ua-*',
'.elementor-loading',
'.elementor-invisible',
//'.elementor-background-video-embed',
]
],
[
'type' => 'class',
'class' => 'elementor-invisible',
'sheet' => 'id:elementor-',
'search' => [
'.animated'
]
],
[
'type' => 'class',
'class' => 'elementor-invisible',
'sheet' => 'id:elementor-',
'search' => [
'.animated'
]
],
];
if (defined('ELEMENTOR_PRO_VERSION')) {
$this->allow_selectors = array_merge($this->allow_selectors, [
[
'type' => 'class',
'class' => 'elementor-posts-container',
// 'sheet' => 'id:elementor-',
'search' => [
'.elementor-posts-container',
'.elementor-has-item-ratio'
]
],
]);
}
add_filter('soledad_pagespeed/allow_css_selectors', function($allow, \Soledad\PageSpeed\RemoveCss $remove_css) {
$html = $remove_css->html;
if (strpos($html, 'background_slideshow_gallery') !== false) {
array_push($this->allow_selectors, ...[
[
'type' => 'any',
'sheet' => 'id:elementor-',
'search' => [
'*.swiper-*',
'*.elementor-background-slideshow*',
'.elementor-ken-burns*',
]
],
]);
}
return array_merge($allow, $this->allow_selectors);
}, 10, 2);
}
}
pagespeed/inc/integrations/wpbakery.php 0000644 00000006513 14760034774 0014336 0 ustar 00 register_hooks();
}
public function register_hooks()
{
add_action('wp', [$this, 'setup']);
}
public function setup()
{
$this->setup_remove_css();
}
/**
* Special rules related to remove css when WPBakery is active.
*
* @return void
*/
public function setup_remove_css()
{
// Add all WPBakery frontend files for CSS processing.
add_filter('soledad_pagespeed/remove_css_includes', function($include) {
// Only need to remove unused on this. All other CSS files are modular and included only if needed.
$include[] = 'id:js_composer_front';
// FontAwesome can also go through this.
$include[] = 'js_composer/*font-awesome';
return $include;
});
add_filter('soledad_pagespeed/remove_css_excludes', function($exclude, \Soledad\PageSpeed\RemoveCss $remove_css) {
// // Don't bother with animations CSS file as it won't remove much.
// $exclude[] = 'id:vc_animate-css';
// // Pageable owl carousel.
// $exclude[] = 'id:vc_pageable_owl-carousel';
// // prettyPhoto would need all the CSS.
// $exclude[] = 'js_composer/*prettyphoto';
// // owlcarousel is added only if needed.
// $exclude[] = 'js_composer/*owl';
return $exclude;
}, 10, 2);
/**
* WPbakery selectors extras.
*/
$this->allow_selectors = [
[
'type' => 'any',
'search' => [
'.vc_mobile',
'.vc_desktop',
]
],
[
'type' => 'class',
'class' => 'vc_parallax',
'sheet' => 'id:js_composer_front',
'search' => [
'*.vc_parallax*',
'.vc_hidden'
]
],
[
'type' => 'class',
'class' => 'vc_pie_chart',
'sheet' => 'id:js_composer_front',
'search' => [
'.vc_ready'
]
],
[
'type' => 'class',
'class' => 'wpb_gmaps_widget',
'sheet' => 'id:js_composer_front',
'search' => [
'.map_ready',
]
],
[
'type' => 'class',
'class' => 'wpb_animate_when_almost_visible',
'sheet' => 'id:js_composer_front',
'search' => [
'.wpb_start_animation',
'.animated'
]
],
[
'type' => 'class',
'class' => 'vc_toggle',
'sheet' => 'id:js_composer_front',
'search' => [
'.vc_toggle_active',
]
],
[
'type' => 'class',
'class' => 'vc_grid',
'sheet' => 'id:js_composer_front',
'search' => [
'.vc_grid-loading',
'.vc_visible-item',
'.vc_is-hover',
// -complete and -failed etc. too
'*.vc-spinner*',
'*.vc-grid*.vc_active*',
// Other dependencies maybe: prettyPhoto, owlcarousel for pagination of specific type.
]
],
];
add_filter('soledad_pagespeed/allow_css_selectors', function($allow, \Soledad\PageSpeed\RemoveCss $remove_css) {
// $html = $remove_css->html;
// if (strpos($html, 'background_slideshow_gallery') !== false) {
// array_push($this->allow_selectors, ...[
// [
// 'type' => 'any',
// 'sheet' => 'id:elementor-',
// 'search' => [
// '*.swiper-*',
// '*.elementor-background-slideshow*',
// '.elementor-ken-burns*',
// ]
// ],
// ]);
// }
return array_merge($allow, $this->allow_selectors);
}, 10, 2);
}
}
pagespeed/inc/optimize-css/google-fonts.php 0000644 00000005463 14760034774 0015040 0 ustar 00 active = true;
}
/**
* Late processing and injecting data in HTML DOM.
*
* @param string $html
*
* @return string
*/
public function render_in_dom( $html ) {
if ( $this->renders ) {
$html = str_replace( '', '' . implode( '', $this->renders ), $html );
}
$html = $this->add_hints( $html );
return $html;
}
/**
* Add resource hints for preconnect and so on.
*
* @param string $html Full DOM HTML.
*
* @return void
*/
public function add_hints( $html ) {
if ( ! $this->active || ! get_theme_mod( 'penci_speed_optimize_gfonts' ) ) {
return $html;
}
// preconnect with dns-prefetch fallback for Firefox. Due to safari bug, can't be in same rel.
$hint = '';
$hint .= '';
// Only needed if not inlined.
if ( ! get_theme_mod( 'penci_speed_optimize_css' ) || ! get_theme_mod( 'penci_speed_optimize_gfonts_inline' ) ) {
$hint .= '';
$hint .= '';
}
// Add in to head.
$html = str_replace( '', '' . $hint, $html );
// No longer needed as preconnect's better and we always want to use https.
$html = str_replace( "", '', $html );
return $html;
}
public function optimize( Stylesheet $sheet ) {
if ( ! $sheet->is_google_fonts() ) {
return;
}
if ( get_theme_mod('penci_speed_optimize_gfonts_delay')) {
$sheet->delay_type = 'preload';
return;
}
// Set normal render if optimizations are disabled.
if ( ! get_theme_mod( 'penci_speed_optimize_gfonts' ) ) {
$sheet->render_type = 'normal';
return;
}
$sheet->delay_type = 'preload';
if ( strpos( $sheet->url, 'display=' ) === false ) {
$sheet->url .= '&display=swap';
}
}
public function do_render( Stylesheet $sheet ) {
return $sheet->render();
// $orig_media = $sheet->media;
// // If no display= or if display=auto.
// if (!preg_match('/display=(?!auto)/', $sheet->url)) {
// $sheet->media = 'x';
// }
// // Add the JS to render with display: swap on mobile.
// $extra = "";
// $this->renders[] = $sheet->render() . $extra;
// // Return empty space to strip out the tag.
// return ' ';
}
}
pagespeed/inc/optimize-css/optimize-css.php 0000644 00000016336 14760034774 0015064 0 ustar 00 dom = $dom;
$this->html = $raw_html;
}
public function process() {
$this->find_stylesheets();
// Setup optimization excludes.
$default_excludes = [
'id:woocommerce-smallscreen',
'id:penci-dark-style',
];
$excludes = array_merge( $default_excludes, Util\option_to_array( get_theme_mod( 'penci_speed_optimize_css_excludes' ) ) );
$this->exclude_sheets = apply_filters( 'soledad_pagespeed/optimize_css_excludes', $excludes );
// Remove CSS first, if any.
if ( $this->should_remove_css() ) {
$remove_css = new RemoveCss( $this->stylesheets, $this->dom, $this->html );
$this->html = $remove_css->process();
}
/**
* Process and replace stylesheets with CSS cleaned.
*/
$has_gfonts = false;
$replacements = [];
/** @var Stylesheet $sheet */
foreach ( $this->stylesheets as $sheet ) {
$replacement = '';
// Optimizations such as min and inline.
$this->optimize( $sheet );
if ( get_theme_mod( 'penci_speed_optimize_gfonts' ) && $sheet->is_google_fonts() ) {
$replacement = Plugin::google_fonts()->do_render( $sheet );
$has_gfonts = true;
}
if ( ! $replacement ) {
// Get the rendered stylesheet to replace original with.
$replacement = $sheet->render();
}
if ( $replacement ) {
// Util\debug_log('Replacing: ' . print_r($sheet, true));
$replacements[ $sheet->orig_url ] = $replacement;
if ( $sheet->has_delay() ) {
// onload and preload type doesn't need prefetch; both use a non-JS method.
if ( ! in_array( $sheet->delay_type, [ 'onload', 'preload' ] ) ) {
Plugin::delay_load()->add_preload( $sheet );
}
}
}
// Found Google Fonts.
if ( $sheet->is_google_fonts() ) {
$has_gfonts = true;
}
// Free up memory.
$sheet->content = null;
$sheet->parsed_data = null;
}
/**
* Make stylesheet replacements, if any. Slightly more efficient in one go.
*/
if ( $replacements ) {
$urls = array_map( 'preg_quote', array_keys( $replacements ) );
// Using callback to prevent issues with backreferences such as $1 or \0 in replacement string.
$this->html = preg_replace_callback( '#]*href=(?:"|\'|)(' . implode( '|', $urls ) . ')(?:"|\'|\s)[^>]*>#Usi', function ( $match ) use ( $replacements ) {
if ( ! empty( $replacements[ $match[1] ] ) ) {
return $replacements[ $match[1] ];
}
return $match[0];
}, $this->html );
}
if ( $has_gfonts ) {
Plugin::google_fonts()->enable();
$this->html = Plugin::google_fonts()->render_in_dom( $this->html );
}
return $this->html;
}
/**
* Find all the stylesheet links.
*
* @return Stylesheet[]
*/
public function find_stylesheets() {
$stylesheets = [];
$nodes = $this->dom->xpath->query( "//link[@rel='stylesheet']" );
foreach ( $nodes as $node ) {
/** @var \DOMElement $node */
$elements[] = $node;
if ( ! $node->hasAttributes() ) {
continue;
}
$url = $node->getAttribute( 'href' );
if ( ! $url ) {
continue;
}
$sheet = new Stylesheet( $node->getAttribute( 'id' ), $url );
$sheet->media = $node->getAttribute( 'media' );
$stylesheets[] = $sheet;
}
$this->stylesheets = $stylesheets;
return $this->stylesheets;
}
/**
* Should unused CSS be removed.
*
* @return boolean
*/
public function should_remove_css() {
// check post type + page remove here
$valid = get_theme_mod( 'penci_speed_remove_css' );
return apply_filters( 'soledad_pagespeed/should_remove_css', $valid );
}
/**
* Apply CSS optimizes such as minify and inline, if enabled.
*
* @param Stylesheet $sheet
*
* @return void
*/
public function optimize( Stylesheet $sheet ) {
if ( ! $this->should_optimize( $sheet ) ) {
return;
}
// We're going to use onload delay method (non-JS) fixing render blocking.
$sheet->delay_type = 'delay';
$sheet->set_render( 'delay' );
// Should the sheet be minified.
$minify = get_theme_mod( 'penci_speed_optimize_css_minify' );
$delay_gfonts = get_theme_mod( 'penci_speed_optimize_gfonts_delay' );
// For inline CSS, minification is enforced.
if ( get_theme_mod( 'penci_speed_optimize_css_to_inline' ) ) {
$minify = true;
}
// Will optimize if it's a google font. Note: Has to be done before minify.
Plugin::google_fonts()->optimize( $sheet );
// Google Fonts inline also relies on minification.
if ( $sheet->is_google_fonts() && get_theme_mod( 'penci_speed_optimize_gfonts_inline' ) && ! $delay_gfonts ) {
$minify = true;
}
$disable_icon_delay = get_theme_mod( 'penci_speed_optimize_disable_icon_delay' );
// Theme Icon Font
if ( $this->delay_all_fonts( $sheet ) && ! $disable_icon_delay ) {
$sheet->set_render( 'delay' );
$minify = false;
} elseif ( ( $this->delay_all_fonts( $sheet ) && $disable_icon_delay ) || $this->is_darkfile( $sheet ) ) {
$sheet->set_render( 'inline' );
$minify = true;
}
if ( $minify ) {
$minifier = new Minifier( $sheet );
$minifier->process();
}
if ( get_theme_mod( 'penci_speed_optimize_css_to_inline' ) && ! $this->delay_all_fonts( $sheet ) && ! $delay_gfonts ) {
if ( ! $sheet->content ) {
$file = Plugin::file_system()->url_to_local( $sheet->get_content_url() );
if ( $file ) {
$sheet->content = Plugin::file_system()->get_contents( $file );
}
}
// If we have content by now.
if ( $sheet->content ) {
$sheet->set_render( 'delay' );
}
}
}
/**
* Determine if stylesheet should be optimized, based on exclusion and inclusion
* rules and settings.
*
* @param Stylesheet $sheet
*
* @return boolean
*/
public function should_optimize( Stylesheet $sheet ) {
// Only go ahead if optimizations are enabled and remove css hasn't happened.
if ( ! get_theme_mod( 'penci_speed_optimize_css' ) || $sheet->render_type === 'remove_css' ) {
return false;
}
// Debugging scripts. Can't be minified, so can't be inline either.
if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
return;
}
// Handle manual excludes first.
if ( $this->exclude_sheets ) {
foreach ( $this->exclude_sheets as $exclude ) {
if ( Util\asset_match( $exclude, $sheet ) ) {
return false;
}
}
}
return true;
}
public function is_darkfile(Stylesheet $sheet){
return Util\asset_match( 'id:penci-dark-style', $sheet );
}
public function delay_all_fonts( Stylesheet $sheet ) {
// Only go ahead if optimizations are enabled and remove css hasn't happened.
if ( ! get_theme_mod( 'penci_speed_optimize_css' ) || $sheet->render_type === 'remove_css' ) {
return false;
}
$fonts = [
'id:penci_icon',
'id:penci-font-iweather',
'id:penci-font-awesome',
'id:penci-font-awesomeold',
'id:elementor-icons-*',
];
foreach ( $fonts as $font ) {
if ( Util\asset_match( $font, $sheet ) ) {
return true;
}
}
return false;
}
}
pagespeed/inc/optimize-css/stylesheet.php 0000644 00000010571 14760034774 0014622 0 ustar 00 id = $id;
$this->url = $url;
$this->orig_url = $url;
if ( ! $this->id ) {
$this->id = md5( $this->url );
}
$this->is_google_fonts = stripos( $this->url, 'fonts.googleapis.com/css' ) !== false;
}
public function render() {
if ( $this->render_string ) {
return $this->render_string;
}
$attrs = [
'rel' => 'stylesheet',
'id' => $this->id,
];
if ( $this->media ) {
$attrs['media'] = $this->media;
}
/**
* 1. Render type 'delay' at onload or delay via JS for later.
*/
if ( $this->render_type === 'delay' ) {
if ( $this->media === 'print' ) {
return '';
}
$attrs += [
'data-soledad_pagespeed-delay' => true,
'data-href' => $this->get_content_url()
];
return sprintf(
'',
implode( ' ', $this->render_attrs( $attrs, [ 'onload' ] ) )
);
}
/**
* 2. Render type 'inline' when the CSS has to be inlined.
*/
if ( $this->render_type === 'inline' ) {
if ( ! $this->content ) {
return '';
}
return sprintf(
'',
implode( ' ', $this->render_attrs( $attrs ) ),
$this->content
);
}
/**
* 3. Normal render. Usually for stylesheets that just have to be minified.
*/
if ( $this->render_type === 'normal' ) {
$attrs += [
'href' => $this->get_content_url()
];
return sprintf(
'',
implode( ' ', $this->render_attrs( $attrs ) )
);
}
// Default to nothing.
return '';
}
public function set_render( $type, $render_string = '' ) {
$this->render_type = $type;
if ( $render_string ) {
$this->render_string = $render_string;
}
}
/**
* Whether or not stylesheet is delayed or has a delayed factor to it (such as
* remove_css + delay combo).
*
* @return boolean
*/
public function has_delay() {
return $this->has_delay || $this->render_type === 'delay';
}
public function convert_urls() {
// Original base URL.
$base_url = preg_replace( '#[^/]+\?.*$#', '', $this->url );
// Borrowed and modified from MatthiasMullie\Minify\CSS.
$regex = '/
# open url()
url\(
\s*
# open path enclosure
(?P["\'])?
# fetch path
(?P.+?)
# close path enclosure, conditional
(?(quotes)(?P=quotes))
\s*
# close url()
\)
/ix';
preg_match_all( $regex, $this->content, $matches, PREG_SET_ORDER );
if ( ! $matches ) {
return;
}
foreach ( $matches as $match ) {
$url = trim( $match['path'] );
if ( substr( $url, 0, 5 ) === 'data:' ) {
continue;
}
$parsed_url = parse_url( $url );
// Skip known host and protocol-relative paths.
if ( ! empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) || $parsed_url['path'][0] === '/' ) {
continue;
}
$new_url = $base_url . $url;
// URLs with quotes, #, brackets or characters above 0x7e should be quoted.
// Restore original quotes.
// Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/url()#syntax
if ( preg_match( '/[\s\)\'"#\x{7f}-\x{9f}]/u', $new_url ) ) {
$new_url = $match['quotes'] . $new_url . $match['quotes'];
}
$new_url = 'url(' . $new_url . ')';
$this->content = str_replace( $match[0], $new_url, $this->content );
}
}
/**
* Whether the style is a google fonts URL.
*
* @return boolean
*/
public function is_google_fonts() {
return $this->is_google_fonts;
}
}
pagespeed/inc/optimize-js/script.php 0000644 00000007551 14760034774 0013565 0 ustar 00 id = $id;
$this->url = $url;
$this->orig_url = $url;
$this->orig_html = $orig_html;
if ( ! $this->id ) {
$this->id = md5( $this->url ?: uniqid( 'soledad_pagespeed_script', true ) );
}
// Has to be processed early as it might have to be searched for things like defer.
$this->process_content();
}
/**
* Set the inner content, if any.
*
* @return void
*/
public function process_content() {
if ( ! $this->url ) {
$this->content = preg_replace( '#\s*$#is', '\\1', $this->orig_html );
}
}
/**
* Factory method: Create an instance provided an HTML tag.
*
* @param string $tag
*
* @return boolean|self
*/
public static function from_tag( string $tag ) {
$attrs = Util\parse_attrs( $tag );
// Only process scripts of type javascript or missing type (assumed JS by browser).
if ( isset( $attrs['type'] ) && strpos( $attrs['type'], 'javascript' ) === false ) {
return false;
}
$script = new self( $attrs['id'] ?? '', $attrs['src'] ?? '', $tag );
// Note: We keep 'id' just in case if there was an original id.
$script->attrs = \array_diff_key( $attrs, array_flip( [ 'src', 'defer', 'async' ] ) );
if ( isset( $attrs['defer'] ) ) {
$script->defer = true;
}
if ( isset( $attrs['async'] ) ) {
$script->async = true;
}
return $script;
}
/**
* Render the HTML.
*
* @return string
*/
public function render() {
// Add src if available.
if ( $this->url ) {
$this->attrs += [
'src' => $this->get_content_url(),
'id' => $this->id,
];
}
$this->process_delay();
// Setting local to avoid mutating.
$content = $this->content;
if ( ! $this->url && $content && is_callable( $this->minifier ) ) {
$content = call_user_func( $this->minifier, $content );
}
// Add defer for external scripts. For inline scripts, use data uri in src.
if ( $this->defer ) {
$this->attrs['defer'] = true;
if ( ! $this->url ) {
$this->attrs['src'] = 'data:text/javascript;base64,' . base64_encode( $content );
$content = '';
}
}
if ( $this->async ) {
$this->attrs['async'] = true;
}
$render_atts = implode( ' ', $this->render_attrs( $this->attrs ) );
$new_tag = sprintf(
'',
$render_atts ? " $render_atts" : '',
! $this->url ? $content : ''
);
return $new_tag;
}
/**
* Process and add delayed script attributes if needed.
*
* @return void
*/
public function process_delay() {
if ( ! $this->delay ) {
return;
}
// Defer / async shouldn't be enabled anymore. Delay load logic implies deferred anyways.
$this->defer = false;
$this->async = false;
// Normal script with a URL.
if ( ! empty( $this->attrs['type'] ) ) {
$this->attrs['data-pencilazy-type'] = $this->attrs['type'];
}
$this->attrs['type'] = 'PenciLazyScript';
}
/**
* Set a callable minifier function.
*
* @param callable $minifier
*
* @return void
*/
public function set_minifier( $minifier ) {
$this->minifier = $minifier;
}
}
pagespeed/inc/optimize-js/optimize-js.php 0000644 00000020161 14760034774 0014523 0 ustar 00 html = $html;
}
public function process() {
$this->find_scripts();
// Figure out valid scripts.
$this->setup_valid_scripts();
do_action( 'soledad_pagespeed/optimize_js_begin', $this );
/**
* Setup scripts defer and delays and render the replacements.
*/
$has_delayed = false;
$script_array = $this->scripts;
if ( is_array( $script_array ) || is_object( $script_array ) ) {
foreach ( $script_array as $script ) {
// Has to be done for all scripts.
if ( get_theme_mod( 'penci_speed_minify_js' ) ) {
$this->minify_js( $script );
$script->render = true;
}
// Should this script be delayed.
if ( $this->should_delay_script( $script ) ) {
$script->delay = true;
$script->render = true;
$has_delayed = true;
} else {
Util\debug_log( 'Skipping: ' . print_r( $script, true ) );
}
if ( $script->render ) {
$this->html = str_replace( $script->orig_html, $script->render(), $this->html );
}
}
}
if ( $has_delayed ) {
Plugin::delay_load()->enable( true );
}
return $this->html;
}
public function find_scripts() {
/**
* Collect all valid enqueues.
*/
$all_scripts = [];
foreach ( wp_scripts()->registered as $handle => $script ) {
// Not an enqueued script, ignore.
if ( ! in_array( $handle, wp_scripts()->done ) ) {
continue;
}
// Don't mutate original.
$script = clone $script;
$script->deps = array_map( function ( $id ) {
// jQuery JS should be mapped to core. Add -js suffix for others.
return $id === 'jquery' ? 'jquery-core-js' : $id . '-js';
}, $script->deps );
// Add -js prefix to match the IDs that will retrieved from attrs.
$handle .= '-js';
$all_scripts[ $handle ] = $script;
// Pseudo entry for extras, adding a dependency.
if ( isset( $script->extra['after'] ) ) {
$all_scripts[ $handle . '-after' ] = (object) [
'deps' => [ $handle ]
];
}
}
$this->enqueues = $all_scripts;
/**
* Find all scripts.
*/
if ( ! preg_match_all( '##si', $this->html, $matches ) ) {
return;
}
foreach ( $matches[0] as $script ) {
$script = Script::from_tag( $script );
if ( $script ) {
$script->deps = $this->enqueues[ $script->id ]->deps ?? [];
$this->scripts[ $script->id ] = $script;
// Inline script has jQuery content? Ensure dependency.
if ( ! $script->url && in_array( 'jquery-core-js', $script->deps ) && strpos( $script->content, 'jQuery' ) !== false ) {
$script->deps[] = 'jquery-core-js';
}
}
}
}
/**
* Setup includes and excludes based on options.
*
* @return void
*/
public function setup_valid_scripts() {
// Used by both delay and defer.
$shared_excludes = [];
/**
* Delayed load scripts.
*/
$excludes = Util\option_to_array( get_theme_mod( 'penci_speed_delay_js_excludes' ) );
$default_ex = [
'analytics.js',
'google-analytics',
'googletagmanager',
'analytics',
'gtag',
'jetpack',
// Jetpack stats.
'url://stats.wp.com',
'_stq.push',
// WPML browser redirect.
'browser-redirect/app.js',
// WordPress jQuery
//'jquery-core',
//'jquery-migrate',
// Skip -js-extra as it's global variables and localization that shouldn't be delayed.
'id:-js-extra',
//Extra
//'/wp-includes/js/wp-embed.min.js',
'et_core_page_resource_fallback',
'window.\$us === undefined',
'js-extra',
'fusionNavIsCollapsed',
'eio_lazy_vars',
'et_animation_data',
'wpforms_settings',
'var nfForms',
'//stats.wp.com', // Jetpack Stats.
'_stq.push', // Jetpack Stats.
'fluent_form_ff_form_instance_', // Fluent Forms.
'cpLoadCSS', // Convert Pro.
'ninja_column_', // Ninja Tables.
'var rbs_gallery_', // Robo Gallery.
'var lepopup_', // Green Popup.
'var billing_additional_field', // Woo Autocomplete Nish.
'var gtm4wp',
'var dataLayer_content',
'/ewww-image-optimizer/includes/load[_-]webp(\.min)?.js', // EWWW WebP rewrite external script.
'/ewww-image-optimizer/includes/check-webp(\.min)?.js', // EWWW WebP check external script.
'ewww_webp_supported', // EWWW WebP inline scripts.
'/dist/js/browser-redirect/app.js', // WPML browser redirect script.
'/perfmatters/js/lazyload.min.js',
'scripts.mediavine.com/tags/', // allows mediavine-video schema to be accessible by search engines.
'initCubePortfolio', // Cube Portfolio show images.
'jetpack-lazy-images-js-enabled', // Jetpack Boost plugin lazyload.
'jetpack-boost-critical-css', // Jetpack Boost plugin critical CSS.
];
$default = [
'document\.body\.classList\.remove\("no-js"\)',
'document\.documentElement\.className\.replace\( \'no-js\', \'js\' \)',
'id:penci-dm-checking',
'penci_dmgetcookie',
];
if ( get_theme_mod( 'penci_speed_delay_js_excludes' ) == '' || ! get_theme_mod( 'penci_speed_delay_js_excludes' ) ) {
$default = array_merge( $default_ex, $default );
}
$excludes = array_merge( $excludes, $shared_excludes, $default );
$this->exclude_scripts = apply_filters( 'soledad_pagespeed/delay_js_excludes', $excludes );
// Enable delay adsense.
if ( get_theme_mod( 'penci_speed_delay_js_adsense' ) ) {
$this->include_scripts[] = 'adsbygoogle.js';
}
$this->include_scripts = apply_filters( 'soledad_pagespeed/delay_js_includes', $this->include_scripts );
}
/**
* Minify the JS file, if possible.
*
* @param Script $script
*
* @return void
*/
public function minify_js( Script $script ) {
$minifier = new Minifier( $script );
$minifier->process();
}
/**
* Should the script be delayed using one of the JS delay methods.
*
* @param Script $script
*
* @return boolean
*/
public function should_delay_script( Script $script ) {
if ( ! get_theme_mod( 'penci_speed_delay_js' ) ) {
return false;
}
// Excludes should be handled before includes and parents check.
foreach ( $this->exclude_scripts as $exclude ) {
if ( Util\asset_match( $exclude, $script, 'orig_html' ) ) {
return false;
}
}
// Delay all.
if ( get_theme_mod( 'penci_speed_delay_js' ) ) {
return true;
}
if ( $this->check_dependency( $script, $this->done_delay ) ) {
$this->done_delay[ $script->id ] = true;
return true;
}
foreach ( $this->include_scripts as $include ) {
if ( Util\asset_match( $include, $script, 'orig_html' ) ) {
$this->done_delay[ $script->id ] = true;
return true;
}
}
return false;
}
/**
* Check if the script has a parent dependency that is delayed/deferred.
*
* @param Script $script
* @param array $valid
*
* @return boolean
*/
public function check_dependency( Script $script, array $valid ) {
// Check if one of parent dependencies is delayed/deferred.
foreach ( (array) $script->deps as $dep ) {
if ( isset( $valid[ $dep ] ) ) {
return true;
}
}
// For translations, if wp-i18n-js is valid, so should be translations.
if ( preg_match( '/-js-translations/', $script->id, $matches ) ) {
if ( isset( $valid['wp-i18n-js'] ) ) {
return true;
}
}
// Special case: Inline script with a parent dep. If parent is true, so is child.
// Note: 'before' and 'extra' isn't accounted for and is not usually needed. Since 'extra' is
// usually localization and 'before' has to happen before anyways.
if ( preg_match( '/(.+?-js)-(before|after)/', $script->id, $matches ) ) {
$handle = $matches[1];
// Parent was valid, so is the current child.
if ( isset( $valid[ $handle ] ) ) {
return true;
}
}
return false;
}
}
pagespeed/inc/remove-css/sanitizer.php 0000644 00000032565 14760034774 0014105 0 ustar 00 [],
'tags' => [],
'ids' => []
];
protected $allow_selectors = [];
/**
* @param Stylesheet $sheet
* @param array $used_markup {
*
* @type array $classes
* @type array $tags
* @type array $ids
* }
*/
public function __construct( Stylesheet $sheet, array $used_markup, $allow = [] ) {
$this->sheet = $sheet;
$this->css = $sheet->content;
$this->used_markup = array_replace( $this->used_markup, $used_markup );
$this->allow_selectors = $allow;
}
public function set_cache( $data ) {
$this->cache = $data;
}
public function sanitize() {
$data = $this->sheet->parsed_data ?: [];
if ( ! $data ) {
// Strip the dreaded UTF-8 byte order mark (BOM, \uFEFF). Ref: https://github.com/sabberworm/PHP-CSS-Parser/issues/150
$this->css = preg_replace( '/^\xEF\xBB\xBF/', '', $this->css );
$config = Settings::create()->withMultibyteSupport( false );
$parser = new CSSParser( $this->css, $config );
$parsed = $parser->parse();
// Fix relative URLs.
$this->convert_urls( $parsed );
$data = $this->transform_data( $parsed );
$this->sheet->parsed_data = $data;
}
$this->process_allowed_selectors();
return $this->render_css( $data );
}
/**
* Convert relative URLs to full URLs for inline inclusion or changed paths.
*
* @param Document $data
*
* @return void
*/
public function convert_urls( Document $data ) {
$base_url = preg_replace( '#[^/]+\?.*$#', '', $this->sheet->url );
$values = $data->getAllValues();
foreach ( $values as $value ) {
if ( ! ( $value instanceof URL ) ) {
continue;
}
$url = $value->getURL()->getString();
// if (substr($url, 0, 5) === 'data:') {
// continue;
// }
if ( preg_match( '/^(https?|data):/', $url ) ) {
continue;
}
$parsed_url = parse_url( $url );
// Skip known host and protocol-relative paths.
if ( ! empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) || $parsed_url['path'][0] === '/' ) {
continue;
}
$new_url = $base_url . $url;
$value->getUrl()->setString( $new_url );
}
}
/**
* Transform data structure to store in our format. This data will be used without
* loading CSS Parser on further requests.
*
* @param CSSBlockList $data
*
* @return array
*/
public function transform_data( CSSBlockList $data ) {
$items = [];
foreach ( $data->getContents() as $content ) {
if ( $content instanceof AtRuleBlockList ) {
$items[] = [
'rulesets' => $this->transform_data( $content ),
'at_rule' => "@{$content->atRuleName()} {$content->atRuleArgs()}",
];
} else {
$item = [
//'css' => $content->render(OutputFormat::createPretty())
'css' => $content->render( OutputFormat::createCompact() )
];
if ( $content instanceof DeclarationBlock ) {
$item['selectors'] = $this->parse_selectors( $content->getSelectors() );
}
$items[] = $item;
}
}
return $items;
}
/**
* Parse selectors to get classes, id, tags and attrs.
*
* @param array $selectors
*
* @return array
*/
protected function parse_selectors( $selectors ) {
$selectors = array_map(
function ( $sel ) {
return $sel->__toString();
},
$selectors
);
$selectors_data = [];
foreach ( $selectors as $selector ) {
$data = [
'classes' => [],
'ids' => [],
'tags' => [],
// 'pseudo' => [],
'attrs' => [],
'selector' => trim( $selector ),
];
// if (strpos($selector, ':root') !== false) {
// $data['pseudo'][':root'] = 1;
// }
// Based on AMP plugin.
// Handle :not() and pseudo selectors to eliminate false negatives.
$selector = preg_replace( '/(?allow_selectors as $key => $value ) {
// Check if selector rule valid for current sheet.
if ( isset( $value['sheet'] ) && ! Util\asset_match( $value['sheet'], $this->sheet ) ) {
unset( $this->allow_selectors[ $key ] );
continue;
}
$value = $this->add_search_regex( $value );
$regex = $value['search_regex'] ?? '';
// Pre-compute the matching regex for performance.
if ( isset( $value['search'] ) ) {
$value['search'] = array_filter( (array) $value['search'] );
// If we still have something.
if ( $value['search'] ) {
$loose_regex = '(' . implode( '|', array_map( 'preg_quote', $value['search'] ) ) . ')(?=\s|\.|\:|,|\[|$)';
// Combine with search_regex if available.
$regex = $regex ? "($loose_regex|$regex)" : $loose_regex;
}
}
if ( $regex ) {
$value['computed_search_regex'] = $regex;
}
$this->allow_selectors[ $key ] = $value;
}
}
/**
* Add search regex to array by converting astrisks to proper regex search.
*
* @param array $value
*
* @return array
*/
protected function add_search_regex( array $value ) {
if ( isset( $value['search_regex'] ) ) {
return $value;
}
if ( isset( $value['search'] ) ) {
$value['search'] = (array) $value['search'];
$regex = [];
foreach ( $value['search'] as $key => $search ) {
if ( strpos( $search, '*' ) !== false ) {
$search = trim( $search );
// Optimize regex for starting.
// Note: Ending asterisk removal isn't necessary. PCRE engine is optimized for that.
$has_first_asterisk = 0;
$search = preg_replace( '/^\*(.+?)/', '\\1', $search, 1, $has_first_asterisk );
// 1. Space and asterisk matches a class itself, followed by space (child), or comma separator.
// 2. Only asterisk is considered more of a prefix/suffix and .class* will match .classname too.
$search = preg_quote( $search );
$search = str_replace( ' \*', '(\s|$|,|\:).*?', $search );
$search = str_replace( '\*', '.*?', $search );
// Note: To prevent ^(.*?) which is slow, we add starting position match only
// if the search doesn't start with asterisk match.
$regex[] = ( $has_first_asterisk ? '' : '^' ) . $search;
unset( $value['search'][ $key ] );
}
}
if ( $regex ) {
$value['search_regex'] = '(' . implode( '|', $regex ) . ')';
}
}
return $value;
}
public function render_css( $data ) {
$rendered = [];
foreach ( $data as $item ) {
// Has CSS.
if ( isset( $item['css'] ) ) {
$css = $item['css'];
// Render only if at least one selector meets the should_include criteria.
$should_render = ! isset( $item['selectors'] ) ||
0 !== count(
array_filter(
$item['selectors'],
function ( $selector ) {
return $this->should_include( $selector );
}
)
);
if ( $should_render && $this->should_render_props( $css ) ) {
$rendered[] = $css;
}
continue;
}
// Nested ruleset.
if ( ! empty( $item['rulesets'] ) ) {
$child_rulesets = $this->render_css( $item['rulesets'] );
if ( $child_rulesets && $this->should_render_props( $child_rulesets ) ) {
$rendered[] = sprintf(
'%s { %s }',
$item['at_rule'],
$child_rulesets
);
}
}
}
return implode( "", $rendered );
}
/**
* Whether to include a selector in the output.
*
* @param array $selector {
*
* @type string[]|null $classes
* @type string[]|null $ids
* @type string[]|null $tags
* }
* @return boolean
*/
public function should_include( $selector ) {
// :root is always valid.
// Note: Selectors of type `:root .class` will not match this but will be validated below
// if .class is used, as intended.
if ( $selector['selector'] === ':root' ) {
return true;
}
// If it's an attribute selector with nothing else, it should be kept. Perhaps *[attr] or [attr].
if ( ! empty( $selector['attrs'] )
&& ( empty( $selector['classes'] ) && empty( $selector['ids'] ) && empty( $selector['tags'] ) )
) {
return true;
}
// Check allow list.
// @todo move to cached pre-processed. Clear on settings change.
// $this->allow_selectors = [
// [
// 'type' => 'any',
// 'search' => '.auth-modal',
// ],
// [
// 'type' => 'prefix',
// 'class' => 's-dark'
// ],
// [
// 'type' => 'class',
// 'class' => 'has-lb',
// 'search' => ['.mfp-']
// ],
// [
// 'type' => 'any',
// 'class' => 'has-lb',
// 'search' => ['.mfp-']
// ],
// ];
if ( $this->allow_selectors ) {
foreach ( $this->allow_selectors as $include ) {
/**
* Prefix-based + all other classes/tags/etc. in selector exist in doc.
*
* Note: It's basically to ignore the first class and include the sub-classes based
* on their existence in doc. Example: .scheme-dark or .scheme-light.
*/
if ( $include['type'] === 'prefix' ) {
// Check if exact match.
if ( ( '.' . $include['class'] ) === $selector['selector'] ) {
return true;
}
// Check if first class matches.
$has_prefix = $include['class'] === substr( $selector['selector'], 1, strlen( $include['class'] ) );
if ( $has_prefix ) {
// Will check for validity later below. Remove first class as it's allowed.
if ( isset( $selector['classes'] ) ) {
$selector['classes'] = array_diff( $selector['classes'], [ $include['class'] ] );
}
// WARNING: Due to this break, if there's a rule to allow all selectors of this prefix
// that appear later, it won't be validated.
// @todo Sort prefixes to be at the end or run them later.
break;
}
continue;
}
// Check if a class exists in document.
if ( $include['type'] === 'class' ) {
if ( ! $this->is_used( $include['class'], 'classes' ) ) {
continue;
}
}
// Simple search selector string.
// $search = !empty($include['search']) ? (array) $include['search'] : [];
// Any type, normal selector string match.
// Note: The regex is equal at n=1 and faster at n>1, surprisingly.
// if ($search) {
// foreach ($search as $to_match) {
// if (strpos($selector['selector'], $to_match) !== false) {
// return true;
// }
// }
// }
// Pre-computed regex - combined 'search' and 'search_regex'.
if ( ! empty( $include['computed_search_regex'] ) ) {
if ( preg_match( '#' . $include['computed_search_regex'] . '#', $selector['selector'] ) ) {
return true;
}
}
}
}
$valid = true;
if (
// Check if all classes are used.
( ! empty( $selector['classes'] ) && ! $this->is_used( $selector['classes'], 'classes' ) )
// Check if all the ids are used.
|| ( ! empty( $selector['ids'] ) && ! $this->is_used( $selector['ids'], 'ids' ) )
// Check for the target tags in used.
|| ( ! empty( $selector['tags'] ) && ! $this->is_used( $selector['tags'], 'tags' ) )
) {
$valid = false;
}
return $valid;
}
/**
* Test if a selector classes, ids, or tags are used in the doc (provided in $this->used_markup).
*
* @param string|array $targets
* @param string $type 'classes', 'tags', or 'ids'.
*
* @return boolean
*/
public function is_used( $targets, $type = '' ) {
if ( ! $type ) {
return false;
}
if ( ! is_array( $targets ) ) {
$targets = (array) $targets;
}
foreach ( $targets as $target ) {
// All targets must exist.
if ( ! isset( $this->used_markup[ $type ][ $target ] ) ) {
return false;
}
}
return true;
}
public function should_render_props( $prop ) {
$render = true;
if ( get_theme_mod( 'penci_speed_disable_first_screen' ) && strpos( $prop, 'background-image' ) !== false ) {
$render = false;
}
return $render;
}
}
pagespeed/inc/remove-css/remove-css.php 0000644 00000027733 14760034774 0014161 0 ustar 00 stylesheets = $stylesheets;
$this->dom = $dom;
$this->html = $raw_html;
}
public function process() {
// Collect all the classes, ids, tags used in DOM.
$this->find_used_selectors();
// Figure out valid sheets.
$this->setup_valid_sheets();
/**
* Process and replace stylesheets with CSS cleaned.
*/
do_action( 'soledad_pagespeed/remove_css_begin', $this );
$allow_selectors = $this->get_allowed_selectors();
foreach ( $this->stylesheets as $sheet ) {
if ( ! $this->should_process_stylesheet( $sheet ) ) {
// Util\debug_log('Skipping: ' . print_r($sheet, true));
continue;
}
$org_url = $sheet->url;
if ( substr( $org_url, 0, 12 ) === "/wp-content/" ) {
$sheet->url = home_url( $org_url );
}
if ( substr( $sheet->url, 0, 2 ) === "//" ) {
$sheet->url = 'https:' . $sheet->url;
}
// Check URL content.
if ( $this->is_external_url( $sheet->url ) ) {
$file_data = $this->fetch_remote_content( $sheet->url );
} else {
$file_data = $this->process_file_by_url( $sheet->url );
}
if ( ! $file_data ) {
continue;
}
$sheet->content = $file_data['content'];
$sheet->file = $file_data['file'];
// Parsed sheet will be cached instead of being parsed by Sabberworm again.
$this->setup_sheet_cache( $sheet );
/**
* Fire up sanitizer to processa and remove unused CSS.
*/
$cache_name = 'soledad_pagespeed_sheet_cache_penci_crcss_' . $sheet->id;
if ( is_multisite() ) {
$cache_name = 'soledad_pagespeed_sheet_cache_site_' . get_current_blog_id() . '_penci_crcss ' . $sheet->id;
}
if ( is_home() ) {
$cache_name = $cache_name . '_home';
}
if ( is_front_page() ) {
$cache_name = $cache_name . '_front';
}
if ( is_page() ) {
$page_specify = [
'page-fullwidth.php',
'elementor_header_footer',
'page-vc.php',
'page-vc-sidebar.php',
'template-custom-all-blog-posts.php',
];
$page_id = get_the_ID();
$custom_cr = get_post_meta( $page_id, 'penci_post_critical_css', true );
$custom_temp = get_post_meta( $page_id, '_wp_page_template', true );
$cache_name = $custom_cr || in_array( $custom_temp, $page_specify ) ? $cache_name . '_page_' . $page_id : $cache_name . '_page_' . get_post_type();
}
if ( is_singular() ) {
$singular_id = get_the_ID();
$custom_cr = get_post_meta( $singular_id, 'penci_post_critical_css', true );
$cache_name = $custom_cr ? $cache_name . '_post_' . get_post_type() . '_' . $singular_id : $cache_name . '_post_' . get_post_type();
}
if ( is_category() ) {
$cat_data = get_queried_object();
$cat_id = $cat_data->term_id;
$cat_custom_data = get_option( "category_$cat_id" );
$cache_name = isset( $cat_custom_data['penci_critical_css'] ) && $cat_custom_data['penci_critical_css'] ? $cache_name . '_cat_' . $cat_data->taxonomy . '_' . $cat_id : $cache_name . '_cat_' . $cat_data->taxonomy;
}
if ( is_tag() ) {
$tag_data = get_queried_object();
$cache_name = $cache_name . '_tag_' . $tag_data->taxonomy;
}
if ( is_tax() ) {
$tax_data = get_queried_object();
$cache_name = $cache_name . '_tax_' . $tax_data->taxonomy;
}
if ( is_search() || is_author() || is_date() || is_day() || is_month() || is_year() ) {
$cache_name = $cache_name . '_archive';
}
$sanitized_css = Plugin::file_cache()->get( $cache_name );
if ( $sanitized_css ) {
$use_cache = true;
$sanitized_css = Plugin::file_system()->get_contents( $sanitized_css );
} else {
$use_cache = false;
$sanitizer = new Sanitizer( $sheet, $this->used_markup, $allow_selectors );
$sanitized_css = $sanitizer->sanitize();
Plugin::file_cache()->set( $cache_name, $sanitized_css );
}
if ( get_theme_mod( 'penci_speed_optimize_gfonts_delay' ) ) {
$sanitized_css = preg_replace( '/@font-face[^{]*{([^{}]|{[^{}]*})*}/', '', $sanitized_css );
}
// Store sizes for debug info.
$sheet->original_size = strlen( $sheet->content );
$sheet->new_size = $sanitized_css ? strlen( $sanitized_css ) : $sheet->original_size;
if ( $sanitized_css ) {
$extra_class = $use_cache ? 'cached' : 'no-cached';
// 'id' is pre-sanitized as it was extracted from HTML.
$replacement = "';
// Add tags for delayed CSS files.
if ( Plugin::delay_load()->should_delay_css() ) {
$sheet->delay_type = get_theme_mod( 'penci_speed_delay_css_type' );
$sheet->set_render( 'delay' );
$sheet->has_delay = true;
// Add the delay load CSS tag in addition to inlined sanitized CSS above.
$replacement .= $sheet->render();
}
$sheet->set_render( 'remove_css', $replacement );
$this->save_sheet_cache( $sheet );
}
$sheet->content = '';
$sheet->parsed_data = '';
}
// $this->stylesheets = array_map(function($sheet) {
// if (isset($sheet->original_size)) {
// $sheet->saved = $sheet->original_size - $sheet->new_size;
// }
// return $sheet;
// }, $this->stylesheets);
return $this->html;
}
/**
* Find all the classes, ids, and tags used in the document.
*
* @return void
*/
protected function find_used_selectors() {
$this->used_markup = [
'tags' => [],
'classes' => [],
'ids' => [],
];
/**
* @var DOMElement $node
*/
$classes = [];
foreach ( $this->dom->getElementsByTagName( '*' ) as $node ) {
$this->used_markup['tags'][ $node->tagName ] = 1;
// Collect tag classes.
if ( $node->hasAttribute( 'class' ) ) {
$class = $node->getAttribute( 'class' );
$ele_classes = preg_split( '/\s+/', $class );
array_push( $classes, ...$ele_classes );
}
if ( $node->hasAttribute( 'id' ) ) {
$this->used_markup['ids'][ $node->getAttribute( 'id' ) ] = 1;
}
}
// Add the classes.
$classes = array_filter( array_unique( $classes ) );
if ( $classes ) {
$this->used_markup['classes'] = array_fill_keys( $classes, 1 );
}
}
/**
* Setup includes and excludes based on options.
*
* @return void
*/
public function setup_valid_sheets() {
$default_excludes = [
'wp-includes/css/dashicons.css',
'admin-bar.css',
'wp-mediaelement',
'id:penci-font-awesome',
'id:penci-font-awesomeold',
'id:penci-font-iweather',
'id:penci_icon',
'id:penci-dark-style',
'id:woocommerce-smallscreen',
];
$excludes = array_merge( $default_excludes, Util\option_to_array( get_theme_mod( 'penci_speed_remove_css_excludes' ) ) );
$this->exclude_sheets = apply_filters( 'soledad_pagespeed/remove_css_excludes', $excludes, $this );
if ( get_theme_mod( 'penci_speed_remove_css' ) ) {
return;
}
$this->include_sheets[] = content_url( 'themes' ) . '/*';
$this->include_sheets[] = content_url( 'plugins' ) . '/*';
$this->include_sheets = apply_filters( 'soledad_pagespeed/remove_css_includes', $this->include_sheets, $this );
}
/**
* Get all the allowed selectors in correct data format to be used by sanitizer.
*
* @return array
*/
public function get_allowed_selectors() {
// Allowed selectors of type 'any': simple match in selector string.
$allowed_any = array_map( function ( $value ) {
if ( ! $value ) {
return '';
}
return [
'type' => 'any',
'search' => [ $value ]
];
}, Util\option_to_array( (string) get_theme_mod( 'penci_speed_allow_css_selectors' ) ) );
return apply_filters( 'soledad_pagespeed/allow_css_selectors', array_filter( $allowed_any ), $this );
}
/**
* Determine if stylesheet should be processed, based on exclusion and inclusion
* rules and settings.
*
* @param Stylesheet $sheet
*
* @return boolean
*/
public function should_process_stylesheet( Stylesheet $sheet ) {
// Handle manual excludes first.
if ( $this->exclude_sheets ) {
foreach ( $this->exclude_sheets as $exclude ) {
if ( Util\asset_match( $exclude, $sheet ) ) {
return false;
}
}
}
foreach ( $this->include_sheets as $include ) {
if ( Util\asset_match( $include, $sheet ) ) {
return true;
}
}
// All stylesheets are valid.
if ( get_theme_mod( 'penci_speed_remove_css' ) ) {
return true;
}
return false;
}
public function is_external_url( $url ): bool {
$external = false;
$assets_domain = $this->get_url_domain( $url );
$site_domain = $this->get_url_domain( home_url() );
if ( $assets_domain !== $site_domain ) {
$external = true;
}
return $external;
}
public function get_url_domain( $url ) {
$pieces = parse_url( $url );
$domain = isset( $pieces['host'] ) && $pieces['host'] ? $pieces['host'] : $pieces['path'];
if ( preg_match( '/(?P[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs ) ) {
return $regs['domain'];
}
return false;
}
/**
* Get remote asset content and add to asset content.
*
* @return array
*/
public function fetch_remote_content( $url ) {
$file = Plugin::file_cache()->get( $url );
if ( ! $file ) {
$request = wp_remote_get( $url, [
'timeout' => 10,
// For google fonts mainly.
'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
] );
if ( is_wp_error( $request ) || empty( $request['body'] ) ) {
return;
}
$content = $request['body'];
} else {
$content = Plugin::file_system()->get_contents( $file );
}
return [
'content' => $content,
'file' => $url
];
}
/**
* Process a stylesheet via URL
*
* @return boolean|array With 'content' and 'file'.
* @uses Plugin::file_system()->url_to_local()
* @uses Plugin::file_system()
*/
public function process_file_by_url( $url ) {
// Try to get local path for this stylesheet
$file = Plugin::file_system()->url_to_local( $url );
if ( ! $file ) {
return false;
}
// We can only support .css files yet
if ( substr( $file, - 4 ) !== '.css' ) {
return false;
}
$content = Plugin::file_system()->get_contents( $file );
if ( ! $content ) {
return false;
}
return [
'content' => $content,
'file' => $file
];
}
/**
* Add parsed data cache to stylesheet object. Will be used by save_sheet_cache later.
*
* @param Stylesheet $sheet
*
* @return void
*/
public function setup_sheet_cache( Stylesheet $sheet ) {
if ( ! isset( $sheet->file ) ) {
return;
}
$cache = get_transient( $this->get_transient_id( $sheet ) );
if ( $cache && $cache['mtime'] < Plugin::file_system()->mtime( $sheet->file ) ) {
return;
}
if ( $cache && ! empty( $cache['data'] ) ) {
$sheet->parsed_data = $cache['data'];
$sheet->has_cache = true;
return;
}
}
protected function get_transient_id( $sheet ) {
return substr( 'soledad_pagespeed_sheet_cache_' . $sheet->id, 0, 190 );
}
/**
* Cache the parsed data.
*
* Note: This doesn't cache whole CSS as that would vary based on found selectors.
*
* @param Stylesheet $sheet
*
* @return void
*/
public function save_sheet_cache( Stylesheet $sheet ) {
if ( $sheet->has_cache ) {
return;
}
$cache_data = [
'data' => $sheet->parsed_data,
'mtime' => Plugin::file_system()->mtime( $sheet->file )
];
// With expiry; won't be auto-loaded.
set_transient( $this->get_transient_id( $sheet ), $cache_data, MONTH_IN_SECONDS );
}
}
pagespeed/inc/autoloader.php 0000644 00000004305 14760034774 0012140 0 ustar 00 namespaces = $namespaces;
}
spl_autoload_register(array($this, 'load'), true, $prepend);
}
/**
* Autoloader the class either using a class map or via conversion of
* class name to file.
*
* @param string $class
*/
public function load($class)
{
if (isset($this->class_map[$class])) {
$file = $this->class_map[$class];
}
else {
foreach ($this->namespaces as $namespace => $dir) {
if (strpos($class, $namespace) !== false) {
$file = $this->get_file_path($class, $namespace, $dir);
break;
}
}
}
if (!empty($file)) {
require_once $file;
}
}
/**
* Get file path to include.
*
* Examples:
*
* Bunyad_Theme_Foo_Bar to inc/foo/bar/bar.php (fallback to inc/foo/bar.php)
* Bunyad\Blocks\FooBar to blocks/foo-bar/foo-bar.php (fallback to inc/foo-bar.php)
*
* @return string Relative path to the file from the theme dir
*/
public function get_file_path($class, $prefix = '', $path = '')
{
// Remove namespace and convert underscore as a namespace delim.
$class = str_replace($prefix, '', $class);
$class = str_replace('_', '\\', $class);
// Split to convert CamelCase.
$parts = explode('\\', $class);
foreach ($parts as $key => $part) {
$test = substr($part, 1);
// Convert CamelCase to Camel-Case
if (strtolower($test) !== $test) {
$part = preg_replace('/(.)(?=[A-Z])/u', '$1-', $part);
}
$parts[$key] = $part;
}
$name = strtolower(array_pop($parts));
$path = $path . '/' . strtolower(
implode('/', $parts)
);
$path = trailingslashit($path);
// Preferred and fallback file path.
$pref_file = $path . "{$name}/{$name}.php";
$alt_file = $path . "{$name}.php";
// Try with directory path pattern first.
if (file_exists($pref_file)) {
return $pref_file;
}
else if (file_exists($alt_file)) {
return $alt_file;
}
}
}
pagespeed/inc/options.php 0000644 00000003144 14760034774 0011474 0 ustar 00 option_key = $option_key;
}
/**
* Initialize
*/
public function init()
{
$this->load_defaults();
if (is_array($this->option_key)) {
$this->_options = [];
foreach ($this->option_key as $key) {
$this->_options = array_merge($this->_options, (array) get_option($key));
}
} else {
$this->_options = (array) get_option($this->option_key);
}
$this->_options = apply_filters('soledad_pagespeed/init_options', $this->_options);
}
public function load_defaults()
{
if (!class_exists('Soledad\PageSpeed\Admin\OptionsData')) {
return;
}
$this->defaults = array_reduce(
OptionsData::get_all(),
function($acc, $option) {
$acc[$option['id']] = isset($option['default']) ? $option['default'] : '';
return $acc;
},
[]
);
}
/**
* Get an option
*/
public function get($key, $fallback = '')
{
if (array_key_exists($key, $this->_options)) {
return $this->_options[$key];
}
if (array_key_exists($key, $this->defaults)) {
return $this->defaults[$key];
}
return $fallback;
}
public function __get($key)
{
return $this->get($key);
}
public function __set($key, $value)
{
$this->_options[$key] = $value;
}
}
pagespeed/inc/plugin.php 0000644 00000006502 14760034774 0011300 0 ustar 00 get( $name, $args );
}
/**
* Gets an object from container.
*/
public function get( $name, $args = array() ) {
if ( ! isset( $this->container[ $name ] ) ) {
throw new \Exception( "No container exists with key '{$name}'" );
}
$object = $this->container[ $name ];
if ( is_callable( $object ) ) {
return call_user_func_array( $object, $args );
} else if ( is_string( $object ) ) {
$object = new $object;
}
return $object;
}
/**
* @return $this
*/
public static function get_instance() {
if ( self::$instance == null ) {
self::$instance = new static();
}
return self::$instance;
}
/**
* Set it hooks on init.
*/
public function init() {
$this->dir_path = plugin_dir_path( $this->plugin_file );
$this->dir_url = plugin_dir_url( $this->plugin_file );
// Fire up the main autoloader.
require_once $this->dir_path . 'inc/autoloader.php';
new Autoloader( [
'Soledad\PageSpeed\\' => $this->dir_path . 'inc',
] );
// Composer autoloader.
require_once $this->dir_path . 'vendor/autoload.php';
/**
* Setup and init common requires.
*/
// The process handler.
$this->container['process'] = $this->shared( __NAMESPACE__ . '\Process' );
// File system with lazy init singleton.
$this->container['file_system'] = $this->shared( __NAMESPACE__ . '\FileSystem' );
// Delay Load assets.
$this->container['delay_load'] = $this->shared( __NAMESPACE__ . '\DelayLoad' );
// File cache handler.
$this->container['file_cache'] = $this->shared( __NAMESPACE__ . '\FileCache' );
// Google Fonts is a singleton.
$this->container['google_fonts'] = $this->shared( __NAMESPACE__ . '\OptimizeCss\GoogleFonts' );
// Utility functions.
require_once $this->dir_path . 'inc/util.php';
if ( current_user_can( 'manage_options' ) ) {
$admin = new Admin;
$admin->init();
}
// Init process to setup hooks.
self::process()->init();
}
/**
* Creates a single instance class for container.
*
* @param string $class Fully-qualifed class name
* @param array|null $args Bound args to pass to constructor
*/
public function shared( $class, $args = null ) {
return function ( $fresh = false ) use ( $class, $args ) {
static $object;
if ( ! $object || $fresh ) {
if ( ! $args ) {
$object = new $class;
} else {
$ref = new \ReflectionClass( $class );
$object = $ref->newInstanceArgs( $args );
}
}
return $object;
};
}
}
pagespeed/inc/file-system.php 0000644 00000011725 14760034774 0012246 0 ustar 00 0) {
$creds = request_filesystem_credentials('');
}
else {
ob_start();
$creds = request_filesystem_credentials('');
ob_end_clean();
}
if (!$creds) {
$creds = array();
}
$filesystem = WP_Filesystem($creds);
if (!$filesystem) {
// Fallback to lax permissions
$upload = wp_upload_dir();
WP_Filesystem(false, $upload['basedir'], true);
}
}
$this->filesystem = $wp_filesystem;
}
/**
* Recursively make parent directories for a path.
*
* Note: Adapted a bit from wp_mkdir_p().
*
* @param string $path
* @return boolean true on success, false or failure.
*/
public function mkdir_p($path)
{
if ($this->is_dir($path)) {
return;
}
$path = str_replace('//', '/', $path);
// Safe mode safety.
$path = rtrim($path, '/');
$path = $path ?: '/';
$created = $this->mkdir($path, FS_CHMOD_DIR);
if ($created) {
return true;
}
// If it failed above, try again by creating the parent first, recursively.
$parent = dirname($path);
if ($parent !== '/' && $this->mkdir_p($parent)) {
return $this->mkdir($path, FS_CHMOD_DIR);
}
return true;
}
/**
* Get a local file by the provided URL, if possible.
*
* @see wp_normalize_path()
*
* @return bool|string Either the path or false on failure.
*/
public function url_to_local($url)
{
$url = trim($url);
// Not a URL, just return the path.
if (substr($url, 0, 4) !== 'http' && substr($url, 0, 2) !== '//') {
return $url;
}
$url = explode('?', trim($url));
$url = trim($url[0]);
// We're not working with encoded URLs.
if (strpos($url, '%') !== false) {
$url = urldecode($url);
}
// Add https:// for parse_url() or it fails.
$url_no_proto = preg_replace(self::PROTO_REMOVE_PATTERN, '', $url);
$url_host = parse_url('https://' . $url_no_proto, PHP_URL_HOST);
// Not a known host / URL.
if (!in_array($url_host, $this->get_valid_hosts())) {
return false;
}
/**
* Go through each known path url map and stop at first matched.
*/
$valid_urls = $this->get_paths_urls();
$url_dirname = dirname($url_no_proto);
$matched = [];
foreach ($valid_urls as $path_url) {
if (strpos($url_dirname, untrailingslashit($path_url['url'])) !== false) {
$matched = $path_url;
break;
}
}
// We have a matched path.
if (!empty($matched['path'])) {
$path = wp_normalize_path(
$matched['path'] . str_replace($matched['url'], '', $url_dirname)
);
$file = trailingslashit($path) . wp_basename($url_no_proto);
if (file_exists($file) && is_file($file) && is_readable($file)) {
return $file;
}
}
return false;
}
/**
* Get recognized hostnames for stylesheet URLs.
*
* @return array
*/
public function get_valid_hosts()
{
if (!$this->valid_hosts) {
$this->valid_hosts = wp_list_pluck(
$this->get_paths_urls(),
'host'
);
}
return apply_filters('soledad_pagespeed/file_system/valid_hosts', $this->valid_hosts);
}
/**
* Get a map of known path URLs, associated local path, and host.
*
* @return array
*/
public function get_paths_urls()
{
if (!$this->paths_urls) {
// We add https:// back for parse_url() to prevent it from failing
$site_url = preg_replace(self::PROTO_REMOVE_PATTERN, '', site_url());
$site_host = parse_url('https://' . $site_url, PHP_URL_HOST);
$content_url = preg_replace(self::PROTO_REMOVE_PATTERN, '', content_url());
$content_host = parse_url('https://' . $content_url, PHP_URL_HOST);
/**
* This array will be processed in order it's defined to find the matching host and URL.
*/
$hosts = [
// First priority to use content_host and content_url()
'content' => [
'url' => $content_url,
'path' => WP_CONTENT_DIR,
'host' => $content_host
],
// Fallback to using site URL with ABSPATH
'site' => [
'url' => $site_url,
'path' => ABSPATH,
'host' => $site_host
],
];
$this->paths_urls = apply_filters('soledad_pagespeed/file_system/paths_urls', $hosts);
}
return $this->paths_urls;
}
/**
* Proxies to WP_Filesystem_Base
*/
public function __call($name, $arguments)
{
return call_user_func_array([$this->filesystem, $name], $arguments);
}
}
pagespeed/inc/file-cache.php 0000644 00000010644 14760034774 0011764 0 ustar 00 fs = Plugin::file_system();
$this->cache_path = WP_CONTENT_DIR . '/cache/soledad_pagespeed/';
$this->cache_url = WP_CONTENT_URL . '/cache/soledad_pagespeed/';
if ( is_multisite() ) {
$this->cache_path = WP_CONTENT_DIR . '/cache/soledad_pagespeed/site-' . get_current_blog_id() . '/';
$this->cache_url = WP_CONTENT_URL . '/cache/soledad_pagespeed/site-' . get_current_blog_id() . '/';
}
if ( get_option( 'penci_speed_file_version' ) ) {
$this->file_version = get_option( 'penci_speed_file_version' );
}
}
/**
* Get a cached file URL.
*
* @param string $id
*
* @return bool|string
*/
public function get_url( $id ) {
if ( ! $this->get( $id ) ) {
return false;
}
$file = $this->get_file_name( $id );
if ( ! $file ) {
return false;
}
$file_url = $this->cache_url . $this->get_cache_type( $file ) . '/' . $file;
return add_query_arg( 'v', $this->file_version , $file_url );
}
/**
* Get a cached file path.
*
* @param string $id
*
* @return bool
*/
public function get( $id ) {
$file = $this->get_file_name( $id );
if ( ! $file ) {
return false;
}
$file = $this->get_cache_path( $id ) . $file;
if ( $this->fs->is_file( $file ) ) {
return $file;
}
return false;
}
/**
* Get file name for cache storage/retrieval provided a URL or path.
*
* @param string $id URL or path of a file.
*
* @return string
*/
protected function get_file_name( $id ) {
// Get a local path if a valid URL was provided. Remote URLs also work.
$file = $this->fs->url_to_local( $id );
if ( ! $file ) {
// Remote here.
}
// MD5 on the id as it's likely to be a URL that can has a changing query string.
$hash = md5( $id );
return $hash . '.' . $this->get_cache_type( $file ?: $id );
}
/**
* Get the type of cache group based on file extension.
*
* @param string $id File path or name. URLs fine too.
*
* @return string
*/
protected function get_cache_type( string $file ) {
$extension = pathinfo( $file, PATHINFO_EXTENSION );
$type = $extension === 'js' ? 'js' : 'css';
return $type;
}
/**
* Get cache path based on provided file path or id.
*
* @param string $asset A local path or a URL.
*
* @return string
*/
public function get_cache_path( $asset = '' ) {
$path = $this->cache_path;
$asset = $asset ? $this->fs->url_to_local( $asset ) : '';
// Path with asset type directory.
return $path . $this->get_cache_type( $asset ) . '/';
}
/**
* Save a file in the cache.
*
* @param string $id Path, a local asset URL, or a unqiue name.
* @param string $content
*
* @return bool|string File path on success or false on failure.
*/
public function set( $id, string $content = '' ) {
if ( ! $content ) {
return false;
}
// Ensure path exists.
$path = $this->get_cache_path( $id );
$this->fs->mkdir_p( $path );
$file = $path . $this->get_file_name( $id );
if ( $this->fs->put_contents( $file, $content ) ) {
return $file;
}
return false;
}
/**
* Delete all the cached files for specified cache.
*
* @param string $type
*
* @return void
*/
public function delete_cache( $type = 'js' ) {
$dir = $this->cache_path . $type . '/';
$files = (array) $this->fs->dirlist( $dir );
if ( ! empty( $files ) ) {
foreach ( $files as $file ) {
if ( isset( $file['name'] ) ) {
$this->fs->delete( $dir . $file['name'] );
}
}
}
}
/**
* Delete all the cached files for specified cache.
*
* @param string $type
*
* @return void
*/
public function delete_all_cache( $type = 'js' ) {
$dir = WP_CONTENT_DIR . '/cache/soledad_pagespeed/' . $type . '/';
$files = (array) $this->fs->dirlist( $dir );
if ( ! empty( $files ) ) {
foreach ( $files as $file ) {
if ( isset( $file['name'] ) ) {
$this->fs->delete( $dir . $file['name'] );
}
}
}
}
/**
* Get number of files in cache for a specific type.
*
* @param string $type
*
* @return integer
*/
public function get_stats( $type = 'js' ) {
$dir = $this->cache_path . $type;
$files = $this->fs->dirlist( $dir );
return $files ? count( $files ) : 0;
}
}
pagespeed/inc/process.php 0000644 00000044166 14760034774 0011470 0 ustar 00 should_process() ) {
// Load integrations.
if ( class_exists( '\Elementor\Plugin', false ) ) {
new Integrations\Elementor;
}
if ( class_exists( 'Vc_Manager' ) ) {
new Integrations\Wpbakery;
}
// Check lazy images load
add_action( 'wp_enqueue_scripts', function () {
if ( ! $this->should_optimize_css() ) {
return false;
}
wp_dequeue_script( 'pc-lazy' );
if ( get_theme_mod( 'penci_speed_disable_first_screen', false ) ) {
wp_enqueue_script( 'pc-lazy' );
}
}, 99 );
/**
* Process HTML for inline and local stylesheets.
*
* wp_ob_end_flush_all() will take care of flushing it.
*
* Note: Autoptimize starts at priority 2 so we use 3 to process BEFORE AO.
*/
add_action( 'template_redirect', function () {
if ( ! apply_filters( 'soledad_pagespeed/should_process', true ) ) {
return false;
}
// Can't go in should_process() as that's too early.
if ( function_exists( '\amp_is_request' ) && \amp_is_request() ) {
return false;
}
if ( ( function_exists( 'is_penci_amp' ) && is_penci_amp() ) || ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) ) {
return false;
}
// Shouldn't process feeds, embeds (iframe), or robots.txt request.
if ( \is_feed() || \is_embed() || \is_robots() ) {
return false;
}
ob_start( [ $this, 'process_markup' ] );
}, - 999 );
// DEBUG: Devs if your output is disappearing - which you need for debugging,
// uncomment below and comment the init action above.
// add_action('template_redirect', function() { ob_start(); }, -999);
// add_action('shutdown', function() {
// $content = ob_get_clean();
// echo $this->process_markup($content);
// }, -10);
}
}
/**
* Should any processing be done at all.
*
* @return boolean
*/
public function should_process() {
if ( is_admin() ) {
return false;
}
if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) {
return false;
}
if ( isset( $_GET['nosoledad_pagespeed'] ) ) {
return false;
}
if ( Util\is_elementor() ) {
return false;
}
// WPBakery Page Builder. vc_is_page_editable() isn't reliable at all hooks.
if ( ! empty( $_GET['vc_editable'] ) ) {
return false;
}
if ( ! get_theme_mod( 'penci_speed_disable_cssjs', false ) && is_user_logged_in() ) {
return false;
}
return true;
}
/**
* Should the CSS be optimized.
*
* @return boolean
*/
public function should_optimize_css() {
$valid = get_theme_mod( 'penci_speed_remove_css' ) || get_theme_mod( 'penci_speed_optimize_css' );
return apply_filters( 'soledad_pagespeed/should_optimize_css', $valid );
}
/**
* Process DOM Markup provided with the html.
*
* @param string $html
*
* @return string
*/
public function process_markup( $html ) {
do_action( 'soledad_pagespeed/process_markup', $this );
if ( ! $this->is_valid_markup( $html ) ) {
return $html;
}
$dom = null;
if ( $this->should_optimize_css() ) {
// fix google fonts character
$html = str_replace( '&', '&', $html );
$html = hpp_defer_media_large( $html );
// progress
$dom = $this->get_dom( $html );
$optimize = new OptimizeCss( $dom, $html );
$html = $optimize->process();
}
if ( $this->should_optimize_js() ) {
$optimize_js = new OptimizeJs( $html );
$html = $optimize_js->process();
}
// Add delay load JS and extras as needed.
$html = Plugin::delay_load()->render( $html );
// Failed at processing DOM, return original.
if ( ! $dom ) {
return $html;
}
return $html;
}
public function is_valid_markup( $html ) {
if ( stripos( $html, 'loadHTML( $html );
libxml_clear_errors();
libxml_use_internal_errors( $libxml_previous );
if ( $result ) {
$dom->xpath = new \DOMXPath( $dom );
}
return $result ? $dom : false;
}
/**
* Should the JS be optimized.
*
* @return boolean
*/
public function should_optimize_js() {
$valid = true;
return apply_filters( 'soledad_pagespeed/should_optimize_js', $valid );
}
/**
* Conditions test to see if current page matches in the provided valid conditions.
*
* @param array $enable_on
*
* @return boolean
*/
public function check_enabled( array $enable_on ) {
if ( in_array( 'all', $enable_on ) ) {
return true;
}
$conditions = [
'pages' => 'is_page',
'posts' => 'is_single',
'archives' => 'is_archive',
'archive' => 'is_archive', // Alias
'categories' => 'is_category',
'tags' => 'is_tag',
'search' => 'is_search',
'404' => 'is_404',
'home' => function () {
return is_home() || is_front_page();
},
];
$satisfy = false;
foreach ( $enable_on as $key ) {
if ( ! isset( $conditions[ $key ] ) || ! is_callable( $conditions[ $key ] ) ) {
continue;
}
$satisfy = call_user_func( $conditions[ $key ] );
// Stop going further in loop once satisfied.
if ( $satisfy ) {
break;
}
}
return $satisfy;
}
}
function hpp_defer_media_large( $str ) {
$dt = [];
$str = hpp_treat_tag( $str, $dt, [ 'script' ] );
#$base64 = apply_filters('hpp_defer_src_holder', 'data:image/gif;base64,');
//$chunk = hpp_split_tag($str, '
$s1 ) {
if ( $i % 2 == 0 ) {
continue;
}//$i>=2 &&
$tg = substr( '<' . $chunk[ $i - 1 ] . ' ' . $s1, 0, 1200 );
$class = 'penci-lazy';
$disable_iframe = get_theme_mod( 'penci_disable_lazyload_iframe' );
if ( strpos( $tg, ' ' . $class . ' ' ) === false /*|| strpos($tg, (strpos($str, $base64)===false)? 'data:image/gif;base64,': $base64)===false*/ ) {
//should after to prevent duplicate lazy
if ( stripos( $tg, '