403Webshell
Server IP : 146.59.209.152  /  Your IP : 216.73.216.46
Web Server : Apache
System : Linux webm005.cluster131.gra.hosting.ovh.net 5.15.167-ovh-vps-grsec-zfs-classid #1 SMP Tue Sep 17 08:14:20 UTC 2024 x86_64
User : infrafs ( 43850)
PHP Version : 8.2.29
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/i/n/f/infrafs/INFRABIKEIT/wp-content/plugins/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/i/n/f/infrafs/INFRABIKEIT/wp-content/plugins/classes.zip
PK{�1\&O����'class-twentynineteen-walker-comment.phpnu&1i�<?php
/**
 * Custom comment walker for this theme
 *
 * @package WordPress
 * @subpackage Twenty_Nineteen
 * @since Twenty Nineteen 1.0
 */

/**
 * This class outputs custom comment walker for HTML5 friendly WordPress comment and threaded replies.
 *
 * @since Twenty Nineteen 1.0
 */
class TwentyNineteen_Walker_Comment extends Walker_Comment {

	/**
	 * Outputs a comment in the HTML5 format.
	 *
	 * @see wp_list_comments()
	 *
	 * @param WP_Comment $comment Comment to display.
	 * @param int        $depth   Depth of the current comment.
	 * @param array      $args    An array of arguments.
	 */
	protected function html5_comment( $comment, $depth, $args ) {

		$tag = ( 'div' === $args['style'] ) ? 'div' : 'li';

		?>
		<<?php echo $tag; ?> id="comment-<?php comment_ID(); ?>" <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?>>
			<article id="div-comment-<?php comment_ID(); ?>" class="comment-body">
				<footer class="comment-meta">
					<div class="comment-author vcard">
						<?php
						$comment_author_url = get_comment_author_url( $comment );
						$comment_author     = get_comment_author( $comment );
						$avatar             = get_avatar( $comment, $args['avatar_size'] );
						if ( 0 != $args['avatar_size'] ) {
							if ( empty( $comment_author_url ) ) {
								echo $avatar;
							} else {
								printf( '<a href="%s" rel="external nofollow" class="url">', $comment_author_url );
								echo $avatar;
							}
						}

						/*
						 * Using the `check` icon instead of `check_circle`, since we can't add a
						 * fill color to the inner check shape when in circle form.
						 */
						if ( twentynineteen_is_comment_by_post_author( $comment ) ) {
							printf( '<span class="post-author-badge" aria-hidden="true">%s</span>', twentynineteen_get_icon_svg( 'check', 24 ) );
						}

						printf(
							wp_kses(
								/* translators: %s: Comment author link. */
								__( '%s <span class="screen-reader-text says">says:</span>', 'twentynineteen' ),
								array(
									'span' => array(
										'class' => array(),
									),
								)
							),
							'<b class="fn">' . $comment_author . '</b>'
						);

						if ( ! empty( $comment_author_url ) ) {
							echo '</a>';
						}
						?>
					</div><!-- .comment-author -->

					<div class="comment-metadata">
						<?php
						/* translators: 1: Comment date, 2: Comment time. */
						$comment_timestamp = sprintf( __( '%1$s at %2$s', 'twentynineteen' ), get_comment_date( '', $comment ), get_comment_time() );

						printf(
							'<a href="%s"><time datetime="%s" title="%s">%s</time></a>',
							esc_url( get_comment_link( $comment, $args ) ),
							get_comment_time( 'c' ),
							esc_attr( $comment_timestamp ),
							$comment_timestamp
						);

						$edit_comment_icon = twentynineteen_get_icon_svg( 'edit', 16 );
						edit_comment_link( __( 'Edit', 'twentynineteen' ), ' <span class="edit-link-sep">&mdash;</span> <span class="edit-link">' . $edit_comment_icon, '</span>' );
						?>
					</div><!-- .comment-metadata -->

					<?php
					$commenter = wp_get_current_commenter();
					if ( $commenter['comment_author_email'] ) {
						$moderation_note = __( 'Your comment is awaiting moderation.', 'twentynineteen' );
					} else {
						$moderation_note = __( 'Your comment is awaiting moderation. This is a preview; your comment will be visible after it has been approved.', 'twentynineteen' );
					}
					?>

					<?php if ( '0' == $comment->comment_approved ) : ?>
					<p class="comment-awaiting-moderation"><?php echo $moderation_note; ?></p>
					<?php endif; ?>

				</footer><!-- .comment-meta -->

				<div class="comment-content">
					<?php comment_text(); ?>
				</div><!-- .comment-content -->

			</article><!-- .comment-body -->

			<?php
			comment_reply_link(
				array_merge(
					$args,
					array(
						'add_below' => 'div-comment',
						'depth'     => $depth,
						'max_depth' => $args['max_depth'],
						'before'    => '<div class="comment-reply">',
						'after'     => '</div>',
					)
				)
			);
			?>
		<?php
	}
}
PK{�1\������"class-twentynineteen-svg-icons.phpnu&1i�<?php
/**
 * SVG Icons class
 *
 * @package WordPress
 * @subpackage Twenty_Nineteen
 * @since Twenty Nineteen 1.0
 */

/**
 * This class is in charge of displaying SVG icons across the site.
 *
 * Place each <svg> source on its own array key, without adding the
 * both `width` and `height` attributes, since these are added dynamically,
 * before rendering the SVG code.
 *
 * All icons are assumed to have equal width and height, hence the option
 * to only specify a `$size` parameter in the svg methods.
 *
 * @since Twenty Nineteen 1.0
 */
class TwentyNineteen_SVG_Icons {

	/**
	 * Gets the SVG code for a given icon.
	 */
	public static function get_svg( $group, $icon, $size ) {
		if ( 'ui' === $group ) {
			$arr = self::$ui_icons;
		} elseif ( 'social' === $group ) {
			$arr = self::$social_icons;
		} else {
			$arr = array();
		}
		if ( array_key_exists( $icon, $arr ) ) {
			$repl = sprintf( '<svg class="svg-icon" width="%d" height="%d" aria-hidden="true" role="img" focusable="false" ', $size, $size );
			$svg  = preg_replace( '/^<svg /', $repl, trim( $arr[ $icon ] ) ); // Add extra attributes to SVG code.
			$svg  = preg_replace( "/([\n\t]+)/", ' ', $svg ); // Remove newlines & tabs.
			$svg  = preg_replace( '/>\s*</', '><', $svg );    // Remove whitespace between SVG tags.
			return $svg;
		}
		return null;
	}

	/**
	 * Detects the social network from a URL and returns the SVG code for its icon.
	 */
	public static function get_social_link_svg( $uri, $size ) {
		static $regex_map; // Only compute regex map once, for performance.
		if ( ! isset( $regex_map ) ) {
			$regex_map = array();
			$map       = &self::$social_icons_map; // Use reference instead of copy, to save memory.
			foreach ( array_keys( self::$social_icons ) as $icon ) {
				$domains            = array_key_exists( $icon, $map ) ? $map[ $icon ] : array( sprintf( '%s.com', $icon ) );
				$domains            = array_map( 'trim', $domains ); // Remove leading/trailing spaces, to prevent regex from failing to match.
				$domains            = array_map( 'preg_quote', $domains );
				$regex_map[ $icon ] = sprintf( '/(%s)/i', implode( '|', $domains ) );
			}
		}
		foreach ( $regex_map as $icon => $regex ) {
			if ( preg_match( $regex, $uri ) ) {
				return self::get_svg( 'social', $icon, $size );
			}
		}
		return null;
	}

	/**
	 * User Interface icons – svg sources.
	 *
	 * @var array
	 */
	public static $ui_icons = array(
		'link'                     => /* material-design – link */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <path d="M0 0h24v24H0z" fill="none"></path>
    <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path>
</svg>',

		'watch'                    => /* material-design – watch-later */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <defs>
        <path id="a" d="M0 0h24v24H0V0z"></path>
    </defs>
    <clipPath id="b">
        <use xlink:href="#a" overflow="visible"></use>
    </clipPath>
    <path clip-path="url(#b)" d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm4.2 14.2L11 13V7h1.5v5.2l4.5 2.7-.8 1.3z"></path>
</svg>',

		'archive'                  => /* material-design – folder */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"></path>
    <path d="M0 0h24v24H0z" fill="none"></path>
</svg>',

		'tag'                      => /* material-design – local_offer */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
	<path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"></path>
	<path d="M0 0h24v24H0z" fill="none"></path>
</svg>',

		'comment'                  => /* material-design – comment */ '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18z"></path>
    <path d="M0 0h24v24H0z" fill="none"></path>
</svg>',

		'person'                   => /* material-design – person */ '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"></path>
    <path d="M0 0h24v24H0z" fill="none"></path>
</svg>',

		'edit'                     => /* material-design – edit */ '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"></path>
    <path d="M0 0h24v24H0z" fill="none"></path>
</svg>',

		'chevron_left'             => /* material-design – chevron_left */ '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path>
    <path d="M0 0h24v24H0z" fill="none"></path>
</svg>',

		'chevron_right'            => /* material-design – chevron_right */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path>
    <path d="M0 0h24v24H0z" fill="none"></path>
</svg>',

		'check'                    => /* material-design – check */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <path d="M0 0h24v24H0z" fill="none"></path>
    <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path>
</svg>',

		'arrow_drop_down_circle'   => /* material-design – arrow_drop_down_circle */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
	<path d="M0 0h24v24H0z" fill="none"></path>
	<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 12l-4-4h8l-4 4z"></path>
</svg>',

		'keyboard_arrow_down'      => /* material-design – keyboard_arrow_down */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
	<path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"></path>
	<path fill="none" d="M0 0h24v24H0V0z"></path>
</svg>',

		'keyboard_arrow_right'     => /* material-design – keyboard_arrow_right */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
	<path d="M10 17l5-5-5-5v10z"></path>
	<path fill="none" d="M0 24V0h24v24H0z"></path>
</svg>',

		'keyboard_arrow_left'      => /* material-design – keyboard_arrow_left */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
	<path d="M14 7l-5 5 5 5V7z"></path>
	<path fill="none" d="M24 0v24H0V0h24z"></path>
</svg>',

		'arrow_drop_down_ellipsis' => /* custom – arrow_drop_down_ellipsis */ '
<svg xmlns="http://www.w3.org/2000/svg">
    <g fill="none" fill-rule="evenodd">
        <path d="M0 0h24v24H0z"/>
        <path fill="currentColor" fill-rule="nonzero" d="M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2zM6 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6 0a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6 0a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
    </g>
</svg>',

	);

	/**
	 * Social Icons – domain mappings.
	 *
	 * By default, each Icon ID is matched against a .com TLD. To override this behavior,
	 * specify all the domains it covers (including the .com TLD too, if applicable).
	 *
	 * @var array
	 */
	public static $social_icons_map = array(
		'amazon'      => array(
			'amazon.com',
			'amazon.cn',
			'amazon.in',
			'amazon.fr',
			'amazon.de',
			'amazon.it',
			'amazon.nl',
			'amazon.es',
			'amazon.co',
			'amazon.ca',
		),
		'apple'       => array(
			'apple.com',
			'itunes.com',
		),
		'behance'     => array(
			'behance.net',
		),
		'codepen'     => array(
			'codepen.io',
		),
		'facebook'    => array(
			'facebook.com',
			'fb.me',
		),
		'feed'        => array(
			'feed',
		),
		'google-plus' => array(
			'plus.google.com',
		),
		'lastfm'      => array(
			'last.fm',
		),
		'mail'        => array(
			'mailto:',
		),
		'slideshare'  => array(
			'slideshare.net',
		),
		'pocket'      => array(
			'getpocket.com',
		),
		'twitch'      => array(
			'twitch.tv',
		),
		'wordpress'   => array(
			'wordpress.com',
			'wordpress.org',
		),
	);

	/**
	 * Social Icons – svg sources.
	 *
	 * @var array
	 */
	public static $social_icons = array(
		'500px'       => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M6.94026,15.1412c.00437.01213.108.29862.168.44064a6.55008,6.55008,0,1,0,6.03191-9.09557,6.68654,6.68654,0,0,0-2.58357.51467A8.53914,8.53914,0,0,0,8.21268,8.61344L8.209,8.61725V3.22948l9.0504-.00008c.32934-.0036.32934-.46353.32934-.61466s0-.61091-.33035-.61467L7.47248,2a.43.43,0,0,0-.43131.42692v7.58355c0,.24466.30476.42131.58793.4819.553.11812.68074-.05864.81617-.2457l.018-.02481A10.52673,10.52673,0,0,1,9.32258,9.258a5.35268,5.35268,0,1,1,7.58985,7.54976,5.417,5.417,0,0,1-3.80867,1.56365,5.17483,5.17483,0,0,1-2.69822-.74478l.00342-4.61111a2.79372,2.79372,0,0,1,.71372-1.78792,2.61611,2.61611,0,0,1,1.98282-.89477,2.75683,2.75683,0,0,1,1.95525.79477,2.66867,2.66867,0,0,1,.79656,1.909,2.724,2.724,0,0,1-2.75849,2.748,4.94651,4.94651,0,0,1-.86254-.13719c-.31234-.093-.44519.34058-.48892.48349-.16811.54966.08453.65862.13687.67489a3.75751,3.75751,0,0,0,1.25234.18375,3.94634,3.94634,0,1,0-2.82444-6.742,3.67478,3.67478,0,0,0-1.13028,2.584l-.00041.02323c-.0035.11667-.00579,2.881-.00644,3.78811l-.00407-.00451a6.18521,6.18521,0,0,1-1.0851-1.86092c-.10544-.27856-.34358-.22925-.66857-.12917-.14192.04372-.57386.17677-.47833.489Zm4.65165-1.08338a.51346.51346,0,0,0,.19513.31818l.02276.022a.52945.52945,0,0,0,.3517.18416.24242.24242,0,0,0,.16577-.0611c.05473-.05082.67382-.67812.73287-.738l.69041.68819a.28978.28978,0,0,0,.21437.11032.53239.53239,0,0,0,.35708-.19486c.29792-.30419.14885-.46821.07676-.54751l-.69954-.69975.72952-.73469c.16-.17311.01874-.35708-.12218-.498-.20461-.20461-.402-.25742-.52855-.14083l-.7254.72665-.73354-.73375a.20128.20128,0,0,0-.14179-.05695.54135.54135,0,0,0-.34379.19648c-.22561.22555-.274.38149-.15656.5059l.73374.7315-.72942.73072A.26589.26589,0,0,0,11.59191,14.05782Zm1.59866-9.915A8.86081,8.86081,0,0,0,9.854,4.776a.26169.26169,0,0,0-.16938.22759.92978.92978,0,0,0,.08619.42094c.05682.14524.20779.531.50006.41955a8.40969,8.40969,0,0,1,2.91968-.55484,7.87875,7.87875,0,0,1,3.086.62286,8.61817,8.61817,0,0,1,2.30562,1.49315.2781.2781,0,0,0,.18318.07586c.15529,0,.30425-.15253.43167-.29551.21268-.23861.35873-.4369.1492-.63538a8.50425,8.50425,0,0,0-2.62312-1.694A9.0177,9.0177,0,0,0,13.19058,4.14283ZM19.50945,18.6236h0a.93171.93171,0,0,0-.36642-.25406.26589.26589,0,0,0-.27613.06613l-.06943.06929A7.90606,7.90606,0,0,1,7.60639,18.505a7.57284,7.57284,0,0,1-1.696-2.51537,8.58715,8.58715,0,0,1-.5147-1.77754l-.00871-.04864c-.04939-.25873-.28755-.27684-.62981-.22448-.14234.02178-.5755.088-.53426.39969l.001.00712a9.08807,9.08807,0,0,0,15.406,4.99094c.00193-.00192.04753-.04718.0725-.07436C19.79425,19.16234,19.87422,18.98728,19.50945,18.6236Z"></path>
</svg>',

		'amazon'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M13.582,8.182C11.934,8.367,9.78,8.49,8.238,9.166c-1.781,0.769-3.03,2.337-3.03,4.644 c0,2.953,1.86,4.429,4.253,4.429c2.02,0,3.125-0.477,4.685-2.065c0.516,0.747,0.685,1.109,1.629,1.894 c0.212,0.114,0.483,0.103,0.672-0.066l0.006,0.006c0.567-0.505,1.599-1.401,2.18-1.888c0.231-0.188,0.19-0.496,0.009-0.754 c-0.52-0.718-1.072-1.303-1.072-2.634V8.305c0-1.876,0.133-3.599-1.249-4.891C15.23,2.369,13.422,2,12.04,2 C9.336,2,6.318,3.01,5.686,6.351C5.618,6.706,5.877,6.893,6.109,6.945l2.754,0.298C9.121,7.23,9.308,6.977,9.357,6.72 c0.236-1.151,1.2-1.706,2.284-1.706c0.584,0,1.249,0.215,1.595,0.738c0.398,0.584,0.346,1.384,0.346,2.061V8.182z M13.049,14.088 c-0.451,0.8-1.169,1.291-1.967,1.291c-1.09,0-1.728-0.83-1.728-2.061c0-2.42,2.171-2.86,4.227-2.86v0.615 C13.582,12.181,13.608,13.104,13.049,14.088z M20.683,19.339C18.329,21.076,14.917,22,11.979,22c-4.118,0-7.826-1.522-10.632-4.057 c-0.22-0.199-0.024-0.471,0.241-0.317c3.027,1.762,6.771,2.823,10.639,2.823c2.608,0,5.476-0.541,8.115-1.66 C20.739,18.62,21.072,19.051,20.683,19.339z M21.336,21.043c-0.194,0.163-0.379,0.076-0.293-0.139 c0.284-0.71,0.92-2.298,0.619-2.684c-0.301-0.386-1.99-0.183-2.749-0.092c-0.23,0.027-0.266-0.173-0.059-0.319 c1.348-0.946,3.555-0.673,3.811-0.356C22.925,17.773,22.599,19.986,21.336,21.043z"></path>
</svg>',

		'apple'       => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M20.07,17.586a10.874,10.874,0,0,1-1.075,1.933,9.822,9.822,0,0,1-1.385,1.674,2.687,2.687,0,0,1-1.78.784,4.462,4.462,0,0,1-1.644-.393,4.718,4.718,0,0,0-1.77-.391,4.878,4.878,0,0,0-1.82.391A4.9,4.9,0,0,1,9.021,22a2.53,2.53,0,0,1-1.82-.8A10.314,10.314,0,0,1,5.752,19.46,11.987,11.987,0,0,1,4.22,16.417a11.143,11.143,0,0,1-.643-3.627,6.623,6.623,0,0,1,.87-3.465A5.1,5.1,0,0,1,6.268,7.483a4.9,4.9,0,0,1,2.463-.695,5.8,5.8,0,0,1,1.9.443,6.123,6.123,0,0,0,1.511.444,9.04,9.04,0,0,0,1.675-.523,5.537,5.537,0,0,1,2.277-.4,4.835,4.835,0,0,1,3.788,1.994,4.213,4.213,0,0,0-2.235,3.827,4.222,4.222,0,0,0,1.386,3.181,4.556,4.556,0,0,0,1.385.909q-.167.483-.353.927ZM16.211,2.4a4.267,4.267,0,0,1-1.094,2.8,3.726,3.726,0,0,1-3.1,1.528A3.114,3.114,0,0,1,12,6.347a4.384,4.384,0,0,1,1.16-2.828,4.467,4.467,0,0,1,1.414-1.061A4.215,4.215,0,0,1,16.19,2a3.633,3.633,0,0,1,.021.4Z"></path>
</svg>',

		'bandcamp'    => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M15.27 17.289 3 17.289 8.73 6.711 21 6.711 15.27 17.289"></path>
</svg>',

		'behance'     => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M7.799,5.698c0.589,0,1.12,0.051,1.606,0.156c0.482,0.102,0.894,0.273,1.241,0.507c0.344,0.235,0.612,0.546,0.804,0.938 c0.188,0.387,0.281,0.871,0.281,1.443c0,0.619-0.141,1.137-0.421,1.551c-0.284,0.413-0.7,0.751-1.255,1.014 c0.756,0.218,1.317,0.601,1.689,1.146c0.374,0.549,0.557,1.205,0.557,1.975c0,0.623-0.12,1.161-0.359,1.612 c-0.241,0.457-0.569,0.828-0.973,1.114c-0.408,0.288-0.876,0.5-1.399,0.637C9.052,17.931,8.514,18,7.963,18H2V5.698H7.799 M7.449,10.668c0.481,0,0.878-0.114,1.192-0.345c0.311-0.228,0.463-0.603,0.463-1.119c0-0.286-0.051-0.523-0.152-0.707 C8.848,8.315,8.711,8.171,8.536,8.07C8.362,7.966,8.166,7.894,7.94,7.854c-0.224-0.044-0.457-0.06-0.697-0.06H4.709v2.874H7.449z M7.6,15.905c0.267,0,0.521-0.024,0.759-0.077c0.243-0.053,0.457-0.137,0.637-0.261c0.182-0.12,0.332-0.283,0.441-0.491 C9.547,14.87,9.6,14.602,9.6,14.278c0-0.633-0.18-1.084-0.533-1.357c-0.356-0.27-0.83-0.404-1.413-0.404H4.709v3.388L7.6,15.905z M16.162,15.864c0.367,0.358,0.897,0.538,1.583,0.538c0.493,0,0.92-0.125,1.277-0.374c0.354-0.248,0.571-0.514,0.654-0.79h2.155 c-0.347,1.072-0.872,1.838-1.589,2.299C19.534,18,18.67,18.23,17.662,18.23c-0.701,0-1.332-0.113-1.899-0.337 c-0.567-0.227-1.041-0.544-1.439-0.958c-0.389-0.415-0.689-0.907-0.904-1.484c-0.213-0.574-0.32-1.21-0.32-1.899 c0-0.666,0.11-1.288,0.329-1.863c0.222-0.577,0.529-1.075,0.933-1.492c0.406-0.42,0.885-0.751,1.444-0.994 c0.558-0.241,1.175-0.363,1.857-0.363c0.754,0,1.414,0.145,1.98,0.44c0.563,0.291,1.026,0.686,1.389,1.181 c0.363,0.493,0.622,1.057,0.783,1.69c0.16,0.632,0.217,1.292,0.171,1.983h-6.428C15.557,14.84,15.795,15.506,16.162,15.864 M18.973,11.184c-0.291-0.321-0.783-0.496-1.384-0.496c-0.39,0-0.714,0.066-0.973,0.2c-0.254,0.132-0.461,0.297-0.621,0.491 c-0.157,0.197-0.265,0.405-0.328,0.628c-0.063,0.217-0.101,0.413-0.111,0.587h3.98C19.478,11.969,19.265,11.509,18.973,11.184z M15.057,7.738h4.985V6.524h-4.985L15.057,7.738z"></path>
</svg>',

		'chain'       => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M19.647,16.706a1.134,1.134,0,0,0-.343-.833l-2.549-2.549a1.134,1.134,0,0,0-.833-.343,1.168,1.168,0,0,0-.883.392l.233.226q.2.189.264.264a2.922,2.922,0,0,1,.184.233.986.986,0,0,1,.159.312,1.242,1.242,0,0,1,.043.337,1.172,1.172,0,0,1-1.176,1.176,1.237,1.237,0,0,1-.337-.043,1,1,0,0,1-.312-.159,2.76,2.76,0,0,1-.233-.184q-.073-.068-.264-.264l-.226-.233a1.19,1.19,0,0,0-.4.895,1.134,1.134,0,0,0,.343.833L15.837,19.3a1.13,1.13,0,0,0,.833.331,1.18,1.18,0,0,0,.833-.318l1.8-1.789a1.12,1.12,0,0,0,.343-.821Zm-8.615-8.64a1.134,1.134,0,0,0-.343-.833L8.163,4.7a1.134,1.134,0,0,0-.833-.343,1.184,1.184,0,0,0-.833.331L4.7,6.473a1.12,1.12,0,0,0-.343.821,1.134,1.134,0,0,0,.343.833l2.549,2.549a1.13,1.13,0,0,0,.833.331,1.184,1.184,0,0,0,.883-.38L8.728,10.4q-.2-.189-.264-.264A2.922,2.922,0,0,1,8.28,9.9a.986.986,0,0,1-.159-.312,1.242,1.242,0,0,1-.043-.337A1.172,1.172,0,0,1,9.254,8.079a1.237,1.237,0,0,1,.337.043,1,1,0,0,1,.312.159,2.761,2.761,0,0,1,.233.184q.073.068.264.264l.226.233a1.19,1.19,0,0,0,.4-.895ZM22,16.706a3.343,3.343,0,0,1-1.042,2.488l-1.8,1.789a3.536,3.536,0,0,1-4.988-.025l-2.525-2.537a3.384,3.384,0,0,1-1.017-2.488,3.448,3.448,0,0,1,1.078-2.561l-1.078-1.078a3.434,3.434,0,0,1-2.549,1.078,3.4,3.4,0,0,1-2.5-1.029L3.029,9.794A3.4,3.4,0,0,1,2,7.294,3.343,3.343,0,0,1,3.042,4.806l1.8-1.789A3.384,3.384,0,0,1,7.331,2a3.357,3.357,0,0,1,2.5,1.042l2.525,2.537a3.384,3.384,0,0,1,1.017,2.488,3.448,3.448,0,0,1-1.078,2.561l1.078,1.078a3.551,3.551,0,0,1,5.049-.049l2.549,2.549A3.4,3.4,0,0,1,22,16.706Z"></path>
</svg>',

		'codepen'     => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M22.016,8.84c-0.002-0.013-0.005-0.025-0.007-0.037c-0.005-0.025-0.008-0.048-0.015-0.072 c-0.003-0.015-0.01-0.028-0.013-0.042c-0.008-0.02-0.015-0.04-0.023-0.062c-0.007-0.015-0.013-0.028-0.02-0.042 c-0.008-0.02-0.018-0.037-0.03-0.057c-0.007-0.013-0.017-0.027-0.025-0.038c-0.012-0.018-0.023-0.035-0.035-0.052 c-0.01-0.013-0.02-0.025-0.03-0.037c-0.015-0.017-0.028-0.032-0.043-0.045c-0.01-0.012-0.022-0.023-0.035-0.035 c-0.015-0.015-0.032-0.028-0.048-0.04c-0.012-0.01-0.025-0.02-0.037-0.03c-0.005-0.003-0.01-0.008-0.015-0.012l-9.161-6.096 c-0.289-0.192-0.666-0.192-0.955,0L2.359,8.237C2.354,8.24,2.349,8.245,2.344,8.249L2.306,8.277 c-0.017,0.013-0.033,0.027-0.048,0.04C2.246,8.331,2.234,8.342,2.222,8.352c-0.015,0.015-0.028,0.03-0.042,0.047 c-0.012,0.013-0.022,0.023-0.03,0.037C2.139,8.453,2.125,8.471,2.115,8.488C2.107,8.501,2.099,8.514,2.09,8.526 C2.079,8.548,2.069,8.565,2.06,8.585C2.054,8.6,2.047,8.613,2.04,8.626C2.032,8.648,2.025,8.67,2.019,8.69 c-0.005,0.013-0.01,0.027-0.013,0.042C1.999,8.755,1.995,8.778,1.99,8.803C1.989,8.817,1.985,8.828,1.984,8.84 C1.978,8.879,1.975,8.915,1.975,8.954v6.093c0,0.037,0.003,0.075,0.008,0.112c0.002,0.012,0.005,0.025,0.007,0.038 c0.005,0.023,0.008,0.047,0.015,0.072c0.003,0.015,0.008,0.028,0.013,0.04c0.007,0.022,0.013,0.042,0.022,0.063 c0.007,0.015,0.013,0.028,0.02,0.04c0.008,0.02,0.018,0.038,0.03,0.058c0.007,0.013,0.015,0.027,0.025,0.038 c0.012,0.018,0.023,0.035,0.035,0.052c0.01,0.013,0.02,0.025,0.03,0.037c0.013,0.015,0.028,0.032,0.042,0.045 c0.012,0.012,0.023,0.023,0.035,0.035c0.015,0.013,0.032,0.028,0.048,0.04l0.038,0.03c0.005,0.003,0.01,0.007,0.013,0.01 l9.163,6.095C11.668,21.953,11.833,22,12,22c0.167,0,0.332-0.047,0.478-0.144l9.163-6.095l0.015-0.01 c0.013-0.01,0.027-0.02,0.037-0.03c0.018-0.013,0.035-0.028,0.048-0.04c0.013-0.012,0.025-0.023,0.035-0.035 c0.017-0.015,0.03-0.032,0.043-0.045c0.01-0.013,0.02-0.025,0.03-0.037c0.013-0.018,0.025-0.035,0.035-0.052 c0.008-0.013,0.018-0.027,0.025-0.038c0.012-0.02,0.022-0.038,0.03-0.058c0.007-0.013,0.013-0.027,0.02-0.04 c0.008-0.022,0.015-0.042,0.023-0.063c0.003-0.013,0.01-0.027,0.013-0.04c0.007-0.025,0.01-0.048,0.015-0.072 c0.002-0.013,0.005-0.027,0.007-0.037c0.003-0.042,0.007-0.079,0.007-0.117V8.954C22.025,8.915,22.022,8.879,22.016,8.84z M12.862,4.464l6.751,4.49l-3.016,2.013l-3.735-2.492V4.464z M11.138,4.464v4.009l-3.735,2.494L4.389,8.954L11.138,4.464z M3.699,10.562L5.853,12l-2.155,1.438V10.562z M11.138,19.536l-6.749-4.491l3.015-2.011l3.735,2.492V19.536z M12,14.035L8.953,12 L12,9.966L15.047,12L12,14.035z M12.862,19.536v-4.009l3.735-2.492l3.016,2.011L12.862,19.536z M20.303,13.438L18.147,12 l2.156-1.438L20.303,13.438z"></path>
</svg>',

		'deviantart'  => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M 18.19 5.636 18.19 2 18.188 2 14.553 2 14.19 2.366 12.474 5.636 11.935 6 5.81 6 5.81 10.994 9.177 10.994 9.477 11.357 5.81 18.363 5.81 22 5.811 22 9.447 22 9.81 21.634 11.526 18.364 12.065 18 18.19 18 18.19 13.006 14.823 13.006 14.523 12.641 18.19 5.636z"></path>
</svg>',

		'digg'        => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M4.5,5.4h2.2V16H1V8.5h3.5V5.4L4.5,5.4z M4.5,14.2v-4H3.2v4H4.5z M7.6,8.5V16h2.2V8.5C9.8,8.5,7.6,8.5,7.6,8.5z M7.6,5.4 v2.2h2.2V5.4C9.8,5.4,7.6,5.4,7.6,5.4z M10.7,8.5h5.7v10.1h-5.7v-1.8h3.5V16h-3.5C10.7,16,10.7,8.5,10.7,8.5z M14.2,14.2v-4h-1.3v4 H14.2z M17.3,8.5H23v10.1h-5.7v-1.8h3.5V16h-3.5C17.3,16,17.3,8.5,17.3,8.5z M20.8,14.2v-4h-1.3v4H20.8z"></path>
</svg>',

		'dribbble'    => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12,22C6.486,22,2,17.514,2,12S6.486,2,12,2c5.514,0,10,4.486,10,10S17.514,22,12,22z M20.434,13.369 c-0.292-0.092-2.644-0.794-5.32-0.365c1.117,3.07,1.572,5.57,1.659,6.09C18.689,17.798,20.053,15.745,20.434,13.369z M15.336,19.876c-0.127-0.749-0.623-3.361-1.822-6.477c-0.019,0.006-0.038,0.013-0.056,0.019c-4.818,1.679-6.547,5.02-6.701,5.334 c1.448,1.129,3.268,1.803,5.243,1.803C13.183,20.555,14.311,20.313,15.336,19.876z M5.654,17.724 c0.193-0.331,2.538-4.213,6.943-5.637c0.111-0.036,0.224-0.07,0.337-0.102c-0.214-0.485-0.448-0.971-0.692-1.45 c-4.266,1.277-8.405,1.223-8.778,1.216c-0.003,0.087-0.004,0.174-0.004,0.261C3.458,14.207,4.29,16.21,5.654,17.724z M3.639,10.264 c0.382,0.005,3.901,0.02,7.897-1.041c-1.415-2.516-2.942-4.631-3.167-4.94C5.979,5.41,4.193,7.613,3.639,10.264z M9.998,3.709 c0.236,0.316,1.787,2.429,3.187,5c3.037-1.138,4.323-2.867,4.477-3.085C16.154,4.286,14.17,3.471,12,3.471 C11.311,3.471,10.641,3.554,9.998,3.709z M18.612,6.612C18.432,6.855,17,8.69,13.842,9.979c0.199,0.407,0.389,0.821,0.567,1.237 c0.063,0.148,0.124,0.295,0.184,0.441c2.842-0.357,5.666,0.215,5.948,0.275C20.522,9.916,19.801,8.065,18.612,6.612z"></path>
</svg>',

		'dropbox'     => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12,6.134L6.069,9.797L2,6.54l5.883-3.843L12,6.134z M2,13.054l5.883,3.843L12,13.459L6.069,9.797L2,13.054z M12,13.459 l4.116,3.439L22,13.054l-4.069-3.257L12,13.459z M22,6.54l-5.884-3.843L12,6.134l5.931,3.663L22,6.54z M12.011,14.2l-4.129,3.426 l-1.767-1.153v1.291l5.896,3.539l5.897-3.539v-1.291l-1.769,1.153L12.011,14.2z"></path>
</svg>',

		'etsy'        => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M9.16033,4.038c0-.27174.02717-.43478.48913-.43478h6.22283c1.087,0,1.68478.92391,2.11957,2.663l.35326,1.38587h1.05978C19.59511,3.712,19.75815,2,19.75815,2s-2.663.29891-4.23913.29891h-7.962L3.29076,2.163v1.1413L4.731,3.57609c1.00543.19022,1.25.40761,1.33152,1.33152,0,0,.08152,2.71739.08152,7.20109s-.08152,7.17391-.08152,7.17391c0,.81522-.32609,1.11413-1.33152,1.30435l-1.44022.27174V22l4.2663-.13587h7.11957c1.60326,0,5.32609.13587,5.32609.13587.08152-.97826.625-5.40761.70652-5.89674H19.7038L18.644,18.52174c-.84239,1.90217-2.06522,2.038-3.42391,2.038H11.1712c-1.3587,0-2.01087-.54348-2.01087-1.712V12.65217s3.0163,0,3.99457.08152c.76087.05435,1.22283.27174,1.46739,1.33152l.32609,1.413h1.16848l-.08152-3.55978.163-3.587H15.02989l-.38043,1.57609c-.24457,1.03261-.40761,1.22283-1.46739,1.33152-1.38587.13587-4.02174.1087-4.02174.1087Z"></path>
</svg>',

		'facebook'    => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M20.007,3H3.993C3.445,3,3,3.445,3,3.993v16.013C3,20.555,3.445,21,3.993,21h8.621v-6.971h-2.346v-2.717h2.346V9.31 c0-2.325,1.42-3.591,3.494-3.591c0.993,0,1.847,0.074,2.096,0.107v2.43l-1.438,0.001c-1.128,0-1.346,0.536-1.346,1.323v1.734h2.69 l-0.35,2.717h-2.34V21h4.587C20.555,21,21,20.555,21,20.007V3.993C21,3.445,20.555,3,20.007,3z"></path>
</svg>',

		'feed'        => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M2,8.667V12c5.515,0,10,4.485,10,10h3.333C15.333,14.637,9.363,8.667,2,8.667z M2,2v3.333 c9.19,0,16.667,7.477,16.667,16.667H22C22,10.955,13.045,2,2,2z M4.5,17C3.118,17,2,18.12,2,19.5S3.118,22,4.5,22S7,20.88,7,19.5 S5.882,17,4.5,17z"></path>
</svg>',

		'flickr'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M6.5,7c-2.75,0-5,2.25-5,5s2.25,5,5,5s5-2.25,5-5S9.25,7,6.5,7z M17.5,7c-2.75,0-5,2.25-5,5s2.25,5,5,5s5-2.25,5-5 S20.25,7,17.5,7z"></path>
</svg>',

		'foursquare'  => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M17.573,2c0,0-9.197,0-10.668,0S5,3.107,5,3.805s0,16.948,0,16.948c0,0.785,0.422,1.077,0.66,1.172 c0.238,0.097,0.892,0.177,1.285-0.275c0,0,5.035-5.843,5.122-5.93c0.132-0.132,0.132-0.132,0.262-0.132h3.26 c1.368,0,1.588-0.977,1.732-1.552c0.078-0.318,0.692-3.428,1.225-6.122l0.675-3.368C19.56,2.893,19.14,2,17.573,2z M16.495,7.22 c-0.053,0.252-0.372,0.518-0.665,0.518c-0.293,0-4.157,0-4.157,0c-0.467,0-0.802,0.318-0.802,0.787v0.508 c0,0.467,0.337,0.798,0.805,0.798c0,0,3.197,0,3.528,0s0.655,0.362,0.583,0.715c-0.072,0.353-0.407,2.102-0.448,2.295 c-0.04,0.193-0.262,0.523-0.655,0.523c-0.33,0-2.88,0-2.88,0c-0.523,0-0.683,0.068-1.033,0.503 c-0.35,0.437-3.505,4.223-3.505,4.223c-0.032,0.035-0.063,0.027-0.063-0.015V4.852c0-0.298,0.26-0.648,0.648-0.648 c0,0,8.228,0,8.562,0c0.315,0,0.61,0.297,0.528,0.683L16.495,7.22z"></path>
</svg>',

		'goodreads'   => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M17.3,17.5c-0.2,0.8-0.5,1.4-1,1.9c-0.4,0.5-1,0.9-1.7,1.2C13.9,20.9,13.1,21,12,21c-0.6,0-1.3-0.1-1.9-0.2 c-0.6-0.1-1.1-0.4-1.6-0.7c-0.5-0.3-0.9-0.7-1.2-1.2c-0.3-0.5-0.5-1.1-0.5-1.7h1.5c0.1,0.5,0.2,0.9,0.5,1.2 c0.2,0.3,0.5,0.6,0.9,0.8c0.3,0.2,0.7,0.3,1.1,0.4c0.4,0.1,0.8,0.1,1.2,0.1c1.4,0,2.5-0.4,3.1-1.2c0.6-0.8,1-2,1-3.5v-1.7h0 c-0.4,0.8-0.9,1.4-1.6,1.9c-0.7,0.5-1.5,0.7-2.4,0.7c-1,0-1.9-0.2-2.6-0.5C8.7,15,8.1,14.5,7.7,14c-0.5-0.6-0.8-1.3-1-2.1 c-0.2-0.8-0.3-1.6-0.3-2.5c0-0.9,0.1-1.7,0.4-2.5c0.3-0.8,0.6-1.5,1.1-2c0.5-0.6,1.1-1,1.8-1.4C10.3,3.2,11.1,3,12,3 c0.5,0,0.9,0.1,1.3,0.2c0.4,0.1,0.8,0.3,1.1,0.5c0.3,0.2,0.6,0.5,0.9,0.8c0.3,0.3,0.5,0.6,0.6,1h0V3.4h1.5V15 C17.6,15.9,17.5,16.7,17.3,17.5z M13.8,14.1c0.5-0.3,0.9-0.7,1.3-1.1c0.3-0.5,0.6-1,0.8-1.6c0.2-0.6,0.3-1.2,0.3-1.9 c0-0.6-0.1-1.2-0.2-1.9c-0.1-0.6-0.4-1.2-0.7-1.7c-0.3-0.5-0.7-0.9-1.3-1.2c-0.5-0.3-1.1-0.5-1.9-0.5s-1.4,0.2-1.9,0.5 c-0.5,0.3-1,0.7-1.3,1.2C8.5,6.4,8.3,7,8.1,7.6C8,8.2,7.9,8.9,7.9,9.5c0,0.6,0.1,1.3,0.2,1.9C8.3,12,8.6,12.5,8.9,13 c0.3,0.5,0.8,0.8,1.3,1.1c0.5,0.3,1.1,0.4,1.9,0.4C12.7,14.5,13.3,14.4,13.8,14.1z"></path>
</svg>',

		'google-plus' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M8,11h6.61c0.06,0.35,0.11,0.7,0.11,1.16c0,4-2.68,6.84-6.72,6.84c-3.87,0-7-3.13-7-7s3.13-7,7-7 c1.89,0,3.47,0.69,4.69,1.83l-1.9,1.83C10.27,8.16,9.36,7.58,8,7.58c-2.39,0-4.34,1.98-4.34,4.42S5.61,16.42,8,16.42 c2.77,0,3.81-1.99,3.97-3.02H8V11L8,11z M23,11h-2V9h-2v2h-2v2h2v2h2v-2h2"></path>
</svg>',

		'google'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12.02,10.18v3.72v0.01h5.51c-0.26,1.57-1.67,4.22-5.5,4.22c-3.31,0-6.01-2.75-6.01-6.12s2.7-6.12,6.01-6.12 c1.87,0,3.13,0.8,3.85,1.48l2.84-2.76C16.99,2.99,14.73,2,12.03,2c-5.52,0-10,4.48-10,10s4.48,10,10,10c5.77,0,9.6-4.06,9.6-9.77 c0-0.83-0.11-1.42-0.25-2.05H12.02z"></path>
</svg>',

		'github'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path>
</svg>',

		'instagram'   => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12,4.622c2.403,0,2.688,0.009,3.637,0.052c0.877,0.04,1.354,0.187,1.671,0.31c0.42,0.163,0.72,0.358,1.035,0.673 c0.315,0.315,0.51,0.615,0.673,1.035c0.123,0.317,0.27,0.794,0.31,1.671c0.043,0.949,0.052,1.234,0.052,3.637 s-0.009,2.688-0.052,3.637c-0.04,0.877-0.187,1.354-0.31,1.671c-0.163,0.42-0.358,0.72-0.673,1.035 c-0.315,0.315-0.615,0.51-1.035,0.673c-0.317,0.123-0.794,0.27-1.671,0.31c-0.949,0.043-1.233,0.052-3.637,0.052 s-2.688-0.009-3.637-0.052c-0.877-0.04-1.354-0.187-1.671-0.31c-0.42-0.163-0.72-0.358-1.035-0.673 c-0.315-0.315-0.51-0.615-0.673-1.035c-0.123-0.317-0.27-0.794-0.31-1.671C4.631,14.688,4.622,14.403,4.622,12 s0.009-2.688,0.052-3.637c0.04-0.877,0.187-1.354,0.31-1.671c0.163-0.42,0.358-0.72,0.673-1.035 c0.315-0.315,0.615-0.51,1.035-0.673c0.317-0.123,0.794-0.27,1.671-0.31C9.312,4.631,9.597,4.622,12,4.622 M12,3 C9.556,3,9.249,3.01,8.289,3.054C7.331,3.098,6.677,3.25,6.105,3.472C5.513,3.702,5.011,4.01,4.511,4.511 c-0.5,0.5-0.808,1.002-1.038,1.594C3.25,6.677,3.098,7.331,3.054,8.289C3.01,9.249,3,9.556,3,12c0,2.444,0.01,2.751,0.054,3.711 c0.044,0.958,0.196,1.612,0.418,2.185c0.23,0.592,0.538,1.094,1.038,1.594c0.5,0.5,1.002,0.808,1.594,1.038 c0.572,0.222,1.227,0.375,2.185,0.418C9.249,20.99,9.556,21,12,21s2.751-0.01,3.711-0.054c0.958-0.044,1.612-0.196,2.185-0.418 c0.592-0.23,1.094-0.538,1.594-1.038c0.5-0.5,0.808-1.002,1.038-1.594c0.222-0.572,0.375-1.227,0.418-2.185 C20.99,14.751,21,14.444,21,12s-0.01-2.751-0.054-3.711c-0.044-0.958-0.196-1.612-0.418-2.185c-0.23-0.592-0.538-1.094-1.038-1.594 c-0.5-0.5-1.002-0.808-1.594-1.038c-0.572-0.222-1.227-0.375-2.185-0.418C14.751,3.01,14.444,3,12,3L12,3z M12,7.378 c-2.552,0-4.622,2.069-4.622,4.622S9.448,16.622,12,16.622s4.622-2.069,4.622-4.622S14.552,7.378,12,7.378z M12,15 c-1.657,0-3-1.343-3-3s1.343-3,3-3s3,1.343,3,3S13.657,15,12,15z M16.804,6.116c-0.596,0-1.08,0.484-1.08,1.08 s0.484,1.08,1.08,1.08c0.596,0,1.08-0.484,1.08-1.08S17.401,6.116,16.804,6.116z"></path>
</svg>',

		'lastfm'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M10.5002,0 C4.7006,0 0,4.70109753 0,10.4998496 C0,16.2989526 4.7006,21 10.5002,21 C16.299,21 21,16.2989526 21,10.4998496 C21,4.70109753 16.299,0 10.5002,0 Z M14.69735,14.7204413 C13.3164,14.7151781 12.4346,14.0870017 11.83445,12.6859357 L11.6816001,12.3451305 L10.35405,9.31011397 C9.92709997,8.26875064 8.85260001,7.57120012 7.68010001,7.57120012 C6.06945001,7.57120012 4.75925001,8.88509738 4.75925001,10.5009524 C4.75925001,12.1164565 6.06945001,13.4303036 7.68010001,13.4303036 C8.77200001,13.4303036 9.76514999,12.827541 10.2719501,11.8567047 C10.2893,11.8235214 10.3239,11.8019673 10.36305,11.8038219 C10.4007,11.8053759 10.43535,11.8287847 10.4504,11.8631709 L10.98655,13.1045863 C11.0016,13.1389726 10.9956,13.17782 10.97225,13.2068931 C10.1605001,14.1995341 8.96020001,14.7683115 7.68010001,14.7683115 C5.33305,14.7683115 3.42340001,12.8535563 3.42340001,10.5009524 C3.42340001,8.14679459 5.33300001,6.23203946 7.68010001,6.23203946 C9.45720002,6.23203946 10.8909,7.19074535 11.6138,8.86359341 C11.6205501,8.88018505 12.3412,10.5707777 12.97445,12.0190621 C13.34865,12.8739575 13.64615,13.3959676 14.6288,13.4291508 C15.5663001,13.4612814 16.25375,12.9121534 16.25375,12.1484869 C16.25375,11.4691321 15.8320501,11.3003585 14.8803,10.98216 C13.2365,10.4397989 12.34495,9.88605929 12.34495,8.51817658 C12.34495,7.1809207 13.26665,6.31615054 14.692,6.31615054 C15.62875,6.31615054 16.3155,6.7286858 16.79215,7.5768142 C16.80495,7.60062396 16.8079001,7.62814302 16.8004001,7.65420843 C16.7929,7.68027384 16.7748,7.70212868 16.7507001,7.713808 L15.86145,8.16900031 C15.8178001,8.19200805 15.7643,8.17807308 15.73565,8.13847371 C15.43295,7.71345711 15.0956,7.52513451 14.6423,7.52513451 C14.05125,7.52513451 13.6220001,7.92899802 13.6220001,8.48649708 C13.6220001,9.17382194 14.1529001,9.34144259 15.0339,9.61923972 C15.14915,9.65578139 15.26955,9.69397731 15.39385,9.73432853 C16.7763,10.1865133 17.57675,10.7311301 17.57675,12.1836251 C17.57685,13.629654 16.3389,14.7204413 14.69735,14.7204413 Z"></path>
</svg>',

		'linkedin'    => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path>
</svg>',

		'mail'        => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M20,4H4C2.895,4,2,4.895,2,6v12c0,1.105,0.895,2,2,2h16c1.105,0,2-0.895,2-2V6C22,4.895,21.105,4,20,4z M20,8.236l-8,4.882 L4,8.236V6h16V8.236z"></path>
</svg>',

		'meetup'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M19.24775,14.722a3.57032,3.57032,0,0,1-2.94457,3.52073,3.61886,3.61886,0,0,1-.64652.05634c-.07314-.0008-.10187.02846-.12507.09547A2.38881,2.38881,0,0,1,13.49453,20.094a2.33092,2.33092,0,0,1-1.827-.50716.13635.13635,0,0,0-.19878-.00408,3.191,3.191,0,0,1-2.104.60248,3.26309,3.26309,0,0,1-3.00324-2.71993,2.19076,2.19076,0,0,1-.03512-.30865c-.00156-.08579-.03413-.1189-.11608-.13493a2.86421,2.86421,0,0,1-1.23189-.56111,2.945,2.945,0,0,1-1.166-2.05749,2.97484,2.97484,0,0,1,.87524-2.50774.112.112,0,0,0,.02091-.16107,2.7213,2.7213,0,0,1-.36648-1.48A2.81256,2.81256,0,0,1,6.57673,7.58838a.35764.35764,0,0,0,.28869-.22819,4.2208,4.2208,0,0,1,6.02892-1.90111.25161.25161,0,0,0,.22023.0243,3.65608,3.65608,0,0,1,3.76031.90678A3.57244,3.57244,0,0,1,17.95918,8.626a2.97339,2.97339,0,0,1,.01829.57356.10637.10637,0,0,0,.0853.12792,1.97669,1.97669,0,0,1,1.27939,1.33733,2.00266,2.00266,0,0,1-.57112,2.12652c-.05284.05166-.04168.08328-.01173.13489A3.51189,3.51189,0,0,1,19.24775,14.722Zm-6.35959-.27836a1.6984,1.6984,0,0,0,1.14556,1.61113,3.82039,3.82039,0,0,0,1.036.17935,1.46888,1.46888,0,0,0,.73509-.12255.44082.44082,0,0,0,.26057-.44274.45312.45312,0,0,0-.29211-.43375.97191.97191,0,0,0-.20678-.063c-.21326-.03806-.42754-.0701-.63973-.11215a.54787.54787,0,0,1-.50172-.60926,2.75864,2.75864,0,0,1,.1773-.901c.1763-.535.414-1.045.64183-1.55913A12.686,12.686,0,0,0,15.85,10.47863a1.58461,1.58461,0,0,0,.04861-.87208,1.04531,1.04531,0,0,0-.85432-.83981,1.60658,1.60658,0,0,0-1.23654.16594.27593.27593,0,0,1-.36286-.03413c-.085-.0747-.16594-.15379-.24918-.23055a.98682.98682,0,0,0-1.33577-.04933,6.1468,6.1468,0,0,1-.4989.41615.47762.47762,0,0,1-.51535.03566c-.17448-.09307-.35512-.175-.53531-.25665a1.74949,1.74949,0,0,0-.56476-.2016,1.69943,1.69943,0,0,0-1.61654.91787,8.05815,8.05815,0,0,0-.32952.80126c-.45471,1.2557-.82507,2.53825-1.20838,3.81639a1.24151,1.24151,0,0,0,.51532,1.44389,1.42659,1.42659,0,0,0,1.22008.17166,1.09728,1.09728,0,0,0,.66994-.69764c.44145-1.04111.839-2.09989,1.25981-3.14926.11581-.28876.22792-.57874.35078-.86438a.44548.44548,0,0,1,.69189-.19539.50521.50521,0,0,1,.15044.43836,1.75625,1.75625,0,0,1-.14731.50453c-.27379.69219-.55265,1.38236-.82766,2.074a2.0836,2.0836,0,0,0-.14038.42876.50719.50719,0,0,0,.27082.57722.87236.87236,0,0,0,.66145.02739.99137.99137,0,0,0,.53406-.532q.61571-1.20914,1.228-2.42031.28423-.55863.57585-1.1133a.87189.87189,0,0,1,.29055-.35253.34987.34987,0,0,1,.37634-.01265.30291.30291,0,0,1,.12434.31459.56716.56716,0,0,1-.04655.1915c-.05318.12739-.10286.25669-.16183.38156-.34118.71775-.68754,1.43273-1.02568,2.152A2.00213,2.00213,0,0,0,12.88816,14.44366Zm4.78568,5.28972a.88573.88573,0,0,0-1.77139.00465.8857.8857,0,0,0,1.77139-.00465Zm-14.83838-7.296a.84329.84329,0,1,0,.00827-1.68655.8433.8433,0,0,0-.00827,1.68655Zm10.366-9.43673a.83506.83506,0,1,0-.0091,1.67.83505.83505,0,0,0,.0091-1.67Zm6.85014,5.22a.71651.71651,0,0,0-1.433.0093.71656.71656,0,0,0,1.433-.0093ZM5.37528,6.17908A.63823.63823,0,1,0,6.015,5.54483.62292.62292,0,0,0,5.37528,6.17908Zm6.68214,14.80843a.54949.54949,0,1,0-.55052.541A.54556.54556,0,0,0,12.05742,20.98752Zm8.53235-8.49689a.54777.54777,0,0,0-.54027.54023.53327.53327,0,0,0,.532.52293.51548.51548,0,0,0,.53272-.5237A.53187.53187,0,0,0,20.58977,12.49063ZM7.82846,2.4715a.44927.44927,0,1,0,.44484.44766A.43821.43821,0,0,0,7.82846,2.4715Zm13.775,7.60492a.41186.41186,0,0,0-.40065.39623.40178.40178,0,0,0,.40168.40168A.38994.38994,0,0,0,22,10.48172.39946.39946,0,0,0,21.60349,10.07642ZM5.79193,17.96207a.40469.40469,0,0,0-.397-.39646.399.399,0,0,0-.396.405.39234.39234,0,0,0,.39939.389A.39857.39857,0,0,0,5.79193,17.96207Z"></path>
</svg>',

		'medium'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M20.962,7.257l-5.457,8.867l-3.923-6.375l3.126-5.08c0.112-0.182,0.319-0.286,0.527-0.286c0.05,0,0.1,0.008,0.149,0.02 c0.039,0.01,0.078,0.023,0.114,0.041l5.43,2.715l0.006,0.003c0.004,0.002,0.007,0.006,0.011,0.008 C20.971,7.191,20.98,7.227,20.962,7.257z M9.86,8.592v5.783l5.14,2.57L9.86,8.592z M15.772,17.331l4.231,2.115 C20.554,19.721,21,19.529,21,19.016V8.835L15.772,17.331z M8.968,7.178L3.665,4.527C3.569,4.479,3.478,4.456,3.395,4.456 C3.163,4.456,3,4.636,3,4.938v11.45c0,0.306,0.224,0.669,0.498,0.806l4.671,2.335c0.12,0.06,0.234,0.088,0.337,0.088 c0.29,0,0.494-0.225,0.494-0.602V7.231C9,7.208,8.988,7.188,8.968,7.178z"></path>
</svg>',

		'pinterest'   => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12.289,2C6.617,2,3.606,5.648,3.606,9.622c0,1.846,1.025,4.146,2.666,4.878c0.25,0.111,0.381,0.063,0.439-0.169 c0.044-0.175,0.267-1.029,0.365-1.428c0.032-0.128,0.017-0.237-0.091-0.362C6.445,11.911,6.01,10.75,6.01,9.668 c0-2.777,2.194-5.464,5.933-5.464c3.23,0,5.49,2.108,5.49,5.122c0,3.407-1.794,5.768-4.13,5.768c-1.291,0-2.257-1.021-1.948-2.277 c0.372-1.495,1.089-3.112,1.089-4.191c0-0.967-0.542-1.775-1.663-1.775c-1.319,0-2.379,1.309-2.379,3.059 c0,1.115,0.394,1.869,0.394,1.869s-1.302,5.279-1.54,6.261c-0.405,1.666,0.053,4.368,0.094,4.604 c0.021,0.126,0.167,0.169,0.25,0.063c0.129-0.165,1.699-2.419,2.142-4.051c0.158-0.59,0.817-2.995,0.817-2.995 c0.43,0.784,1.681,1.446,3.013,1.446c3.963,0,6.822-3.494,6.822-7.833C20.394,5.112,16.849,2,12.289,2"></path>
</svg>',

		'pocket'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M21.927,4.194C21.667,3.48,20.982,3,20.222,3h-0.01h-1.721H3.839C3.092,3,2.411,3.47,2.145,4.17 C2.066,4.378,2.026,4.594,2.026,4.814v6.035l0.069,1.2c0.29,2.73,1.707,5.115,3.899,6.778c0.039,0.03,0.079,0.059,0.119,0.089 l0.025,0.018c1.175,0.859,2.491,1.441,3.91,1.727c0.655,0.132,1.325,0.2,1.991,0.2c0.615,0,1.232-0.057,1.839-0.17 c0.073-0.014,0.145-0.028,0.219-0.044c0.02-0.004,0.042-0.012,0.064-0.023c1.359-0.297,2.621-0.864,3.753-1.691l0.025-0.018 c0.04-0.029,0.08-0.058,0.119-0.089c2.192-1.664,3.609-4.049,3.898-6.778l0.069-1.2V4.814C22.026,4.605,22,4.398,21.927,4.194z M17.692,10.481l-4.704,4.512c-0.266,0.254-0.608,0.382-0.949,0.382c-0.342,0-0.684-0.128-0.949-0.382l-4.705-4.512 C5.838,9.957,5.82,9.089,6.344,8.542c0.524-0.547,1.392-0.565,1.939-0.04l3.756,3.601l3.755-3.601 c0.547-0.524,1.415-0.506,1.939,0.04C18.256,9.089,18.238,9.956,17.692,10.481z"></path>
</svg>',

		'reddit'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M22,11.816c0-1.256-1.021-2.277-2.277-2.277c-0.593,0-1.122,0.24-1.526,0.614c-1.481-0.965-3.455-1.594-5.647-1.69 l1.171-3.702l3.18,0.748c0.008,1.028,0.846,1.862,1.876,1.862c1.035,0,1.877-0.842,1.877-1.878c0-1.035-0.842-1.877-1.877-1.877 c-0.769,0-1.431,0.466-1.72,1.13l-3.508-0.826c-0.203-0.047-0.399,0.067-0.46,0.261l-1.35,4.268 c-2.316,0.038-4.411,0.67-5.97,1.671C5.368,9.765,4.853,9.539,4.277,9.539C3.021,9.539,2,10.56,2,11.816 c0,0.814,0.433,1.523,1.078,1.925c-0.037,0.221-0.061,0.444-0.061,0.672c0,3.292,4.011,5.97,8.941,5.97s8.941-2.678,8.941-5.97 c0-0.214-0.02-0.424-0.053-0.632C21.533,13.39,22,12.661,22,11.816z M18.776,4.394c0.606,0,1.1,0.493,1.1,1.1s-0.493,1.1-1.1,1.1 s-1.1-0.494-1.1-1.1S18.169,4.394,18.776,4.394z M2.777,11.816c0-0.827,0.672-1.5,1.499-1.5c0.313,0,0.598,0.103,0.838,0.269 c-0.851,0.676-1.477,1.479-1.812,2.36C2.983,12.672,2.777,12.27,2.777,11.816z M11.959,19.606c-4.501,0-8.164-2.329-8.164-5.193 S7.457,9.22,11.959,9.22s8.164,2.329,8.164,5.193S16.46,19.606,11.959,19.606z M20.636,13.001c-0.326-0.89-0.948-1.701-1.797-2.384 c0.248-0.186,0.55-0.301,0.883-0.301c0.827,0,1.5,0.673,1.5,1.5C21.223,12.299,20.992,12.727,20.636,13.001z M8.996,14.704 c-0.76,0-1.397-0.616-1.397-1.376c0-0.76,0.637-1.397,1.397-1.397c0.76,0,1.376,0.637,1.376,1.397 C10.372,14.088,9.756,14.704,8.996,14.704z M16.401,13.328c0,0.76-0.616,1.376-1.376,1.376c-0.76,0-1.399-0.616-1.399-1.376 c0-0.76,0.639-1.397,1.399-1.397C15.785,11.931,16.401,12.568,16.401,13.328z M15.229,16.708c0.152,0.152,0.152,0.398,0,0.55 c-0.674,0.674-1.727,1.002-3.219,1.002c-0.004,0-0.007-0.002-0.011-0.002c-0.004,0-0.007,0.002-0.011,0.002 c-1.492,0-2.544-0.328-3.218-1.002c-0.152-0.152-0.152-0.398,0-0.55c0.152-0.152,0.399-0.151,0.55,0 c0.521,0.521,1.394,0.775,2.669,0.775c0.004,0,0.007,0.002,0.011,0.002c0.004,0,0.007-0.002,0.011-0.002 c1.275,0,2.148-0.253,2.669-0.775C14.831,16.556,15.078,16.556,15.229,16.708z"></path>
</svg>',

		'skype'       => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M10.113,2.699c0.033-0.006,0.067-0.013,0.1-0.02c0.033,0.017,0.066,0.033,0.098,0.051L10.113,2.699z M2.72,10.223 c-0.006,0.034-0.011,0.069-0.017,0.103c0.018,0.032,0.033,0.064,0.051,0.095L2.72,10.223z M21.275,13.771 c0.007-0.035,0.011-0.071,0.018-0.106c-0.018-0.031-0.033-0.064-0.052-0.095L21.275,13.771z M13.563,21.199 c0.032,0.019,0.065,0.035,0.096,0.053c0.036-0.006,0.071-0.011,0.105-0.017L13.563,21.199z M22,16.386 c0,1.494-0.581,2.898-1.637,3.953c-1.056,1.057-2.459,1.637-3.953,1.637c-0.967,0-1.914-0.251-2.75-0.725 c0.036-0.006,0.071-0.011,0.105-0.017l-0.202-0.035c0.032,0.019,0.065,0.035,0.096,0.053c-0.543,0.096-1.099,0.147-1.654,0.147 c-1.275,0-2.512-0.25-3.676-0.743c-1.125-0.474-2.135-1.156-3.002-2.023c-0.867-0.867-1.548-1.877-2.023-3.002 c-0.493-1.164-0.743-2.401-0.743-3.676c0-0.546,0.049-1.093,0.142-1.628c0.018,0.032,0.033,0.064,0.051,0.095L2.72,10.223 c-0.006,0.034-0.011,0.069-0.017,0.103C2.244,9.5,2,8.566,2,7.615c0-1.493,0.582-2.898,1.637-3.953 c1.056-1.056,2.46-1.638,3.953-1.638c0.915,0,1.818,0.228,2.622,0.655c-0.033,0.007-0.067,0.013-0.1,0.02l0.199,0.031 c-0.032-0.018-0.066-0.034-0.098-0.051c0.002,0,0.003-0.001,0.004-0.001c0.586-0.112,1.187-0.169,1.788-0.169 c1.275,0,2.512,0.249,3.676,0.742c1.124,0.476,2.135,1.156,3.002,2.024c0.868,0.867,1.548,1.877,2.024,3.002 c0.493,1.164,0.743,2.401,0.743,3.676c0,0.575-0.054,1.15-0.157,1.712c-0.018-0.031-0.033-0.064-0.052-0.095l0.034,0.201 c0.007-0.035,0.011-0.071,0.018-0.106C21.754,14.494,22,15.432,22,16.386z M16.817,14.138c0-1.331-0.613-2.743-3.033-3.282 l-2.209-0.49c-0.84-0.192-1.807-0.444-1.807-1.237c0-0.794,0.679-1.348,1.903-1.348c2.468,0,2.243,1.696,3.468,1.696 c0.645,0,1.209-0.379,1.209-1.031c0-1.521-2.435-2.663-4.5-2.663c-2.242,0-4.63,0.952-4.63,3.488c0,1.221,0.436,2.521,2.839,3.123 l2.984,0.745c0.903,0.223,1.129,0.731,1.129,1.189c0,0.762-0.758,1.507-2.129,1.507c-2.679,0-2.307-2.062-3.743-2.062 c-0.645,0-1.113,0.444-1.113,1.078c0,1.236,1.501,2.886,4.856,2.886C15.236,17.737,16.817,16.199,16.817,14.138z"></path>
</svg>',

		'slideshare'  => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M11.738,10.232a2.142,2.142,0,0,1-.721,1.619,2.556,2.556,0,0,1-3.464,0,2.183,2.183,0,0,1,0-3.243,2.572,2.572,0,0,1,3.464,0A2.136,2.136,0,0,1,11.738,10.232Zm5.7,0a2.15,2.15,0,0,1-.715,1.619,2.563,2.563,0,0,1-3.469,0,2.183,2.183,0,0,1,0-3.243,2.58,2.58,0,0,1,3.469,0A2.144,2.144,0,0,1,17.439,10.232Zm2.555,2.045V4.7a2.128,2.128,0,0,0-.363-1.4,1.614,1.614,0,0,0-1.261-.415H5.742a1.656,1.656,0,0,0-1.278.386A2.246,2.246,0,0,0,4.129,4.7v7.643a8.212,8.212,0,0,0,1,.454q.516.193.92.318a6.847,6.847,0,0,0,.92.21q.516.085.806.125a6.615,6.615,0,0,0,.795.045l.665.006q.16,0,.642-.023t.506-.023a1.438,1.438,0,0,1,1.079.307,1.134,1.134,0,0,0,.114.1,7.215,7.215,0,0,0,.693.579q.079-1.033,1.34-.988.057,0,.415.017l.488.023q.13.006.517.011t.6-.011l.619-.051a5.419,5.419,0,0,0,.693-.1l.7-.153a5.353,5.353,0,0,0,.761-.221q.345-.131.766-.307a8.727,8.727,0,0,0,.818-.392Zm1.851-.057a10.4,10.4,0,0,1-4.225,2.862,6.5,6.5,0,0,1-.261,5.281,3.524,3.524,0,0,1-2.078,1.681,2.452,2.452,0,0,1-2.067-.17,1.915,1.915,0,0,1-.931-1.863l-.011-3.7V16.3l-.279-.068q-.188-.045-.267-.057l-.011,3.839a1.9,1.9,0,0,1-.943,1.863,2.481,2.481,0,0,1-2.078.17,3.519,3.519,0,0,1-2.067-1.7,6.546,6.546,0,0,1-.25-5.258A10.4,10.4,0,0,1,2.152,12.22a.56.56,0,0,1-.045-.715q.238-.3.681.011l.125.079a.767.767,0,0,1,.125.091V3.8a1.987,1.987,0,0,1,.534-1.4,1.7,1.7,0,0,1,1.295-.579H19.141a1.7,1.7,0,0,1,1.295.579,1.985,1.985,0,0,1,.534,1.4v7.882l.238-.17q.443-.307.681-.011a.56.56,0,0,1-.045.715Z"></path>
</svg>',

		'snapchat'    => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12.065,2a5.526,5.526,0,0,1,3.132.892A5.854,5.854,0,0,1,17.326,5.4a5.821,5.821,0,0,1,.351,2.33q0,.612-.117,2.487a.809.809,0,0,0,.365.091,1.93,1.93,0,0,0,.664-.176,1.93,1.93,0,0,1,.664-.176,1.3,1.3,0,0,1,.729.234.7.7,0,0,1,.351.6.839.839,0,0,1-.41.7,2.732,2.732,0,0,1-.9.41,3.192,3.192,0,0,0-.9.378.728.728,0,0,0-.41.618,1.575,1.575,0,0,0,.156.56,6.9,6.9,0,0,0,1.334,1.953,5.6,5.6,0,0,0,1.881,1.315,5.875,5.875,0,0,0,1.042.3.42.42,0,0,1,.365.456q0,.911-2.852,1.341a1.379,1.379,0,0,0-.143.507,1.8,1.8,0,0,1-.182.605.451.451,0,0,1-.429.241,5.878,5.878,0,0,1-.807-.085,5.917,5.917,0,0,0-.833-.085,4.217,4.217,0,0,0-.807.065,2.42,2.42,0,0,0-.82.293,6.682,6.682,0,0,0-.755.5q-.351.267-.755.527a3.886,3.886,0,0,1-.989.436A4.471,4.471,0,0,1,11.831,22a4.307,4.307,0,0,1-1.256-.176,3.784,3.784,0,0,1-.976-.436q-.4-.26-.749-.527a6.682,6.682,0,0,0-.755-.5,2.422,2.422,0,0,0-.807-.293,4.432,4.432,0,0,0-.82-.065,5.089,5.089,0,0,0-.853.1,5,5,0,0,1-.762.1.474.474,0,0,1-.456-.241,1.819,1.819,0,0,1-.182-.618,1.411,1.411,0,0,0-.143-.521q-2.852-.429-2.852-1.341a.42.42,0,0,1,.365-.456,5.793,5.793,0,0,0,1.042-.3,5.524,5.524,0,0,0,1.881-1.315,6.789,6.789,0,0,0,1.334-1.953A1.575,1.575,0,0,0,6,12.9a.728.728,0,0,0-.41-.618,3.323,3.323,0,0,0-.9-.384,2.912,2.912,0,0,1-.9-.41.814.814,0,0,1-.41-.684.71.71,0,0,1,.338-.593,1.208,1.208,0,0,1,.716-.241,1.976,1.976,0,0,1,.625.169,2.008,2.008,0,0,0,.69.169.919.919,0,0,0,.416-.091q-.117-1.849-.117-2.474A5.861,5.861,0,0,1,6.385,5.4,5.516,5.516,0,0,1,8.625,2.819,7.075,7.075,0,0,1,12.062,2Z"></path>
</svg>',

		'soundcloud'  => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M8.9,16.1L9,14L8.9,9.5c0-0.1,0-0.1-0.1-0.1c0,0-0.1-0.1-0.1-0.1c-0.1,0-0.1,0-0.1,0.1c0,0-0.1,0.1-0.1,0.1L8.3,14l0.1,2.1 c0,0.1,0,0.1,0.1,0.1c0,0,0.1,0.1,0.1,0.1C8.8,16.3,8.9,16.3,8.9,16.1z M11.4,15.9l0.1-1.8L11.4,9c0-0.1,0-0.2-0.1-0.2 c0,0-0.1,0-0.1,0s-0.1,0-0.1,0c-0.1,0-0.1,0.1-0.1,0.2l0,0.1l-0.1,5c0,0,0,0.7,0.1,2v0c0,0.1,0,0.1,0.1,0.1c0.1,0.1,0.1,0.1,0.2,0.1 c0.1,0,0.1,0,0.2-0.1c0.1,0,0.1-0.1,0.1-0.2L11.4,15.9z M2.4,12.9L2.5,14l-0.2,1.1c0,0.1,0,0.1-0.1,0.1c0,0-0.1,0-0.1-0.1L2.1,14 l0.1-1.1C2.2,12.9,2.3,12.9,2.4,12.9C2.3,12.9,2.4,12.9,2.4,12.9z M3.1,12.2L3.3,14l-0.2,1.8c0,0.1,0,0.1-0.1,0.1 c-0.1,0-0.1,0-0.1-0.1L2.8,14L3,12.2C3,12.2,3,12.2,3.1,12.2C3.1,12.2,3.1,12.2,3.1,12.2z M3.9,11.9L4.1,14l-0.2,2.1 c0,0.1,0,0.1-0.1,0.1c-0.1,0-0.1,0-0.1-0.1L3.5,14l0.2-2.1c0-0.1,0-0.1,0.1-0.1C3.9,11.8,3.9,11.8,3.9,11.9z M4.7,11.9L4.9,14 l-0.2,2.1c0,0.1-0.1,0.1-0.1,0.1c-0.1,0-0.1,0-0.1-0.1L4.3,14l0.2-2.2c0-0.1,0-0.1,0.1-0.1C4.7,11.7,4.7,11.8,4.7,11.9z M5.6,12 l0.2,2l-0.2,2.1c0,0.1-0.1,0.1-0.1,0.1c0,0-0.1,0-0.1,0c0,0,0-0.1,0-0.1L5.1,14l0.2-2c0,0,0-0.1,0-0.1s0.1,0,0.1,0 C5.5,11.9,5.5,11.9,5.6,12L5.6,12z M6.4,10.7L6.6,14l-0.2,2.1c0,0,0,0.1,0,0.1c0,0-0.1,0-0.1,0c-0.1,0-0.1-0.1-0.2-0.2L5.9,14 l0.2-3.3c0-0.1,0.1-0.2,0.2-0.2c0,0,0.1,0,0.1,0C6.4,10.7,6.4,10.7,6.4,10.7z M7.2,10l0.2,4.1l-0.2,2.1c0,0,0,0.1,0,0.1 c0,0-0.1,0-0.1,0c-0.1,0-0.2-0.1-0.2-0.2l-0.1-2.1L6.8,10c0-0.1,0.1-0.2,0.2-0.2c0,0,0.1,0,0.1,0S7.2,9.9,7.2,10z M8,9.6L8.2,14 L8,16.1c0,0.1-0.1,0.2-0.2,0.2c-0.1,0-0.2-0.1-0.2-0.2L7.5,14l0.1-4.4c0-0.1,0-0.1,0.1-0.1c0,0,0.1-0.1,0.1-0.1c0.1,0,0.1,0,0.1,0.1 C8,9.6,8,9.6,8,9.6z M11.4,16.1L11.4,16.1L11.4,16.1z M9.7,9.6L9.8,14l-0.1,2.1c0,0.1,0,0.1-0.1,0.2s-0.1,0.1-0.2,0.1 c-0.1,0-0.1,0-0.1-0.1s-0.1-0.1-0.1-0.2L9.2,14l0.1-4.4c0-0.1,0-0.1,0.1-0.2s0.1-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0.1S9.7,9.5,9.7,9.6 L9.7,9.6z M10.6,9.8l0.1,4.3l-0.1,2c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0.1-0.2,0.1c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2L10,14 l0.1-4.3c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0.1S10.6,9.7,10.6,9.8z M12.4,14l-0.1,2c0,0.1,0,0.1-0.1,0.2 c-0.1,0.1-0.1,0.1-0.2,0.1c-0.1,0-0.1,0-0.2-0.1c-0.1-0.1-0.1-0.1-0.1-0.2l-0.1-1l-0.1-1l0.1-5.5v0c0-0.1,0-0.2,0.1-0.2 c0.1,0,0.1-0.1,0.2-0.1c0,0,0.1,0,0.1,0c0.1,0,0.1,0.1,0.1,0.2L12.4,14z M22.1,13.9c0,0.7-0.2,1.3-0.7,1.7c-0.5,0.5-1.1,0.7-1.7,0.7 h-6.8c-0.1,0-0.1,0-0.2-0.1c-0.1-0.1-0.1-0.1-0.1-0.2V8.2c0-0.1,0.1-0.2,0.2-0.3c0.5-0.2,1-0.3,1.6-0.3c1.1,0,2.1,0.4,2.9,1.1 c0.8,0.8,1.3,1.7,1.4,2.8c0.3-0.1,0.6-0.2,1-0.2c0.7,0,1.3,0.2,1.7,0.7C21.8,12.6,22.1,13.2,22.1,13.9L22.1,13.9z"></path>
</svg>',

		'spotify'     => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12,2C6.477,2,2,6.477,2,12c0,5.523,4.477,10,10,10c5.523,0,10-4.477,10-10C22,6.477,17.523,2,12,2 M16.586,16.424 c-0.18,0.295-0.563,0.387-0.857,0.207c-2.348-1.435-5.304-1.76-8.785-0.964c-0.335,0.077-0.67-0.133-0.746-0.469 c-0.077-0.335,0.132-0.67,0.469-0.746c3.809-0.871,7.077-0.496,9.713,1.115C16.673,15.746,16.766,16.13,16.586,16.424 M17.81,13.7 c-0.226,0.367-0.706,0.482-1.072,0.257c-2.687-1.652-6.785-2.131-9.965-1.166C6.36,12.917,5.925,12.684,5.8,12.273 C5.675,11.86,5.908,11.425,6.32,11.3c3.632-1.102,8.147-0.568,11.234,1.328C17.92,12.854,18.035,13.335,17.81,13.7 M17.915,10.865 c-3.223-1.914-8.54-2.09-11.618-1.156C5.804,9.859,5.281,9.58,5.131,9.086C4.982,8.591,5.26,8.069,5.755,7.919 c3.532-1.072,9.404-0.865,13.115,1.338c0.445,0.264,0.59,0.838,0.327,1.282C18.933,10.983,18.359,11.129,17.915,10.865"></path>
</svg>',

		'stumbleupon' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12,4.294c-2.469,0-4.471,2.002-4.471,4.471v6.353c0,0.585-0.474,1.059-1.059,1.059c-0.585,0-1.059-0.474-1.059-1.059 v-2.824H2v2.941c0,2.469,2.002,4.471,4.471,4.471c2.469,0,4.471-2.002,4.471-4.471V8.765c0-0.585,0.474-1.059,1.059-1.059 s1.059,0.474,1.059,1.059v1.294l1.412,0.647l2-0.647V8.765C16.471,6.296,14.469,4.294,12,4.294z M13.059,12.353v2.882 c0,2.469,2.002,4.471,4.471,4.471S22,17.704,22,15.235v-2.824h-3.412v2.824c0,0.585-0.474,1.059-1.059,1.059 c-0.585,0-1.059-0.474-1.059-1.059v-2.882l-2,0.647L13.059,12.353z"></path>
</svg>',

		'tumblr'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M16.749,17.396c-0.357,0.17-1.041,0.319-1.551,0.332c-1.539,0.041-1.837-1.081-1.85-1.896V9.847h3.861V6.937h-3.847V2.039 c0,0-2.77,0-2.817,0c-0.046,0-0.127,0.041-0.138,0.144c-0.165,1.499-0.867,4.13-3.783,5.181v2.484h1.945v6.282 c0,2.151,1.587,5.206,5.775,5.135c1.413-0.024,2.982-0.616,3.329-1.126L16.749,17.396z"></path>
</svg>',

		'twitch'      => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M16.499,8.089h-1.636v4.91h1.636V8.089z M12,8.089h-1.637v4.91H12V8.089z M4.228,3.178L3,6.451v13.092h4.499V22h2.456 l2.454-2.456h3.681L21,14.636V3.178H4.228z M19.364,13.816l-2.864,2.865H12l-2.453,2.453V16.68H5.863V4.814h13.501V13.816z"></path>
</svg>',

		'twitter'     => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M22.23,5.924c-0.736,0.326-1.527,0.547-2.357,0.646c0.847-0.508,1.498-1.312,1.804-2.27 c-0.793,0.47-1.671,0.812-2.606,0.996C18.324,4.498,17.257,4,16.077,4c-2.266,0-4.103,1.837-4.103,4.103 c0,0.322,0.036,0.635,0.106,0.935C8.67,8.867,5.647,7.234,3.623,4.751C3.27,5.357,3.067,6.062,3.067,6.814 c0,1.424,0.724,2.679,1.825,3.415c-0.673-0.021-1.305-0.206-1.859-0.513c0,0.017,0,0.034,0,0.052c0,1.988,1.414,3.647,3.292,4.023 c-0.344,0.094-0.707,0.144-1.081,0.144c-0.264,0-0.521-0.026-0.772-0.074c0.522,1.63,2.038,2.816,3.833,2.85 c-1.404,1.1-3.174,1.756-5.096,1.756c-0.331,0-0.658-0.019-0.979-0.057c1.816,1.164,3.973,1.843,6.29,1.843 c7.547,0,11.675-6.252,11.675-11.675c0-0.178-0.004-0.355-0.012-0.531C20.985,7.47,21.68,6.747,22.23,5.924z"></path>
</svg>',

		'vimeo'       => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M22.396,7.164c-0.093,2.026-1.507,4.799-4.245,8.32C15.322,19.161,12.928,21,10.97,21c-1.214,0-2.24-1.119-3.079-3.359 c-0.56-2.053-1.119-4.106-1.68-6.159C5.588,9.243,4.921,8.122,4.206,8.122c-0.156,0-0.701,0.328-1.634,0.98L1.594,7.841 c1.027-0.902,2.04-1.805,3.037-2.708C6.001,3.95,7.03,3.327,7.715,3.264c1.619-0.156,2.616,0.951,2.99,3.321 c0.404,2.557,0.685,4.147,0.841,4.769c0.467,2.121,0.981,3.181,1.542,3.181c0.435,0,1.09-0.688,1.963-2.065 c0.871-1.376,1.338-2.422,1.401-3.142c0.125-1.187-0.343-1.782-1.401-1.782c-0.498,0-1.012,0.115-1.541,0.341 c1.023-3.35,2.977-4.977,5.862-4.884C21.511,3.066,22.52,4.453,22.396,7.164z"></path>
</svg>',

		'vk'          => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M22,7.1c0.2,0.4-0.4,1.5-1.6,3.1c-0.2,0.2-0.4,0.5-0.7,0.9c-0.5,0.7-0.9,1.1-0.9,1.4c-0.1,0.3-0.1,0.6,0.1,0.8 c0.1,0.1,0.4,0.4,0.8,0.9h0l0,0c1,0.9,1.6,1.7,2,2.3c0,0,0,0.1,0.1,0.1c0,0.1,0,0.1,0.1,0.3c0,0.1,0,0.2,0,0.4 c0,0.1-0.1,0.2-0.3,0.3c-0.1,0.1-0.4,0.1-0.6,0.1l-2.7,0c-0.2,0-0.4,0-0.6-0.1c-0.2-0.1-0.4-0.1-0.5-0.2l-0.2-0.1 c-0.2-0.1-0.5-0.4-0.7-0.7s-0.5-0.6-0.7-0.8c-0.2-0.2-0.4-0.4-0.6-0.6C14.8,15,14.6,15,14.4,15c0,0,0,0-0.1,0c0,0-0.1,0.1-0.2,0.2 c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.1-0.1,0.3-0.2,0.5c-0.1,0.2-0.1,0.5-0.1,0.8c0,0.1,0,0.2,0,0.3c0,0.1-0.1,0.2-0.1,0.2l0,0.1 c-0.1,0.1-0.3,0.2-0.6,0.2h-1.2c-0.5,0-1,0-1.5-0.2c-0.5-0.1-1-0.3-1.4-0.6s-0.7-0.5-1.1-0.7s-0.6-0.4-0.7-0.6l-0.3-0.3 c-0.1-0.1-0.2-0.2-0.3-0.3s-0.4-0.5-0.7-0.9s-0.7-1-1.1-1.6c-0.4-0.6-0.8-1.3-1.3-2.2C2.9,9.4,2.5,8.5,2.1,7.5C2,7.4,2,7.3,2,7.2 c0-0.1,0-0.1,0-0.2l0-0.1c0.1-0.1,0.3-0.2,0.6-0.2l2.9,0c0.1,0,0.2,0,0.2,0.1S5.9,6.9,5.9,7L6,7c0.1,0.1,0.2,0.2,0.3,0.3 C6.4,7.7,6.5,8,6.7,8.4C6.9,8.8,7,9,7.1,9.2l0.2,0.3c0.2,0.4,0.4,0.8,0.6,1.1c0.2,0.3,0.4,0.5,0.5,0.7s0.3,0.3,0.4,0.4 c0.1,0.1,0.3,0.1,0.4,0.1c0.1,0,0.2,0,0.3-0.1c0,0,0,0,0.1-0.1c0,0,0.1-0.1,0.1-0.2c0.1-0.1,0.1-0.3,0.1-0.5c0-0.2,0.1-0.5,0.1-0.8 c0-0.4,0-0.8,0-1.3c0-0.3,0-0.5-0.1-0.8c0-0.2-0.1-0.4-0.1-0.5L9.6,7.6C9.4,7.3,9.1,7.2,8.7,7.1C8.6,7.1,8.6,7,8.7,6.9 C8.9,6.7,9,6.6,9.1,6.5c0.4-0.2,1.2-0.3,2.5-0.3c0.6,0,1,0.1,1.4,0.1c0.1,0,0.3,0.1,0.3,0.1c0.1,0.1,0.2,0.1,0.2,0.3 c0,0.1,0.1,0.2,0.1,0.3s0,0.3,0,0.5c0,0.2,0,0.4,0,0.6c0,0.2,0,0.4,0,0.7c0,0.3,0,0.6,0,0.9c0,0.1,0,0.2,0,0.4c0,0.2,0,0.4,0,0.5 c0,0.1,0,0.3,0,0.4s0.1,0.3,0.1,0.4c0.1,0.1,0.1,0.2,0.2,0.3c0.1,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3-0.1c0.1-0.1,0.2-0.2,0.4-0.4 s0.3-0.4,0.5-0.7c0.2-0.3,0.5-0.7,0.7-1.1c0.4-0.7,0.8-1.5,1.1-2.3c0-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1-0.1,0.1-0.1l0,0l0.1,0 c0,0,0,0,0.1,0s0.2,0,0.2,0l3,0c0.3,0,0.5,0,0.7,0S21.9,7,21.9,7L22,7.1z"></path>
</svg>',

		'wordpress'   => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12.158,12.786L9.46,20.625c0.806,0.237,1.657,0.366,2.54,0.366c1.047,0,2.051-0.181,2.986-0.51 c-0.024-0.038-0.046-0.079-0.065-0.124L12.158,12.786z M3.009,12c0,3.559,2.068,6.634,5.067,8.092L3.788,8.341 C3.289,9.459,3.009,10.696,3.009,12z M18.069,11.546c0-1.112-0.399-1.881-0.741-2.48c-0.456-0.741-0.883-1.368-0.883-2.109 c0-0.826,0.627-1.596,1.51-1.596c0.04,0,0.078,0.005,0.116,0.007C16.472,3.904,14.34,3.009,12,3.009 c-3.141,0-5.904,1.612-7.512,4.052c0.211,0.007,0.41,0.011,0.579,0.011c0.94,0,2.396-0.114,2.396-0.114 C7.947,6.93,8.004,7.642,7.52,7.699c0,0-0.487,0.057-1.029,0.085l3.274,9.739l1.968-5.901l-1.401-3.838 C9.848,7.756,9.389,7.699,9.389,7.699C8.904,7.67,8.961,6.93,9.446,6.958c0,0,1.484,0.114,2.368,0.114 c0.94,0,2.397-0.114,2.397-0.114c0.485-0.028,0.542,0.684,0.057,0.741c0,0-0.488,0.057-1.029,0.085l3.249,9.665l0.897-2.996 C17.841,13.284,18.069,12.316,18.069,11.546z M19.889,7.686c0.039,0.286,0.06,0.593,0.06,0.924c0,0.912-0.171,1.938-0.684,3.22 l-2.746,7.94c2.673-1.558,4.47-4.454,4.47-7.771C20.991,10.436,20.591,8.967,19.889,7.686z M12,22C6.486,22,2,17.514,2,12 C2,6.486,6.486,2,12,2c5.514,0,10,4.486,10,10C22,17.514,17.514,22,12,22z"></path>
</svg>',

		'yelp'        => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M12.271,16.718v1.417q-.011,3.257-.067,3.4a.707.707,0,0,1-.569.446,4.637,4.637,0,0,1-2.024-.424A4.609,4.609,0,0,1,7.8,20.565a.844.844,0,0,1-.19-.4.692.692,0,0,1,.044-.29,3.181,3.181,0,0,1,.379-.524q.335-.412,2.019-2.409.011,0,.669-.781a.757.757,0,0,1,.44-.274.965.965,0,0,1,.552.039.945.945,0,0,1,.418.324.732.732,0,0,1,.139.468Zm-1.662-2.8a.783.783,0,0,1-.58.781l-1.339.435q-3.067.981-3.257.981a.711.711,0,0,1-.6-.4,2.636,2.636,0,0,1-.19-.836,9.134,9.134,0,0,1,.011-1.857,3.559,3.559,0,0,1,.335-1.389.659.659,0,0,1,.625-.357,22.629,22.629,0,0,1,2.253.859q.781.324,1.283.524l.937.379a.771.771,0,0,1,.4.34A.982.982,0,0,1,10.609,13.917Zm9.213,3.313a4.467,4.467,0,0,1-1.021,1.8,4.559,4.559,0,0,1-1.512,1.417.671.671,0,0,1-.7-.078q-.156-.112-2.052-3.2l-.524-.859a.761.761,0,0,1-.128-.513.957.957,0,0,1,.217-.513.774.774,0,0,1,.926-.29q.011.011,1.327.446,2.264.736,2.7.887a2.082,2.082,0,0,1,.524.229.673.673,0,0,1,.245.68Zm-7.5-7.049q.056,1.137-.6,1.361-.647.19-1.272-.792L6.237,4.08a.7.7,0,0,1,.212-.691,5.788,5.788,0,0,1,2.314-1,5.928,5.928,0,0,1,2.5-.352.681.681,0,0,1,.547.5q.034.2.245,3.407T12.327,10.181Zm7.384,1.2a.679.679,0,0,1-.29.658q-.167.112-3.67.959-.747.167-1.015.257l.011-.022a.769.769,0,0,1-.513-.044.914.914,0,0,1-.413-.357.786.786,0,0,1,0-.971q.011-.011.836-1.137,1.394-1.908,1.673-2.275a2.423,2.423,0,0,1,.379-.435A.7.7,0,0,1,17.435,8a4.482,4.482,0,0,1,1.372,1.489,4.81,4.81,0,0,1,.9,1.868v.034Z"></path>
</svg>',

		'youtube'     => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path>
</svg>',

	);

}
PK.�1\���b�i�i&autoptimizeCriticalCSSSettingsAjax.phpnu�[���<?php
/**
 * Critical CSS settings AJAX logic.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCriticalCSSSettingsAjax {
    /**
     * Critical CSS object.
     *
     * @var object
     */
    protected $criticalcss;
    
    public function __construct() {
        $this->criticalcss = autoptimize()->criticalcss();
        $this->run();
    }

    public function run() {
        // add filters.
        add_action( 'wp_ajax_fetch_critcss', array( $this, 'critcss_fetch_callback' ) );
        add_action( 'wp_ajax_save_critcss', array( $this, 'critcss_save_callback' ) );
        add_action( 'wp_ajax_rm_critcss', array( $this, 'critcss_rm_callback' ) );
        add_action( 'wp_ajax_rm_critcss_all', array( $this, 'critcss_rm_all_callback' ) );
        add_action( 'wp_ajax_ao_ccss_export', array( $this, 'ao_ccss_export_callback' ) );
        add_action( 'wp_ajax_ao_ccss_import', array( $this, 'ao_ccss_import_callback' ) );
        add_action( 'wp_ajax_ao_ccss_queuerunner', array( $this, 'ao_ccss_queuerunner_callback' ) );
        add_action( 'wp_ajax_ao_ccss_saverules', array( $this, 'ao_ccss_saverules_callback' ) );
    }

    public function critcss_fetch_callback() {
        // Ajax handler to obtain a critical CSS file from the filesystem.
        // Check referer.
        check_ajax_referer( 'fetch_critcss_nonce', 'critcss_fetch_nonce' );

        // Initialize error flag.
        $error = true;

        // Allow no content for MANUAL rules (as they may not exist just yet).
        if ( current_user_can( 'manage_options' ) && empty( $_POST['critcssfile'] ) ) {
            $content = '';
            $error   = false;
        } elseif ( current_user_can( 'manage_options' ) && $this->critcss_check_filename( $_POST['critcssfile'] ) ) {
            // Or check user permissios and filename.
            // Set file path and obtain its content.
            $critcssfile = AO_CCSS_DIR . strip_tags( $_POST['critcssfile'] );
            if ( file_exists( $critcssfile ) ) {
                $content = file_get_contents( $critcssfile );
                $error   = false;
            }
        }

        // Prepare response.
        if ( $error ) {
            $response['code']   = '500';
            $response['string'] = 'Error reading file ' . $critcssfile . '.';
        } else {
            $response['code']   = '200';
            $response['string'] = $content;
        }

        // Dispatch respose.
        echo json_encode( $response );

        // Close ajax request.
        wp_die();
    }

    public function critcss_save_callback() {
        $error    = false;
        $status   = false;
        $response = array();

        // Ajax handler to write a critical CSS to the filesystem
        // Check referer.
        check_ajax_referer( 'save_critcss_nonce', 'critcss_save_nonce' );

        // Allow empty contents for MANUAL rules (as they are fetched later).
        if ( current_user_can( 'manage_options' ) && empty( $_POST['critcssfile'] ) ) {
            $critcssfile = false;
            $status      = true;
        } elseif ( current_user_can( 'manage_options' ) && $this->critcss_check_filename( $_POST['critcssfile'] ) ) {
            // Or check user permissios and filename
            // Set critical CSS content.
            $critcsscontents = stripslashes( $_POST['critcsscontents'] );

            // If there is content and it's valid, write the file.
            if ( $critcsscontents && $this->criticalcss->check_contents( $critcsscontents ) ) {
                // Set file path and status.
                $critcssfile = AO_CCSS_DIR . strip_tags( $_POST['critcssfile'] );
                $status      = file_put_contents( $critcssfile, $critcsscontents, LOCK_EX );
                // Or set as error.
            } else {
                $error       = true;
                $critcssfile = 'CCSS content not acceptable.';
            }
            // Or just set an error.
        } else {
            $error       = true;
            $critcssfile = 'Not allowed or problem with CCSS filename.';
        }

        // Prepare response.
        if ( ! $status || $error ) {
            $response['code']   = '500';
            $response['string'] = 'Error saving file ' . $critcssfile . '.';
        } else {
            $response['code'] = '200';
            if ( $critcssfile ) {
                $response['string'] = 'File ' . $critcssfile . ' saved.';

                if ( true === apply_filters( 'autoptimize_filter_ccss_ajax_do_actions', true ) ) {
                    $rule_identifiers = $this->fetch_rule_from_ccssfile( $critcssfile );
                    if ( ! empty( $rule_identifiers ) && is_array( $rule_identifiers ) ) {
                        do_action( 'autoptimize_action_ccss_ajax_css_changed', $rule_identifiers[0], $critcssfile, $rule_identifiers[1] );
                    }
                }
            } else {
                $response['string'] = 'Empty content does not need to be saved.';
            }
        }

        // Dispatch respose.
        echo json_encode( $response );

        // Close ajax request.
        wp_die();
    }

    public function critcss_rm_callback() {
        // Ajax handler to delete a critical CSS from the filesystem
        // Check referer.
        check_ajax_referer( 'rm_critcss_nonce', 'critcss_rm_nonce' );

        // Initialize error and status flags.
        $error  = true;
        $status = false;

        // Allow no file for MANUAL rules (as they may not exist just yet).
        if ( current_user_can( 'manage_options' ) && empty( $_POST['critcssfile'] ) ) {
            $error = false;
        } elseif ( current_user_can( 'manage_options' ) && $this->critcss_check_filename( $_POST['critcssfile'] ) ) {
            // Or check user permissios and filename
            // Set file path and delete it.
            $critcssfile = AO_CCSS_DIR . strip_tags( $_POST['critcssfile'] );
            if ( file_exists( $critcssfile ) ) {
                $status = unlink( $critcssfile );
                $error  = false;
            }
        }

        // Prepare response.
        if ( $error ) {
            $response['code']   = '500';
            $response['string'] = 'Error removing file ' . $critcssfile . '.';
        } else {
            $response['code'] = '200';
            if ( $status ) {
                $response['string'] = 'File ' . $critcssfile . ' removed.';

                if ( true === apply_filters( 'autoptimize_filter_ccss_ajax_do_actions', true ) ) {
                    $rule_identifiers = $this->fetch_rule_from_ccssfile( $critcssfile );
                    if ( ! empty( $rule_identifiers ) && is_array( $rule_identifiers ) ) {
                        do_action( 'autoptimize_action_ccss_ajax_css_removed', $rule_identifiers[0], $critcssfile, $rule_identifiers[1] );
                    }
                }
            } else {
                $response['string'] = 'No file to be removed.';
            }
        }

        // Dispatch respose.
        echo json_encode( $response );

        // Close ajax request.
        wp_die();
    }

    public function critcss_rm_all_callback() {
        // Ajax handler to delete a critical CSS from the filesystem
        // Check referer.
        check_ajax_referer( 'rm_critcss_all_nonce', 'critcss_rm_all_nonce' );

        // Initialize error and status flags.
        $error  = true;
        $status = false;

        // Remove all ccss files on filesystem.
        if ( current_user_can( 'manage_options' ) ) {
            if ( file_exists( AO_CCSS_DIR ) && is_dir( AO_CCSS_DIR ) ) {
                array_map( 'unlink', glob( AO_CCSS_DIR . 'ccss_*.css', GLOB_BRACE ) );
                $error  = false;
                $status = true;

                if ( true === apply_filters( 'autoptimize_filter_ccss_ajax_do_actions', true ) ) {
                    do_action( 'autoptimize_action_ccss_ajax_all_css_removed' );
                }
            }
        }

        // Prepare response.
        if ( $error ) {
            $response['code']   = '500';
            $response['string'] = 'Error removing all critical CSS files.';
        } else {
            $response['code'] = '200';
            if ( $status ) {
                $response['string'] = 'Critical CSS Files removed.';
            } else {
                $response['string'] = 'No file removed.';
            }
        }

        // Dispatch respose.
        echo json_encode( $response );

        // Close ajax request.
        wp_die();
    }

    public function ao_ccss_export_callback() {
        // Ajax handler export settings
        // Check referer.
        check_ajax_referer( 'ao_ccss_export_nonce', 'ao_ccss_export_nonce' );

        if ( ! class_exists( 'ZipArchive' ) ) {
            $response['code'] = '500';
            $response['msg']  = 'PHP ZipArchive not present, cannot create zipfile';
            echo json_encode( $response );
            wp_die();
        }

        // Init array, get options and prepare the raw object.
        $settings                        = array();

        // CCSS settings.
        $settings['ccss']['rules']       = get_option( 'autoptimize_ccss_rules' );
        $settings['ccss']['additional']  = get_option( 'autoptimize_ccss_additional' );
        $settings['ccss']['viewport']    = get_option( 'autoptimize_ccss_viewport' );
        $settings['ccss']['finclude']    = get_option( 'autoptimize_ccss_finclude' );
        $settings['ccss']['rtimelimit']  = get_option( 'autoptimize_ccss_rtimelimit' );
        $settings['ccss']['noptimize']   = get_option( 'autoptimize_ccss_noptimize' );
        $settings['ccss']['debug']       = get_option( 'autoptimize_ccss_debug' );
        $settings['ccss']['key']         = get_option( 'autoptimize_ccss_key' );
        $settings['ccss']['deferjquery'] = get_option( 'autoptimize_ccss_deferjquery' );
        $settings['ccss']['domain']      = get_option( 'autoptimize_ccss_domain' );
        $settings['ccss']['forcepath']   = get_option( 'autoptimize_ccss_forcepath' );
        $settings['ccss']['loggedin']    = get_option( 'autoptimize_ccss_loggedin' );
        $settings['ccss']['rlimit']      = get_option( 'autoptimize_ccss_rlimit' );
        $settings['ccss']['unloadccss']  = get_option( 'autoptimize_ccss_unloadccss' );

        // JS settings.
        $settings['js']['root']                = get_option( 'autoptimize_js' );
        $settings['js']['aggregate']           = get_option( 'autoptimize_js_aggregate' );
        $settings['js']['defer_not_aggregate'] = get_option( 'autoptimize_js_defer_not_aggregate' );
        $settings['js']['defer_inline']        = get_option( 'autoptimize_js_defer_inline' );
        $settings['js']['exclude']             = get_option( 'autoptimize_js_exclude' );
        $settings['js']['forcehead']           = get_option( 'autoptimize_js_forcehead' );
        $settings['js']['justhead']            = get_option( 'autoptimize_js_justhead' );
        $settings['js']['trycatch']            = get_option( 'autoptimize_js_trycatch' );
        $settings['js']['include_inline']      = get_option( 'autoptimize_js_include_inline' );

        // CSS settings.
        $settings['css']['root']           = get_option( 'autoptimize_css' );
        $settings['css']['aggregate']      = get_option( 'autoptimize_css_aggregate' );
        $settings['css']['datauris']       = get_option( 'autoptimize_css_datauris' );
        $settings['css']['justhead']       = get_option( 'autoptimize_css_justhead' );
        $settings['css']['defer']          = get_option( 'autoptimize_css_defer' );
        $settings['css']['defer_inline']   = get_option( 'autoptimize_css_defer_inline' );
        $settings['css']['inline']         = get_option( 'autoptimize_css_inline' );
        $settings['css']['exclude']        = get_option( 'autoptimize_css_exclude' );
        $settings['css']['include_inline'] = get_option( 'autoptimize_css_include_inline' );

        // Others.
        $settings['other']['autoptimize_imgopt_settings']         = get_option( 'autoptimize_imgopt_settings' );
        $settings['other']['autoptimize_extra_settings']          = get_option( 'autoptimize_extra_settings' );
        $settings['other']['autoptimize_cache_fallback']          = get_option( 'autoptimize_cache_fallback' );
        $settings['other']['autoptimize_cache_nogzip']            = get_option( 'autoptimize_cache_nogzip' );
        $settings['other']['autoptimize_cdn_url']                 = get_option( 'autoptimize_cdn_url' );
        $settings['other']['autoptimize_enable_meta_ao_settings'] = get_option( 'autoptimize_enable_meta_ao_settings' );
        $settings['other']['autoptimize_enable_site_config']      = get_option( 'autoptimize_enable_site_config' );
        $settings['other']['autoptimize_html']                    = get_option( 'autoptimize_html' );
        $settings['other']['autoptimize_html_keepcomments']       = get_option( 'autoptimize_html_keepcomments' );
        $settings['other']['autoptimize_minify_excluded']         = get_option( 'autoptimize_minify_excluded' );
        $settings['other']['autoptimize_optimize_checkout']       = get_option( 'autoptimize_optimize_checkout' );
        $settings['other']['autoptimize_optimize_logged']         = get_option( 'autoptimize_optimize_logged' );

        if ( defined( 'AO_PRO_VERSION' ) ) {
            $settings['pro']['boosters']  = get_option( 'autoptimize_pro_boosters' );
            $settings['pro']['pagecache'] = get_option( 'autoptimize_pro_pagecache' );
        }

        // Initialize error flag.
        $error = true;

        // Check user permissions.
        if ( current_user_can( 'manage_options' ) ) {
            // Prepare settings file path and content.
            $exportfile = AO_CCSS_DIR . 'settings.json';
            $contents   = json_encode( $settings );
            $status     = file_put_contents( $exportfile, $contents, LOCK_EX );
            $error      = false;
        }

        // Prepare archive.
        $zipfile = AO_CCSS_DIR . str_replace( array( '.', '/' ), '_', parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ) . '_' . date( 'Ymd-H\hi' ) . '_ao_ccss_settings.zip'; // @codingStandardsIgnoreLine
        $file    = pathinfo( $zipfile, PATHINFO_BASENAME );
        $zip     = new ZipArchive();
        $ret     = $zip->open( $zipfile, ZipArchive::CREATE );
        if ( true !== $ret ) {
            $error = true;
        } else {
            $zip->addFile( AO_CCSS_DIR . 'settings.json', 'settings.json' );
            if ( file_exists( AO_CCSS_DIR . 'queue.json' ) ) {
                $zip->addFile( AO_CCSS_DIR . 'queue.json', 'queue.json' );
            }
            $options = array(
                'add_path'        => './',
                'remove_all_path' => true,
            );
            $zip->addGlob( AO_CCSS_DIR . '*.css', 0, $options );
            $zip->close();
        }

        // settings.json has been added to zipfile, so can be removed now.
        if ( file_exists( $exportfile ) ) {
            unlink( $exportfile );
        }

        // Prepare response.
        if ( ! $status || $error ) {
            $response['code'] = '500';
            $response['msg']  = 'Error saving file ' . $file . ', code: ' . $ret;
        } else {
            $response['code'] = '200';
            $response['msg']  = 'File ' . $file . ' saved.';
            $response['file'] = $file;
        }

        // Dispatch respose.
        echo json_encode( $response );

        // Close ajax request.
        wp_die();
    }

    public function ao_ccss_import_callback() {
        // Ajax handler import settings
        // Check referer.
        check_ajax_referer( 'ao_ccss_import_nonce', 'ao_ccss_import_nonce' );

        // Initialize error flag.
        $error = false;

        // Process an uploaded file with no errors.
        if ( current_user_can( 'manage_options' ) && ! $_FILES['file']['error'] && $_FILES['file']['size'] < 500001 && strpos( $_FILES['file']['name'], '.zip' ) === strlen( $_FILES['file']['name'] ) - 4 ) {
            // create tmp dir with hard guess name in AO_CCSS_DIR.
            $_secret_dir     = wp_hash( uniqid( md5( AUTOPTIMIZE_CACHE_URL ), true ) );
            $_import_tmp_dir = trailingslashit( AO_CCSS_DIR . $_secret_dir );
            mkdir( $_import_tmp_dir, 0774, true );

            // Save file to that tmp directory but give it our own name to prevent directory traversal risks when using original name.
            $zipfile = $_import_tmp_dir . uniqid( 'import_settings-', true ) . '.zip';
            move_uploaded_file( $_FILES['file']['tmp_name'], $zipfile );

            // Extract archive in the tmp directory.
            $zip = new ZipArchive;
            if ( $zip->open( $zipfile ) === true ) {
                // loop through all files in the zipfile.
                for ( $i = 0; $i < $zip->numFiles; $i++ ) { // @codingStandardsIgnoreLine
                    // but only extract known good files.
                    if ( preg_match( '/^settings\.json$|^\.\/ccss_[a-z0-9]{32}\.css$/', $zip->getNameIndex( $i ) ) > 0 ) {
                        $zip->extractTo( AO_CCSS_DIR, $zip->getNameIndex( $i ) );
                    }
                }
                $zip->close();
            } else {
                $error = 'could not extract';
            }

            // and remove temp. dir with all contents (the import-zipfile).
            $this->rrmdir( $_import_tmp_dir );

            if ( ! $error ) {
                // Archive extraction ok, continue importing settings from AO_CCSS_DIR.
                // Settings file.
                $importfile = AO_CCSS_DIR . 'settings.json';

                if ( file_exists( $importfile ) ) {
                    // Get settings and turn them into an object.
                    $settings = json_decode( file_get_contents( $importfile ), true );

                    // Update options from settings, but only for known options.
                    // CCSS.
                    foreach ( array( 'rules', 'additional', 'viewport', 'finclude', 'rtimelimit', 'noptimize', 'debug', 'key', 'deferjquery', 'domain', 'forcepath', 'loggedin', 'rlimit', 'unloadccss' ) as $ccss_setting ) {
                        if ( false === array_key_exists( 'ccss', $settings ) || false === array_key_exists( $ccss_setting, $settings['ccss'] ) ) {
                            continue;
                        } else {
                            update_option( 'autoptimize_ccss_' . $ccss_setting, autoptimizeUtils::strip_tags_array( $settings['ccss'][ $ccss_setting ] ) );
                        }
                    }

                    // JS.
                    foreach ( array( 'root', 'aggregate', 'defer_not_aggregate', 'defer_inline', 'exclude', 'forcehead', 'trycatch', 'include_inline' ) as $js_setting ) {
                        if ( false === array_key_exists( 'js', $settings ) || false === array_key_exists( $js_setting, $settings['js'] ) ) {
                            continue;
                        } else if ( 'root' === $js_setting ) {
                            update_option( 'autoptimize_js', $settings['js']['root'] );
                        } else {
                            update_option( 'autoptimize_js_' . $js_setting, $settings['js'][ $js_setting ] );
                        }
                    }

                    // CSS.
                    foreach ( array( 'root', 'aggregate', 'datauris', 'justhead', 'defer', 'defer_inline', 'inline', 'exclude', 'include_inline' ) as $css_setting ) {
                        if ( false === array_key_exists( 'css', $settings ) || false === array_key_exists( $css_setting, $settings['css'] ) ) {
                            continue;
                        } else if ( 'root' === $css_setting ) {
                            update_option( 'autoptimize_css', $settings['css']['root'] );
                        } else {
                            update_option( 'autoptimize_css_' . $css_setting, $settings['css'][ $css_setting ] );
                        }
                    }

                    // Other.
                    foreach ( array( 'autoptimize_imgopt_settings', 'autoptimize_extra_settings', 'autoptimize_cache_fallback', 'autoptimize_cache_nogzip', 'autoptimize_cdn_url', 'autoptimize_enable_meta_ao_settings', 'autoptimize_enable_site_config', 'autoptimize_html', 'autoptimize_html_keepcomments', 'autoptimize_minify_excluded', 'autoptimize_optimize_checkout', 'autoptimize_optimize_logged' ) as $other_setting ) {
                        if ( false === array_key_exists( 'other', $settings ) || false === array_key_exists( $other_setting, $settings['other'] ) ) {
                            continue;
                        } else {
                            update_option( $other_setting, $settings['other'][ $other_setting ] );
                        }
                    }

                    // AO Pro.
                    if ( defined( 'AO_PRO_VERSION' ) && array_key_exists( 'pro', $settings ) ) {
                        update_option( 'autoptimize_pro_boosters', $settings['pro']['boosters'] );
                        update_option( 'autoptimize_pro_pagecache', $settings['pro']['pagecache'] );
                    }

                    // settings.json has been imported, so can be removed now.
                    if ( file_exists( $importfile ) ) {
                        unlink( $importfile );
                    }
                } else {
                    // Settings file doesn't exist, update error flag.
                    $error = 'settings file does not exist';
                }
            }
        } else {
            $error = 'file could not be saved';
        }

        // Prepare response.
        if ( $error ) {
            $response['code'] = '500';
            $response['msg']  = 'Error importing settings: ' . $error;
        } else {
            $response['code'] = '200';
            $response['msg']  = 'Settings imported successfully';
        }

        // Dispatch respose.
        echo json_encode( $response );

        // Close ajax request.
        wp_die();
    }

    public function ao_ccss_queuerunner_callback() {
        check_ajax_referer( 'ao_ccss_queuerunner_nonce', 'ao_ccss_queuerunner_nonce' );

        // Process an uploaded file with no errors.
        if ( current_user_can( 'manage_options' ) ) {
            if ( ! file_exists( AO_CCSS_LOCK ) ) {
                $ccss_cron = new autoptimizeCriticalCSSCron();
                $ccss_cron->ao_ccss_queue_control();
                $response['code'] = '200';
                $response['msg']  = 'Queue processing done';
            } else {
                $response['code'] = '302';
                $response['msg']  = 'Lock file found';
            }
        } else {
            $response['code'] = '500';
            $response['msg']  = 'Not allowed';
        }

        // Dispatch respose.
        echo json_encode( $response );

        // Close ajax request.
        wp_die();
    }

    public function ao_ccss_saverules_callback() {
        check_ajax_referer( 'ao_ccss_saverules_nonce', 'ao_ccss_saverules_nonce' );

        // save rules over AJAX, too many users forget to press "save changes".
        if ( current_user_can( 'manage_options' ) ) {
            if ( array_key_exists( 'critcssrules', $_POST ) ) {
                $rules = stripslashes( $_POST['critcssrules'] ); // ugly, but seems correct as per https://developer.wordpress.org/reference/functions/stripslashes_deep/#comment-1045 .
                if ( ! empty( $rules ) ) {
                    $_unsafe_rules_array = json_decode( wp_strip_all_tags( $rules ), true );
                    if ( ! empty( $_unsafe_rules_array ) && is_array( $_unsafe_rules_array ) ) {
                        $_safe_rules_array = array();
                        if ( array_key_exists( 'paths', $_unsafe_rules_array ) ) {
                            $_safe_rules_array['paths'] = $_unsafe_rules_array['paths'];
                        }
                        if ( array_key_exists( 'types', $_unsafe_rules_array ) ) {
                            $_safe_rules_array['types'] = $_unsafe_rules_array['types'];
                        }
                        $_safe_rules = json_encode( $_safe_rules_array, JSON_FORCE_OBJECT );
                        if ( ! empty( $_safe_rules ) ) {
                            update_option( 'autoptimize_ccss_rules', $_safe_rules );
                            $response['code'] = '200';
                            $response['msg']  = 'Rules saved';
                        } else {
                            $_error = 'Could not auto-save rules (safe rules empty)';
                        }
                    } else {
                        $_error = 'Could not auto-save rules (rules could not be json_decoded)';
                    }
                } else {
                    $_error = 'Could not auto-save rules (rules empty)';
                }
            } else {
                $_error = 'Could not auto-save rules (rules not in $_POST)';
            }
        } else {
            $_error = 'Not allowed';
        }

        if ( ! isset( $response ) && $_error ) {
            $response['code'] = '500';
            $response['msg']  = $_error;
        }

        // Dispatch respose.
        echo json_encode( $response );

        // Close ajax request.
        wp_die();
    }

    public function critcss_check_filename( $filename ) {
        // Try to avoid directory traversal when reading/writing/deleting critical CSS files.
        if ( strpos( $filename, 'ccss_' ) !== 0 ) {
            return false;
        } elseif ( substr( $filename, -4, 4 ) !== '.css' ) {
            return false;
        } elseif ( sanitize_file_name( $filename ) !== $filename ) {
            // Use WordPress core's sanitize_file_name to see if anything fishy is going on.
            return false;
        } else {
            return true;
        }
    }

    public function rrmdir( $path ) {
        // recursively remove a directory as found on
        // https://andy-carter.com/blog/recursively-remove-a-directory-in-php.
        $files = glob( $path . '/*' );
        foreach ( $files as $file ) {
            is_dir( $file ) ? $this->rrmdir( $file ) : unlink( $file );
        }
        rmdir( $path );

        return;
    }
    
    public function fetch_rule_from_ccssfile( $ccss_file = '' ) {
        if ( empty( $ccss_file ) ) {
            return false;   
        }

        $ccss_file = str_replace( AO_CCSS_DIR, '', $ccss_file );
        
        static $rules = null;
        if ( null === $rules ) {
            $rules = $this->criticalcss->get_option( 'rules' );
        }

        foreach ( $rules as $ruletype => $rulechilds ) {
            foreach ( $rulechilds as $identifier => $properties ) {
                if ( $properties['file'] === $ccss_file ) {
                    return array( $ruletype, $identifier );
                }
            }
        }
        
        return false;
    }
}
PK.�1\�����autoptimizePartners.phpnu�[���<?php
/**
 * Handles adding "more tools" tab in AO admin settings page which promotes (future) AO
 * addons and/or affiliate services.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizePartners
{
    public function __construct()
    {
        $this->run();
    }

    public function run()
    {
        if ( $this->enabled() ) {
            add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_partner_tabs' ), 10, 1 );
        }
        if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
            add_action( 'network_admin_menu', array( $this, 'add_admin_menu' ) );
        } else {
            add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        }
    }

    protected function enabled()
    {
        return apply_filters( 'autoptimize_filter_show_partner_tabs', true );
    }

    public function add_partner_tabs( $in )
    {
        $in = array_merge(
            $in,
            array(
                'ao_partners' => esc_html__( 'Optimize More!', 'autoptimize' ),
            )
        );

        return $in;
    }

    public function add_admin_menu()
    {
        if ( $this->enabled() ) {
            add_submenu_page( '', 'AO partner', 'AO partner', 'manage_options', 'ao_partners', array( $this, 'ao_partners_page' ) );
        }
    }

    protected function get_ao_partner_feed_markup()
    {
        $no_feed_text = sprintf( esc_html__( 'Have a look at %1$sAutoptimize Pro%2$s to power-up your site!', 'autoptimize' ), '<a href="http://autoptimize.com/pro">', '</a>' );
        $output       = '';
        if ( apply_filters( 'autoptimize_settingsscreen_remotehttp', true ) ) {
            $rss      = fetch_feed( 'http://feeds.feedburner.com/OptimizingMattersDownloads' );
            $maxitems = 0;

            if ( ! is_wp_error( $rss ) ) {
                $maxitems  = $rss->get_item_quantity( 20 );
                $rss_items = $rss->get_items( 0, $maxitems );
            }

            if ( 0 == $maxitems ) {
                $output .= $no_feed_text;
            } else {
                $output .= '<ul>';
                foreach ( $rss_items as $item ) {
                    $item_url  = esc_url( $item->get_permalink() );
                    $enclosure = $item->get_enclosure();

                    $output .= '<li class="itemDetail">';
                    $output .= '<h3 class="itemTitle"><a href="' . $item_url . '" target="_blank">' . esc_html( $item->get_title() ) . '</a></h3>';

                    if ( $enclosure && ( false !== strpos( $enclosure->get_type(), 'image' ) ) ) {
                        $img_url = esc_url( $enclosure->get_link() );
                        $output .= '<div class="itemImage"><a href="' . $item_url . '" target="_blank"><img src="' . $img_url . '"></a></div>';
                    }

                    $output .= '<div class="itemDescription">' . wp_kses_post( $item->get_description() ) . '</div>';
                    $output .= '<div class="itemButtonRow"><div class="itemButton button-secondary"><a href="' . $item_url . '" target="_blank">' . esc_html__( 'More info', 'autoptimize' ) . '</a></div></div>';
                    $output .= '</li>';
                }
                $output .= '</ul>';
            }
        } else {
            $output .= $no_feed_text;
        }

        return $output;
    }

    public function ao_partners_page()
    {
        ?>
<style>
    .itemDetail {
        background: #fff;
        width: 250px;
        min-height: 290px;
        border: 1px solid #ccc;
        float: left;
        padding: 15px;
        position: relative;
        margin: 0 10px 10px 0;
    }
    .itemTitle {
        margin-top:0px;
        margin-bottom:10px;
    }
    .itemImage {
        text-align: center;
    }
    .itemImage img {
        max-width: 95%;
        max-height: 150px;
    }
    .itemDescription {
        margin-bottom:30px;
    }
    .itemButtonRow {
        position: absolute;
        bottom: 10px;
        right: 10px;
        width:100%;
    }
    .itemButton {
        float:right;
    }
    .itemButton a {
        text-decoration: none;
        color: #555;
    }
    .itemButton a:hover {
        text-decoration: none;
        color: #23282d;
    }
    </style>
    <script>document.title = "Autoptimize: <?php esc_html_e( 'Optimize More!', 'autoptimize' ); ?> " + document.title;</script>
    <div class="wrap">
        <h1><?php apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html_e( 'Autoptimize Pro Settings', 'autoptimize' ) : esc_html_e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
        <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
        <?php echo '<h2>' . esc_html__( "These Autoptimize power-ups and related services will improve your site's performance even more!", 'autoptimize' ) . '</h2>'; ?>
        <div>
            <?php echo $this->get_ao_partner_feed_markup(); ?>
        </div>
    </div>
        <?php
    }
}
PK.�1\�y|B�1�1autoptimizeImages.phpnu�[���<?php
/**
 * Handles optimizing images.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeImages
{
    /**
     * Options.
     *
     * @var array
     */
    protected $options = array();

    /**
     * Singleton instance.
     *
     * @var self|null
     */
    protected static $instance = null;
    
    /**
     * lazyload counter.
     * 
     * @var int
     */
    protected $lazyload_counter = 0;

    public function __construct( array $options = array() )
    {
        // If options are not provided, fetch them.
        if ( empty( $options ) ) {
            $options = $this->fetch_options();
        }

        $this->set_options( $options );
    }

    public function set_options( array $options )
    {
        $this->options = $options;

        return $this;
    }

    public static function fetch_options()
    {
        $value = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings' );
        if ( empty( $value ) ) {
            // Fallback to returning defaults when no stored option exists yet.
            $value = autoptimizeConfig::get_ao_imgopt_default_options();
        }

        // get service availability and add it to the options-array.
        $value['availabilities'] = autoptimizeOptionWrapper::get_option( 'autoptimize_service_availablity' );

        if ( empty( $value['availabilities'] ) || ! is_array( $value['availabilities'] ) ) {
            $value['availabilities'] = null;

            if ( true === autoptimizeImages::imgopt_active() ) {
                $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
            }

            if ( null === $value['availabilities'] ) {
                // We can't seem to check service availability, use mock result with imgopt status UP.
                $_mock_settings = array(
                    'extra_imgopt' => array(
                        'status' => 'up',
                        'hosts' => array(
                            '1' => 'https://sp-ao.shortpixel.ai/',
                        ),
                    ),
                    'critcss' => array(
                        'status' => 'up',
                    ),
                );
                $value['availabilities'] = $_mock_settings;
            }
        }

        return $value;
    }

    public static function imgopt_active()
    {
        // function to quickly check if imgopt is active, used below but also in
        // autoptimizeMain.php to start ob_ even if no HTML, JS or CSS optimizing is done
        // and does not use/ request the availablity data (which could slow things down).
        static $imgopt_active = null;

        if ( null === $imgopt_active ) {
            $opts = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings', '' );
            if ( ! empty( $opts ) && is_array( $opts ) && array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $opts ) && ! empty( $opts['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $opts['autoptimize_imgopt_checkbox_field_1'] ) {
                $imgopt_active = true;
            } else {
                $imgopt_active = false;
            }
        }

        return $imgopt_active;
    }

    /**
     * Helper for getting a singleton instance. While being an
     * anti-pattern generally, it comes in handy for now from a
     * readability/maintainability perspective, until we get some
     * proper dependency injection going.
     *
     * @return self
     */
    public static function instance()
    {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    public function run()
    {
        if ( is_admin() ) {
            if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
                add_action( 'network_admin_menu', array( $this, 'imgopt_admin_menu' ) );
            } else {
                add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
            }
            add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_imgopt_tab' ), 9 );
        } else {
            add_action( 'wp', array( $this, 'run_on_frontend' ) );
        }
    }

    public function run_on_frontend() {
        if ( ! $this->should_run() ) {
            if ( $this->should_lazyload() ) {
                add_filter(
                    'wp_lazy_loading_enabled',
                    array( $this, 'should_disable_core_lazyload' ),
                    10,
                    3
                );
                add_filter(
                    'autoptimize_html_after_minify',
                    array( $this, 'filter_lazyload_images' ),
                    10,
                    1
                );
                add_action(
                    'wp_footer',
                    array( $this, 'add_lazyload_js_footer' ),
                    10,
                    0
                );
            }
            return;
        }

        $active = false;

        if ( apply_filters( 'autoptimize_filter_imgopt_do', true ) ) {
            add_filter(
                'autoptimize_html_after_minify',
                array( $this, 'filter_optimize_images' ),
                10,
                1
            );
            $active = true;
        }

        if ( apply_filters( 'autoptimize_filter_imgopt_do_css', true ) ) {
            // fixme: also act on already minified CSS!
            add_filter(
                'autoptimize_filter_base_replace_cdn',
                array( $this, 'filter_optimize_css_images' ),
                10,
                1
            );

            add_filter(
                'autoptimize_html_after_minify',
                array( $this, 'filter_optimize_inline_css_images' ),
                10,
                1
            );

            $active = true;
        }

        if ( $active ) {
            add_filter(
                'autoptimize_extra_filter_tobepreconn',
                array( $this, 'filter_preconnect_imgopt_url' ),
                10,
                1
            );
        }

        if ( $this->should_lazyload() ) {
            add_filter(
                'wp_lazy_loading_enabled',
                array( $this, 'should_disable_core_lazyload' ),
                10,
                3
            );
            add_action(
                'wp_footer',
                array( $this, 'add_lazyload_js_footer' ),
                10,
                0
            );
        }
    }

    /**
     * Disables core's native lazyload for images, not for iframes.
     *
     * @param bool   $flag      Incoming flag (mostly true).
     * @param string $tag       Tag (img or iframe).
     * @param string $context   Full context.
     *
     * @return bool
     */
    public function should_disable_core_lazyload( $flag = true, $tag = '', $context = '' ) {
        if ( 'img' === $tag ) {
            return false;
        }
        return $flag;
    }

    /**
     * Basic checks before we can run.
     *
     * @return bool
     */
    protected function should_run()
    {
        $opts              = $this->options;
        $service_not_down  = ( 'down' !== $opts['availabilities']['extra_imgopt']['status'] );
        $not_launch_status = ( 'launch' !== $opts['availabilities']['extra_imgopt']['status'] );

        $do_cdn      = true;
        $_userstatus = $this->get_imgopt_provider_userstatus();
        if ( isset( $_userstatus['Status'] ) && ( -2 == $_userstatus['Status'] || -3 == $_userstatus['Status'] ) ) {
            // don't even attempt to put images on CDN if heavily exceeded threshold or if site not reachable.
            $do_cdn = false;
        }

        if (
            $this->imgopt_active()
            && $do_cdn
            && $service_not_down
            && ( $not_launch_status || $this->launch_ok() )
        ) {
            return true;
        }
        return false;
    }

    public function get_imgopt_host()
    {
        static $imgopt_host = null;

        if ( null === $imgopt_host ) {
            $imgopt_host  = 'https://sp-ao.shortpixel.ai/';
            $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
            if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
                $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
            }
            $imgopt_host = apply_filters( 'autoptimize_filter_imgopt_host', $imgopt_host );
        }

        return $imgopt_host;
    }

    public static function get_imgopt_host_wrapper()
    {
        // needed for CI tests.
        $self = new self();
        return $self->get_imgopt_host();
    }

    public static function get_service_url_suffix()
    {
        $suffix = '/af/ZQXTBY0109483/' . AUTOPTIMIZE_SITE_DOMAIN;

        return $suffix;
    }

    public function get_img_quality_string()
    {
        static $quality = null;

        if ( null === $quality ) {
            $q_array = $this->get_img_quality_array();
            $setting = $this->get_img_quality_setting();
            $quality = apply_filters(
                'autoptimize_filter_imgopt_quality',
                'q_' . $q_array[ $setting ]
            );
        }

        return $quality;
    }

    public function get_img_quality_array()
    {
        static $map = null;

        if ( null === $map ) {
            $map = array(
                '1' => 'lossy',
                '2' => 'glossy',
                '3' => 'lossless',
            );
            $map = apply_filters(
                'autoptimize_filter_imgopt_quality_array',
                $map
            );
        }

        return $map;
    }

    public function get_img_quality_setting()
    {
        static $q = null;

        if ( null === $q ) {
            if ( is_array( $this->options ) && array_key_exists( 'autoptimize_imgopt_select_field_2', $this->options ) ) {
                $setting = $this->options['autoptimize_imgopt_select_field_2'];
            }

            if ( ! isset( $setting ) || empty( $setting ) || ( '1' !== $setting && '3' !== $setting ) ) {
                // default image opt. value is 2 ("glossy").
                $q = '2';
            } else {
                $q = $setting;
            }
        }

        return $q;
    }

    public function filter_preconnect_imgopt_url( array $in )
    {
        $url_parts = parse_url( $this->get_imgopt_base_url() );
        $in[]      = $url_parts['scheme'] . '://' . $url_parts['host'];

        return $in;
    }

    /**
     * Makes sure given url contains the full scheme and hostname
     * in case they're not present already.
     *
     * @param string $in Image url to normalize.
     *
     * @return string
     */
    private function normalize_img_url( $in )
    {
        // Only parse the site url once.
        static $parsed_site_url = null;
        if ( null === $parsed_site_url ) {
            $parsed_site_url = parse_url( site_url() );
        }

        // get CDN domain once.
        static $cdn_domain = null;
        if ( is_null( $cdn_domain ) ) {
            $cdn_url = $this->get_cdn_url();
            if ( ! empty( $cdn_url ) ) {
                $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
            } else {
                $cdn_domain = '';
            }
        }

        /**
         * This method gets called a lot, often for identical urls it seems.
         * `filter_optimize_css_images()` calls us, uses the resulting url and
         * gives it to `can_optimize_image()`, and if that returns trueish
         * then `build_imgopt_url()` is called (which, again, calls this method).
         * Until we dig deeper into whether this all must really happen that
         * way, having an internal cache here helps (to avoid doing repeated
         * identical string operations).
         */
        static $cache = null;
        if ( null === $cache ) {
            $cache = array();
        }

        // Do the work on cache miss only.
        if ( ! isset( $cache[ $in ] ) ) {
            // Default to (the trimmed version of) what was given to us.
            $result = trim( $in );

            // Some silly plugins wrap background images in html-encoded quotes, so remove those from the img url.
            $result = $this->fix_silly_bgimg_quotes( $result );

            if ( autoptimizeUtils::is_protocol_relative( $result ) ) {
                $result = $parsed_site_url['scheme'] . ':' . $result;
            } elseif ( 0 === strpos( $result, '/' ) ) {
                // Root-relative...
                $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $result;
            } elseif ( ! empty( $cdn_domain ) && false === strpos( $this->get_imgopt_host(), $cdn_domain ) && strpos( $result, $cdn_domain ) !== 0 ) {
                // remove CDN except if it is the image optimization one.
                $result = str_replace( $cdn_domain, $parsed_site_url['host'], $result );
            }

            // filter (default off) to remove QS from image URL's to avoid eating away optimization credits.
            if ( apply_filters( 'autoptimize_filter_imgopt_no_querystring', false ) && strpos( $result, '?' ) !== false ) {
                $result = strtok( $result, '?' );
            }

            $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );

            // Store in cache.
            $cache[ $in ] = $result;
        }

        return $cache[ $in ];
    }

    public function filter_optimize_css_images( $in )
    {
        $in = $this->normalize_img_url( $in );

        if ( $this->can_optimize_image( $in ) && false === strpos( $in, $this->get_imgopt_host() ) ) {
            return $this->build_imgopt_url( $in, '', '' );
        } else {
            return $in;
        }
    }
    
    public function filter_optimize_inline_css_images( $html ) {
        preg_match_all( '#<style[^>]*>([^<]*)</style>#Um', $html, $inline_css_blocks, PREG_SET_ORDER );
        foreach ( $inline_css_blocks as $inline_css_block ) {
            if ( false !== strpos( $inline_css_block[0], 'background' ) ) { 
                $inline_css_block_new = $this->replace_background_img_css( $inline_css_block[0] );
                if ( $inline_css_block_new !== $inline_css_block[0] ) {
                    $html = str_replace( $inline_css_block[0], $inline_css_block_new, $html );
                }
            }
        }
        return $html;
    }

    public static function replace_background_img_css( $css ) {
        // fixme; can/ should we cache these?
        preg_match_all( '#background[^;}]*url\((.*)\)#Ui', $css, $backgrounds, PREG_SET_ORDER );
        if ( is_array( $backgrounds ) && ! empty( $backgrounds ) ) {
            foreach ( $backgrounds as $background ) {
                if ( autoptimizeImages::can_optimize_image_wrapper( $background[1] ) ) {
                    $css = str_replace( $background[1], autoptimizeImages::build_imgopt_url_wrapper( $background[1] ), $css );
                }
            }
        }
        return $css;
    }

    private function get_imgopt_base_url()
    {
        static $imgopt_base_url = null;

        if ( null === $imgopt_base_url ) {
            $imgopt_host     = $this->get_imgopt_host();
            $quality         = $this->get_img_quality_string();
            $ret_val         = apply_filters( 'autoptimize_filter_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
            if ( $this->should_ngimg() ) {
                $sp_to_string = 'to_auto';
            } else {
                $sp_to_string = 'to_webp';
            }
            $sp_to_string    = apply_filters( 'autoptimize_filter_imgopt_format', $sp_to_string ); // values: empty (= jpeg), to_webp (smart; webp or fallback), to_avif (avif or fallback) or to_auto (smart avif, webp or fallback).
            $imgopt_base_url = $imgopt_host . 'client/' . $sp_to_string . ',' . $quality . ',' . $ret_val;
            $imgopt_base_url = apply_filters( 'autoptimize_filter_imgopt_base_url', $imgopt_base_url );
        }

        return $imgopt_base_url;
    }

    public static function can_optimize_image_wrapper( $url, $tag = '', $testing = false ) {
        $self = new self();
        return $self->can_optimize_image( $url, $tag = '', $testing = false );
    }

    private function can_optimize_image( $url, $tag = '', $testing = false )
    {
        static $cdn_url      = null;
        static $nopti_images = null;

        if ( null === $cdn_url ) {
            $cdn_url = apply_filters(
                'autoptimize_filter_base_cdnurl',
                autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' )
            );
        }

        if ( null === $nopti_images || $testing ) {
            if ( is_array( $this->options ) && array_key_exists( 'autoptimize_imgopt_text_field_6', $this->options ) ) {
                $nopti_images = $this->options['autoptimize_imgopt_text_field_6'];
            }
            $nopti_images = apply_filters( 'autoptimize_filter_imgopt_noptimize', $nopti_images );
        }

        $site_host  = AUTOPTIMIZE_SITE_DOMAIN;
        $url        = $this->normalize_img_url( $url );
        $url_parsed = parse_url( $url );

        if ( false === is_array( $url_parsed ) ) {
            return false;
        } elseif ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
            return false;
        } elseif ( autoptimizeUtils::is_local_server() ) {
            return false;
        } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
            return false;
        } elseif ( strpos( $url, '.php' ) !== false ) {
            return false;
        } elseif ( false === array_key_exists( 'path', $url_parsed ) || str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp', '.avif' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
            // fixme: better check against end of string.
            return false;
        } elseif ( ! empty( $nopti_images ) ) {
            $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
            foreach ( $nopti_images_array as $nopti_image ) {
                if ( strpos( $url, $nopti_image ) !== false || ( ( '' !== $tag && strpos( $tag, $nopti_image ) !== false ) ) ) {
                    return false;
                }
            }
        }
        return true;
    }

    // wrapper for reuse in AOPro.
    public static function build_imgopt_url_wrapper( $orig_url, $width = 0, $height = 0 ) {
        $self = new self();
        return $self->build_imgopt_url( $orig_url, $width = 0, $height = 0 );
    }

    private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
    {
        // sanitize width and height.
        if ( strpos( $width, '%' ) !== false ) {
            $width = 0;
        }
        if ( strpos( $height, '%' ) !== false ) {
            $height = 0;
        }
        $width  = (int) $width;
        $height = (int) $height;

        $filtered_url = apply_filters(
            'autoptimize_filter_imgopt_build_url',
            $orig_url,
            $width,
            $height
        );

        // If filter modified the url, return that.
        if ( $filtered_url !== $orig_url ) {
            return $filtered_url;
        }

        $normalized_url = $this->normalize_img_url( $orig_url );

        // if the URL is ascii we check if we have a real URL with filter_var (which only works on ascii url's) and if not a real URL we return the original one.
        if ( apply_filters( 'autoptimize_filter_imgopt_check_normalized_url', true ) && ! preg_match( '/[^\x20-\x7e]/', $normalized_url ) && false === filter_var( $normalized_url, FILTER_VALIDATE_URL ) ) {
            return $orig_url;
        }

        $imgopt_base_url = $this->get_imgopt_base_url();
        $imgopt_size     = '';

        if ( $width && 0 !== $width ) {
            $imgopt_size = ',w_' . $width;
        }

        if ( $height && 0 !== $height ) {
            $imgopt_size .= ',h_' . $height;
        }

        $url = $imgopt_base_url . $imgopt_size . '/' . $normalized_url;
        $url = apply_filters( 'autoptimize_filter_imgopt_after_build_imgopt_url', $url );

        return $url;
    }

    public function replace_data_thumbs( $matches )
    {
        return $this->replace_img_callback( $matches, 150, 150 );
    }

    public function replace_img_callback( $matches, $width = 0, $height = 0 )
    {
        $_normalized_img_url = $this->normalize_img_url( $matches[1] );
        if ( $this->can_optimize_image( $matches[1], $matches[0] ) ) {
            return str_replace( $matches[1], $this->build_imgopt_url( $_normalized_img_url, $width, $height ), $matches[0] );
        } else {
            return $matches[0];
        }
    }

    public function replace_icon_callback( $matches )
    {
        if ( array_key_exists( '2', $matches ) ) {
            $sizes  = explode( 'x', $matches[2] );
            $width  = $sizes[0];
            $height = $sizes[1];
        } else {
            $width  = 180;
            $height = 180;
        }

        // make sure we're not trying to optimize a *.ico file.
        if ( strpos( $matches[1], '.ico' ) === false ) {
            return $this->replace_img_callback( $matches, $width, $height );
        } else {
            return $matches[0];
        }
    }

    public function filter_optimize_images( $in, $testing = false )
    {
        /*
         * potential future functional improvements:
         *
         * filter for critical CSS.
         */
        $to_replace = array();
        $to_preload = '';

        // hide (no)script tags to avoid replacing (and potentially breaking) images in script tags.
        if ( apply_filters( 'autoptimize_filter_imgopt_hide_script', true ) || $this->should_lazyload() ) {
            $in = autoptimizeBase::replace_contents_with_marker_if_exists(
                'SCRIPT',
                '<script',
                '#<(?:no)?script.*?<\/(?:no)?script>#is',
                $in
            );
        }

        // get img preloads as set in post metabox, exploding ", " instead of "," because LCP preload 
        // could be a shortpixel URL, which has comma's and results in way too many preloads.
        $metabox_preloads = array_filter( array_map( 'trim', explode( ', ', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );
        $metabox_preloads = apply_filters( 'autoptimize_filter_images_metabox_preloads', $metabox_preloads );

        // extract img tags.
        if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
            foreach ( $matches[0] as $tag ) {
                $tag = apply_filters( 'autoptimize_filter_imgopt_tag_preopt', $tag );

                $orig_tag = $tag;
                $imgopt_w = '';
                $imgopt_h = '';

                // first do (data-)srcsets.
                if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
                    foreach ( $allsrcsets as $srcset ) {
                        $srcset      = $srcset[2];
                        $orig_srcset = $srcset;
                        $srcsets     = explode( ',', $srcset );
                        foreach ( $srcsets as $indiv_srcset ) {
                            $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
                            if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
                                $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
                            }
                            if ( $this->can_optimize_image( $indiv_srcset_parts[0], $tag, $testing ) && false === apply_filters( 'autoptimize_filter_imgopt_do_spai', false ) ) {
                                $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
                                $srcset     = str_replace( $indiv_srcset_parts[0], $imgopt_url, $srcset );
                            }
                        }
                        $tag = str_replace( $orig_srcset, $srcset, $tag );
                    }
                }

                // proceed with img src.
                // get width and height and add to $imgopt_size.
                $_get_size = $this->get_size_from_tag( $tag );
                $imgopt_w  = $_get_size['width'];
                $imgopt_h  = $_get_size['height'];

                // then start replacing images src.
                if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
                    foreach ( $urls as $url ) {
                        $full_src_orig = $url[0];
                        $url           = $url[1];
                        if ( $this->can_optimize_image( $url, $tag, $testing ) && false === apply_filters( 'autoptimize_filter_imgopt_do_spai', false ) ) {
                            $imgopt_url      = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
                            $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
                            $tag             = str_replace( $full_src_orig, $full_imgopt_src, $tag );
                        }
                    }
                }

                // check if the image needs to be prelaoded.
                if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && str_replace( $metabox_preloads, '', $tag ) !== $tag ) {
                    $to_preload .= $this->create_img_preload_tag( $tag );
                }

                // do lazyload stuff.
                if ( $this->should_lazyload( $in ) && ! empty( $url ) ) {
                    // first do lpiq placeholder logic.
                    if ( strpos( $url, $this->get_imgopt_host() ) === 0 ) {
                        // if all img src have been replaced during srcset, we have to extract the
                        // origin url from the imgopt one to be able to set a lqip placeholder.
                        $_url = substr( $url, strpos( $url, '/http' ) + 1 );
                    } else {
                        $_url = $url;
                    }

                    $_url = $this->normalize_img_url( $_url );

                    $placeholder = '';
                    if ( $this->can_optimize_image( $_url, $tag ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', false, $_url ) && false === apply_filters( 'autoptimize_filter_imgopt_do_spai', false ) ) {
                        $lqip_w = '';
                        $lqip_h = '';
                        if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
                            $lqip_w = ',w_' . $imgopt_w;
                        }
                        if ( isset( $imgopt_h ) && ! empty( $imgopt_h ) ) {
                            $lqip_h = ',h_' . $imgopt_h;
                        }
                        $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait' . $lqip_w . $lqip_h . '/' . $_url;
                    }
                    // then call add_lazyload-function with lpiq placeholder if set.
                    $tag = $this->add_lazyload( $tag, $placeholder );
                }

                // add decoding="async" behind filter, not sure if I'll make it default true yet.
                if ( true === apply_filters( 'autoptimize_filter_imgopt_add_decoding', true ) && false === strpos( $tag, ' decoding=' ) ) {
                    $tag = str_replace( '<img ', '<img decoding="async" ', $tag );
                }

                $tag = apply_filters( 'autoptimize_filter_imgopt_tag_postopt', $tag );

                // and add tag to array for later replacement.
                if ( $tag !== $orig_tag ) {
                    $to_replace[ $orig_tag ] = $tag;
                }
            }
        }

        // and replace all.
        $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );

        // misc. node attributes that might hold image url's (incl. the previously separate data-thumb).
        $extra_attr_with_img = apply_filters( 'autoptimize_filter_imgopt_attr_with_img', array( array( 'div', 'data-thumb'), array( 'div', 'data-background' ), array( 'img', 'data-retina' ) ) );
        if ( ! empty( $extra_attr_with_img ) && is_array( $extra_attr_with_img ) ) {
            foreach ( $extra_attr_with_img as $candidate ) {
                if ( is_array( $candidate ) && strpos( $out, $candidate[1] ) !== false ) {
                    $_regex = '/\<' . $candidate[0] . '(?:[^>]*)?\s' . $candidate[1] . '=(?:"|\')(.+?)(?:"|\')(?:[^>]*)?>/s';
                    $out = preg_replace_callback(
                        $_regex,
                        array( $this, 'replace_img_callback' ),
                        $out
                    );
                }
            }
        }

        // background-image in inline style.
        if ( ( strpos( $out, 'background-image:' ) !== false || strpos( $out, 'background:' ) !== false ) && strpos( $out, 'url(' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
            $out = preg_replace_callback(
               '/style=(?:"|\')[^<>]*?background(?:-image)?:[^;"\'()>]*url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
                array( $this, 'replace_img_callback' ),
                $out
            );
        }

        // act on icon links.
        if ( ( strpos( $out, '<link rel="icon"' ) !== false || ( strpos( $out, "<link rel='icon'" ) !== false ) ) && apply_filters( 'autoptimize_filter_imgopt_linkicon', true ) ) {
            $out = preg_replace_callback(
                '/<link\srel=(?:"|\')(?:apple-touch-)?icon(?:-precomposed)?(?:"|\').*\shref=(?:"|\')(.*)(?:"|\')(?:\ssizes=(?:"|\')(\d*x\d*)(?:"|\'))?\s?\/?>/Um',
                array( $this, 'replace_icon_callback' ),
                $out
            );
        }

        // lazyload picture source tags and bgimage.
        if ( $this->should_lazyload() ) {
            $out = $this->process_picture_tag( $out, true, true );
            $out = $this->process_bgimage( $out );
        } else {
            $out = $this->process_picture_tag( $out, true, false );
        }

        // restore (no)script tags.
        if ( apply_filters( 'autoptimize_filter_imgopt_hide_script', true ) || $this->should_lazyload() ) {
            $out = autoptimizeBase::restore_marked_content(
                'SCRIPT',
                $out
            );
        }

        if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && empty( $to_preload ) && false !== apply_filters( 'autoptimize_filter_imgopt_dopreloads', true ) ) {
            // the preload was not in an img tag, so adding a non-responsive preload instead.
            foreach ( $metabox_preloads as $img_preload ) {
                $to_preload .= '<link rel="preload" href="' . $img_preload . '" as="image">';
            }
        }

        if ( ! empty( $to_preload ) ) {
            $out = autoptimizeExtra::inject_preloads( $to_preload, $out );
        }

        return $out;
    }

    public static function get_size_from_tag( $tag ) {
        // reusable function to extract widht and height from an image tag
        // enforcing a filterable maximum width and height (default 4999X4999).
        $width  = '';
        $height = '';

        if ( preg_match( '#width=("|\')(.*)("|\')#Usmi', $tag, $_width ) ) {
            if ( strpos( $_width[2], '%' ) === false ) {
                $width = (int) $_width[2];
            }
        }
        if ( preg_match( '#height=("|\')(.*)("|\')#Usmi', $tag, $_height ) ) {
            if ( strpos( $_height[2], '%' ) === false ) {
                $height = (int) $_height[2];
            }
        }

        // check for and enforce (filterable) max sizes.
        $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
        if ( $width > $_max_width ) {
            $_width = $_max_width;
            if ( ! empty( $height ) && is_int( $height ) ) {
                $height = $_width / $width * $height;
            }
            $width  = $_width;
        }
        $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
        if ( $height > $_max_height ) {
            $_height = $_max_height;
            if ( ! empty( $width ) && is_int( $width ) ) {
                $width   = $_height / $height * $width;
            }
            $height  = $_height;
        }

        return array(
            'width'  => $width,
            'height' => $height,
        );
    }

    /**
     * Lazyload functions
     */
    public static function should_lazyload_wrapper( $no_meta = false ) {
        // needed in autoptimizeMain.php.
        $self = new self();
        return $self->should_lazyload( '', $no_meta );
    }

    public function should_lazyload( $context = '', $no_meta = false ) {
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && false === $this->check_nolazy() ) {
            $lazyload_return = true;
        } else {
            $lazyload_return = false;
        }

        // If page/ post check post_meta to see if lazyload is off for page.
        if ( false === $no_meta && false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_lazyload' ) ) {
              $lazyload_return = false;
        }

        $lazyload_return = apply_filters( 'autoptimize_filter_imgopt_should_lazyload', $lazyload_return, $context );

        return $lazyload_return;
    }

    public static function check_nolazy() {
        if ( array_key_exists( 'ao_nolazy', $_GET ) && '1' === $_GET['ao_nolazy'] ) {
            return true;
        } else {
            return false;
        }
    }

    public function filter_lazyload_images( $in )
    {
        // only used is image optimization is NOT active but lazyload is.
        $to_replace = array();
        $to_preload = '';

        // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
        $out = autoptimizeBase::replace_contents_with_marker_if_exists(
            'SCRIPT',
            '<script',
            '#<(?:no)?script.*?<\/(?:no)?script>#is',
            $in
        );

        // get img preloads as set in post metabox.
        $metabox_preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );

        // extract img tags and add lazyload attribs/ add preloads.
        if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
            foreach ( $matches[0] as $tag ) {
                // check if image needs to be preloaded.
                if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && str_replace( $metabox_preloads, '', $tag ) !== $tag ) {
                    $to_preload .= $this->create_img_preload_tag( $tag );
                }

                // and lazyloaded.
                if ( $this->should_lazyload( $out ) ) {
                    $to_replace[ $tag ] = $this->add_lazyload( $tag );
                }
            }
            $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
        }

        // and also lazyload picture tag.
        $out = $this->process_picture_tag( $out, false, true );

        // and inline style blocks with background-image.
        $out = $this->process_bgimage( $out );

        // restore noscript tags.
        $out = autoptimizeBase::restore_marked_content(
            'SCRIPT',
            $out
        );

        if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && empty( $to_preload ) && false !== apply_filters( 'autoptimize_filter_imgopt_dopreloads', true ) ) {
            // the preload was not in an img tag, so adding a non-responsive preload instead.
            foreach ( $metabox_preloads as $img_preload ) {
                $to_preload .= '<link rel="preload" href="' . $img_preload . '" as="image">';
            }
        }

        if ( ! empty( $to_preload ) ) {
            $out = autoptimizeExtra::inject_preloads( $to_preload, $out );
        }

        return $out;
    }

    public function add_lazyload( $tag, $placeholder = '' ) {
        // adds actual lazyload-attributes to an image node.
        $this->lazyload_counter++;

        $_lazyload_from_nth = '';
        if ( array_key_exists( 'autoptimize_imgopt_number_field_7', $this->options ) ) {
            $_lazyload_from_nth = $this->options['autoptimize_imgopt_number_field_7'];
        }
        $_lazyload_from_nth = apply_filters( 'autoptimize_filter_imgopt_lazyload_from_nth', $_lazyload_from_nth );

        if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag && $this->lazyload_counter >= $_lazyload_from_nth ) {
            $tag = $this->maybe_fix_missing_quotes( $tag );

            // store original tag for use in noscript version.
            $noscript_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>';

            $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );

            // insert lazyload class.
            $tag = $this->inject_classes_in_tag( $tag, "$lazyload_class " );

            if ( ! $placeholder || empty( $placeholder ) ) {
                // get image width & heigth for placeholder fun (and to prevent content reflow).
                $_get_size = $this->get_size_from_tag( $tag );
                $width     = $_get_size['width'];
                $height    = $_get_size['height'];
                if ( false === $width || empty( $width ) ) {
                    $width = 210; // default width for SVG placeholder.
                }
                if ( false === $height || empty( $height ) ) {
                    $height = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
                }

                // insert the actual lazyload stuff.
                // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
                $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
            }

            $tag = preg_replace( '/(\s)src=/', ' src=\'' . $placeholder . '\' data-src=', $tag );
            $tag = preg_replace( '/(\s)srcset=/', ' data-srcset=', $tag );

            // move sizes to data-sizes unless filter says no.
            if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
                $tag = str_replace( ' sizes=', ' data-sizes=', $tag );
            }

            // add the noscript-tag from earlier.
            $tag = $noscript_tag . $tag;
            $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
        } else {
            $tag = apply_filters( 'autoptimize_filter_imgopt_not_lazyloaded_img', $tag );
        }

        return $tag;
    }

    public function add_lazyload_js_footer() {
        if ( false === autoptimizeMain::should_buffer() || autoptimizeMain::is_amp_markup( '' ) ) {
            return;
        }

        // The JS will by default be excluded form autoptimization but this can be changed with a filter.
        $noptimize_flag = '';
        if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
            $noptimize_flag = ' data-noptimize="1"';
        }

        $_extra = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings', '' );
        if ( is_array( $_extra ) && array_key_exists( 'autoptimize_extra_checkbox_field_0', $_extra ) && ! empty( $_extra['autoptimize_extra_checkbox_field_0'] ) ) {
            // if "remove query strings" is active in "extra", then let's be consistant and not add one ourselves :-) ?
            $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js', __FILE__ );
        } else {
            $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
        }

        $cdn_url      = $this->get_cdn_url();
        if ( ! empty( $cdn_url ) ) {
            $cdn_url      = rtrim( $cdn_url, '/' );
            $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
        }

        $type_js = '';
        if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
            $type_js = ' type="text/javascript"';
        }

        // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<noscript><style>.lazyload{display:none;}</style></noscript>' );
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
    }

    public static function create_img_preload_tag( $tag ) {
        if ( false === apply_filters( 'autoptimize_filter_imgopt_dopreloads', true ) ) {
            return '';
        }

        // clean up; remove tabs/ linebreaks/ spaces.
        $tag = preg_replace( '/\s+/', ' ', $tag );
        
        // remove noscript.
        if ( false !== strpos( $tag, '<noscript' ) ) {
            $tag = preg_replace( '/<noscript.*<\/noscript>/mU', '', $tag );
        }

        // rewrite img tag to link preload img.
        $_from = array( '<img ', ' src=', ' sizes=', ' srcset=' );
        $_to   = array( '<link rel="preload" as="image" ', ' href=', ' imagesizes=', ' imagesrcset=' );
        $tag   = str_replace( $_from, $_to, $tag );

        // and remove title, alt, class and id.
        $tag = preg_replace( '/ ((?:title|alt|class|id|loading|fetchpriority|decoding|data-no-lazy|width|height)=".*")/Um', '', $tag );
        if ( str_replace( array( ' title=', ' class=', ' alt=', ' id=', ' fetchpriority=', ' decoding=', ' data-no-lazy=' ), '', $tag ) !== $tag ) {
            // 2nd regex pass if still title/ class/ alt in case single quotes were used iso doubles.
            $tag = preg_replace( '/ ((?:title|alt|class|id|loading|fetchpriority|decoding|data-no-lazy)=\'.*\')/Um', '', $tag );
        }

        return $tag;
    }

    public static function get_cdn_url() {
        // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
        static $cdn_url = null;

        if ( null === $cdn_url ) {
            $cdn_url = autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' );
            $cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $cdn_url );
            $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url );
        }

        return $cdn_url;
    }

    public function get_lazyload_exclusions() {
        // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
        static $exclude_lazyload_array = null;

        if ( null === $exclude_lazyload_array ) {
            $options = $this->options;

            // set default exclusions.
            $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset', 'data:image/', 'data-lazyload', 'rev-slidebg', 'loading="eager"', 'fetchpriority="high"' );

            // add from setting.
            if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
                $exclude_lazyload_option = $options['autoptimize_imgopt_text_field_5'];
                if ( ! empty( $exclude_lazyload_option ) ) {
                    $exclude_lazyload_array = array_merge( $exclude_lazyload_array, array_filter( array_map( 'trim', explode( ',', $options['autoptimize_imgopt_text_field_5'] ) ) ) );
                }
            }

            // and filter for developer-initiated changes.
            $exclude_lazyload_array = apply_filters( 'autoptimize_filter_imgopt_lazyload_exclude_array', $exclude_lazyload_array );
        }

        return $exclude_lazyload_array;
    }

    public function inject_classes_in_tag( $tag, $target_class ) {
        if ( strpos( $tag, 'class=' ) !== false ) {
            $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
        } else {
            $tag = preg_replace( '/(<[a-zA-Z]*)\s/', '$1 class="' . trim( $target_class ) . '" ', $tag );
        }

        return $tag;
    }

    public function get_default_lazyload_placeholder( $imgopt_w, $imgopt_h ) {
        return 'data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20' . $imgopt_w . '%20' . $imgopt_h . '%22%3E%3C/svg%3E';
    }

    public function should_ngimg() {
        static $ngimg_return = null;

        if ( is_null( $ngimg_return ) ) {
            // nextgen img only works if imgopt is active.
            if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && $this->imgopt_active() ) {
                $ngimg_return = true;
            } else {
                $ngimg_return = false;
            }
        }

        return $ngimg_return;
    }

    public function process_picture_tag( $in, $imgopt = false, $lazy = false ) {
        // check if "<picture" is present and if filter allows us to process <picture>.
        if ( strpos( $in, '<picture' ) === false || apply_filters( 'autoptimize_filter_imgopt_dopicture', true ) === false ) {
            return $in;
        }

        $_exclusions     = $this->get_lazyload_exclusions();
        $to_replace_pict = array();

        // extract and process each picture-node.
        preg_match_all( '#<picture.*</picture>#Usmi', $in, $_pictures, PREG_SET_ORDER );
        foreach ( $_pictures as $_picture ) {
            $_picture = $this->maybe_fix_missing_quotes( $_picture );
            if ( strpos( $_picture[0], '<source ' ) !== false && preg_match_all( '#<source .*srcset=(?:"|\')(?!data)(.*)(?:"|\').*>#Usmi', $_picture[0], $_sources, PREG_SET_ORDER ) !== false ) {
                foreach ( $_sources as $_source ) {
                    $_picture_replacement = $_source[0];

                    // should we optimize the image?
                    if ( $imgopt && $this->can_optimize_image( $_source[1], $_picture[0] ) ) {
                        $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
                    }
                    // should we lazy-load?
                    if ( $lazy && $this->should_lazyload() && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
                        $_picture_replacement = str_replace( ' srcset=', ' data-srcset=', $_picture_replacement );
                    }
                    $to_replace_pict[ $_source[0] ] = $_picture_replacement;
                }
            }
        }

        // and return the fully procesed $in.
        $out = str_replace( array_keys( $to_replace_pict ), array_values( $to_replace_pict ), $in );

        return $out;
    }

    public function process_bgimage( $in ) {
        if ( strpos( $in, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_lazyload_backgroundimages', true ) ) {
            $out = preg_replace_callback(
                '/(<(?:article|aside|body|div|footer|header|p|section|span|table)[^>]*)\sstyle=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)[^>]*/',
                array( $this, 'lazyload_bgimg_callback' ),
                $in
            );
            return $out;
        }
        return $in;
    }

    public function lazyload_bgimg_callback( $matches ) {
        if ( str_ireplace( $this->get_lazyload_exclusions(), '', $matches[0] ) === $matches[0] ) {
            // get placeholder & lazyload class strings.
            $placeholder    = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( 500, 300 ) );
            $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
            // remove quotes from url() to be able to replace in next step.
            $out = str_replace( array( "url('" . $matches[2] . "')", 'url("' . $matches[2] . '")' ), 'url(' . $matches[2] . ')', $matches[0] );
            // replace background-image URL with SVG placeholder.
            $out = str_replace( 'url(' . $matches[2], 'url(' . $placeholder, $out );
            // sanitize bgimg src for quote sillyness.
            $bgimg_src = $this->fix_silly_bgimg_quotes( $matches[2] );
            // add data-bg attribute with real background-image URL for lazyload to pick up.
            $out = str_replace( $matches[1], $matches[1] . ' data-bg="' . $bgimg_src . '"', $out );
            // and finally add lazyload class to tag.
            $out = $this->inject_classes_in_tag( $out, "$lazyload_class " );
            return $out;
        }
        return $matches[0];
    }

    public function fix_silly_bgimg_quotes( $tag_in ) {
        // some themes/ pagebuilders wrap backgroundimages in HTML-encoded quotes (or linebreaks) which breaks imgopt/ lazyloading, this removes them.
        return trim( str_replace( array( "\r\n", '"', '&quot;', '&#034;', '&apos;', '&#039;' ), '', $tag_in ) );
    }

    public function maybe_fix_missing_quotes( $tag_in ) {
        // W3TC's Minify_HTML class removes quotes around attribute value, this re-adds them for the class and width/height attributes so we can lazyload properly.
        if ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) {
            $tag_out = preg_replace( '/class\s?=([^("|\')]*)(\s|>)/U', 'class=\'$1\'$2', $tag_in );
            $tag_out = preg_replace( '/\s(width|height)=(?:"|\')?([^\s"\'>]*)(?:"|\')?/', ' $1=\'$2\'', $tag_out );
            return $tag_out;
        } else {
            return $tag_in;
        }
    }

    /**
     * Admin page logic and related functions below.
     */
    public function imgopt_admin_menu()
    {
        // no acces if multisite and not network admin and no site config allowed.
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
            add_submenu_page(
                '',
                'autoptimize_imgopt',
                'autoptimize_imgopt',
                'manage_options',
                'autoptimize_imgopt',
                array( $this, 'imgopt_options_page' )
            );
        }
        register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
    }

    public function add_imgopt_tab( $in )
    {
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
            $in = array_merge( $in, array( 'autoptimize_imgopt' => apply_filters( 'autoptimize_filter_imgopt_tab_text', esc_html__( 'Images', 'autoptimize' ) ) ) );
        }

        return $in;
    }

    public function imgopt_options_page()
    {
        // phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace
        // phpcs:disable Generic.Formatting.DisallowMultipleStatements.SameLine

        // Check querystring for "refreshCacheChecker" and call cachechecker if so.
        if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
            $this->query_img_provider_stats( true );
        }

        $options       = $this->fetch_options();
        $sp_url_suffix = $this->get_service_url_suffix();
        ?>
    <style>
        #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
        #ao_settings_form .form-table th {font-weight: normal;}
        #autoptimize_imgopt_descr{font-size: 120%;}
    </style>
    <script>document.title = "Autoptimize: <?php esc_html_e( 'Images', 'autoptimize' ); ?> " + document.title;</script>
    <div class="wrap">
    <h1><?php apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html_e( 'Autoptimize Pro Settings', 'autoptimize' ) : esc_html_e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
        <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
        <?php if ( autoptimizeUtils::is_local_server() ) { ?>
            <div class="notice-warning notice"><p>
            <?php
            echo esc_html__( 'The image optimization service does not work on locally hosted sites or when the server is on a private network.', 'autoptimize' );
            ?>
            </p></div>
        <?php } ?>
        <?php if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) { ?>
            <div class="notice-warning notice"><p>
            <?php
            // translators: "Autoptimize support forum" will appear in a "a href".
            echo sprintf( esc_html__( 'The image optimization service is currently down, image optimization will be skipped until further notice. Check the %1$sAutoptimize support forum%2$s for more info.', 'autoptimize' ), '<a href="https://wordpress.org/support/plugin/autoptimize/" target="_blank">', '</a>' );
            ?>
            </p></div>
        <?php } ?>

        <?php if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] && ! autoptimizeImages::instance()->launch_ok() ) { ?>
            <div class="notice-warning notice"><p>
            <?php esc_html_e( 'The image optimization service is launching, but not yet available for this domain, it should become available in the next couple of days.', 'autoptimize' ); ?>
            </p></div>
        <?php } ?>

        <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
            <div class="notice-warning notice"><p>
            <?php
            // translators: "disable  Jetpack's site accelerator for images" will appear in a "a href" linking to the jetpack settings page.
            echo sprintf( esc_html__( 'Please %1$sdisable Jetpack\'s site accelerator for images%2$s to be able to use Autoptomize\'s advanced image optimization features below.', 'autoptimize' ), '<a href="admin.php?page=jetpack#/settings">', '</a>' );
            ?>
            </p></div>
        <?php } ?>
    <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
        <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
        <h2><?php esc_html_e( 'Image optimization', 'autoptimize' ); ?></h2>
        <span id='autoptimize_imgopt_descr'><?php echo apply_filters( 'autoptimize_filter_imgopt_intro_copy', esc_html__( 'Make your site significantly faster by simply ticking a few boxes and start serving CDN powered, optimized images in next-get formats like WebP and AVIF! No additional plugins or services needed.', 'autoptimize' ) ); ?></span>
        <table class="form-table">
            <tr>
                <th scope="row"><?php esc_html_e( 'Image optimization & CDN', 'autoptimize' ); ?></th>
                <td>
                    <label><input id='autoptimize_imgopt_checkbox' type='checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php echo apply_filters( 'autoptimize_filter_imgopt_main_setting_copy', esc_html__( 'On-the-fly image optimization and fast delivery via the Shortpixel global CDN.', 'autoptimize' ) ); ?></label>
                    <?php
                    // show shortpixel status.
                    $_notice = autoptimizeImages::instance()->get_imgopt_status_notice();
                    if ( $_notice ) {
                        switch ( $_notice['status'] ) {
                            case 2:
                                $_notice_color = 'green';
                                break;
                            case 1:
                                $_notice_color = 'orange';
                                break;
                            case -1:
                            case -2:
                            case -3:
                                $_notice_color = 'red';
                                break;
                            default:
                                $_notice_color = 'green';
                        }
                        echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . esc_html__( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
                    } else {
                        // translators: link points to shortpixel.
                        $upsell_msg_1 = '<p>' . sprintf( esc_html__( 'Get more Google love by speeding up your website. Start serving on-the-fly optimized images (also in the "next-gen" %4$sWebP%5$s and %4$sAVIF%5$s image formats) by %1$sShortPixel%2$s. No additional image optimization plugins are needed: your images are optimized, cached and served from %3$sShortPixel\'s global CDN%2$s.', 'autoptimize' ), '<a href="https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell" target="_blank">', '</a>', '<a href="https://help.shortpixel.com/article/62-where-does-the-cdn-has-pops" target="_blank">', '<strong>', '</strong>' );
                        if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
                            $upsell_msg_2 = sprintf( esc_html__( 'For a limited time only, this service is offered free for all Autoptimize users, %1$sdon\'t miss the chance to test it%2$s and see how much it could improve your site\'s speed.', 'autoptimize' ), '<strong>', '</strong>' );
                        } else {
                            // translators: 1st link points to autoptimize.com.pro, 2nd to shortpixel.
                            $upsell_msg_2 = sprintf( esc_html__( 'For (nearly) %5$sunlimited image optimizations %1$sbuy Autoptimize Pro%2$s%6$s which also includes Critical CSS and extra "booster" options or %3$ssign up at Shortpixel%4$s.', 'autoptimize' ), '<a href="https://autoptimize.com/pro/" target="_blank">', '</a>', '<a href="https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell" target="_blank">', '</a>', '<strong>', '</strong>' );
                        }
                        echo apply_filters( 'autoptimize_filter_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
                    }
                    // translators: link points to shortpixel FAQ.
                    $faqcopy = sprintf( esc_html__( '%3$sQuestions%4$s? Take a look at the %1$sAutoptimize + ShortPixel FAQ%2$s!', 'autoptimize' ), '<strong><a href="https://help.shortpixel.com/category/405-autoptimize" target="_blank">', '</strong></a>', '<strong>', '</strong>' );
                    $faqcopy = $faqcopy . ' ' . esc_html__( 'Only works for websites and images that are publicly available.', 'autoptimize' );
                    // translators: links points to shortpixel TOS & Privacy Policy.
                    $toscopy = sprintf( esc_html__( 'Usage of this feature is subject to Shortpixel\'s %1$sTerms of Use%2$s and %3$sPrivacy policy%4$s.', 'autoptimize' ), '<a href="https://shortpixel.com/tos' . $sp_url_suffix . '" target="_blank">', '</a>', '<a href="https://shortpixel.com/privacy' . $sp_url_suffix . '" target="_blank">', '</a>' );
                    echo apply_filters( 'autoptimize_filter_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
                    ?>
                </td>
            </tr>
            <tr id='autoptimize_imgopt_optimization_exclusions' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
                <th scope="row"><?php esc_html_e( 'Optimization exclusions', 'autoptimize' ); ?></th>
                <td>
                    <label><input type='text' style='width:80%' id='autoptimize_imgopt_optimization_exclusions' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_6]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_6'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_6'] ); } ?>'><br /><?php esc_html_e( 'Comma-separated list of image classes or filenames that should not be optimized.', 'autoptimize' ); ?></label>
                </td>
            </tr>
            <tr id='autoptimize_imgopt_quality' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
                <th scope="row"><?php esc_html_e( 'Image Optimization quality', 'autoptimize' ); ?></th>
                <td>
                    <label>
                    <select name='autoptimize_imgopt_settings[autoptimize_imgopt_select_field_2]'>
                        <?php
                        $_imgopt_array = autoptimizeImages::instance()->get_img_quality_array();
                        $_imgopt_val   = autoptimizeImages::instance()->get_img_quality_setting();

                        foreach ( $_imgopt_array as $key => $value ) {
                            echo '<option value="' . $key . '"';
                            if ( $_imgopt_val == $key ) {
                                echo ' selected';
                            }
                            echo '>' . ucfirst( $value ) . '</option>';
                        }
                        echo "\n";
                        ?>
                    </select>
                    </label>
                    <p>
                        <?php
                            // translators: link points to shortpixel image test page.
                            echo apply_filters( 'autoptimize_filter_imgopt_quality_copy', sprintf( esc_html__( 'You can %1$stest compression levels here%2$s.', 'autoptimize' ), '<a href="https://shortpixel.com/online-image-compression' . $sp_url_suffix . '" target="_blank">', '</a>' ) );
                        ?>
                    </p>
                </td>
            </tr>
            <?php
            if ( apply_filters( 'autoptimize_filter_imgopt_settings_show_avif', true ) ) {
                ?>
                <tr id='autoptimize_imgopt_ngimg' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
                    <th scope="row"><?php esc_html_e( 'Load AVIF in supported browsers?', 'autoptimize' ); ?></th>
                    <td>
                        <label><input type='checkbox' id='autoptimize_imgopt_ngimg_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_4]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_4'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_4'] ) { echo 'checked="checked"'; } ?> value='1'><?php esc_html_e( 'Automatically serve AVIF image format to any browser that supports it.', 'autoptimize' ); ?></label>
                    </td>
                </tr>
                <?php
            } else {
                ?>
                <input type='hidden' id='autoptimize_imgopt_ngimg_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_4]' value='0'>
                <?php
            }
            ?>
            <tr>
                <th scope="row"><?php esc_html_e( 'Lazy-load images?', 'autoptimize' ); ?></th>
                <td>
                    <label><input type='checkbox' id='autoptimize_imgopt_lazyload_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_3]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php esc_html_e( 'Image lazy-loading will delay the loading of non-visible images to allow the browser to optimally load all resources for the "above the fold"-page first.', 'autoptimize' ); ?></label>
                </td>
            </tr>
            <tr id='autoptimize_imgopt_lazyload_exclusions' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_3', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_3'] ) ) { echo 'class="autoptimize_lazyload_child hidden"'; } else { echo 'class="autoptimize_lazyload_child"'; } ?>>
                <th scope="row"><?php esc_html_e( 'Lazy-load exclusions', 'autoptimize' ); ?></th>
                <td>
                    <label><input type='text' style='width:80%' id='autoptimize_imgopt_lazyload_exclusions_text' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_5]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_5'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_5'] ); } ?>'><br /><?php esc_html_e( 'Comma-separated list of to be excluded image classes or filenames.', 'autoptimize' ); ?></label>
                </td>
            </tr>
            <tr id='autoptimize_imgopt_lazyload_from_nth_image' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_3', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_3'] ) ) { echo 'class="autoptimize_lazyload_child hidden"'; } else { echo 'class="autoptimize_lazyload_child"'; } ?>>
                <th scope="row"><?php esc_html_e( 'Lazy-load from nth image', 'autoptimize' ); ?></th>
                <td>
                    <label><input type='number' min='0' max='50' style='width:80%' id='autoptimize_imgopt_lazyload_from_nth_image_number' name='autoptimize_imgopt_settings[autoptimize_imgopt_number_field_7]' value='<?php if ( ! empty( $options['autoptimize_imgopt_number_field_7'] ) ) { echo esc_attr( $options['autoptimize_imgopt_number_field_7'] ); } else { echo '1'; } ?>'><br /><?php esc_html_e( 'Don\'t lazyload the first X images, \'1\' lazyloads all.', 'autoptimize' ); ?></label>
                </td>
            </tr>
        </table>
        <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php esc_html_e( 'Save Changes', 'autoptimize' ); ?>" /></p>
    </form>
    <script>
        jQuery(document).ready(function() {
            jQuery("#autoptimize_imgopt_checkbox").change(function() {
                if (this.checked) {
                    jQuery("#autoptimize_imgopt_quality").show("slow");
                    jQuery("#autoptimize_imgopt_ngimg").show("slow");
                    jQuery("#autoptimize_imgopt_optimization_exclusions").show("slow");
                } else {
                    jQuery("#autoptimize_imgopt_quality").hide("slow");
                    jQuery("#autoptimize_imgopt_ngimg").hide("slow");
                    jQuery("#autoptimize_imgopt_optimization_exclusions").hide("slow");
                }
            });
            jQuery("#autoptimize_imgopt_lazyload_checkbox").change(function() {
                if (this.checked) {
                    jQuery(".autoptimize_lazyload_child").show("slow");
                } else {
                    jQuery(".autoptimize_lazyload_child").hide("slow");
                }
            });
        });
    </script>
        <?php
    }

    /**
     * Ïmg opt status as used on dashboard.
     */
    public function get_imgopt_status_notice() {
        if ( $this->imgopt_active() && apply_filters( 'autoptimize_filter_imgopt_status_shortpixel', true ) ) {
            $_imgopt_notice  = '';
            $_stat           = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
            $_site_host      = AUTOPTIMIZE_SITE_DOMAIN;
            $_imgopt_upsell  = 'https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell';
            $_imgopt_assoc   = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
            $_imgopt_unreach = 'https://shortpixel.helpscoutdocs.com/article/148-why-are-my-images-redirected-from-cdn-shortpixel-ai';

            if ( is_array( $_stat ) ) {
                if ( 1 == $_stat['Status'] ) {
                    // translators: "add more credits" will appear in a "a href".
                    $_imgopt_notice = sprintf( esc_html__( 'Your ShortPixel image optimization and CDN quota is almost used, make sure you %1$sadd more credits%2$s to avoid slowing down your website %4$sor consider using %3$sAutoptimize Pro%2$s which comes with (nearly) unlimited image optimization%5$s but also automated critical CSS and extra booster options.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>', '<a href="https://autoptimize.com/pro/" target="_blank">', '<strong>', '</strong>' );
                } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
                    // translators: "add more credits" will appear in a "a href".
                    $_imgopt_notice = sprintf( esc_html__( 'Your ShortPixel image optimization and CDN quota has been exhausted, %1$sadd more credits%2$s to continue to quickly deliver optimized images on your website %4$sor consider using %3$sAutoptimize Pro%2$s which comes with (nearly) unlimited image optimization%5$s but also automated critical CSS and extra booster options.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>', '<a href="https://autoptimize.com/pro/" target="_blank">', '<strong>', '</strong>' );
                    // translators: "associate your domain" will appear in a "a href".
                    $_imgopt_notice = $_imgopt_notice . ' ' . sprintf( esc_html__( 'If you have enough CDN quota remaining, then you may need to %1$sassociate your domain%2$s to your Shortpixel account.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $_imgopt_assoc . '" target="_blank">', '</a>' );
                } elseif ( -3 == $_stat['Status'] ) {
                    // translators: "check the documentation here" will appear in a "a href".
                    $_imgopt_notice = sprintf( esc_html__( 'It seems ShortPixel image optimization is not able to fetch images from your site, %1$scheck the documentation here%2$s for more information', 'autoptimize' ), '<a href="' . $_imgopt_unreach . '" target="_blank">', '</a>' );
                } else {
                    $_imgopt_upsell = 'https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell';
                    // translators: "log in to check your account" will appear in a "a href".
                    $_imgopt_notice = sprintf( esc_html__( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
                }

                // add info on freshness + refresh link if status is not 2 (good shape).
                if ( 2 != $_stat['Status'] ) {
                    $_imgopt_stats_refresh_url = add_query_arg(
                        array(
                            'page'                => 'autoptimize_imgopt',
                            'refreshImgProvStats' => '1',
                        ),
                        admin_url( 'options-general.php' )
                    );
                    if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
                        $_imgopt_stats_last_run = esc_html__( 'based on status at ', 'autoptimize' ) . date_i18n( autoptimizeOptionWrapper::get_option( 'time_format' ), $_stat['timestamp'] );
                    } else {
                        $_imgopt_stats_last_run = esc_html__( 'based on previously fetched data', 'autoptimize' );
                    }
                    $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
                    // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
                    $_imgopt_notice .= sprintf( esc_html__( 'you can click %1$shere to refresh your quota status%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
                }

                // and make the full notice filterable.
                $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );

                return array(
                    'status' => $_stat['Status'],
                    'notice' => $_imgopt_notice,
                );
            }
        }
        return false;
    }

    public static function get_imgopt_status_notice_wrapper() {
        // needed for notice being shown in autoptimizeCacheChecker.php.
        $self = new self();
        return $self->get_imgopt_status_notice();
    }

    /**
     * Get img provider stats (used to display notice).
     *
     * @param bool $_refresh Should the stats be forcefully refreshed or not.
     */
    public function query_img_provider_stats( $_refresh = false ) {
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) && apply_filters( 'autoptimize_filter_imgopt_status_shortpixel', true ) ) {
            $url      = '';
            $stat_dom = 'https://no-cdn.shortpixel.ai/';
            $endpoint = $stat_dom . 'read-domain/';
            $domain   = AUTOPTIMIZE_SITE_DOMAIN;

            // make sure parse_url result makes sense, keeping $url empty if not.
            if ( $domain && ! empty( $domain ) ) {
                $url = $endpoint . $domain;
                if ( true === $_refresh ) {
                    $url = $url . '/refresh';
                }
            }

            $url = apply_filters(
                'autoptimize_filter_imgopt_stat_url',
                $url
            );

            // only do the remote call if $url is not empty to make sure no parse_url
            // weirdness results in useless calls.
            if ( ! empty( $url ) ) {
                $response = wp_remote_get( $url );
                if ( ! is_wp_error( $response ) ) {
                    if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
                        $stats = json_decode( wp_remote_retrieve_body( $response ), true );
                        autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_provider_stat', $stats );
                    }
                }
            }
        }
    }

    public static function get_img_provider_stats()
    {
        // wrapper around query_img_provider_stats() so we can get to $this->options from cronjob() in autoptimizeCacheChecker.
        $self = new self();
        return $self->query_img_provider_stats();
    }

    /**
     * Determines and returns the service launch status.
     *
     * @return bool
     */
    public function launch_ok()
    {
        static $launch_status = null;

        if ( null === $launch_status ) {
            $avail_imgopt = '';
            if ( is_array( $this->options ) && array_key_exists( 'availabilities', $this->options ) && is_array( $this->options['availabilities'] ) && array_key_exists( 'extra_imgopt', $this->options['availabilities'] ) ) {
                $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
            }

            $magic_number  = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
            $has_launched  = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_launched', '' );
            $launch_status = false;
            if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
                $launch_status = true;
                if ( ! $has_launched ) {
                    autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_launched', 'on' );
                }
            }
        }

        return $launch_status;
    }

    public static function launch_ok_wrapper() {
        // needed for "plug" notice in autoptimizeMain.php.
        $self = new self();
        return $self->launch_ok();
    }

    public function get_imgopt_provider_userstatus() {
        static $_provider_userstatus = null;

        if ( is_null( $_provider_userstatus ) ) {
            $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
            if ( is_array( $_stat ) ) {
                if ( array_key_exists( 'Status', $_stat ) ) {
                    $_provider_userstatus['Status'] = $_stat['Status'];
                } else {
                    // if no stats then we assume all is well.
                    $_provider_userstatus['Status'] = 2;
                }
                if ( array_key_exists( 'timestamp', $_stat ) ) {
                    $_provider_userstatus['timestamp'] = $_stat['timestamp'];
                } else {
                    // if no timestamp then we return "".
                    $_provider_userstatus['timestamp'] = '';
                }
            } else {
                // no provider_stat yet, assume/ return all OK.
                $_provider_userstatus['Status']    = 2;
                $_provider_userstatus['timestamp'] = '';
            }
        }

        return $_provider_userstatus;
    }
}
PK.�1\T�H��
�
static/toolbar.jsnu�[���jQuery( document ).ready(function()
{
	var percentage = jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar' ).attr('percentage');
	var rotate = percentage * 1.8;

	jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .mask.full, #wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill' ).css({
		'-webkit-transform' : 'rotate(' + rotate + 'deg)',
		'-ms-transform'     : 'rotate(' + rotate + 'deg)',
		'transform'         : 'rotate(' + rotate + 'deg)'
	});

	// Fix Background color of circle percentage & delete cache to fit with the current color theme
	jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .inset' ).css( 'background-color',  jQuery( '#wp-admin-bar-autoptimize .ab-sub-wrapper' ).css( 'background-color') );
	jQuery( '#wp-admin-bar-autoptimize-delete-cache .ab-item' ).css( 'background-color',  jQuery( '#wpadminbar' ).css( 'background-color') );

	jQuery( '#wp-admin-bar-autoptimize-default li' ).on('click', function(e)
	{
		var id = ( typeof e.target.id != 'undefined' && e.target.id ) ? e.target.id : jQuery( e.target ).parent( 'li' ).attr( 'id' );
		var action = '';

		if( id == 'wp-admin-bar-autoptimize-delete-cache' ){
			action = 'autoptimize_delete_cache';
		} else {
			return;
		}

		// Remove the class "hover" from drop-down Autoptimize menu to hide it.
		jQuery( '#wp-admin-bar-autoptimize' ).removeClass( 'hover' );

		// Create and Show the Autoptimize Loading Modal
		var modal_loading = jQuery( '<div class="autoptimize-loading"></div>' ).appendTo( 'body' ).show();

		var success = function() {
			// Reset output values & class names of cache info
			jQuery( '#wp-admin-bar-autoptimize-cache-info .size' ).attr( 'class', 'size green' ).html( '0.00 B' );
			jQuery( '#wp-admin-bar-autoptimize-cache-info .files' ).html( '0' );
			jQuery( '#wp-admin-bar-autoptimize-cache-info .percentage .numbers' ).attr( 'class', 'numbers green' ).html( '0%' );
			jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill' ).attr( 'class', 'fill bg-green' );

			// Reset the class names of bullet icon
			jQuery( '#wp-admin-bar-autoptimize' ).attr( 'class', 'menupop bullet-green' );

			// Reset the Radial Bar progress
			jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .mask.full, #wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill' ).css({
				'-webkit-transform'    : 'rotate(0deg)',
				'-ms-transform'        : 'rotate(0deg)',
				'transform'            : 'rotate(0deg)'
			});
		};

		var notice = function() {
			jQuery( '<div id="ao-delete-cache-timeout" class="notice notice-error is-dismissible"><p><strong><span style="display:block;clear:both;">' + autoptimize_ajax_object.error_msg + '</span></strong></p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' +  autoptimize_ajax_object.dismiss_msg + '</span></button></div><br>' ).insertAfter( '#wpbody .wrap h1:first-of-type' ).show();
		};

		jQuery.ajax({
			type     : 'GET',
			url      : autoptimize_ajax_object.ajaxurl,
			data     : {'action':action, 'nonce':autoptimize_ajax_object.nonce},
			dataType : 'json',
			cache    : false,
			timeout  : 9000,
			success  : function( cleared )
			{
				// Remove the Autoptimize Loading Modal
				modal_loading.remove();
				if ( cleared ) {
					success();
				} else {
					notice();
				}
			},
			error: function( jqXHR, textStatus )
			{
				// Remove the Autoptimize Loading Modal
				modal_loading.remove();

				// WordPress Admin Notice
				notice();
			}
		});
	});
});
PK.�1\�אh��static/toolbar.min.cssnu�[���.autoptimize-loading{display:none;position:fixed;background-color:rgba(102,102,102,.8);background-image:url(loading.gif);background-position:center;background-repeat:no-repeat;top:0;left:0;width:100%;height:100%;z-index:9000000000}#wp-admin-bar-autoptimize .white{color:#eee}#wp-admin-bar-autoptimize .green{color:#26bd26}#wp-admin-bar-autoptimize .orange{color:#ec9103}#wp-admin-bar-autoptimize .red{color:#ea1919}#wp-admin-bar-autoptimize .bg-green{background:#26bd26}#wp-admin-bar-autoptimize .bg-orange{background:#ec9103}#wp-admin-bar-autoptimize .bg-red{background:#ea1919}#wp-admin-bar-autoptimize.bullet-green .ab-icon:before,#wp-admin-bar-autoptimize.bullet-green:hover .ab-icon:before{content:"\f159";color:#02ca02;font-size:14px}#wp-admin-bar-autoptimize.bullet-orange .ab-icon:before,#wp-admin-bar-autoptimize.bullet-orange:hover .ab-icon:before{content:"\f159";color:#ec9103;font-size:14px}#wp-admin-bar-autoptimize.bullet-red .ab-icon:before,#wp-admin-bar-autoptimize.bullet-red:hover .ab-icon:before{content:"\f159";color:#ea1919;font-size:14px;-webkit-animation:blink 1s step-end infinite;animation:blink 1s step-end infinite}@-webkit-keyframes blink{50%{visibility:hidden}}@keyframes blink{50%{visibility:hidden}}#wp-admin-bar-autoptimize table,#wp-admin-bar-autoptimize th,#wp-admin-bar-autoptimize td{border:0 !important}#wp-admin-bar-autoptimize-default{padding-top:0 !important}#wp-admin-bar-autoptimize-delete-cache .ab-item{cursor:pointer !important;background:#464b50}#wp-admin-bar-autoptimize-delete-cache .ab-item:hover{color:rgba(240,245,250,.85) !important;background:#b57373 !important}#wp-admin-bar-autoptimize-cache-info{padding-top:8px !important;padding-bottom:8px !important}#wp-admin-bar-autoptimize-cache-info,#wp-admin-bar-autoptimize-cache-info .ab-item{height:auto !important;cursor:default !important}#wp-admin-bar-autoptimize-cache-info td+td{padding-left:3px}#wp-admin-bar-autoptimize-cache-info .ab-item,#wp-admin-bar-autoptimize-cache-info .ab-item:hover{color:#b4b9be !important}#wp-admin-bar-autoptimize-cache-info .ab-item>p{display:block}#wp-admin-bar-autoptimize-cache-info .ab-item p,#wp-admin-bar-autoptimize-cache-info .ab-item td{font-size:11px !important;line-height:16px !important}#wp-admin-bar-autoptimize-cache-info .ab-item table{display:inline-block !important;margin-left:10px !important}.autoptimize-radial-bar{display:inline-block !important;margin-top:5px !important}.autoptimize-radial-bar,.autoptimize-radial-bar .mask,.autoptimize-radial-bar .fill,.autoptimize-radial-bar .shadow{width:36px !important;height:36px !important}.autoptimize-radial-bar{background-color:#d6dadc}.autoptimize-radial-bar .fill{background-color:#02ca02}.autoptimize-radial-bar .numbers{color:#02ca02}.autoptimize-radial-bar .mask{clip:rect(0px,36px,36px,18px)}.autoptimize-radial-bar .fill{clip:rect(0px,18px,36px,0px)}.autoptimize-radial-bar .inset{width:26px !important;height:26px !important;margin-left:5px !important;margin-top:5px !important;background-color:#32373c}.autoptimize-radial-bar .percentage{width:26px !important;height:16px !important;line-height:11px !important;top:7px !important;left:0 !important;overflow:hidden}.autoptimize-radial-bar .numbers{width:26px !important;font-weight:600 !important;font-size:9px !important;margin-top:-5px !important;display:inline-block;vertical-align:top;text-align:center}.autoptimize-radial-bar .inset{box-shadow:3px 3px 5px rgba(0,0,0,.3) !important}.autoptimize-radial-bar .shadow{box-shadow:3px 3px 5px rgba(0,0,0,.3) inset !important}.autoptimize-radial-bar .mask,.autoptimize-radial-bar .fill,.autoptimize-radial-bar .shadow,.autoptimize-radial-bar .inset,.autoptimize-radial-bar .percentage{position:absolute !important}.autoptimize-radial-bar,.autoptimize-radial-bar .mask,.autoptimize-radial-bar .fill,.autoptimize-radial-bar .shadow,.autoptimize-radial-bar .inset{border-radius:50% !important}#wp-admin-bar-autoptimize tr{border:0 !important}#wp-admin-bar-autoptimize td{background-color:#32373c !important}
PK.�1\Oy�i_
_
static/toolbar.min.jsnu�[���jQuery(document).ready(function()
{var percentage=jQuery('#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar').attr('percentage');var rotate=percentage*1.8;jQuery('#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .mask.full, #wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill').css({'-webkit-transform':'rotate('+rotate+'deg)','-ms-transform':'rotate('+rotate+'deg)','transform':'rotate('+rotate+'deg)'});jQuery('#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .inset').css('background-color',jQuery('#wp-admin-bar-autoptimize .ab-sub-wrapper').css('background-color'));jQuery('#wp-admin-bar-autoptimize-delete-cache .ab-item').css('background-color',jQuery('#wpadminbar').css('background-color'));jQuery('#wp-admin-bar-autoptimize-default li').on('click',function(e)
{var id=(typeof e.target.id!='undefined'&&e.target.id)?e.target.id:jQuery(e.target).parent('li').attr('id');var action='';if(id=='wp-admin-bar-autoptimize-delete-cache'){action='autoptimize_delete_cache';}else{return;}
jQuery('#wp-admin-bar-autoptimize').removeClass('hover');var modal_loading=jQuery('<div class="autoptimize-loading"></div>').appendTo('body').show();var success=function(){jQuery('#wp-admin-bar-autoptimize-cache-info .size').attr('class','size green').html('0.00 B');jQuery('#wp-admin-bar-autoptimize-cache-info .files').html('0');jQuery('#wp-admin-bar-autoptimize-cache-info .percentage .numbers').attr('class','numbers green').html('0%');jQuery('#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill').attr('class','fill bg-green');jQuery('#wp-admin-bar-autoptimize').attr('class','menupop bullet-green');jQuery('#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .mask.full, #wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill').css({'-webkit-transform':'rotate(0deg)','-ms-transform':'rotate(0deg)','transform':'rotate(0deg)'});};var notice=function(){jQuery('<div id="ao-delete-cache-timeout" class="notice notice-error is-dismissible"><p><strong><span style="display:block;clear:both;">'+autoptimize_ajax_object.error_msg+'</span></strong></p><button type="button" class="notice-dismiss"><span class="screen-reader-text">'+autoptimize_ajax_object.dismiss_msg+'</span></button></div><br>').insertAfter('#wpbody .wrap h1:first-of-type').show();};jQuery.ajax({type:'GET',url:autoptimize_ajax_object.ajaxurl,data:{'action':action,'nonce':autoptimize_ajax_object.nonce},dataType:'json',cache:false,timeout:9000,success:function(cleared)
{modal_loading.remove();if(cleared){success();}else{notice();}},error:function(jqXHR,textStatus)
{modal_loading.remove();notice();}});});});
PK.�1\4�''"static/exit-survey/exit-survey.cssnu�[���
tr[data-slug="autoptimize"] span.deactivate{
    position: relative;
}

.ao-feedback {
    background: #fff;
    max-width: 400px;
    z-index: 10000;
    box-shadow: 0 0 15px -5px rgba(0, 0, 0, .5);
    transition: all .3s ease-out;
}


.ao-feedback .popup--header {
    position: relative;
    background-color: #e5e5e5;
}

.ao-feedback .popup--header h5 {
    margin: 0;
    font-size: 16px;
    padding: 10px 15px;
    color: #222;
    font-weight: 900;
    text-align: left;
}

.ao-feedback .popup--body {
    padding: 15px;
    padding-top: 5px;
}

.ao-feedback .popup--form {
    margin: 0;
    font-size: 13px;
    padding-top: 10px;
}

.ao-feedback .popup--form input[type="radio"] {
    margin: 0 10px 0 0;
}

.ao-feedback .popup--form input[type="radio"]:checked ~ textarea {
    display: block;
}

.ao-feedback .popup--form textarea {
    width: 100%;
    margin: 10px 0 0;
    display: none;
    max-height: 150px;
}

.ao-feedback .popup--form input[type='email'] {
    width: 100%;
    margin: 10px 0 0;
}

.ao-feedback .popup--form input[type='email']:invalid {
    color:red;
    border-color:red;
}

.ao-feedback .popup--form p.last-attempt {
    display: none;
}

.ao-feedback li {
    display: flex;
    align-items: center;
    margin-bottom: 15px;
    flex-wrap: wrap;
}

.ao-feedback li label {
    max-width: 90%;
}

.ao-feedback li:last-child {
    margin-bottom: 0;
}

.ao-feedback .popup--footer {
    padding: 0 15px 15px;
}

.ao-feedback .actions {
    display: flex;
    flex-wrap: wrap;
}

.info-disclosure-link {
    width: 100%;
    margin-bottom: 15px;
}

.ao-feedback .info-disclosure-content {
    max-height: 0;
    overflow: hidden;
    width: 100%;
    transition: .3s ease;
}

.ao-feedback .info-disclosure-content.active {
    max-height: 300px;
}

.ao-feedback .info-disclosure-content p {
    margin: 0;
}

.ao-feedback .info-disclosure-content ul {
    margin: 10px 0;
    border-radius: 3px;
}

.ao-feedback .info-disclosure-content ul li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0;
    padding: 5px 0;
    border-bottom: 1px solid #ccc;
}

.ao-feedback .buttons {
    display: flex;
    width: 100%;
}

.ao-feedback .buttons input:nth-child(2) {
    margin: auto;
}

.ao-feedback .buttons input:last-child {
    margin-left: auto;
}

.ao-plugin-uninstall-feedback-popup .popup--header:before {
    content: "";
    display: block;
    position: absolute;
    border: 20px solid #e5e5e5;
    left: -10px;
    top: 50%;
    border-top: 20px solid transparent;
    border-bottom: 20px solid transparent;
    border-left: 0;
    transform: translateY(-50%);
}

.ao-plugin-uninstall-feedback-popup {
    display: none;
    position: absolute;
    white-space: normal;
    width: 400px;
    left: 122%;
    top: -21px;
}

.ao-plugin-uninstall-feedback-popup.sending-feedback .popup--body i {
    animation: rotation 2s infinite linear;
    display: block;
    float: none;
    align-items: center;
    width: 100%;
    margin: 0 auto;
    height: 100%;
    background: transparent;
    padding: 0;
}

.ao-plugin-uninstall-feedback-popup.sending-feedback .popup--body i:before {
    padding: 0;
    background: transparent;
    box-shadow: none;
    color: #b4b9be
}


.ao-plugin-uninstall-feedback-popup.active {
    display: block;
}

body.ao-feedback-open .ao-feedback-overlay {
    content: "";
    display: block;
    background-color: rgba(0, 0, 0, 0.5);
    top: 0;
    bottom: 0;
    right: 0;
    left: 0;
    z-index: 10000;
    position: fixed;
}

@media (max-width: 768px) {
    .ao-plugin-uninstall-feedback-popup {
        position: fixed;
        max-width: 100%;
        margin: 0 auto;
        left: 50%;
        top: 50px;
        transform: translateX(-50%);
    }

    .ao-plugin-uninstall-feedback-popup .popup--header:before {
        display: none;
    }
}
PK.�1\��FF!static/exit-survey/exit-survey.jsnu�[���(function ($) {
    $(document).ready(function () {
        var targetElement = 'tr[data-plugin="autoptimize/autoptimize.php"] span.deactivate a';
        var redirectUrl = $(targetElement).attr('href');
        if ($('.ao-feedback-overlay').length === 0) {
            $('body').prepend('<div class="ao-feedback-overlay"></div>');
        }
        $('#ao_uninstall_feedback_popup').appendTo($(targetElement).parent());

        $(targetElement).on('click', function (e) {
			e.preventDefault();
            $('#ao_uninstall_feedback_popup ').addClass('active');
            $('body').addClass('ao-feedback-open');
            $('.ao-feedback-overlay').on('click', function () {
                $('#ao_uninstall_feedback_popup ').removeClass('active');
                $('body').removeClass('ao-feedback-open');
            });
        });

        $('#ao_uninstall_feedback_popup .info-disclosure-link').on('click', function (e) {
            e.preventDefault();
            $(this).parent().find('.info-disclosure-content').toggleClass('active');
        });

        $('#ao_uninstall_feedback_popup input[type="radio"]').on('change', function () {
            var radio = $(this);
            $('p.last-attempt').hide();
            if (radio.parent().find('textarea').length > 0 &&
                radio.parent().find('textarea').val().length === 0) {
                $('#ao_uninstall_feedback_popup #ao-deactivate-yes').attr('disabled', 'disabled');
                radio.parent().find('textarea').on('keyup', function (e) {
                    if ($(this).val().length === 0) {
                        $('#ao_uninstall_feedback_popup #ao-deactivate-yes').attr('disabled', 'disabled');
                    } else if ( $('#ao_feedback998')[0].checkValidity() == true ) {
                        $('#ao_uninstall_feedback_popup #ao-deactivate-yes').removeAttr('disabled');
                    }
                });
            } else {
                if ( $('#ao_feedback998')[0].checkValidity() == true ) {
                    $('#ao_uninstall_feedback_popup #ao-deactivate-yes').removeAttr('disabled');
                }
                $(this).siblings('p.last-attempt').show();
            }
        });

        $('#ao_feedback998').on('keyup', function (e) {
            email_node = $(this);
            email_val = email_node.val();
            if ( email_val.length > 0 && email_node[0].checkValidity() == false ) {
                $('#ao_uninstall_feedback_popup #ao-deactivate-yes').attr('disabled', 'disabled');
            } else if ( $( '#ao_uninstall_feedback_popup input[name="ao-deactivate-option"]:checked' ).length > 0 ) {
                $('#ao_uninstall_feedback_popup #ao-deactivate-yes').removeAttr('disabled');
            }
        });

        $('#ao_uninstall_feedback_popup #ao-deactivate-no').on('click', function (e) {
            e.preventDefault();
            e.stopPropagation();
            $(targetElement).unbind('click');
            $('body').removeClass('ao-feedback-open');
            $('#ao_uninstall_feedback_popup').remove();
            if (redirectUrl !== '') {
                location.href = redirectUrl;
            }
        });

        $('#ao_uninstall_feedback_popup #ao-deactivate-cancel').on('click', function (e) {
            e.preventDefault();
            e.stopPropagation();
            $('#ao_uninstall_feedback_popup ').removeClass('active');
            $('body').removeClass('ao-feedback-open');
        });
        
        $('#ao_feedback_email_toggle').on('click', function (e) {
            $('#ao_feedback998').toggle();
        });

        $('#ao_uninstall_feedback_popup #ao-deactivate-yes').on('click', function (e) {
            e.preventDefault();
            e.stopPropagation();
            $(targetElement).unbind('click');

            var modal_data = JSON.parse(atob($('#ao_uninstall_feedback_popup').data('modal')))

            var selectedOption = $( '#ao_uninstall_feedback_popup input[name="ao-deactivate-option"]:checked' );

            var reason;

            if( selectedOption.attr("id") === "ao_feedback999" ){
                reason = 'Other: ' + selectedOption.parent().find('textarea').val().trim()
            }else{
                reason = selectedOption.parent().find('label').attr('data-reason').trim()
            }

            var data = {
                'url': modal_data.home,
                'reason': reason,
                'type': 'WP ' + $('#core_version').text().trim(),
                'version' : 'AO ' + $('#ao_plugin_version').text().trim(),
                'email': $('#ao_feedback998').val().trim(),
            };

            $.ajax({
                type: 'POST',
                url: atob( modal_data.dest ),
                data: data,
                complete() {
                    $('body').removeClass('ao-feedback-open');
                    $('#ao_uninstall_feedback_popup').remove();
                    if (redirectUrl !== '') {
                        location.href = redirectUrl;
                    }
                },
                beforeSend() {
                    $('#ao_uninstall_feedback_popup').addClass('sending-feedback');
                    $('#ao_uninstall_feedback_popup .popup--footer').remove();
                    $('#ao_uninstall_feedback_popup .popup--body').html('<i class="dashicons dashicons-update-alt"></i>');
                }
            });
        });
    });
})(jQuery);
PK.�1\*�pƵ�static/toolbar.cssnu�[���/* Loading Modal */
.autoptimize-loading
{
	display: none;
	position: fixed;
	background-color: rgba(102, 102, 102, 0.8);
	background-image: url('loading.gif');
	background-position: center;
	background-repeat: no-repeat;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	z-index: 9000000000;
}

/* Toolbar Font Colors */
#wp-admin-bar-autoptimize .white
{
	color: #EEE;
}

#wp-admin-bar-autoptimize .green
{
	color: #26BD26;
}

#wp-admin-bar-autoptimize .orange
{
	color: #EC9103;
}

#wp-admin-bar-autoptimize .red
{
	color: #EA1919;
}

#wp-admin-bar-autoptimize .bg-green
{
	background: #26BD26;
}

#wp-admin-bar-autoptimize .bg-orange
{
	background: #EC9103;
}

#wp-admin-bar-autoptimize .bg-red
{
	background: #EA1919;
}

/* Toolbar Bullet Icons */
#wp-admin-bar-autoptimize.bullet-green .ab-icon::before,
#wp-admin-bar-autoptimize.bullet-green:hover .ab-icon::before
{
	content: "\f159";
	color: #02CA02;
	font-size: 14px;
}

#wp-admin-bar-autoptimize.bullet-orange .ab-icon::before,
#wp-admin-bar-autoptimize.bullet-orange:hover .ab-icon::before
{
	content: "\f159";
	color: #EC9103;
	font-size: 14px;
}

#wp-admin-bar-autoptimize.bullet-red .ab-icon::before,
#wp-admin-bar-autoptimize.bullet-red:hover .ab-icon::before
{
	content: "\f159";
	color: #EA1919;
	font-size: 14px;
	-webkit-animation: blink 1s step-end infinite;
	animation: blink 1s step-end infinite;
}

@-webkit-keyframes blink { 50% { visibility: hidden; }}
        @keyframes blink { 50% { visibility: hidden; }}


/* Some cosmetic Toolbar things */
#wp-admin-bar-autoptimize table, #wp-admin-bar-autoptimize  th, #wp-admin-bar-autoptimize  td
{
    border: 0px !important;
}

#wp-admin-bar-autoptimize-default
{
	padding-top: 0 !important;
}

#wp-admin-bar-autoptimize-delete-cache .ab-item
{
	cursor: pointer !important;
	background: #464b50;
}

#wp-admin-bar-autoptimize-delete-cache .ab-item:hover
{
	color: rgba(240,245,250,0.85) !important;
	background: #B57373 !important;
}

#wp-admin-bar-autoptimize-cache-info
{
	padding-top: 8px !important;
	padding-bottom: 8px !important;
}

#wp-admin-bar-autoptimize-cache-info,
#wp-admin-bar-autoptimize-cache-info .ab-item
{
	height: auto !important;
	cursor: default !important;
}

#wp-admin-bar-autoptimize-cache-info td + td
{
	padding-left: 3px;
}

#wp-admin-bar-autoptimize-cache-info .ab-item,
#wp-admin-bar-autoptimize-cache-info .ab-item:hover
{
	color: #b4b9be !important;
}

#wp-admin-bar-autoptimize-cache-info .ab-item > p
{
	display: block;
}

#wp-admin-bar-autoptimize-cache-info .ab-item p,
#wp-admin-bar-autoptimize-cache-info .ab-item td
{
	font-size: 11px !important;
	line-height: 16px !important;
}

#wp-admin-bar-autoptimize-cache-info .ab-item table
{
	display: inline-block !important;
	margin-left: 10px !important;
}

/* Radial Bar */
.autoptimize-radial-bar
{
	display: inline-block !important;
	margin-top: 5px !important;
}
.autoptimize-radial-bar,
.autoptimize-radial-bar	.mask,
.autoptimize-radial-bar .fill,
.autoptimize-radial-bar .shadow
{
	width				: 36px !important;
	height				: 36px !important;
}
.autoptimize-radial-bar
{
	background-color	: #d6dadc;
}
.autoptimize-radial-bar .fill
{
	background-color	: #02ca02;
}
.autoptimize-radial-bar .numbers
{
	color	: #02ca02;
}
.autoptimize-radial-bar .mask
{
	clip				: rect(0px, 36px, 36px, 18px);
}
.autoptimize-radial-bar .fill
{
	clip				: rect(0px, 18px, 36px, 0px);
}
.autoptimize-radial-bar .inset
{
	width				: 26px !important;
	height				: 26px !important;

	margin-left			: 5px !important;
	margin-top			: 5px !important;

	background-color	: #32373c;
}
.autoptimize-radial-bar .percentage
{
	width				: 26px !important;
	height				: 16px !important;
	line-height			: 11px !important;

	top					: 7px !important;
	left				: 0px !important;

	overflow			: hidden;
}
.autoptimize-radial-bar .numbers
{
	width				: 26px !important;
	font-weight			: 600 !important;
	font-size			: 9px !important;

	margin-top			: -5px !important;

	display				: inline-block;
	vertical-align		: top;
	text-align			: center;
}

.autoptimize-radial-bar .inset
{
	box-shadow			: 3px 3px 5px rgba(0,0,0,0.3) !important;
}
.autoptimize-radial-bar .shadow
{
	box-shadow			: 3px 3px 5px rgba(0,0,0,0.3) inset !important;
}

.autoptimize-radial-bar	.mask,
.autoptimize-radial-bar .fill,
.autoptimize-radial-bar .shadow,
.autoptimize-radial-bar .inset,
.autoptimize-radial-bar .percentage
{
	position			: absolute !important;
}

.autoptimize-radial-bar,
.autoptimize-radial-bar .mask,
.autoptimize-radial-bar .fill,
.autoptimize-radial-bar .shadow,
.autoptimize-radial-bar .inset
{
	border-radius		: 50% !important;
}

/* fixes for toolbar on frontend for other themes messing things up */
#wp-admin-bar-autoptimize tr{border:0 !important}
#wp-admin-bar-autoptimize td{background-color:#32373c !important}
PK.�1\Z�j
  static/loading.gifnu�[���GIF89aXX�O�Ϻ�n������!�NETSCAPE2.0!�XMP DataXMP<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.0-c061 64.140949, 2010/12/07-10:57:01        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmp.did:57E0EBF072226811BD6584DB68DC4565" xmpMM:DocumentID="xmp.did:BB499BE6526A11E19592A88DB5429835" xmpMM:InstanceID="xmp.iid:BB499BE5526A11E19592A88DB5429835" xmp:CreatorTool="Adobe Photoshop CS5.1 Macintosh"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:FCAFA0629E226811BD6584DB68DC4565" stRef:documentID="xmp.did:57E0EBF072226811BD6584DB68DC4565"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"! 

	!�,XX�H����IY�8�z��^(:�If��¶0���KӶ���n���7ƀ"�ѣd��gO
57�*%��jeA��BM�ǵ�mV[���Y��y�q�nw��}.�X��{
�U���~x�}�i�����������������������������������������ø����ɳ�Ǟ����������������ܾ�������7��������������5��WL�ja�w��7�a��D&����G�1Ѹ�ETI�\ɲe)0cʜI�f8s��ɳg�@�
J�hͣHe��ɴiΟE�J
����N��,0��Q�`a.�JVT�h�
;�lֳi����mS�q�εZ��ּ^�V��w'^�D'%\�)W�ze���c�_%Ӥ�0f���Vf�s��XG�,���L΅YCv�4�j��ӎ	�on���ھ��w��z�5�r���:N<:���q_�,{��۹��������>/7�w��z��~��#�.O6�!�,X:���%�I��$�ͻ`�\dIyh��,d��*m���9�m���s�]�u�%gKV��EE�WUu
eM�T�-�QcW�r��Gkv��n��sM~��{7}~����=������B��������������������{�����u�����������������������ξ�ѻ�����������޿��������������������������m���?r��f� ��	�-���!�rN�V�z�;��m���"Đ�F��X�$J�$ñ,�2$L^%��xs�̙�vF�+�A�	���	4R�J��T_ԩ��ʻʴ��x\��rYX�NɖM����
�v
w�\�i�2;��.7�b�j$궝`m	!�,XX���%�I��8��;�`(f^	�h��좾�Զq�ά������^	H�;�d�T����U���n�.�
��
s

P��n;���o�;ʮ� �$y�+��A�����I����������������������������������������������•�ŧ���ˑ��������ֿؼڹܶ޲��䦴����������������H�ݼ|�����~#�+����3
�ȑ��A� 0�H�@ǎCVY2�I�)U&d���K�1e^���fĜ:���	�gA�Ab$j��@�IE.e��)<�I�R�gubT�Z�v�J�+��TǾ�-S�˚��(�vlu��yףܹ��� �L�-	V���⿀�|��ԭ�(W|3׾�CJ�	���.Ko���ͨ������ k7L!�,:X�(����I�����"�`ō���.�N�ھh��th�x��/����戄)K���lN����ڨ"����f�ǀ��;T�>7<'?���dyN��{vZ~�y�h�c�����������������������������������������������������������Ŏǣ��˞͍���ӝ��כ����߰�������������������������
H�`*\Ȱa��lH�bˆ
���raƏ'vI$H�$9���2eŕ[���̙kF���·<{���Q(E�E�2DJ0hO���*q�ҜTCZ��3�ԙY�rUv�W�eϦL�u,۱]��%��P�m�օ��mع#!�,XX�����I��8�ͻ��#�L��h��lˑ0$���Y��[n��5�W��)�Ռ=�'�4T%]���6��Y��U}��Ֆ��sj����{|gw*jy�
}ps�%��A���=���k�
�h�c��`�[��X�S����K���D��P�d��~����q�����L�ɓ�:���l���-���������������������������������.�Ap	\�-�@�8����~^ė���{
~�2��%����r��w-U�,s�L�5��d�S��=u��o(Qs?�"5�T Ӧ�B'u*§V�aͺ�*ׯ`ê#@��ٳhӊݖ��[�k����6n�x�ڽ��޾~���w��†�"N�v1c��7�,r��gW�,��cόA'm��`ӀQ�U��u�˘�R�횰�!�,X:�8����I��8�ͻ��#�L��h��lˑ0$���Y��[n��5�W��)�Ռ=�'�4T%]���6��Y��U}��Ֆ��sj����{|gw*jy�
}ps�%��A���=���k�
�h�c��`�[��X�S����K���D��P�d��~����q�����L�ɓ�:���l���-������������������������������������	xϟAy�\��Cv	2�X��È�(R��s�7�9��
$C�Iv3��?��Xt��6�i�
�@�y�l��C�}wi҂K�5}
5*��T+Z�z4�V�X�n�*��ذT�v5��S�eŶ]+,ݴs�⵫�m^�^$!�,XX�H����I��8�ͻ��#�L��h��lˑ0$���Y��[n��5�W��)�Ռ=�'�4T%]���6��Y��U}��Ֆ��sj����{|gw*jy�
}ps�%��A���=���k�
�h�c��`�[��X�S����K���D��P�d��~����q�����L�ɓ�:���l���-���������������������������������.��wj;xP�B�&�P"A�-ĸO��?��<��O�=�M�C9Oe;��\��	O�:�5m�ÉP':�|��)�Т��"%wt鸦N�A��m*Ղ8����u�Ү`Ê=��ٳhӪ�M�۷g�F�Kw�ܺx��-�7�޾x��+x��†�M|�.c�cC+9-��.W�,��cόA'm��`ӀQ�U��1沬�~�n!�,:X�H����I�����"�`ō���.�N�ھh��th�x��/����戄)K���lN����ڨ"����f�ǃ��;T�>7<'?���dyN��{vZ~�y�h�c���������������������������������������������������������ÍŦ��ɭ����Ѡ�ԟ��ز����ߜ������������������������
H0���*\Ȑ�Ç
2�H!ċT��q��9���cȑKz<���J�,[v|�0�L�4!ڼi1�Ý<I��t�P�E��R�Mo>��1)Ω �Z�5�L�Y�&�kK�fQ��*v�X�Sӎt�VhW�[����\�	;PK.�1\?�������autoptimizeConfig.phpnu�[���<?php
/**
 * Main configuration logic.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeConfig
{
    /**
     * Options.
     *
     * @var array
     */
    private $config = null;

    /**
     * Singleton instance.
     *
     * @var self|null
     */
    static private $instance = null;

    /**
     * Options.
     *
     * @var bool
     */
    private $settings_screen_do_remote_http = true;

    /**
     * Singleton.
     */
    private function __construct()
    {
        if ( is_admin() ) {
            // Add the admin page and settings.
            if ( autoptimizeOptionWrapper::is_ao_active_for_network() ) {
                add_action( 'network_admin_menu', array( $this, 'addmenu' ) );
            }

            add_action( 'admin_menu', array( $this, 'addmenu' ) );
            add_action( 'admin_init', array( $this, 'registersettings' ) );
            add_action( 'admin_init', array( 'PAnD', 'init' ) );

            // Set meta info.
            if ( function_exists( 'plugin_row_meta' ) ) {
                // 2.8 and higher.
                add_filter( 'plugin_row_meta', array( $this, 'setmeta' ), 10, 2 );
            } elseif ( function_exists( 'post_class' ) ) {
                // 2.7 and lower.
                $plugin = plugin_basename( AUTOPTIMIZE_PLUGIN_DIR . 'autoptimize.php' );
                add_filter( 'plugin_action_links_' . $plugin, array( $this, 'setmeta' ) );
            }

            // Clean cache?
            if ( autoptimizeOptionWrapper::get_option( 'autoptimize_cache_clean' ) ) {
                autoptimizeCache::clearall();
                autoptimizeOptionWrapper::update_option( 'autoptimize_cache_clean', 0 );
            }

            $this->settings_screen_do_remote_http = apply_filters( 'autoptimize_settingsscreen_remotehttp', $this->settings_screen_do_remote_http );

            if ( $this->is_ao_meta_settings_active() ) {
                $meta_box = new autoptimizeMetabox();
            }
        }

        // Adds the Autoptimize Toolbar to the Admin bar.
        // (we load outside the is_admin check so it's also displayed on the frontend toolbar).
        $toolbar = new autoptimizeToolbar();
    }

    /**
     * Instantiates aoconfig.
     *
     * @return autoptimizeConfig
     */
    static public function instance()
    {
        // Only one instance.
        if ( null === self::$instance ) {
            self::$instance = new autoptimizeConfig();
        }

        return self::$instance;
    }

    public function show_network_message() {
        ?>
        <div class="wrap">
            <h1><?php esc_html_e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
            <?php echo $this->ao_admin_tabs(); ?>
            <p style="font-size:120%;"><?php echo apply_filters( 'autoptimize_filter_settingsscreen_multisite_network_message', esc_html__( 'Autoptimize is enabled and configured on a WordPress network level. Please contact your network administrator if you need Autoptimize settings changed.', 'autoptimize' ) ); ?></p>
        </div>
        <?php
    }

    public function show_config()
    {
        // phpcs:disable Generic.WhiteSpace.ScopeIndent.IncorrectExact
        // phpcs:disable Generic.WhiteSpace.ScopeIndent.Incorrect
        $conf = self::instance();
        ?>
<style>
/* title and button */
#ao_title_and_button:after {content:''; display:block; clear:both;}

/* form */
.itemDetail {
    background: #fff;
    border: 1px solid #ccc;
    padding: 15px;
    margin: 15px 10px 10px 0;
}
.itemTitle {
    margin-top: 0;
}

.form-table th{font-weight:normal;}
#autoptimize_main form input:invalid {box-shadow: 0 0 1px 1px red;}
#autoptimize_main .cb_label {display: block; padding-left: 25px; text-indent: -25px;}
#autoptimize_main .form-table th {padding-top: 15px; padding-bottom: 15px;}
#autoptimize_main .js_aggregate td, #autoptimize_main .js_aggregate th, #autoptimize_main .js_not_aggregate td, #autoptimize_main .js_not_aggregate th{padding-top:0px;}

/* rss block */
#futtta_feed ul{list-style:outside;}
#futtta_feed {font-size:medium; margin:0px 20px;}

/* banner + unslider */
.autoptimize_banner {
    margin: 0 38px;
    padding-bottom: 5px;
}
.autoptimize_banner ul li {
    font-size:medium;
    text-align:center;
}
.unslider {
    position:relative;
}
.unslider-arrow {
    display: block;
    left: unset;
    margin-top: -35px;
    margin-left: 7px;
    margin-right: 7px;
    border-radius: 32px;
    background: rgba(0, 0, 0, 0.10) no-repeat 50% 50%;
    color: rgba(255, 255, 255, 0.8);
    font: normal 20px/1 dashicons;
    speak: none;
    padding: 3px 2px 3px 4px;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
.unslider-arrow:hover {
    background-color: rgba(0, 0, 0, 0.20);
    color: #FFF;
}
.unslider-arrow.prev {
    padding: 3px 4px 3px 2px;
}
.unslider-arrow.next {
    right: 0px;
}
.unslider-arrow.prev::before {
    content: "\f341";
}
.unslider-arrow.next::before {
    content: "\f345";
}

/* responsive stuff: hide admin-feed on smaller screens */
@media (min-width: 961px) {
    #autoptimize_main {float:left;width:69%;}
    #autoptimize_admin_feed{float:right;width:30%;display:block !important;}
    }
@media (max-width: 960px) {
    #autoptimize_main {width:100%;}
    #autoptimize_admin_feed {width:0%;display:none !important;}
}
@media (max-width: 782px) {
    #autoptimize_main input[type="checkbox"] {margin-left: 10px;}
    #autoptimize_main .cb_label {display: block; padding-left: 45px; text-indent: -45px;}
}
</style>

<div class="wrap">

<div id="autoptimize_main">
    <h1 id="ao_title"><?php apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html_e( 'Autoptimize Pro Settings', 'autoptimize' ) : esc_html_e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
    <?php echo $this->ao_admin_tabs(); ?>

<form method="post" action="<?php echo admin_url( 'options.php' ); ?>">
<?php settings_fields( 'autoptimize' ); ?>

<ul>

<?php
// Only show enable site configuration in network site option.
if ( is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
?>
    <li class="itemDetail multiSite">
        <h2 class="itemTitle"><?php esc_html_e( 'Multisite Options', 'autoptimize' ); ?></h2>
        <table class="form-table">
            <tr valign="top">
                <th scope="row"><?php esc_html_e( 'Enable site configuration?', 'autoptimize' ); ?></th>
                <td><label class="cb_label"><input type="checkbox" id="autoptimize_enable_site_config" name="autoptimize_enable_site_config" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_enable_site_config' ) ? 'checked="checked" ' : ''; ?>/>
                <?php esc_html_e( 'Enable Autoptimize configuration per site.', 'autoptimize' ); ?></label></td>
            </tr>
        </table>
    </li>
<?php } else { ?>
    <input type="hidden" id="autoptimize_enable_site_config" name="autoptimize_enable_site_config" value="on" />
<?php } ?>

<li class="itemDetail">
<h2 class="itemTitle"><?php esc_html_e( 'JavaScript Options', 'autoptimize' ); ?></h2>
<table class="form-table">
<tr valign="top">
<th scope="row"><?php esc_html_e( 'Optimize JavaScript Code?', 'autoptimize' ); ?></th>
<td><input type="checkbox" id="autoptimize_js" name="autoptimize_js" <?php echo $conf->get( 'autoptimize_js' ) ? 'checked="checked" ' : ''; ?>/></td>
</tr>
<tr valign="top" class="js_sub js_aggregate_master">
<th scope="row"><?php esc_html_e( 'Aggregate JS-files?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" id="autoptimize_js_aggregate" name="autoptimize_js_aggregate" <?php echo $conf->get( 'autoptimize_js_aggregate' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Aggregate all linked JS-files to have them loaded non-render blocking?', 'autoptimize' ); ?></label></td>
</tr>
<tr valign="top" class="js_sub js_aggregate hidden">
<th scope="row">&emsp;<?php esc_html_e( 'Also aggregate inline JS?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_include_inline" <?php echo $conf->get( 'autoptimize_js_include_inline' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Let Autoptimize also extract JS from the HTML (discouraged as it can make Autoptimize\'s cache size grow quickly)', 'autoptimize' ); ?></label></td>
</tr>
<tr valign="top" class="js_sub js_aggregate hidden">
<th scope="row">&emsp;<?php esc_html_e( 'Force JavaScript in &lt;head&gt;?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_forcehead" <?php echo $conf->get( 'autoptimize_js_forcehead' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Load JavaScript early (discouraged as it makes the JS render blocking)', 'autoptimize' ); ?></label></td>
</tr>
<tr valign="top" class="js_sub js_aggregate hidden">
<th scope="row">&emsp;<?php esc_html_e( 'Add try-catch wrapping?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_trycatch" <?php echo $conf->get( 'autoptimize_js_trycatch' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'If your aggregated scripts break because of a JS-error, you might want to try this, but generally discouraged.', 'autoptimize' ); ?></label></td>
</tr>
<tr valign="top" class="js_sub js_not_aggregate_master">
<th scope="row"><?php esc_html_e( 'Do not aggregate but defer?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" id="autoptimize_js_defer_not_aggregate" name="autoptimize_js_defer_not_aggregate" <?php echo $conf->get( 'autoptimize_js_defer_not_aggregate' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Individual JS-files will be minified and deferred, making them non-render-blocking.', 'autoptimize' ); ?></label></td>
</tr>
<tr valign="top" id="js_defer_inline" class="js_sub js_not_aggregate hidden">
<th scope="row">&emsp;<?php esc_html_e( 'Also defer inline JS?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_defer_inline" <?php echo $conf->get( 'autoptimize_js_defer_inline' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Also defer inline JS. Generally this will allow all JS to be deferred, so you should remove default exclusions, test and only exclude specific items if still needed.', 'autoptimize' ); ?></label></td>
</tr>
<?php if ( autoptimizeOptionWrapper::get_option( 'autoptimize_js_justhead' ) ) { ?>
<tr valign="top" class="js_sub js_aggregate">
<th scope="row">
<?php
    esc_html_e( 'Look for scripts only in &lt;head&gt;?', 'autoptimize' );
    echo ' <i>' . esc_html__( '(deprecated)', 'autoptimize' ) . '</i>';
?>
</th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_justhead" <?php echo $conf->get( 'autoptimize_js_justhead' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Mostly useful in combination with previous option when using jQuery-based templates, but might help keeping cache size under control.', 'autoptimize' ); ?></label></td>
</tr>
<?php } ?>
<tr valign="top" class="js_sub">
<th scope="row"><?php esc_html_e( 'Exclude scripts from Autoptimize:', 'autoptimize' ); ?></th>
<td><label><input type="text" pattern="[^\*]*" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo esc_attr( autoptimizeOptionWrapper::get_option( 'autoptimize_js_exclude', '' ) ); ?>"/><br />
<?php
echo esc_html__( 'A comma-separated list of scripts you do not want optimized, for example \'whatever.js, my_var\' (without the quotes).', 'autoptimize' ) . ' ' . esc_html__( 'Important: when "aggregate JS-files" is on, excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
?>
</label></td>
</tr>
<tr valign="top">
<th scope="row"><?php esc_html_e( 'Remove Unused JavaScript?', 'autoptimize' ); ?></th>
<td><?php esc_html_e( 'Autoptimize combines your theme & plugins\' JavaScript, but does not know what is used and what not. If Google Pagespeed Insights detects unused JavaScript, consider using a plugin like "Plugin Organizer" or similar to manage what JavaScript is added where.', 'autoptimize' ); ?></td>
</tr>
</table>
</li>

<li class="itemDetail">
<h2 class="itemTitle"><?php esc_html_e( 'CSS Options', 'autoptimize' ); ?></h2>
<table class="form-table">
<tr valign="top">
<th scope="row"><?php esc_html_e( 'Optimize CSS Code?', 'autoptimize' ); ?></th>
<td><input type="checkbox" id="autoptimize_css" name="autoptimize_css" <?php echo $conf->get( 'autoptimize_css' ) ? 'checked="checked" ' : ''; ?>/></td>
</tr>
<tr class="css_sub" valign="top">
<th scope="row"><?php esc_html_e( 'Aggregate CSS-files?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" id="autoptimize_css_aggregate" name="autoptimize_css_aggregate" <?php echo $conf->get( 'autoptimize_css_aggregate' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Aggregate all linked CSS-files? If this option is off, the individual CSS-files will remain in place but will be minified.', 'autoptimize' ); ?></label></td>
</tr>
<tr valign="top" class="css_sub css_aggregate">
<th scope="row"><?php esc_html_e( 'Also aggregate inline CSS?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_include_inline" <?php echo $conf->get( 'autoptimize_css_include_inline', '1' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Check this option for Autoptimize to also aggregate CSS in the HTML.', 'autoptimize' ); ?></label></td>
</tr>
<tr class="css_sub css_aggregate" valign="top">
<th scope="row"><?php esc_html_e( 'Generate data: URIs for images?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_datauris" <?php echo $conf->get( 'autoptimize_css_datauris' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Enable this to include small background-images in the CSS itself instead of as separate downloads.', 'autoptimize' ); ?></label></td>
</tr>
<?php if ( autoptimizeOptionWrapper::get_option( 'autoptimize_css_justhead' ) ) { ?>
<tr valign="top" class="css_sub css_aggregate">
<th scope="row">
<?php
esc_html_e( 'Look for styles only in &lt;head&gt;?', 'autoptimize' );
echo ' <i>' . esc_html__( '(deprecated)', 'autoptimize' ) . '</i>';
?>
</th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_justhead" <?php echo $conf->get( 'autoptimize_css_justhead' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Don\'t autoptimize CSS outside the head-section. If the cache gets big, you might want to enable this.', 'autoptimize' ); ?></label></td>
</tr>
<?php } ?>
<tr valign="top" class="css_sub">
<th scope="row"><?php esc_html_e( 'Eliminate render-blocking CSS?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_defer" id="autoptimize_css_defer" <?php echo $conf->get( 'autoptimize_css_defer' ) ? 'checked="checked" ' : ''; ?>/>
<?php
_e( 'Inline "above the fold CSS" while loading the main autoptimized CSS only after page load. <a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank">Check the FAQ</a> for more info.', 'autoptimize' );
echo ' ';
$critcss_settings_url = get_admin_url( null, 'options-general.php?page=ao_critcss' );
// translators: links "autoptimize critical CSS" tab.
echo sprintf( esc_html__( 'You can manually create rules for different types of pages or have this done fully automated on the %s tab.', 'autoptimize' ), '<a href="' . $critcss_settings_url . '">CriticalCSS</a>' );
?>
</label></td>
</tr>
<tr valign="top" class="css_sub" id="autoptimize_css_defer_inline">
<th scope="row"></th>
<td><label><textarea rows="10" cols="10" style="width:100%;" spellcheck="false" placeholder="<?php esc_html_e( 'Paste the above the fold CSS here. You can leave this empty when using the automated Critical CSS integration.', 'autoptimize' ); ?>" name="autoptimize_css_defer_inline"><?php echo autoptimizeStyles::sanitize_css( autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer_inline' ) ); ?></textarea></label></td>
</tr>
<tr valign="top" class="css_sub css_aggregate">
<th scope="row"><?php esc_html_e( 'Inline all CSS?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" id="autoptimize_css_inline" name="autoptimize_css_inline" <?php echo $conf->get( 'autoptimize_css_inline' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Inlining all CSS is an easy way to stop the CSS from being render-blocking, but is generally not recommended because the size of the HTML increases significantly. Additionally it might push meta-tags down to a position where e.g. Facebook and Whatsapp will not find them any more, breaking thumbnails when sharing.', 'autoptimize' ); ?></label></td>
</tr>
<tr valign="top" class="css_sub">
<th scope="row"><?php esc_html_e( 'Exclude CSS from Autoptimize:', 'autoptimize' ); ?></th>
<td><label><input type="text" pattern="[^\*]*" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo esc_attr( $conf->get( 'autoptimize_css_exclude', '' ) ); ?>"/><br />
<?php
echo esc_html__( 'A comma-separated list of CSS you want to exclude from being optimized.', 'autoptimize' ) . ' ' . esc_html__( 'Important: excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
?>
</label></td>
</tr>
<?php 
    $_availabilities = autoptimizeOptionWrapper::get_option( 'autoptimize_service_availablity' );
    if ( empty( $_availabilities ) || ! is_array( $_availabilities ) || ! array_key_exists( 'rapidload', $_availabilities ) || ! array_key_exists( 'status', $_availabilities['rapidload'] ) ) {
        $rapidload_true = true;
    } else if ( $_availabilities['rapidload']['status'] === 'up' ) {
        $rapidload_true = true;
    } else if ( $_availabilities['rapidload']['status'] !== 'up' ) {
        $rapidload_true = false;
    } else {
        $rapidload_true = false;
    }
?>
<?php if ( $rapidload_true && false === autoptimizeUtils::is_plugin_active( 'unusedcss/unusedcss.php' ) ) { ?>
<tr valign="top">
<th scope="row"><?php esc_html_e( 'Remove Unused CSS?', 'autoptimize' ); ?></th>
<?php
$_rapidload_link = 'https://misc.optimizingmatters.com/partners/?from=csssettings&partner=rapidload';
?>
<td><?php 
// translators: a link to rapidload + strong tags
echo sprintf( esc_html__( 'If Google Pagespeed Insights detects unused CSS, consider using %1$sthe premium Rapidload service%2$s to %3$sreduce your site\'s CSS size to up to 90&#37;%4$s, resulting in a slimmer, faster site!', 'autoptimize' ), '<a href="' . $_rapidload_link . '" target="_blank">', '</a>', '<strong>', '</strong>' ); ?></td>
</tr>
<?php } ?>
</table>
</li>

<li class="itemDetail">
<h2 class="itemTitle"><?php esc_html_e( 'HTML Options', 'autoptimize' ); ?></h2>
<table class="form-table">
<tr valign="top">
<th scope="row"><?php esc_html_e( 'Optimize HTML Code?', 'autoptimize' ); ?></th>
<td><input type="checkbox" id="autoptimize_html" name="autoptimize_html" <?php echo $conf->get( 'autoptimize_html' ) ? 'checked="checked" ' : ''; ?>/></td>
</tr>
<tr class="html_sub" valign="top">
<th scope="row"><?php esc_html_e( 'Also minify inline JS/ CSS?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_html_minify_inline" <?php echo $conf->get( 'autoptimize_html_minify_inline' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Enable this if you want inline JS or CSS to be minified as well.', 'autoptimize' ); ?></label></td>
</tr>
<tr class="html_sub" valign="top">
<th scope="row"><?php esc_html_e( 'Keep HTML comments?', 'autoptimize' ); ?></th>
<td><label class="cb_label"><input type="checkbox" name="autoptimize_html_keepcomments" <?php echo $conf->get( 'autoptimize_html_keepcomments' ) ? 'checked="checked" ' : ''; ?>/>
<?php esc_html_e( 'Enable this if you want HTML comments to remain in the page.', 'autoptimize' ); ?></label></td>
</tr>
</table>
</li>

<li class="itemDetail">
<h2 class="itemTitle"><?php esc_html_e( 'CDN Options', 'autoptimize' ); ?></h2>
<table class="form-table">
<tr valign="top">
<th scope="row"><?php esc_html_e( 'CDN Base URL', 'autoptimize' ); ?></th>
<?php
if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'autoptimize_filter_cdn_set_by_imgopt', false ) ) {
    // cdn set by imgopt, not to be changealbe in the settings.
    $cdn_editable    = 'disabled';
    $cdn_placeholder = 'placeholder="' . esc_html__( 'The CDN has automatically been set to make use of the image optimization CDN.', 'autoptimize' ) . ' "';
    $cdn_description = '';
} else {
    $cdn_editable    = '';
    $cdn_placeholder = 'placeholder="' . esc_html__( 'example: //cdn.yoursite.com/', 'autoptimize' ) . ' "';
    $cdn_description = esc_html__( 'Enter your CDN root URL to enable CDN for Autoptimized files. The URL can be http, https or protocol-relative. This is not needed for Cloudflare.', 'autoptimize' );
}
?>
<td><label><input id="cdn_url" type="text" name="autoptimize_cdn_url" pattern="^(https?:)?\/\/([\da-z\.-]+)\.([\da-z\.]{2,6})([\/\w \.-]*)*(:\d{2,5})?\/?$" style="width:100%" <?php echo $cdn_placeholder . $cdn_editable; ?> value="<?php echo esc_url( autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' ), array( 'http', 'https' ) ); ?>" /><br />
<?php echo $cdn_description; ?>
</label></td>
</tr>
</table>
</li>

<li class="itemDetail">
<h2 class="itemTitle"><?php esc_html_e( 'Cache Info', 'autoptimize' ); ?></h2>
<table class="form-table" >
<tr valign="top" >
<th scope="row"><?php esc_html_e( 'Cache folder', 'autoptimize' ); ?></th>
<td><?php echo htmlentities( AUTOPTIMIZE_CACHE_DIR ); ?></td>
</tr>
<tr valign="top" >
<th scope="row"><?php esc_html_e( 'Can we write?', 'autoptimize' ); ?></th>
<td><?php echo ( autoptimizeCache::cacheavail() ? esc_html__( 'Yes', 'autoptimize' ) : esc_html__( 'No', 'autoptimize' ) ); ?></td>
</tr>
<tr valign="top" >
<th scope="row"><?php esc_html_e( 'Cached styles and scripts', 'autoptimize' ); ?></th>
<td>
    <?php
    $ao_stat_arr = autoptimizeCache::stats();
    if ( ! empty( $ao_stat_arr ) && is_array( $ao_stat_arr ) ) {
        $ao_cache_size = size_format( $ao_stat_arr[1], 2 );
        $details       = '';
        if ( $ao_cache_size > 0 ) {
            $details = ', ~' . $ao_cache_size . ' total';
        }
        // translators: Kilobytes + timestamp shown.
        printf( esc_html__( '%1$s files, totalling %2$s (calculated at %3$s)', 'autoptimize' ), $ao_stat_arr[0], $ao_cache_size, wp_date( 'H:i', $ao_stat_arr[2] ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
    }
    ?>
</td>
</tr>
</table>
</li>

<li class="itemDetail">
<h2 class="itemTitle"><?php esc_html_e( 'Misc Options', 'autoptimize' ); ?></h2>
<table class="form-table">
    <tr valign="top">
    <th scope="row"><?php esc_html_e( 'Save aggregated script/css as static files?', 'autoptimize' ); ?></th>
    <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_nogzip" <?php echo $conf->get( 'autoptimize_cache_nogzip' ) ? 'checked="checked" ' : ''; ?>/>
    <?php esc_html_e( 'By default files saved are static css/js, uncheck this option if your webserver doesn\'t properly handle the compression and expiry.', 'autoptimize' ); ?></label></td>
    </tr>
    <?php
    $_min_excl_class = '';
    if ( ! $conf->get( 'autoptimize_css_aggregate' ) && ! $conf->get( 'autoptimize_js_aggregate' ) ) {
        $_min_excl_class = 'hidden';
    }
    ?>
    <tr valign="top" id="min_excl_row" class="<?php echo $_min_excl_class; ?>">
        <th scope="row"><?php esc_html_e( 'Minify excluded CSS and JS files?', 'autoptimize' ); ?></th>
        <td><label class="cb_label"><input type="checkbox" name="autoptimize_minify_excluded" <?php echo $conf->get( 'autoptimize_minify_excluded' ) ? 'checked="checked" ' : ''; ?>/>
        <?php esc_html_e( 'When aggregating JS or CSS, excluded files that are not minified (based on filename) are by default minified by Autoptimize despite being excluded. Uncheck this option if anything breaks despite excluding.', 'autoptimize' ); ?></label></td>
    </tr>
    <tr valign="top">
        <th scope="row"><?php esc_html_e( 'Enable 404 fallbacks?', 'autoptimize' ); ?></th>
        <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_fallback" <?php echo $conf->get( 'autoptimize_cache_fallback' ) ? 'checked="checked" ' : ''; ?>/>
        <?php
        // translators; just 2 opening and closing <code> tags.
        printf( esc_html__( 'Sometimes Autoptimized JS/ CSS is referenced in cached HTML but is already removed, resulting in broken sites. With this option on, Autoptimize will try to redirect those not-found files to "fallback"-versions, keeping the page/ site somewhat intact. In some cases this will require extra web-server level configuration to ensure %1$swp-content/autoptimize_404_handler.php%2$s is set to handle 404\'s in %1$swp-content/cache/autoptimize%2$s.', 'autoptimize' ), '<code>', '</code>' );
        ?>
        </label></td>
    </tr>
    <tr valign="top">
    <th scope="row"><?php esc_html_e( 'Also optimize for logged in editors/ administrators?', 'autoptimize' ); ?></th>
    <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_logged" <?php echo $conf->get( 'autoptimize_optimize_logged' ) ? 'checked="checked" ' : ''; ?>/>
    <?php esc_html_e( 'By default Autoptimize is also active for logged on editors/ administrators, uncheck this option if you don\'t want Autoptimize to optimize when logged in e.g. to use a pagebuilder.', 'autoptimize' ); ?></label></td>
    </tr>
    <?php
    if ( function_exists( 'is_checkout' ) || function_exists( 'is_cart' ) || function_exists( 'edd_is_checkout' ) || function_exists( 'wpsc_is_cart' ) || function_exists( 'wpsc_is_checkout' ) ) {
    ?>
    <tr valign="top" >
        <th scope="row"><?php esc_html_e( 'Also optimize shop cart/ checkout?', 'autoptimize' ); ?></th>
        <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_checkout" <?php echo $conf->get( 'autoptimize_optimize_checkout' ) ? 'checked="checked" ' : ''; ?>/>
            <?php esc_html_e( 'By default Autoptimize is also active on your shop\'s cart/ checkout, uncheck not to optimize those.', 'autoptimize' ); ?></label>
        </td>
    </tr>
    <?php } ?>
    <?php
    if ( true === apply_filters( 'autoptimize_filter_enable_meta_ao_settings', true ) ) {
    ?>
    <tr valign="top">
    <th scope="row"><?php esc_html_e( 'Enable configuration per post/ page?', 'autoptimize' ); ?></th>
    <td><label class="cb_label"><input type="checkbox" name="autoptimize_enable_meta_ao_settings" <?php echo $conf->get( 'autoptimize_enable_meta_ao_settings' ) ? 'checked="checked" ' : ''; ?>/>
    <?php esc_html_e( 'Add a "metabox" to the post/ page edit screen allowing different optimizations to be turned off on a per post/ page level?', 'autoptimize' ); ?></label></td>
    </tr>
    <?php } ?>
    <tr valign="top">
    <th scope="row"><?php esc_html_e( 'Disable extra compatibilty logic?', 'autoptimize' ); ?></th>
    <td><label class="cb_label"><input type="checkbox" name="autoptimize_installed_before_compatibility" <?php echo $conf->get( 'autoptimize_installed_before_compatibility' ) ? 'checked="checked" ' : ''; ?>/>
    <?php esc_html_e( 'Autoptimize applies extra "compatibiity logic" to prevent issues with JS optimization (for e.g. Gutenberg blocks, Revolution Slider, jQuery-heavy plugins, ...) but may sometimes be a bit too careful. If you have render-blocking JS issues, you can try disabling this logic here. Make sure to test your site thoroughly though!', 'autoptimize' ); ?></label></td>
    </tr>
</table>
</li>

</ul>

<p class="submit">
<input type="submit" class="button-secondary" value="<?php esc_html_e( 'Save Changes', 'autoptimize' ); ?>" />
<input type="submit" class="button-primary" name="autoptimize_cache_clean" value="<?php esc_html_e( 'Save Changes and Empty Cache', 'autoptimize' ); ?>" />
</p>

</form>
</div>
<div id="autoptimize_admin_feed">
    <?php if ( apply_filters( 'autoptimize_filter_show_partner_tabs', true ) ) { ?>
    <div class="autoptimize_banner hidden">
    <ul>
    <?php
    if ( $this->settings_screen_do_remote_http ) {
        $ao_banner = get_transient( 'autoptimize_banner' );
        if ( empty( $ao_banner ) ) {
            $banner_resp = wp_remote_get( 'https://misc.optimizingmatters.com/autoptimize_news.html?ao_ver=' . AUTOPTIMIZE_PLUGIN_VERSION );
            if ( ! is_wp_error( $banner_resp ) ) {
                if ( '200' == wp_remote_retrieve_response_code( $banner_resp ) ) {
                    $ao_banner = wp_kses_post( wp_remote_retrieve_body( $banner_resp ) );
                    set_transient( 'autoptimize_banner', $ao_banner, WEEK_IN_SECONDS );
                }
            }
        }
        echo $ao_banner;
    }
    ?>
        <li>
        <?php
            // translators: link to the AO FAQ.
            printf( esc_html__( 'Need help? %1$sCheck out the FAQ here%2$s.', 'autoptimize' ), '<a href=\'https://wordpress.org/plugins/autoptimize/faq/\'>', '</a>' );
        ?>
        </li>
        <li><?php esc_html_e( 'Happy with Autoptimize?', 'autoptimize' ); ?><br /><a href="<?php echo network_admin_url(); ?>plugin-install.php?tab=search&type=author&s=optimizingmatters"><?php esc_html_e( 'Try my other plugins!', 'autoptimize' ); ?></a></li>
    </ul>
    </div>
    <?php } ?>
    <div style="margin-left:10px;margin-top:-5px;">
        <h2>
            <?php esc_html_e( 'Autoptimize news', 'autoptimize' ); ?>
        </h2>
        <div id="futtta_feed">
            <div id="autoptimizefeed">
                <?php $this->get_futtta_feeds( 'http://feeds.feedburner.com/futtta_autoptimize' ); ?>
            </div>
        </div>
    </div>
    <?php if ( apply_filters( 'autoptimize_filter_show_partner_tabs', true ) ) { ?>
    <div style="float:right;margin:50px 15px;"><a href="https://blog.futtta.be/2013/10/21/do-not-donate-to-me/" target="_blank"><img width="100px" height="85px" src="<?php echo plugins_url() . '/' . plugin_basename( dirname( __FILE__ ) ) . '/external/do_not_donate_smallest.png'; ?>" title="<?php esc_html_e( 'Do not donate for this plugin!', 'autoptimize' ); ?>"></a></div>
    <?php } ?>
</div>
<script type="text/javascript">
    jQuery(document).ready(function() {
        check_ini_state();

        jQuery('.autoptimize_banner').unslider({autoplay:true, delay:3500, infinite: false, arrows:{prev:'<a class="unslider-arrow prev"></a>', next:'<a class="unslider-arrow next"></a>'}}).fadeTo("slow",1).show();

        jQuery( "#autoptimize_html" ).change(function() {
            if (this.checked) {
                jQuery(".html_sub:visible").fadeTo("fast",1);
            } else {
                jQuery(".html_sub:visible").fadeTo("fast",.33);
            }
        });

        jQuery( "#autoptimize_js" ).change(function() {
            if (this.checked) {
                jQuery(".js_sub:visible").fadeTo("fast",1);
            } else {
                jQuery(".js_sub:visible").fadeTo("fast",.33);
            }
        });

        jQuery( "#autoptimize_js_aggregate" ).change(function() {
            if (this.checked && jQuery("#autoptimize_js").prop('checked')) {
                jQuery( "#autoptimize_js_defer_not_aggregate" ).prop( 'checked', false ); // uncheck "defer not aggregate"
                jQuery( ".js_aggregate_master:visible" ).fadeTo( 'slow', 1 ); // ungrey self
                jQuery( ".js_aggregate" ).show( 'slow' ); // show sub-items
                jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'slow', .33 ); // grey out "not aggregate"
                jQuery( ".js_not_aggregate" ).hide( 'slow' ); // hide not aggregate sub-items
                jQuery( "#min_excl_row" ).show(); // make sure "minify excluded" is visible
                check_exclusions( "js", "on" );
            } else {
                jQuery( ".js_aggregate" ).hide( 'slow' ); // hide sub-itmes
                jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'slow', 1 ); // un-grey-out "not aggregate"
                if ( jQuery( "#autoptimize_css_aggregate" ).prop( 'checked' ) == false ) { // hide "minify excluded"
                    jQuery( "#min_excl_row" ).hide();
                }
                check_exclusions( "js", "off" );
            }
        });

        jQuery( "#autoptimize_js_defer_not_aggregate" ).change(function() {
            if (this.checked && jQuery("#autoptimize_js").prop('checked')) {
                jQuery( "#autoptimize_js_aggregate" ).prop( 'checked', false ); // uncheck "aggregate JS"
                jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'slow', 1 ); // ungrey self
                jQuery( ".js_not_aggregate" ).show( 'slow'); // show sub-items
                jQuery( ".js_aggregate_master:visible" ).fadeTo( 'slow', .33 ); // grey out "aggregate"
                jQuery( ".js_aggregate" ).hide( 'slow' ); // hide aggregate sub-items
                check_exclusions( "js", "off" );
            } else {
                jQuery( ".js_not_aggregate" ).hide( 'slow' ); // hide sub-items
                jQuery( ".js_aggregate_master:visible" ).fadeTo( 'slow', 1 ); // un-grey-out "aggregate"
            }
        });

        jQuery( "#autoptimize_css" ).change(function() {
            if (this.checked) {
                jQuery(".css_sub:visible").fadeTo("fast",1);
            } else {
                jQuery(".css_sub:visible").fadeTo("fast",.33);
            }
        });

        jQuery( "#autoptimize_css_aggregate" ).change(function() {
            if (this.checked && jQuery("#autoptimize_css").prop('checked')) {
                jQuery(".css_aggregate:visible").fadeTo("fast",1);
                jQuery( "#min_excl_row" ).show();
                check_exclusions( "css", "on" );
            } else {
                jQuery(".css_aggregate:visible").fadeTo("fast",.33);
                if ( jQuery( "#autoptimize_js_aggregate" ).prop('checked') == false ) {
                    jQuery( "#min_excl_row" ).hide();
                }
                check_exclusions( "css", "off" );
            }
        });

        jQuery( "#autoptimize_css_inline" ).change(function() {
            if (this.checked) {
                jQuery("#autoptimize_css_defer").prop("checked",false);
                jQuery("#autoptimize_css_defer_inline").hide("slow");
            }
        });

        jQuery( "#autoptimize_css_defer" ).change(function() {
            if (this.checked) {
                jQuery("#autoptimize_css_inline").prop("checked",false);
                jQuery("#autoptimize_css_defer_inline").show("slow");
            } else {
                jQuery("#autoptimize_css_defer_inline").hide("slow");
            }
        });

        jQuery( "#autoptimize_enable_site_config" ).change(function() {
            if (this.checked) {
                jQuery("li.itemDetail:not(.multiSite)").fadeTo("fast",.33);
            } else {
                jQuery("li.itemDetail:not(.multiSite)").fadeTo("fast",1);
            }
        });
    })

    // validate cdn_url.
    var cdn_url=document.getElementById("cdn_url");
    cdn_url_baseCSS=cdn_url.style.cssText;
    if ("validity" in cdn_url) {
        jQuery("#cdn_url").focusout(function (event) {
        if (cdn_url.validity.valid) {
            cdn_url.style.cssText=cdn_url_baseCSS;
        } else {
            cdn_url.style.cssText=cdn_url_baseCSS+"border:1px solid #f00;color:#f00;box-shadow: 0 0 2px #f00;";
        }});
    }

    function check_ini_state() {
        if (!jQuery("#autoptimize_css_defer").prop('checked')) {
            jQuery("#autoptimize_css_defer_inline").hide();
        }
        if (!jQuery("#autoptimize_html").prop('checked')) {
            jQuery(".html_sub:visible").fadeTo('fast',.33);
        }
        if (!jQuery("#autoptimize_css").prop('checked')) {
            jQuery(".css_sub:visible").fadeTo('fast',.33);
        }
        if (!jQuery("#autoptimize_css_aggregate").prop('checked')) {
            jQuery(".css_aggregate:visible").fadeTo('fast',.33);
        }
        if (jQuery("#autoptimize_js_aggregate").prop('checked')) {
            jQuery( ".js_aggregate" ).show( 'fast' );
            jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'fast', .33 );
        }
        if (jQuery("#autoptimize_js_defer_not_aggregate").prop('checked')) {
            jQuery( ".js_not_aggregate" ).show( 'fast' );
            jQuery( ".js_aggregate_master:visible" ).fadeTo( 'fast', .33 );
        }
        if (jQuery("#autoptimize_enable_site_config").prop('checked')) {
            jQuery("li.itemDetail:not(.multiSite)").fadeTo('fast',.33);
        }
        if (!jQuery("#autoptimize_js").prop('checked')) {
            jQuery(".js_sub:visible").fadeTo('fast',.33);
        }
    }

    function check_exclusions( what, state ) {
        exclusion_node = 'input[name="autoptimize_' + what + '_exclude"]';
        current_exclusion = jQuery( exclusion_node ).val();

        if ( what == "js" ) {
            default_exclusion = ", wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.min.js";
        } else if ( what == "css")  {
            default_exclusion = ", admin-bar.min.css, dashicons.min.css, wp-content/cache/, wp-content/uploads/";
        }

        default_in_current = current_exclusion.indexOf(default_exclusion);

        if ( state == "on" && default_in_current == -1 ) {
            jQuery( exclusion_node ).val( current_exclusion + default_exclusion );
        } else if ( state = "off" && current_exclusion == default_exclusion ) {
            jQuery( exclusion_node ).val( "" );
        } else if ( state = "off" && default_in_current != -1 ) {
            new_exclusion = current_exclusion.substring( 0, default_in_current) + current_exclusion.substring( default_in_current + default_exclusion.length, current_exclusion.length );
            jQuery( exclusion_node ).val( new_exclusion );
        }
    }
</script>
</div>

<?php
    }

    public function addmenu()
    {
        $_my_name = apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html__( 'Autoptimize Pro', 'autoptimize' ) : esc_html__( 'Autoptimize', 'autoptimize' );

        global $title;
        if ( empty( $title ) && true === autoptimizeUtils::is_ao_settings() ) {
            $title = $_my_name;
        }

        if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
            // multisite, network admin, ao network activated: add normal settings page at network level.
            $hook = add_submenu_page( 'settings.php', esc_html__( 'Autoptimize Options', 'autoptimize' ), $_my_name, 'manage_network_options', 'autoptimize', array( $this, 'show_config' ) );
        } elseif ( is_multisite() && ! is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_enable_site_config' ) ) {
            // multisite, ao network activated, not network admin so site specific settings, but "autoptimize_enable_site_config" is off: show "sorry, ask network admin" message iso options.
            $hook = add_options_page( esc_html__( 'Autoptimize Options', 'autoptimize' ), $_my_name, 'manage_options', 'autoptimize', array( $this, 'show_network_message' ) );
        } else {
            // default: show normal options page if not multisite, if multisite but not network activated, if multisite and network activated and "autoptimize_enable_site_config" is on.
            $hook = add_options_page( esc_html__( 'Autoptimize Options', 'autoptimize' ), $_my_name, 'manage_options', 'autoptimize', array( $this, 'show_config' ) );
        }

        add_action( 'admin_print_scripts-' . $hook, array( $this, 'autoptimize_admin_scripts' ) );
        add_action( 'admin_print_styles-' . $hook, array( $this, 'autoptimize_admin_styles' ) );
    }

    public function autoptimize_admin_scripts()
    {
        wp_enqueue_script( 'unslider', plugins_url( '/external/js/unslider.min.js', __FILE__ ), array( 'jquery' ), AUTOPTIMIZE_PLUGIN_VERSION, true );
    }

    public function autoptimize_admin_styles()
    {
        wp_enqueue_style( 'unslider', plugins_url( '/external/js/unslider.css', __FILE__ ), null, AUTOPTIMIZE_PLUGIN_VERSION );
        wp_enqueue_style( 'unslider-dots', plugins_url( '/external/js/unslider-dots.css', __FILE__ ), null, AUTOPTIMIZE_PLUGIN_VERSION );
    }

    public function registersettings() {
        register_setting( 'autoptimize', 'autoptimize_html' );
        register_setting( 'autoptimize', 'autoptimize_html_keepcomments' );
        register_setting( 'autoptimize', 'autoptimize_html_minify_inline' );
        register_setting( 'autoptimize', 'autoptimize_enable_site_config' );
        register_setting( 'autoptimize', 'autoptimize_js' );
        register_setting( 'autoptimize', 'autoptimize_js_aggregate' );
        register_setting( 'autoptimize', 'autoptimize_js_defer_not_aggregate' );
        register_setting( 'autoptimize', 'autoptimize_js_defer_inline' );
        register_setting( 'autoptimize', 'autoptimize_js_exclude' );
        register_setting( 'autoptimize', 'autoptimize_js_trycatch' );
        register_setting( 'autoptimize', 'autoptimize_js_justhead' );
        register_setting( 'autoptimize', 'autoptimize_js_forcehead' );
        register_setting( 'autoptimize', 'autoptimize_js_include_inline' );
        register_setting( 'autoptimize', 'autoptimize_css' );
        register_setting( 'autoptimize', 'autoptimize_css_aggregate' );
        register_setting( 'autoptimize', 'autoptimize_css_exclude' );
        register_setting( 'autoptimize', 'autoptimize_css_justhead' );
        register_setting( 'autoptimize', 'autoptimize_css_datauris' );
        register_setting( 'autoptimize', 'autoptimize_css_defer' );
        register_setting( 'autoptimize', 'autoptimize_css_defer_inline' );
        register_setting( 'autoptimize', 'autoptimize_css_inline' );
        register_setting( 'autoptimize', 'autoptimize_css_include_inline' );
        register_setting( 'autoptimize', 'autoptimize_cdn_url' );
        register_setting( 'autoptimize', 'autoptimize_cache_clean' );
        register_setting( 'autoptimize', 'autoptimize_cache_nogzip' );
        register_setting( 'autoptimize', 'autoptimize_optimize_logged' );
        register_setting( 'autoptimize', 'autoptimize_optimize_checkout' );
        register_setting( 'autoptimize', 'autoptimize_minify_excluded' );
        register_setting( 'autoptimize', 'autoptimize_cache_fallback' );
        register_setting( 'autoptimize', 'autoptimize_enable_meta_ao_settings' );
        register_setting( 'autoptimize', 'autoptimize_installed_before_compatibility' );
    }

    public function setmeta( $links, $file = null )
    {
        // Inspired on http://wpengineer.com/meta-links-for-wordpress-plugins/.
        // Do it only once - saves time.
        static $plugin;
        if ( empty( $plugin ) ) {
            $plugin = plugin_basename( AUTOPTIMIZE_PLUGIN_DIR . 'autoptimize.php' );
        }

        if ( null === $file ) {
            // 2.7 and lower.
            $settings_link = sprintf( '<a href="options-general.php?page=autoptimize">%s</a>', esc_html__( 'Settings' ) );
            array_unshift( $links, $settings_link );
        } else {
            // 2.8 and higher.
            // If it's us, add the link.
            if ( $file === $plugin ) {
                $newlink = array( sprintf( '<a href="options-general.php?page=autoptimize">%s</a>', esc_html__( 'Settings' ) ) );
                $links   = array_merge( $links, $newlink );
            }
        }

        return $links;
    }

    /**
     * Provides the default options.
     *
     * @return array
     */
    public static function get_defaults()
    {
        static $config = array(
            'autoptimize_html'                           => 1,
            'autoptimize_html_keepcomments'              => 0,
            'autoptimize_html_minify_inline'             => 0,
            'autoptimize_enable_site_config'             => 1,
            'autoptimize_js'                             => 1,
            'autoptimize_js_aggregate'                   => 0,
            'autoptimize_js_defer_not_aggregate'         => 1,
            'autoptimize_js_defer_inline'                => 1,
            'autoptimize_js_exclude'                     => '',
            'autoptimize_js_trycatch'                    => 0,
            'autoptimize_js_justhead'                    => 0,
            'autoptimize_js_include_inline'              => 0,
            'autoptimize_js_forcehead'                   => 0,
            'autoptimize_css'                            => 1,
            'autoptimize_css_aggregate'                  => 0,
            'autoptimize_css_exclude'                    => '',
            'autoptimize_css_justhead'                   => 0,
            'autoptimize_css_include_inline'             => 0,
            'autoptimize_css_defer'                      => 0,
            'autoptimize_css_defer_inline'               => '',
            'autoptimize_css_inline'                     => 0,
            'autoptimize_css_datauris'                   => 0,
            'autoptimize_cdn_url'                        => '',
            'autoptimize_cache_nogzip'                   => 1,
            'autoptimize_optimize_logged'                => 1,
            'autoptimize_optimize_checkout'              => 0,
            'autoptimize_minify_excluded'                => 1,
            'autoptimize_cache_fallback'                 => 1,
            'autoptimize_enable_meta_ao_settings'        => 1,
            'autoptimize_installed_before_compatibility' => 0,
        );

        return $config;
    }

    /**
     * Returns default option values for autoptimizeExtra.
     *
     * @return array
     */
    public static function get_ao_extra_default_options()
    {
        $defaults = array(
            'autoptimize_extra_checkbox_field_1' => '0',
            'autoptimize_extra_checkbox_field_0' => '0',
            'autoptimize_extra_radio_field_4'    => '1',
            'autoptimize_extra_text_field_2'     => '',
            'autoptimize_extra_text_field_3'     => '',
            'autoptimize_extra_text_field_7'     => '',
            'autoptimize_extra_checkbox_field_8' => '0',
        );

        return $defaults;
    }

    /**
     * Returns default option values for autoptimizeExtra.
     *
     * @return array
     */
    public static function get_ao_imgopt_default_options()
    {
        $defaults = array(
            'autoptimize_imgopt_checkbox_field_1' => '0', // imgopt off.
            'autoptimize_imgopt_select_field_2'   => '2', // quality glossy.
            'autoptimize_imgopt_checkbox_field_3' => '0', // lazy load off.
            'autoptimize_imgopt_checkbox_field_4' => '0', // webp off (might be removed).
            'autoptimize_imgopt_text_field_5'     => '',  // lazy load exclusions empty.
            'autoptimize_imgopt_text_field_6'     => '',  // optimization exclusions empty.
            'autoptimize_imgopt_number_field_7'   => '2', // lazy load from nth image (0 = lazyload all).
        );
        return $defaults;
    }

    /**
     * Returns preload JS onload handler.
     *
     * @param string $media media attribute value the JS to use.
     *
     * @return string
     */
    public static function get_ao_css_preload_onload( $media = 'all' )
    {
        $preload_onload = apply_filters( 'autoptimize_filter_css_preload_onload', "this.onload=null;this.media='" . $media . "';" );
        return $preload_onload;
    }

    public function get( $key )
    {
        if ( ! is_array( $this->config ) ) {
            // Default config.
            $config = self::get_defaults();

            // Override with user settings.
            foreach ( array_keys( $config ) as $name ) {
                $conf = autoptimizeOptionWrapper::get_option( $name );
                if ( false !== $conf ) {
                    // It was set before!
                    $config[ $name ] = $conf;
                }
            }

            // Save for next call.
            $this->config = apply_filters( 'autoptimize_filter_get_config', $config );
        }

        if ( isset( $this->config[ $key ] ) ) {
            return $this->config[ $key ];
        }

        return false;
    }

    private function get_futtta_feeds( $url ) {
        if ( $this->settings_screen_do_remote_http ) {
            $rss      = fetch_feed( $url );
            $maxitems = 0;

            if ( ! is_wp_error( $rss ) ) {
                $maxitems  = $rss->get_item_quantity( 7 );
                $rss_items = $rss->get_items( 0, $maxitems );
            }
            ?>
            <ul>
                <?php if ( 0 == $maxitems ) : ?>
                    <li><?php esc_html_e( 'No items', 'autoptimize' ); ?></li>
                <?php else : ?>
                    <?php foreach ( $rss_items as $item ) : ?>
                        <li>
                            <a href="<?php echo esc_url( $item->get_permalink() ); ?>"
                                <?php // translators: the variable contains a date. ?>
                                title="<?php printf( esc_html__( 'Posted %s', 'autoptimize' ), $item->get_date( 'j F Y | g:i a' ) ); ?>">
                                <?php echo esc_html( $item->get_title() ); ?>
                            </a>
                        </li>
                    <?php endforeach; ?>
                <?php endif; ?>
            </ul>
        <?php
        }
    }

    static function ao_admin_tabs()
    {
        // based on http://wordpress.stackexchange.com/a/58826 .
        $tabs        = apply_filters( 'autoptimize_filter_settingsscreen_tabs', array( 'autoptimize' => esc_html__( 'JS, CSS  &amp; HTML', 'autoptimize' ) ) );
        $tab_content = '';
        $tabs_count  = count( $tabs );
        if ( $tabs_count > 1 ) {
            if ( isset( $_GET['page'] ) ) {
                $current_id = $_GET['page'];
            } else {
                $current_id = 'autoptimize';
            }
            $tab_content .= '<h2 class="nav-tab-wrapper">';
            foreach ( $tabs as $tab_id => $tab_name ) {
                if ( $current_id == $tab_id ) {
                    $class = ' nav-tab-active';
                } else {
                    $class = '';
                }
                $tab_content .= '<a class="nav-tab' . $class . '" href="?page=' . $tab_id . '">' . $tab_name . '</a>';
            }
            $tab_content .= '</h2>';
        } else {
            $tab_content = '<hr/>';
        }

        return $tab_content;
    }

    /**
     * Returns true if in admin (and not in admin-ajax.php!)
     *
     * @return bool
     */
    public static function is_admin_and_not_ajax()
    {
        return ( is_admin() && ! self::doing_ajax() );
    }

    /**
     * Returns true if doing ajax.
     *
     * @return bool
     */
    protected static function doing_ajax()
    {
        if ( function_exists( 'wp_doing_ajax' ) ) {
            return wp_doing_ajax();
        }
        return ( defined( 'DOING_AJAX' ) && DOING_AJAX );
    }

    /**
     * Returns true menu or tab is to be shown.
     *
     * @return bool
     */
    public static function should_show_menu_tabs() {
        if ( ! is_multisite() || is_network_admin() || 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_enable_site_config' ) || false === autoptimizeOptionWrapper::is_ao_active_for_network() ) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns the post meta AO settings for reuse in different optimizers.
     *
     * @param string $optim What optimization we need meta setting for.
     *
     * @return bool
     */
    public static function get_post_meta_ao_settings( $optim ) {
        if ( ! autoptimizeConfig::is_ao_meta_settings_active() ) {
            // Per page/post settings not active, so always return true (as in; can be optimized).
            if ( in_array( $optim, apply_filters( 'autoptimize_filter_meta_inactive_return_false_for', array( 'ao_post_preload' ) ) ) ) {
                // but make sure to return false for text input.
                return false;
            }
            return true;
        }

        static $_meta_value = null;
        if ( null === $_meta_value ) {
            global $wp_query;
            if ( isset( $wp_query ) ) {
                $_meta_value = get_post_meta( get_the_ID(), 'ao_post_optimize', true );
            } else {
                $_meta_value = false;
            }
        }

        // If autoptimize_post_optimize !== 'on' (except for ao_post_preload, which can have other values) then always return false as all is off.
        // fixme: need unit tests to ensure below logic is sane!
        if ( empty( $_meta_value ) || ! is_array( $_meta_value ) ) {
            // no metabox values so all optimizations are a go.
            if ( in_array( $optim, apply_filters( 'autoptimize_filter_meta_optim_nonbool', array( 'ao_post_preload' ) ) ) ) {
                // but make sure to return false for text input.
                return false;
            }
            return true;
        } else if ( array_key_exists( 'ao_post_optimize', $_meta_value ) && 'on' !== $_meta_value['ao_post_optimize'] ) {
            // ao entirely off for this page.
            return false;
        } else if ( in_array( $optim, apply_filters( 'autoptimize_filter_meta_optim_with_filters', array() ) ) ) {
            // if an $optim is registered as having a filter, apply filter and return that (default false).
            return apply_filters( 'autoptimize_filter_meta_filtered_optim', false, $optim, $_meta_value );
        } else if ( array_key_exists( $optim, $_meta_value ) && empty( $_meta_value[ $optim ] ) ) {
            // sub-optimization off for this page.
            return false;
        } else if ( array_key_exists( $optim, $_meta_value ) && 'on' === $_meta_value[ $optim ] ) {
            // sub-optimization is explictly on.
            return true;
        } else if ( array_key_exists( $optim, $_meta_value ) && in_array( $optim, array( 'ao_post_preload' ) ) && ! empty( $_meta_value[ $optim ] ) ) {
            // a non-bool metabox optimization (currently only preload field), return value instead of bool.
            return $_meta_value[ $optim ];
        } else if ( in_array( $optim, array( 'ao_post_preload' ) ) && ( ! array_key_exists( $optim, $_meta_value ) || empty( $_meta_value[ $optim ] ) ) ) {
            // a non-bool metabox optimization not found or empty, so returning false.
            return false;
        } else {
            // when in doubt "go" for optimization, but this should never happen?
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( 'AO metabox logic fallback; well, how did I get here? Maybe this helps: looking for ' . $optim . ' in ' . json_encode( $_meta_value ) );
            }
            return true;
        }
    }

    /**
     * Are the post meta AO settings active (default: no)?
     *
     * @return bool
     */
    public static function is_ao_meta_settings_active() {
        static $_meta_settings_active = null;

        if ( null === $_meta_settings_active ) {
            $_meta_settings_active = apply_filters( 'autoptimize_filter_enable_meta_ao_settings', autoptimizeOptionWrapper::get_option( 'autoptimize_enable_meta_ao_settings', '1' ) );
        }

        return $_meta_settings_active;
    }
}
PK.�1\.���X�XautoptimizeUtils.phpnu�[���<?php
/**
 * General helpers.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeUtils
{
    /**
     * Returns true when mbstring is available.
     *
     * @param bool|null $override Allows overriding the decision.
     *
     * @return bool
     */
    public static function mbstring_available( $override = null )
    {
        static $available = null;

        if ( null === $available ) {
            $available = \extension_loaded( 'mbstring' );
        }

        if ( null !== $override ) {
            $available = $override;
        }

        return $available;
    }

    /**
     * Multibyte-capable strpos() if support is available on the server.
     * If not, it falls back to using \strpos().
     *
     * @param string      $haystack Haystack.
     * @param string      $needle   Needle.
     * @param int         $offset   Offset.
     * @param string|null $encoding Encoding. Default null.
     *
     * @return int|false
     */
    public static function strpos( $haystack, $needle, $offset = 0, $encoding = null )
    {
        if ( self::mbstring_available() ) {
            return ( null === $encoding ) ? \mb_strpos( $haystack, $needle, $offset ) : \mb_strpos( $haystack, $needle, $offset, $encoding );
        } else {
            return \strpos( $haystack, $needle, $offset );
        }
    }

    /**
     * Attempts to return the number of characters in the given $string if
     * mbstring is available. Returns the number of bytes
     * (instead of characters) as fallback.
     *
     * @param string      $string   String.
     * @param string|null $encoding Encoding.
     *
     * @return int Number of characters or bytes in given $string
     *             (characters if/when supported, bytes otherwise).
     */
    public static function strlen( $string, $encoding = null )
    {
        if ( self::mbstring_available() ) {
            return ( null === $encoding ) ? \mb_strlen( $string ) : \mb_strlen( $string, $encoding );
        } else {
            return \strlen( $string );
        }
    }

    /**
     * Our wrapper around implementations of \substr_replace()
     * that attempts to not break things horribly if at all possible.
     * Uses mbstring if available, before falling back to regular
     * substr_replace() (which works just fine in the majority of cases).
     *
     * @param string      $string      String.
     * @param string      $replacement Replacement.
     * @param int         $start       Start offset.
     * @param int|null    $length      Length.
     * @param string|null $encoding    Encoding.
     *
     * @return string
     */
    public static function substr_replace( $string, $replacement, $start, $length = null, $encoding = null )
    {
        if ( self::mbstring_available() ) {
            $strlen = self::strlen( $string, $encoding );

            if ( $start < 0 ) {
                if ( -$start < $strlen ) {
                    $start = $strlen + $start;
                } else {
                    $start = 0;
                }
            } elseif ( $start > $strlen ) {
                $start = $strlen;
            }

            if ( null === $length || '' === $length ) {
                $start2 = $strlen;
            } elseif ( $length < 0 ) {
                $start2 = $strlen + $length;
                if ( $start2 < $start ) {
                    $start2 = $start;
                }
            } else {
                $start2 = $start + $length;
            }

            if ( null === $encoding ) {
                $leader  = $start ? \mb_substr( $string, 0, $start ) : '';
                $trailer = ( $start2 < $strlen ) ? \mb_substr( $string, $start2, null ) : '';
            } else {
                $leader  = $start ? \mb_substr( $string, 0, $start, $encoding ) : '';
                $trailer = ( $start2 < $strlen ) ? \mb_substr( $string, $start2, null, $encoding ) : '';
            }

            return "{$leader}{$replacement}{$trailer}";
        }

        return ( null === $length ) ? \substr_replace( $string, $replacement, $start ) : \substr_replace( $string, $replacement, $start, $length );
    }

    /**
     * Decides whether this is a "subdirectory site" or not.
     *
     * @param bool $override Allows overriding the decision when needed.
     *
     * @return bool
     */
    public static function siteurl_not_root( $override = null )
    {
        static $subdir = null;

        if ( null === $subdir ) {
            $parts  = self::get_ao_wp_site_url_parts();
            $subdir = ( isset( $parts['path'] ) && ( '/' !== $parts['path'] ) );
        }

        if ( null !== $override ) {
            $subdir = $override;
        }

        return $subdir;
    }

    /**
     * Parse AUTOPTIMIZE_WP_SITE_URL into components using \parse_url(), but do
     * so only once per request/lifecycle.
     *
     * @return array
     */
    public static function get_ao_wp_site_url_parts()
    {
        static $parts = array();

        if ( empty( $parts ) ) {
            $parts = \parse_url( AUTOPTIMIZE_WP_SITE_URL );
        }

        return $parts;
    }

    /**
     * Modify given $cdn_url to include the site path when needed.
     *
     * @param string $cdn_url          CDN URL to tweak.
     * @param bool   $force_cache_miss Force a cache miss in order to be able
     *                                 to re-run the filter.
     *
     * @return string
     */
    public static function tweak_cdn_url_if_needed( $cdn_url, $force_cache_miss = false )
    {
        static $results = array();

        if ( ! isset( $results[ $cdn_url ] ) || $force_cache_miss ) {

            // In order to return unmodified input when there's no need to tweak.
            $results[ $cdn_url ] = $cdn_url;

            // Behind a default true filter for backcompat, and only for sites
            // in a subfolder/subdirectory, but still easily turned off if
            // not wanted/needed...
            if ( autoptimizeUtils::siteurl_not_root() ) {
                $check = apply_filters( 'autoptimize_filter_cdn_magic_path_check', true, $cdn_url );
                if ( $check ) {
                    $site_url_parts = autoptimizeUtils::get_ao_wp_site_url_parts();
                    $cdn_url_parts  = \parse_url( $cdn_url );
                    $schemeless     = self::is_protocol_relative( $cdn_url );
                    $cdn_url_parts  = self::maybe_replace_cdn_path( $site_url_parts, $cdn_url_parts );
                    if ( false !== $cdn_url_parts ) {
                        $results[ $cdn_url ] = self::assemble_parsed_url( $cdn_url_parts, $schemeless );
                    }
                }
            }
        }

        return $results[ $cdn_url ];
    }

    /**
     * When siteurl contains a path other than '/' and the CDN URL does not have
     * a path or it's path is '/', this will modify the CDN URL's path component
     * to match that of the siteurl.
     * This is to support "magic" CDN urls that worked that way before v2.4...
     *
     * @param array $site_url_parts Site URL components array.
     * @param array $cdn_url_parts  CDN URL components array.
     *
     * @return array|false
     */
    public static function maybe_replace_cdn_path( array $site_url_parts, array $cdn_url_parts )
    {
        if ( isset( $site_url_parts['path'] ) && '/' !== $site_url_parts['path'] ) {
            if ( ! isset( $cdn_url_parts['path'] ) || '/' === $cdn_url_parts['path'] ) {
                $cdn_url_parts['path'] = $site_url_parts['path'];
                return $cdn_url_parts;
            }
        }

        return false;
    }

    /**
     * Given an array or components returned from \parse_url(), assembles back
     * the complete URL.
     * If optional
     *
     * @param array $parsed_url URL components array.
     * @param bool  $schemeless Whether the assembled URL should be
     *                          protocol-relative (schemeless) or not.
     *
     * @return string
     */
    public static function assemble_parsed_url( array $parsed_url, $schemeless = false )
    {
        $scheme = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] . '://' : '';
        if ( $schemeless ) {
            $scheme = '//';
        }
        $host     = isset( $parsed_url['host'] ) ? $parsed_url['host'] : '';
        $port     = isset( $parsed_url['port'] ) ? ':' . $parsed_url['port'] : '';
        $user     = isset( $parsed_url['user'] ) ? $parsed_url['user'] : '';
        $pass     = isset( $parsed_url['pass'] ) ? ':' . $parsed_url['pass'] : '';
        $pass     = ( $user || $pass ) ? "$pass@" : '';
        $path     = isset( $parsed_url['path'] ) ? $parsed_url['path'] : '';
        $query    = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : '';
        $fragment = isset( $parsed_url['fragment'] ) ? '#' . $parsed_url['fragment'] : '';

        return "$scheme$user$pass$host$port$path$query$fragment";
    }

    /**
     * Returns true if given $url is protocol-relative.
     *
     * @param string $url URL to check.
     *
     * @return bool
     */
    public static function is_protocol_relative( $url )
    {
        $result = false;

        if ( ! empty( $url ) ) {
            $result = ( 0 === strpos( $url, '//' ) );
        }

        return $result;
    }

    /**
     * Canonicalizes the given path regardless of it existing or not.
     *
     * @param string $path Path to normalize.
     *
     * @return string
     */
    public static function path_canonicalize( $path )
    {
        $patterns     = array(
            '~/{2,}~',
            '~/(\./)+~',
            '~([^/\.]+/(?R)*\.{2,}/)~',
            '~\.\./~',
        );
        $replacements = array(
            '/',
            '/',
            '',
            '',
        );

        return preg_replace( $patterns, $replacements, $path );
    }

    /**
     * Checks to see if 3rd party services are available and stores result in option
     *
     * TODO This should be two separate methods.
     *
     * @param string $return_result should we return resulting service status array (default no).
     *
     * @return null|array Service status or null.
     */
    public static function check_service_availability( $return_result = false )
    {
        $service_availability_resp = wp_remote_get( 'https://misc.optimizingmatters.com/api/autoptimize_service_availablity.json?from=aomain&ver=' . AUTOPTIMIZE_PLUGIN_VERSION );
        if ( ! is_wp_error( $service_availability_resp ) ) {
            if ( '200' == wp_remote_retrieve_response_code( $service_availability_resp ) ) {
                $availabilities = json_decode( wp_remote_retrieve_body( $service_availability_resp ), true );
                if ( is_array( $availabilities ) ) {
                    autoptimizeOptionWrapper::update_option( 'autoptimize_service_availablity', $availabilities );
                    if ( $return_result ) {
                        return $availabilities;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Returns true if the string is a valid regex.
     *
     * @param string $string String, duh.
     *
     * @return bool
     */
    public static function str_is_valid_regex( $string )
    {
        set_error_handler( function() {}, E_WARNING );
        $is_regex = ( false !== preg_match( $string, '' ) );
        restore_error_handler();

        return $is_regex;
    }

    /**
     * Returns true if a certain WP plugin is active/loaded.
     *
     * @param string $plugin_file Main plugin file.
     *
     * @return bool
     */
    public static function is_plugin_active( $plugin_file )
    {
        static $ipa_exists = null;
        if ( null === $ipa_exists ) {
            if ( ! function_exists( '\is_plugin_active' ) ) {
                require_once ABSPATH . 'wp-admin/includes/plugin.php';
            }
            $ipa_exists = function_exists( '\is_plugin_active' );
        }

        return $ipa_exists && \is_plugin_active( $plugin_file );
    }

    /**
     * Returns a node without ID attrib for use in noscript tags
     *
     * @param string $node an html tag.
     *
     * @return string
     */
    public static function remove_id_from_node( $node ) {
        if ( strpos( $node, 'id=' ) === false || apply_filters( 'autoptimize_filter_utils_keep_ids', false ) ) {
            return $node;
        } else {
            return preg_replace( '#(.*) id=[\'|"].*[\'|"] (.*)#Um', '$1 $2', $node );
        }
    }

    /**
     * Returns true if given $str ends with given $test.
     *
     * @param string $str String to check.
     * @param string $test Ending to match.
     *
     * @return bool
     */
    public static function str_ends_in( $str, $test )
    {
        // @codingStandardsIgnoreStart
        // substr_compare() is bugged on 5.5.11: https://3v4l.org/qGYBH
        // return ( 0 === substr_compare( $str, $test, -strlen( $test ) ) );
        // @codingStandardsIgnoreEnd

        $length = strlen( $test );

        return ( substr( $str, -$length, $length ) === $test );
    }

    /**
     * Returns true if a pagecache is found, false if not.
     * Now used to show notice, might be used later on to (un)hide page caching in AO if no page cache found.
     *
     * @param bool $disregard_transient False by default, but can be used to ignore the transient and retest.
     *
     * @return bool
     */
    public static function find_pagecache( $disregard_transient = false ) {
        static $_found_pagecache = null;

        if ( null === $_found_pagecache ) {
            $_page_cache_constants = array(
                'NgInx' => 'NGINX_HELPER_BASENAME',
                'Kinsta' => 'KINSTAMU_VERSION',
                'Presslabs' => 'PL_INSTANCE_REF',
                'Cache Enabler' => 'CACHE_ENABLER_VERSION',
                'Speed Booster Pack' => 'SBP_PLUGIN_NAME',
                'Servebolt' => 'SERVEBOLT_PLUGIN_FILE',
                'WP CloudFlare Super Page Cache' => 'SWCFPC_PLUGIN_PATH',
                'Cachify' => 'CACHIFY_CACHE_DIR',
                'WP Rocket' => 'WP_ROCKET_CACHE_PATH',
                'WP Optimize' => 'WPO_VERSION',
                'Autoptimize Pro' => 'AO_PRO_PAGECACHE_CACHE_DIR',
            );
            $_page_cache_classes = array(
                'Pressidium' => 'Ninukis_Plugin',
                'Swift Performance' => 'Swift_Performance_Cache',
                'WP Fastest Cache' => 'WpFastestCache',
                'Quick Cache' => 'c_ws_plugin__qcache_purging_routines',
                'ZenCache' => 'zencache',
                'Comet Cache' => 'comet_cache',
                'WP Engine' => 'WpeCommon',
                'Flywheel' => 'FlywheelNginxCompat',
                'Pagely' => 'PagelyCachePurge',
            );
            $_page_cache_functions = array(
                'WP Super Cache' => 'wp_cache_clear_cache',
                'W3 Total Cache' => 'w3tc_pgcache_flush',
                'WP Fast Cache' => 'wp_fast_cache_bulk_delete_all',
                'Rapidcache' => 'rapidcache_clear_cache',
                'Siteground' => 'sg_cachepress_purge_cache',
                'WP Super Cache' => 'prune_super_cache',
            );

            $_found_pagecache = false;
            if ( true !== $disregard_transient ) {
                $_ao_pagecache_transient = 'autoptimize_pagecache_check';
                $_found_pagecache        = get_transient( $_ao_pagecache_transient );
            }

            if ( current_user_can( 'manage_options' ) && false === $_found_pagecache ) {
                // loop through known pagecache constants.
                foreach ( $_page_cache_constants as $_name => $_constant ) {
                    if ( defined( $_constant ) ) {
                        $_found_pagecache = $_name;
                        break;
                    }
                }
                // and loop through known pagecache classes.
                if ( false === $_found_pagecache ) {
                    foreach ( $_page_cache_classes as $_name => $_class ) {
                        if ( class_exists( $_class ) ) {
                            $_found_pagecache = $_name;
                            break;
                        }
                    }
                }
                // and loop through known pagecache functions.
                if ( false === $_found_pagecache ) {
                    foreach ( $_page_cache_functions as $_name => $_function ) {
                        if ( function_exists( $_function ) ) {
                            $_found_pagecache = $_name;
                            break;
                        }
                    }
                }

                // store in transient for 1 week if pagecache found.
                if ( true === $_found_pagecache && true !== $disregard_transient ) {
                    set_transient( $_ao_pagecache_transient, true, WEEK_IN_SECONDS );
                }
            }
        }

        return $_found_pagecache;
    }

    /**
     * Returns true if on one of the AO settings tabs, false if not.
     * Used to limit notifications to AO settings pages.
     *
     * @return bool
     */
    public static function is_ao_settings() {
        $_is_ao_settings = ( str_replace( array( 'autoptimize', 'autoptimize_imgopt', 'ao_critcss', 'autoptimize_extra', 'ao_partners', 'ao_pro_boosters', 'ao_pro_pagecache', 'ao_protab' ), '', $_SERVER['REQUEST_URI'] ) !== $_SERVER['REQUEST_URI'] ? true : false );
        return $_is_ao_settings;
    }

    /**
     * Returns false if no conflicting plugins are found, the name if the plugin if found.
     *
     * @return bool|string
     */
    public static function find_potential_conflicts() {
        if ( defined( 'WPFC_WP_CONTENT_BASENAME' ) ) {
            $_wpfc_options = json_decode( get_option( 'WpFastestCache' ) );
            foreach ( array( 'wpFastestCacheMinifyCss', 'wpFastestCacheCombineCss', 'wpFastestCacheCombineJs' ) as $_wpfc_conflicting ) {
                if ( isset( $_wpfc_options->$_wpfc_conflicting ) && 'on' === $_wpfc_options->$_wpfc_conflicting ) {
                    return 'WP Fastest Cache';
                }
            }
        } elseif ( defined( 'W3TC_VERSION' ) ) {
            $w3tc_config     = file_get_contents( WP_CONTENT_DIR . '/w3tc-config/master.php' );
            $w3tc_minify_on = strpos( $w3tc_config, '"minify.enabled": true' );
            if ( $w3tc_minify_on ) {
                return 'W3 Total Cache';
            }
        } elseif ( defined( 'SiteGround_Optimizer\VERSION' ) ) {
            if ( get_option( 'siteground_optimizer_optimize_css' ) == 1 || get_option( 'siteground_optimizer_optimize_javascript' ) == 1 || get_option( 'siteground_optimizer_combine_javascript' ) == 1 || get_option( 'siteground_optimizer_combine_css' ) == 1 ) {
                return 'Siteground Optimizer';
            }
        } elseif ( defined( 'WPO_VERSION' ) ) {
            $_wpo_options = get_site_option( 'wpo_minify_config' );
            if ( is_array( $_wpo_options ) && 1 == $_wpo_options['enabled'] && ( 1 == $_wpo_options['enable_css'] || 1 == $_wpo_options['enable_js'] ) ) {
                return 'WP Optimize';
            }
        } elseif ( defined( 'WPACU_PLUGIN_VERSION' ) || defined( 'WPACU_PRO_PLUGIN_VERSION' ) ) {
            $wpacu_settings_class = new \WpAssetCleanUp\Settings();
            $wpacu_settings      = $wpacu_settings_class->getAll();

            if ( $wpacu_settings['minify_loaded_css'] || $wpacu_settings['minify_loaded_js'] || $wpacu_settings['combine_loaded_js'] || $wpacu_settings['combine_loaded_css'] ) {
                return 'Asset Cleanup';
            }
        } elseif ( defined( 'WP_ROCKET_VERSION' ) && function_exists( 'get_rocket_option' ) ) {
            if ( get_rocket_option( 'minify_js' ) || get_rocket_option( 'minify_concatenate_js' ) || get_rocket_option( 'minify_css' ) || get_rocket_option( 'minify_concatenate_css' ) || get_rocket_option( 'async_css' ) ) {
                return 'WP Rocket';
            }
        } elseif ( function_exists( 'fvm_get_settings' ) ) {
            return 'Fast Velocity Minify';
        }

        return false;
    }
    
    /**
     * Returns true if false if on a local dev environment, true if not.
     * Used to disallow image opt/ critcss for local dev environments.
     *
     * @return bool
     */
    public static function is_local_server( $_domain = AUTOPTIMIZE_SITE_DOMAIN ) {
        static $_is_local_server = null;

        if ( null === $_is_local_server ) {
            if ( false === strpos( $_domain, '.' ) && false === strpos( $_domain, ':' ) ) {
                // no dots in domain or colon (ipv6 address), so impossible to reach, this also matches 'localhost' or any other single-word domain.
                $_is_local_server = true;
            } elseif ( in_array( $_domain, array( '127.0.0.1', '0000:0000:0000:0000:0000:0000:0000:0001', '0:0:0:0:0:0:0:1', '::1' ) ) ) {
                // localhost IPv4/ IPv6.
                $_is_local_server = true;
            } elseif ( 0 === strpos( $_domain, '127.' ) || 0 === strpos( $_domain, '192.168.' ) || 0 === strpos( $_domain, '10.' ) || 0 === strpos( $_domain, 'fd' ) ) {
                // private ranges so unreachable for imgopt/ CCSS.
                // fixme; 172.16.0.0–172.31.255.255 also private.
                $_is_local_server = true;
            } elseif ( autoptimizeUtils::str_ends_in( $_domain, '.local') ) {
                // matches 'whatever.local'.
                $_is_local_server = true;
            } else {
                // likely OK.
                $_is_local_server = false;
            }
        }

        // filter to override result for testing purposes.
        return apply_filters( 'autoptimize_filter_utils_is_local_server', $_is_local_server );
    }

    public static function strip_tags_array( $array ) {
        // strip all tags in an array (use case: avoid XSS in CCSS rules both when importing and when outputting).
        // based on https://stackoverflow.com/a/44732196/237449 but heavily tweaked.
        if ( is_array( $array ) ) {
            $result = array();
            foreach ( $array as $key => $value ){
                if ( is_array( $value ) ) {
                    $result[$key] = autoptimizeUtils::strip_tags_array( $value );
                } else if ( is_string( $value ) ) {
                    $result[$key] = wp_strip_all_tags( $value );
                } else {
                    $result[$key] = $value;
                }
            }
        } else {
            $result = wp_strip_all_tags( $array );
        }
        return $result;
    }
}
PK.�1\�E��autoptimizeToolbar.phpnu�[���<?php
/**
 * Handles toolbar-related stuff.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeToolbar
{
    public function __construct()
    {
        // If Cache is not available we don't add the toolbar.
        if ( ! autoptimizeCache::cacheavail() ) {
            return;
        }

        // Load admin toolbar feature once WordPress, all plugins, and the theme are fully loaded and instantiated.
        if ( is_admin() ) {
            add_action( 'wp_loaded', array( $this, 'load_toolbar' ) );
        } else {
            // to avoid AMP complaining about the AMP conditional being checked too early.
            add_action( 'wp', array( $this, 'load_toolbar' ) );
        }
    }

    public function load_toolbar()
    {
        // Check permissions and that toolbar is not hidden via filter.
        if ( current_user_can( 'manage_options' ) && apply_filters( 'autoptimize_filter_toolbar_show', true ) && ! autoptimizeMain::is_amp_markup( '' ) ) {

            // Create a handler for the AJAX toolbar requests.
            add_action( 'wp_ajax_autoptimize_delete_cache', array( $this, 'delete_cache' ) );

            // Load custom styles, scripts and menu only when needed.
            if ( is_admin_bar_showing() ) {
                if ( is_admin() ) {
                    add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
                } else {
                    add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
                }

                // Add the Autoptimize Toolbar to the Admin bar.
                add_action( 'admin_bar_menu', array( $this, 'add_toolbar' ), 100 );
            }
        }
    }

    public function add_toolbar()
    {
        global $wp_admin_bar;

        // Retrieve the Autoptimize Cache Stats information.
        $stats = autoptimizeCache::stats();

        // Set the Max Size recommended for cache files.
        $max_size = apply_filters( 'autoptimize_filter_cachecheck_maxsize', 512 * 1024 * 1024 );

        // Retrieve the current Total Files in cache.
        $files = $stats[0];
        // Retrieve the current Total Size of the cache.
        $bytes = $stats[1];
        $size  = $this->format_filesize( $bytes );

        // Calculate the percentage of cache used.
        $percentage = ceil( $bytes / $max_size * 100 );
        if ( $percentage > 100 ) {
            $percentage = 100;
        }

        /**
         * We define the type of color indicator for the current state of cache size:
         * - "green" if the size is less than 80% of the total recommended.
         * - "orange" if over 80%.
         * - "red" if over 100%.
         */
        $color = ( 100 == $percentage ) ? 'red' : ( ( $percentage > 80 ) ? 'orange' : 'green' );

        // Create or add new items into the Admin Toolbar.
        // Main "Autoptimize" node.
        $_my_name = apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html__( 'Autoptimize Pro', 'autoptimize' ) : esc_html__( 'Autoptimize', 'autoptimize' );
        $wp_admin_bar->add_node(
            array(
                'id'    => 'autoptimize',
                'title' => '<span class="ab-icon"></span><span class="ab-label">' . $_my_name . '</span>',
                'href'  => admin_url( 'options-general.php?page=autoptimize' ),
                'meta'  => array( 'class' => 'bullet-' . $color ),
            )
        );

        // "Cache Info" node.
        $wp_admin_bar->add_node(
            array(
                'id'     => 'autoptimize-cache-info',
                'title'  => '<p>' . esc_html__( 'CSS/ JS Cache Info', 'autoptimize' ) . '</p>' .
                            '<div class="autoptimize-radial-bar" percentage="' . $percentage . '">' .
                            '<div class="autoptimize-circle">' .
                            '<div class="mask full"><div class="fill bg-' . $color . '"></div></div>' .
                            '<div class="mask half"><div class="fill bg-' . $color . '"></div></div>' .
                            '<div class="shadow"></div>' .
                            '</div>' .
                            '<div class="inset"><div class="percentage"><div class="numbers ' . $color . '">' . $percentage . '%</div></div></div>' .
                            '</div>' .
                            '<table>' .
                            '<tr><td>' . esc_html__( 'Size', 'autoptimize' ) . ':</td><td class="size ' . $color . '">' . $size . '</td></tr>' .
                            '<tr><td>' . esc_html__( 'Files', 'autoptimize' ) . ':</td><td class="files white">' . $files . '</td></tr>' .
                            '</table>',
                'parent' => 'autoptimize',
            )
        );

        // "Delete Cache" node.
        $wp_admin_bar->add_node(
            array(
                'id'     => 'autoptimize-delete-cache',
                'title'  => esc_html__( 'Clear CSS/ JS Cache', 'autoptimize' ),
                'parent' => 'autoptimize',
            )
        );
    }

    public function delete_cache()
    {
        check_ajax_referer( 'ao_delcache_nonce', 'nonce' );

        $result = false;
        if ( current_user_can( 'manage_options' ) ) {
            // We call the function for cleaning the Autoptimize cache.
            $result = autoptimizeCache::clearall();
        }

        wp_send_json( $result );
    }

    public function enqueue_scripts()
    {
        // Autoptimize Toolbar Styles.
        wp_enqueue_style( 'autoptimize-toolbar', plugins_url( '/static/toolbar.min.css', __FILE__ ), array(), AUTOPTIMIZE_PLUGIN_VERSION, 'all' );

        // Autoptimize Toolbar Javascript.
        wp_enqueue_script( 'autoptimize-toolbar', plugins_url( '/static/toolbar.min.js', __FILE__ ), array( 'jquery' ), AUTOPTIMIZE_PLUGIN_VERSION, true );

        // Localizes a registered script with data for a JavaScript variable.
        // Needed for the AJAX to work properly on the frontend.
        wp_localize_script(
            'autoptimize-toolbar',
            'autoptimize_ajax_object',
            array(
                'ajaxurl'     => admin_url( 'admin-ajax.php' ),
                // translators: links to the Autoptimize settings page.
                'error_msg'   => sprintf( esc_html__( 'Your Autoptimize cache might not have been purged successfully, please check on the %1$sAutoptimize settings page%2$s.', 'autoptimize' ), '<a href="' . admin_url( 'options-general.php?page=autoptimize' ) . '" style="white-space:nowrap;">', '</a>' ),
                'dismiss_msg' => esc_html__( 'Dismiss this notice.' ),
                'nonce'       => wp_create_nonce( 'ao_delcache_nonce' ),
            )
        );
    }

    public function format_filesize( $bytes, $decimals = 2 )
    {
        $units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' );

        for ( $i = 0; ( $bytes / 1024) > 0.9; $i++, $bytes /= 1024 ) {} // @codingStandardsIgnoreLine

        return sprintf( "%1.{$decimals}f %s", round( $bytes, $decimals ), $units[ $i ] );
    }
}
PK.�1\��mJ�J�autoptimizeMain.phpnu�[���<?php
/**
 * Wraps base plugin logic/hooks and handles activation/deactivation/uninstall.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeMain
{
    const INIT_EARLIER_PRIORITY = -1;
    const DEFAULT_HOOK_PRIORITY = 2;

    /**
     * Version string.
     *
     * @var string
     */
    protected $version = null;

    /**
     * Main plugin filepath.
     * Used for activation/deactivation/uninstall hooks.
     *
     * @var string
     */
    protected $filepath = null;

    /**
     * Critical CSS base object
     *
     * @var object
     */
    protected $_criticalcss = null;

    /**
     * Constructor.
     *
     * @param string $version Version.
     * @param string $filepath Filepath. Needed for activation/deactivation/uninstall hooks.
     */
    public function __construct( $version, $filepath )
    {
        $this->version  = $version;
        $this->filepath = $filepath;
    }

    public function run()
    {
        $this->add_hooks();

        // Runs cache size checker.
        $checker = new autoptimizeCacheChecker();
        $checker->run();
    }

    protected function add_hooks()
    {
        if ( ! defined( 'AUTOPTIMIZE_SETUP_INITHOOK' ) ) {
            define( 'AUTOPTIMIZE_SETUP_INITHOOK', 'plugins_loaded' );
        }

        add_action( AUTOPTIMIZE_SETUP_INITHOOK, array( $this, 'setup' ) );
        add_action( AUTOPTIMIZE_SETUP_INITHOOK, array( $this, 'hook_page_cache_purge' ) );

        add_action( 'autoptimize_setup_done', array( $this, 'version_upgrades_check' ) );
        add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) );
        add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_compat' ), 10 );
        add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_extra' ), 15 );
        add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_admin_only_trinkets' ), 20 );
        add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_criticalcss' ), 11 );
        add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_notfound_fallback' ), 10 );

        add_action( 'init', array( $this, 'load_textdomain' ) );

        if ( is_multisite() && is_admin() ) {
            // Only if multisite and if in admin we want to check if we need to save options on network level.
            add_action( 'init', 'autoptimizeOptionWrapper::check_multisite_on_saving_options' );
        }

        // register uninstall & deactivation hooks.
        register_uninstall_hook( $this->filepath, 'autoptimizeMain::on_uninstall' );
        register_deactivation_hook( $this->filepath, 'autoptimizeMain::on_deactivation' );
    }

    public function load_textdomain()
    {
        load_plugin_textdomain( 'autoptimize' );
    }

    public function setup()
    {
        // Do we gzip in php when caching or is the webserver doing it?
        define( 'AUTOPTIMIZE_CACHE_NOGZIP', (bool) autoptimizeOptionWrapper::get_option( 'autoptimize_cache_nogzip' ) );

        // These can be overridden by specifying them in wp-config.php or such.
        if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_NAME' ) ) {
            define( 'AUTOPTIMIZE_WP_CONTENT_NAME', '/' . wp_basename( WP_CONTENT_DIR ) );
        }
        if ( ! defined( 'AUTOPTIMIZE_CACHE_CHILD_DIR' ) ) {
            define( 'AUTOPTIMIZE_CACHE_CHILD_DIR', '/cache/autoptimize/' );
        }
        if ( ! defined( 'AUTOPTIMIZE_CACHEFILE_PREFIX' ) ) {
            define( 'AUTOPTIMIZE_CACHEFILE_PREFIX', 'autoptimize_' );
        }
        // Note: trailing slash is not optional!
        if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
            define( 'AUTOPTIMIZE_CACHE_DIR', autoptimizeCache::get_pathname() );
        }

        define( 'WP_ROOT_DIR', substr( WP_CONTENT_DIR, 0, strlen( WP_CONTENT_DIR ) - strlen( AUTOPTIMIZE_WP_CONTENT_NAME ) ) );

        if ( ! defined( 'AUTOPTIMIZE_WP_SITE_URL' ) ) {
            if ( function_exists( 'domain_mapping_siteurl' ) ) {
                define( 'AUTOPTIMIZE_WP_SITE_URL', domain_mapping_siteurl( get_current_blog_id() ) );
            } else {
                define( 'AUTOPTIMIZE_WP_SITE_URL', site_url() );
            }
        }
        if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_URL' ) ) {
            if ( function_exists( 'get_original_url' ) ) {
                define( 'AUTOPTIMIZE_WP_CONTENT_URL', str_replace( get_original_url( AUTOPTIMIZE_WP_SITE_URL ), AUTOPTIMIZE_WP_SITE_URL, content_url() ) );
            } else {
                define( 'AUTOPTIMIZE_WP_CONTENT_URL', content_url() );
            }
        }
        if ( ! defined( 'AUTOPTIMIZE_CACHE_URL' ) ) {
            if ( is_multisite() && apply_filters( 'autoptimize_separate_blog_caches', true ) ) {
                $blog_id = get_current_blog_id();
                define( 'AUTOPTIMIZE_CACHE_URL', AUTOPTIMIZE_WP_CONTENT_URL . AUTOPTIMIZE_CACHE_CHILD_DIR . $blog_id . '/' );
            } else {
                define( 'AUTOPTIMIZE_CACHE_URL', AUTOPTIMIZE_WP_CONTENT_URL . AUTOPTIMIZE_CACHE_CHILD_DIR );
            }
        }
        if ( ! defined( 'AUTOPTIMIZE_WP_ROOT_URL' ) ) {
            define( 'AUTOPTIMIZE_WP_ROOT_URL', str_replace( AUTOPTIMIZE_WP_CONTENT_NAME, '', AUTOPTIMIZE_WP_CONTENT_URL ) );
        }
        if ( ! defined( 'AUTOPTIMIZE_HASH' ) ) {
            define( 'AUTOPTIMIZE_HASH', wp_hash( AUTOPTIMIZE_CACHE_URL ) );
        }
        if ( ! defined( 'AUTOPTIMIZE_SITE_DOMAIN' ) ) {
            define( 'AUTOPTIMIZE_SITE_DOMAIN', parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) );
        }

        // Multibyte-capable string replacements are available with a filter.
        // Also requires 'mbstring' extension.
        $with_mbstring = apply_filters( 'autoptimize_filter_main_use_mbstring', false );
        if ( $with_mbstring ) {
            autoptimizeUtils::mbstring_available( \extension_loaded( 'mbstring' ) );
        } else {
            autoptimizeUtils::mbstring_available( false );
        }

        do_action( 'autoptimize_setup_done' );
    }

    /**
     * Checks if there's a need to upgrade/update options and whatnot,
     * in which case we might need to do stuff and flush the cache
     * to avoid old versions of aggregated files lingering around.
     */
    public function version_upgrades_check()
    {
        autoptimizeVersionUpdatesHandler::check_installed_and_update( $this->version );
    }

    public function check_cache_and_run()
    {
        if ( autoptimizeCache::cacheavail() ) {
            $conf = autoptimizeConfig::instance();
            if ( $conf->get( 'autoptimize_html' ) || $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) || autoptimizeImages::imgopt_active() || autoptimizeImages::should_lazyload_wrapper() ) {
                if ( ! defined( 'AUTOPTIMIZE_NOBUFFER_OPTIMIZE' ) ) {
                    // Hook into WordPress frontend.
                    if ( defined( 'AUTOPTIMIZE_INIT_EARLIER' ) ) {
                        add_action(
                            'init',
                            array( $this, 'start_buffering' ),
                            self::INIT_EARLIER_PRIORITY
                        );
                    } else {
                        if ( ! defined( 'AUTOPTIMIZE_HOOK_INTO' ) ) {
                            define( 'AUTOPTIMIZE_HOOK_INTO', 'template_redirect' );
                        }
                        add_action(
                            constant( 'AUTOPTIMIZE_HOOK_INTO' ),
                            array( $this, 'start_buffering' ),
                            self::DEFAULT_HOOK_PRIORITY
                        );
                    }
                }

                // And disable Jetpack's site accelerator if JS or CSS opt. are active.
                if ( class_exists( 'Jetpack' ) && apply_filters( 'autoptimize_filter_main_disable_jetpack_cdn', true ) && ( $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) || autoptimizeImages::imgopt_active() ) ) {
                    add_filter( 'jetpack_force_disable_site_accelerator', '__return_true' ); // this does not seemt to work any more?
                    if ( true === autoptimizeImages::imgopt_active() ) {
                        // only disable photon if AO is optimizing images.
                        add_filter( 'jetpack_photon_skip_for_url', '__return_true' );
                    }
                }

                // Add "no cache found" notice.
                add_action( 'admin_notices', 'autoptimizeMain::notice_nopagecache', 99 );
                add_action( 'admin_notices', 'autoptimizeMain::notice_potential_conflict', 99 );
            }
        } else {
            add_action( 'admin_notices', 'autoptimizeMain::notice_cache_unavailable' );
        }
    }

    public function maybe_run_ao_extra()
    {
        if ( apply_filters( 'autoptimize_filter_extra_activate', true ) ) {
            $ao_imgopt = new autoptimizeImages();
            $ao_imgopt->run();
            $ao_extra = new autoptimizeExtra();
            $ao_extra->run();

            // And show the imgopt notice.
            add_action( 'admin_notices', 'autoptimizeMain::notice_plug_imgopt' );
            add_action( 'admin_notices', 'autoptimizeMain::notice_imgopt_issue' );
        }
    }

    public function maybe_run_admin_only_trinkets()
    {
        // Loads partners tab and exit survey code if in admin (and not in admin-ajax.php)!
        if ( autoptimizeConfig::is_admin_and_not_ajax() ) {
            new autoptimizePartners();
            new autoptimizeExitSurvey();
            new autoptimizeProTab();
        }
    }

    public function criticalcss()
    {
        if ( apply_filters( 'autoptimize_filter_criticalcss_active', true ) && ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
            return $this->_criticalcss;
        } else {
            return false;
        }
    }

    public function maybe_run_criticalcss()
    {
        // Loads criticalcss if the filter returns true & old power-up is not active.
        if ( apply_filters( 'autoptimize_filter_criticalcss_active', true ) && ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
            $this->_criticalcss = new autoptimizeCriticalCSSBase();
            $this->_criticalcss->setup();
            $this->_criticalcss->load_requires();
        }
    }

    public function maybe_run_notfound_fallback()
    {
        if ( autoptimizeCache::do_fallback() ) {
            add_action( 'template_redirect', array( 'autoptimizeCache', 'wordpress_notfound_fallback' ) );
        }
    }

    public function maybe_run_ao_compat()
    {
        $conf = autoptimizeConfig::instance();

        // Condtionally loads the compatibility-class to ensure more out-of-the-box compatibility with big players.
        $_run_compat = true;

        if ( 'on' === $conf->get( 'autoptimize_installed_before_compatibility' ) ) {
            // If AO was already running before Compatibility logic was added, don't run compat by default
            // because it can be assumed everything works and we want to avoid (perf) regressions that
            // could occur due to compatibility code.
            $_run_compat = false;
        }

        if ( apply_filters( 'autoptimize_filter_init_compatibility', $_run_compat ) ) {
             new autoptimizeCompatibility();
        }
    }

    public function hook_page_cache_purge()
    {
        // hook into a collection of page cache purge actions if filter allows.
        if ( apply_filters( 'autoptimize_filter_main_hookpagecachepurge', true ) ) {
            $page_cache_purge_actions = array(
                'after_rocket_clean_domain', // exists.
                'hyper_cache_purged', // Stefano confirmed this will be added.
                'w3tc_flush_posts', // exits.
                'w3tc_flush_all', // exists.
                'ce_action_cache_cleared', // Sven confirmed this will be added.
                'aoce_action_cache_cleared', // Some other cache enabler.
                'comet_cache_wipe_cache', // still to be confirmed by Raam.
                'wp_cache_cleared', // cfr. https://github.com/Automattic/wp-super-cache/pull/537.
                'wpfc_delete_cache', // Emre confirmed this will be added this.
                'swift_performance_after_clear_all_cache', // swift perf. yeah!
                'wpo_cache_flush', // wp-optimize.
                'rt_nginx_helper_after_fastcgi_purge_all', // nginx helper.
            );
            $page_cache_purge_actions = apply_filters( 'autoptimize_filter_main_pagecachepurgeactions', $page_cache_purge_actions );
            foreach ( $page_cache_purge_actions as $purge_action ) {
                add_action( $purge_action, 'autoptimizeCache::clearall_actionless' );
            }
        }
    }

    /**
     * Setup output buffering if needed.
     *
     * @return void
     */
    public function start_buffering()
    {
        if ( $this->should_buffer() ) {

            // Load speedupper conditionally (true by default).
            if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
                $ao_speedupper = new autoptimizeSpeedupper();
            }

            $conf = autoptimizeConfig::instance();

            if ( $conf->get( 'autoptimize_js' ) ) {
                if ( ! defined( 'CONCATENATE_SCRIPTS' ) ) {
                    define( 'CONCATENATE_SCRIPTS', false );
                }
                if ( ! defined( 'COMPRESS_SCRIPTS' ) ) {
                    define( 'COMPRESS_SCRIPTS', false );
                }
            }

            if ( $conf->get( 'autoptimize_css' ) ) {
                if ( ! defined( 'COMPRESS_CSS' ) ) {
                    define( 'COMPRESS_CSS', false );
                }
            }

            if ( apply_filters( 'autoptimize_filter_obkiller', false ) ) {
                while ( ob_get_level() > 0 ) {
                    ob_end_clean();
                }
            }

            // Now, start the real thing!
            ob_start( array( $this, 'end_buffering' ) );
        }
    }

    /**
     * Returns true if all the conditions to start output buffering are satisfied.
     *
     * @param bool $doing_tests Allows overriding the optimization of only
     *                          deciding once per request (for use in tests).
     * @return bool
     */
    public static function should_buffer( $doing_tests = false )
    {
        static $do_buffering = null;

        // Only check once in case we're called multiple times by others but
        // still allows multiple calls when doing tests.
        if ( null === $do_buffering || $doing_tests ) {

            $ao_noptimize = false;

            // Checking for DONOTMINIFY constant as used by e.g. WooCommerce POS.
            if ( defined( 'DONOTMINIFY' ) && ( constant( 'DONOTMINIFY' ) === true || constant( 'DONOTMINIFY' ) === 'true' ) ) {
                $ao_noptimize = true;
            }

            // Skip checking query strings if they're disabled.
            if ( apply_filters( 'autoptimize_filter_honor_qs_noptimize', true ) ) {
                // Check for `ao_noptimize` (and other) keys in the query string
                // to get non-optimized page for debugging.
                $keys = array(
                    'ao_noptimize',
                    'ao_noptirocket',
                );
                foreach ( $keys as $key ) {
                    if ( array_key_exists( $key, $_GET ) && '1' === $_GET[ $key ] ) {
                        $ao_noptimize = true;
                        break;
                    }
                }
            }

            // If setting says not to optimize logged in user and user is logged in...
            if ( false === $ao_noptimize && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_logged', 'on' ) && is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
                $ao_noptimize = true;
            }

            // If setting says not to optimize cart/checkout.
            if ( false === $ao_noptimize && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_checkout', 'off' ) ) {
                // Checking for woocommerce, easy digital downloads and wp ecommerce...
                foreach ( array( 'is_checkout', 'is_cart', 'is_account_page', 'edd_is_checkout', 'wpsc_is_cart', 'wpsc_is_checkout' ) as $func ) {
                    if ( function_exists( $func ) && $func() ) {
                        $ao_noptimize = true;
                        break;
                    }
                }
            }

            // Misc. querystring paramaters that will stop AO from doing optimizations (pagebuilders +
            // 2 generic parameters that could/ should become standard between optimization plugins?).
            if ( false === $ao_noptimize ) {
                $_qs_showstoppers = array( 'no_cache', 'no_optimize', 'tve', 'elementor-preview', 'fl_builder', 'vc_action', 'et_fb', 'bt-beaverbuildertheme', 'ct_builder', 'fb-edit', 'siteorigin_panels_live_editor', 'preview', 'td_action' );

                // doing Jonathan a quick favor to allow correct unused CSS generation ;-) .
                if ( apply_filters( 'autoptimize_filter_main_showstoppers_do_wp_rocket_a_favor', true ) ) {
                    $_qs_showstoppers[] = 'nowprocket';
                }

                foreach ( $_qs_showstoppers as $_showstopper ) {
                    if ( array_key_exists( $_showstopper, $_GET ) ) {
                        $ao_noptimize = true;
                        break;
                    }
                }
            }

            // Also honor PageSpeed=off parameter as used by mod_pagespeed, in use by some pagebuilders,
            // see https://www.modpagespeed.com/doc/experiment#ModPagespeed for info on that.
            if ( false === $ao_noptimize && array_key_exists( 'PageSpeed', $_GET ) && 'off' === $_GET['PageSpeed'] ) {
                $ao_noptimize = true;
            }

            // If page/ post check post_meta to see if optimize is off.
            if ( false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_optimize' ) ) {
                $ao_noptimize = true;
            }

            // And finally allows blocking of autoptimization on your own terms regardless of above decisions.
            $ao_noptimize = (bool) apply_filters( 'autoptimize_filter_noptimize', $ao_noptimize );

            // Check for site being previewed in the Customizer (available since WP 4.0).
            $is_customize_preview = false;
            if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) {
                $is_customize_preview = is_customize_preview();
            }
            
            // explicitly disable when is_login exists and is true but don't use it direclty because older versions of WordPress don't have that yet.
            $is_login = false;
            if ( function_exists( 'is_login' ) && true === is_login() ) {
                $is_login = true;
            }

            /**
             * We only buffer the frontend requests (and then only if not a feed
             * and not turned off explicitly and not when being previewed in Customizer)!
             * NOTE: Tests throw a notice here due to is_feed() being called
             * while the main query hasn't been ran yet. Thats why we use
             * AUTOPTIMIZE_INIT_EARLIER in tests.
             */
            $do_buffering = ( ! is_admin() && ! is_feed() && ! is_embed() && ! $is_login && ! $is_customize_preview && ! $ao_noptimize );
        }

        return $do_buffering;
    }

    /**
     * Returns true if given markup is considered valid/processable/optimizable.
     *
     * @param string $content Markup.
     *
     * @return bool
     */
    public function is_valid_buffer( $content )
    {
        // Defaults to true.
        $valid = true;

        $has_no_html_tag    = ( false === stripos( $content, '<html' ) );
        $has_xsl_stylesheet = ( false !== stripos( $content, '<xsl:stylesheet' ) || false !== stripos( $content, '<?xml-stylesheet' ) );
        $has_html5_doctype  = ( preg_match( '/^<!DOCTYPE.+html>/i', ltrim( $content ) ) > 0 );
        $has_noptimize_page = ( false !== stripos( $content, '<!-- noptimize-page -->' ) );

        if ( $has_no_html_tag ) {
            // Can't be valid amp markup without an html tag preceding it.
            $is_amp_markup = false;
        } else {
            $is_amp_markup = self::is_amp_markup( $content );
        }

        // If it's not html, or if it's amp or contains xsl stylesheets we don't touch it.
        if ( $has_no_html_tag && ! $has_html5_doctype || $is_amp_markup || $has_xsl_stylesheet || $has_noptimize_page ) {
            $valid = false;
        }

        return $valid;
    }

    /**
     * Returns true if given $content is considered to be AMP markup.
     * This is far from actual validation against AMP spec, but it'll do for now.
     *
     * @param string $content Markup to check.
     *
     * @return bool
     */
    public static function is_amp_markup( $content )
    {
        // Short-circuit if the page is already AMP from the start.
        if (
            preg_match(
                sprintf(
                    '#^(?:<!.*?>|\s+)*+<html(?=\s)[^>]*?\s(%1$s|%2$s|%3$s)(\s|=|>)#is',
                    'amp',
                    "\xE2\x9A\xA1", // From \AmpProject\Attribute::AMP_EMOJI.
                    "\xE2\x9A\xA1\xEF\xB8\x8F" // From \AmpProject\Attribute::AMP_EMOJI_ALT, per https://github.com/ampproject/amphtml/issues/25990.
                ),
                $content
            )
        ) {
            return true;
        }

        // Or else short-circuit if the AMP plugin will be processing the output to be an AMP page.
        if ( function_exists( 'amp_is_request' ) ) {
            return amp_is_request(); // For AMP plugin v2.0+.
        } elseif ( function_exists( 'is_amp_endpoint' ) ) {
            return is_amp_endpoint(); // For older/other AMP plugins (still supported in 2.0 as an alias).
        }

        return false;
    }

    /**
     * Processes/optimizes the output-buffered content and returns it.
     * If the content is not processable, it is returned unmodified.
     *
     * @param string $content Buffered content.
     *
     * @return string
     */
    public function end_buffering( $content )
    {
        // Bail early without modifying anything if we can't handle the content.
        if ( ! $this->is_valid_buffer( $content ) ) {
            return $content;
        }

        $conf = autoptimizeConfig::instance();

        // Determine what needs to be ran.
        $classes = array();
        if ( $conf->get( 'autoptimize_js' ) ) {
            $classes[] = 'autoptimizeScripts';
        }
        if ( $conf->get( 'autoptimize_css' ) ) {
            $classes[] = 'autoptimizeStyles';
        }
        if ( $conf->get( 'autoptimize_html' ) ) {
            $classes[] = 'autoptimizeHTML';
        }

        $classoptions = array(
            'autoptimizeScripts' => array(
                'aggregate'           => $conf->get( 'autoptimize_js_aggregate' ),
                'defer_not_aggregate' => $conf->get( 'autoptimize_js_defer_not_aggregate' ),
                'defer_inline'        => $conf->get( 'autoptimize_js_defer_inline' ),
                'justhead'            => $conf->get( 'autoptimize_js_justhead' ),
                'forcehead'           => $conf->get( 'autoptimize_js_forcehead' ),
                'trycatch'            => $conf->get( 'autoptimize_js_trycatch' ),
                'js_exclude'          => $conf->get( 'autoptimize_js_exclude' ),
                'cdn_url'             => $conf->get( 'autoptimize_cdn_url' ),
                'include_inline'      => $conf->get( 'autoptimize_js_include_inline' ),
                'minify_excluded'     => $conf->get( 'autoptimize_minify_excluded' ),
            ),
            'autoptimizeStyles'  => array(
                'aggregate'       => $conf->get( 'autoptimize_css_aggregate' ),
                'justhead'        => $conf->get( 'autoptimize_css_justhead' ),
                'datauris'        => $conf->get( 'autoptimize_css_datauris' ),
                'defer'           => $conf->get( 'autoptimize_css_defer' ),
                'defer_inline'    => $conf->get( 'autoptimize_css_defer_inline' ),
                'inline'          => $conf->get( 'autoptimize_css_inline' ),
                'css_exclude'     => $conf->get( 'autoptimize_css_exclude' ),
                'cdn_url'         => $conf->get( 'autoptimize_cdn_url' ),
                'include_inline'  => $conf->get( 'autoptimize_css_include_inline' ),
                'nogooglefont'    => $conf->get( 'autoptimize_css_nogooglefont' ),
                'minify_excluded' => $conf->get( 'autoptimize_minify_excluded' ),
            ),
            'autoptimizeHTML'    => array(
                'keepcomments'  => $conf->get( 'autoptimize_html_keepcomments' ),
                'minify_inline' => $conf->get( 'autoptimize_html_minify_inline' ),
            ),
        );

        $content = apply_filters( 'autoptimize_filter_html_before_minify', $content );

        // Run the classes!
        foreach ( $classes as $name ) {
            $instance = new $name( $content );
            if ( $instance->read( $classoptions[ $name ] ) ) {
                $instance->minify();
                $instance->cache();
                $content = $instance->getcontent();
            }
            unset( $instance );
        }

        $content = apply_filters( 'autoptimize_html_after_minify', $content );

        return $content;
    }

    public static function autoptimize_nobuffer_optimize( $html_in ) {
        $html_out = $html_in;

        if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
            $ao_speedupper = new autoptimizeSpeedupper();
        }

        $self = new self( AUTOPTIMIZE_PLUGIN_VERSION, AUTOPTIMIZE_PLUGIN_FILE );
        if ( $self->should_buffer() ) {
            $html_out = $self->end_buffering( $html_in );
        }
        return $html_out;
    }

    public static function on_uninstall()
    {
        // clear the cache.
        autoptimizeCache::clearall();

        // remove postmeta if active.
        if ( autoptimizeConfig::is_ao_meta_settings_active() ) {
            delete_post_meta_by_key( 'ao_post_optimize' );
        }

        // remove all options.
        $delete_options = array(
            'autoptimize_cache_clean',
            'autoptimize_cache_nogzip',
            'autoptimize_css',
            'autoptimize_css_aggregate',
            'autoptimize_css_datauris',
            'autoptimize_css_justhead',
            'autoptimize_css_defer',
            'autoptimize_css_defer_inline',
            'autoptimize_css_inline',
            'autoptimize_css_exclude',
            'autoptimize_html',
            'autoptimize_html_keepcomments',
            'autoptimize_html_minify_inline',
            'autoptimize_enable_site_config',
            'autoptimize_enable_meta_ao_settings',
            'autoptimize_js',
            'autoptimize_js_aggregate',
            'autoptimize_js_defer_not_aggregate',
            'autoptimize_js_defer_inline',
            'autoptimize_js_exclude',
            'autoptimize_js_forcehead',
            'autoptimize_js_justhead',
            'autoptimize_js_trycatch',
            'autoptimize_version',
            'autoptimize_show_adv',
            'autoptimize_cdn_url',
            'autoptimize_cachesize_notice',
            'autoptimize_css_include_inline',
            'autoptimize_js_include_inline',
            'autoptimize_optimize_logged',
            'autoptimize_optimize_checkout',
            'autoptimize_extra_settings',
            'autoptimize_service_availablity',
            'autoptimize_imgopt_provider_stat',
            'autoptimize_imgopt_launched',
            'autoptimize_imgopt_settings',
            'autoptimize_minify_excluded',
            'autoptimize_cache_fallback',
            'autoptimize_ccss_rules',
            'autoptimize_ccss_additional',
            'autoptimize_ccss_queue',
            'autoptimize_ccss_viewport',
            'autoptimize_ccss_finclude',
            'autoptimize_ccss_rlimit',
            'autoptimize_ccss_rtimelimit',
            'autoptimize_ccss_noptimize',
            'autoptimize_ccss_debug',
            'autoptimize_ccss_key',
            'autoptimize_ccss_keyst',
            'autoptimize_ccss_version',
            'autoptimize_ccss_loggedin',
            'autoptimize_ccss_forcepath',
            'autoptimize_ccss_deferjquery',
            'autoptimize_ccss_domain',
            'autoptimize_ccss_unloadccss',
            'autoptimize_installed_before_compatibility',
        );

        if ( ! is_multisite() ) {
            foreach ( $delete_options as $del_opt ) {
                delete_option( $del_opt );
            }
            autoptimizeMain::remove_cronjobs();
        } else {
            global $wpdb;
            $blog_ids         = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
            $original_blog_id = get_current_blog_id();
            foreach ( $blog_ids as $blog_id ) {
                switch_to_blog( $blog_id );
                foreach ( $delete_options as $del_opt ) {
                    delete_option( $del_opt );
                }
                autoptimizeMain::remove_cronjobs();
            }
            switch_to_blog( $original_blog_id );
        }

        // Remove AO CCSS cached files and directory.
        $ao_ccss_dir = WP_CONTENT_DIR . '/uploads/ao_ccss/';
        if ( file_exists( $ao_ccss_dir ) && is_dir( $ao_ccss_dir ) && defined( 'GLOB_BRACE' ) ) {
            // fixme: should check for subdirs when in multisite and remove contents of those as well.
            // fixme: if GLOB_BRACE is not avaible we need to remove AO_CCSS_DIR differently?
            array_map( 'unlink', glob( $ao_ccss_dir . '*.{css,html,json,log,zip,lock}', GLOB_BRACE ) );
            rmdir( $ao_ccss_dir );
        }

        // Remove 404-handler (although that should have been removed in clearall already).
        $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php';
        if ( file_exists( $_fallback_php ) ) {
            unlink( $_fallback_php );
        }
    }

    public static function on_deactivation()
    {
        if ( is_multisite() && is_network_admin() ) {
            global $wpdb;
            $blog_ids         = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
            $original_blog_id = get_current_blog_id();
            foreach ( $blog_ids as $blog_id ) {
                switch_to_blog( $blog_id );
                autoptimizeMain::remove_cronjobs();
            }
            switch_to_blog( $original_blog_id );
        } else {
            autoptimizeMain::remove_cronjobs();
        }
        autoptimizeCache::clearall();
    }

    public static function remove_cronjobs() {
        // Remove scheduled events.
        foreach ( array( 'ao_cachechecker', 'ao_ccss_queue', 'ao_ccss_maintenance', 'ao_ccss_keychecker' ) as $_event ) {
            if ( wp_get_schedule( $_event ) ) {
                wp_clear_scheduled_hook( $_event );
            }
        }
    }

    public static function notice_cache_unavailable()
    {
        echo '<div class="error"><p>';
        // Translators: %s is the cache directory location.
        printf( esc_html__( 'Autoptimize cannot write to the cache directory (%s), please fix to enable CSS/ JS optimization!', 'autoptimize' ), AUTOPTIMIZE_CACHE_DIR );
        echo '</p></div>';
    }

    public static function notice_installed()
    {
        echo '<div class="updated"><p>';
        // translators: the variables contain opening and closing <a> tags to link to the settings page.
        printf( esc_html__( 'Thank you for installing and activating Autoptimize. Your site is being optimized immediately, please test the frontend to ensure everything still works as expected. If needed you can change JavaScript or CSS optimization settings under %1$sSettings -> Autoptimize%2$s .', 'autoptimize' ), '<a href="options-general.php?page=autoptimize">', '</a>' );
        echo '</p></div>';
    }

    public static function notice_updated()
    {
        echo '<div class="updated"><p>';
        printf( esc_html_e( 'Autoptimize has just been updated. Please %1$stest your site now%2$s and adapt Autoptimize config if needed.', 'autoptimize' ), '<strong>', '</strong>' );
        echo '</p></div>';
    }

    public static function notice_plug_imgopt()
    {
        // Translators: the URL added points to the Autopmize Extra settings.
        $_ao_imgopt_plug_notice      = sprintf( esc_html__( 'Did you know that Autoptimize offers on-the-fly image optimization (with support for WebP and AVIF) and CDN via ShortPixel? Check out the %1$sAutoptimize Image settings%2$s to enable this option.', 'autoptimize' ), '<a href="options-general.php?page=autoptimize_imgopt">', '</a>' );
        $_ao_imgopt_plug_notice      = apply_filters( 'autoptimize_filter_main_imgopt_plug_notice', $_ao_imgopt_plug_notice );
        $_ao_imgopt_launch_ok        = autoptimizeImages::launch_ok_wrapper();
        $_ao_imgopt_plug_dismissible = 'ao-img-opt-plug-123';
        $_ao_imgopt_active           = autoptimizeImages::imgopt_active();
        $_is_ao_settings_page        = autoptimizeUtils::is_ao_settings();

        if ( current_user_can( 'manage_options' ) && ! defined( 'AO_PRO_VERSION' ) && $_is_ao_settings_page && '' !== $_ao_imgopt_plug_notice && ! $_ao_imgopt_active && $_ao_imgopt_launch_ok && PAnD::is_admin_notice_active( $_ao_imgopt_plug_dismissible ) ) {
            echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_imgopt_plug_dismissible . '"><p>';
            echo $_ao_imgopt_plug_notice;
            echo '</p></div>';
        }
    }

    public static function notice_imgopt_issue()
    {
        // Translators: the URL added points to the Autopmize Extra settings.
        $_ao_imgopt_issue_notice      = sprintf( esc_html__( 'Shortpixel reports it cannot always reach your site, which might mean some images are not optimized. You can %1$sread more about why this happens and how you can fix that problem here%2$s.', 'autoptimize' ), '<a href="https://shortpixel.com/knowledge-base/article/469-i-received-an-e-mail-that-says-some-of-my-images-are-not-accessible-what-should-i-do#fullarticle" target="_blank">', '</a>' );
        $_ao_imgopt_issue_notice      = apply_filters( 'autoptimize_filter_main_imgopt_issue_notice', $_ao_imgopt_issue_notice );
        $_ao_imgopt_issue_dismissible = 'ao-img-opt-issue-14';
        $_ao_imgopt_active            = autoptimizeImages::imgopt_active();
        $_ao_imgopt_status            = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );

        if ( is_array( $_ao_imgopt_status ) && array_key_exists( 'TemporaryRedirectOrigin', $_ao_imgopt_status ) && ( $_ao_imgopt_status['TemporaryRedirectOrigin'] === "true" || $_ao_imgopt_status['TemporaryRedirectOrigin'] === true ) ) {
            $_ao_imgopt_status_redirect_warning = true;            
        } else {
            $_ao_imgopt_status_redirect_warning = false;
        }

        if ( current_user_can( 'manage_options' ) && $_ao_imgopt_active && $_ao_imgopt_status_redirect_warning && '' !== $_ao_imgopt_issue_notice && PAnD::is_admin_notice_active( $_ao_imgopt_issue_dismissible ) ) {
            echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_imgopt_issue_dismissible . '"><p>';
            echo $_ao_imgopt_issue_notice;
            echo '</p></div>';
        }
    }


    public static function notice_nopagecache()
    {
        /*
         * Autoptimize does not do page caching (yet) but not everyone knows, so below logic tries to find out if page caching is available and if not show a notice on the AO Settings pages.
         *
         * uses helper function in autoptimizeUtils.php
         */
        // translators: strong tags and a break.
        $_ao_nopagecache_notice      = sprintf( esc_html__( 'It looks like your site might not have %1$spage caching%2$s which is a %1$smust-have for performance%2$s. If you are sure you have a page cache, you can close this notice.%3$sWhen in doubt check with your host if they offer this or install a free page caching plugin like for example KeyCDN Cache Enabler', 'autoptimize' ), '<strong>', '</strong>', '<br />' );
        // translators: strong tags.
        $_ao_nopagecache_notice     .= ' ' . esc_html__('or consider ', 'autoptimize') . '<strong><a href="https://autoptimize.com/pro/">Autoptimize Pro</a></strong>' . sprintf( esc_html__( ' which not only has page caching but also image optimization, critical CSS and advanced booster options %1$sto make your site significantly faster%2$s!', 'autoptimize' ), '<strong>', '</strong>' );
        $_ao_nopagecache_dismissible = 'ao-nopagecache-forever'; // the notice is only shown once and will not re-appear when dismissed.
        $_is_ao_settings_page        = autoptimizeUtils::is_ao_settings();

        if ( current_user_can( 'manage_options' ) && $_is_ao_settings_page && PAnD::is_admin_notice_active( $_ao_nopagecache_dismissible ) && true === apply_filters( 'autoptimize_filter_main_show_pagecache_notice', true ) ) {
            if ( false === autoptimizeUtils::find_pagecache() ) {
                echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_nopagecache_dismissible . '"><p>';
                echo $_ao_nopagecache_notice;
                echo '</p></div>';
            }
        }
    }

    public static function notice_potential_conflict()
    {
        /*
         * Using other plugins to do CSS/ JS optimization can cause unexpected and hard to troubleshoot issues, warn users who seem to be in that situation.
         */
        // Translators: some strong tags + the sentence will be finished with the name of the offending plugin and a final stop.
        $_ao_potential_conflict_notice      = sprintf( esc_html__( 'It looks like you have %1$sanother plugin also doing CSS and/ or JS optimization%2$s, which can result in hard to troubleshoot %1$sconflicts%2$s. For this reason it is recommended to disable this functionality in', 'autoptimize' ), '<strong>', '</strong>' ) . ' ';
        $_ao_potential_conflict_dismissible = 'ao-potential-conflict-forever'; // the notice is only shown once and will not re-appear when dismissed.
        $_is_ao_settings_page               = autoptimizeUtils::is_ao_settings();

        if ( current_user_can( 'manage_options' ) && $_is_ao_settings_page && PAnD::is_admin_notice_active( $_ao_potential_conflict_dismissible ) && true === apply_filters( 'autoptimize_filter_main_show_potential_conclict_notice', true ) ) {
            $_potential_conflicts = autoptimizeUtils::find_potential_conflicts();
            if ( false !== $_potential_conflicts ) {
                $_ao_potential_conflict_notice .= '<strong>' . $_potential_conflicts . '</strong>.';
                echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_potential_conflict_dismissible . '"><p>';
                echo $_ao_potential_conflict_notice;
                echo '</p></div>';
            }
        }
    }
}
PK.�1\Q��"��autoptimizeCriticalCSSCron.phpnu�[���<?php
/**
 * Critical CSS Cron logic:
 * processes the queue, submitting jobs to criticalcss.com and retrieving generated CSS from criticalcss.com and saving rules.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCriticalCSSCron {
    /**
     * Critical CSS object.
     *
     * @var object
     */
    protected $criticalcss;

    public function __construct() {
        $this->criticalcss = autoptimize()->criticalcss();

        // Add queue control to a registered event.
        add_action( 'ao_ccss_queue', array( $this, 'ao_ccss_queue_control' ) );
        // Add cleaning job to a registered event.
        add_action( 'ao_ccss_maintenance', array( $this, 'ao_ccss_cleaning' ) );
    }

    public function ao_ccss_queue_control() {
        // The queue execution backend.
        $key = $this->criticalcss->get_option( 'key' );

        if ( empty( $key ) ) {
            // no key set, not processing the queue!
            $this->criticalcss->log( 'No key set, so not processing queue.', 3 );
            return;
        }

        /**
         * Provide a debug facility for the queue
         *    This debug facility provides a way to easily force some queue behaviors useful for development and testing.
         *    To enable this feature, create the file AO_CCSS_DIR . 'queue.json' with a JSON object like the one bellow:
         *
         *    {"enable":bool,"htcode":int,"status":0|"str","resultStatus ":0|"str","validationStatus":0|"str"}
         *
         *    Where values are:
         *    - enable                    : 0 or 1, enable or disable this debug facility straight from the file
         *    - htcode                    : 0 or any HTTP reponse code (e.g. 2xx, 4xx, 5xx) to force API responses
         *    - status                    : 0 or a valid value for 'status' (see 'Generating critical css - Job Status Types' in spec docs)
         *    - resultStatus        : 0 or a valid value for 'resultStatus' (see 'Appendix - Result status types' in the spec docs)
         *    - validationStatus: 0 or a valid value for 'validationStatus' (see 'Appendix - Validation status types' in the spec docs)
         *
         *    When properly set, queue will always finish a job with the declared settings above regardless of the real API responses.
         */
        $queue_debug = false;
        if ( file_exists( AO_CCSS_DEBUG ) ) {
            $qdobj_raw = file_get_contents( AO_CCSS_DEBUG );
            $qdobj     = json_decode( $qdobj_raw, true );
            if ( $qdobj ) {
                if ( 1 === $qdobj['enable'] ) {
                    $queue_debug = true;
                    $this->criticalcss->log( 'Queue operating in debug mode with the following settings: <' . $qdobj_raw . '>', 3 );
                }
            }
        }

        // Set some default values for $qdobj to avoid function call warnings.
        if ( ! $queue_debug ) {
            $qdobj['htcode'] = false;
        }

        // Check if queue is already running.
        $queue_lock = false;
        if ( file_exists( AO_CCSS_LOCK ) ) {
            $queue_lock = true;
        }

        // Proceed with the queue if it's not already running.
        if ( ! $queue_lock ) {

            // Log queue start and create the lock file.
            $this->criticalcss->log( 'Queue control started', 3 );
            if ( touch( AO_CCSS_LOCK ) ) {
                $this->criticalcss->log( 'Queue control locked', 3 );
            }

            // Attach required variables.
            $queue = $this->criticalcss->get_option( 'queue' );
            $rtimelimit = $this->criticalcss->get_option( 'rtimelimit' );

            // make sure we have the queue and bail if not.
            if ( empty( $queue ) || ! is_array( $queue ) ) {
                $this->criticalcss->log( 'Job processing cannot work on an empty queue, aborting.', 3 );
                unlink( AO_CCSS_LOCK );
                return;
            }

            // Initialize counters.
            if ( 0 == $rtimelimit ) {
                // no time limit set, let's go with 1000 seconds.
                $rtimelimit = 1000;
            }
            $mt = time() + $rtimelimit; // maxtime queue processing can run.
            $jc = 1; // job count number.
            $jr = 1; // jobs requests number.
            $jt = count( $queue ); // number of jobs in queue.

            // Sort queue by ascending job status (e.g. ERROR, JOB_ONGOING, JOB_QUEUED, NEW...).
            array_multisort( array_column( $queue, 'jqstat' ), $queue ); // @codingStandardsIgnoreLine

            // Iterates over the entire queue.
            foreach ( $queue as $path => $jprops ) {
                // Prepare flags and target rule.
                $update      = false;
                $deljob      = false;
                $rule_update = false;
                $oldccssfile = false;
                $trule       = explode( '|', $jprops['rtarget'] );

                // Log job count.
                $this->criticalcss->log( 'Processing job ' . $jc . ' of ' . $jt . ' with id <' . $jprops['ljid'] . '> and status <' . $jprops['jqstat'] . '>', 3 );

                // Process NEW jobs.
                if ( 'NEW' == $jprops['jqstat'] ) {

                    // Log the new job.
                    $this->criticalcss->log( 'Found NEW job with local ID <' . $jprops['ljid'] . '>, starting its queue processing', 3 );

                    // Compare job and rule hashes (if any).
                    $hash = $this->ao_ccss_diff_hashes( $jprops['ljid'], $jprops['hash'], $jprops['hashes'], $jprops['rtarget'] );

                    // If job hash is new or different of a previous one.
                    if ( $hash ) {
                        if ( $jr > 2 ) {
                            // we already posted 2 jobs to criticalcss.com, don't post more this run
                            // but we can keep on processing the queue to keep it tidy.
                            $this->criticalcss->log( 'Holding off on generating request for job with local ID <' . $jprops['ljid'] . '>, maximum number of POSTS reached.', 3 );
                            continue;
                        }

                        // Set job hash.
                        $jprops['hash'] = $hash;

                        // Dispatch the job generate request and increment request count.
                        $apireq = $this->ao_ccss_api_generate( $path, $queue_debug, $qdobj['htcode'] );
                        $jr++;

                        // NOTE: All the following conditions maps to the ones in admin_settings_queue.js.php.
                        if ( empty( $apireq ) ) {
                            // ERROR: no response
                            // Update job properties.
                            $jprops['jqstat'] = 'NO_RESPONSE';
                            $jprops['jrstat'] = 'NONE';
                            $jprops['jvstat'] = 'NONE';
                            $jprops['jftime'] = microtime( true );
                            $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> request has no response, status now is <' . $jprops['jqstat'] . '>', 3 );
                        } elseif ( array_key_exists( 'errorCode', $apireq ) && 'INVALID_JWT_TOKEN' == $apireq['errorCode'] ) {
                            // ERROR: key validation
                            // Update job properties.
                            $jprops['jqstat'] = $apireq['errorCode'];
                            $jprops['jrstat'] = $apireq['error'];
                            $jprops['jvstat'] = 'NONE';
                            $jprops['jftime'] = microtime( true );
                            $this->criticalcss->log( 'API key validation error when processing job id <' . $jprops['ljid'] . '>, job status is now <' . $jprops['jqstat'] . '>', 3 );
                        } elseif ( array_key_exists( 'job', $apireq ) && array_key_exists( 'status', $apireq['job'] ) && ( 'JOB_QUEUED' == $apireq['job']['status'] || 'JOB_ONGOING' == $apireq['job']['status'] ) ) {
                            // SUCCESS: request has a valid result.
                            // Update job properties.
                            $jprops['jid']    = $apireq['job']['id'];
                            $jprops['jqstat'] = $apireq['job']['status'];
                            $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> generate request successful, remote id <' . $jprops['jid'] . '>, status now is <' . $jprops['jqstat'] . '>', 3 );
                        } elseif ( array_key_exists( 'job', $apireq ) && array_key_exists( 'status', $apireq['job'] ) && 'STATUS_JOB_BAD' == $apireq['job']['status'] ) {
                            // ERROR: concurrent requests
                            // Update job properties.
                            $jprops['jid']    = $apireq['job']['id'];
                            $jprops['jqstat'] = $apireq['job']['status'];
                            if ( $apireq['job']['error'] ) {
                                $jprops['jrstat'] = $apireq['job']['error'];
                            } else {
                                $jprops['jrstat'] = 'Baby did a bad bad thing';
                            }
                            $jprops['jvstat'] = 'NONE';
                            $jprops['jftime'] = microtime( true );
                            $this->criticalcss->log( 'Concurrent requests when processing job id <' . $jprops['ljid'] . '>, job status is now <' . $jprops['jqstat'] . '>', 3 );
                        } else {
                            // UNKNOWN: unhandled generate exception
                            // Update job properties.
                            $jprops['jqstat'] = 'JOB_UNKNOWN';
                            $jprops['jrstat'] = 'NONE';
                            $jprops['jvstat'] = 'NONE';
                            $jprops['jftime'] = microtime( true );
                            $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> generate request has an UNKNOWN condition, status now is <' . $jprops['jqstat'] . '>, check log messages above for more information', 2 );
                            $this->criticalcss->log( 'Job response was: ' . json_encode( $apireq ), 3 );
                        }
                    } else {
                        // SUCCESS: Job hash is equal to a previous one, so it's done
                        // Update job status and finish time.
                        $jprops['jqstat'] = 'JOB_DONE';
                        $jprops['jftime'] = microtime( true );
                        $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> requires no further processing, status now is <' . $jprops['jqstat'] . '>', 3 );
                    }

                    // Set queue update flag.
                    $update = true;

                } elseif ( 'JOB_QUEUED' == $jprops['jqstat'] || 'JOB_ONGOING' == $jprops['jqstat'] ) {
                    // Process QUEUED and ONGOING jobs
                    // Log the pending job.
                    $this->criticalcss->log( 'Found PENDING job with local ID <' . $jprops['ljid'] . '>, continuing its queue processing', 3 );

                    // Dispatch the job result request and increment request count.
                    $apireq = $this->ao_ccss_api_results( $jprops['jid'], $queue_debug, $qdobj['htcode'] );

                    // NOTE: All the following condigitons maps to the ones in admin_settings_queue.js.php
                    // Replace API response values if queue debugging is enabled and some value is set.
                    if ( $queue_debug ) {
                        if ( $qdobj['status'] ) {
                            $apireq['status'] = $qdobj['status'];
                        }
                        if ( $qdobj['resultStatus'] ) {
                            $apireq['resultStatus'] = $qdobj['resultStatus'];
                        }
                        if ( $qdobj['validationStatus'] ) {
                            $apireq['validationStatus'] = $qdobj['validationStatus'];
                        }
                    }

                    if ( empty( $apireq ) || ! is_array( $apireq ) ) {
                        // ERROR: no response
                        // Update job properties.
                        $jprops['jqstat'] = 'NO_RESPONSE';
                        $jprops['jrstat'] = 'NONE';
                        $jprops['jvstat'] = 'NONE';
                        $jprops['jftime'] = microtime( true );
                        $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> request has no response, status now is <' . $jprops['jqstat'] . '>', 3 );
                    } elseif ( array_key_exists( 'status', $apireq ) && ( 'JOB_QUEUED' == $apireq['status'] || 'JOB_ONGOING' == $apireq['status'] ) ) {
                        // SUCCESS: request has a valid result
                        // Process a PENDING job
                        // Update job properties.
                        $jprops['jqstat'] = $apireq['status'];
                        $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . '> unchanged', 3 );
                    } elseif ( array_key_exists( 'status', $apireq ) && 'JOB_DONE' == $apireq['status'] ) {
                        // Process a DONE job
                        // New resultStatus from ccss.com "HTML_404", consider as "GOOD" for now.
                        if ( 'HTML_404' == $apireq['resultStatus'] ) {
                            $apireq['resultStatus'] = 'GOOD';
                        }

                        if ( 'GOOD' == $apireq['resultStatus'] && ( 'GOOD' == $apireq['validationStatus'] || 'WARN' == $apireq['validationStatus'] ) ) {
                            // SUCCESS: GOOD job with GOOD or WARN validation
                            // Update job properties.
                            $jprops['file']   = $this->ao_ccss_save_file( $apireq['css'], $trule, false );
                            $jprops['jqstat'] = $apireq['status'];
                            $jprops['jrstat'] = $apireq['resultStatus'];
                            $jprops['jvstat'] = $apireq['validationStatus'];
                            $jprops['jftime'] = microtime( true );
                            $rule_update      = true;
                            do_action( 'autoptimize_action_ccss_cron_rule_saved', $jprops['rtarget'], $jprops['file'] );
                            $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . '>, file saved <' . $jprops['file'] . '>', 3 );
                        } elseif ( 'GOOD' == $apireq['resultStatus'] && ( 'BAD' == $apireq['validationStatus'] || 'SCREENSHOT_WARN_BLANK' == $apireq['validationStatus'] ) ) {
                            // SUCCESS: GOOD job with BAD or SCREENSHOT_WARN_BLANK validation
                            // Update job properties.
                            $jprops['jqstat'] = $apireq['status'];
                            $jprops['jrstat'] = $apireq['resultStatus'];
                            $jprops['jvstat'] = $apireq['validationStatus'];
                            $jprops['jftime'] = microtime( true );
                            if ( apply_filters( 'autoptimize_filter_ccss_save_review_rules', true ) ) {
                                $jprops['file']   = $this->ao_ccss_save_file( $apireq['css'], $trule, true );
                                $rule_update      = true;
                                do_action( 'autoptimize_action_ccss_cron_rule_saved', $jprops['rtarget'], $jprops['file'] );
                                $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . ', file saved <' . $jprops['file'] . '> but requires REVIEW', 3 );
                            } else {
                                $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . ', file not saved because it required REVIEW.', 3 );
                            }
                        } elseif ( 'GOOD' != $apireq['resultStatus'] && ( 'GOOD' != $apireq['validationStatus'] || 'WARN' != $apireq['validationStatus'] || 'BAD' != $apireq['validationStatus'] || 'SCREENSHOT_WARN_BLANK' != $apireq['validationStatus'] ) ) {
                            // ERROR: no GOOD, WARN or BAD results
                            // Update job properties.
                            $jprops['jqstat'] = $apireq['status'];
                            $jprops['jrstat'] = $apireq['resultStatus'];
                            $jprops['jvstat'] = $apireq['validationStatus'];
                            $jprops['jftime'] = microtime( true );
                            $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful but job FAILED, status now is <' . $jprops['jqstat'] . '>', 3 );
                            $apireq['css'] = '/* critical css removed for DEBUG logging purposes */';
                            $this->criticalcss->log( 'Job response was: ' . json_encode( $apireq ), 3 );
                        } else {
                            // UNKNOWN: unhandled JOB_DONE exception
                            // Update job properties.
                            $jprops['jqstat'] = 'JOB_UNKNOWN';
                            $jprops['jrstat'] = $apireq['resultStatus'];
                            $jprops['jvstat'] = $apireq['validationStatus'];
                            $jprops['jftime'] = microtime( true );
                            $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful but job is UNKNOWN, status now is <' . $jprops['jqstat'] . '>', 2 );
                            $apireq['css'] = '/* critical css removed for DEBUG logging purposes */';
                            $this->criticalcss->log( 'Job response was: ' . json_encode( $apireq ), 3 );
                        }
                    } elseif ( array_key_exists( 'job', $apireq ) && is_array( $apireq['job'] ) && array_key_exists( 'status', $apireq['job'] ) && ( 'JOB_FAILED' == $apireq['job']['status'] || 'STATUS_JOB_BAD' == $apireq['job']['status'] ) ) {
                        // ERROR: failed job
                        // Update job properties.
                        $jprops['jqstat'] = $apireq['job']['status'];
                        if ( $apireq['job']['error'] ) {
                            $jprops['jrstat'] = $apireq['job']['error'];
                        } else {
                            $jprops['jrstat'] = 'Baby did a bad bad thing';
                        }
                        $jprops['jvstat'] = 'NONE';
                        $jprops['jftime'] = microtime( true );
                        $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful but job FAILED, status now is <' . $jprops['jqstat'] . '>', 3 );
                    } elseif ( array_key_exists( 'error', $apireq ) && 'This css no longer exists. Please re-generate it.' == $apireq['error'] ) {
                        // ERROR: CSS doesn't exist
                        // Update job properties.
                        $jprops['jqstat'] = 'NO_CSS';
                        $jprops['jrstat'] = $apireq['error'];
                        $jprops['jvstat'] = 'NONE';
                        $jprops['jftime'] = microtime( true );
                        $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful but job FAILED, status now is <' . $jprops['jqstat'] . '>', 3 );
                    } else {
                        // UNKNOWN: unhandled results exception
                        // Update job properties.
                        $jprops['jqstat'] = 'JOB_UNKNOWN';
                        $jprops['jrstat'] = 'NONE';
                        $jprops['jvstat'] = 'NONE';
                        $jprops['jftime'] = microtime( true );
                        $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request has an UNKNOWN condition, status now is <' . $jprops['jqstat'] . '>, check log messages above for more information', 2 );
                    }

                    // Set queue update flag.
                    $update = true;
                }

                // Mark DONE jobs for removal.
                if ( 'JOB_DONE' == $jprops['jqstat'] ) {
                    $update = true;
                    $deljob = true;
                }

                // Persist updated queue object.
                if ( $update ) {
                    if ( ! $deljob ) {
                        // Update properties of a NEW or PENDING job...
                        $queue[ $path ] = $jprops;
                    } else {
                        // ...or remove the DONE job.
                        unset( $queue[ $path ] );
                        $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> is DONE and was removed from the queue', 3 );
                    }

                    // Update queue object.
                    $queue_raw = json_encode( $queue );
                    update_option( 'autoptimize_ccss_queue', $queue_raw, false );
                    $this->criticalcss->log( 'Queue updated by job id <' . $jprops['ljid'] . '>', 3 );

                    // Update target rule.
                    if ( $rule_update ) {
                        $this->ao_ccss_rule_update( $jprops['ljid'], $jprops['rtarget'], $jprops['file'], $jprops['hash'] );
                        $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> updated the target rule <' . $jprops['rtarget'] . '>', 3 );
                    }
                } else {
                    // Or log no queue action.
                    $this->criticalcss->log( 'Nothing to do on this job', 3 );
                }

                // Break the loop if request time limit is (almost exceeded).
                if ( time() > $mt ) {
                    $this->criticalcss->log( 'The time limit of ' . $rtimelimit . ' seconds was exceeded, queue control must finish now', 3 );
                    break;
                }

                // Increment job counter.
                $jc++;
            }

            // Remove the lock file and log the queue end.
            if ( file_exists( AO_CCSS_LOCK ) ) {
                unlink( AO_CCSS_LOCK );
                $this->criticalcss->log( 'Queue control unlocked', 3 );
            }
            $this->criticalcss->log( 'Queue control finished', 3 );

            // Log that queue is locked.
        } else {
            $this->criticalcss->log( 'Queue is already running, skipping the attempt to run it again', 3 );
        }
    }

    public function ao_ccss_diff_hashes( $ljid, $hash, $hashes, $rule ) {
        // Compare job hashes
        // STEP 1: update job hashes.
        if ( 1 == count( $hashes ) ) {
            // Job with a single hash
            // Set job hash.
            $hash = $hashes[0];
            $this->criticalcss->log( 'Job id <' . $ljid . '> updated with SINGLE hash <' . $hash . '>', 3 );
        } else {
            // Job with multiple hashes
            // Loop through hashes to concatenate them.
            $nhash = '';
            foreach ( $hashes as $shash ) {
                $nhash .= $shash;
            }

            // Set job hash.
            $hash = md5( $nhash );
            $this->criticalcss->log( 'Job id <' . $ljid . '> updated with a COMPOSITE hash <' . $hash . '>', 3 );
        }

        // STEP 2: compare job to existing jobs to prevent double submission for same type+hash.
        $queue = $this->criticalcss->get_option( 'queue' );

        foreach ( $queue as $queue_item ) {
            $this->criticalcss->log( 'Comparing <' . $rule . $hash . '> with <' . $queue_item['rtarget'] . $queue_item['hash'] . '>', 3 );
            if ( $queue_item['hash'] == $hash && $queue_item['rtarget'] == $rule && in_array( $queue_item['jqstat'], array( 'JOB_QUEUED', 'JOB_ONGOING', 'JOB_DONE' ) ) ) {
                $this->criticalcss->log( 'Job id <' . $ljid . '> matches the already pending job <' . $queue_item['ljid'] . '>', 3 );
                return false;
            }
        }

        // STEP 3: compare job and existing rule (if any) hashes
        // Attach required arrays.
        $rules = $this->criticalcss->get_option( 'rules' );

        // Prepare rule variables.
        $trule = explode( '|', $rule );
        if ( is_array( $trule ) && ! empty( $trule ) && array_key_exists( $trule[1], $rules[ $trule[0] ] ) ) {
            $srule = $rules[ $trule[0] ][ $trule[1] ];
        } else {
            $srule = '';
        }

        // If hash is empty, set it to now for a "forced job".
        if ( empty( $hash ) ) {
            $hash = 'new';
            $this->criticalcss->log( 'Job id <' . $ljid . '> had no hash, assuming forced job so setting hash to new', 3 );
        }

        // Check if a MANUAL rule exist and return false.
        if ( ! empty( $srule ) && ( 0 == $srule['hash'] && 0 != $srule['file'] ) ) {
            $this->criticalcss->log( 'Job id <' . $ljid . '> matches the MANUAL rule <' . $trule[0] . '|' . $trule[1] . '>', 3 );
            return false;
        } elseif ( ! empty( $srule ) ) {
            // Check if an AUTO rule exist.
            if ( $hash === $srule['hash'] && is_file( AO_CCSS_DIR . $srule['file'] ) && 0 != filesize( AO_CCSS_DIR . $srule['file'] ) ) {
                // Check if job hash matches rule, if the CCSS file exists said file is not empty and return FALSE is so.
                $this->criticalcss->log( 'Job id <' . $ljid . '> with hash <' . $hash . '> MATCH the one in rule <' . $trule[0] . '|' . $trule[1] . '>', 3 );
                return false;
            } else {
                // Or return the new hash if they differ.
                $this->criticalcss->log( 'Job id <' . $ljid . '> with hash <' . $hash . '> DOES NOT MATCH the one in rule <' . $trule[0] . '|' . $trule[1] . '> or rule\'s CCSS file was invalid.', 3 );
                return $hash;
            }
        } else {
            // Return the hash for a job that has no rule yet.
            $this->criticalcss->log( 'Job id <' . $ljid . '> with hash <' . $hash . '> has no rule yet', 3 );
            return $hash;
        }
    }

    public function ao_ccss_api_generate( $path, $debug, $dcode ) {
        // POST jobs to criticalcss.com and return responses
        // Get key and key status.
        $key = $this->criticalcss->get_option( 'key' );
        $key_status = $this->criticalcss->get_option( 'keyst' );
        $noptimize = $this->criticalcss->get_option( 'noptimize' );

        // Prepare full URL to request.
        $site_host = get_site_url();
        $site_path = parse_url( $site_host, PHP_URL_PATH );

        if ( ! empty( $site_path ) ) {
            $site_host = str_replace( $site_path, '', $site_host );
        }

        // Logic to bind to one domain to avoid site clones of sites would
        // automatically begin spawning requests to criticalcss.com which has
        // a per domain cost.
        $domain = $this->criticalcss->get_option( 'domain' );
        if ( empty( $domain ) ) {
            // first request being done, update option to allow future requests are only allowed if from same domain.
            update_option( 'autoptimize_ccss_domain', str_rot13( $site_host ) );
        } elseif ( trim( $domain, '\'"' ) !== 'none' && parse_url( $site_host, PHP_URL_HOST ) !== parse_url( $domain, PHP_URL_HOST ) && apply_filters( 'autoptimize_filter_ccss_bind_domain', true ) ) {
            // not the same domain, log as error and return without posting to criticalcss.com.
            $this->criticalcss->log( 'Request for domain ' . $site_host . ' does not match bound domain ' . $domain . ' so not proceeding.', 2 );
            return false;
        }

        $src_url = $site_host . $path;

        // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
        if ( ! empty( $noptimize ) ) {
            $src_url .= '?ao_noptirocket=1';
        } elseif ( ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) || apply_filters( 'autoptimize_filter_ccss_enforce_nolazy', false ) ) {
            $src_url .= '?ao_nolazy=1';
        }

        $src_url = apply_filters( 'autoptimize_filter_ccss_cron_srcurl', $src_url );

        if ( true !== autoptimizeUtils::is_local_server( parse_url( $src_url, PHP_URL_HOST ) ) ) {
            // Initialize request body.
            $body           = array();
            $body['url']    = $src_url;
            $body['aff']    = 1;
            $body['aocssv'] = AO_CCSS_VER;

            // Prepare and add viewport size to the body if available.
            $viewport = $this->criticalcss->viewport();
            if ( ! empty( $viewport['w'] ) && ! empty( $viewport['h'] ) ) {
                $body['width']  = $viewport['w'];
                $body['height'] = $viewport['h'];
            }

            // Prepare and add forceInclude to the body if available.
            $finclude = $this->criticalcss->get_option( 'finclude' );
            $finclude = $this->ao_ccss_finclude( $finclude );
            if ( ! empty( $finclude ) ) {
                $body['forceInclude'] = $finclude;
            }

            // Add filter to allow the body array to be altered (e.g. to add customPageHeaders).
            $body = apply_filters( 'autoptimize_ccss_cron_api_generate_body', $body );

            // Body must be json and log it.
            $body = json_encode( $body, JSON_UNESCAPED_SLASHES );
            $this->criticalcss->log( 'criticalcss.com: POST generate request body is ' . $body, 3 );

            // Prepare the request.
            $url  = esc_url_raw( AO_CCSS_API . 'generate?aover=' . AO_CCSS_VER );
            $args = array(
                'headers' => apply_filters(
                    'autoptimize_ccss_cron_api_generate_headers',
                    array(
                        'User-Agent'    => 'Autoptimize v' . AO_CCSS_VER,
                        'Content-type'  => 'application/json; charset=utf-8',
                        'Authorization' => 'JWT ' . $key,
                        'Connection'    => 'close',
                    )
                ),
                'body'    => $body,
            );

            // Dispatch the request and store its response code.
            $req  = wp_safe_remote_post( $url, $args );
            $code = wp_remote_retrieve_response_code( $req );
            $body = json_decode( wp_remote_retrieve_body( $req ), true );

            if ( $debug && $dcode ) {
                // If queue debug is active, change response code.
                $code = $dcode;
            }

            if ( 200 == $code ) {
                // Response code is OK.
                // Workaround criticalcss.com non-RESTful reponses.
                if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] || 'STATUS_JOB_BAD' == $body['job']['status'] ) {
                    // Log successful and return encoded request body.
                    $this->criticalcss->log( 'criticalcss.com: POST generate request for path <' . $src_url . '> replied successfully', 3 );

                    // This code also means the key is valid, so cache key status for 24h if not already cached.
                    if ( ( ! $key_status || 2 != $key_status ) && $key ) {
                        update_option( 'autoptimize_ccss_keyst', 2 );
                        $this->criticalcss->log( 'criticalcss.com: API key is valid, updating key status', 3 );
                    }

                    // Return the request body.
                    return $body;
                } else {
                    // Log successful requests with invalid reponses.
                    $this->criticalcss->log( 'criticalcss.com: POST generate request for path <' . $src_url . '> replied with code <' . $code . '> and an UNKNOWN error condition, body follows...', 2 );
                    $this->criticalcss->log( print_r( $body, true ), 2 );
                    return $body;
                }
            } else {
                // Response code is anything else.
                // Log failed request with a valid response code and return body.
                if ( $code ) {
                    $this->criticalcss->log( 'criticalcss.com: POST generate request for path <' . $src_url . '> replied with error code <' . $code . '>, body follows...', 2 );
                    $this->criticalcss->log( print_r( $body, true ), 2 );

                    if ( 401 == $code ) {
                        // If request is unauthorized, also clear key status.
                        update_option( 'autoptimize_ccss_keyst', 1 );
                        $this->criticalcss->log( 'criticalcss.com: API key is invalid, updating key status', 3 );
                    }

                    // Return the request body.
                    return $body;
                } else {
                    // Log failed request with no response and return false.
                    $this->criticalcss->log( 'criticalcss.com: POST generate request for path <' . $src_url . '> has no response, this could be a service timeout', 2 );
                    if ( is_wp_error( $req ) ) {
                        $this->criticalcss->log( $req->get_error_message(), 2 );
                    }

                    return false;
                }
            }
        } else {
            // localhost/ private network server, no CCSS possible.
            $this->criticalcss->log( 'ccss cron: job not created at ccss.com as for local server', 3 );
            return false;
        }
    }

    public function ao_ccss_api_results( $jobid, $debug, $dcode ) {
        // GET jobs from criticalcss.com and return responses
        // Get key.
        $key = $this->criticalcss->get_option( 'key' );

        // Prepare the request.
        $url  = AO_CCSS_API . 'results?resultId=' . $jobid;
        $args = array(
            'headers' => apply_filters(
                'autoptimize_ccss_cron_api_generate_headers',
                array(
                    'User-Agent'    => 'Autoptimize CriticalCSS Power-Up v' . AO_CCSS_VER,
                    'Authorization' => 'JWT ' . $key,
                    'Connection'    => 'close',
                )
            ),
        );

        // Dispatch the request and store its response code.
        $req  = wp_safe_remote_get( $url, $args );
        $code = wp_remote_retrieve_response_code( $req );
        $body = json_decode( wp_remote_retrieve_body( $req ), true );

        if ( $debug && $dcode ) {
            // If queue debug is active, change response code.
            $code = $dcode;
        }

        if ( 200 == $code ) {
            // Response code is OK.
            if ( is_array( $body ) && ( array_key_exists( 'status', $body ) || array_key_exists( 'job', $body ) ) && ( 'JOB_QUEUED' == $body['status'] || 'JOB_ONGOING' == $body['status'] || 'JOB_DONE' == $body['status'] || 'JOB_FAILED' == $body['status'] || 'JOB_UNKNOWN' == $body['status'] || 'STATUS_JOB_BAD' == $body['job']['status'] ) ) {
                // Workaround criticalcss.com non-RESTful reponses
                // Log successful and return encoded request body.
                $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied successfully', 3 );
                return $body;
            } elseif ( is_array( $body ) && ( array_key_exists( 'error', $body ) && 'This css no longer exists. Please re-generate it.' == $body['error'] ) ) {
                // Handle no CSS reply
                // Log no CSS error and return encoded request body.
                $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied successfully but the CSS for it does not exist anymore', 3 );
                return $body;
            } else {
                // Log failed request and return false.
                $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied with code <' . $code . '> and an UNKNOWN error condition, body follows...', 2 );
                $this->criticalcss->log( print_r( $body, true ), 2 );
                return false;
            }
        } else {
            // Response code is anything else
            // Log failed request with a valid response code and return body.
            if ( $code ) {
                $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied with error code <' . $code . '>, body follows...', 2 );
                $this->criticalcss->log( print_r( $body, true ), 2 );
                if ( 401 == $code ) {
                    // If request is unauthorized, also clear key status.
                    update_option( 'autoptimize_ccss_keyst', 1 );
                    $this->criticalcss->log( 'criticalcss.com: API key is invalid, updating key status', 3 );
                }

                // Return the request body.
                return $body;
            } else {
                // Log failed request with no response and return false.
                $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> has no response, this could be a service timeout', 2 );
                return false;
            }
        }
    }

    public function ao_ccss_save_file( $ccss, $target, $review ) {
        // Save critical CSS into the filesystem and return its filename
        // Prepare review mark.
        if ( $review ) {
            $rmark = '_R';
        } else {
            $rmark = '';
        }

        // Prepare target rule, filename and content.
        $filename = false;
        $content  = $ccss;

        if ( $this->criticalcss->check_contents( $content ) ) {
            // Sanitize content, set filename and try to save file.
            $file     = AO_CCSS_DIR . 'ccss_' . md5( $ccss . $target[1] ) . $rmark . '.css';
            $status   = file_put_contents( $file, $content, LOCK_EX );
            $filename = pathinfo( $file, PATHINFO_BASENAME );
            $this->criticalcss->log( 'Critical CSS file for the rule <' . $target[0] . '|' . $target[1] . '> was saved as <' . $filename . '>, size in bytes is <' . $status . '>', 3 );

            if ( ! $status ) {
                // If file has not been saved, reset filename.
                $this->criticalcss->log( 'Critical CSS file <' . $filename . '> could not be not saved', 2 );
                $filename = false;
                return $filename;
            }
        } else {
            $this->criticalcss->log( 'Critical CSS received did not pass content check', 2 );
            return $filename;
        }

        // Remove old critical CSS if a previous one existed in the rule and if that file exists in filesystem
        // Attach required arrays.
        $rules = $this->criticalcss->get_option( 'rules' );

        // Only proceed if the rule already existed.
        if ( array_key_exists( $target[1], $rules[ $target[0] ] ) ) {
            $srule   = $rules[ $target[0] ][ $target[1] ];
            $oldfile = $srule['file'];

            if ( $oldfile && $oldfile !== $filename ) {
                $delfile = AO_CCSS_DIR . $oldfile;
                if ( file_exists( $delfile ) ) {
                    $unlinkst = unlink( $delfile );
                    if ( $unlinkst ) {
                        $this->criticalcss->log( 'A previous critical CSS file <' . $oldfile . '> was removed for the rule <' . $target[0] . '|' . $target[1] . '>', 3 );
                    }
                }
            }
        }

        // Return filename or false.
        return $filename;
    }

    public function ao_ccss_rule_update( $ljid, $srule, $file, $hash ) {
        // Update or create a rule
        // Attach required arrays.
        $rules = $this->criticalcss->get_option( 'rules' );

        // Prepare rule variables.
        $trule  = explode( '|', $srule );
        if ( array_key_exists( $trule[1], $rules[ $trule[0] ] ) ) {
            $rule = $rules[ $trule[0] ][ $trule[1] ];
        } else {
            $rule = array();
        }
        $action = false;
        $rtype  = '';

        if ( is_array( $rule ) && array_key_exists( 'hash', $rule ) && 0 === $rule['hash'] && array_key_exists( 'file', $rule ) && 0 !== $rule['file'] ) {
            // manual rule, don't ever overwrite.
            $action = 'NOT UPDATED';
            $rtype  = 'MANUAL';
        } elseif ( is_array( $rule ) && array_key_exists( 'hash', $rule ) && 0 === $rule['hash'] && array_key_exists( 'file', $rule ) && 0 === $rule['file'] ) {
            // If this is an user created AUTO rule with no hash and file yet, update its hash and filename
            // Set rule hash, file and action flag.
            $rule['hash'] = $hash;
            $rule['file'] = $file;
            $action       = 'UPDATED';
            $rtype        = 'AUTO';
        } elseif ( is_array( $rule ) && array_key_exists( 'hash', $rule ) && 0 !== $rule['hash'] && ctype_alnum( $rule['hash'] ) ) {
            // If this is an genuine AUTO rule, update its hash and filename
            // Set rule hash, file and action flag.
            $rule['hash'] = $hash;
            $rule['file'] = $file;
            $action       = 'UPDATED';
            $rtype        = 'AUTO';
        } else {
            // If rule doesn't exist, create an AUTO rule
            // AUTO rules were only for types, but will now also work for paths.
            if ( ( 'types' == $trule[0] || 'paths' == $trule[0] ) && ! empty( $trule[1] ) ) {
                // Set rule hash and file and action flag.
                $rule['hash'] = $hash;
                $rule['file'] = $file;
                $action       = 'CREATED';
                $rtype        = 'AUTO';
            } else {
                // Log that no rule was created.
                $this->criticalcss->log( 'Exception, no AUTO rule created', 3 );
            }
        }

        if ( $action ) {
            // If a rule creation/update is required, persist updated rules object.
            $rules[ $trule[0] ][ $trule[1] ] = $rule;
            $rules_raw = json_encode( $rules );
            update_option( 'autoptimize_ccss_rules', $rules_raw );
            $this->criticalcss->flush_options();
            $this->criticalcss->log( 'Target rule <' . $srule . '> of type <' . $rtype . '> was ' . $action . ' for job id <' . $ljid . '>', 3 );
            
            // and trigger action for whoever needs to be aware.
            do_action( 'autoptimize_action_ccss_cron_rule_updated', $srule, $file, '' );
        } else {
            $this->criticalcss->log( 'No rule action required', 3 );
        }
    }

    function ao_ccss_finclude( $finclude_raw ) {
        // Prepare forceInclude object.
        if ( ! empty( $finclude_raw ) ) {
            // If there are any content
            // Convert raw string into arra and initialize the returning object.
            $fincludes = explode( ',', $finclude_raw );
            $finclude  = array();

            // Interacts over every rule.
            $i = 0;
            foreach ( $fincludes as $include ) {
                // Trim leading and trailing whitespaces.
                $include = trim( $include );

                if ( substr( $include, 0, 2 ) === '//' ) {
                    // Regex rule
                    // Format value as required.
                    $include = str_replace( '//', '/', $include );
                    $include = $include . '/i';

                    // Store regex object.
                    $finclude[ $i ]['type']  = 'RegExp';
                    $finclude[ $i ]['value'] = $include;
                } else {
                    // Simple value rule.
                    $finclude[ $i ]['value'] = $include;
                }

                $i++;
            }

            // Return forceInclude object.
            return $finclude;
        } else {
            // Or just return false if empty.
            return false;
        }
    }

    public function ao_ccss_cleaning() {
        // Perform plugin maintenance
        // Truncate log file >= 1MB .
        if ( file_exists( AO_CCSS_LOG ) ) {
            if ( filesize( AO_CCSS_LOG ) >= 1048576 ) {
                $logfile = fopen( AO_CCSS_LOG, 'w' );
                fclose( $logfile );
            }
        }

        // Remove lock file.
        if ( file_exists( AO_CCSS_LOCK ) ) {
            unlink( AO_CCSS_LOCK );
        }

        // Make sure queue processing is scheduled, recreate if not.
        if ( ! wp_next_scheduled( 'ao_ccss_queue' ) ) {
            wp_schedule_event( time(), apply_filters( 'ao_ccss_queue_schedule', 'ao_ccss' ), 'ao_ccss_queue' );
        }

        // Queue cleaning.
        $queue = $this->criticalcss->get_option( 'queue' );

        $queue_purge_threshold = 100;
        $queue_purge_age       = 24 * 60 * 60;
        $queue_length          = count( $queue );
        $timestamp_yesterday   = microtime( true ) - $queue_purge_age;
        $remove_old_new        = false;
        $queue_altered         = false;

        if ( $queue_length > $queue_purge_threshold ) {
            $remove_old_new = true;
        }

        foreach ( $queue as $path => $job ) {
            if ( ( $remove_old_new && 'NEW' == $job['jqstat'] && $job['jctime'] < $timestamp_yesterday ) || in_array( $job['jqstat'], array( 'JOB_FAILED', 'STATUS_JOB_BAD', 'NO_CSS', 'NO_RESPONSE' ) ) ) {
                unset( $queue[ $path ] );
                $queue_altered = true;
            }
        }

        // save queue to options!
        if ( $queue_altered ) {
            $queue_raw = json_encode( $queue );
            update_option( 'autoptimize_ccss_queue', $queue_raw, false );
            $this->criticalcss->log( 'Queue cleaning done.', 3 );
        }

        // re-check key if invalid.
        $keyst = $this->criticalcss->get_option( 'keyst' );
        if ( 1 == $keyst ) {
            $this->ao_ccss_api_generate( '', '', '' );
        }
    }
}
PK.�1\�M��
index.htmlnu�[���<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>PK.�1\���nxxautoptimizeCriticalCSSCore.phpnu�[���<?php
/**
 * Critical CSS Core logic:
 * gets called by AO core, checks the rules and if a matching rule is found returns the associated CCSS.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCriticalCSSCore {
    /**
     * Critical CSS page types.
     *
     * @var array
     */
    protected $_types = null;
    
    /**
     * Critical CSS object.
     *
     * @var object
     */
    protected $criticalcss;

    public function __construct() {
        $this->criticalcss = autoptimize()->criticalcss();
        $this->run();
    }

    public function run() {
        $css_defer   = $this->criticalcss->get_option( 'css_defer' );
        $deferjquery = $this->criticalcss->get_option( 'deferjquery' );
        $unloadccss  = $this->criticalcss->get_option( 'unloadccss' );

        if ( ! $css_defer ) {
            return;
        }

        // add all filters to do CCSS
        // Set AO behavior: disable minification to avoid double minifying and caching.
        add_filter( 'autoptimize_filter_css_critcss_minify', '__return_false' );
        add_filter( 'autoptimize_filter_css_defer_inline', array( $this, 'ao_ccss_frontend' ), 10, 1 );

        // Add the action to enqueue jobs for CriticalCSS cron.
        if ( $this->criticalcss->is_api_active() ) {
            add_action( 'autoptimize_action_css_hash', array( $this->criticalcss, 'enqueue' ), 10, 1 );
        }

        // conditionally add the filter to defer jquery and others but only if not done so in autoptimizeScripts.
        $_native_defer = false;
        if ( 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_not_aggregate' ) && 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_inline' ) ) {
            $_native_defer = true;
        }
        if ( $deferjquery && ! $_native_defer ) {
            add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_defer_jquery' ), 11, 1 );
        }

        // conditionally add filter to unload the CCSS.
        if ( $unloadccss ) {
            add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_unloadccss' ), 12, 1 );
        }

        // Order paths by length, as longest ones have greater priority in the rules.
        $rules = $this->criticalcss->get_option( 'rules' );
        if ( ! empty( $rules['paths'] ) ) {
            $keys = array_map( 'strlen', array_keys( $rules['paths'] ) );
            array_multisort( $keys, SORT_DESC, $rules['paths'] );
            // TODO: Not sure what we're doing here. Sorted the $keys,
            // but they don't seem to be used anywhere.
        }

        // Add an array with default WordPress's conditional tags
        // NOTE: these tags are sorted.
        $this->_types = $this->get_ao_ccss_core_types();

        // Extend conditional tags on plugin initalization.
        add_action( apply_filters( 'autoptimize_filter_ccss_extend_types_hook', 'init' ), array( $this, 'ao_ccss_extend_types' ) );

        // When autoptimize cache is cleared, also clear transient cache for page templates.
        add_action( 'autoptimize_action_cachepurged', array( $this, 'ao_ccss_clear_page_tpl_cache' ), 10, 0 );
    }

    public function ao_ccss_frontend( $inlined ) {
        // Apply CriticalCSS to frontend pages
        // Attach types and settings arrays.
        $rules      = $this->criticalcss->get_option( 'rules' );
        $additional = $this->criticalcss->get_option( 'additional' );
        $loggedin   = $this->criticalcss->get_option( 'loggedin' );
        $debug      = $this->criticalcss->get_option( 'debug' );
        $no_ccss    = '';
        $additional = autoptimizeStyles::sanitize_css( $additional );

        // Only if keystatus is OK and option to add CCSS for logged on users is on or user is not logged in.
        if ( $loggedin || ! is_user_logged_in() ) {
            // Check for a valid CriticalCSS based on path to return its contents.
            $req_path = strtok( $_SERVER['REQUEST_URI'], '?' );
            if ( ! empty( $rules['paths'] ) ) {
                foreach ( $rules['paths'] as $path => $rule ) {
                    // explicit match OR partial match if MANUAL rule.
                    if ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && ( $req_path == $path || urldecode( $req_path ) == $path || ( apply_filters( 'autoptimize_filter_ccss_core_path_partial_match', true ) && false == $rule['hash'] && false != $rule['file'] && strpos( $req_path, str_replace( site_url(), '', $path ) ) !== false ) ) ) {
                        if ( file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
                            $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
                            if ( 'none' != $_ccss_contents ) {
                                if ( $debug ) {
                                    $_ccss_contents = '/* PATH: ' . $path . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
                                }
                                return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
                            } else {
                                if ( $debug ) {
                                    $this->criticalcss->log( 'Path based rule with value "none" found.', 3 );
                                }
                                $no_ccss = 'none';
                            }
                        }
                    }
                }
            }

            // Check for a valid CriticalCSS based on conditional tags to return its contents.
            if ( ! empty( $rules['types'] ) && 'none' !== $no_ccss ) {
                // order types-rules by the order of the original $ao_ccss_types array so as not to depend on the order in which rules were added.
                $rules['types'] = array_replace( array_intersect_key( array_flip( $this->_types ), $rules['types'] ), $rules['types'] );
                $is_front_page  = is_front_page();

                foreach ( $rules['types'] as $type => $rule ) {
                    if ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && in_array( $type, $this->_types ) && file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
                        $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
                        if ( $is_front_page && 'is_front_page' == $type ) {
                            if ( 'none' != $_ccss_contents ) {
                                if ( $debug ) {
                                    $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
                                }
                                return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
                            } else {
                                if ( $debug ) {
                                    $this->criticalcss->log( 'Conditional rule for is_front_page with value "none" found.', 3 );
                                }
                                $no_ccss = 'none';
                            }
                        } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && strpos( $type, 'custom_post_' ) === 0 && ! $is_front_page ) {
                            if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
                                if ( 'none' != $_ccss_contents ) {
                                    if ( $debug ) {
                                        $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
                                    }
                                    return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
                                } else {
                                    if ( $debug ) {
                                        $this->criticalcss->log( 'Conditional rule custom_post with value "none" found.', 3 );
                                    }
                                    $no_ccss = 'none';
                                }
                            }
                        } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && 0 === strpos( $type, 'template_' ) && ! $is_front_page ) {
                            if ( is_page_template( substr( $type, 9 ) ) ) {
                                if ( 'none' != $_ccss_contents ) {
                                    if ( $debug ) {
                                        $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
                                    }
                                    return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
                                } else {
                                    if ( $debug ) {
                                        $this->criticalcss->log( 'Conditional rule for template with value "none" found.', 3 );
                                    }
                                    $no_ccss = 'none';
                                }
                            }
                        } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && ! $is_front_page ) {
                            // all "normal" conditional tags, core + woo + buddypress + edd + bbpress
                            // but we have to remove the prefix for the non-core ones for them to function.
                            $type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
                            if ( function_exists( $type ) && call_user_func( $type ) ) {
                                if ( 'none' != $_ccss_contents ) {
                                    if ( $debug ) {
                                        $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
                                    }
                                    return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
                                } else {
                                    if ( $debug ) {
                                        $this->criticalcss->log( 'Conditional rule for ' . $type . ' with value "none" found.', 3 );
                                    }
                                    $no_ccss = 'none';
                                }
                            }
                        }
                    }
                }
            }
        }

        // Finally, inline the default CriticalCSS if any or else the entire CSS for the page
        // This also applies to logged in users if the option to add CCSS for logged in users has been disabled.
        if ( ! empty( $inlined ) && 'none' !== $no_ccss ) {
            if ( $debug ) {
                $this->criticalcss->log( 'Using default "above the fold" CSS.', 3 );
            }
            return apply_filters( 'autoptimize_filter_ccss_core_ccss', $inlined . $additional );
        } else {
            if ( $debug ) {
                $this->criticalcss->log( 'No matching CCSS found, switching to inlining full CSS.', 3 );
            }
            add_filter( 'autoptimize_filter_css_inline', '__return_true' );
            return;
        }
    }

    public function ao_ccss_defer_jquery( $in ) {
        $loggedin = $this->criticalcss->get_option( 'loggedin' );

        // defer all linked and inline JS.
        if ( ( ! is_user_logged_in() || $loggedin ) && preg_match_all( '#<script.*>(.*)</script>#Usmi', $in, $matches, PREG_SET_ORDER ) ) {
            foreach ( $matches as $match ) {
                if ( str_replace( apply_filters( 'autoptimize_filter_ccss_core_defer_exclude', array( 'data-noptimize="1"', 'data-cfasync="false"', 'data-pagespeed-no-defer' ) ), '', $match[0] ) !== $match[0] ) {
                    // do not touch JS with noptimize/ cfasync/ pagespeed-no-defer flags.
                    continue;
                } elseif ( '' !== $match[1] && ( ! preg_match( '/<script.* type\s?=.*>/', $match[0] ) || preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $match[0] ) ) ) {
                    // base64-encode and defer all inline JS.
                    $base64_js = '<script defer src="data:text/javascript;base64,' . base64_encode( $match[1] ) . '"></script>';
                    $in        = str_replace( $match[0], $base64_js, $in );
                } elseif ( str_replace( array( ' defer', ' async' ), '', $match[0] ) === $match[0] ) {
                    // and defer linked JS unless already deferred or asynced.
                    $new_match = str_replace( '<script ', '<script defer ', $match[0] );
                    $in        = str_replace( $match[0], $new_match, $in );
                }
            }
        }
        return $in;
    }

    public function ao_ccss_unloadccss( $html_in ) {
        // set media attrib of inline CCSS to none at onLoad to avoid it impacting full CSS (rarely needed).
        $_unloadccss_js = apply_filters( 'autoptimize_filter_ccss_core_unloadccss_js', '<script>window.addEventListener("load", function(event) {var el = document.getElementById("aoatfcss"); if(el) el.media = "none";})</script>' );

        if ( false !== strpos( $html_in, $_unloadccss_js . '</body>' ) ) {
            return $html_in;
        }

        return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
    }

    /**
     * Get the types array.
     *
     * @return array|null
     */
    public function get_types() {
        return $this->_types;
    }

    public function ao_ccss_extend_types() {
        // Extend contidional tags
        // Attach the conditional tags array.

        // in some cases $ao_ccss_types is empty and/or not an array, this should work around that problem.
        if ( empty( $this->_types ) || ! is_array( $this->_types ) ) {
            $this->_types = $this->get_ao_ccss_core_types();
            $this->ao_ccss_log( 'Empty types array in extend, refetching array with core conditionals.', 3 );
        }

        // Custom Post Types.
        $cpts = get_post_types(
            array(
                'public'   => true,
                '_builtin' => false,
            ),
            'names',
            'and'
        );
        foreach ( $cpts as $cpt ) {
            array_unshift( $this->_types, 'custom_post_' . $cpt );
        }

        // Templates.
        // Transient cache to avoid frequent disk reads.
        $templates = get_transient( 'autoptimize_ccss_page_templates' );
        if ( ! $templates ) {
            $templates = wp_get_theme()->get_page_templates();
            set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
        }
        foreach ( $templates as $tplfile => $tplname ) {
            array_unshift( $this->_types, 'template_' . $tplfile );
        }

        // bbPress tags.
        if ( function_exists( 'is_bbpress' ) ) {
            $this->_types = array_merge(
                array(
                    'bbp_is_bbpress',
                    'bbp_is_favorites',
                    'bbp_is_forum_archive',
                    'bbp_is_replies_created',
                    'bbp_is_reply_edit',
                    'bbp_is_reply_move',
                    'bbp_is_search',
                    'bbp_is_search_results',
                    'bbp_is_single_forum',
                    'bbp_is_single_reply',
                    'bbp_is_single_topic',
                    'bbp_is_single_user',
                    'bbp_is_single_user_edit',
                    'bbp_is_single_view',
                    'bbp_is_subscriptions',
                    'bbp_is_topic_archive',
                    'bbp_is_topic_edit',
                    'bbp_is_topic_merge',
                    'bbp_is_topic_split',
                    'bbp_is_topic_tag',
                    'bbp_is_topic_tag_edit',
                    'bbp_is_topics_created',
                    'bbp_is_user_home',
                    'bbp_is_user_home_edit',
                ),
                $this->_types
            );
        }

        // BuddyPress tags.
        if ( function_exists( 'is_buddypress' ) ) {
            $this->_types = array_merge(
                array(
                    'bp_is_activation_page',
                    'bp_is_activity',
                    'bp_is_blogs',
                    'bp_is_buddypress',
                    'bp_is_change_avatar',
                    'bp_is_create_blog',
                    'bp_is_friend_requests',
                    'bp_is_friends',
                    'bp_is_friends_activity',
                    'bp_is_friends_screen',
                    'bp_is_group_admin_page',
                    'bp_is_group_create',
                    'bp_is_group_forum',
                    'bp_is_group_forum_topic',
                    'bp_is_group_home',
                    'bp_is_group_invites',
                    'bp_is_group_leave',
                    'bp_is_group_members',
                    'bp_is_group_single',
                    'bp_is_groups',
                    'bp_is_messages',
                    'bp_is_messages_compose_screen',
                    'bp_is_messages_conversation',
                    'bp_is_messages_inbox',
                    'bp_is_messages_sentbox',
                    'bp_is_my_activity',
                    'bp_is_my_blogs',
                    'bp_is_notices',
                    'bp_is_profile_edit',
                    'bp_is_register_page',
                    'bp_is_settings_component',
                    'bp_is_user',
                    'bp_is_user_profile',
                    'bp_is_wire',
                ),
                $this->_types
            );
        }

        // Easy Digital Downloads (EDD) tags.
        if ( function_exists( 'edd_is_checkout' ) ) {
            $this->_types = array_merge(
                array(
                    'edd_is_checkout',
                    'edd_is_failed_transaction_page',
                    'edd_is_purchase_history_page',
                    'edd_is_success_page',
                ),
                $this->_types
            );
        }

        // WooCommerce tags.
        if ( class_exists( 'WooCommerce' ) ) {
            $this->_types = array_merge(
                array(
                    'woo_is_account_page',
                    'woo_is_cart',
                    'woo_is_checkout',
                    'woo_is_product',
                    'woo_is_product_category',
                    'woo_is_product_tag',
                    'woo_is_shop',
                    'woo_is_wc_endpoint_url',
                    'woo_is_woocommerce',
                ),
                $this->_types
            );
        }
    }

    public function get_ao_ccss_core_types() {
        return array(
            'is_404',
            'is_front_page',
            'is_home',
            'is_page',
            'is_single',
            'is_category',
            'is_author',
            'is_archive',
            'is_search',
            'is_attachment',
            'is_sticky',
            'is_paged',
        );
    }

    public function ao_ccss_key_status( $render ) {
        // Provide key status
        // Get key and key status.
        $key = $this->criticalcss->get_option( 'key' );
        $key_status = $this->criticalcss->get_option( 'keyst' );

        // Prepare returned variables.
        $key_return = array();
        $status     = false;

        if ( $key && 2 == $key_status ) {
            // Key exists and its status is valid.
            // Set valid key status.
            $status     = 'valid';
            $status_msg = esc_html__( 'Valid' );
            $color      = '#46b450'; // Green.
            $message    = null;
        } elseif ( $key && 1 == $key_status ) {
            // Key exists but its validation has failed.
            // Set invalid key status.
            $status     = 'invalid';
            $status_msg = esc_html__( 'Invalid' );
            $color      = '#dc3232'; // Red.
            // Translators: link to criticalcss.com page.
            $message    = sprintf( esc_html__( 'Your API key is invalid. Please enter a valid %1$scriticalcss.com%2$s key.', 'autoptimize' ), '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>' );
        } elseif ( $key && ! $key_status ) {
            // Key exists but it has no valid status yet
            // Perform key validation.
            $key_check = $this->ao_ccss_key_validation( $key );

            // Key is valid, set valid status.
            if ( $key_check ) {
                $status     = 'valid';
                $status_msg = esc_html__( 'Valid' );
                $color      = '#46b450'; // Green.
                $message    = null;
            } else {
                // Key is invalid, set invalid status.
                $status     = 'invalid';
                $status_msg = esc_html__( 'Invalid' );
                $color      = '#dc3232'; // Red.
                if ( get_option( 'autoptimize_ccss_keyst' ) == 1 ) {
                    // Translators: link to criticalcss.com page.
                    $message = sprintf( esc_html__( 'Your API key is invalid. Please enter a valid %1$scriticalcss.com%2$s key.', 'autoptimize' ), '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>' );
                } else {
                    $message = esc_html__( 'Something went wrong when checking your API key, make sure you server can communicate with https://criticalcss.com and/ or try again later.', 'autoptimize' );
                }
            }
        } else {
            // No key nor status
            // Set no key status.
            $status     = 'nokey';
            $status_msg = esc_html__( 'None' );
            $color      = '#ffb900'; // Yellow.
            // Translators: link to criticalcss.com page.
            $message    = sprintf( esc_html__( 'Please enter a valid %1$scriticalcss.com%2$s API key to start.', 'autoptimize' ), '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>' );
        }

        // Fill returned values.
        $key_return['status'] = $status;
        // Provide rendering information if required.
        if ( $render ) {
            $key_return['stmsg'] = $status_msg;
            $key_return['color'] = $color;
            $key_return['msg']   = $message;
        }

        // Return key status.
        return $key_return;
    }

    public function ao_ccss_key_validation( $key ) {
        $noptimize = $this->criticalcss->get_option( 'noptimize' );

        // POST a dummy job to criticalcss.com to check for key validation
        // Prepare home URL for the request.
        $src_url = get_home_url();

        // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
        if ( ! empty( $noptimize ) ) {
            $src_url .= '/?ao_noptirocket=1';
        } elseif ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) {
            $src_url .= '/?ao_nolazy=1';    
        }

        $src_url = apply_filters( 'autoptimize_filter_ccss_cron_srcurl', $src_url );

        if ( true !== autoptimizeUtils::is_local_server( parse_url( $src_url,  PHP_URL_HOST ) ) ) {
            // Prepare the request.
            $url  = esc_url_raw( AO_CCSS_API . 'generate' );
            $args = array(
                'headers' => apply_filters(
                    'autoptimize_ccss_cron_api_generate_headers',
                    array(
                        'User-Agent'    => 'Autoptimize v' . AO_CCSS_VER,
                        'Content-type'  => 'application/json; charset=utf-8',
                        'Authorization' => 'JWT ' . $key,
                        'Connection'    => 'close',
                    )
                ),
                // Body must be JSON.
                'body'    => json_encode(
                    apply_filters(
                        'autoptimize_ccss_cron_api_generate_body',
                        array(
                            'url'    => $src_url,
                            'aff'    => 1,
                            'aocssv' => AO_CCSS_VER,
                        )
                    ),
                    JSON_UNESCAPED_SLASHES
                ),
            );

            // Dispatch the request and store its response code.
            $req  = wp_safe_remote_post( $url, $args );
            $code = wp_remote_retrieve_response_code( $req );
            $body = json_decode( wp_remote_retrieve_body( $req ), true );

            if ( 200 == $code ) {
                // Response is OK.
                // Set key status as valid and log key check.
                update_option( 'autoptimize_ccss_keyst', 2 );
                $this->ao_ccss_log( 'criticalcss.com: API key is valid, updating key status', 3 );

                // extract job-id from $body and put it in the queue as a P job
                // but only if no jobs and no rules!
                $queue = $this->criticalcss->get_option( 'queue' );
                $rules = $this->criticalcss->get_option( 'rules' );

                if ( 0 == count( $queue ) && 0 == count( $rules['types'] ) && 0 == count( $rules['paths'] ) ) {
                    if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] ) {
                        $jprops['ljid']     = 'firstrun';
                        $jprops['rtarget']  = 'types|is_front_page';
                        $jprops['ptype']    = 'is_front_page';
                        $jprops['hashes'][] = 'dummyhash';
                        $jprops['hash']     = 'dummyhash';
                        $jprops['file']     = null;
                        $jprops['jid']      = $body['job']['id'];
                        $jprops['jqstat']   = $body['job']['status'];
                        $jprops['jrstat']   = null;
                        $jprops['jvstat']   = null;
                        $jprops['jctime']   = microtime( true );
                        $jprops['jftime']   = null;
                        $queue['/'] = $jprops;
                        $queue_raw  = json_encode( $queue );
                        update_option( 'autoptimize_ccss_queue', $queue_raw, false );
                        $this->ao_ccss_log( 'Created P job for is_front_page based on API key check response.', 3 );
                    }
                }
                return true;
            } elseif ( 401 == $code ) {
                // Response is unauthorized
                // Set key status as invalid and log key check.
                update_option( 'autoptimize_ccss_keyst', 1 );
                $this->ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
                return false;
            } else {
                // Response unkown
                // Log key check attempt.
                $this->ao_ccss_log( 'criticalcss.com: could not check API key status, this is a service error, body follows if any...', 2 );
                if ( ! empty( $body ) ) {
                    $this->ao_ccss_log( print_r( $body, true ), 2 );
                }
                if ( is_wp_error( $req ) ) {
                    $this->ao_ccss_log( $req->get_error_message(), 2 );
                }
                return false;
            }
        } else {
            // localhost/ private network server, no API check possible.
            return false;
        }
    }

    public function ao_ccss_viewport() {
        // Get viewport size
        // Attach viewport option.
        $viewport = $this->criticalcss->get_option( 'viewport' );

        return array(
            'w' => ! empty( $viewport['w'] ) ? $viewport['w'] : '',
            'h' => ! empty( $viewport['h'] ) ? $viewport['h'] : '',
        );
    }

    public function ao_ccss_check_contents( $ccss ) {
        // Perform basic exploit avoidance and CSS validation.
        if ( ! empty( $ccss ) ) {
            // Try to avoid code injection.
            $blocklist = array( '#!/', 'function(', '<script', '<?php', '</style', ' onload=', ' onerror=', ' onmouse', ' onscroll=', ' onclick=' );
            foreach ( $blocklist as $blocklisted ) {
                if ( stripos( $ccss, $blocklisted ) !== false ) {
                    $this->ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
                    return false;
                }
            }

            // Check for most basics CSS structures.
            $needlist = array( '{', '}', ':' );
            foreach ( $needlist as $needed ) {
                if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
                    $this->ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
                    return false;
                }
            }
        }

        // Return true if file critical CSS is sane.
        return true;
    }

    public function ao_ccss_log( $msg, $lvl ) {
        // Commom logging facility
        // Attach debug option.
        $debug = $this->criticalcss->get_option( 'debug' );

        // Prepare log levels, where accepted $lvl are:
        // 1: II (for info)
        // 2: EE (for error)
        // 3: DD (for debug)
        // Default: UU (for unkown).
        $level = false;
        if ( $debug ) {
            switch ( $lvl ) {
                case 1:
                    $level = 'II';
                    break;
                case 2:
                    $level = 'EE';
                    break;
                case 3:
                    $level = 'DD';
                    break;
                default:
                    $level = 'UU';
            }
        }

        // Prepare and write a log message if there's a valid level.
        if ( $level ) {

            // Prepare message.
            $message = date( 'c' ) . ' - [' . $level . '] ' . htmlentities( $msg ) . '<br>'; // @codingStandardsIgnoreLine

            // Write message to log file.
            error_log( $message, 3, AO_CCSS_LOG );
        }
    }

    public function ao_ccss_clear_page_tpl_cache() {
        // Clears transient cache for page templates.
        delete_transient( 'autoptimize_ccss_page_templates' );
    }
}
PK.�1\ccCۋ>�>!autoptimizeCriticalCSSEnqueue.phpnu�[���<?php
/**
 * Critical CSS job enqueue logic.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCriticalCSSEnqueue {
    /**
     * Critical CSS object.
     *
     * @var object
     */
    protected $criticalcss;

    public function __construct() {
        $this->criticalcss = autoptimize()->criticalcss();
    }

    public function ao_ccss_enqueue( $hash = '', $path = '', $type = 'is_page' ) {
        // Get key status.
        $key = $this->criticalcss->key_status( false );

        // Queue is available to anyone...
        $enqueue = true;

        // ... which are not the ones below.
        if ( true === autoptimizeUtils::is_local_server() ) {
            $enqueue = false;
            $this->criticalcss->log( 'cant enqueue as local/ private', 3 );
        } elseif ( 'nokey' == $key['status'] || 'invalid' == $key['status'] ) {
            $enqueue = false;
            $this->criticalcss->log( 'Job queuing is not available: no valid API key found.', 3 );
        } elseif ( ! empty( $hash ) && ( is_user_logged_in() || is_feed() || is_404() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || $this->ao_ccss_ua() || false === apply_filters( 'autoptimize_filter_ccss_enqueue_should_enqueue', true ) ) ) {
            $enqueue = false;
            $this->criticalcss->log( 'Job queuing is not available for WordPress\'s logged in users, feeds, error pages, ajax calls or calls from criticalcss.com itself.', 3 );
        } elseif ( empty( $hash ) && empty( $path ) || ( ( 'is_single' !== $type ) && ( 'is_page' !== $type ) ) ) {
            $enqueue = false;
            $this->criticalcss->log( 'Forced job queuing failed, no path or not right type', 3 );
        }

        if ( ! $enqueue ) {
            return;
        }

        // Continue if queue is available
        // Attach required arrays/ vars.
        $rules = $this->criticalcss->get_option( 'rules' );
        $queue_raw = $this->criticalcss->get_option( 'queue_raw' );
        $queue = $this->criticalcss->get_option( 'queue' );
        $forcepath = $this->criticalcss->get_option( 'forcepath' );

        // Get request path and page type, and initialize the queue update flag.
        if ( ! empty( $hash ) ) {
            $req_orig = $_SERVER['REQUEST_URI'];
            $req_type = $this->ao_ccss_get_type();
        } elseif ( ! empty( $path ) ) {
            $req_orig = $path;
            if ( '/' === $path ) {
                $req_type = 'is_front_page';
            } else {
                $req_type = $type;
            }
        }
        $req_path = strtok( $req_orig, '?' );

        // now that we really have the path, check if there's no garbage in there (due to some themes serving a non 404 page even if the resource does not exist resulting in all sorts of nonsense rules).
        if ( true === apply_filters( 'autoptimize_filter_ccss_enqueue_block_garbage' , true ) && str_ireplace( apply_filters( 'autoptimize_filter_ccss_enqueue_blocklist', array( '.php', 'data:text/javascript;base64', '/.', '/null', '.jpeg', '.jpg', '.png' ) ), '', $req_path ) !== $req_path ) {
            $this->criticalcss->log( 'Job not enqueued looks like the path is just garbage; ' . $req_path, 3 );
            return;
        }

        // Check if we have a lang param. we need to keep as WPML can switch languages based on that
        // and that includes RTL -> LTR so diff. structure, so rules would be RTL vs LTR
        // but this needs changes in the structure of the rule object so off by default for now
        // as now this will simply result in conditional rules being overwritten.
        if ( apply_filters( 'autoptimize_filter_ccss_coreenqueue_honor_lang', false ) && strpos( $req_orig, 'lang=' ) !== false ) {
            $req_params = strtok( '?' );
            parse_str( $req_params, $req_params_arr );
            if ( array_key_exists( 'lang', $req_params_arr ) && ! empty( $req_params_arr['lang'] ) ) {
                $req_path .= '?lang=' . $req_params_arr['lang'];
            }
        }

        $job_qualify     = false;
        $target_rule     = false;
        $rule_properties = false;
        $queue_update    = false;

        // Match for paths in rules.
        foreach ( $rules['paths'] as $path => $props ) {

            // Prepare rule target and log.
            $target_rule = 'paths|' . $path;
            $this->criticalcss->log( 'Qualifying path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );

            // Path match
            // -> exact match needed for AUTO rules
            // -> partial match OK for MANUAL rules (which have empty hash and a file with CCSS).
            if ( $path === $req_path || ( false == $props['hash'] && false != $props['file'] && preg_match( '|' . $path . '|', $req_path ) ) ) {

                // There's a path match in the rule, so job QUALIFIES with a path rule match.
                $job_qualify     = true;
                $rule_properties = $props;
                $this->criticalcss->log( 'Path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );

                // Stop processing other path rules.
                break;
            }
        }

        // Match for types in rules if no path rule matches and if we're not enforcing paths.
        if ( '' !== $hash && ! $job_qualify && ( ! $forcepath || ! in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) || ! apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) ) ) {
            foreach ( $rules['types'] as $type => $props ) {

                // Prepare rule target and log.
                $target_rule = 'types|' . $type;
                $this->criticalcss->log( 'Qualifying page type <' . $req_type . '> on path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );

                if ( $req_type == $type ) {
                    // Type match.
                    // There's a type match in the rule, so job QUALIFIES with a type rule match.
                    $job_qualify     = true;
                    $rule_properties = $props;
                    $this->criticalcss->log( 'Page type <' . $req_type . '> on path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );

                    // Stop processing other type rules.
                    break;
                }
            }
        }

        if ( $job_qualify && ( ( false == $rule_properties['hash'] && false != $rule_properties['file'] ) || strpos( $req_type, 'template_' ) !== false ) ) {
            // If job qualifies but rule hash is false and file isn't false (MANUAL rule) or if template, job does not qualify despite what previous evaluations says.
            $job_qualify = false;
            $this->criticalcss->log( 'Job submission DISQUALIFIED by MANUAL rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
        } elseif ( ! $job_qualify && empty( $rule_properties ) ) {
            // But if job does not qualify and rule properties are set, job qualifies as there is no matching rule for it yet
            // Fill-in the new target rule.
            $job_qualify = true;

            // Should we switch to path-base AUTO-rules? Conditions:
            // 1. forcepath option has to be enabled (off by default)
            // 2. request type should be (by default, but filterable) one of is_page (removed for now: woo_is_product or woo_is_product_category).
            if ( ( $forcepath && in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) ) || apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) || empty( $hash ) ) {
                if ( '/' !== $req_path ) {
                    $target_rule = 'paths|' . $req_path;
                } else {
                    // Exception; we don't want a path-based rule for "/" as that messes things up, hard-switch this to a type-based is_front_page rule.
                    $target_rule = 'types|' . 'is_front_page';
                }
            } else {
                $target_rule = 'types|' . $req_type;
            }
            $this->criticalcss->log( 'Job submission QUALIFIED by MISSING rule for page type <' . $req_type . '> on path <' . $req_path . '>, new rule target is <' . $target_rule . '>', 3 );
        } else {
            // Or just log a job qualified by a matching rule.
            $this->criticalcss->log( 'Job submission QUALIFIED by AUTO rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
        }

        // Submit job.
        if ( $job_qualify ) {
            if ( ! array_key_exists( $req_path, $queue ) ) {
                // This is a NEW job
                // Merge job into the queue.
                $queue[ $req_path ] = $this->ao_ccss_define_job(
                    $req_path,
                    $target_rule,
                    $req_type,
                    $hash,
                    null,
                    null,
                    null,
                    null,
                    true
                );
                // Set update flag.
                $queue_update = true;
            } else {
                // This is an existing job
                // The job is still NEW, most likely this is extra CSS file for the same page that needs a hash.
                if ( 'NEW' == $queue[ $req_path ]['jqstat'] ) {
                    // Add hash if it's not already in the job.
                    if ( ! in_array( $hash, $queue[ $req_path ]['hashes'] ) ) {
                        // Push new hash to its array and update flag.
                        $queue_update = array_push( $queue[ $req_path ]['hashes'], $hash );

                        // Log job update.
                        $this->criticalcss->log( 'Hashes UPDATED on local job id <' . $queue[ $req_path ]['ljid'] . '>, job status NEW, target rule <' . $queue[ $req_path ]['rtarget'] . '>, hash added: ' . $hash, 3 );

                        // Return from here as the hash array is already updated.
                        return true;
                    }
                } elseif ( 'NEW' != $queue[ $req_path ]['jqstat'] && 'JOB_QUEUED' != $queue[ $req_path ]['jqstat'] && 'JOB_ONGOING' != $queue[ $req_path ]['jqstat'] ) {
                    // Allow requeuing jobs that are not NEW, JOB_QUEUED or JOB_ONGOING
                    // Merge new job keeping some previous job values.
                    $queue[ $req_path ] = $this->ao_ccss_define_job(
                        $req_path,
                        $target_rule,
                        $req_type,
                        $hash,
                        $queue[ $req_path ]['file'],
                        $queue[ $req_path ]['jid'],
                        $queue[ $req_path ]['jrstat'],
                        $queue[ $req_path ]['jvstat'],
                        false
                    );
                    // Set update flag.
                    $queue_update = true;
                }
            }

            if ( $queue_update ) {
                // Persist the job to the queue and return.
                $queue_raw = json_encode( $queue );
                update_option( 'autoptimize_ccss_queue', $queue_raw, false );
                $this->criticalcss->flush_options();
                return true;
            } else {
                // Or just return false if no job was added.
                $this->criticalcss->log( 'A job for path <' . $req_path . '> already exist with NEW or PENDING status, skipping job creation', 3 );
                return false;
            }
        }
    }

    public function ao_ccss_get_type() {
        // Get the type of a page
        // Attach the conditional tags array.
        $types = $this->criticalcss->get_types();
        $forcepath = $this->criticalcss->get_option( 'forcepath' );

        // By default, a page type is false.
        $page_type = false;

        // Iterates over the array to match a type.
        foreach ( $types as $type ) {
            if ( is_404() ) {
                $page_type = 'is_404';
                break;
            } elseif ( is_front_page() ) {
                // identify frontpage immediately to avoid it also matching a CPT or template.
                $page_type = 'is_front_page';
                break;
            } elseif ( strpos( $type, 'custom_post_' ) !== false && ( ! $forcepath || ! is_page() ) && is_singular() )  {
                // Match custom post types and not page or page not forced to path-based.
                if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
                    $page_type = $type;
                    break;
                }
            } elseif ( strpos( $type, 'template_' ) !== false && ( ! $forcepath || ! is_page() ) ) {
                // Match templates if not page or if page is not forced to path-based.
                if ( is_page_template( substr( $type, 9 ) ) ) {
                    $page_type = $type;
                    break;
                }
            } else {
                // Match all other existing types
                // but remove prefix to be able to check if the function exists & returns true.
                $_type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
                if ( function_exists( $_type ) && call_user_func( $_type ) ) {
                    // Make sure we only return for one page, not for the "paged pages" (/page/2 ..).
                    if ( ! is_page() || ! is_paged() ) {
                        $page_type = $type;
                        break;
                    }
                }
            }
        }

        // Return the page type.
        return $page_type;
    }

    public function ao_ccss_define_job( $path, $target, $type, $hash, $file, $jid, $jrstat, $jvstat, $create ) {
        // Define a job entry to be created or updated
        // Define commom job properties.
        $path            = array();
        $path['ljid']    = $this->ao_ccss_job_id();
        $path['rtarget'] = $target;
        $path['ptype']   = $type;
        $path['hashes']  = array( $hash );
        $path['hash']    = $hash;
        $path['file']    = $file;
        $path['jid']     = $jid;
        $path['jqstat']  = 'NEW';
        $path['jrstat']  = $jrstat;
        $path['jvstat']  = $jvstat;
        $path['jctime']  = microtime( true );
        $path['jftime']  = null;

        // Set operation requested.
        if ( $create ) {
            $operation = 'CREATED';
        } else {
            $operation = 'UPDATED';
        }

        // Log job creation.
        $this->criticalcss->log( 'Job ' . $operation . ' with local job id <' . $path['ljid'] . '> for target rule <' . $target . '>', 3 );

        return $path;
    }

    public function ao_ccss_job_id( $length = 6 ) {
        // Generate random strings for the local job ID
        // Based on https://stackoverflow.com/a/4356295 .
        $characters        = '0123456789abcdefghijklmnopqrstuvwxyz';
        $characters_length = strlen( $characters );
        $random_string     = 'j-';
        for ( $i = 0; $i < $length; $i++ ) {
            $random_string .= $characters[ rand( 0, $characters_length - 1 ) ];
        }
        return $random_string;
    }

    public function ao_ccss_ua() {
        // Check for criticalcss.com user agent.
        $agent = '';
        if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
            $agent = $_SERVER['HTTP_USER_AGENT'];
        }

        // Check for UA and return TRUE when criticalcss.com is the detected UA, false when not.
        $rtn = strpos( $agent, AO_CCSS_URL );
        if ( 0 === $rtn ) {
            $rtn = true;
        } else {
            $rtn = false;
        }
        return ( $rtn );
    }
}
PK.�1\DE/����autoptimizeStyles.phpnu�[���<?php
/**
 * Class for CSS optimization.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeStyles extends autoptimizeBase
{
    const ASSETS_REGEX = '/url\s*\(\s*(?!["\']?data:)(?![\'|\"]?[\#|\%|])([^)]+)\s*\)([^;},\s]*)/i';

    /**
     * Font-face regex-fu from HamZa at: https://stackoverflow.com/a/21395083
     */
    const FONT_FACE_REGEX = '~@font-face\s*(\{(?:[^{}]+|(?1))*\})~xsi'; // added `i` flag for case-insensitivity.

    /**
     * Store CSS.
     *
     * @var array
     */
    private $css = array();

    /**
     * To store CSS code
     *
     * @var array
     */
    private $csscode = array();

    /**
     * To store urls
     *
     * @var array
     */
    private $url = array();

    /**
     * String to store rest of content (when old setting "only in head" is used)
     *
     * @var string
     */
    private $restofcontent = '';

    /**
     * Setting to change small images to inline CSS
     *
     * @var bool
     */
    private $datauris = false;

    /**
     * Array to store hashmap
     *
     * @var array
     */
    private $hashmap = array();

    /**
     * Flag to indicate if CSS is already minified
     *
     * @var bool
     */
    private $alreadyminified = false;

    /**
     * Setting if CSS should be aggregated
     *
     * @var bool
     */
    private $aggregate = true;

    /**
     * Setting if all CSS should be inlined
     *
     * @var bool
     */
    private $inline = false;

    /**
     * Setting if CSS should be deferred
     *
     * @var bool
     */
    private $defer = false;

    /**
     * Setting for to be inlined CSS.
     *
     * @var string
     */
    private $defer_inline = '';

    /**
     * Setting for allowlist of what should be aggregated.
     *
     * @var string
     */
    private $allowlist = '';

    /**
     * Setting (only filter) for size under which CSS should be inlined instead of linked.
     *
     * @var string
     */
    private $cssinlinesize = '';

    /**
     * Setting (only filter) of CSS that can be removed.
     *
     * @var array
     */
    private $cssremovables = array();

    /**
     * Setting: should inline CSS be aggregated.
     *
     * @var bool
     */
    private $include_inline = false;

    /**
     * Setting (only filter) if minified CSS can be injected after minificatoin of aggregated CSS.
     *
     * @var bool
     */
    private $inject_min_late = true;

    /**
     * Holds all exclusions.
     *
     * @var array
     */
    private $dontmove = array();

    /**
     * Holds all options.
     *
     * @var array
     */
    private $options = array();

    /**
     * Setting; should excluded CSS-files be minified.
     *
     * @var bool
     */
    private $minify_excluded = true;

    /**
     * Setting (filter only); should all media-attributes be forced to "all".
     *
     * @var bool
     */
    private $media_force_all = false;

    /**
     * Reads the page and collects style tags.
     *
     * @param array $options all options.
     */
    public function read( $options )
    {
        $noptimize_css = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content );
        if ( $noptimize_css || false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_css_optimize' ) ) {
            return false;
        }

        $allowlist_css = apply_filters( 'autoptimize_filter_css_allowlist', '', $this->content );
        if ( ! empty( $allowlist_css ) ) {
            $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_css ) ) );
        }

        $removable_css = apply_filters( 'autoptimize_filter_css_removables', '' );
        if ( ! empty( $removable_css ) ) {
            $this->cssremovables = array_filter( array_map( 'trim', explode( ',', $removable_css ) ) );
        }

        $this->cssinlinesize = apply_filters( 'autoptimize_filter_css_inlinesize', 256 );

        // filter to "late inject minified CSS", default to true for now (it is faster).
        $this->inject_min_late = apply_filters( 'autoptimize_filter_css_inject_min_late', true );

        // Remove everything that's not the header.
        if ( apply_filters( 'autoptimize_filter_css_justhead', $options['justhead'] ) ) {
            $content             = explode( '</head>', $this->content, 2 );
            $this->content       = $content[0] . '</head>';
            $this->restofcontent = $content[1];
        }

        // Determine whether we're doing CSS-files aggregation or not.
        if ( isset( $options['aggregate'] ) && ! $options['aggregate'] ) {
            $this->aggregate = false;
        }
        // Returning true for "dontaggregate" turns off aggregation.
        if ( $this->aggregate && apply_filters( 'autoptimize_filter_css_dontaggregate', false ) ) {
            $this->aggregate = false;
        }
        // and the filter that should have been there to begin with.
        $this->aggregate = apply_filters( 'autoptimize_filter_css_aggregate', $this->aggregate );

        // include inline?
        if ( apply_filters( 'autoptimize_css_include_inline', $options['include_inline'] ) ) {
            $this->include_inline = true;
        }

        // List of CSS strings which are excluded from autoptimization.
        $exclude_css = apply_filters( 'autoptimize_filter_css_exclude', $options['css_exclude'], $this->content );
        if ( '' !== $exclude_css ) {
            $this->dontmove = array_filter( array_map( 'trim', explode( ',', $exclude_css ) ) );
        } else {
            $this->dontmove = array();
        }

        // forcefully exclude CSS with data-noptimize attrib.
        $this->dontmove[] = 'data-noptimize';

        // forcefully exclude inline CSS with ".wp-container-" which due to the random-ish nature busts AO's cache continuously.
        $this->dontmove[] = '.wp-container-';

        // Should we defer css?
        // value: true / false.
        $this->defer = $options['defer'];
        $this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer, $this->content );

        // If page/ post check post_meta to see if optimize is off.
        if ( $this->defer && false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_ccss' ) ) {
             $this->defer = false;
        }

        // Should we inline while deferring?
        // value: inlined CSS.
        $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $this->sanitize_css( $options['defer_inline'] ), $this->content );

        // Should we inline?
        // value: true / false.
        $this->inline = $options['inline'];
        $this->inline = apply_filters( 'autoptimize_filter_css_inline', $this->inline, $this->content );

        // Store cdn url.
        $this->cdn_url = $options['cdn_url'];

        // Store data: URIs setting for later use.
        $this->datauris = $options['datauris'];

        // Determine whether excluded files should be minified if not yet so.
        if ( ! $options['minify_excluded'] && $options['aggregate'] ) {
            $this->minify_excluded = false;
        }
        $this->minify_excluded = apply_filters( 'autoptimize_filter_css_minify_excluded', $this->minify_excluded, '' );

        // should we force all media-attributes to all?
        $this->media_force_all = apply_filters( 'autoptimize_filter_css_tagmedia_forceall', false );

        // noptimize me.
        $this->content = $this->hide_noptimize( $this->content );

        // Exclude (no)script, as those may contain CSS which should be left as is.
        $this->content = $this->replace_contents_with_marker_if_exists(
            'SCRIPT',
            '<script',
            '#<(?:no)?script.*?<\/(?:no)?script>#is',
            $this->content
        );

        // Save IE hacks.
        $this->content = $this->hide_iehacks( $this->content );

        // Hide HTML comments.
        $this->content = $this->hide_comments( $this->content );

        // Get <style> and <link>.
        if ( preg_match_all( '#(<style[^>]*>.*</style>)|(<link[^>]*stylesheet[^>]*>)#Usmi', $this->content, $matches ) ) {

            foreach ( $matches[0] as $tag ) {
                if ( $this->isremovable( $tag, $this->cssremovables ) ) {
                    $this->content = str_replace( $tag, '', $this->content );
                } elseif ( $this->ismovable( $tag ) ) {
                    // Get the media.
                    if ( false !== strpos( $tag, 'media=' ) ) {
                        preg_match( '#media=(?:"|\')([^>]*)(?:"|\')#Ui', $tag, $medias );
                        if ( ! empty( $medias ) ) {
                            $medias = explode( ',', $medias[1] );
                            $media  = array();
                            foreach ( $medias as $elem ) {
                                if ( empty( $elem ) ) {
                                    $elem = 'all';
                                }

                                $media[] = $elem;
                            }
                        } else {
                            $media = array( 'all' );
                        }
                    } else {
                        // No media specified - applies to all.
                        $media = array( 'all' );
                    }

                    // forcing media attribute to all to merge all in one file.
                    if ( $this->media_force_all ) {
                        $media = array( 'all' );
                    }

                    $media = apply_filters( 'autoptimize_filter_css_tagmedia', $media, $tag );

                    if ( preg_match( '#<link.*href=("|\')(.*)("|\')#Usmi', $tag, $source ) ) {
                        // <link>.
                        $url  = current( explode( '?', $source[2], 2 ) );
                        $path = $this->getpath( $url );

                        if ( false !== $path && preg_match( '#\.css$#', $path ) ) {
                            // Good link.
                            $this->css[] = array( $media, $path );
                        } else {
                            // Link is dynamic (.php etc).
                            $new_tag = $this->optionally_defer_excluded( $tag, 'none' );
                            if ( '' !== $new_tag && $new_tag !== $tag ) {
                                $this->content = str_replace( $tag, $new_tag, $this->content );
                            }
                            $tag = '';
                        }
                    } else {
                        // Inline css in style tags can be wrapped in comment tags, so restore comments.
                        $tag = $this->restore_comments( $tag );
                        preg_match( '#<style.*>(.*)</style>#Usmi', $tag, $code );

                        // And re-hide them to be able to to the removal based on tag.
                        $tag = $this->hide_comments( $tag );

                        if ( $this->include_inline ) {
                            $code        = preg_replace( '#^.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*$#sm', '$1', $code[1] );
                            $this->css[] = array( $media, 'INLINE;' . $code );
                        } else {
                            $tag = '';
                        }
                    }

                    // Remove the original style tag.
                    $this->content = str_replace( $tag, '', $this->content );
                } else {
                    if ( preg_match( '#<link.*href=("|\')(.*)("|\')#Usmi', $tag, $source ) ) {
                        $exploded_url = explode( '?', $source[2], 2 );
                        $url          = $exploded_url[0];
                        $path         = $this->getpath( $url );
                        $new_tag      = $tag;

                        // Excluded CSS, minify that file:
                        // -> if aggregate is on and exclude minify is on
                        // -> if aggregate is off and the file is not in dontmove.
                        if ( $path && $this->minify_excluded ) {
                            $consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false );
                            if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
                                $minified_url = $this->minify_single( $path );
                                if ( ! empty( $minified_url ) ) {
                                    // Replace orig URL with cached minified URL.
                                    $new_tag = str_replace( $url, $minified_url, $tag );
                                } elseif ( apply_filters( 'autoptimize_filter_ccsjs_remove_empty_minified_url', false ) ) {
                                    // Remove the original style tag, because cache content is empty but only if
                                    // filter is true-ed because $minified_url is also false if file is minified already.
                                    $new_tag = '';
                                }
                            }
                        }

                        if ( '' !== $new_tag ) {
                            // Optionally defer (preload) non-aggregated CSS.
                            $new_tag = $this->optionally_defer_excluded( $new_tag, $url );

                            // Check if we still need to CDN (esp. for already minified resources).
                            if ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) {
                                $new_tag = str_replace( $url, $this->url_replace_cdn( $url ), $new_tag );
                            }
                        }

                        // And replace!
                        if ( ( '' !== $new_tag && $new_tag !== $tag ) || ( '' === $new_tag && apply_filters( 'autoptimize_filter_css_remove_empty_files', false ) ) ) {
                            $this->content = str_replace( $tag, $new_tag, $this->content );
                        }
                    }
                }
            }
            return true;
        }

        // Really, no styles?
        return false;
    }

    /**
     * Checks if non-optimized CSS is to be preloaded and if so return
     * the tag with preload code.
     *
     * @param string $tag (required).
     * @param string $url (optional).
     *
     * @return string $new_tag
     */
    private function optionally_defer_excluded( $tag, $url = '' )
    {
        // Defer single CSS if "inline & defer" is ON and there is inline CSS.
        if ( ! empty( $tag ) && false === strpos( $tag, ' onload=' ) && $this->defer && ! empty( $this->defer_inline ) && apply_filters( 'autoptimize_filter_css_defer_excluded', true, $tag ) ) {
            // get media attribute and based on that create onload JS attribute value.
            if ( false === strpos( $tag, 'media=' ) ) {
                $tag = str_replace( '<link', "<link media='all'", $tag );
            }

            preg_match( '#media=(?:"|\')([^>]*)(?:"|\')#Ui', $tag, $_medias );
            $_media          = $_medias[1];
            $_preload_onload = autoptimizeConfig::get_ao_css_preload_onload( $_media );

            if ( 'print' !== $_media ) {
                // If not media=print, adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback.
                $new_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>' . str_replace(
                    $_medias[0],
                    "media='print' onload=\"" . $_preload_onload . '"',
                    $tag
                );

                // Optionally (but default false) preload the (excluded) CSS-file.
                if ( apply_filters( 'autoptimize_fitler_css_preload_and_print', false ) && 'none' !== $url ) {
                    $new_tag = '<link rel="preload" as="stylesheet" href="' . $url . '"/>' . $new_tag;
                }
            } else {
                $new_tag = $tag;
            }

            return $new_tag;
        }

        // Return unchanged $tag.
        return $tag;
    }

    /**
     * Checks if the local file referenced by $path is a valid
     * candidate for being inlined into a data: URI
     *
     * @param string $path image path.
     * @return boolean
     */
    private function is_datauri_candidate( $path )
    {
        // Call only once since it's called from a loop.
        static $max_size = null;
        if ( null === $max_size ) {
            $max_size = $this->get_datauri_maxsize();
        }

        if ( $path && preg_match( '#\.(jpe?g|png|gif|webp|bmp)$#i', $path ) &&
            file_exists( $path ) && is_readable( $path ) && filesize( $path ) <= $max_size ) {

            // Seems we have a candidate.
            $is_candidate = true;
        } else {
            // Filter allows overriding default decision (which checks for local file existence).
            $is_candidate = apply_filters( 'autoptimize_filter_css_is_datauri_candidate', false, $path );
        }

        return $is_candidate;
    }

    /**
     * Returns the amount of bytes that shouldn't be exceeded if a file is to
     * be inlined into a data: URI. Defaults to 4096, passed through
     * `autoptimize_filter_css_datauri_maxsize` filter.
     *
     * @return mixed
     */
    private function get_datauri_maxsize()
    {
        static $max_size = null;

        /**
         * No need to apply the filter multiple times in case the
         * method itself is invoked multiple times during a single request.
         * This prevents some wild stuff like having different maxsizes
         * for different files/site-sections etc. But if you're into that sort
         * of thing you're probably better of building assets completely
         * outside of WordPress anyway.
         */
        if ( null === $max_size ) {
            $max_size = (int) apply_filters( 'autoptimize_filter_css_datauri_maxsize', 4096 );
        }

        return $max_size;
    }

    private function check_datauri_exclude_list( $url )
    {
        static $exclude_list = null;
        static $no_datauris  = array();

        // Again, skip doing certain stuff repeatedly when loop-called.
        if ( null === $exclude_list ) {
            $exclude_list = apply_filters( 'autoptimize_filter_css_datauri_exclude', '' );
            $no_datauris  = array_filter( array_map( 'trim', explode( ',', $exclude_list ) ) );
        }

        $matched = false;

        if ( ! empty( $exclude_list ) ) {
            foreach ( $no_datauris as $no_datauri ) {
                if ( false !== strpos( $url, $no_datauri ) ) {
                    $matched = true;
                    break;
                }
            }
        }

        return $matched;
    }

    private function build_or_get_datauri_image( $path )
    {
        /**
         * TODO/FIXME: document the required return array format, or better yet,
         * use a string, since we don't really need an array for this. That would, however,
         * require changing even more code, which is not happening right now...
         */

        // Allows short-circuiting datauri generation for an image.
        $result = apply_filters( 'autoptimize_filter_css_datauri_image', array(), $path );
        if ( ! empty( $result ) ) {
            if ( is_array( $result ) && isset( $result['full'] ) && isset( $result['base64data'] ) ) {
                return $result;
            }
        }

        $hash  = md5( $path );
        $check = new autoptimizeCache( $hash, 'img' );
        if ( $check->check() ) {
            // we have the base64 image in cache.
            $head_and_data = $check->retrieve();
            $_base64data   = explode( ';base64,', $head_and_data );
            $base64data    = $_base64data[1];
            unset( $_base64data );
        } else {
            // It's an image and we don't have it in cache, get the type by extension.
            $exploded_path = explode( '.', $path );
            $type          = end( $exploded_path );

            switch ( $type ) {
                case 'jpg':
                case 'jpeg':
                    $dataurihead = 'data:image/jpeg;base64,';
                    break;
                case 'gif':
                    $dataurihead = 'data:image/gif;base64,';
                    break;
                case 'png':
                    $dataurihead = 'data:image/png;base64,';
                    break;
                case 'bmp':
                    $dataurihead = 'data:image/bmp;base64,';
                    break;
                case 'webp':
                    $dataurihead = 'data:image/webp;base64,';
                    break;
                default:
                    $dataurihead = 'data:application/octet-stream;base64,';
            }

            // Encode the data.
            $base64data    = base64_encode( file_get_contents( $path ) );
            $head_and_data = $dataurihead . $base64data;

            // Save in cache.
            $check->cache( $head_and_data, 'text/plain' );
        }
        unset( $check );

        return array(
            'full'       => $head_and_data,
            'base64data' => $base64data,
        );
    }

    /**
     * Given an array of key/value pairs to replace in $string,
     * it does so by replacing the longest-matching strings first.
     *
     * @param string $string string in which to replace.
     * @param array  $replacements to be replaced strings and replacement.
     *
     * @return string
     */
    protected static function replace_longest_matches_first( $string, $replacements = array() )
    {
        if ( ! empty( $replacements ) ) {
            // Sort the replacements array by key length in desc order (so that the longest strings are replaced first).
            $keys = array_map( 'strlen', array_keys( $replacements ) );
            array_multisort( $keys, SORT_DESC, $replacements );
            $string = str_replace( array_keys( $replacements ), array_values( $replacements ), $string );
        }

        return $string;
    }

    /**
     * Rewrites/Replaces any ASSETS_REGEX-matching urls in a string.
     * Removes quotes/cruft around each one and passes it through to
     * `autoptimizeBase::url_replace_cdn()`.
     * Replacements are performed in a `longest-match-replaced-first` way.
     *
     * @param string $code CSS code.
     *
     * @return string
     */
    public function replace_urls( $code = '' )
    {
        $replacements = array();

        preg_match_all( self::ASSETS_REGEX, $code, $url_src_matches );
        if ( is_array( $url_src_matches ) && ! empty( $url_src_matches ) ) {
            foreach ( $url_src_matches[1] as $count => $original_url ) {
                // Removes quotes and other cruft.
                $url = trim( $original_url, " \t\n\r\0\x0B\"'" );

                /**
                 * TODO/FIXME: Add a way for other code / callable to be called here
                 * and provide it's own results for the $replacements array
                 * for the "current" key.
                 * If such a result is returned/provided, we sholud then avoid
                 * calling url_replace_cdn() here for the current iteration.
                 *
                 * This would maybe allow the inlining logic currently present
                 * in `autoptimizeStyles::rewrite_assets()` to be "pulled out"
                 * and given as a callable to this method or something... and
                 * then we could "just" call `replace_urls()` from within
                 * `autoptimizeStyles::rewrite_assets()` and avoid some
                 * (currently present) code/logic duplication.
                 */

                // Do CDN replacement if needed.
                if ( ! empty( $this->cdn_url ) ) {
                    $replacement_url = $this->url_replace_cdn( $url );
                    // Prepare replacements array.
                    $replacements[ $url_src_matches[1][ $count ] ] = str_replace(
                        $original_url,
                        $replacement_url,
                        $url_src_matches[1][ $count ]
                    );
                }
            }
        }

        $code = self::replace_longest_matches_first( $code, $replacements );

        return $code;
    }

    /**
     * "Hides" @font-face declarations by replacing them with `%%FONTFACE%%` markers.
     * Also does CDN replacement of any font-urls within those declarations if the `autoptimize_filter_css_fonts_cdn`
     * filter is used.
     *
     * @param string $code HTML being processed to hide fonts.
     * @return string
     */
    public function hide_fontface_and_maybe_cdn( $code )
    {
        // Proceed only if @font-face declarations exist within $code.
        preg_match_all( self::FONT_FACE_REGEX, $code, $fontfaces );
        if ( isset( $fontfaces[0] ) ) {
            // Check if we need to cdn fonts or not.
            $do_font_cdn = apply_filters( 'autoptimize_filter_css_fonts_cdn', false );

            foreach ( $fontfaces[0] as $full_match ) {
                // Keep original match so we can search/replace it.
                $match_search = $full_match;

                // Do font cdn if needed.
                if ( $do_font_cdn ) {
                    $full_match = $this->replace_urls( $full_match );
                }

                // Replace declaration with its base64 encoded string.
                $replacement = self::build_marker( 'FONTFACE', $full_match );
                $code        = str_replace( $match_search, $replacement, $code );
            }
        }

        return $code;
    }

    /**
     * Restores original @font-face declarations that have been "hidden"
     * using `hide_fontface_and_maybe_cdn()`.
     *
     * @param string $code HTML being processed to unhide fonts.
     * @return string
     */
    public function restore_fontface( $code )
    {
        return $this->restore_marked_content( 'FONTFACE', $code );
    }

    /**
     * Re-write (and/or inline) referenced assets.
     *
     * @param string $code HTML being processed rewrite assets.
     * @return string
     */
    public function rewrite_assets( $code )
    {
        // Handle @font-face rules by hiding and processing them separately.
        $code = $this->hide_fontface_and_maybe_cdn( $code );

        /**
         * TODO/FIXME:
         * Certain code parts below are kind-of repeated now in `replace_urls()`, which is not ideal.
         * There is maybe a way to separate/refactor things and then be able to keep
         * the ASSETS_REGEX rewriting/handling logic in a single place (along with removing quotes/cruft from matched urls).
         * See comments in `replace_urls()` regarding this. The idea is to extract the inlining
         * logic out (which is the only real difference between replace_urls() and the code below), but still
         * achieve identical results as before.
         */

        // Re-write (and/or inline) URLs to point them to the CDN host.
        $url_src_matches = array();
        $imgreplace      = array();

        // Matches and captures anything specified within the literal `url()` and excludes those containing data: URIs.
        preg_match_all( self::ASSETS_REGEX, $code, $url_src_matches );
        if ( is_array( $url_src_matches ) && ! empty( $url_src_matches ) ) {
            foreach ( $url_src_matches[1] as $count => $original_url ) {
                // Removes quotes and other cruft.
                $url = trim( $original_url, " \t\n\r\0\x0B\"'" );

                // If datauri inlining is turned on, do it.
                $inlined = false;
                if ( $this->datauris ) {
                    $iurl = $url;
                    if ( false !== strpos( $iurl, '?' ) ) {
                        $iurl = strtok( $iurl, '?' );
                    }

                    $ipath = $this->getpath( $iurl );

                    $excluded = $this->check_datauri_exclude_list( $ipath );
                    if ( ! $excluded ) {
                        $is_datauri_candidate = $this->is_datauri_candidate( $ipath );
                        if ( $is_datauri_candidate ) {
                            $datauri    = $this->build_or_get_datauri_image( $ipath );
                            $base64data = $datauri['base64data'];
                            // Add it to the list for replacement.
                            $imgreplace[ $url_src_matches[1][ $count ] ] = str_replace(
                                $original_url,
                                $datauri['full'],
                                $url_src_matches[1][ $count ]
                            );
                            $inlined                                     = true;
                        }
                    }
                }

                /**
                 * Doing CDN URL replacement for every found match (if CDN is
                 * specified). This way we make sure to do it even if
                 * inlining isn't turned on, or if a resource is skipped from
                 * being inlined for whatever reason above.
                 */
                if ( ! $inlined && ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) ) {
                    // Just do the "simple" CDN replacement.
                    $replacement_url                             = $this->url_replace_cdn( $url );
                    $imgreplace[ $url_src_matches[1][ $count ] ] = str_replace(
                        $original_url,
                        $replacement_url,
                        $url_src_matches[1][ $count ]
                    );
                }
            }
        }

        $code = self::replace_longest_matches_first( $code, $imgreplace );

        // Replace back font-face markers with actual font-face declarations.
        $code = $this->restore_fontface( $code );

        return $code;
    }

    /**
     * Joins and optimizes CSS.
     */
    public function minify()
    {
        foreach ( $this->css as $group ) {
            list( $media, $css ) = $group;
            if ( preg_match( '#^INLINE;#', $css ) ) {
                // <style>.
                $css      = preg_replace( '#^INLINE;#', '', $css );
                $css      = self::fixurls( ABSPATH . 'index.php', $css ); // ABSPATH already contains a trailing slash.
                $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, '' );
                if ( has_filter( 'autoptimize_css_individual_style' ) && ! empty( $tmpstyle ) ) {
                    $css                   = $tmpstyle;
                    $this->alreadyminified = true;
                }
            } else {
                // <link>
                if ( false !== $css && file_exists( $css ) && is_readable( $css ) ) {
                    $css_path = $css;
                    $css      = self::fixurls( $css_path, file_get_contents( $css_path ) );
                    $css      = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $css );
                    $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, $css_path );
                    if ( has_filter( 'autoptimize_css_individual_style' ) && ! empty( $tmpstyle ) ) {
                        $css                   = $tmpstyle;
                        $this->alreadyminified = true;
                    } elseif ( $this->can_inject_late( $css_path, $css ) ) {
                        $css = self::build_injectlater_marker( $css_path, md5( $css ) );
                    }
                } else {
                    // Couldn't read CSS. Maybe getpath isn't working?
                    $css = '';
                }
            }

            foreach ( $media as $elem ) {
                if ( ! empty( $css ) ) {
                    if ( ! isset( $this->csscode[ $elem ] ) ) {
                        $this->csscode[ $elem ] = '';
                    }
                    $this->csscode[ $elem ] .= "\n/*FILESTART*/" . $css;
                }
            }
        }

        // Check for duplicate code.
        $md5list = array();
        $tmpcss  = $this->csscode;
        foreach ( $tmpcss as $media => $code ) {
            $md5sum    = md5( $code );
            $medianame = $media;
            foreach ( $md5list as $med => $sum ) {
                // If same code.
                if ( $sum === $md5sum ) {
                    // Add the merged code.
                    $medianame                   = $med . ', ' . $media;
                    $this->csscode[ $medianame ] = $code;
                    $md5list[ $medianame ]       = $md5list[ $med ];
                    unset( $this->csscode[ $med ], $this->csscode[ $media ], $md5list[ $med ] );
                }
            }
            $md5list[ $medianame ] = $md5sum;
        }
        unset( $tmpcss );

        // Manage @imports, while is for recursive import management.
        foreach ( $this->csscode as &$thiscss ) {
            // Flag to trigger import reconstitution and var to hold external imports.
            $fiximports       = false;
            $external_imports = '';

            // remove comments to avoid importing commented-out imports.
            $thiscss_nocomments = preg_replace( '#/\*.*\*/#Us', '', $thiscss );
            while ( preg_match_all( '#@import +(?:url)?(?:(?:\((["\']?)(?:[^"\')]+)\1\)|(["\'])(?:[^"\']+)\2)(?:[^,;"\']+(?:,[^,;"\']+)*)?)(?:;)#mi', $thiscss_nocomments, $matches ) ) {
                foreach ( $matches[0] as $import ) {
                    if ( $this->isremovable( $import, $this->cssremovables ) ) {
                        $thiscss   = str_replace( $import, '', $thiscss );
                        $import_ok = true;
                    } else {
                        $url       = trim( preg_replace( '#^.*((?:https?:|ftp:)?//.*\.css).*$#', '$1', trim( $import ) ), " \t\n\r\0\x0B\"'" );
                        $path      = $this->getpath( $url );
                        $import_ok = false;
                        if ( file_exists( $path ) && is_readable( $path ) ) {
                            $code     = addcslashes( self::fixurls( $path, file_get_contents( $path ) ), '\\' );
                            $code     = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $code );
                            $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $code, '' );
                            if ( has_filter( 'autoptimize_css_individual_style' ) && ! empty( $tmpstyle ) ) {
                                $code                  = $tmpstyle;
                                $this->alreadyminified = true;
                            } elseif ( $this->can_inject_late( $path, $code ) ) {
                                $code = self::build_injectlater_marker( $path, md5( $code ) );
                            }

                            if ( ! empty( $code ) ) {
                                $tmp_thiscss = str_replace( $import, stripcslashes( $code ), $thiscss );
                                if ( ! empty( $tmp_thiscss ) ) {
                                    $thiscss   = $tmp_thiscss;
                                    $import_ok = true;
                                    unset( $tmp_thiscss );
                                }
                            }
                            unset( $code );
                        }
                    }
                    if ( ! $import_ok ) {
                        // External imports and general fall-back.
                        $external_imports .= $import;

                        $thiscss    = str_replace( $import, '', $thiscss );
                        $fiximports = true;
                    }
                }
                $thiscss = preg_replace( '#/\*FILESTART\*/#', '', $thiscss );
                $thiscss = preg_replace( '#/\*FILESTART2\*/#', '/*FILESTART*/', $thiscss );

                // and update $thiscss_nocomments before going into next iteration in while loop.
                $thiscss_nocomments = preg_replace( '#/\*.*\*/#Us', '', $thiscss );
            }
            unset( $thiscss_nocomments );

            // Add external imports to top of aggregated CSS.
            if ( $fiximports ) {
                $thiscss = $external_imports . $thiscss;
            }
        }
        unset( $thiscss );

        // $this->csscode has all the uncompressed code now.
        foreach ( $this->csscode as &$code ) {
            // Check for already-minified code.
            $hash = md5( $code );
            do_action( 'autoptimize_action_css_hash', $hash );
            $ccheck = new autoptimizeCache( $hash, 'css' );
            if ( $ccheck->check() ) {
                $code                          = $ccheck->retrieve();
                $this->hashmap[ md5( $code ) ] = $hash;
                continue;
            }
            unset( $ccheck );

            // Rewrite and/or inline referenced assets.
            $code = $this->rewrite_assets( $code );

            // Minify.
            $code = $this->run_minifier_on( $code );

            // Bring back INJECTLATER stuff.
            $code = $this->inject_minified( $code );

            // Filter results.
            $tmp_code = apply_filters( 'autoptimize_css_after_minify', $code );
            if ( ! empty( $tmp_code ) ) {
                $code = $tmp_code;
                unset( $tmp_code );
            }

            $this->hashmap[ md5( $code ) ] = $hash;
        }

        unset( $code );
        return true;
    }

    public function run_minifier_on( $code )
    {
        if ( ! $this->alreadyminified ) {
            $do_minify = apply_filters( 'autoptimize_css_do_minify', true );

            if ( $do_minify ) {
                $cssmin   = new autoptimizeCSSmin();
                $tmp_code = trim( $cssmin->run( $code ) );

                if ( ! empty( $tmp_code ) ) {
                    $code = $tmp_code;
                    unset( $tmp_code );
                }
            }
        }

        return $code;
    }

    /**
     * Caches the CSS in uncompressed, deflated and gzipped form.
     */
    public function cache()
    {
        // CSS cache.
        foreach ( $this->csscode as $media => $code ) {
            if ( empty( $code ) ) {
                continue;
            }

            $md5   = $this->hashmap[ md5( $code ) ];
            $cache = new autoptimizeCache( $md5, 'css' );
            if ( ! $cache->check() ) {
                // Cache our code.
                $cache->cache( $code, 'text/css' );
            }
            $this->url[ $media ] = AUTOPTIMIZE_CACHE_URL . $cache->getname();
        }
    }

    /**
     * Returns the content.
     */
    public function getcontent()
    {
        // Restore the full content (only applies when "autoptimize_filter_css_justhead" filter is true).
        if ( ! empty( $this->restofcontent ) ) {
            $this->content      .= $this->restofcontent;
            $this->restofcontent = '';
        }

        // type is not added by default.
        $type_css = '';
        if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
            $type_css = 'type="text/css" ';
        }

        // Inject the new stylesheets, if possible after SEO stuff, but we need to
        // already restore script to be able to inject before ld+json instead of title
        // this should be safe here as all has been extracted  already but behind a filter anyway.
        if ( $this->inline && true === apply_filters( 'autoptimize_filter_css_restore_js_early', true ) ) {
            $this->content = $this->restore_marked_content( 'SCRIPT', $this->content );
        }
        $_strpos_ldjson = strpos( $this->content, '<script type="application/ld+json"' );
        if ( false !== $_strpos_ldjson && $_strpos_ldjson < strpos( $this->content, '</head' ) ) {
            $replace_tag = array( '<script type="application/ld+json"', 'before' );            
        } else {
            $replace_tag = array( '<title', 'before' );
        }
        $replace_tag = apply_filters( 'autoptimize_filter_css_replacetag', $replace_tag, $this->content );

        if ( $this->inline ) {
            foreach ( $this->csscode as $media => $code ) {
                $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<style ' . $type_css . 'media="' . $media . '">' . $code . '</style>' ), $replace_tag );
            }
        } else {
            if ( $this->defer ) {
                $preload_css_block  = '';
                $inlined_ccss_block = '';
                $noscript_css_block = '<noscript id="aonoscrcss">';

                $defer_inline_code = $this->defer_inline;
                if ( ! empty( $defer_inline_code ) ) {
                    if ( apply_filters( 'autoptimize_filter_css_critcss_minify', true ) ) {
                        $icss_hash  = md5( $defer_inline_code );
                        $icss_cache = new autoptimizeCache( $icss_hash, 'css' );
                        if ( $icss_cache->check() ) {
                            // we have the optimized inline CSS in cache.
                            $defer_inline_code = $icss_cache->retrieve();
                        } else {
                            $cssmin   = new autoptimizeCSSmin();
                            $tmp_code = trim( $cssmin->run( $defer_inline_code ) );

                            if ( ! empty( $tmp_code ) ) {
                                $defer_inline_code = $tmp_code;
                                $icss_cache->cache( $defer_inline_code, 'text/css' );
                                unset( $tmp_code );
                            }
                        }
                    }
                    // inlined critical css set here, but injected when full CSS is injected
                    // to avoid CSS containing SVG with <title tag receiving the full CSS link.
                    $inlined_ccss_block = '<style ' . $type_css . 'id="aoatfcss" media="all">' . $defer_inline_code . '</style>';
                }
            }

            foreach ( $this->url as $media => $url ) {
                $url = $this->url_replace_cdn( $url );

                // Add the stylesheet either deferred (import at bottom) or normal links in head.
                if ( $this->defer && 'print' !== $media ) {
                    $preload_onload = autoptimizeConfig::get_ao_css_preload_onload( $media );

                    $preload_css_block .= apply_filters( 'autoptimize_filter_css_single_deferred_link', '<link rel="stylesheet" media="print" href="' . $url . '" onload="' . $preload_onload . '">' );
                    if ( apply_filters( 'autoptimize_filter_css_preload_and_print', false ) ) {
                        $preload_css_block = '<link rel="preload" as="stylesheet" href="' . $url . '"/>' . $preload_css_block;
                    }
                    $noscript_css_block .= '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet">';
                } else {
                    if ( strlen( $this->csscode[ $media ] ) > $this->cssinlinesize ) {
                        $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet">' ), $replace_tag );
                    } elseif ( strlen( $this->csscode[ $media ] ) > 0 ) {
                        $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<style ' . $type_css . 'media="' . $media . '">' . $this->csscode[ $media ] . '</style>' ), $replace_tag );
                    }
                }
            }

            if ( $this->defer ) {
                $noscript_css_block .= '</noscript>';
                // Inject inline critical CSS, the preloaded full CSS and the noscript-CSS.
                $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', $inlined_ccss_block . $preload_css_block . $noscript_css_block ), $replace_tag );
            }
        }

        // restore comments.
        $this->content = $this->restore_comments( $this->content );

        // restore IE hacks.
        $this->content = $this->restore_iehacks( $this->content );

        // restore (no)script.
        $this->content = $this->restore_marked_content( 'SCRIPT', $this->content );

        // Restore noptimize.
        $this->content = $this->restore_noptimize( $this->content );

        // Return the modified stylesheet.
        return $this->content;
    }

    /**
     * Make sure URL's are absolute iso relative to original CSS location.
     *
     * @param string $file filename of optimized CSS-file.
     * @param string $code CSS-code in which to fix URL's.
     */
    static function fixurls( $file, $code )
    {
        // Switch all imports to the url() syntax.
        $code = preg_replace( '#@import ("|\')(.+?)\.css.*?("|\')#', '@import url("${2}.css")', $code );

        if ( preg_match_all( self::ASSETS_REGEX, $code, $matches ) ) {
            $file = str_replace( WP_ROOT_DIR, '/', $file );
            /**
             * Rollback as per https://github.com/futtta/autoptimize/issues/94
             * $file = str_replace( AUTOPTIMIZE_WP_CONTENT_NAME, '', $file );
             */
            $dir = dirname( $file ); // Like /themes/expound/css.

            /**
             * $dir should not contain backslashes, since it's used to replace
             * urls, but it can contain them when running on Windows because
             * fixurls() is sometimes called with `ABSPATH . 'index.php'`
             */
            $dir = str_replace( '\\', '/', $dir );
            unset( $file ); // not used below at all.

            $replace = array();
            foreach ( $matches[1] as $k => $url ) {
                // Remove quotes.
                $url      = trim( $url, " \t\n\r\0\x0B\"'" );
                $no_q_url = trim( $url, "\"'" );
                if ( $url !== $no_q_url ) {
                    $removed_quotes = true;
                } else {
                    $removed_quotes = false;
                }

                if ( '' === $no_q_url ) {
                    continue;
                }

                $url = $no_q_url;
                if ( '/' === $url[0] || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) {
                    // URL is protocol-relative, host-relative or something we don't touch.
                    continue;
                } else { // Relative URL.

                    /*
                     * rollback as per https://github.com/futtta/autoptimize/issues/94
                     * $newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_CONTENT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) );
                     */
                    $newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_ROOT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) );
                    $newurl = apply_filters( 'autoptimize_filter_css_fixurl_newurl', $newurl );

                    /**
                     * Hash the url + whatever was behind potentially for replacement
                     * We must do this, or different css classes referencing the same bg image (but
                     * different parts of it, say, in sprites and such) loose their stuff...
                     */
                    $hash = md5( $url . $matches[2][ $k ] );
                    $code = str_replace( $matches[0][ $k ], $hash, $code );

                    if ( $removed_quotes ) {
                        $replace[ $hash ] = "url('" . $newurl . "')" . $matches[2][ $k ];
                    } else {
                        $replace[ $hash ] = 'url(' . $newurl . ')' . $matches[2][ $k ];
                    }
                }
            }

            $code = self::replace_longest_matches_first( $code, $replace );
        }

        return $code;
    }

    private function ismovable( $tag )
    {
        if ( ! $this->aggregate ) {
            return false;
        }

        if ( ! empty( $this->allowlist ) ) {
            foreach ( $this->allowlist as $match ) {
                if ( false !== strpos( $tag, $match ) ) {
                    return true;
                }
            }
            // no match with allowlist.
            return false;
        } else {
            if ( is_array( $this->dontmove ) && ! empty( $this->dontmove ) ) {
                foreach ( $this->dontmove as $match ) {
                    if ( false !== strpos( $tag, $match ) ) {
                        // Matched something.
                        return false;
                    }
                }
            }

            // If we're here it's safe to move.
            return true;
        }
    }

    private function can_inject_late( $css_path, $css )
    {
        $consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false, $css_path );
        if ( true !== $this->inject_min_late ) {
            // late-inject turned off.
            return false;
        } elseif ( ( false === strpos( $css_path, 'min.css' ) ) && ( str_replace( $consider_minified_array, '', $css_path ) === $css_path ) ) {
            // file not minified based on filename & filter.
            return false;
        } elseif ( false !== strpos( $css, '@import' ) ) {
            // can't late-inject files with imports as those need to be aggregated.
            return false;
        } elseif ( ( false !== strpos( $css, '@font-face' ) ) && ( apply_filters( 'autoptimize_filter_css_fonts_cdn', false ) === true ) && ( ! empty( $this->cdn_url ) ) ) {
            // don't late-inject CSS with font-src's if fonts are set to be CDN'ed.
            return false;
        } elseif ( ( ( true == $this->datauris ) || ( ! empty( $this->cdn_url ) ) ) && preg_match( '#background[^;}]*url\(#Ui', $css ) ) {
            // don't late-inject CSS with images if CDN is set OR if image inlining is on.
            return false;
        } else {
            // phew, all is safe, we can late-inject.
            return true;
        }
    }

    /**
     * Minifies (and cdn-replaces) a single local css file
     * and returns its (cached) url.
     *
     * @param string $filepath Filepath.
     * @param bool   $cache_miss Optional. Force a cache miss. Default false.
     *
     * @return bool|string Url pointing to the minified css file or false.
     */
    public function minify_single( $filepath, $cache_miss = false )
    {
        $contents = $this->prepare_minify_single( $filepath );

        if ( empty( $contents ) ) {
            // if aggregate is off and CCSS is used but all files are minified already, then we
            // must make sure the autoptimize_action_css_hash action still fires for CCSS's sake.
            $ao_ccss_key = get_option( 'autoptimize_ccss_key', '' );
            if ( false === $this->aggregate && isset( $ao_ccss_key ) && ! empty( $ao_ccss_key ) ) {
                $hash = 'single_' . md5( file_get_contents( $filepath ) );
                do_action( 'autoptimize_action_css_hash', $hash );
            }
            return false;
        }

        // Check cache.
        $hash  = 'single_' . md5( $contents );
        $cache = new autoptimizeCache( $hash, 'css' );
        do_action( 'autoptimize_action_css_hash', $hash );

        // If not in cache already, minify...
        if ( ! $cache->check() || $cache_miss ) {
            // Fixurls...
            $contents = self::fixurls( $filepath, $contents );
            // CDN-replace any referenced assets if needed...
            $contents = $this->hide_fontface_and_maybe_cdn( $contents );
            $contents = $this->replace_urls( $contents );
            $contents = $this->restore_fontface( $contents );
            // Now minify...
            $cssmin   = new autoptimizeCSSmin();
            $contents = trim( $cssmin->run( $contents ) );

            // Check if minified cache content is empty.
            if ( empty( $contents ) ) {
                return false;
            }

            // Filter contents of excluded minified CSS.
            $contents = apply_filters( 'autoptimize_filter_css_single_after_minify', $contents );

            // Store in cache.
            $cache->cache( $contents, 'text/css' );
        }

        $url = $this->build_minify_single_url( $cache );

        return $url;
    }

    /**
     * Returns whether we're doing aggregation or not.
     *
     * @return bool
     */
    public function aggregating()
    {
        return $this->aggregate;
    }

    public function getOptions()
    {
        return $this->options;
    }

    public function replaceOptions( $options )
    {
        $this->options = $options;
    }

    public function setOption( $name, $value )
    {
        $this->options[ $name ] = $value;
        $this->$name            = $value;
    }

    public function getOption( $name )
    {
        return $this->options[ $name ];
    }

    /**
     * Sanitize user-provided CSS.
     *
     * For now just strip_tags (the WordPress way) and preg_replace to escape < in certain cases but might do full CSS escaping in the future, see:
     * https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-4-css-encode-and-strictly-validate-before-inserting-untrusted-data-into-html-style-property-values
     * https://github.com/twigphp/Twig/blob/3.x/src/Extension/EscaperExtension.php#L300-L319
     * https://github.com/laminas/laminas-escaper/blob/2.8.x/src/Escaper.php#L205-L221
     *
     * @param string $css the to be sanitized CSS.
     * @return string sanitized CSS.
     */
    public static function sanitize_css( $css )
    {
        $css = wp_strip_all_tags( $css );
        if ( strpos( $css, '<' ) !== false ) {
            $css = preg_replace( '#<(\/?\w+)#', '\00003C$1', $css );
        }
        return $css;
    }
}
PK.�1\�ь��[�[autoptimizeBase.phpnu�[���<?php
/**
 * Base class other (more-specific) classes inherit from.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

abstract class autoptimizeBase
{
    /**
     * Holds content being processed (html, scripts, styles)
     *
     * @var string
     */
    protected $content = '';

    /**
     * Controls debug logging.
     *
     * @var bool
     */
    public $debug_log = false;

    /**
     * Initiated $cdn_url.
     *
     * @var string
     */
    public $cdn_url = '';

    public function __construct( $content )
    {
        $this->content = $content;
    }

    /**
     * Reads the page and collects tags.
     *
     * @param array $options Options.
     *
     * @return bool
     */
    abstract public function read( $options );

    /**
     * Joins and optimizes collected things.
     *
     * @return bool
     */
    abstract public function minify();

    /**
     * Caches the things.
     *
     * @return void
     */
    abstract public function cache();

    /**
     * Returns the content
     *
     * @return string
     */
    abstract public function getcontent();

    /**
     * Tranfsorms a given URL to a full local filepath if possible.
     * Returns local filepath or false.
     *
     * @param string $url URL to transform.
     *
     * @return bool|string
     */
    public function getpath( $url )
    {
        $url = apply_filters( 'autoptimize_filter_cssjs_alter_url', $url );

        if ( is_null( $url ) ) {
            return false;
        }

        if ( false !== strpos( $url, '%' ) ) {
            $url = urldecode( $url );
        }

        $site_host    = parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST );
        $content_host = parse_url( AUTOPTIMIZE_WP_ROOT_URL, PHP_URL_HOST );

        // Normalizing attempts...
        $double_slash_position = strpos( $url, '//' );
        if ( 0 === $double_slash_position ) {
            if ( is_ssl() ) {
                $url = 'https:' . $url;
            } else {
                $url = 'http:' . $url;
            }
        } elseif ( ( false === $double_slash_position ) && ( false === strpos( $url, $site_host ) ) ) {
            if ( AUTOPTIMIZE_WP_SITE_URL === $site_host ) {
                $url = AUTOPTIMIZE_WP_SITE_URL . $url;
            } elseif ( 0 === strpos( $url, '/' ) ) {
                $url = '//' . $site_host . autoptimizeUtils::path_canonicalize( $url );
            } else {
                $url = AUTOPTIMIZE_WP_SITE_URL . autoptimizeUtils::path_canonicalize( $url );
            }
        }

        if ( $site_host !== $content_host ) {
            $url = str_replace( AUTOPTIMIZE_WP_CONTENT_URL, AUTOPTIMIZE_WP_SITE_URL . AUTOPTIMIZE_WP_CONTENT_NAME, $url );
        }

        // First check; hostname wp site should be hostname of url!
        $url_host = @parse_url( $url, PHP_URL_HOST ); // @codingStandardsIgnoreLine
        if ( $url_host !== $site_host ) {
            /**
             * First try to get all domains from WPML (if available)
             * then explicitely declare $this->cdn_url as OK as well
             * then apply own filter autoptimize_filter_cssjs_multidomain takes an array of hostnames
             * each item in that array will be considered part of the same WP multisite installation
             */
            $multidomains = array();

            $multidomains_wpml = apply_filters( 'wpml_setting', array(), 'language_domains' );
            if ( ! empty( $multidomains_wpml ) ) {
                $multidomains = array_map( array( $this, 'get_url_hostname' ), $multidomains_wpml );
            }

            if ( ! empty( $this->cdn_url ) ) {
                $multidomains[] = parse_url( $this->cdn_url, PHP_URL_HOST );
            }

            $multidomains = apply_filters( 'autoptimize_filter_cssjs_multidomain', $multidomains );

            if ( ! empty( $multidomains ) ) {
                if ( in_array( $url_host, $multidomains ) ) {
                    $url = str_replace( $url_host, $site_host, $url );
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        // Try to remove "wp root url" from url while not minding http<>https.
        $tmp_ao_root = preg_replace( '/https?:/', '', AUTOPTIMIZE_WP_ROOT_URL );

        if ( $site_host !== $content_host ) {
            // As we replaced the content-domain with the site-domain, we should match against that.
            $tmp_ao_root = preg_replace( '/https?:/', '', AUTOPTIMIZE_WP_SITE_URL );
        }

        if ( is_multisite() && ! is_main_site() && ! empty( $this->cdn_url ) && apply_filters( 'autoptimize_filter_base_getpage_multisite_cdn_juggling', true ) ) {
            // multisite child sites with CDN need the network_site_url as tmp_ao_root but only if directory-based multisite.
            $_network_site_url = network_site_url();
            if ( strpos( AUTOPTIMIZE_WP_SITE_URL, $_network_site_url ) !== false ) {
                $tmp_ao_root = preg_replace( '/https?:/', '', $_network_site_url );
            }
        }

        $tmp_url = preg_replace( '/https?:/', '', $url );
        $path    = str_replace( $tmp_ao_root, '', $tmp_url );

        // If path starts with :// or //, this is not a URL in the WP context and
        // we have to assume we can't aggregate.
        if ( preg_match( '#^:?//#', $path ) ) {
            // External script/css (adsense, etc).
            return false;
        }

        // Prepend with WP_ROOT_DIR to have full path to file.
        $path = str_replace( '//', '/', trailingslashit( WP_ROOT_DIR ) . $path );

        // Allow path to be altered, e.g. in the case of bedrock-like setups where
        // core, theme & plugins might be in different locations on the filesystem.
        $path = apply_filters( 'autoptimize_filter_base_getpath_path', $path, $url );

        // Final check: does file exist and is it readable?
        if ( file_exists( $path ) && is_file( $path ) && is_readable( $path ) ) {
            return $path;
        } else {
            return false;
        }
    }

    /**
     * Returns the hostname part of a given $url if we're able to parse it.
     * If not, it returns the original url (prefixed with http:// scheme in case
     * it was missing).
     * Used as callback for WPML multidomains filter.
     *
     * @param string $url URL.
     *
     * @return string
     */
    protected function get_url_hostname( $url )
    {
        // Checking that the url starts with something vaguely resembling a protocol.
        if ( ( 0 !== strpos( $url, 'http' ) ) && ( 0 !== strpos( $url, '//' ) ) ) {
            $url = 'http://' . $url;
        }

        // Grab the hostname.
        $hostname = parse_url( $url, PHP_URL_HOST );

        // Fallback when parse_url() fails.
        if ( empty( $hostname ) ) {
            $hostname = $url;
        }

        return $hostname;
    }

    /**
     * Hides everything between noptimize-comment tags.
     *
     * @param string $markup Markup to process.
     *
     * @return string
     */
    protected function hide_noptimize( $markup )
    {
        return $this->replace_contents_with_marker_if_exists(
            'NOPTIMIZE',
            '/<!--\s?noptimize\s?-->/',
            '#<!--\s?noptimize\s?-->.*?<!--\s?/\s?noptimize\s?-->#is',
            $markup
        );
    }

    /**
     * Unhide noptimize-tags.
     *
     * @param string $markup Markup to process.
     *
     * @return string
     */
    protected function restore_noptimize( $markup )
    {
        return $this->restore_marked_content( 'NOPTIMIZE', $markup );
    }

    /**
     * Hides "iehacks" content.
     *
     * @param string $markup Markup to process.
     *
     * @return string
     */
    protected function hide_iehacks( $markup )
    {
        return $this->replace_contents_with_marker_if_exists(
            'IEHACK', // Marker name...
            '<!--[if', // Invalid regex, will fallback to search using strpos()...
            '#<!--\[if.*?\[endif\]-->#is', // Replacement regex...
            $markup
        );
    }

    /**
     * Restores "hidden" iehacks content.
     *
     * @param string $markup Markup to process.
     *
     * @return string
     */
    protected function restore_iehacks( $markup )
    {
        return $this->restore_marked_content( 'IEHACK', $markup );
    }

    /**
     * "Hides" content within HTML comments using a regex-based replacement
     * if HTML comment markers are found.
     * `<!--example-->` becomes `%%COMMENTS%%ZXhhbXBsZQ==%%COMMENTS%%`
     *
     * @param string $markup Markup to process.
     *
     * @return string
     */
    protected function hide_comments( $markup )
    {
        return $this->replace_contents_with_marker_if_exists(
            'COMMENTS',
            '<!--',
            '#<!--.*?-->#is',
            $markup
        );
    }

    /**
     * Restores original HTML comment markers inside a string whose HTML
     * comments have been "hidden" by using `hide_comments()`.
     *
     * @param string $markup Markup to process.
     *
     * @return string
     */
    protected function restore_comments( $markup )
    {
        return $this->restore_marked_content( 'COMMENTS', $markup );
    }

    /**
     * Replaces the given URL with the CDN-version of it when CDN replacement
     * is supposed to be done.
     *
     * @param string $url URL to process.
     *
     * @return string
     */
    public function url_replace_cdn( $url )
    {
        // For 2.3 back-compat in which cdn-ing appeared to be automatically
        // including WP subfolder/subdirectory into account as part of cdn-ing,
        // even though it might've caused serious troubles in certain edge-cases.
        $cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $this->cdn_url );

        // Allows API/filter to further tweak the cdn url...
        $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url );
        if ( ! empty( $cdn_url ) && false === strpos( $url, $cdn_url ) && false !== apply_filters( 'autoptimize_filter_base_apply_cdn', true, $url ) ) {

            // Simple str_replace-based approach fails when $url is protocol-or-host-relative.
            $is_protocol_relative = autoptimizeUtils::is_protocol_relative( $url );
            $is_host_relative     = ( ! $is_protocol_relative && ( '/' === $url[0] ) );
            $cdn_url              = esc_url( rtrim( $cdn_url, '/' ) );

            if ( $is_host_relative ) {
                // Prepending host-relative urls with the cdn url.
                $url = $cdn_url . $url;
            } else {
                // Either a protocol-relative or "regular" url, replacing it either way.
                if ( $is_protocol_relative ) {
                    // Massage $site_url so that simple str_replace() still "works" by
                    // searching for the protocol-relative version of AUTOPTIMIZE_WP_SITE_URL.
                    $site_url = str_replace( array( 'http:', 'https:' ), '', AUTOPTIMIZE_WP_SITE_URL );
                } else {
                    $site_url = AUTOPTIMIZE_WP_SITE_URL;
                }
                $url = str_replace( $site_url, $cdn_url, $url );
            }
        }

        // Allow API filter to take further care of CDN replacement.
        $url = apply_filters( 'autoptimize_filter_base_replace_cdn', $url );

        return $url;
    }

    /**
     * Injects/replaces the given payload markup into `$this->content`
     * at the specified location.
     * If the specified tag cannot be found, the payload is appended into
     * $this->content along with a warning wrapped inside <!--noptimize--> tags.
     *
     * @param string $payload Markup to inject.
     * @param array  $where   Array specifying the tag name and method of injection.
     *                        Index 0 is the tag name (i.e., `</body>`).
     *                        Index 1 specifies ˛'before', 'after' or 'replace'. Defaults to 'before'.
     *
     * @return void
     */
    protected function inject_in_html( $payload, $where )
    {
        $warned   = false;
        $position = autoptimizeUtils::strpos( $this->content, $where[0] );
        if ( false !== $position ) {
            // Found the tag, setup content/injection as specified.
            if ( 'after' === $where[1] ) {
                $content = $where[0] . $payload;
            } elseif ( 'replace' === $where[1] ) {
                $content = $payload;
            } else {
                $content = $payload . $where[0];
            }
            // Place where specified.
            $this->content = autoptimizeUtils::substr_replace(
                $this->content,
                $content,
                $position,
                // Using plain strlen() should be safe here for now, since
                // we're not searching for multibyte chars here still...
                strlen( $where[0] )
            );
        } else {
            // Couldn't find what was specified, just append and add a warning.
            $this->content .= $payload;
            if ( ! $warned ) {
                $tag_display    = str_replace( array( '<', '>' ), '', $where[0] );
                $this->content .= '<!--noptimize--><!-- Autoptimize found a problem with the HTML in your Theme, tag `' . $tag_display . '` missing --><!--/noptimize-->';
                $warned         = true;
            }
        }
    }

    /**
     * Returns true if given `$tag` is found in the list of `$removables`.
     *
     * @param string $tag Tag to search for.
     * @param array  $removables List of things considered completely removable.
     *
     * @return bool
     */
    protected function isremovable( $tag, $removables )
    {
        foreach ( $removables as $match ) {
            if ( false !== strpos( $tag, $match ) ) {
                return true;
            }
        }

        return false;
    }

    /**
     * Callback used in `self::inject_minified()`.
     *
     * @param array $matches Regex matches.
     *
     * @return string
     */
    public function inject_minified_callback( $matches )
    {
        static $conf = null;
        if ( null === $conf ) {
            $conf = autoptimizeConfig::instance();
        }

        /**
         * $matches[1] holds the whole match caught by regex in self::inject_minified(),
         * so we take that and split the string on `|`.
         * First element is the filepath, second is the md5 hash of contents
         * the filepath had when it was being processed.
         * If we don't have those, we'll bail out early.
        */
        $filepath = null;
        $filehash = null;

        // Grab the parts we need.
        $parts = explode( '|', $matches[1] );
        if ( ! empty( $parts ) ) {
            $filepath = isset( $parts[0] ) ? base64_decode( $parts[0] ) : null;
            $filehash = isset( $parts[1] ) ? $parts[1] : null;
        }

        // Bail early if something's not right...
        if ( ! $filepath || ! $filehash ) {
            return "\n";
        }

        $filecontent = file_get_contents( $filepath );

        // Some things are differently handled for css/js...
        $is_js_file = ( '.js' === substr( $filepath, -3, 3 ) );

        $is_css_file = false;
        if ( ! $is_js_file ) {
            $is_css_file = ( '.css' === substr( $filepath, -4, 4 ) );
        }

        // BOMs being nuked here unconditionally (regardless of where they are)!
        $filecontent = preg_replace( "#\x{EF}\x{BB}\x{BF}#", '', $filecontent );

        // Remove comments and blank lines.
        if ( $is_js_file ) {
            $filecontent = preg_replace( '#^\s*\/\/.*$#Um', '', $filecontent );
        }

        // Nuke un-important comments.
        $filecontent = preg_replace( '#^\s*\/\*[^!].*\*\/\s?#Um', '', $filecontent );

        // Normalize newlines.
        $filecontent = preg_replace( '#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#', "\n", $filecontent );

        // JS specifics.
        if ( $is_js_file ) {
            // Append a semicolon at the end of js files if it's missing.
            $last_char = substr( $filecontent, -1, 1 );
            if ( ';' !== $last_char && '}' !== $last_char ) {
                $filecontent .= ';';
            }
            // Check if try/catch should be used.
            $opt_js_try_catch = $conf->get( 'autoptimize_js_trycatch' );
            if ( 'on' === $opt_js_try_catch ) {
                // It should, wrap in try/catch.
                $filecontent = 'try{' . $filecontent . '}catch(e){}';
            }
        } elseif ( $is_css_file ) {
            $filecontent = autoptimizeStyles::fixurls( $filepath, $filecontent );
        } else {
            $filecontent = '';
        }

        // Return modified (or empty!) code/content.
        return "\n" . $filecontent;
    }

    /**
     * Inject already minified code in optimized JS/CSS.
     *
     * @param string $in Markup.
     *
     * @return string
     */
    protected function inject_minified( $in )
    {
        $out = $in;
        if ( false !== strpos( $in, '%%INJECTLATER%%' ) ) {
            $out = preg_replace_callback(
                '#\/\*\!%%INJECTLATER' . AUTOPTIMIZE_HASH . '%%(.*?)%%INJECTLATER%%\*\/#is',
                array( $this, 'inject_minified_callback' ),
                $in
            );
        }

        return $out;
    }

    /**
     * Specialized method to create the INJECTLATER marker.
     * These are somewhat "special", in the sense that they're additionally wrapped
     * within an "exclamation mark style" comment, so that they're not stripped
     * out by minifiers.
     * They also currently contain the hash of the file's contents too (unlike other markers).
     *
     * @param string $filepath Filepath.
     * @param string $hash Hash.
     *
     * @return string
     */
    public static function build_injectlater_marker( $filepath, $hash )
    {
        $contents = '/*!' . self::build_marker( 'INJECTLATER', $filepath, $hash ) . '*/';

        return $contents;
    }

    /**
     * Creates and returns a `%%`-style named marker which holds
     * the base64 encoded `$data`.
     * If `$hash` is provided, it's appended to the base64 encoded string
     * using `|` as the separator (in order to support building the
     * somewhat special/different INJECTLATER marker).
     *
     * @param string      $name Marker name.
     * @param string      $data Marker data which will be base64-encoded.
     * @param string|null $hash Optional.
     *
     * @return string
     */
    public static function build_marker( $name, $data, $hash = null )
    {
        // Start the marker, add the data.
        $marker = '%%' . $name . AUTOPTIMIZE_HASH . '%%' . base64_encode( $data );

        // Add the hash if provided.
        if ( null !== $hash ) {
            $marker .= '|' . $hash;
        }

        // Close the marker.
        $marker .= '%%' . $name . '%%';

        return $marker;
    }

    /**
     * Searches for `$search` in `$content` (using either `preg_match()`
     * or `strpos()`, depending on whether `$search` is a valid regex pattern or not).
     * If something is found, it replaces `$content` using `$re_replace_pattern`,
     * effectively creating our named markers (`%%{$marker}%%`.
     * These are then at some point replaced back to their actual/original/modified
     * contents using `autoptimizeBase::restore_marked_content()`.
     *
     * @param string $marker Marker name (without percent characters).
     * @param string $search A string or full blown regex pattern to search for in $content. Uses `strpos()` or `preg_match()`.
     * @param string $re_replace_pattern Regex pattern to use when replacing contents.
     * @param string $content Content to work on.
     *
     * @return string
     */
    public static function replace_contents_with_marker_if_exists( $marker, $search, $re_replace_pattern, $content )
    {
        $found = false;

        $is_regex = autoptimizeUtils::str_is_valid_regex( $search );
        if ( $is_regex ) {
            $found = preg_match( $search, $content );
        } else {
            $found = ( false !== strpos( $content, $search ) );
        }

        if ( $found ) {
            $content = preg_replace_callback(
                $re_replace_pattern,
                function( $matches ) use ( $marker ) {
                    return autoptimizeBase::build_marker( $marker, $matches[0] );
                },
                $content
            );
        }

        return $content;
    }

    /**
     * Complements `autoptimizeBase::replace_contents_with_marker_if_exists()`.
     *
     * @param string $marker Marker.
     * @param string $content Markup.
     *
     * @return string
     */
    public static function restore_marked_content( $marker, $content )
    {
        if ( false !== strpos( $content, $marker ) ) {
            $content = preg_replace_callback(
                '#%%' . $marker . AUTOPTIMIZE_HASH . '%%(.*?)%%' . $marker . '%%#is',
                function ( $matches ) {
                    return base64_decode( $matches[1] );
                },
                $content
            );
        }

        return $content;
    }

    /**
     * Logs given `$data` for debugging purposes (when debug logging is on).
     *
     * @param mixed $data Data to log.
     *
     * @return void
     */
    protected function debug_log( $data )
    {
        if ( ! isset( $this->debug_log ) || ! $this->debug_log ) {
            return;
        }

        if ( ! is_string( $data ) && ! is_resource( $data ) ) {
            $data = var_export( $data, true );
        }

        error_log( $data );
    }

    /**
     * Checks if a single local css/js file can be minified and returns source if so.
     *
     * @param string $filepath Filepath.
     *
     * @return bool|string to be minified code or false.
     */
    protected function prepare_minify_single( $filepath )
    {
        // Decide what we're dealing with, return false if we don't know.
        if ( autoptimizeUtils::str_ends_in( $filepath, '.js' ) ) {
            $type = 'js';
        } elseif ( autoptimizeUtils::str_ends_in( $filepath, '.css' ) ) {
            $type = 'css';
        } else {
            return false;
        }

        // Bail if it looks like its already minifed (by having -min or .min in filename).
        $minified_variants = array(
            '-min.' . $type,
            '.min.' . $type,
        );
        foreach ( $minified_variants as $ending ) {
            if ( autoptimizeUtils::str_ends_in( $filepath, $ending ) && true === apply_filters( 'autoptimize_filter_base_prepare_exclude_minified', true ) ) {
                return false;
            }
        }

        // Get file contents, bail if empty.
        $contents = file_get_contents( $filepath );

        return $contents;
    }

    /**
     * Given an autoptimizeCache instance returns the (maybe cdn-ed) url of
     * the cached file.
     *
     * @param autoptimizeCache $cache autoptimizeCache instance.
     *
     * @return string
     */
    protected function build_minify_single_url( autoptimizeCache $cache )
    {
        $url = AUTOPTIMIZE_CACHE_URL . $cache->getname();

        // CDN-replace the resulting URL if needed...
        $url = $this->url_replace_cdn( $url );

        return $url;
    }
}
PK.�1\� �P��autoptimizeCacheChecker.phpnu�[���<?php
/**
 * CacheChecker - new in AO 2.0
 *
 * Daily cronned job (filter to change freq. + filter to disable).
 * Checks if cachesize is > 0.5GB (size is filterable), if so, an option is set which controls showing an admin notice.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCacheChecker
{
    const SCHEDULE_HOOK = 'ao_cachechecker';

    public function __construct()
    {
    }

    public function run()
    {
        $this->add_hooks();
    }

    public function add_hooks()
    {
        if ( is_admin() ) {
            add_action( 'plugins_loaded', array( $this, 'setup' ) );
        }
        add_action( self::SCHEDULE_HOOK, array( $this, 'cronjob' ) );
        add_action( 'admin_notices', array( $this, 'show_admin_notice' ) );
    }

    public function setup()
    {
        $do_cache_check = (bool) apply_filters( 'autoptimize_filter_cachecheck_do', true );
        $schedule       = wp_get_schedule( self::SCHEDULE_HOOK );
        $frequency      = apply_filters( 'autoptimize_filter_cachecheck_frequency', 'twicedaily' );
        if ( ! in_array( $frequency, array( 'hourly', 'twicedaily', 'daily', 'weekly', 'monthly' ) ) ) {
            $frequency = 'twicedaily';
        }
        if ( $do_cache_check && ( ! $schedule || $schedule !== $frequency ) ) {
            if ( $schedule ) {
                wp_clear_scheduled_hook( self::SCHEDULE_HOOK );
            }
            wp_schedule_event( time(), $frequency, self::SCHEDULE_HOOK );
        } elseif ( $schedule && ! $do_cache_check ) {
            wp_clear_scheduled_hook( self::SCHEDULE_HOOK );
        }
    }

    public function cronjob()
    {
        // Check cachesize and act accordingly.
        $max_size       = (int) apply_filters( 'autoptimize_filter_cachecheck_maxsize', 536870912 );
        $do_cache_check = (bool) apply_filters( 'autoptimize_filter_cachecheck_do', true );
        $stat_array     = autoptimizeCache::stats();
        $cache_size     = round( $stat_array[1] );
        if ( ( $cache_size > $max_size ) && ( $do_cache_check ) ) {
            autoptimizeOptionWrapper::update_option( 'autoptimize_cachesize_notice', true );
            if ( apply_filters( 'autoptimize_filter_cachecheck_sendmail', true ) ) {
                $home_url  = esc_url( home_url() );
                $ao_mailto = apply_filters( 'autoptimize_filter_cachecheck_mailto', autoptimizeOptionWrapper::get_option( 'admin_email', '' ) );

                $ao_mailsubject = esc_html__( 'Autoptimize cache size warning', 'autoptimize' ) . ' (' . $home_url . ')';
                $ao_mailbody    = esc_html__( 'Autoptimize\'s cache size is getting big, consider purging the cache. Have a look at https://wordpress.org/plugins/autoptimize/faq/ to see how you can keep the cache size under control.', 'autoptimize' ) . ' (site: ' . $home_url . ')';

                if ( ! empty( $ao_mailto ) ) {
                    $ao_mailresult = wp_mail( $ao_mailto, $ao_mailsubject, $ao_mailbody );
                    if ( ! $ao_mailresult ) {
                        error_log( 'Autoptimize could not send cache size warning mail.' );
                    }
                }
            }
        }

        // Check if 3rd party services (e.g. image proxy) are up.
        autoptimizeUtils::check_service_availability();

        // Nukes advanced cache clearing artifacts if they exists...
        autoptimizeCache::delete_advanced_cache_clear_artifacts();

        // Check image optimization stats.
        autoptimizeImages::instance()->query_img_provider_stats();
    }

    public function show_admin_notice()
    {
        if ( (bool) autoptimizeOptionWrapper::get_option( 'autoptimize_cachesize_notice', false ) && current_user_can( 'manage_options' ) ) {
            echo '<div class="notice notice-warning"><p>';
            // Translators: first two variables are strong tags, 3rd is link to the AO FAQ and the 4th closes that tag.
            printf( esc_html__( '%1$sAutoptimize\'s cache size is getting big%2$s, consider purging the cache. Have a look at %3$sthe Autoptimize FAQ%4$s to see how you can keep the cache size under control.', 'autoptimize' ), '<strong>', '</strong>', '<a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank" rel="noopener noreferrer">', '</a>' );
            echo '</p></div>';
            autoptimizeOptionWrapper::update_option( 'autoptimize_cachesize_notice', false );
        }

        // Notice for image proxy usage.
        $_imgopt_notice = autoptimizeImages::instance()->get_imgopt_status_notice_wrapper();
        if ( current_user_can( 'manage_options' ) && is_array( $_imgopt_notice ) && array_key_exists( 'status', $_imgopt_notice ) && in_array( $_imgopt_notice['status'], array( 1, -1, -2, -3 ) ) ) {
            $_dismissible = 'ao-img-opt-notice-';
            $_hide_notice = '7';

            if ( -1 == $_imgopt_notice['status'] || -2 == $_imgopt_notice['status'] || -3 == $_imgopt_notice['status'] ) {
                $_hide_notice = '1';
            }

            $_imgopt_notice_dismissible = apply_filters( 'autoptimize_filter_imgopt_notice_dismissable', $_dismissible . $_hide_notice );

            if ( $_imgopt_notice && PAnD::is_admin_notice_active( $_imgopt_notice_dismissible ) ) {
                echo '<div class="notice notice-warning is-dismissible" data-dismissible="' . $_imgopt_notice_dismissible . '"><p><strong>' . esc_html__( 'Autoptimize', 'autoptimize' ) . '</strong>: ' . $_imgopt_notice['notice'] . '</p></div>';
            }
        }
    }
}
PK.�1\:t��88autoptimizeSpeedupper.phpnu�[���<?php
/**
 * Autoptimize SpeedUp; minify & cache each JS/ CSS separately
 * new in Autoptimize 2.2
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeSpeedupper
{
    public function __construct()
    {
        $this->add_hooks();
    }

    public function add_hooks()
    {
        if ( apply_filters( 'autoptimize_js_do_minify', true ) ) {
            add_filter( 'autoptimize_js_individual_script', array( $this, 'js_snippetcacher' ), 10, 2 );
            add_filter( 'autoptimize_js_after_minify', array( $this, 'js_cleanup' ), 10, 1 );
        }
        if ( apply_filters( 'autoptimize_css_do_minify', true ) ) {
            add_filter( 'autoptimize_css_individual_style', array( $this, 'css_snippetcacher' ), 10, 2 );
            add_filter( 'autoptimize_css_after_minify', array( $this, 'css_cleanup' ), 10, 1 );
        }
    }

    public function js_snippetcacher( $jsin, $jsfilename )
    {
        $md5hash = 'snippet_' . md5( $jsin );
        $ccheck  = new autoptimizeCache( $md5hash, 'js' );
        if ( $ccheck->check() ) {
            $scriptsrc = $ccheck->retrieve();
        } else {
            if ( false === ( strpos( $jsfilename, 'min.js' ) ) && ( str_replace( apply_filters( 'autoptimize_filter_js_consider_minified', false ), '', $jsfilename ) === $jsfilename ) ) {
                $tmp_jscode = trim( JSMin::minify( $jsin ) );
                if ( ! empty( $tmp_jscode ) ) {
                    $scriptsrc = $tmp_jscode;
                    unset( $tmp_jscode );
                } else {
                    $scriptsrc = $jsin;
                }
            } else {
                // Removing comments, linebreaks and stuff!
                $scriptsrc = preg_replace( '#^\s*\/\/.*$#Um', '', $jsin );
                $scriptsrc = preg_replace( '#^\s*\/\*[^!].*\*\/\s?#Us', '', $scriptsrc );
                $scriptsrc = preg_replace( "#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $scriptsrc );
            }

            $last_char = substr( $scriptsrc, -1, 1 );
            if ( ';' !== $last_char && '}' !== $last_char ) {
                $scriptsrc .= ';';
            }

            if ( ! empty( $jsfilename ) && str_replace( apply_filters( 'autoptimize_filter_js_speedup_cache', false ), '', $jsfilename ) === $jsfilename ) {
                // Don't cache inline CSS or if filter says no!
                $ccheck->cache( $scriptsrc, 'text/javascript' );
            }
        }
        unset( $ccheck );

        return $scriptsrc;
    }

    public function css_snippetcacher( $cssin, $cssfilename )
    {
        $md5hash = 'snippet_' . md5( $cssin );
        $ccheck  = new autoptimizeCache( $md5hash, 'css' );
        if ( $ccheck->check() ) {
            $stylesrc = $ccheck->retrieve();
        } else {
            if ( ( false === strpos( $cssfilename, 'min.css' ) ) && ( str_replace( apply_filters( 'autoptimize_filter_css_consider_minified', false ), '', $cssfilename ) === $cssfilename ) ) {
                $cssmin   = new autoptimizeCSSmin();
                $tmp_code = trim( $cssmin->run( $cssin ) );

                if ( ! empty( $tmp_code ) ) {
                    $stylesrc = $tmp_code;
                    unset( $tmp_code );
                } else {
                    $stylesrc = $cssin;
                }
            } else {
                // .min.css -> no heavy-lifting, just some cleanup!
                $stylesrc = preg_replace( '#^\s*\/\*[^!].*\*\/\s?#Us', '', $cssin );
                $stylesrc = preg_replace( "#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $stylesrc );
                $stylesrc = autoptimizeStyles::fixurls( $cssfilename, $stylesrc );
            }
            if ( ! empty( $cssfilename ) && ( str_replace( apply_filters( 'autoptimize_filter_css_speedup_cache', false ), '', $cssfilename ) === $cssfilename ) ) {
                // Only caching CSS if it's not inline and is allowed by filter!
                $ccheck->cache( $stylesrc, 'text/css' );
            }
        }
        unset( $ccheck );

        return $stylesrc;
    }

    public function css_cleanup( $cssin )
    {
        // Speedupper results in aggregated CSS not being minified, so the filestart-marker AO adds when aggregating needs to be removed.
        return trim( str_replace( array( '/*FILESTART*/', '/*FILESTART2*/' ), '', $cssin ) );
    }

    public function js_cleanup( $jsin )
    {
        return trim( $jsin );
    }
}
PK.�1\}S�##autoptimizeOptionWrapper.phpnu�[���<?php
/**
 * Autoptimize options handler.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * This class takes care of the set and get of option for standalone and multisite WordPress instances.
 */
class autoptimizeOptionWrapper {
    /**
     * Constructor, add filter on saving options.
     */
    public function __construct() {
    }

    /**
     * Ensure that is_plugin_active_for_network function is declared.
     */
    public static function maybe_include_plugin_functions() {
        if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
            include_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
    }

    /**
     * Retrieves the option in standalone and multisite instances.
     *
     * @param string $option  Name of option to retrieve. Expected to not be SQL-escaped.
     * @param mixed  $default Optional. Default value to return if the option does not exist.
     * @return mixed Value set for the option.
     */
    public static function get_option( $option, $default = false ) {
        if ( is_multisite() && self::is_ao_active_for_network() ) {
            static $configuration_per_site = null;
            if ( null === $configuration_per_site || defined( 'TEST_MULTISITE_FORCE_AO_ON_NETWORK' ) ) {
                $configuration_per_site = get_network_option( get_main_network_id(), 'autoptimize_enable_site_config', 'on' );
                if ( null === $configuration_per_site ) {
                    // Config per site is off, set as empty string to make sure the var it is not null any more so it can be cached.
                    $configuration_per_site = '';
                }
            }
        } else {
            // Kind of dummy value as when not on multisite or if AO not network enabled, config is always on site-level.
            $configuration_per_site = 'on';
        }

        // This is always a network setting, it is on by default to ensure settings are available at site level unless explicitly turned off.
        if ( 'autoptimize_enable_site_config' === $option ) {
            return $configuration_per_site;
        }

        // If the plugin is network activated and our per site setting is not on, use the network configuration.
        if ( is_multisite() && self::is_ao_active_for_network() && ( 'on' !== $configuration_per_site || is_network_admin() ) ) {
            return get_network_option( get_main_network_id(), $option, $default );
        }

        return get_option( $option, $default );
    }

    /**
     * Saves the option in standalone and multisite instances.
     *
     * @param string      $option   Option name. Expected to not be SQL-escaped.
     * @param mixed       $value    Option value. Must be serializable if non-scalar. Expected to not be SQL-escaped.
     * @param string|bool $autoload Optional. Whether to load the option when WordPress starts up. For existing options,
     *                              `$autoload` can only be updated using `update_option()` if `$value` is also changed.
     *                              Accepts 'yes'|true to enable or 'no'|false to disable. For non-existent options,
     *                              the default value is 'yes'. Default null.
     * @return bool False if value was not updated and true if value was updated.
     */
    public static function update_option( $option, $value, $autoload = null ) {
        if ( self::is_ao_active_for_network() && is_network_admin() ) {
            return update_network_option( get_main_network_id(), $option, $value );
        } elseif ( 'autoptimize_enable_site_config' !== $option ) {
            return update_option( $option, $value, $autoload );
        }
    }

    /**
     * Use the pre_update_option filter to check if the option to be saved if from autoptimize and
     * in that case, take care of multisite case.
     */
    public static function check_multisite_on_saving_options() {
        if ( self::is_ao_active_for_network() ) {
            add_filter( 'pre_update_option', 'autoptimizeOptionWrapper::update_autoptimize_option_on_network', 10, 3 );
        }
    }

    /**
     * The actual magic to differentiate between network options and per-site options.
     *
     * @param mixed  $value     Option value.
     * @param string $option    Option name.
     * @param string $old_value Old value.
     */
    public static function update_autoptimize_option_on_network( $value, $option, $old_value ) {
        if ( strpos( $option, 'autoptimize_' ) === 0 && self::is_options_from_network_admin() ) {
            if ( self::is_ao_active_for_network() ) {
                update_network_option( get_main_network_id(), $option, $value );
                // Return old value, to stop update_option logic.
                return $old_value;
            }
            if ( apply_filters( 'autoptimize_filter_optionwrapper_wp_cache_delete', false ) ) {
                // in some (rare) cases options seem to get stuck in WP's Object cache, this should clear it there.
                wp_cache_delete( $option );
            }
        }
        return $value;
    }

    /**
     * As options are POST-ed to wp-admin/options.php checking is_network_admin() does not
     * work (yet). Instead we compare the network_admin_url with the _wp_http_referer
     * (which should always be available as part of a hidden form field).
     */
    public static function is_options_from_network_admin() {
        static $_really_is_network_admin = null;

        if ( null === $_really_is_network_admin ) {
            if ( array_key_exists( '_wp_http_referer', $_POST ) && strpos( network_admin_url( 'settings.php' ), strtok( $_POST['_wp_http_referer'], '?' ) ) !== false ) {
                $_really_is_network_admin = true;
            } else {
                $_really_is_network_admin = false;
            }
        }

        return $_really_is_network_admin;
    }

    /**
     * Function to check if AO (including beta) is active for network.
     */
    public static function is_ao_active_for_network() {
        static $_is_ao_active_for_network = null;
        if ( null === $_is_ao_active_for_network || defined( 'TEST_MULTISITE_FORCE_AO_ON_NETWORK' ) ) {
            self::maybe_include_plugin_functions();
            if ( is_plugin_active_for_network( 'autoptimize/autoptimize.php' ) || is_plugin_active_for_network( 'autoptimize-beta/autoptimize.php' ) || defined( 'TEST_MULTISITE_FORCE_AO_ON_NETWORK' ) ) {
                $_is_ao_active_for_network = true;
            } else {
                $_is_ao_active_for_network = false;
            }
        }
        return $_is_ao_active_for_network;
    }
}
new autoptimizeOptionWrapper();
PK.�1\D����autoptimizeCompatibility.phpnu�[���<?php
/**
 * Multiple compatibility snippets to ensure important/ stubborn plugins work out of the box.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCompatibility
{
    /**
     * Options.
     *
     * @var array
     */
    protected $conf = array();

    /**
     * Constructor.
     */
    public function __construct()
    {
        if ( ! is_admin() && ! defined( 'DOING_CRON' ) ) {
            $this->conf = autoptimizeConfig::instance();
            $this->run();
        }
    }

    /**
     * Runs multiple compatibility snippets to ensure important plugins work out of the box.
     */
    public function run()
    {
        // Edit with Elementor in frontend admin menu (so for editors/ administrators) needs JS opt. disabled to appear & function.
        if ( defined( 'ELEMENTOR_VERSION' ) && is_user_logged_in() && current_user_can( 'edit_posts' ) && apply_filters( 'autoptimize_filter_compatibility_editelementor_active', true ) ) {
            add_filter( 'autoptimize_filter_js_noptimize', '__return_true' );
        }

        // Revslider; jQuery should not be deferred + exclude all revslider JS.
        if ( defined( 'RS_REVISION' ) && $this->conf->get( 'autoptimize_js' ) && true == $this->inline_js_config_checker() && apply_filters( 'autoptimize_filter_compatibility_revslider_active', true ) ) {
            add_filter(
                'autoptimize_filter_js_exclude',
                function( $js_excl = '', $html = '' ) {
                    $revslider_excl = 'revslider, setREVStartSize, window.RSIW, window.RS_MODULES, jquery.min.js';
                    if ( ! empty( $html ) && false !== strpos( $html, '<rs-slides' ) ) {
                        if ( is_array( $js_excl ) ) {
                            $js_excl = implode( ',', $js_excl );
                        }

                        $js_excl .= ',' . $revslider_excl;
                    }
                    return $js_excl;
                },
                11,
                2
            );
        }

        // Revslider; remove revslider JS if no slides in HTML for non-logged in users.
        if ( defined( 'RS_REVISION' ) && $this->conf->get( 'autoptimize_js' ) && false === is_user_logged_in() && apply_filters( 'autoptimize_filter_compatibility_revslider_remover_active', true ) ) {
            add_filter(
                'autoptimize_filter_js_removables',
                function( $to_remove = '', $html = '' ) {
                    if ( ! empty( $html ) && false === strpos( $html, '<rs-slides' ) ) {
                        $to_remove .= 'plugins/revslider, setREVStartSize, window.RSIW, window.RS_MODULES';
                    }

                    return $to_remove;
                },
                11,
                2
            );
        }

        // Exclude jQuery if inline JS is found that requires jQuery.
        if ( $this->inline_js_config_checker() && false === strpos( $this->conf->get( 'autoptimize_js_exclude' ), 'jquery.min.js' ) && apply_filters( 'autoptimize_filter_compatibility_inline_jquery', true ) ) {
            add_filter(
                'autoptimize_filter_js_exclude',
                function( $js_excl = '', $html = '' ) {
                    if ( ! empty( $html ) && preg_match( '/<script[^>]*>[^<]*(jQuery|\$)\([^<]*<\/script>/Usm', $html ) ) {
                        if ( is_array( $js_excl ) ) {
                            $js_excl = implode( ',', $js_excl );
                        }

                        if ( false === strpos( $js_excl, 'jquery.min.js' ) ) {
                            $js_excl .= ', jquery.min.js';
                        }

                        // also exclude jquery.js if for whatever reason that is still used.
                        if ( false === strpos( $js_excl, 'jquery.js' ) ) {
                            $js_excl .= ', jquery.js';
                        }
                    }
                    return $js_excl;
                },
                12,
                2
            );
        }

        // Make JS-based Gutenberg blocks work OOTB.
        if ( $this->inline_js_config_checker() && apply_filters( 'autoptimize_filter_compatibility_gutenberg_js', true ) ) {
            add_filter(
                'autoptimize_filter_js_exclude',
                function( $js_excl = '', $html = '' ) {
                    if ( ! empty( $html ) && false !== strpos( $html, 'wp.i18n' ) || false !== strpos( $html, 'wp.apiFetch' ) || false !== strpos( $html, 'window.lodash' ) ) {
                        if ( is_array( $js_excl ) ) {
                            $js_excl = implode( ',', $js_excl );
                        }

                        if ( false === strpos( $js_excl, 'jquery.min.js' ) ) {
                            $js_excl .= ', jquery.min.js';
                        }

                        if ( false === strpos( $js_excl, 'wp-includes/js/dist' ) ) {
                            $js_excl .= ', wp-includes/js/dist';
                        }
                    }
                    return $js_excl;
                },
                13,
                2
            );
        }
    }

    public function inline_js_config_checker() {
        static $inline_js_flagged = null;

        if ( null === $inline_js_flagged ) {
            if ( ( $this->conf->get( 'autoptimize_js_aggregate' ) || apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) && apply_filters( 'autoptimize_js_include_inline', $this->conf->get( 'autoptimize_js_include_inline' ) ) ) {
                // if all files and also inline JS are aggregated we don't have to worry about inline JS.
                $inline_js_flagged = false;
            } else if ( apply_filters( 'autoptimize_filter_js_defer_not_aggregate', $this->conf->get( 'autoptimize_js_defer_not_aggregate' ) ) && apply_filters( 'autoptimize_js_filter_defer_inline', $this->conf->get( 'autoptimize_js_defer_inline' ) ) ) {
                // and when not aggregating but deferring all including inline JS, then all is OK too.
                $inline_js_flagged = false;
            }

            // in all other cases we need to pay attention to inline JS requiring src'ed JS to be available.
            $inline_js_flagged = true;
        }

        return $inline_js_flagged;
    }
}
PK.�1\�����autoptimizeCLI.phpnu�[���<?php
/**
 * WP-CLI commands for Autoptimize.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

// This is a WP-CLI command, so bail if it's not available.
if ( ! defined( 'WP_CLI' ) ) {
    return;
}

class autoptimizeCLI extends \WP_CLI_Command
{
    /**
     * Clears the cache.
     *
     * @subcommand clear
     *
     * @param array $args Args.
     * @param array $args_assoc Associative args.
     *
     * @return void
     */
    public function clear( $args, $args_assoc ) {
        WP_CLI::line( esc_html__( 'Flushing the cache...', 'autoptimize' ) );
        autoptimizeCache::clearall();
        WP_CLI::success( esc_html__( 'Cache flushed.', 'autoptimize' ) );
    }
}

WP_CLI::add_command( 'autoptimize', 'autoptimizeCLI' );
PK.�1\S�5��autoptimizeHTML.phpnu�[���<?php
/**
 * Handles minifying HTML markup.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeHTML extends autoptimizeBase
{
    /**
     * Whether HTML comments are kept.
     *
     * @var bool
     */
    private $keepcomments = false;

    /**
     * Whether inline CSS/ JS is minified.
     *
     * @var bool
     */
    private $minify_inline = false;

    /**
     * Whether to force xhtml compatibility.
     *
     * @var bool
     */
    private $forcexhtml = false;

    /**
     * Things to exclude from being minifed.
     *
     * @var array
     */
    private $exclude = array(
        '<!-- ngg_resource_manager_marker -->',
        '<!--noindex-->',
        '<!--/noindex-->',
    );

    public function read( $options )
    {
        // Remove the HTML comments?
        $this->keepcomments = (bool) $options['keepcomments'];

        // Filter to force xhtml.
        $this->forcexhtml = (bool) apply_filters( 'autoptimize_filter_html_forcexhtml', false );

        // minify inline JS/ CSS.
        $this->minify_inline = (bool) apply_filters( 'autoptimize_html_minify_inline_js_css', $options['minify_inline'] );

        // Filterable strings to be excluded from HTML minification.
        $exclude = apply_filters( 'autoptimize_filter_html_exclude', '' );
        if ( '' !== $exclude ) {
            $exclude_arr   = array_filter( array_map( 'trim', explode( ',', $exclude ) ) );
            $this->exclude = array_merge( $exclude_arr, $this->exclude );
        }

        // Nothing else for HTML!
        return true;
    }

    /**
     * Minifies HTML.
     *
     * @return bool
     */
    public function minify()
    {
        $noptimize = apply_filters( 'autoptimize_filter_html_noptimize', false, $this->content );
        if ( $noptimize ) {
            return false;
        }

        // Wrap the to-be-excluded strings in noptimize tags.
        foreach ( $this->exclude as $str ) {
            if ( false !== strpos( $this->content, $str ) ) {
                $replacement   = '<!--noptimize-->' . $str . '<!--/noptimize-->';
                $this->content = str_replace( $str, $replacement, $this->content );
            }
        }

        // Noptimize.
        $this->content = $this->hide_noptimize( $this->content );

        // Preparing options for Minify_HTML.
        $options = array( 'keepComments' => $this->keepcomments );
        if ( $this->forcexhtml ) {
            $options['xhtml'] = true;
        }

        // Optionally minify inline JS & CSS.
        if ( $this->minify_inline ) {
            if ( false != autoptimizeOptionWrapper::get_option( 'autoptimize_js' ) && false != autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_js_optimize' ) && true !== apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content ) && false === strpos( $this->content, 'text/template' ) ) {
                $options['jsMinifier']  = 'JSMin::minify';
            }
            if ( false != autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) && false != autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_css_optimize' ) && true !== apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content ) ) {
                $options['cssMinifier'] = 'autoptimizeCSSmin::minify';
            }
        }

        $tmp_content = AO_Minify_HTML::minify( $this->content, $options );
        if ( ! empty( $tmp_content ) ) {
            $this->content = $tmp_content;
            unset( $tmp_content );
        }

        // Restore noptimize.
        $this->content = $this->restore_noptimize( $this->content );

        // Remove the noptimize-wrapper from around the excluded strings.
        foreach ( $this->exclude as $str ) {
            $replacement = '<!--noptimize-->' . $str . '<!--/noptimize-->';
            if ( false !== strpos( $this->content, $replacement ) ) {
                $this->content = str_replace( $replacement, $str, $this->content );
            }
        }

        // Revslider data attribs somehow suffer from HTML optimization, this fixes that!
        if ( class_exists( 'RevSlider' ) && apply_filters( 'autoptimize_filter_html_dataattrib_cleanup', false ) ) {
            $this->content = preg_replace( '#\n(data-.*$)\n#Um', ' $1 ', $this->content );
            $this->content = preg_replace( '#<[^>]*(=\"[^"\'<>\s]*\")(\w)#', '$1 $2', $this->content );
        }

        return true;
    }

    /**
     * Doesn't do much in case of HTML (no cache in css/js sense there)
     *
     * @return true
     */
    public function cache()
    {
        return true;
    }

    /**
     * Returns the HTML markup.
     *
     * @return string
     */
    public function getcontent()
    {
        return $this->content;
    }
}
PK.�1\�u�:�:autoptimizeCriticalCSSBase.phpnu�[���<?php
/**
 * Critical CSS base file (initializes all ccss files).
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCriticalCSSBase {
    /**
     * Main plugin filepath.
     * Used for activation/deactivation/uninstall hooks.
     *
     * @var string
     */
    protected $filepath = null;

    /**
     * Critical CSS options
     *
     * @var array
     */
    protected $_options = null;

    /**
     * Core object
     *
     * @var object
     */
    protected $_core = null;

    /**
     * Cron object
     *
     * @var object
     */
    protected $_cron = null;

    /**
     * Settings object
     *
     * @var object
     */
    protected $_settings = null;

    /**
     * Enqueue object
     *
     * @var object
     */
    protected $_enqueue = null;

    public function __construct()
    {
        // define constant, but only once.
        if ( ! defined( 'AO_CCSS_DIR' ) ) {
            // Define a constant with the directory to store critical CSS in.
            if ( is_multisite() ) {
                $blog_id = get_current_blog_id();
                define( 'AO_CCSS_DIR', WP_CONTENT_DIR . '/uploads/ao_ccss/' . $blog_id . '/' );
            } else {
                define( 'AO_CCSS_DIR', WP_CONTENT_DIR . '/uploads/ao_ccss/' );
            }
        }
        if ( ! defined( 'AO_CCSS_VER' ) ) {
            // Define plugin version.
            define( 'AO_CCSS_VER', 'AO_' . AUTOPTIMIZE_PLUGIN_VERSION );

            // Define constants for criticalcss.com base path and API endpoints.
            // fixme: AO_CCSS_URL should be read from the autoptimize availability json stored as option.
            define( 'AO_CCSS_URL', 'https://criticalcss.com' );
            define( 'AO_CCSS_API', apply_filters( 'autoptimize_filter_ccss_service_url', AO_CCSS_URL . '/api/premium/' ) );
            define( 'AO_CCSS_SLEEP', 10 );
        }

        // Define support files locations, in case they are not already defined.
        if ( ! defined( 'AO_CCSS_LOCK' ) ) {
            define( 'AO_CCSS_LOCK', AO_CCSS_DIR . 'queue.lock' );
        }
        if ( ! defined( 'AO_CCSS_LOG' ) ) {
            define( 'AO_CCSS_LOG', AO_CCSS_DIR . 'queuelog.html' );
        }
        if ( ! defined( 'AO_CCSS_DEBUG' ) ) {
            define( 'AO_CCSS_DEBUG', AO_CCSS_DIR . 'queue.json' );
        }

        $this->filepath = __FILE__;

        // Add keychecker action for scheduled use.
        add_action( 'ao_ccss_keychecker', array( $this, 'ao_ccss_check_key' ) );
    }

    public function setup() {
        // check if we need to upgrade.
        $this->check_upgrade();

        // add/ remove scheduled jobs.
        if ( $this->is_api_active() ) {
            // make sure the 10 minutes cron schedule is added.
            add_filter( 'cron_schedules', array( $this, 'ao_ccss_interval' ) );

            if ( ! wp_next_scheduled( 'ao_ccss_queue' ) ) {
                // make sure ao_ccss_queue is scheduled OK if an API key is active.
                wp_schedule_event( time(), apply_filters( 'ao_ccss_queue_schedule', 'ao_ccss' ), 'ao_ccss_queue' );
            }

            if ( ! wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
                // and schedule maintenance job.
                wp_schedule_event( time(), 'twicedaily', 'ao_ccss_maintenance' );
            }

            if ( wp_next_scheduled( 'ao_ccss_keychecker' ) ) {
                // api is active now, no need to check key as it is checked by using the API.
                wp_clear_scheduled_hook( 'ao_ccss_keychecker' );
            }
        } else {
            if ( wp_next_scheduled( 'ao_ccss_queue' ) ) {
                wp_clear_scheduled_hook( 'ao_ccss_queue' );
            }

            if ( wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
                wp_clear_scheduled_hook( 'ao_ccss_maintenance' );
            }

            // add keychecker logic if api is not active but we have a key so maybe this is a temporary issue, check if key is OK daily.
            $ao_ccss_key   = $this->get_option( 'key' );
            if ( ! empty( $ao_ccss_key ) && ! wp_next_scheduled( 'ao_ccss_keychecker' ) ) {
                wp_schedule_event( time(), 'twicedaily', 'ao_ccss_keychecker' );
            } else if ( empty( $ao_ccss_key ) && wp_next_scheduled( 'ao_ccss_keychecker' ) ) {
                // edge case: we had a inactive key that was checked daily, but it is now removed, so remove keychecker from schedule.
                wp_clear_scheduled_hook( 'ao_ccss_keychecker' );
            }
        }

        // check/ create AO_CCSS_DIR.
        if ( ! file_exists( AO_CCSS_DIR ) ) {
            $this->create_ao_ccss_dir();
        }
    }

    public function load_requires() {
        // Required libs, core is always needed.
        $this->_core = new autoptimizeCriticalCSSCore();

        if ( ( defined( 'WP_CLI' ) || defined( 'DOING_CRON' ) || is_admin() ) && $this->is_api_active() ) {
            // cron only initiated when doing cron (or wp_cli or is_amdin) and when we have an active API key.
            $this->_cron = new autoptimizeCriticalCSSCron();
        }

        if ( is_admin() ) {
            $this->_settings = new autoptimizeCriticalCSSSettings();
        } else if ( $this->is_api_active() ) {
            // enqueuing only done when not wp-admin and when API is active.
            $this->_enqueue = new autoptimizeCriticalCSSEnqueue();
        }
    }

    /**
     * Log a message via CCSS Core object
     *
     * @param string $msg Message to log.
     * @param int    $lvl Loglevel.
     *
     * @return empty
     */
    public function log( $msg, $lvl ) {
        return $this->_core->ao_ccss_log( $msg, $lvl );
    }

    /**
     * Get viewport from CCSS Core object
     *
     * @return array
     */
    public function viewport() {
        return $this->_core->ao_ccss_viewport();
    }

    /**
     * Check CCSS contents from Core object
     *
     * @param string $ccss Critical CSS to be checked.
     *
     * @return bool
     */
    public function check_contents( $ccss ) {
        return $this->_core->ao_ccss_check_contents( $ccss );
    }

    /**
     * Get key status from Core object
     *
     * @param bool $render Indicates if key status is to be rendered.
     *
     * @return array
     */
    public function key_status( $render ) {
        return $this->_core->ao_ccss_key_status( $render );
    }

    /**
     * Return valid types from core object
     *
     * @return array
     */
    public function get_types() {
        return $this->_core->get_types();
    }

    /**
     * Run enqueue in CCSS Enqueue object
     *
     * @param string $hash Hash (default empty).
     * @param string $path Path (default empty).
     * @param string $type (default is_page).
     */
    public function enqueue( $hash = '', $path = '', $type = 'is_page' ) {
        // Enqueue is sometimes required on wp-admin requests, load it just-in-time.
        if ( is_null( $this->_enqueue ) && $this->is_api_active() ) {
            $this->_enqueue = new autoptimizeCriticalCSSEnqueue();
        }

        return $this->_enqueue->ao_ccss_enqueue( $hash, $path, $type );
    }

    /**
     * Check auto-rules in CCSS Settings object
     */
    public function has_autorules() {
        return $this->_settings->ao_ccss_has_autorules();
    }

    /**
     * Get a Critical CSS option
     *
     * @param string $name The option name.
     *
     * @return mixed
     */
    public function get_option( $name ) {
        if ( is_null( $this->_options ) ) {
            $this->fetch_options();
        }

        if ( isset( $this->_options[ $name ] ) ) {
            return $this->_options[ $name ];
        }

        return null;
    }

    public function flush_options() {
        $this->_options = null;
    }

    protected function fetch_options() {
        if ( ! is_null( $this->_options ) ) {
            return $this->_options;
        }

        $this->_options = array(
            'css_defer'         => autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer' ),
            'css_defer_inline'  => autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer_inline' ),
            'rules_raw'     => get_option( 'autoptimize_ccss_rules', false ),
            'additional'    => get_option( 'autoptimize_ccss_additional' ),
            'queue_raw'     => get_option( 'autoptimize_ccss_queue', false ),
            'viewport'      => get_option( 'autoptimize_ccss_viewport', false ),
            'finclude'      => get_option( 'autoptimize_ccss_finclude', false ),
            'rtimelimit'    => get_option( 'autoptimize_ccss_rtimelimit', '30' ),
            'noptimize'     => get_option( 'autoptimize_ccss_noptimize', false ),
            'debug'         => get_option( 'autoptimize_ccss_debug', false ),
            'key'           => apply_filters( 'autoptimize_filter_ccss_key', get_option( 'autoptimize_ccss_key' ) ),
            'keyst'         => get_option( 'autoptimize_ccss_keyst' ),
            'loggedin'      => get_option( 'autoptimize_ccss_loggedin', '1' ),
            'forcepath'     => get_option( 'autoptimize_ccss_forcepath', '1' ),
            'servicestatus' => get_option( 'autoptimize_service_availablity' ),
            'deferjquery'   => get_option( 'autoptimize_ccss_deferjquery', false ),
            'domain'        => get_option( 'autoptimize_ccss_domain' ),
            'unloadccss'    => get_option( 'autoptimize_ccss_unloadccss', false ),
        );

        if ( strpos( $this->_options['domain'], 'http' ) === false && strpos( $this->_options['domain'], 'uggc' ) === 0 ) {
            $this->_options['domain'] = str_rot13( $this->_options['domain'] );
        } elseif ( strpos( $this->_options['domain'], 'http' ) !== false ) {
            // not rot13'ed yet, do so now (goal; avoid migration plugins change the bound domain).
            update_option( 'autoptimize_ccss_domain', str_rot13( $this->_options['domain'] ) );
        }

        // Setup the rules array.
        if ( empty( $this->_options['rules_raw'] ) ) {
            $this->_options['rules'] = array(
                'paths' => array(),
                'types' => array(),
            );
        } else {
            $this->_options['rules'] = json_decode( $this->_options['rules_raw'], true );
        }

        // Setup the queue array.
        if ( empty( $this->_options['queue_raw'] ) ) {
            $this->_options['queue'] = array();
        } else {
            $this->_options['queue'] = json_decode( $this->_options['queue_raw'], true );
        }

        // Override API key if constant is defined.
        if ( defined( 'AUTOPTIMIZE_CRITICALCSS_API_KEY' ) ) {
            $this->_options['key'] = AUTOPTIMIZE_CRITICALCSS_API_KEY;
        }

        return $this->_options;
    }

    public function on_upgrade() {
        $key = $this->get_option( 'key' );

        // Create the cache directory if it doesn't exist already.
        if ( ! file_exists( AO_CCSS_DIR ) ) {
            $this->create_ao_ccss_dir();
        }

        // Create a scheduled event for the queue.
        if ( $this->is_api_active() && ! wp_next_scheduled( 'ao_ccss_queue' ) ) {
            wp_schedule_event( time(), apply_filters( 'ao_ccss_queue_schedule', 'ao_ccss' ), 'ao_ccss_queue' );
        }

        // Create a scheduled event for log maintenance.
        if ( $this->is_api_active() && ! wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
            wp_schedule_event( time(), 'twicedaily', 'ao_ccss_maintenance' );
        }
    }

    public function check_upgrade() {
        $db_version = get_option( 'autoptimize_ccss_version', '' );
        if ( AO_CCSS_VER !== $db_version ) {
            // check schedules & re-schedule if needed.
            $this->on_upgrade();
            // and update db_version.
            update_option( 'autoptimize_ccss_version', AO_CCSS_VER );
        }
    }

    public function ao_ccss_interval( $schedules ) {
        // Let interval be configurable.
        if ( ! defined( 'AO_CCSS_DEBUG_INTERVAL' ) ) {
            $intsec = 600;
            $inttxt = '10 minutes';
        } else {
            $intsec = AO_CCSS_DEBUG_INTERVAL;
            if ( $intsec >= 120 ) {
                $inttxt = $intsec / 60 . ' minutes';
            } else {
                $inttxt = $intsec . ' second(s)';
            }
            $this->log( 'Using custom WP-Cron interval of ' . $inttxt, 3 );
        }

        // Attach interval to schedule.
        $schedules['ao_ccss'] = array(
            'interval' => $intsec,
            // translators: the variable contains a string describing the insterval.
            'display'  => sprintf( esc_html__( 'Every %s (Autoptimize Crit. CSS)', 'autoptimize' ), $inttxt ),
        );
        return $schedules;
    }

    public function create_ao_ccss_dir() {
        // Make sure dir to write ao_ccss exists and is writable.
        if ( ! is_dir( AO_CCSS_DIR ) ) {
            // TODO: use wp_mkdir_p() ?
            $mkdirresp = @mkdir( AO_CCSS_DIR, 0775, true ); // @codingStandardsIgnoreLine
        } else {
            $mkdirresp = true;
        }

        // Make sure our index.html is there.
        if ( ! is_file( AO_CCSS_DIR . 'index.html' ) ) {
            $fileresp = file_put_contents( AO_CCSS_DIR . 'index.html', '<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>' );
        } else {
            $fileresp = true;
        }

        if ( true === $fileresp && true === $mkdirresp ) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Helper function to determine if there is an active API key.
     *
     * @return bool
     */
    public function is_api_active() {
        // using options instead of more complex $this->key_status (which gave some dependancy issues ... ;-) ).
        $ao_ccss_key   = $this->get_option( 'key' );
        $ao_ccss_keyst = $this->get_option( 'keyst' );

        if ( ! empty( $ao_ccss_key ) && $ao_ccss_keyst && 2 == $ao_ccss_keyst ) {
            return true;
        }

        return false;
    }

    /**
     * Helper function to determine if a rule is MANUAL.
     *
     * @param array $rule Rule to check.
     *
     * @return bool
     */
    public function is_rule_manual( $rule ) {
        if ( is_array( $rule ) && false == $rule['hash'] && false != $rule['file'] ) {
            return true;
        }

        return false;
    }

    /**
     * Scheduled action to check an inactive key. Not part of autoptimizeCriticalCSSCron.php
     * to allow us to only load the main cron logic if we have an active key to begin with.
     */
    public function ao_ccss_check_key() {
        $ao_ccss_key = $this->get_option( 'key' );
        $_result = $this->_core->ao_ccss_key_validation( $ao_ccss_key );
        $_resmsg = ( true === $_result ) ? 'ok' : 'nok';
        $this->log( 'Inactive key checked, result was ' . $_resmsg, 3 );
    }
}
PK.�1\7̀��~�~autoptimizeScripts.phpnu�[���<?php
/**
 * Class for JS optimization.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeScripts extends autoptimizeBase
{

    /**
     * Stores founds scripts.
     *
     * @var array
     */
    private $scripts = array();

    /**
     * Stores to be moved JS.
     *
     * @var array
     */
    private $move = array(
        'first' => array(),
        'last'  => array(),
    );

    /**
     * List of not to be moved JS.
     *
     * @var array
     */
    private $dontmove = array(
        'document.write',
        'html5.js',
        'show_ads.js',
        'google_ad',
        'histats.com/js',
        'statcounter.com/counter/counter.js',
        'ws.amazon.com/widgets',
        'media.fastclick.net',
        '/ads/',
        'comment-form-quicktags/quicktags.php',
        'edToolbar',
        'intensedebate.com',
        'scripts.chitika.net/',
        '_gaq.push',
        'jotform.com/',
        'admin-bar.min.js',
        'GoogleAnalyticsObject',
        'plupload.full.min.js',
        'syntaxhighlighter',
        'adsbygoogle',
        'gist.github.com',
        '_stq',
        'nonce',
        'post_id',
        'data-noptimize',
        'data-cfasync',
        'data-pagespeed-no-defer',
        'logHuman',
        'amp-mobile-version-switcher',
        'data-rocketlazyloadscript',
        'rocket-browser-checker-js-after',
    );

    /**
     * List of to be moved JS.
     *
     * @var array
     */
    private $domove = array(
        'gaJsHost',
        'load_cmc',
        'jd.gallery.transitions.js',
        'swfobject.embedSWF(',
        'tiny_mce.js',
        'tinyMCEPreInit.go',
    );

    /**
     * List of JS that can be moved last (not used any more).
     *
     * @var array
     */
    private $domovelast = array(
        'addthis.com',
        '/afsonline/show_afs_search.js',
        'disqus.js',
        'networkedblogs.com/getnetworkwidget',
        'infolinks.com/js/',
        'jd.gallery.js.php',
        'jd.gallery.transitions.js',
        'swfobject.embedSWF(',
        'linkwithin.com/widget.js',
        'tiny_mce.js',
        'tinyMCEPreInit.go',
    );

    /**
     * Setting CDN base URL.
     *
     * @var string
     */
    public $cdn_url = '';

    /**
     * Setting; aggregate or not.
     *
     * @var bool
     */
    private $aggregate = true;

    /**
     * Setting; if not aggregated, should we defer?
     *
     * @var bool
     */
    private $defer_not_aggregate = false;

    /**
     * Setting; defer inline JS?
     *
     * @var bool
     */
    private $defer_inline = false;

    /**
     * Setting; try/catch wrapping or not.
     *
     * @var bool
     */
    private $trycatch = false;

    /**
     * State; is JS already minified.
     *
     * @var bool
     */
    private $alreadyminified = false;

    /**
     * Setting; force JS in head or not.
     *
     * @var bool
     */
    private $forcehead = true;

    /**
     * Setting; aggregate inline JS or not.
     *
     * @var bool
     */
    private $include_inline = false;

    /**
     * State; holds JS code.
     *
     * @var string
     */
    private $jscode = '';

    /**
     * State; holds URL of JS-file.
     *
     * @var string
     */
    private $url = '';

    /**
     * State; stores rest of HTML if (old) option "only in head" is on.
     *
     * @var string
     */
    private $restofcontent = '';

    /**
     * State; holds md5-hash.
     *
     * @var string
     */
    private $md5hash = '';

    /**
     * Setting (filter); allowlist of to be aggregated JS.
     *
     * @var string
     */
    private $allowlist = '';

    /**
     * Setting (filter); holds JS that should be removed.
     *
     * @var array
     */
    private $jsremovables = array();

    /**
     * Setting (filter); can we inject already minified files after the
     * unminified aggregate JS has been minified.
     *
     * @var bool
     */
    private $inject_min_late = true;

    /**
     * Setting; should excluded JS be minified (if not already).
     *
     * @var bool
     */
    private $minify_excluded = true;

    /**
     * Reads the page and collects script tags.
     *
     * @param array $options all options.
     */
    public function read( $options )
    {
        $noptimize_js = false;

        // If page/ post check post_meta to see if optimize is off.
        if ( false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_js_optimize' ) ) {
            $noptimize_js = true;
        }

        // And a filter to enforce JS noptimize.
        $noptimize_js = apply_filters( 'autoptimize_filter_js_noptimize', $noptimize_js, $this->content );

        // And finally bail if noptimize_js is true.
        if ( $noptimize_js ) {
            return false;
        }

        // only optimize known good JS?
        $allowlist_js = apply_filters( 'autoptimize_filter_js_allowlist', '', $this->content );
        if ( ! empty( $allowlist_js ) ) {
            $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_js ) ) );
        }

        // is there JS we should simply remove?
        $removable_js = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
        if ( ! empty( $removable_js ) ) {
            $this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removable_js ) ) );
        }

        // only header?
        if ( apply_filters( 'autoptimize_filter_js_justhead', $options['justhead'] ) ) {
            $content             = explode( '</head>', $this->content, 2 );
            $this->content       = $content[0] . '</head>';
            $this->restofcontent = $content[1];
        }

        // Determine whether we're doing JS-files aggregation or not.
        if ( ! $options['aggregate'] ) {
            $this->aggregate = false;
        }
        // Returning true for "dontaggregate" turns off aggregation.
        if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) {
            $this->aggregate = false;
        }
        // and the filter that should have been there to begin with.
        $this->aggregate = apply_filters( 'autoptimize_filter_js_aggregate', $this->aggregate );

        // Defer when not aggregating.
        if ( false === $this->aggregate && apply_filters( 'autoptimize_filter_js_defer_not_aggregate', $options['defer_not_aggregate'] ) ) {
            $this->defer_not_aggregate = true;
        }

        // Defer inline JS?
        if ( ( true === $this->defer_not_aggregate && apply_filters( 'autoptimize_js_filter_defer_inline', $options['defer_inline'] ) ) || apply_filters( 'autoptimize_js_filter_force_defer_inline', false ) ) {
            $this->defer_inline = true;
        }

        // include inline?
        if ( apply_filters( 'autoptimize_js_include_inline', $options['include_inline'] ) ) {
            $this->include_inline = true;
        }

        // filter to "late inject minified JS", default to true for now (it is faster).
        $this->inject_min_late = apply_filters( 'autoptimize_filter_js_inject_min_late', true );

        // filters to override hardcoded do(nt)move(last) array contents (array in, array out!).
        $this->dontmove   = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove );
        $this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast );
        $this->domove     = apply_filters( 'autoptimize_filter_js_domove', $this->domove );

        // Determine whether excluded files should be minified if not yet so.
        if ( ! $options['minify_excluded'] && $options['aggregate'] ) {
            $this->minify_excluded = false;
        }
        $this->minify_excluded = apply_filters( 'autoptimize_filter_js_minify_excluded', $this->minify_excluded, '' );

        // get extra exclusions settings or filter.
        $exclude_js = $options['js_exclude'];
        $exclude_js = apply_filters( 'autoptimize_filter_js_exclude', $exclude_js, $this->content );

        if ( '' !== $exclude_js ) {
            if ( is_array( $exclude_js ) ) {
                $remove_keys = array_keys( $exclude_js, 'remove' );
                if ( false !== $remove_keys ) {
                    foreach ( $remove_keys as $remove_key ) {
                        unset( $exclude_js[ $remove_key ] );
                        $this->jsremovables[] = $remove_key;
                    }
                }
                $excl_js_arr = array_keys( $exclude_js );
            } else {
                $excl_js_arr = array_filter( array_map( 'trim', explode( ',', $exclude_js ) ) );
            }
            $this->dontmove = array_merge( $excl_js_arr, $this->dontmove );
        }

        // Should we add try-catch?
        if ( $options['trycatch'] ) {
            $this->trycatch = true;
        }

        // force js in head?
        if ( $options['forcehead'] ) {
            $this->forcehead = true;
        } else {
            $this->forcehead = false;
        }

        $this->forcehead = apply_filters( 'autoptimize_filter_js_forcehead', $this->forcehead );

        // get cdn url.
        $this->cdn_url = $options['cdn_url'];

        // noptimize me.
        $this->content = $this->hide_noptimize( $this->content );

        // Save IE hacks.
        $this->content = $this->hide_iehacks( $this->content );

        // comments.
        $this->content = $this->hide_comments( $this->content );

        // Get script files.
        if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) {
            foreach ( $matches[0] as $tag ) {
                // only consider script aggregation for types allowlisted in should_aggregate-function.
                $should_aggregate = $this->should_aggregate( $tag );
                if ( ! $should_aggregate ) {
                    $tag = '';
                    continue;
                }

                if ( preg_match( '#<script[^>]*src=("|\')([^>]*)("|\')#Usmi', $tag, $source ) ) {
                    // non-inline script.
                    if ( $this->isremovable( $tag, $this->jsremovables ) ) {
                        $this->content = str_replace( $tag, '', $this->content );
                        continue;
                    }

                    $orig_tag = null;
                    $url      = current( explode( '?', $source[2], 2 ) );
                    $path     = $this->getpath( $url );
                    if ( false !== $path && preg_match( '#\.js$#', $path ) && $this->ismergeable( $tag ) ) {
                        // ok to optimize, add to array.
                        $this->scripts[] = $path;
                    } else {
                        $orig_tag = $tag;
                        $new_tag  = $tag;

                        // non-mergeable script (excluded or dynamic or external).
                        if ( is_array( $exclude_js ) ) {
                            // should we add flags?
                            foreach ( $exclude_js as $excl_tag => $excl_flags ) {
                                if ( false !== strpos( $orig_tag, $excl_tag ) && in_array( $excl_flags, array( 'async', 'defer' ) ) ) {
                                    $new_tag = str_replace( '<script ', '<script ' . $excl_flags . ' ', $new_tag );
                                }
                            }
                        }

                        // not aggregating but deferring?
                        if ( $this->defer_not_aggregate && false === $this->aggregate && ( str_replace( $this->dontmove, '', $path ) === $path || ( apply_filters( 'autoptimize_filter_js_defer_external', true ) && str_replace( $this->dontmove, '', $orig_tag ) === $orig_tag ) ) && strpos( $new_tag, ' defer' ) === false ) {
                            if ( false !== strpos( $new_tag, ' async' ) && true === apply_filters( 'autoptimize_filter_js_defer_trumps_async', true ) ) {
                                // remove async flag to ensure JS is properly deferred, otherwise the asynced JS might fire 
                                // before deferred inlined JS is executed, off course except filter is set to false which
                                // re-institutes previous behavior.
                                $new_tag = str_replace( array( " async='async'", ' async="async"', ' async=async', ' async' ), '', $new_tag );
                            }
                            
                            if ( false === strpos( $new_tag, ' async' ) ) {
                                // either async wasn't there to begin with or it was removed.
                                // if async is there, the autoptimize_filter_js_defer_trumps_async
                                // filter was set to false and in that case defer should not be added.
                                $new_tag = str_replace( '<script ', '<script defer ', $new_tag );
                            }
                        }

                        // Should we minify the non-aggregated script?
                        // -> if aggregate is on and exclude minify is on
                        // -> if aggregate is off and the file is not in dontmove.
                        if ( $path && $this->minify_excluded ) {
                            $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
                            if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
                                $minified_url = $this->minify_single( $path );
                                if ( ! empty( $minified_url ) ) {
                                    // Replace original URL with minified URL from cache.
                                    $new_tag = str_replace( $url, $minified_url, $new_tag );
                                } elseif ( apply_filters( 'autoptimize_filter_ccsjs_remove_empty_minified_url', false ) ) {
                                    // Remove the original script tag, because cache content is empty but only if filter
                                    // is trued because $minified_url is also false if original JS is minified already.
                                    $new_tag = '';
                                }
                            }
                        }

                        // Check if we still need to CDN (esp. for already minified resources).
                        if ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) {
                            $new_tag = str_replace( $url, $this->url_replace_cdn( $url ), $new_tag );
                        }

                        if ( $this->ismovable( $new_tag ) ) {
                            // can be moved, flags and all.
                            if ( $this->movetolast( $new_tag ) ) {
                                $this->move['last'][] = $new_tag;
                            } else {
                                $this->move['first'][] = $new_tag;
                            }
                        } else {
                            // cannot be moved, so if flag was added re-inject altered tag immediately.
                            if ( ( '' !== $new_tag && $orig_tag !== $new_tag ) || ( '' === $new_tag && apply_filters( 'autoptimize_filter_js_remove_empty_files', false ) ) ) {
                                $this->content = str_replace( $orig_tag, $new_tag, $this->content );
                                $orig_tag      = '';
                            }
                            // and forget about the $tag (not to be touched any more).
                            $tag = '';
                        }
                    }
                } else {
                    // Inline script.
                    if ( $this->isremovable( $tag, $this->jsremovables ) ) {
                        $this->content = str_replace( $tag, '', $this->content );
                        continue;
                    }

                    // unhide comments, as javascript may be wrapped in comment-tags for old times' sake.
                    $tag = $this->restore_comments( $tag );
                    if ( $this->ismergeable( $tag ) && $this->include_inline ) {
                        preg_match( '#<script.*>(.*)</script>#Usmi', $tag, $code );
                        $code            = preg_replace( '#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm', '$1', $code[1] );
                        $code            = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code );
                        $this->scripts[] = 'INLINE;' . $code;
                    } else {
                        $_inline_deferable = apply_filters( 'autoptimize_filters_js_inline_deferable', array( 'nonce', 'post_id', 'syntaxhighlighter' ) );
                        $_inline_dontmove  = array_values( array_diff( $this->dontmove, $_inline_deferable ) );
                        if ( false === $this->defer_inline ) {
                            // Can we move this?
                            $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
                            if ( $this->ismovable( $tag ) || '' !== $autoptimize_js_moveable ) {
                                if ( $this->movetolast( $tag ) || 'last' === $autoptimize_js_moveable ) {
                                    $this->move['last'][] = $tag;
                                } else {
                                    $this->move['first'][] = $tag;
                                }
                            } else {
                                $tag = '';
                            }
                        } else if ( str_replace( $_inline_dontmove, '', $tag ) === $tag && strlen( $tag ) < apply_filters( 'autoptimize_filter_script_defer_inline_maxsize', 200000 ) ) {
                            // defer inline JS by base64 encoding it but only if string is not ridiculously huge (to avoid issues with below regex mainly).
                            preg_match( '#<script(?:[^>](?!id=))*\s*(?:id=(["\'])([^"\']+)\1)*+[^>]*+>(.*?)<\/script>#is', $tag, $match );
                            if ( $match[2] ) {
                                $_id = 'id="' . $match[2] . '" ';
                            } else {
                                $_id = '';
                            }

                            // if "minify inline" is on and if more then 9 spaces or 4 line breaks are found 
                            // in the inline JS then it is likely not minified, so minify before base64-encoding.
                            $_script_contents = $match[3];
                            if ( 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_html_minify_inline', 'off' ) && substr_count( $_script_contents, ' ' ) > 9 && substr_count( $_script_contents, "\n" ) > 4 && true === apply_filters( 'autoptimize_filter_script_defer_inline_minify', true ) ) {
                                $_tmp_script_contents = trim( JSMin::minify( $_script_contents ) );
                                if ( ! empty( $_tmp_script_contents ) ) {
                                    $_script_contents = $_tmp_script_contents;
                                }
                            }

                            // base64 and defer the lot already.
                            $new_tag       = '<script defer ' . $_id . 'src="data:text/javascript;base64,' . base64_encode( $_script_contents ) . '"></script>';
                            $this->content = str_replace( $this->hide_comments( $tag ), $new_tag, $this->content );
                            $tag           = '';
                        } else {
                            $tag = '';
                        }
                    }
                    // Re-hide comments to be able to do the removal based on tag from $this->content.
                    $tag = $this->hide_comments( $tag );
                }

                // Remove the original script tag.
                $this->content = str_replace( $tag, '', $this->content );
            }

            return true;
        }

        // No script files, great ;-) .
        return false;
    }

    /**
     * Determines wheter a certain `<script>` $tag should be aggregated or not.
     *
     * We consider these as "aggregation-safe" currently:
     * - script tags without a `type` attribute
     * - script tags with these `type` attribute values: `text/javascript`, `text/ecmascript`, `application/javascript`,
     * and `application/ecmascript`
     *
     * Everything else should return false.
     *
     * @link https://developer.mozilla.org/en/docs/Web/HTML/Element/script#attr-type
     *
     * @param string $tag Script node & child(ren).
     * @return bool
     */
    public static function should_aggregate( $tag )
    {
        if ( empty( $tag ) ) {
            return false;
        }

        // We're only interested in the type attribute of the <script> tag itself, not any possible
        // inline code that might just contain the 'type=' string...
        $tag_parts = array();
        preg_match( '#<(script[^>]*)>#i', $tag, $tag_parts );
        $tag_without_contents = null;
        if ( ! empty( $tag_parts[1] ) ) {
            $tag_without_contents = $tag_parts[1];
        }

        $has_type = ( strpos( $tag_without_contents, 'type' ) !== false );

        $type_valid = false;
        if ( $has_type ) {
            $type_valid = (bool) preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $tag_without_contents );
        }

        $should_aggregate = false;
        if ( ! $has_type || $type_valid ) {
            $should_aggregate = true;
        }

        return $should_aggregate;
    }

    /**
     * Joins and optimizes JS.
     */
    public function minify()
    {
        foreach ( $this->scripts as $script ) {
            if ( empty( $script ) ) {
                continue;
            }

            // TODO/FIXME: some duplicate code here, can be reduced/simplified.
            if ( preg_match( '#^INLINE;#', $script ) ) {
                // Inline script.
                $script = preg_replace( '#^INLINE;#', '', $script );
                $script = rtrim( $script, ";\n\t\r" ) . ';';
                // Add try-catch?
                if ( $this->trycatch ) {
                    $script = 'try{' . $script . '}catch(e){}';
                }
                $tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, '' );
                if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscript ) ) {
                    $script                = $tmpscript;
                    $this->alreadyminified = true;
                }
                $this->jscode .= "\n" . $script;
            } else {
                // External script.
                if ( false !== $script && file_exists( $script ) && is_readable( $script ) ) {
                    $scriptsrc = file_get_contents( $script );
                    $scriptsrc = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $scriptsrc );
                    $scriptsrc = rtrim( $scriptsrc, ";\n\t\r" ) . ';';
                    // Add try-catch?
                    if ( $this->trycatch ) {
                        $scriptsrc = 'try{' . $scriptsrc . '}catch(e){}';
                    }
                    $tmpscriptsrc = apply_filters( 'autoptimize_js_individual_script', $scriptsrc, $script );
                    if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscriptsrc ) ) {
                        $scriptsrc             = $tmpscriptsrc;
                        $this->alreadyminified = true;
                    } elseif ( $this->can_inject_late( $script ) ) {
                        $scriptsrc = self::build_injectlater_marker( $script, md5( $scriptsrc ) );
                    }
                    $this->jscode .= "\n" . $scriptsrc;
                }
            }
        }

        // Check for already-minified code.
        $this->md5hash = md5( $this->jscode );
        $ccheck        = new autoptimizeCache( $this->md5hash, 'js' );
        if ( $ccheck->check() ) {
            $this->jscode = $ccheck->retrieve();
            return true;
        }
        unset( $ccheck );

        // $this->jscode has all the uncompressed code now.
        if ( true !== $this->alreadyminified ) {
            if ( apply_filters( 'autoptimize_js_do_minify', true ) ) {
                $tmp_jscode = trim( JSMin::minify( $this->jscode ) );
                if ( ! empty( $tmp_jscode ) ) {
                    $this->jscode = $tmp_jscode;
                    unset( $tmp_jscode );
                }
                $this->jscode = $this->inject_minified( $this->jscode );
                $this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode );
                return true;
            } else {
                $this->jscode = $this->inject_minified( $this->jscode );
                return false;
            }
        }

        $this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode );
        return true;
    }

    /**
     * Caches the JS in uncompressed, deflated and gzipped form.
     */
    public function cache()
    {
        $cache = new autoptimizeCache( $this->md5hash, 'js' );
        if ( ! $cache->check() ) {
            // Cache our code.
            $cache->cache( $this->jscode, 'text/javascript' );
        }
        $this->url = AUTOPTIMIZE_CACHE_URL . $cache->getname();
        $this->url = $this->url_replace_cdn( $this->url );
    }

    /**
     * Returns the content.
     */
    public function getcontent()
    {
        // Restore the full content.
        if ( ! empty( $this->restofcontent ) ) {
            $this->content      .= $this->restofcontent;
            $this->restofcontent = '';
        }

        // Add the scripts taking forcehead/ deferred (default) into account.
        if ( $this->forcehead ) {
            $replace_tag = array( '</head>', 'before' );
            $defer       = '';
        } else {
            $replace_tag = array( '</body>', 'before' );
            $defer       = 'defer ';
        }

        $defer   = apply_filters( 'autoptimize_filter_js_defer', $defer );
        $type_js = '';
        if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
            $type_js = 'type="text/javascript" ';
        }

        $bodyreplacementpayload = '<script ' . $type_js . $defer . 'src="' . $this->url . '"></script>';
        $bodyreplacementpayload = apply_filters( 'autoptimize_filter_js_bodyreplacementpayload', $bodyreplacementpayload );

        $bodyreplacement  = implode( '', $this->move['first'] );
        $bodyreplacement .= $bodyreplacementpayload;
        $bodyreplacement .= implode( '', $this->move['last'] );

        $replace_tag = apply_filters( 'autoptimize_filter_js_replacetag', $replace_tag );

        if ( strlen( $this->jscode ) > 0 ) {
            $this->inject_in_html( $bodyreplacement, $replace_tag );
        }

        // Restore comments.
        $this->content = $this->restore_comments( $this->content );

        // Restore IE hacks.
        $this->content = $this->restore_iehacks( $this->content );

        // Restore noptimize.
        $this->content = $this->restore_noptimize( $this->content );

        // Return the modified HTML.
        return $this->content;
    }

    /**
     * Checks against the allow- and blocklists.
     *
     * @param string $tag JS tag.
     */
    private function ismergeable( $tag )
    {
        if ( empty( $tag ) || ! $this->aggregate ) {
            return false;
        }

        if ( ! empty( $this->allowlist ) ) {
            foreach ( $this->allowlist as $match ) {
                if ( false !== strpos( $tag, $match ) ) {
                    return true;
                }
            }
            // No match with allowlist.
            return false;
        } else {
            foreach ( $this->domove as $match ) {
                if ( false !== strpos( $tag, $match ) ) {
                    // Matched something.
                    return false;
                }
            }

            if ( $this->movetolast( $tag ) ) {
                return false;
            }

            foreach ( $this->dontmove as $match ) {
                if ( false !== strpos( $tag, $match ) ) {
                    // Matched something.
                    return false;
                }
            }

            // If we're here it's safe to merge.
            return true;
        }
    }

    /**
     * Checks agains the blocklist.
     *
     * @param string $tag tag to check for blocklist (exclusions).
     */
    private function ismovable( $tag )
    {
        if ( empty( $tag ) || true !== $this->include_inline || apply_filters( 'autoptimize_filter_js_unmovable', true ) ) {
            return false;
        }

        foreach ( $this->domove as $match ) {
            if ( false !== strpos( $tag, $match ) ) {
                // Matched something.
                return true;
            }
        }

        if ( $this->movetolast( $tag ) ) {
            return true;
        }

        foreach ( $this->dontmove as $match ) {
            if ( false !== strpos( $tag, $match ) ) {
                // Matched something.
                return false;
            }
        }

        // If we're here it's safe to move.
        return true;
    }

    private function movetolast( $tag )
    {
        if ( empty( $tag ) ) {
            return false;
        }

        foreach ( $this->domovelast as $match ) {
            if ( false !== strpos( $tag, $match ) ) {
                // Matched, return true.
                return true;
            }
        }

        // Should be in 'first'.
        return false;
    }

    /**
     * Determines wheter a <script> $tag can be excluded from minification (as already minified) based on:
     * - inject_min_late being active
     * - filename ending in `min.js`
     * - filename matching one passed in the consider minified filter
     *
     * @param string $js_path Path to JS file.
     * @return bool
     */
    private function can_inject_late( $js_path ) {
        $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
        if ( true !== $this->inject_min_late ) {
            // late-inject turned off.
            return false;
        } elseif ( ( false === strpos( $js_path, 'min.js' ) ) && ( str_replace( $consider_minified_array, '', $js_path ) === $js_path ) ) {
            // file not minified based on filename & filter.
            return false;
        } else {
            // phew, all is safe, we can late-inject.
            return true;
        }
    }

    /**
     * Returns whether we're doing aggregation or not.
     *
     * @return bool
     */
    public function aggregating()
    {
        return $this->aggregate;
    }

    /**
     * Minifies a single local js file and returns its (cached) url.
     *
     * @param string $filepath Filepath.
     * @param bool   $cache_miss Optional. Force a cache miss. Default false.
     *
     * @return bool|string Url pointing to the minified js file or false.
     */
    public function minify_single( $filepath, $cache_miss = false )
    {
        $contents = $this->prepare_minify_single( $filepath );

        if ( empty( $contents ) ) {
            return false;
        }

        // Check cache.
        $hash  = 'single_' . md5( $contents );
        $cache = new autoptimizeCache( $hash, 'js' );

        // If not in cache already, minify...
        if ( ! $cache->check() || $cache_miss ) {
            $contents = trim( JSMin::minify( $contents ) );

            // Check if minified cache content is empty.
            if ( empty( $contents ) ) {
                return false;
            }

            // Filter contents of excluded minified CSS.
            $contents = apply_filters( 'autoptimize_filter_js_single_after_minify', $contents );

            // Store in cache.
            $cache->cache( $contents, 'text/javascript' );
        }

        $url = $this->build_minify_single_url( $cache );

        return $url;
    }
}
PK.�1\�̡OOautoptimizeCSSmin.phpnu�[���<?php
/**
 * Thin wrapper around css minifiers to avoid rewriting a bunch of existing code.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCSSmin
{
    /**
     * Minifier instance.
     *
     * @var Autoptimize\tubalmartin\CssMin\Minifier|null
     */
    protected $minifier = null;

    /**
     * Construtor.
     *
     * @param bool $raise_limits Whether to raise memory limits or not. Default true.
     */
    public function __construct( $raise_limits = true )
    {
        $this->minifier = new Autoptimize\tubalmartin\CssMin\Minifier( $raise_limits );
    }

    /**
     * Runs the minifier on given string of $css.
     * Returns the minified css.
     *
     * @param string $css CSS to minify.
     *
     * @return string
     */
    public function run( $css )
    {
        // hide calc() if filter says yes.
        if ( apply_filters( 'autoptimize_filter_css_hide_calc', false ) ) {
            $css = autoptimizeBase::replace_contents_with_marker_if_exists( 'CALC', 'calc(', '#calc\([^\)]*\)#m', $css );
        }

        // minify.
        $result = $this->minifier->run( $css );

        // restore calc() if filter says yes.
        if ( apply_filters( 'autoptimize_filter_css_hide_calc', false ) ) {
            $result = autoptimizeBase::restore_marked_content( 'CALC', $result );
        }

        return $result;
    }

    /**
     * Static helper.
     *
     * @param string $css CSS to minify.
     *
     * @return string
     */
    public static function minify( $css )
    {
        $minifier = new self();

        return $minifier->run( $css );
    }
}
PK.�1\_Ss�^0^0$autoptimizeVersionUpdatesHandler.phpnu�[���<?php
/**
 * Handles version updates and should only be instantiated in autoptimize.php if/when needed.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeVersionUpdatesHandler
{
    /**
     * The current major version string.
     *
     * @var string
     */
    protected $current_major_version = null;

    public function __construct( $current_version )
    {
        $this->current_major_version = substr( $current_version, 0, 3 );
    }

    /**
     * Runs all needed upgrade procedures (depending on the
     * current major version specified during class instantiation)
     */
    public function run_needed_major_upgrades()
    {
        $major_update = false;

        switch ( $this->current_major_version ) {
            case '1.6':
                $this->upgrade_from_1_6();
                $major_update = true;
                // No break, intentionally, so all upgrades are ran during a single request...
            case '1.7':
                $this->upgrade_from_1_7();
                $major_update = true;
                // No break, intentionally, so all upgrades are ran during a single request...
            case '1.9':
                $this->upgrade_from_1_9();
                $major_update = true;
                // No break, intentionally, so all upgrades are ran during a single request...
            case '2.2':
                $this->upgrade_from_2_2();
                $major_update = true;
                // No break, intentionally, so all upgrades are ran during a single request...
            case '2.4':
                if ( autoptimizeOptionWrapper::get_option( 'autoptimize_version', 'none' ) == '2.4.2' ) {
                    $this->upgrade_from_2_4_2();
                }
                $this->upgrade_from_2_4();
                $major_update = false;
                // No break, intentionally, so all upgrades are ran during a single request...
            case '2.7':
                $this->upgrade_from_2_7();
                $major_update = true;
                // No break, intentionally, so all upgrades are ran during a single request...
            case '2.8':
                // nothing.
            case '2.9':
                if ( version_compare( autoptimizeOptionWrapper::get_option( 'autoptimize_version', 'none' ), '2.9.999', 'lt' ) ) {
                    $this->upgrade_from_2_9_before_compatibility();
                }
                $major_update = false;
                // No break, intentionally, so all upgrades are ran during a single request...
            case '3.0':
                // nothing.
            case '3.1':
                $this->upgrade_from_3_1();
                $major_update = false;
        }

        if ( true === $major_update ) {
            $this->on_major_version_update();
        }
    }

    /**
     * Checks specified version against the one stored in the database under `autoptimize_version` and performs
     * any major upgrade routines if needed.
     * Updates the database version to the specified $target if it's different to the one currently stored there.
     *
     * @param string $target Target version to check against (ie., the currently running one).
     */
    public static function check_installed_and_update( $target )
    {
        $db_version = autoptimizeOptionWrapper::get_option( 'autoptimize_version', 'none' );
        if ( $db_version !== $target ) {
            if ( 'none' === $db_version ) {
                add_action( 'admin_notices', 'autoptimizeMain::notice_installed' );
            } else {
                $updater = new self( $db_version );
                $updater->run_needed_major_upgrades();
            }

            // Versions differed, upgrades happened if needed, store the new version.
            autoptimizeOptionWrapper::update_option( 'autoptimize_version', $target );
        }
    }

    /**
     * Called after any major version update (and it's accompanying upgrade procedure)
     * has happened. Clears cache and sets an admin notice.
     */
    protected function on_major_version_update()
    {
        // The transients guard here prevents stale object caches from busting the cache on every request.
        if ( false == get_transient( 'autoptimize_stale_option_buster' ) ) {
            set_transient( 'autoptimize_stale_option_buster', 'Mamsie & Liessie zehhe: ZWIJH!', HOUR_IN_SECONDS );
            autoptimizeCache::clearall();
            add_action( 'admin_notices', 'autoptimizeMain::notice_updated' );
        }
    }

    /**
     * From back in the days when I did not yet consider multisite.
     */
    private function upgrade_from_1_6()
    {
        // If user was on version 1.6.x, force advanced options to be shown by default.
        autoptimizeOptionWrapper::update_option( 'autoptimize_show_adv', '1' );

        // And remove old options.
        $to_delete_options = array(
            'autoptimize_cdn_css',
            'autoptimize_cdn_css_url',
            'autoptimize_cdn_js',
            'autoptimize_cdn_js_url',
            'autoptimize_cdn_img',
            'autoptimize_cdn_img_url',
            'autoptimize_css_yui',
        );
        foreach ( $to_delete_options as $del_opt ) {
            delete_option( $del_opt );
        }
    }

    /**
     * Forces WP 3.8 dashicons in CSS exclude options when upgrading from 1.7 to 1.8
     *
     * @global $wpdb
     */
    private function upgrade_from_1_7()
    {
        if ( ! is_multisite() ) {
            $css_exclude = autoptimizeOptionWrapper::get_option( 'autoptimize_css_exclude' );
            if ( empty( $css_exclude ) ) {
                $css_exclude = 'admin-bar.min.css, dashicons.min.css';
            } elseif ( false === strpos( $css_exclude, 'dashicons.min.css' ) ) {
                $css_exclude .= ', dashicons.min.css';
            }
            autoptimizeOptionWrapper::update_option( 'autoptimize_css_exclude', $css_exclude );
        } else {
            global $wpdb;
            $blog_ids         = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
            $original_blog_id = get_current_blog_id();
            foreach ( $blog_ids as $blog_id ) {
                switch_to_blog( $blog_id );
                $css_exclude = autoptimizeOptionWrapper::get_option( 'autoptimize_css_exclude' );
                if ( empty( $css_exclude ) ) {
                    $css_exclude = 'admin-bar.min.css, dashicons.min.css';
                } elseif ( false === strpos( $css_exclude, 'dashicons.min.css' ) ) {
                    $css_exclude .= ', dashicons.min.css';
                }
                autoptimizeOptionWrapper::update_option( 'autoptimize_css_exclude', $css_exclude );
            }
            switch_to_blog( $original_blog_id );
        }
    }

    /**
     * 2.0 will not aggregate inline CSS/JS by default, but we want users
     * upgrading from 1.9 to keep their inline code aggregated by default.
     *
     * @global $wpdb
     */
    private function upgrade_from_1_9()
    {
        if ( ! is_multisite() ) {
            autoptimizeOptionWrapper::update_option( 'autoptimize_css_include_inline', 'on' );
            autoptimizeOptionWrapper::update_option( 'autoptimize_js_include_inline', 'on' );
        } else {
            global $wpdb;
            $blog_ids         = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
            $original_blog_id = get_current_blog_id();
            foreach ( $blog_ids as $blog_id ) {
                switch_to_blog( $blog_id );
                autoptimizeOptionWrapper::update_option( 'autoptimize_css_include_inline', 'on' );
                autoptimizeOptionWrapper::update_option( 'autoptimize_js_include_inline', 'on' );
            }
            switch_to_blog( $original_blog_id );
        }
    }

    /**
     * 2.3 has no "remove google fonts" in main screen, moved to "extra"
     *
     * @global $wpdb
     */
    private function upgrade_from_2_2()
    {
        if ( ! is_multisite() ) {
            $this->do_2_2_settings_update();
        } else {
            global $wpdb;
            $blog_ids         = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
            $original_blog_id = get_current_blog_id();
            foreach ( $blog_ids as $blog_id ) {
                switch_to_blog( $blog_id );
                $this->do_2_2_settings_update();
            }
            switch_to_blog( $original_blog_id );
        }
    }

    /**
     * Helper for 2.2 autoptimize_extra_settings upgrade to avoid duplicate code
     */
    private function do_2_2_settings_update()
    {
        $nogooglefont    = autoptimizeOptionWrapper::get_option( 'autoptimize_css_nogooglefont', '' );
        $ao_extrasetting = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings', '' );
        if ( ( $nogooglefont ) && ( empty( $ao_extrasetting ) ) ) {
            autoptimizeOptionWrapper::update_option( 'autoptimize_extra_settings', autoptimizeConfig::get_ao_extra_default_options() );
        }
        delete_option( 'autoptimize_css_nogooglefont' );
    }

    /**
     * 2.4.2 introduced too many cronned ao_cachecheckers, make this right
     */
    private function upgrade_from_2_4_2() {
        // below code by Thomas Sjolshagen (http://eighty20results.com/)
        // as found on https://www.paidmembershipspro.com/deleting-oldextra-cron-events/.
        $jobs = _get_cron_array();

        // Remove all ao_cachechecker cron jobs (for now).
        foreach ( $jobs as $when => $job ) {
            $name = key( $job );

            if ( false !== strpos( $name, 'ao_cachechecker' ) ) {
                unset( $jobs[ $when ] );
            }
        }

        // Save the data.
        _set_cron_array( $jobs );
    }

    /**
     * Migrate imgopt options from autoptimize_extra_settings to autoptimize_imgopt_settings
     */
    private function upgrade_from_2_4() {
        $extra_settings  = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings', '' );
        $imgopt_settings = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings', '' );
        if ( empty( $imgopt_settings ) && ! empty( $extra_settings ) ) {
            $imgopt_settings = autoptimizeConfig::get_ao_imgopt_default_options();
            if ( array_key_exists( 'autoptimize_extra_checkbox_field_5', $extra_settings ) ) {
                $imgopt_settings['autoptimize_imgopt_checkbox_field_1'] = $extra_settings['autoptimize_extra_checkbox_field_5'];
            }
            if ( array_key_exists( 'autoptimize_extra_select_field_6', $extra_settings ) ) {
                $imgopt_settings['autoptimize_imgopt_select_field_2'] = $extra_settings['autoptimize_extra_select_field_6'];
            }
            autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_settings', $imgopt_settings );
        }
    }

    /**
     * Remove CCSS request limit option + update jquery exclusion to include WordPress 5.6 jquery.min.js.
     */
    private function upgrade_from_2_7() {
        delete_option( 'autoptimize_ccss_rlimit' );
        $js_exclusions = get_option( 'autoptimize_js_exclude', '' );
        if ( strpos( $js_exclusions, 'js/jquery/jquery.js' ) !== false && strpos( $js_exclusions, 'js/jquery/jquery.min.js' ) === false ) {
            $js_exclusions .= ', js/jquery/jquery.min.js';
            autoptimizeOptionWrapper::update_option( 'autoptimize_js_exclude', $js_exclusions );
        }
    }

    /**
     * Set an option to indicate the AO installation predates the compatibility logic, this way we
     * can avoid adding compatibility code that is likely not needed and maybe not wanted as it
     * can introduce performance regressions.
     */
    private function upgrade_from_2_9_before_compatibility() {
        autoptimizeOptionWrapper::update_option( 'autoptimize_installed_before_compatibility', true );
    }

    /**
     * If the 404 handler is active, delete the current PHP-file so it can be re-created to fix the double underscore bug.
     */
    private function upgrade_from_3_1() {
        if ( autoptimizeCache::do_fallback() && version_compare( autoptimizeOptionWrapper::get_option( 'autoptimize_version', 'none' ), '3.1.2', 'lt' ) ) {
            $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php';
            @unlink( $_fallback_php ); // @codingStandardsIgnoreLine
        }
    }
}
PK.�1\�pYHHexternal/js/jquery.cookie.jsnu�[���/*jslint browser: true */ /*global jQuery: true */

/**
 * jQuery Cookie plugin
 *
 * Copyright (c) 2010 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */

// TODO JsDoc

/**
 * Create a cookie with the given key and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String key The key of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path attribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */

/**
 * Get the value of a cookie with the given key.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String key The key of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function (key, value, options) {

    // key and value given, set cookie...
    if (arguments.length > 1 && (value === null || typeof value !== "object")) {
        options = jQuery.extend({}, options);

        if (value === null) {
            options.expires = -1;
        }

        if (typeof options.expires === 'number') {
            var days = options.expires, t = options.expires = new Date();
            t.setDate(t.getDate() + days);
        }

        return (document.cookie = [
            encodeURIComponent(key), '=',
            options.raw ? String(value) : encodeURIComponent(String(value)),
            options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
            options.path ? '; path=' + options.path : '',
            options.domain ? '; domain=' + options.domain : '',
            options.secure ? '; secure' : ''
        ].join(''));
    }

    // key and possibly options given, get cookie...
    options = value || {};
    var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent;
    return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null;
};
PK.�1\B.xG��external/js/index.htmlnu�[���<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
PK.�1\�Uۅ(( external/js/jquery.cookie.min.jsnu�[���jQuery.cookie=function(key,value,options){if(arguments.length>1&&(value===null||typeof value!=="object")){options=jQuery.extend({},options);if(value===null){options.expires=-1;}if(typeof options.expires==='number'){var days=options.expires,t=options.expires=new Date();t.setDate(t.getDate()+days);}return(document.cookie=[encodeURIComponent(key),'=',options.raw?String(value):encodeURIComponent(String(value)),options.expires?'; expires='+options.expires.toUTCString():'',options.path?'; path='+options.path:'',options.domain?'; domain='+options.domain:'',options.secure?'; secure':''].join(''));}options=value||{};var result,decode=options.raw?function(s){return s;}:decodeURIComponent;return(result=new RegExp('(?:^|; )'+encodeURIComponent(key)+'=([^;]*)').exec(document.cookie))?decode(result[1]):null;};
PK.�1\�G��TTexternal/js/unslider.min.jsnu�[���!function($){return $?($.Unslider=function(t,n){var e=this;return e._="unslider",e.defaults={autoplay:!1,delay:3e3,speed:750,easing:"swing",keys:{prev:37,next:39},nav:!0,arrows:{prev:'<a class="'+e._+'-arrow prev">Prev</a>',next:'<a class="'+e._+'-arrow next">Next</a>'},animation:"horizontal",selectors:{container:"ul:first",slides:"li"},animateHeight:!1,activeClass:e._+"-active",swipe:!0,swipeThreshold:.2},e.$context=t,e.options={},e.$parent=null,e.$container=null,e.$slides=null,e.$nav=null,e.$arrows=[],e.total=0,e.current=0,e.prefix=e._+"-",e.eventSuffix="."+e.prefix+~~(2e3*Math.random()),e.interval=null,e.init=function(t){return e.options=$.extend({},e.defaults,t),e.$container=e.$context.find(e.options.selectors.container).addClass(e.prefix+"wrap"),e.$slides=e.$container.children(e.options.selectors.slides),e.setup(),$.each(["nav","arrows","keys","infinite"],function(t,n){e.options[n]&&e["init"+$._ucfirst(n)]()}),jQuery.event.special.swipe&&e.options.swipe&&e.initSwipe(),e.options.autoplay&&e.start(),e.calculateSlides(),e.$context.trigger(e._+".ready"),e.animate(e.options.index||e.current,"init")},e.setup=function(){e.$context.addClass(e.prefix+e.options.animation).wrap('<div class="'+e._+'" />'),e.$parent=e.$context.parent("."+e._);var t=e.$context.css("position");"static"===t&&e.$context.css("position","relative"),e.$context.css("overflow","hidden")},e.calculateSlides=function(){if(e.total=e.$slides.length,"fade"!==e.options.animation){var t="width";"vertical"===e.options.animation&&(t="height"),e.$container.css(t,100*e.total+"%").addClass(e.prefix+"carousel"),e.$slides.css(t,100/e.total+"%")}},e.start=function(){return e.interval=setTimeout(function(){e.next()},e.options.delay),e},e.stop=function(){return clearTimeout(e.interval),e},e.initNav=function(){var t=$('<nav class="'+e.prefix+'nav"><ol /></nav>');e.$slides.each(function(n){var i=this.getAttribute("data-nav")||n+1;$.isFunction(e.options.nav)&&(i=e.options.nav.call(e.$slides.eq(n),n,i)),t.children("ol").append('<li data-slide="'+n+'">'+i+"</li>")}),e.$nav=t.insertAfter(e.$context),e.$nav.find("li").on("click"+e.eventSuffix,function(){var t=$(this).addClass(e.options.activeClass);t.siblings().removeClass(e.options.activeClass),e.animate(t.attr("data-slide"))})},e.initArrows=function(){e.options.arrows===!0&&(e.options.arrows=e.defaults.arrows),$.each(e.options.arrows,function(t,n){e.$arrows.push($(n).insertAfter(e.$context).on("click"+e.eventSuffix,e[t]))})},e.initKeys=function(){e.options.keys===!0&&(e.options.keys=e.defaults.keys),$(document).on("keyup"+e.eventSuffix,function(t){$.each(e.options.keys,function(n,i){t.which===i&&$.isFunction(e[n])&&e[n].call(e)})})},e.initSwipe=function(){var t=e.$slides.width();"fade"!==e.options.animation&&e.$container.on({movestart:function(t){return t.distX>t.distY&&t.distX<-t.distY||t.distX<t.distY&&t.distX>-t.distY?!!t.preventDefault():void e.$container.css("position","relative")},move:function(n){e.$container.css("left",-(100*e.current)+100*n.distX/t+"%")},moveend:function(n){Math.abs(n.distX)/t>e.options.swipeThreshold?e[n.distX<0?"next":"prev"]():e.$container.animate({left:-(100*e.current)+"%"},e.options.speed/2)}})},e.initInfinite=function(){var t=["first","last"];$.each(t,function(n,i){e.$slides.push.apply(e.$slides,e.$slides.filter(':not(".'+e._+'-clone")')[i]().clone().addClass(e._+"-clone")["insert"+(0===n?"After":"Before")](e.$slides[t[~~!n]]()))})},e.destroyArrows=function(){$.each(e.$arrows,function(t,n){n.remove()})},e.destroySwipe=function(){e.$container.off("movestart move moveend")},e.destroyKeys=function(){$(document).off("keyup"+e.eventSuffix)},e.setIndex=function(t){return 0>t&&(t=e.total-1),e.current=Math.min(Math.max(0,t),e.total-1),e.options.nav&&e.$nav.find('[data-slide="'+e.current+'"]')._active(e.options.activeClass),e.$slides.eq(e.current)._active(e.options.activeClass),e},e.animate=function(t,n){if("first"===t&&(t=0),"last"===t&&(t=e.total),isNaN(t))return e;e.options.autoplay&&e.stop().start(),e.setIndex(t),e.$context.trigger(e._+".change",[t,e.$slides.eq(t)]);var i="animate"+$._ucfirst(e.options.animation);return $.isFunction(e[i])&&e[i](e.current,n),e},e.next=function(){var t=e.current+1;return t>=e.total&&(t=0),e.animate(t,"next")},e.prev=function(){return e.animate(e.current-1,"prev")},e.animateHorizontal=function(t){var n="left";return"rtl"===e.$context.attr("dir")&&(n="right"),e.options.infinite&&e.$container.css("margin-"+n,"-100%"),e.slide(n,t)},e.animateVertical=function(t){return e.options.animateHeight=!0,e.options.infinite&&e.$container.css("margin-top",-e.$slides.outerHeight()),e.slide("top",t)},e.slide=function(t,n){if(e.options.animateHeight&&e._move(e.$context,{height:e.$slides.eq(n).outerHeight()},!1),e.options.infinite){var i;n===e.total-1&&(i=e.total-3,n=-1),n===e.total-2&&(i=0,n=e.total-2),"number"==typeof i&&(e.setIndex(i),e.$context.on(e._+".moved",function(){e.current===i&&e.$container.css(t,-(100*i)+"%").off(e._+".moved")}))}var o={};return o[t]=-(100*n)+"%",e._move(e.$container,o)},e.animateFade=function(t){var n=e.$slides.eq(t).addClass(e.options.activeClass);e._move(n.siblings().removeClass(e.options.activeClass),{opacity:0}),e._move(n,{opacity:1},!1)},e._move=function(t,n,i,o){return i!==!1&&(i=function(){e.$context.trigger(e._+".moved")}),t._move(n,o||e.options.speed,e.options.easing,i)},e.init(n)},$.fn._active=function(t){return this.addClass(t).siblings().removeClass(t)},$._ucfirst=function(t){return(t+"").toLowerCase().replace(/^./,function(t){return t.toUpperCase()})},$.fn._move=function(){return this.stop(!0,!0),$.fn[$.fn.velocity?"velocity":"animate"].apply(this,arguments)},void($.fn.unslider=function(t){return this.each(function(){var n=$(this);if("string"==typeof t&&n.data("unslider")){t=t.split(":");var e=n.data("unslider")[t[0]];if($.isFunction(e))return e.apply(n,t[1]?t[1].split(","):null)}return n.data("unslider",new $.Unslider(n,t))})})):console.warn("Unslider needs jQuery")}(window.jQuery);
PK.�1\�l�

external/js/unslider-dots.cssnu�[���/**
 *   Here's where everything gets included. You don't need
 *   to change anything here, and doing so might break
 *   stuff. Here be dragons and all that.
 */
/**
 *   Default variables
 *
 *   While these can be set with JavaScript, it's probably
 *   better and faster to just set them here, compile to
 *   CSS and include that instead to use some of that
 *   hardware-accelerated goodness.
 */
.unslider-nav ol {
  list-style: none;
  text-align: center;
}
.unslider-nav ol li {
  display: inline-block;
  width: 6px;
  height: 6px;
  margin: 0 4px;
  background: transparent;
  border-radius: 5px;
  overflow: hidden;
  text-indent: -999em;
  border: 2px solid #fff;
  cursor: pointer;
}
.unslider-nav ol li.unslider-active {
  background: #fff;
  cursor: default;
}
PK.�1\�m~XU&U&external/js/lazysizes.min.jsnu�[���/*! lazysizes + ls unveilhooks - v5.3.1 (incl. ls-uvh data-link fix) */
!function(e){var t=function(u,D,f){"use strict";var k,H;if(function(){var e;var t={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:true,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:true,ricTimeout:0,throttleDelay:125};H=u.lazySizesConfig||u.lazysizesConfig||{};for(e in t){if(!(e in H)){H[e]=t[e]}}}(),!D||!D.getElementsByClassName){return{init:function(){},cfg:H,noSupport:true}}var O=D.documentElement,i=u.HTMLPictureElement,P="addEventListener",$="getAttribute",q=u[P].bind(u),I=u.setTimeout,U=u.requestAnimationFrame||I,o=u.requestIdleCallback,j=/^picture$/i,r=["load","error","lazyincluded","_lazyloaded"],a={},G=Array.prototype.forEach,J=function(e,t){if(!a[t]){a[t]=new RegExp("(\\s|^)"+t+"(\\s|$)")}return a[t].test(e[$]("class")||"")&&a[t]},K=function(e,t){if(!J(e,t)){e.setAttribute("class",(e[$]("class")||"").trim()+" "+t)}},Q=function(e,t){var a;if(a=J(e,t)){e.setAttribute("class",(e[$]("class")||"").replace(a," "))}},V=function(t,a,e){var i=e?P:"removeEventListener";if(e){V(t,a)}r.forEach(function(e){t[i](e,a)})},X=function(e,t,a,i,r){var n=D.createEvent("Event");if(!a){a={}}a.instance=k;n.initEvent(t,!i,!r);n.detail=a;e.dispatchEvent(n);return n},Y=function(e,t){var a;if(!i&&(a=u.picturefill||H.pf)){if(t&&t.src&&!e[$]("srcset")){e.setAttribute("srcset",t.src)}a({reevaluate:true,elements:[e]})}else if(t&&t.src){e.src=t.src}},Z=function(e,t){return(getComputedStyle(e,null)||{})[t]},s=function(e,t,a){a=a||e.offsetWidth;while(a<H.minSize&&t&&!e._lazysizesWidth){a=t.offsetWidth;t=t.parentNode}return a},ee=function(){var a,i;var t=[];var r=[];var n=t;var s=function(){var e=n;n=t.length?r:t;a=true;i=false;while(e.length){e.shift()()}a=false};var e=function(e,t){if(a&&!t){e.apply(this,arguments)}else{n.push(e);if(!i){i=true;(D.hidden?I:U)(s)}}};e._lsFlush=s;return e}(),te=function(a,e){return e?function(){ee(a)}:function(){var e=this;var t=arguments;ee(function(){a.apply(e,t)})}},ae=function(e){var a;var i=0;var r=H.throttleDelay;var n=H.ricTimeout;var t=function(){a=false;i=f.now();e()};var s=o&&n>49?function(){o(t,{timeout:n});if(n!==H.ricTimeout){n=H.ricTimeout}}:te(function(){I(t)},true);return function(e){var t;if(e=e===true){n=33}if(a){return}a=true;t=r-(f.now()-i);if(t<0){t=0}if(e||t<9){s()}else{I(s,t)}}},ie=function(e){var t,a;var i=99;var r=function(){t=null;e()};var n=function(){var e=f.now()-a;if(e<i){I(n,i-e)}else{(o||r)(r)}};return function(){a=f.now();if(!t){t=I(n,i)}}},e=function(){var v,m,c,h,e;var y,z,g,p,C,b,A;var n=/^img$/i;var d=/^iframe$/i;var E="onscroll"in u&&!/(gle|ing)bot/.test(navigator.userAgent);var _=0;var w=0;var M=0;var N=-1;var L=function(e){M--;if(!e||M<0||!e.target){M=0}};var x=function(e){if(A==null){A=Z(D.body,"visibility")=="hidden"}return A||!(Z(e.parentNode,"visibility")=="hidden"&&Z(e,"visibility")=="hidden")};var W=function(e,t){var a;var i=e;var r=x(e);g-=t;b+=t;p-=t;C+=t;while(r&&(i=i.offsetParent)&&i!=D.body&&i!=O){r=(Z(i,"opacity")||1)>0;if(r&&Z(i,"overflow")!="visible"){a=i.getBoundingClientRect();r=C>a.left&&p<a.right&&b>a.top-1&&g<a.bottom+1}}return r};var t=function(){var e,t,a,i,r,n,s,o,l,u,f,c;var d=k.elements;if((h=H.loadMode)&&M<8&&(e=d.length)){t=0;N++;for(;t<e;t++){if(!d[t]||d[t]._lazyRace){continue}if(!E||k.prematureUnveil&&k.prematureUnveil(d[t])){R(d[t]);continue}if(!(o=d[t][$]("data-expand"))||!(n=o*1)){n=w}if(!u){u=!H.expand||H.expand<1?O.clientHeight>500&&O.clientWidth>500?500:370:H.expand;k._defEx=u;f=u*H.expFactor;c=H.hFac;A=null;if(w<f&&M<1&&N>2&&h>2&&!D.hidden){w=f;N=0}else if(h>1&&N>1&&M<6){w=u}else{w=_}}if(l!==n){y=innerWidth+n*c;z=innerHeight+n;s=n*-1;l=n}a=d[t].getBoundingClientRect();if((b=a.bottom)>=s&&(g=a.top)<=z&&(C=a.right)>=s*c&&(p=a.left)<=y&&(b||C||p||g)&&(H.loadHidden||x(d[t]))&&(m&&M<3&&!o&&(h<3||N<4)||W(d[t],n))){R(d[t]);r=true;if(M>9){break}}else if(!r&&m&&!i&&M<4&&N<4&&h>2&&(v[0]||H.preloadAfterLoad)&&(v[0]||!o&&(b||C||p||g||d[t][$](H.sizesAttr)!="auto"))){i=v[0]||d[t]}}if(i&&!r){R(i)}}};var a=ae(t);var S=function(e){var t=e.target;if(t._lazyCache){delete t._lazyCache;return}L(e);K(t,H.loadedClass);Q(t,H.loadingClass);V(t,B);X(t,"lazyloaded")};var i=te(S);var B=function(e){i({target:e.target})};var T=function(e,t){var a=e.getAttribute("data-load-mode")||H.iframeLoadMode;if(a==0){e.contentWindow.location.replace(t)}else if(a==1){e.src=t}};var F=function(e){var t;var a=e[$](H.srcsetAttr);if(t=H.customMedia[e[$]("data-media")||e[$]("media")]){e.setAttribute("media",t)}if(a){e.setAttribute("srcset",a)}};var s=te(function(t,e,a,i,r){var n,s,o,l,u,f;if(!(u=X(t,"lazybeforeunveil",e)).defaultPrevented){if(i){if(a){K(t,H.autosizesClass)}else{t.setAttribute("sizes",i)}}s=t[$](H.srcsetAttr);n=t[$](H.srcAttr);if(r){o=t.parentNode;l=o&&j.test(o.nodeName||"")}f=e.firesLoad||"src"in t&&(s||n||l);u={target:t};K(t,H.loadingClass);if(f){clearTimeout(c);c=I(L,2500);V(t,B,true)}if(l){G.call(o.getElementsByTagName("source"),F)}if(s){t.setAttribute("srcset",s)}else if(n&&!l){if(d.test(t.nodeName)){T(t,n)}else{t.src=n}}if(r&&(s||l)){Y(t,{src:n})}}if(t._lazyRace){delete t._lazyRace}Q(t,H.lazyClass);ee(function(){var e=t.complete&&t.naturalWidth>1;if(!f||e){if(e){K(t,H.fastLoadedClass)}S(u);t._lazyCache=true;I(function(){if("_lazyCache"in t){delete t._lazyCache}},9)}if(t.loading=="lazy"){M--}},true)});var R=function(e){if(e._lazyRace){return}var t;var a=n.test(e.nodeName);var i=a&&(e[$](H.sizesAttr)||e[$]("sizes"));var r=i=="auto";if((r||!m)&&a&&(e[$]("src")||e.srcset)&&!e.complete&&!J(e,H.errorClass)&&J(e,H.lazyClass)){return}t=X(e,"lazyunveilread").detail;if(r){re.updateElem(e,true,e.offsetWidth)}e._lazyRace=true;M++;s(e,t,r,i,a)};var r=ie(function(){H.loadMode=3;a()});var o=function(){if(H.loadMode==3){H.loadMode=2}r()};var l=function(){if(m){return}if(f.now()-e<999){I(l,999);return}m=true;H.loadMode=3;a();q("scroll",o,true)};return{_:function(){e=f.now();k.elements=D.getElementsByClassName(H.lazyClass);v=D.getElementsByClassName(H.lazyClass+" "+H.preloadClass);q("scroll",a,true);q("resize",a,true);q("pageshow",function(e){if(e.persisted){var t=D.querySelectorAll("."+H.loadingClass);if(t.length&&t.forEach){U(function(){t.forEach(function(e){if(e.complete){R(e)}})})}}});if(u.MutationObserver){new MutationObserver(a).observe(O,{childList:true,subtree:true,attributes:true})}else{O[P]("DOMNodeInserted",a,true);O[P]("DOMAttrModified",a,true);setInterval(a,999)}q("hashchange",a,true);["focus","mouseover","click","load","transitionend","animationend"].forEach(function(e){D[P](e,a,true)});if(/d$|^c/.test(D.readyState)){l()}else{q("load",l);D[P]("DOMContentLoaded",a);I(l,2e4)}if(k.elements.length){t();ee._lsFlush()}else{a()}},checkElems:a,unveil:R,_aLSL:o}}(),re=function(){var a;var n=te(function(e,t,a,i){var r,n,s;e._lazysizesWidth=i;i+="px";e.setAttribute("sizes",i);if(j.test(t.nodeName||"")){r=t.getElementsByTagName("source");for(n=0,s=r.length;n<s;n++){r[n].setAttribute("sizes",i)}}if(!a.detail.dataAttr){Y(e,a.detail)}});var i=function(e,t,a){var i;var r=e.parentNode;if(r){a=s(e,r,a);i=X(e,"lazybeforesizes",{width:a,dataAttr:!!t});if(!i.defaultPrevented){a=i.detail.width;if(a&&a!==e._lazysizesWidth){n(e,r,i,a)}}}};var e=function(){var e;var t=a.length;if(t){e=0;for(;e<t;e++){i(a[e])}}};var t=ie(e);return{_:function(){a=D.getElementsByClassName(H.autosizesClass);q("resize",t)},checkElems:t,updateElem:i}}(),t=function(){if(!t.i&&D.getElementsByClassName){t.i=true;re._();e._()}};return I(function(){H.init&&t()}),k={cfg:H,autoSizer:re,loader:e,init:t,uP:Y,aC:K,rC:Q,hC:J,fire:X,gW:s,rAF:ee}}(e,e.document,Date);e.lazySizes=t,"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:{});
!function(e,t){var a=function(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)};t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):"function"==typeof define&&define.amd?define(["lazysizes"],t):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(e,i,o){"use strict";var l,d,u={};function s(e,t,a){var n,r;u[e]||(n=i.createElement(t?"link":"script"),r=i.getElementsByTagName("script")[0],t?(n.rel="stylesheet",n.href=e):(n.onload=function(){n.onerror=null,n.onload=null,a()},n.onerror=n.onload,n.src=e),u[e]=!0,u[n.src||n.href]=!0,r.parentNode.insertBefore(n,r))}i.addEventListener&&(l=function(e,t){var a=i.createElement("img");a.onload=function(){a.onload=null,a.onerror=null,a=null,t()},a.onerror=a.onload,a.src=e,a&&a.complete&&a.onload&&a.onload()},addEventListener("lazybeforeunveil",function(e){var t,a,n;if(e.detail.instance==o&&!e.defaultPrevented){var r=e.target;if("none"==r.preload&&(r.preload=r.getAttribute("data-preload")||"auto"),null!=r.getAttribute("data-autoplay"))if(r.getAttribute("data-expand")&&!r.autoplay)try{r.play()}catch(e){}else requestAnimationFrame(function(){r.setAttribute("data-expand","-10"),o.aC(r,o.cfg.lazyClass)});(t=r.getAttribute("data-link"))&&"img"!=r.tagName.toLowerCase()&&s(t,!0),(t=r.getAttribute("data-script"))&&(e.detail.firesLoad=!0,s(t,null,function(){e.detail.firesLoad=!1,o.fire(r,"_lazyloaded",{},!0,!0)})),(t=r.getAttribute("data-require"))&&(o.cfg.requireJs?o.cfg.requireJs([t]):s(t)),(a=r.getAttribute("data-bg"))&&(e.detail.firesLoad=!0,l(a,function(){r.style.backgroundImage="url("+(d.test(a)?JSON.stringify(a):a)+")",e.detail.firesLoad=!1,o.fire(r,"_lazyloaded",{},!0,!0)})),(n=r.getAttribute("data-poster"))&&(e.detail.firesLoad=!0,l(n,function(){r.poster=n,e.detail.firesLoad=!1,o.fire(r,"_lazyloaded",{},!0,!0)}))}},!(d=/\(|\)|\s|'/)))});
PK.�1\N/>>external/js/unslider.cssnu�[���.unslider{overflow:auto;margin:0;padding:0}.unslider-wrap{position:relative}.unslider-wrap.unslider-carousel>li{float:left}.unslider-vertical>ul{height:100%}.unslider-vertical li{float:none;width:100%}.unslider-fade{position:relative}.unslider-fade .unslider-wrap li{position:absolute;left:0;top:0;right:0;z-index:8}.unslider-fade .unslider-wrap li.unslider-active{z-index:10}.unslider li,.unslider ol,.unslider ul{list-style:none;margin:0;padding:0;border:none}.unslider-arrow{position:absolute;left:20px;z-index:2;cursor:pointer}.unslider-arrow.next{left:auto;right:20px}
PK.�1\���"q%q%#external/do_not_donate_smallest.pngnu�[����PNG


IHDRdU��A\sRGB���gAMA���a cHRMz&�����u0�`:�p��Q<tEXtSoftwarePaint.NET v3.5.6Ѓ�Z$�IDATx^�XT�ֆ��b��^���{�
�{�^�5��1���Q{4VK��^�����3s��kDpD�����y�=s���Z��ٓ<��B�d)xzi�-�DJ��$=�LJ�t$y�����Y�%��K���z��2W���� iA�M����yM�HQ���u���@~#3IR�d"��4wz�߳bR�*�2����	%R����D��\�F��Z̲-�E��8lk��(���N�Z���o�z"�r�L$��g����ؑ�dw$nȒ�L�u4�%�&�"K��_����5�f�Gt�!!b+�H�t��#yH�ZUxq�H12�\������y���JH��E\�K6�܃�Ƥv=I�?	Α!�V��/��Eee��tq��!b
+
'�,$�b�XQLb�c)a�t�I=IE��!D�~4�{�'�D ٯ+���'��H"�O>6�ąȾ.��d�D��D:�Q�ּ����ۦ�L�\Fr�#��?J��DRb�d։C��zN~'��7��I�l,%�N'�C$�$��@lHNR�t#3�>"��%��RDX�1�!�/�e�0�EDZTb:ZqE>ă8�J$;IMd|���|D0	��b;�$�7�a$_:Ldh����M7��O$T��`"�$��H��"(� �����a)�>��*��"�	�LQxb�.&�����!q)=I��"�II%qm2v&#C6	F��D*���X��[�'$��)��;!9�T�D`Ӊ���O&;��$�{�_IBݬ��� ����~�UM�M*ӝ$���2n"����ylI\k�2�&� �5�$��d��D����^���L��E$[��%C��vK;|Y�!�M�|���d�<�`��7,�C��Nm�q>�:�ʉ�IBYq���X���M-J"!��`ik�?A$���Đ�^������^StC�F)�^����t��YZ:|.����W|QQ���eH�/�1�Z{�D@Ib�~.���8R�D��K�$9ً��h�p���X�DM�#zI6g��-ʭW���K"C�y��[�h�'����|��mt�X�yH|m3r��0��R�0]�d�2" n&!��9Ѥ�ʠ�<���_<Pz"٭%-J.T:�r���XP<�NčZ��"�{��3D�gq����	bt�H�B���&�4w�ϐ"��/���I��ߦ��15S!K�|Hn�5d�-)-#f]��1�#G���o2���d4a�2&���]5�C���y!�y�C�4)��n�r�Of�ň�H�Ȏ�}s��MN�1���r��ׄ��~�G^\�\�;�Q\2��~.�v��!k��l$򔉹��5�Kq���Ƒ�q�Nq�W'���YM�]�R�AD*IVlLز�	�
7z��ou*bZ��(��"�7߲ҥF���q����<�:	#q��Xn1'�~�^�L�̀��|fIN;��1������'{.�١+pnxg��r+p�e��X�Jyo!}jK\�Lj�m�P|�$N�������R���0�uY�ڙ�Y�Z�>���C!z��*���aܫ^���ȝ�|”<9м���� ���`~~�7w�"�G��{�����e��"�q\���k}�7�#uꏯhK-+M`H�H���
��� \����=Ů��78ݬ��-�M�������%�QvvW?�S��qm�\4X�7QVV�O?�\�[���G����[�,��.(z"�.(��x�<W��XE�T����T�Uw�p�RtoB�:ao���[�9/�]Ǯ��^͋���1���G�Q�8Ѱ,�b!�5��ez��Sã��t0or1%�)\�6 �>:�'�L����r!���ϖ��
�{*��#�M�
)S&��� ��Z��v
�#���@!�������#�]e�
9�!o���-�o�-�:�Ůrn8�;��j�XC�#
��j(Y���Jtm�������>߿K?�Gk^�l���J���=|��1s��"-k@�X��a۬����8ۀ����[���I:<����ф�}(���p6���Y�w��9����F�0�Ǯ2�q��[��t[�}���+rؙ$kf`�-]֝X��s�mB������A���A�5���;����M��c�7��VH���Z"��X!�q.�?�����k�D,�k�zE"�f��0��$o�����7���<^�� ��䁛�X��gx��~�\�W5<�C�A
�ә�@������H���,6z�2=^_�ş��|�s���.p���{h�dm�<-"z��N�p���N�ѿ��HS��Q-
��E�ե�,�0
�!�H;߰���wh6v��p�t�)�<��"�O*8��E�����U����眦�E�!RF��2��Y�}�}�����9zY�^f#hYO!mv޲����,7 �"<?�?߯�n��bv,��)���,Ì<=���gD�(w�ݢ(v��~m�|ݔzv�]��V�i-(3.@�K5�3��}���0�cj�uj�����K5�ﵙ?���۷]{�sDSOOFc�{'d�KyBʸypȠv,�T�JYZh�kiXM���W�zz,�
<fPޛʂ})��J�2Y��DrB��p�����Pua|�rsx%�t���l������P��	�X>ayt�R��u*k&����ztH��>��*v�RW�Ґ�Ʋ��Pc�*�z�6��e*����p��a��4(��v���(�&�r�!�jB��.��#����W�2���,P?�7(��:�'�DK!\��	S�D޽��(AN6��k��jo]�3.P�vR��+�įˠ�f7p��P+�+�_���J�w@mٶRYU�r���L���-Z�F~?G9v��T��9
Q��W�怠	���ˈ��Xn���A��i�Gh�`.(���Vs�H���A�_)uv�A��3�B}�h��y��{x>�e꾇Aխ��Y�ʘjt7Y_��"��$܈��0u�w��]-0�q\ɳ|;����`s���|�A���c��,��c>�RE�@m�BA<�WA�ܕ��Y*e���i߉�犫
�ݭ��P�W�BM��T����H.�m��V7�mUnn+��5?�uW(��6h�\���_?O-�Z�odO�'g���8u���~[5G��S{}g������������&̚�����n�CS���*%Tۺ���M�s�$,wGy�3��*\n�Q�a2t�`d5;+��C-�/�.O�z��x�(<�@��f��j��-��2?wܾ�YU�2V��:PM�5[�<�I�����|�܊=ǩd��a��P�vY�n���k���h�����꺨2mG�u��n�*��j�ڹ5�[g��V!V#�y�&��uTE���&̘��_ݪ-\�P+�f�f��e��e����q����M�SS]��֫U���:֋I"��m�ڜu�}�:�<,hg��p�A	�(���bP���o�o.���Ί����	Pٳ��AEZ�R�|]��v[6�^
5
�]�a�e�
�\��
p��-RT��|?�nlV��.V�u��\T�AՄ�3T�>�U���J��Š�J�PzF?�\�9������mnt/T_�l�M�3K?����t��<�[�P?�\�<w��:���;�\&M�^�ܬ��$Ňݥ�Sը�RWW�/�D��6R]�I�T�G���B_�u<~��HwuZ�ԾF0��1�Ӡ���?�u�,ec>�W�*Ay-�v[tk#���=�W��=�ŵӫ�q�|�|��ܱ_P��p��:����\�_��5[�ˤ)��J���<��xӡh\�|jV��dK+��z*e
DT,�ouT9*��&͞���nTO��*vedֲ9���
*�g�Z�j�J_e��z�$���:�z��(臎%H%��\�W6��-Ey��PK�Տ��j_����%�g�&�v��9Q�ع�G����X
CkY�*kF��� �l���Q��X�s�	3�������,�e.����]p�s)�_0���Z����
e�/�ǹ}�нFxe�
}FRbeZ�2��`��]w��wQ�=�����YƆAW�:���S�b�wS��Cz6�>c&����J�f�Mm� 
�)�=ՠ�e��F��N:�-P��ڞگ6;.N���vW<k�Ϟ:�!�+����X�5(G�5�{��u�\�o<�ua��������Ј���hԛb���
�������8���#&MB�J�����w��L9`�|s-ޒ����J�7mk"��[̜=�+�G����{�z��b�3ixn�G����)������j�$�]e�ӕ������f&�u
l���.:w��wVW���7K����/�+U�Ɲ�g8p`-��
��\ŵt�C1~�0d�6eZ|�{δ��F^�3�#k�A(P���끇{��v�HQa�O��ɾ��>x=�e��.y�IIX�2-�+��
-��	m��"M�~=�+�l��ܧ���C��]pveS\Z��V�����ཪ9�[����S�Y��z��fQ�T��*����3��z�J�o�(ˡ�N�A�	���ϼ�r�`H��hv	<�ZUZ�0����;��ϵpye-\T����]�o`W�L���UF�fG���)*�Gg�mNe<�Y�S%Y$%��.&䯊C�y���j�P}g�T���5���*x��2Fn
+�~hյN�X�V���mP�Y_����‰�%p��"�
`� �������*�(�g���V.R�����%��҇4�k��p��,?�8`hgA&,�|�^����z_$g���@�&=Q����ˉC���z6��e��f�n�mz�r���V�$��}�$
k�����s~'�W���jA��sj!W��l8��Ф�Ml���8���5�,��rͺñ�3�w�^�F�e���P�
b�c)\��Ë̙�R��6�JA�
p+���uڎ�:��ґo7��xp�}�d8T��ۢ�+�����-d� �R�047VM��|u���.��p�2���W���uZm<�P�5�R��f9;�=G!�L�6��Z=��{�%p�iQ8�m:���*t�%x�
�ҭ
�뉲��c�5���D�V^L��֝�Á�\�}���
M��%��}�v#3ƏC�%0�Z5�	���Ov
[�xLAOtj{	�FԶ��o6=�u�}�d�d�bd����''K)HQ�ޭR@�?9����_��R�6: r�=��t�;/�ȖP��V՚�I�4�J��2#bJ!��d�/���F5>EB�8��x��""��S���Ak	�\��\]Z�D�,Yp��qDDD <<k֬A޼yѭN���\\��Q؈�
��:Z�	Z�����5Eg�<�t�d�+=r*7��$�C��C�rރ��&������ʒ�p�e=���-N�s���,]��I��tI�y#=+���-d���a�g-�YƁD���µ1���.2eʄ'N 2�w�)J� =���$���<��[O�3��cC�]�t�[�����xݙCR%�x0�F�Q�%��=u����C|�5O�Њ�����B*sr}#n��5.�r��=�GZ�2�J���xi)F�|��<����1���љ�ł#/�tC�v
��}E\��_?~A�ِ�7��oh��@�F4׳z��x�SJE=��;��v�
;J.�c>����!�rr00�7��-}�i͡�g�E�m*��͡�0���GԹg��0�����.�ܘx��q���8���"^�Өq=b$�Sq��|Z�x�	=�;봝��n,:��b��*��(ۧ��p��\K�ġV\��pi���)�E����B.��YH��xPCn7���
py&�.����b4��0�O"ː���d(���.��y�+�|e�X�����tj�8^A�֨}\��d�x�k����
<��FbP�k=�T|���
��(r��h�g׆d��knvx���qj���?+�Zeb5�-Z��o���-	Ek�s������"�92Wa -(ݔ%��t��t
Szy\�G��&��?��Xn���-��7�.�B�V
�uY�AojL#�
|f` 4o0�{��;4��W{��B7���/��S�݇���7����?�/�=��Eö����V�!��qg���k�;D�8�-
�qcѓ�hIn���$
�Cu��x���T��]�QӪi�=#ฉ@�	�',��$?��)~��Sx~c�n��=Y�v�1�jȾec֬��ҍ�s(�XlQ�\�Y���O�:�=<bG��������6��ַR�d"HН���~�@�\7�9��l?J�V�:�>����Ӏ�3���
�[��n�6F�U�9�.ixr^Á���Jʇ�)����?Pp/���xa*��A�g|��=dyl.p�{�/�ŝ#����$��5�Q�߻�|�6-t��(�{����N��7�Z�pe��gs����4�16!|b�� �KG�s��9��5
�<\��!xS�Uz�
w�A��ߧ�a����5������y4��&^���C�H�,��9��-���륡�SD�n:�	�d�a���>_��q�;'��Z��=�ú6�7Ď��-�`���ԩ:w���_�c�M&��ZΠQ_��n[+����s��Ҳ����Ʀ82}���G�3�t�j�m���eݻ�j�+�m*ڟ�˿����C�9�|�3L��>�R%�Te�Ω����)�)K���@��*m�PU"���y���j�V,&S�ل|��E�Dn)6.|K�adx�l��(d�a.,:�Z)�k��$w��hڴ)^�x�ׯ_��Çh��Y����MV����Nȑ#��|կ_?G�j�lڷo�i�…i�lْ���5���[�~�#ʲ�ߥ�3���\�,O��ll	�n�C�����L�$3�}�����o����@\y�ҲeK����ŧO��y���W�=0����}�>.T�І�ٳ�H��-[��3
P;�L{��Iܵ�d�3��e�qK����U���C�́����'o�Wi�3g��Y���+S�-ȳgό�PDxzz"((�+^˓'���/�$�T�R���Su���y�=��	g��d%���I�~X�<�	�����7nLAғ�7o�@7�aӦMFv-Y���ںF�L���U�VƱ-����<xиL�Zv��e�Ȝ8�BT����̙�PÆ
�������G�1�����a�9�����2�l�9
��&l���͛W���٩{��
�u�&4�˩7�K��>i���fm�K
Az��m�o�Ν�\�ri����<xpn��O����"c��o���>drKw"�%�d��>|8u�f�j��_fMv[�{�s�^�>�O��q	B7��݇(PqY�"�����9�z��=ɚ5�R���lK�^��#�U�)�d�IK��Ĉ&�����}J1�Ο?����.�����u8��h
�Ջ�\�T�N՜I_lAv��
��#G��^�|�q��C�s�ν�D�Sč��kҤ�����amm-ő�+I~�.s"J+NL�h��Ofk�"�l��]�r%�mcc�cǎ8u�._��3�O��=z��#�؂<y��u떱���<x�۷o-!v"��y��uE�2|� ��D~�/se%���I$5�䲰>?z5V`�
�A�CW��W�^aٲehݺ5�-�� �"XR���0�|�}���	"�K&��>6ĕH�.�Od�ׄ'J�����7]TM۬Y3U���Ɩ..���~��1�����%BD�#n�����(,:"�c$��İ��%	L��1o�9��C�Aٵk����8�f͗�G��Ν;bl�"JϞ=���9s&Q��%�'�$���T�Й=���V�n�d�T|�#}�7
���ag翆��E�g0~~~�O�٪{��D��*B�K�l�mI��Q��'>AJ��+4Yc-�*�:���ll�+���%{v��O�wq��h�FB�vY��BLV"؊HFmv�1�{��e�I�;��Df��$˜��I��t��^�O��5�T����JR��ra���FAط���1�~�!!}�g�$�L�'�t��O��]]��;�%4Ifg��L�&�&�'F[��vRː�AG�����?G,Z�Ș7H����_� &Q�uMd���$�"�Lh,S��Sɸ��Ӎ�#��(�+�^$�ܴ�L�,[Jrف�Dd�.a����'��i3��5���8�B�|��޽k��Gt?V��9K�GYq�m^��H�*�uB�WL˒ AO	�ee.�F�_�ѓ�Qr!J�ʗa��&F��F�+d�Qɗ�ύ��Ke%��Z�J�m�.U
m��x{{'���CW�O�y���1�Uy��EdZؤJE RyeI8�gWJ�JqE��Ԃ`�͏�?��-W��DC�^�0
T��R�J2ABCC���<���Aƌ�0���unv{V�X������+�m#	:��.$��D���(ì�T�o%� 2�;f��+WNspp���C�<!?+M�2�@�>%��WT�	�(t+aL�
N�&|��ս���]�1}�ۭ[�s���C$��aZ�V�p�on޼����ޱV�t�2"���/mb�L�c��E��@a|���ȼ�a�ҥ��_�
�$	{c
B���O�[���?;��&?�EX�Q��FQR�e�x���`]���/_��%�C
�c��&�R�ALa���2���Է�L%v�%I,��h���(�uZ�d-s�2a
Xnۖ��7eŊQ�|�!�7���$6�mex�k׮zFVa��^<�gH��jL}��_�����o�d�I�Y*����l�Y���}G]��ִ���F��[��-+n8]�A����;�JM���|��]��`�x�rf�%*��n�?S)aFr���S#љ!a�w��fee^��XD���w )آ��H6>�p�f͚�p�B��V�1��-� 2��~��j<�Y�9W0u���d�]"�aDđAG��X2����S���>$��\EL	�%���D�K>�`��~|�W��M� �'O�_b����9���
�PY�J�o��#��$^D�nV�^�����Z��R�O�Ed"��8���V��/�7�2� *T0���ҥK	B�}�+� �9�DW�T��YI0��c���	d�m�[�����X����C�x_Ƶ>*��С�Q>膪U��}�EȳXB�ڵQ�xq�YC����ON�tIEND�B`�PK.�1\B.xG��external/index.htmlnu�[���<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
PK.�1\B.xG��.external/php/yui-php-cssmin-bundled/index.htmlnu�[���<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
PK.�1\����-external/php/yui-php-cssmin-bundled/Utils.phpnu�[���<?php

namespace Autoptimize\tubalmartin\CssMin;

class Utils
{
    /**
     * Clamps a number between a minimum and a maximum value.
     * @param int|float $n the number to clamp
     * @param int|float $min the lower end number allowed
     * @param int|float $max the higher end number allowed
     * @return int|float
     */
    public static function clampNumber($n, $min, $max)
    {
        return min(max($n, $min), $max);
    }

    /**
     * Clamps a RGB color number outside the sRGB color space
     * @param int|float $n the number to clamp
     * @return int|float
     */
    public static function clampNumberSrgb($n)
    {
        return self::clampNumber($n, 0, 255);
    }

    /**
     * Converts a HSL color into a RGB color
     * @param array $hslValues
     * @return array
     */
    public static function hslToRgb($hslValues)
    {
        $h = floatval($hslValues[0]);
        $s = floatval(str_replace('%', '', $hslValues[1]));
        $l = floatval(str_replace('%', '', $hslValues[2]));

        // Wrap and clamp, then fraction!
        $h = ((($h % 360) + 360) % 360) / 360;
        $s = self::clampNumber($s, 0, 100) / 100;
        $l = self::clampNumber($l, 0, 100) / 100;

        if ($s == 0) {
            $r = $g = $b = self::roundNumber(255 * $l);
        } else {
            $v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
            $v1 = (2 * $l) - $v2;
            $r = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h + (1/3)));
            $g = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h));
            $b = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h - (1/3)));
        }

        return array($r, $g, $b);
    }

    /**
     * Tests and selects the correct formula for each RGB color channel
     * @param $v1
     * @param $v2
     * @param $vh
     * @return mixed
     */
    public static function hueToRgb($v1, $v2, $vh)
    {
        $vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);

        if ($vh * 6 < 1) {
            return $v1 + ($v2 - $v1) * 6 * $vh;
        }

        if ($vh * 2 < 1) {
            return $v2;
        }

        if ($vh * 3 < 2) {
            return $v1 + ($v2 - $v1) * ((2 / 3) - $vh) * 6;
        }

        return $v1;
    }

    /**
     * Convert strings like "64M" or "30" to int values
     * @param mixed $size
     * @return int
     */
    public static function normalizeInt($size)
    {
        if (is_string($size)) {
            $letter = substr($size, -1);
            $size = intval($size);
            switch ($letter) {
                case 'M':
                case 'm':
                    return (int) $size * 1048576;
                case 'K':
                case 'k':
                    return (int) $size * 1024;
                case 'G':
                case 'g':
                    return (int) $size * 1073741824;
            }
        }
        return (int) $size;
    }

    /**
     * Converts a string containing and RGB percentage value into a RGB integer value i.e. '90%' -> 229.5
     * @param $rgbPercentage
     * @return int
     */
    public static function rgbPercentageToRgbInteger($rgbPercentage)
    {
        if (strpos($rgbPercentage, '%') !== false) {
            $rgbPercentage = self::roundNumber(floatval(str_replace('%', '', $rgbPercentage)) * 2.55);
        }

        return intval($rgbPercentage, 10);
    }

    /**
     * Converts a RGB color into a HEX color
     * @param array $rgbColors
     * @return array
     */
    public static function rgbToHex($rgbColors)
    {
        $hexColors = array();

        // Values outside the sRGB color space should be clipped (0-255)
        for ($i = 0, $l = count($rgbColors); $i < $l; $i++) {
            $hexColors[$i] = sprintf("%02x", self::clampNumberSrgb(self::rgbPercentageToRgbInteger($rgbColors[$i])));
        }

        return $hexColors;
    }

    /**
     * Rounds a number to its closest integer
     * @param $n
     * @return int
     */
    public static function roundNumber($n)
    {
        return intval(round(floatval($n)), 10);
    }
}
PK.�1\�ڸH
�
�0external/php/yui-php-cssmin-bundled/Minifier.phpnu�[���<?php

/*!
 * CssMin
 * Author: Tubal Martin - http://tubalmartin.me/
 * Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
 *
 * This is a PHP port of the CSS minification tool distributed with YUICompressor,
 * itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
 * Permission is hereby granted to use the PHP version under the same
 * conditions as the YUICompressor.
 */

/*!
 * YUI Compressor
 * http://developer.yahoo.com/yui/compressor/
 * Author: Julien Lecomte - http://www.julienlecomte.net/
 * Copyright (c) 2013 Yahoo! Inc. All rights reserved.
 * The copyrights embodied in the content of this file are licensed
 * by Yahoo! Inc. under the BSD (revised) open source license.
 */

namespace Autoptimize\tubalmartin\CssMin;

class Minifier
{
    const QUERY_FRACTION = '_CSSMIN_QF_';
    const COMMENT_TOKEN = '_CSSMIN_CMT_%d_';
    const COMMENT_TOKEN_START = '_CSSMIN_CMT_';
    const RULE_BODY_TOKEN = '_CSSMIN_RBT_%d_';
    const PRESERVED_TOKEN = '_CSSMIN_PTK_%d_';
    const UNQUOTED_FONT_TOKEN = '_CSSMIN_UFT_%d_';

    // Token lists
    private $comments = array();
    private $ruleBodies = array();
    private $preservedTokens = array();
    private $unquotedFontTokens = array();

    // Output options
    private $keepImportantComments = true;
    private $keepSourceMapComment = false;
    private $linebreakPosition = 0;

    // PHP ini limits
    private $raisePhpLimits;
    private $memoryLimit;
    private $maxExecutionTime = 60; // 1 min
    private $pcreBacktrackLimit;
    private $pcreRecursionLimit;

    // Color maps
    private $hexToNamedColorsMap;
    private $namedToHexColorsMap;

    // Regexes
    private $numRegex;
    private $charsetRegex = '/@charset [^;]+;/Si';
    private $importRegex = '/@import [^;]+;/Si';
    private $namespaceRegex = '/@namespace [^;]+;/Si';
    private $namedToHexColorsRegex;
    private $shortenOneZeroesRegex;
    private $shortenTwoZeroesRegex;
    private $shortenThreeZeroesRegex;
    private $shortenFourZeroesRegex;
    private $unitsGroupRegex = '(?:ch|cm|em|ex|gd|in|mm|px|pt|pc|q|rem|vh|vmax|vmin|vw|%)';
    private $unquotedFontsRegex = '/(font-family:|font:)([^\'"]+?)[^};]*/Si';

    /**
     * @param bool|int $raisePhpLimits If true, PHP settings will be raised if needed
     */
    public function __construct($raisePhpLimits = true)
    {
        $this->raisePhpLimits = (bool) $raisePhpLimits;
        $this->memoryLimit = 128 * 1048576; // 128MB in bytes
        $this->pcreBacktrackLimit = 1000 * 1000;
        $this->pcreRecursionLimit = 500 * 1000;
        $this->hexToNamedColorsMap = Colors::getHexToNamedMap();
        $this->namedToHexColorsMap = Colors::getNamedToHexMap();
        $this->namedToHexColorsRegex = sprintf(
            '/([:,( ])(%s)( |,|\)|;|$)/Si',
            implode('|', array_keys($this->namedToHexColorsMap))
        );
        $this->numRegex = sprintf('-?\d*\.?\d+%s?', $this->unitsGroupRegex);
        $this->setShortenZeroValuesRegexes();
    }

    /**
     * Parses & minifies the given input CSS string
     * @param string $css
     * @return string
     */
    public function run($css = '')
    {
        if (empty($css) || !is_string($css)) {
            return '';
        }

        $this->resetRunProperties();

        if ($this->raisePhpLimits) {
            $this->doRaisePhpLimits();
        }

        return $this->minify($css);
    }

    /**
     * Sets whether to keep or remove sourcemap special comment.
     * Sourcemap comments are removed by default.
     * @param bool $keepSourceMapComment
     */
    public function keepSourceMapComment($keepSourceMapComment = true)
    {
        $this->keepSourceMapComment = (bool) $keepSourceMapComment;
    }

    /**
     * Sets whether to keep or remove important comments.
     * Important comments outside of a declaration block are kept by default.
     * @param bool $removeImportantComments
     */
    public function removeImportantComments($removeImportantComments = true)
    {
        $this->keepImportantComments = !(bool) $removeImportantComments;
    }

    /**
     * Sets the approximate column after which long lines will be splitted in the output
     * with a linebreak.
     * @param int $position
     */
    public function setLineBreakPosition($position)
    {
        $this->linebreakPosition = (int) $position;
    }

    /**
     * Sets the memory limit for this script
     * @param int|string $limit
     */
    public function setMemoryLimit($limit)
    {
        $this->memoryLimit = Utils::normalizeInt($limit);
    }

    /**
     * Sets the maximum execution time for this script
     * @param int|string $seconds
     */
    public function setMaxExecutionTime($seconds)
    {
        $this->maxExecutionTime = (int) $seconds;
    }

    /**
     * Sets the PCRE backtrack limit for this script
     * @param int $limit
     */
    public function setPcreBacktrackLimit($limit)
    {
        $this->pcreBacktrackLimit = (int) $limit;
    }

    /**
     * Sets the PCRE recursion limit for this script
     * @param int $limit
     */
    public function setPcreRecursionLimit($limit)
    {
        $this->pcreRecursionLimit = (int) $limit;
    }

    /**
     * Builds regular expressions needed for shortening zero values
     */
    private function setShortenZeroValuesRegexes()
    {
        $zeroRegex = '0'. $this->unitsGroupRegex;
        $numOrPosRegex = '('. $this->numRegex .'|top|left|bottom|right|center) ';
        $oneZeroSafeProperties = array(
            '(?:line-)?height',
            '(?:(?:min|max)-)?width',
            'top',
            'left',
            'background-position',
            'bottom',
            'right',
            'border(?:-(?:top|left|bottom|right))?(?:-width)?',
            'border-(?:(?:top|bottom)-(?:left|right)-)?radius',
            'column-(?:gap|width)',
            'margin(?:-(?:top|left|bottom|right))?',
            'outline-width',
            'padding(?:-(?:top|left|bottom|right))?'
        );

        // First zero regex
        $regex = '/(^|;)('. implode('|', $oneZeroSafeProperties) .'):%s/Si';
        $this->shortenOneZeroesRegex = sprintf($regex, $zeroRegex);

        // Multiple zeroes regexes
        $regex = '/(^|;)(margin|padding|border-(?:width|radius)|background-position):%s/Si';
        $this->shortenTwoZeroesRegex = sprintf($regex, $numOrPosRegex . $zeroRegex);
        $this->shortenThreeZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $zeroRegex);
        $this->shortenFourZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $numOrPosRegex . $zeroRegex);
    }

    /**
     * Resets properties whose value may change between runs
     */
    private function resetRunProperties()
    {
        $this->comments = array();
        $this->ruleBodies = array();
        $this->preservedTokens = array();
    }

    /**
     * Tries to configure PHP to use at least the suggested minimum settings
     * @return void
     */
    private function doRaisePhpLimits()
    {
        $phpLimits = array(
            'memory_limit' => $this->memoryLimit,
            'max_execution_time' => $this->maxExecutionTime,
            'pcre.backtrack_limit' => $this->pcreBacktrackLimit,
            'pcre.recursion_limit' =>  $this->pcreRecursionLimit
        );

        // If current settings are higher respect them.
        foreach ($phpLimits as $name => $suggested) {
            $current = Utils::normalizeInt(ini_get($name));

            if ($current >= $suggested) {
                continue;
            }

            // memoryLimit exception: allow -1 for "no memory limit".
            if ($name === 'memory_limit' && $current === -1) {
                continue;
            }

            // maxExecutionTime exception: allow 0 for "no memory limit".
            if ($name === 'max_execution_time' && $current === 0) {
                continue;
            }

            ini_set($name, $suggested);
        }
    }

    /**
     * Registers a preserved token
     * @param string $token
     * @return string The token ID string
     */
    private function registerPreservedToken($token)
    {
        $tokenId = sprintf(self::PRESERVED_TOKEN, count($this->preservedTokens));
        $this->preservedTokens[$tokenId] = $token;
        return $tokenId;
    }

    /**
     * Registers a candidate comment token
     * @param string $comment
     * @return string The comment token ID string
     */
    private function registerCommentToken($comment)
    {
        $tokenId = sprintf(self::COMMENT_TOKEN, count($this->comments));
        $this->comments[$tokenId] = $comment;
        return $tokenId;
    }

    /**
     * Registers a rule body token
     * @param string $body the minified rule body
     * @return string The rule body token ID string
     */
    private function registerRuleBodyToken($body)
    {
        if (empty($body)) {
            return '';
        }

        $tokenId = sprintf(self::RULE_BODY_TOKEN, count($this->ruleBodies));
        $this->ruleBodies[$tokenId] = $body;
        return $tokenId;
    }

    private function registerUnquotedFontToken($body)
    {
        if (empty($body)) {
            return '';
        }

        $tokenId = sprintf(self::UNQUOTED_FONT_TOKEN, count($this->unquotedFontTokens));
        $this->unquotedFontTokens[$tokenId] = $body;
        return $tokenId;
    }

    /**
     * Parses & minifies the given input CSS string
     * @param string $css
     * @return string
     */
    private function minify($css)
    {
        // Process data urls
        $css = $this->processDataUrls($css);

        // Process comments
        $css = preg_replace_callback(
            '/(?<!\\\\)\/\*(.*?)\*(?<!\\\\)\//Ss',
            array($this, 'processCommentsCallback'),
            $css
        );

        // IE7: Process Microsoft matrix filters (whitespaces between Matrix parameters). Can contain strings inside.
        $css = preg_replace_callback(
            '/filter:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^)]+)\)/Ss',
            array($this, 'processOldIeSpecificMatrixDefinitionCallback'),
            $css
        );

        // Process quoted unquotable attribute selectors to unquote them. Covers most common cases.
        // Likelyhood of a quoted attribute selector being a substring in a string: Very very low.
        $css = preg_replace(
            '/\[\s*([a-z][a-z-]+)\s*([\*\|\^\$~]?=)\s*[\'"](-?[a-z_][a-z0-9-_]+)[\'"]\s*\]/Ssi',
            '[$1$2$3]',
            $css
        );

        // Process strings so their content doesn't get accidentally minified
        $css = preg_replace_callback(
            '/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S",
            array($this, 'processStringsCallback'),
            $css
        );

        // Normalize all whitespace strings to single spaces. Easier to work with that way.
        $css = preg_replace('/\s+/S', ' ', $css);

        // Process import At-rules with unquoted URLs so URI reserved characters such as a semicolon may be used safely.
        $css = preg_replace_callback(
            '/@import url\(([^\'"]+?)\)( |;)/Si',
            array($this, 'processImportUnquotedUrlAtRulesCallback'),
            $css
        );

        // Process comments
        $css = $this->processComments($css);

        // Process rule bodies
        $css = $this->processRuleBodies($css);

        // Process at-rules and selectors
        $css = $this->processAtRulesAndSelectors($css);

        // Restore preserved rule bodies before splitting
        $css = strtr($css, $this->ruleBodies);

        // Split long lines in output if required
        $css = $this->processLongLineSplitting($css);

        // Restore preserved comments and strings
        $css = strtr($css, $this->preservedTokens);

        return trim($css);
    }

    /**
     * Searches & replaces all data urls with tokens before we start compressing,
     * to avoid performance issues running some of the subsequent regexes against large string chunks.
     * @param string $css
     * @return string
     */
    private function processDataUrls($css)
    {
        $ret = '';
        $searchOffset = $substrOffset = 0;

        // Since we need to account for non-base64 data urls, we need to handle
        // ' and ) being part of the data string.
        while (preg_match('/url\(\s*(["\']?)data:/Si', $css, $m, PREG_OFFSET_CAPTURE, $searchOffset)) {
            $matchStartIndex = $m[0][1];
            $dataStartIndex = $matchStartIndex + 4; // url( length
            $searchOffset = $matchStartIndex + strlen($m[0][0]);
            $terminator = $m[1][0]; // ', " or empty (not quoted)
            $terminatorRegex = '/(?<!\\\\)'. (strlen($terminator) === 0 ? '' : $terminator.'\s*') .'(\))/S';

            $ret .= substr($css, $substrOffset, $matchStartIndex - $substrOffset);

            // Terminator found
            if (preg_match($terminatorRegex, $css, $matches, PREG_OFFSET_CAPTURE, $searchOffset)) {
                $matchEndIndex = $matches[1][1];
                $searchOffset = $matchEndIndex + 1;
                $token = substr($css, $dataStartIndex, $matchEndIndex - $dataStartIndex);

                // Remove all spaces only for base64 encoded URLs.
                if (stripos($token, 'base64,') !== false) {
                    $token = preg_replace('/\s+/S', '', $token);
                }

                $ret .= 'url('. $this->registerPreservedToken(trim($token)) .')';
            // No end terminator found, re-add the whole match. Should we throw/warn here?
            } else {
                $ret .= substr($css, $matchStartIndex, $searchOffset - $matchStartIndex);
            }

            $substrOffset = $searchOffset;
        }

        $ret .= substr($css, $substrOffset);

        return $ret;
    }

    /**
     * Registers all comments found as candidates to be preserved.
     * @param array $matches
     * @return string
     */
    private function processCommentsCallback($matches)
    {
        return '/*'. $this->registerCommentToken($matches[1]) .'*/';
    }

    /**
     * Preserves old IE Matrix string definition
     * @param array $matches
     * @return string
     */
    private function processOldIeSpecificMatrixDefinitionCallback($matches)
    {
        return 'filter:progid:DXImageTransform.Microsoft.Matrix('. $this->registerPreservedToken($matches[1]) .')';
    }

    /**
     * Preserves strings found
     * @param array $matches
     * @return string
     */
    private function processStringsCallback($matches)
    {
        $match = $matches[0];
        $quote = substr($match, 0, 1);
        $match = substr($match, 1, -1);

        // maybe the string contains a comment-like substring?
        // one, maybe more? put'em back then
        if (strpos($match, self::COMMENT_TOKEN_START) !== false) {
            $match = strtr($match, $this->comments);
        }

        // minify alpha opacity in filter strings
        $match = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $match);

        return $quote . $this->registerPreservedToken($match) . $quote;
    }

    /**
     * Searches & replaces all import at-rule unquoted urls with tokens so URI reserved characters such as a semicolon
     * may be used safely in a URL.
     * @param array $matches
     * @return string
     */
    private function processImportUnquotedUrlAtRulesCallback($matches)
    {
        return '@import url('. $this->registerPreservedToken($matches[1]) .')'. $matches[2];
    }

    /**
     * Preserves or removes comments found.
     * @param string $css
     * @return string
     */
    private function processComments($css)
    {
        foreach ($this->comments as $commentId => $comment) {
            $commentIdString = '/*'. $commentId .'*/';

            // ! in the first position of the comment means preserve
            // so push to the preserved tokens keeping the !
            if ($this->keepImportantComments && strpos($comment, '!') === 0) {
                $preservedTokenId = $this->registerPreservedToken($comment);
                // Put new lines before and after /*! important comments
                $css = str_replace($commentIdString, "\n/*$preservedTokenId*/\n", $css);
                continue;
            }

            // # sourceMappingURL= in the first position of the comment means sourcemap
            // so push to the preserved tokens if {$this->keepSourceMapComment} is truthy.
            if ($this->keepSourceMapComment && strpos($comment, '# sourceMappingURL=') === 0) {
                $preservedTokenId = $this->registerPreservedToken($comment);
                // Add new line before the sourcemap comment
                $css = str_replace($commentIdString, "\n/*$preservedTokenId*/", $css);
                continue;
            }

            // Keep empty comments after child selectors (IE7 hack)
            // e.g. html >/**/ body
            if (strlen($comment) === 0 && strpos($css, '>/*'.$commentId) !== false) {
                $css = str_replace($commentId, $this->registerPreservedToken(''), $css);
                continue;
            }

            // in all other cases kill the comment
            $css = str_replace($commentIdString, '', $css);
        }

        // Normalize whitespace again
        $css = preg_replace('/ +/S', ' ', $css);

        return $css;
    }

    /**
     * Finds, minifies & preserves all rule bodies.
     * @param string $css the whole stylesheet.
     * @return string
     */
    private function processRuleBodies($css)
    {
        $ret = '';
        $searchOffset = $substrOffset = 0;

        while (($blockStartPos = strpos($css, '{', $searchOffset)) !== false) {
            $blockEndPos = strpos($css, '}', $blockStartPos);
            // When ending curly brace is missing, let's
            // behave like there was one at the end of the block...
            if ( false === $blockEndPos ) {
                $blockEndPos = strlen($css) - 1;
            }
            $nextBlockStartPos = strpos($css, '{', $blockStartPos + 1);
            $ret .= substr($css, $substrOffset, $blockStartPos - $substrOffset);

            if ($nextBlockStartPos !== false && $nextBlockStartPos < $blockEndPos) {
                $ret .= substr($css, $blockStartPos, $nextBlockStartPos - $blockStartPos);
                $searchOffset = $nextBlockStartPos;
            } else {
                $ruleBody = substr($css, $blockStartPos + 1, $blockEndPos - $blockStartPos - 1);
                $ruleBodyToken = $this->registerRuleBodyToken($this->processRuleBody($ruleBody));
                $ret .= '{'. $ruleBodyToken .'}';
                $searchOffset = $blockEndPos + 1;
            }

            $substrOffset = $searchOffset;
        }

        $ret .= substr($css, $substrOffset);

        return $ret;
    }

    /**
     * Compresses non-group rule bodies.
     * @param string $body The rule body without curly braces
     * @return string
     */
    private function processRuleBody($body)
    {
        $body = trim($body);

        // Remove spaces before the things that should not have spaces before them.
        $body = preg_replace('/ ([:=,)*\/;\n])/S', '$1', $body);

        // Remove the spaces after the things that should not have spaces after them.
        $body = preg_replace('/([:=,(*\/!;\n]) /S', '$1', $body);

        // Replace multiple semi-colons in a row by a single one
        $body = preg_replace('/;;+/S', ';', $body);

        // Remove semicolon before closing brace except when:
        // - The last property is prefixed with a `*` (lte IE7 hack) to avoid issues on Symbian S60 3.x browsers.
        if (!preg_match('/\*[a-z0-9-]+:[^;]+;$/Si', $body)) {
            $body = rtrim($body, ';');
        }

        // Remove important comments inside a rule body (because they make no sense here).
        if (strpos($body, '/*') !== false) {
            $body = preg_replace('/\n?\/\*[A-Z0-9_]+\*\/\n?/S', '', $body);
        }

        // Empty rule body? Exit :)
        if (empty($body)) {
            return '';
        }

        // Shorten font-weight values
        $body = preg_replace(
            array('/(font-weight:)bold\b/Si', '/(font-weight:)normal\b/Si'),
            array('${1}700', '${1}400'),
            $body
        );

        // Shorten background property
        $body = preg_replace('/(background:)(?:none|transparent)( !|;|$)/Si', '${1}0 0$2', $body);

        // Shorten opacity IE filter
        $body = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $body);

        // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
        // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
        // This makes it more likely that it'll get further compressed in the next step.
        $body = preg_replace_callback(
            '/(rgb|hsl)\(([0-9,.% -]+)\)(.|$)/Si',
            array($this, 'shortenHslAndRgbToHexCallback'),
            $body
        );

        // Shorten colors from #AABBCC to #ABC or shorter color name:
        // - Look for hex colors which don't have a "=" in front of them (to avoid MSIE filters)
        $body = preg_replace_callback(
            '/(?<!=)#([0-9a-f]{3,6})( |,|\)|;|$)/Si',
            array($this, 'shortenHexColorsCallback'),
            $body
        );

        // Tokenize unquoted font names in order to hide them from
        // color name replacements.
        $body = preg_replace_callback(
            $this->unquotedFontsRegex,
            array($this, 'preserveUnquotedFontTokens'),
            $body
        );

        // Shorten long named colors with a shorter HEX counterpart: white -> #fff.
        // Run at least 2 times to cover most cases
        $body = preg_replace_callback(
            array($this->namedToHexColorsRegex, $this->namedToHexColorsRegex),
            array($this, 'shortenNamedColorsCallback'),
            $body
        );

        // Restore unquoted font tokens now after colors have been changed.
        $body = $this->restoreUnquotedFontTokens($body);

        // Replace positive sign from numbers before the leading space is removed.
        // +1.2em to 1.2em, +.8px to .8px, +2% to 2%
        $body = preg_replace('/([ :,(])\+(\.?\d+)/S', '$1$2', $body);

        // shorten ms to s
        $body = preg_replace_callback('/([ :,(])(-?)(\d{3,})ms/Si', function ($matches) {
            return $matches[1] . $matches[2] . ((int) $matches[3] / 1000) .'s';
        }, $body);

        // Remove leading zeros from integer and float numbers.
        // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
        $body = preg_replace('/([ :,(])(-?)0+([1-9]?\.?\d+)/S', '$1$2$3', $body);

        // Remove trailing zeros from float numbers.
        // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
        $body = preg_replace('/([ :,(])(-?\d?\.\d+?)0+([^\d])/S', '$1$2$3', $body);

        // Remove trailing .0 -> -9.0 to -9
        $body = preg_replace('/([ :,(])(-?\d+)\.0([^\d])/S', '$1$2$3', $body);

        // Replace 0 length numbers with 0
        $body = preg_replace('/([ :,(])-?\.?0+([^\d])/S', '${1}0$2', $body);

        // Shorten zero values for safe properties only
        $body = preg_replace(
            array(
                $this->shortenOneZeroesRegex,
                $this->shortenTwoZeroesRegex,
                $this->shortenThreeZeroesRegex,
                $this->shortenFourZeroesRegex
            ),
            array(
                '$1$2:0',
                '$1$2:$3 0',
                '$1$2:$3 $4 0',
                '$1$2:$3 $4 $5 0'
            ),
            $body
        );

        // Replace 0 0 0; or 0 0 0 0; with 0 0 for background-position property.
        $body = preg_replace('/(background-position):0(?: 0){2,3}( !|;|$)/Si', '$1:0 0$2', $body);

        // Shorten suitable shorthand properties with repeated values
        $body = preg_replace(
            array(
                '/(margin|padding|border-(?:width|radius)):('.$this->numRegex.')(?: \2)+( !|;|$)/Si',
                '/(border-(?:style|color)):([#a-z0-9]+)(?: \2)+( !|;|$)/Si'
            ),
            '$1:$2$3',
            $body
        );
        $body = preg_replace(
            array(
                '/(margin|padding|border-(?:width|radius)):'.
                '('.$this->numRegex.') ('.$this->numRegex.') \2 \3( !|;|$)/Si',
                '/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) \2 \3( !|;|$)/Si'
            ),
            '$1:$2 $3$4',
            $body
        );
        $body = preg_replace(
            array(
                '/(margin|padding|border-(?:width|radius)):'.
                '('.$this->numRegex.') ('.$this->numRegex.') ('.$this->numRegex.') \3( !|;|$)/Si',
                '/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) ([#a-z0-9]+) \3( !|;|$)/Si'
            ),
            '$1:$2 $3 $4$5',
            $body
        );

        // Lowercase some common functions that can be values
        $body = preg_replace_callback(
            '/(?:attr|blur|brightness|circle|contrast|cubic-bezier|drop-shadow|ellipse|from|grayscale|'.
            'hsla?|hue-rotate|inset|invert|local|minmax|opacity|perspective|polygon|rgba?|rect|repeat|saturate|sepia|'.
            'steps|to|url|var|-webkit-gradient|'.
            '(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|(?:repeating-)?(?:linear|radial)-gradient))\(/Si',
            array($this, 'strtolowerCallback'),
            $body
        );

        // Lowercase all uppercase properties
        $body = preg_replace_callback('/(?:^|;)[A-Z-]+:/S', array($this, 'strtolowerCallback'), $body);

        return $body;
    }

    private function preserveUnquotedFontTokens($matches)
    {
        return $this->registerUnquotedFontToken($matches[0]);
    }

    private function restoreUnquotedFontTokens($body)
    {
        return strtr($body, $this->unquotedFontTokens);
    }

    /**
     * Compresses At-rules and selectors.
     * @param string $css the whole stylesheet with rule bodies tokenized.
     * @return string
     */
    private function processAtRulesAndSelectors($css)
    {
        $charset = '';
        $imports = '';
        $namespaces = '';

        // Remove spaces before the things that should not have spaces before them.
        $css = preg_replace('/ ([@{};>+)\]~=,\/\n])/S', '$1', $css);

        // Remove the spaces after the things that should not have spaces after them.
        $css = preg_replace('/([{}:;>+(\[~=,\/\n]) /S', '$1', $css);

        // Shorten shortable double colon (CSS3) pseudo-elements to single colon (CSS2)
        $css = preg_replace('/::(before|after|first-(?:line|letter))(\{|,)/Si', ':$1$2', $css);

        // Retain space for special IE6 cases
        $css = preg_replace_callback('/:first-(line|letter)(\{|,)/Si', function ($matches) {
            return ':first-'. strtolower($matches[1]) .' '. $matches[2];
        }, $css);

        // Find a fraction that may used in some @media queries such as: (min-aspect-ratio: 1/1)
        // Add token to add the "/" back in later
        $css = preg_replace('/\(([a-z-]+):([0-9]+)\/([0-9]+)\)/Si', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);

        // Remove empty rule blocks up to 2 levels deep.
        $css = preg_replace(array_fill(0, 2, '/(\{)[^{};\/\n]+\{\}/S'), '$1', $css);
        $css = preg_replace('/[^{};\/\n]+\{\}/S', '', $css);

        // Two important comments next to each other? Remove extra newline.
        if ($this->keepImportantComments) {
            $css = str_replace("\n\n", "\n", $css);
        }

        // Restore fraction
        $css = str_replace(self::QUERY_FRACTION, '/', $css);

        // Lowercase some popular @directives
        $css = preg_replace_callback(
            '/(?<!\\\\)@(?:charset|document|font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframes|media|'.
            'namespace|page|supports|viewport)/Si',
            array($this, 'strtolowerCallback'),
            $css
        );

        // Lowercase some popular media types
        $css = preg_replace_callback(
            '/[ ,](?:all|aural|braille|handheld|print|projection|screen|tty|tv|embossed|speech)[ ,;{]/Si',
            array($this, 'strtolowerCallback'),
            $css
        );

        // Lowercase some common pseudo-classes & pseudo-elements
        $css = preg_replace_callback(
            '/(?<!\\\\):(?:active|after|before|checked|default|disabled|empty|enabled|first-(?:child|of-type)|'.
            'focus(?:-within)?|hover|indeterminate|in-range|invalid|lang\(|last-(?:child|of-type)|left|link|not\(|'.
            'nth-(?:child|of-type)\(|nth-last-(?:child|of-type)\(|only-(?:child|of-type)|optional|out-of-range|'.
            'read-(?:only|write)|required|right|root|:selection|target|valid|visited)/Si',
            array($this, 'strtolowerCallback'),
            $css
        );

        // @charset handling
        if (preg_match($this->charsetRegex, $css, $matches)) {
            // Keep the first @charset at-rule found
            $charset = $matches[0];
            // Delete all @charset at-rules
            $css = preg_replace($this->charsetRegex, '', $css);
        }

        // @import handling
        $css = preg_replace_callback($this->importRegex, function ($matches) use (&$imports) {
            // Keep all @import at-rules found for later
            $imports .= $matches[0];
            // Delete all @import at-rules
            return '';
        }, $css);

        // @namespace handling
        $css = preg_replace_callback($this->namespaceRegex, function ($matches) use (&$namespaces) {
            // Keep all @namespace at-rules found for later
            $namespaces .= $matches[0];
            // Delete all @namespace at-rules
            return '';
        }, $css);

        // Order critical at-rules:
        // 1. @charset first
        // 2. @imports below @charset
        // 3. @namespaces below @imports
        $css = $charset . $imports . $namespaces . $css;

        return $css;
    }

    /**
     * Splits long lines after a specific column.
     *
     * Some source control tools don't like it when files containing lines longer
     * than, say 8000 characters, are checked in. The linebreak option is used in
     * that case to split long lines after a specific column.
     *
     * @param string $css the whole stylesheet.
     * @return string
     */
    private function processLongLineSplitting($css)
    {
        if ($this->linebreakPosition > 0) {
            $l = strlen($css);
            $offset = $this->linebreakPosition;
            while (preg_match('/(?<!\\\\)\}(?!\n)/S', $css, $matches, PREG_OFFSET_CAPTURE, $offset)) {
                $matchIndex = $matches[0][1];
                $css = substr_replace($css, "\n", $matchIndex + 1, 0);
                $offset = $matchIndex + 2 + $this->linebreakPosition;
                $l += 1;
                if ($offset > $l) {
                    break;
                }
            }
        }

        return $css;
    }

    /**
     * Converts hsl() & rgb() colors to HEX format.
     * @param $matches
     * @return string
     */
    private function shortenHslAndRgbToHexCallback($matches)
    {
        $type = $matches[1];
        $values = explode(',', $matches[2]);
        $terminator = $matches[3];

        if ($type === 'hsl') {
            $values = Utils::hslToRgb($values);
        }

        $hexColors = Utils::rgbToHex($values);

        // Restore space after rgb() or hsl() function in some cases such as:
        // background-image: linear-gradient(to bottom, rgb(210,180,140) 10%, rgb(255,0,0) 90%);
        if (!empty($terminator) && !preg_match('/[ ,);]/S', $terminator)) {
            $terminator = ' '. $terminator;
        }

        return '#'. implode('', $hexColors) . $terminator;
    }

    /**
     * Compresses HEX color values of the form #AABBCC to #ABC or short color name.
     * @param $matches
     * @return string
     */
    private function shortenHexColorsCallback($matches)
    {
        $hex = $matches[1];

        // Shorten suitable 6 chars HEX colors
        if (strlen($hex) === 6 && preg_match('/^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/Si', $hex, $m)) {
            $hex = $m[1] . $m[2] . $m[3];
        }

        // Lowercase
        $hex = '#'. strtolower($hex);

        // Replace Hex colors with shorter color names
        $color = array_key_exists($hex, $this->hexToNamedColorsMap) ? $this->hexToNamedColorsMap[$hex] : $hex;

        return $color . $matches[2];
    }

    /**
     * Shortens all named colors with a shorter HEX counterpart for a set of safe properties
     * e.g. white -> #fff
     * @param array $matches
     * @return string
     */
    private function shortenNamedColorsCallback($matches)
    {
        return $matches[1] . $this->namedToHexColorsMap[strtolower($matches[2])] . $matches[3];
    }

    /**
     * Makes a string lowercase
     * @param array $matches
     * @return string
     */
    private function strtolowerCallback($matches)
    {
        return strtolower($matches[0]);
    }
}
PK.�1\P���}}.external/php/yui-php-cssmin-bundled/Colors.phpnu�[���<?php

namespace Autoptimize\tubalmartin\CssMin;

class Colors
{
    public static function getHexToNamedMap()
    {
        // Hex colors longer than named counterpart
        return array(
            '#f0ffff' => 'azure',
            '#f5f5dc' => 'beige',
            '#ffe4c4' => 'bisque',
            '#a52a2a' => 'brown',
            '#ff7f50' => 'coral',
            '#ffd700' => 'gold',
            '#808080' => 'gray',
            '#008000' => 'green',
            '#4b0082' => 'indigo',
            '#fffff0' => 'ivory',
            '#f0e68c' => 'khaki',
            '#faf0e6' => 'linen',
            '#800000' => 'maroon',
            '#000080' => 'navy',
            '#fdf5e6' => 'oldlace',
            '#808000' => 'olive',
            '#ffa500' => 'orange',
            '#da70d6' => 'orchid',
            '#cd853f' => 'peru',
            '#ffc0cb' => 'pink',
            '#dda0dd' => 'plum',
            '#800080' => 'purple',
            '#f00'    => 'red',
            '#fa8072' => 'salmon',
            '#a0522d' => 'sienna',
            '#c0c0c0' => 'silver',
            '#fffafa' => 'snow',
            '#d2b48c' => 'tan',
            '#008080' => 'teal',
            '#ff6347' => 'tomato',
            '#ee82ee' => 'violet',
            '#f5deb3' => 'wheat'
        );
    }

    public static function getNamedToHexMap()
    {
        // Named colors longer than hex counterpart
        return array(
            'aliceblue' => '#f0f8ff',
            'antiquewhite' => '#faebd7',
            'aquamarine' => '#7fffd4',
            'black' => '#000',
            'blanchedalmond' => '#ffebcd',
            'blueviolet' => '#8a2be2',
            'burlywood' => '#deb887',
            'cadetblue' => '#5f9ea0',
            'chartreuse' => '#7fff00',
            'chocolate' => '#d2691e',
            'cornflowerblue' => '#6495ed',
            'cornsilk' => '#fff8dc',
            'darkblue' => '#00008b',
            'darkcyan' => '#008b8b',
            'darkgoldenrod' => '#b8860b',
            'darkgray' => '#a9a9a9',
            'darkgreen' => '#006400',
            'darkgrey' => '#a9a9a9',
            'darkkhaki' => '#bdb76b',
            'darkmagenta' => '#8b008b',
            'darkolivegreen' => '#556b2f',
            'darkorange' => '#ff8c00',
            'darkorchid' => '#9932cc',
            'darksalmon' => '#e9967a',
            'darkseagreen' => '#8fbc8f',
            'darkslateblue' => '#483d8b',
            'darkslategray' => '#2f4f4f',
            'darkslategrey' => '#2f4f4f',
            'darkturquoise' => '#00ced1',
            'darkviolet' => '#9400d3',
            'deeppink' => '#ff1493',
            'deepskyblue' => '#00bfff',
            'dodgerblue' => '#1e90ff',
            'firebrick' => '#b22222',
            'floralwhite' => '#fffaf0',
            'forestgreen' => '#228b22',
            'fuchsia' => '#f0f',
            'gainsboro' => '#dcdcdc',
            'ghostwhite' => '#f8f8ff',
            'goldenrod' => '#daa520',
            'greenyellow' => '#adff2f',
            'honeydew' => '#f0fff0',
            'indianred' => '#cd5c5c',
            'lavender' => '#e6e6fa',
            'lavenderblush' => '#fff0f5',
            'lawngreen' => '#7cfc00',
            'lemonchiffon' => '#fffacd',
            'lightblue' => '#add8e6',
            'lightcoral' => '#f08080',
            'lightcyan' => '#e0ffff',
            'lightgoldenrodyellow' => '#fafad2',
            'lightgray' => '#d3d3d3',
            'lightgreen' => '#90ee90',
            'lightgrey' => '#d3d3d3',
            'lightpink' => '#ffb6c1',
            'lightsalmon' => '#ffa07a',
            'lightseagreen' => '#20b2aa',
            'lightskyblue' => '#87cefa',
            'lightslategray' => '#778899',
            'lightslategrey' => '#778899',
            'lightsteelblue' => '#b0c4de',
            'lightyellow' => '#ffffe0',
            'limegreen' => '#32cd32',
            'mediumaquamarine' => '#66cdaa',
            'mediumblue' => '#0000cd',
            'mediumorchid' => '#ba55d3',
            'mediumpurple' => '#9370db',
            'mediumseagreen' => '#3cb371',
            'mediumslateblue' => '#7b68ee',
            'mediumspringgreen' => '#00fa9a',
            'mediumturquoise' => '#48d1cc',
            'mediumvioletred' => '#c71585',
            'midnightblue' => '#191970',
            'mintcream' => '#f5fffa',
            'mistyrose' => '#ffe4e1',
            'moccasin' => '#ffe4b5',
            'navajowhite' => '#ffdead',
            'olivedrab' => '#6b8e23',
            'orangered' => '#ff4500',
            'palegoldenrod' => '#eee8aa',
            'palegreen' => '#98fb98',
            'paleturquoise' => '#afeeee',
            'palevioletred' => '#db7093',
            'papayawhip' => '#ffefd5',
            'peachpuff' => '#ffdab9',
            'powderblue' => '#b0e0e6',
            'rebeccapurple' => '#663399',
            'rosybrown' => '#bc8f8f',
            'royalblue' => '#4169e1',
            'saddlebrown' => '#8b4513',
            'sandybrown' => '#f4a460',
            'seagreen' => '#2e8b57',
            'seashell' => '#fff5ee',
            'slateblue' => '#6a5acd',
            'slategray' => '#708090',
            'slategrey' => '#708090',
            'springgreen' => '#00ff7f',
            'steelblue' => '#4682b4',
            'turquoise' => '#40e0d0',
            'white' => '#fff',
            'whitesmoke' => '#f5f5f5',
            'yellow' => '#ff0',
            'yellowgreen' => '#9acd32'
        );
    }
}
PK.�1\z7�U�"�"external/php/ao-minify-html.phpnu�[���<?php
/**
 * Class Minify_HTML
 * @package Minify
 */

/**
 * Compress HTML
 *
 * This is a heavy regex-based removal of whitespace, unnecessary comments and
 * tokens. IE conditional comments are preserved. There are also options to have
 * STYLE and SCRIPT blocks compressed by callback functions.
 *
 * A test suite is available.
 *
 * @package Minify
 * @author Stephen Clay <steve@mrclay.org>
 */
class AO_Minify_HTML {

    /** @var string */
    private $_html;

    /**
     * "Minify" an HTML page
     *
     * @param string $html
     *
     * @param array $options
     *
     * 'cssMinifier' : (optional) callback function to process content of STYLE
     * elements.
     *
     * 'jsMinifier' : (optional) callback function to process content of SCRIPT
     * elements. Note: the type attribute is ignored.
     *
     * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
     * unset, minify will sniff for an XHTML doctype.
     *
     * 'keepComments' : (optional boolean) should the HTML comments be kept
     * in the HTML Code?
     *
     * @return string
     */
    public static function minify($html, $options = array()) {
        $min = new AO_Minify_HTML($html, $options);
        return $min->process();
    }


    /**
     * Create a minifier object
     *
     * @param string $html
     *
     * @param array $options
     *
     * 'cssMinifier' : (optional) callback function to process content of STYLE
     * elements.
     *
     * 'jsMinifier' : (optional) callback function to process content of SCRIPT
     * elements. Note: the type attribute is ignored.
     *
     * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
     * unset, minify will sniff for an XHTML doctype.
     *
     * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
     * unset, minify will sniff for an XHTML doctype.
     *
     * @return null
     */
    public function __construct($html, $options = array())
    {
        $this->_html = str_replace("\r\n", "\n", trim($html));
        if (isset($options['xhtml'])) {
            $this->_isXhtml = (bool)$options['xhtml'];
        }
        if (isset($options['cssMinifier'])) {
            $this->_cssMinifier = $options['cssMinifier'];
        }
        if (isset($options['jsMinifier'])) {
            $this->_jsMinifier = $options['jsMinifier'];
        }
        if (isset($options['keepComments'])) {
            $this->_keepComments = $options['keepComments'];
        }
    }


    /**
     * Minify the markeup given in the constructor
     *
     * @return string
     */
    public function process()
    {
        if ($this->_isXhtml === null) {
            $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
        }

        $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
        $this->_placeholders = array();

        // replace SCRIPTs (and minify) with placeholders
        $this->_html = preg_replace_callback(
            '/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
            ,array($this, '_removeScriptCB')
            ,$this->_html);

        // replace STYLEs (and minify) with placeholders
        $this->_html = preg_replace_callback(
            '/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i'
            ,array($this, '_removeStyleCB')
            ,$this->_html);

        // remove HTML comments (not containing IE conditional comments).
        if  ($this->_keepComments == false) {
            $this->_html = preg_replace_callback(
                '/<!--([\\s\\S]*?)-->/'
                ,array($this, '_commentCB')
                ,$this->_html);
        }

        // replace PREs with placeholders
        $this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
            ,array($this, '_removePreCB')
            ,$this->_html);

        // replace TEXTAREAs with placeholders
        $this->_html = preg_replace_callback(
            '/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
            ,array($this, '_removeTextareaCB')
            ,$this->_html);

        // replace data: URIs with placeholders
        $this->_html = preg_replace_callback(
            '/(=("|\')data:.*\\2)/Ui'
            ,array($this, '_removeDataURICB')
            ,$this->_html);

        // trim each line.
        // replace by space instead of '' to avoid newline after opening tag getting zapped
        $this->_html = preg_replace('/^\s+|\s+$/m', ' ', $this->_html);

        // remove ws around block/undisplayed elements
        $this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body'
            .'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form'
            .'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav'
            .'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)'
            .'|ul|video)\\b[^>]*>)/i', '$1', $this->_html);

        // remove ws outside of all elements
        $this->_html = preg_replace_callback(
            '/>([^<]+)</'
            ,array($this, '_outsideTagCB')
            ,$this->_html);

        // use newlines before 1st attribute in open tags (to limit line lengths)
        //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);

        // reverse order while preserving keys to ensure the last replacement is done first, etc ...
        $this->_placeholders = array_reverse( $this->_placeholders, true );

        // fill placeholders
        $this->_html = str_replace(
            array_keys($this->_placeholders)
            ,array_values($this->_placeholders)
            ,$this->_html
        );
        return $this->_html;
    }

    protected function _commentCB($m)
    {
        return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
            ? $m[0]
            : '';
    }

    protected function _reservePlace($content)
    {
        $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
        $this->_placeholders[$placeholder] = $content;
        return $placeholder;
    }

    protected $_isXhtml = null;
    protected $_replacementHash = null;
    protected $_placeholders = array();
    protected $_cssMinifier = null;
    protected $_jsMinifier = null;
    protected $_keepComments = false;

    protected function _outsideTagCB($m)
    {
        return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
    }

    protected function _removePreCB($m)
    {
        return $this->_reservePlace($m[1]);
    }

    protected function _removeTextareaCB($m)
    {
        return $this->_reservePlace($m[1]);
    }

    protected function _removeDataURICB($m)
    {
        return $this->_reservePlace($m[1]);
    }

    protected function _removeStyleCB($m)
    {
        $openStyle = $m[1];
        $css = $m[2];
        // remove HTML comments
        $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);

        // remove CDATA section markers
        $css = $this->_removeCdata($css);

        // minify
        $minifier = $this->_cssMinifier
            ? $this->_cssMinifier
            : 'trim';
        $css = call_user_func($minifier, $css);

        return $this->_reservePlace($this->_needsCdata($css)
            ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
            : "{$openStyle}{$css}</style>"
        );
    }

    protected function _removeScriptCB($m)
    {
        $openScript = $m[2];
        $js = $m[3];

        // whitespace surrounding? preserve at least one space
        $ws1 = ($m[1] === '') ? '' : ' ';
        $ws2 = ($m[4] === '') ? '' : ' ';

        if ($this->_keepComments == false) {
            // remove HTML comments (and ending "//" if present)
            $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);

            // remove CDATA section markers
            $js = $this->_removeCdata($js);
        }

        // minify
        $minifier = $this->_jsMinifier
            ? $this->_jsMinifier
            : 'trim';
        $js = call_user_func($minifier, $js);

        return $this->_reservePlace($this->_needsCdata($js)
            ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
            : "{$ws1}{$openScript}{$js}</script>{$ws2}"
        );
    }

    protected function _removeCdata($str)
    {
        return (false !== strpos($str, '<![CDATA['))
            ? str_replace(array('/* <![CDATA[ */','/* ]]> */','/*<![CDATA[*/','/*]]>*/','<![CDATA[', ']]>'), '', $str)
            : $str;
    }

    protected function _needsCdata($str)
    {
        return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
    }
}
PK.�1\B.xG��external/php/index.htmlnu�[���<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
PK.�1\oPU��@�@external/php/jsmin.phpnu�[���<?php

//namespace JSMin;

/**
 * JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
 *
 * <code>
 * $minifiedJs = JSMin::minify($js);
 * </code>
 *
 * This is a modified port of jsmin.c. Improvements:
 *
 * Does not choke on some regexp literals containing quote characters. E.g. /'/
 *
 * Spaces are preserved after some add/sub operators, so they are not mistakenly
 * converted to post-inc/dec. E.g. a + ++b -> a+ ++b
 *
 * Preserves multi-line comments that begin with /*!
 *
 * PHP 5 or higher is required.
 *
 * Permission is hereby granted to use this version of the library under the
 * same terms as jsmin.c, which has the following license:
 *
 * --
 * Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * The Software shall be used for Good, not Evil.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * --
 *
 * @package JSMin
 * @author Ryan Grove <ryan@wonko.com> (PHP port)
 * @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
 * @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
 * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
 * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
 * @license http://opensource.org/licenses/mit-license.php MIT License
 * @link http://code.google.com/p/jsmin-php/
 */
class JSMin {
    const ACTION_KEEP_A     = 1;
    const ACTION_DELETE_A   = 2;
    const ACTION_DELETE_A_B = 3;

    protected $a           = "\n";
    protected $b           = '';
    protected $input       = '';
    protected $inputIndex  = 0;
    protected $inputLength = 0;
    protected $lookAhead   = null;
    protected $output      = '';
    protected $lastByteOut  = '';
    protected $keptComment = '';

    /**
     * Minify Javascript.
     *
     * @param string $js Javascript to be minified
     *
     * @return string
     */
    public static function minify($js)
    {
        $jsmin = new JSMin($js);
        return $jsmin->min();
    }

    /**
     * @param string $input
     */
    public function __construct($input)
    {
        $this->input = $input;
    }

    /**
     * Perform minification, return result
     *
     * @return string
     */
    public function min()
    {
        if ($this->output !== '') { // min already run
            return $this->output;
        }

        $mbIntEnc = null;
        if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
            $mbIntEnc = mb_internal_encoding();
            mb_internal_encoding('8bit');
        }

        if (isset($this->input[0]) && $this->input[0] === "\xef") {
            $this->input = substr($this->input, 3);
        }

        $this->input = str_replace("\r\n", "\n", $this->input);
        $this->inputLength = strlen($this->input);

        $this->action(self::ACTION_DELETE_A_B);

        while ($this->a !== null) {
            // determine next command
            $command = self::ACTION_KEEP_A; // default
            if ($this->isWhiteSpace($this->a)) {
                if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
                        && ($this->b === $this->lastByteOut)) {
                    // Don't delete this space. If we do, the addition/subtraction
                    // could be parsed as a post-increment
                } elseif (! $this->isAlphaNum($this->b)) {
                    $command = self::ACTION_DELETE_A;
                }
            } elseif ($this->isLineTerminator($this->a)) {
                if ($this->isWhiteSpace($this->b)) {
                    $command = self::ACTION_DELETE_A_B;

                    // in case of mbstring.func_overload & 2, must check for null b,
                    // otherwise mb_strpos will give WARNING
                } elseif ($this->b === null
                          || (false === strpos('{[(+-!~#', $this->b)
                              && ! $this->isAlphaNum($this->b))) {
                    $command = self::ACTION_DELETE_A;
                }
            } elseif (! $this->isAlphaNum($this->a)) {
                if ($this->isWhiteSpace($this->b)
                    || ($this->isLineTerminator($this->b)
                        && (false === strpos('}])+-"\'`', $this->a)))) {
                    $command = self::ACTION_DELETE_A_B;
                }
            }
            $this->action($command);
        }
        $this->output = trim($this->output);

        if ($mbIntEnc !== null) {
            mb_internal_encoding($mbIntEnc);
        }
        return $this->output;
    }

    /**
     * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
     * ACTION_DELETE_A = Copy B to A. Get the next B.
     * ACTION_DELETE_A_B = Get the next B.
     *
     * @param int $command
     * @throws JSMin_UnterminatedRegExpException|UnterminatedStringException
     */
    protected function action($command)
    {
        // make sure we don't compress "a + ++b" to "a+++b", etc.
        if ($command === self::ACTION_DELETE_A_B
            && $this->b === ' '
            && ($this->a === '+' || $this->a === '-')) {
            // Note: we're at an addition/substraction operator; the inputIndex
            // will certainly be a valid index
            if ($this->input[$this->inputIndex] === $this->a) {
                // This is "+ +" or "- -". Don't delete the space.
                $command = self::ACTION_KEEP_A;
            }
        }

        switch ($command) {
            case self::ACTION_KEEP_A: // 1
                $this->output .= $this->a;

                if ($this->keptComment) {
                    $this->output = rtrim($this->output, "\n");
                    $this->output .= $this->keptComment;
                    $this->keptComment = '';
                }

                $this->lastByteOut = $this->a;

                // fallthrough intentional
            case self::ACTION_DELETE_A: // 2
                $this->a = $this->b;
                if ($this->a === "'" || $this->a === '"' || $this->a === '`') { // string/template literal
                    $delimiter = $this->a;
                    $str = $this->a; // in case needed for exception
                    for(;;) {
                        $this->output .= $this->a;
                        $this->lastByteOut = $this->a;

                        $this->a = $this->get();
                        if ($this->a === $this->b) { // end quote
                            break;
                        }
                        if ($delimiter === '`' && $this->isLineTerminator($this->a)) {
                            // leave the newline
                        } elseif ($this->isEOF($this->a)) {
                            $byte = $this->inputIndex - 1;
                            throw new JSMin_UnterminatedStringException(
                                "JSMin: Unterminated String at byte {$byte}: {$str}");
                        }
                        $str .= $this->a;
                        if ($this->a === '\\') {
                            $this->output .= $this->a;
                            $this->lastByteOut = $this->a;

                            $this->a = $this->get();
                            $str .= $this->a;
                        }
                    }
                }

                // fallthrough intentional
            case self::ACTION_DELETE_A_B: // 3
                $this->b = $this->next();
                if ($this->b === '/' && $this->isRegexpLiteral()) {
                    $this->output .= $this->a . $this->b;
                    $pattern = '/'; // keep entire pattern in case we need to report it in the exception
                    for(;;) {
                        $this->a = $this->get();
                        $pattern .= $this->a;
                        if ($this->a === '[') {
                            for(;;) {
                                $this->output .= $this->a;
                                $this->a = $this->get();
                                $pattern .= $this->a;
                                if ($this->a === ']') {
                                    break;
                                }
                                if ($this->a === '\\') {
                                    $this->output .= $this->a;
                                    $this->a = $this->get();
                                    $pattern .= $this->a;
                                }
                                if ($this->isEOF($this->a)) {
                                    throw new JSMin_UnterminatedRegExpException(
                                        "JSMin: Unterminated set in RegExp at byte "
                                            . $this->inputIndex .": {$pattern}");
                                }
                            }
                        }

                        if ($this->a === '/') { // end pattern
                            break; // while (true)
                        } elseif ($this->a === '\\') {
                            $this->output .= $this->a;
                            $this->a = $this->get();
                            $pattern .= $this->a;
                        } elseif ($this->isEOF($this->a)) {
                            $byte = $this->inputIndex - 1;
                            throw new JSMin_UnterminatedRegExpException(
                                "JSMin: Unterminated RegExp at byte {$byte}: {$pattern}");
                        }
                        $this->output .= $this->a;
                        $this->lastByteOut = $this->a;
                    }
                    $this->b = $this->next();
                }
            // end case ACTION_DELETE_A_B
        }
    }

    /**
     * @return bool
     */
    protected function isRegexpLiteral()
    {
        if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
            // we can't divide after these tokens
            return true;
        }

        // check if first non-ws token is "/" (see starts-regex.js)
        $length = strlen($this->output);
        if ($this->isWhiteSpace($this->a) || $this->isLineTerminator($this->a)) {
            if ($length < 2) { // weird edge case
                return true;
            }
        }

        // if the "/" follows a keyword, it must be a regexp, otherwise it's best to assume division

        $subject = $this->output . trim($this->a);
        if (!preg_match('/(?:case|else|in|return|typeof)$/', $subject, $m)) {
            // not a keyword
            return false;
        }

        // can't be sure it's a keyword yet (see not-regexp.js)
        $charBeforeKeyword = substr($subject, 0 - strlen($m[0]) - 1, 1);
        if ($this->isAlphaNum($charBeforeKeyword)) {
            // this is really an identifier ending in a keyword, e.g. "xreturn"
            return false;
        }

        // it's a regexp. Remove unneeded whitespace after keyword
        if ($this->isWhiteSpace($this->a) || $this->isLineTerminator($this->a)) {
            $this->a = '';
        }

        return true;
    }

    /**
     * Return the next character from stdin. Watch out for lookahead. If the character is a control character,
     * translate it to a space or linefeed.
     *
     * @return string
     */
    protected function get()
    {
        $c = $this->lookAhead;
        $this->lookAhead = null;
        if ($c === null) {
            // getc(stdin)
            if ($this->inputIndex < $this->inputLength) {
                $c = $this->input[$this->inputIndex];
                $this->inputIndex += 1;
            } else {
                $c = null;
            }
        }
        if ($c === "\r") {
            return "\n";
        }
        return $c;
    }

    /**
     * Does $a indicate end of input?
     *
     * @param string $a
     * @return bool
     */
    protected function isEOF($a)
    {
        return $a === null || $this->isLineTerminator($a);
    }

    /**
     * Get next char (without getting it). If is ctrl character, translate to a space or newline.
     *
     * @return string
     */
    protected function peek()
    {
        $this->lookAhead = $this->get();
        return $this->lookAhead;
    }

    /**
     * Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
     *
     * @param string $c
     *
     * @return bool
     */
    protected function isAlphaNum($c)
    {
        return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
    }

    /**
     * Consume a single line comment from input (possibly retaining it)
     */
    protected function consumeSingleLineComment()
    {
        $comment = '';
        while (true) {
            $get = $this->get();
            $comment .= $get;
            if ($this->isEOF($get)) {
                // if IE conditional comment
                if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
                    $this->keptComment .= "/{$comment}";
                }
                return;
            }
        }
    }

    /**
     * Consume a multiple line comment from input (possibly retaining it)
     *
     * @throws JSMin_UnterminatedCommentException
     */
    protected function consumeMultipleLineComment()
    {
        $this->get();
        $comment = '';
        for(;;) {
            $get = $this->get();
            if ($get === '*') {
                if ($this->peek() === '/') { // end of comment reached
                    $this->get();
                    if (0 === strpos($comment, '!')) {
                        // preserved by YUI Compressor
                        if (!$this->keptComment) {
                            // don't prepend a newline if two comments right after one another
                            $this->keptComment = "\n";
                        }
                        $this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
                    } else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
                        // IE conditional
                        $this->keptComment .= "/*{$comment}*/";
                    }
                    return;
                }
            } elseif ($get === null) {
                throw new JSMin_UnterminatedCommentException(
                    "JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
            }
            $comment .= $get;
        }
    }

    /**
     * Get the next character, skipping over comments. Some comments may be preserved.
     *
     * @return string
     */
    protected function next()
    {
        $get = $this->get();
        if ($get === '/') {
            switch ($this->peek()) {
                case '/':
                    $this->consumeSingleLineComment();
                    $get = "\n";
                    break;
                case '*':
                    $this->consumeMultipleLineComment();
                    $get = ' ';
                    break;
            }
        }
        return $get;
    }

    protected function isWhiteSpace($s) {
        // https://www.ecma-international.org/ecma-262/#sec-white-space
        return $s !== null && strpos(" \t\v\f", $s) !== false;
    }

    protected function isLineTerminator($s) {
        // https://www.ecma-international.org/ecma-262/#sec-line-terminators
        return $s !== null && strpos("\n\r", $s) !== false;
    }
}

class JSMin_UnterminatedStringException extends Exception {}
class JSMin_UnterminatedCommentException extends Exception {}
class JSMin_UnterminatedRegExpException extends Exception {}
PK.�1\
R!��Pexternal/php/persist-admin-notices-dismissal/persist-admin-notices-dismissal.phpnu�[���<?php

/**
 * Persist Admin notices Dismissal
 *
 * Copyright (C) 2016 Collins Agbonghama <http://w3guy.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @package Persist Admin notices Dismissal
 * @author  Collins Agbonghama
 * @author  Andy Fragen
 * @license http://www.gnu.org/licenses GNU General Public License
 * @version 1.4.1
 */

/**
 * Exit if called directly.
 */
if ( ! defined( 'ABSPATH' ) ) {
	die;
}

if ( ! class_exists( 'PAnD' ) ) {

	/**
	 * Class PAnD
	 */
	class PAnD {

		/**
		 * Init hooks.
		 */
		public static function init() {
			add_action( 'admin_enqueue_scripts', array( __CLASS__, 'load_script' ) );
			add_action( 'wp_ajax_dismiss_admin_notice', array( __CLASS__, 'dismiss_admin_notice' ) );
		}

		/**
		 * Enqueue javascript and variables.
		 */
		public static function load_script() {

			if ( is_customize_preview() ) {
				return;
			}

			wp_enqueue_script(
				'dismissible-notices',
				plugins_url( 'dismiss-notice.js', __FILE__ ),
				array( 'jquery', 'common' ),
				false,
				true
			);

			wp_localize_script(
				'dismissible-notices',
				'dismissible_notice',
				array(
					'nonce' => wp_create_nonce( 'dismissible-notice' ),
				)
			);
		}

		/**
		 * Handles Ajax request to persist notices dismissal.
		 * Uses check_ajax_referer to verify nonce.
		 */
		public static function dismiss_admin_notice() {
			$option_name        = sanitize_text_field( $_POST['option_name'] );
			$dismissible_length = sanitize_text_field( $_POST['dismissible_length'] );

			if ( 'forever' != $dismissible_length ) {
				// If $dismissible_length is not an integer default to 1
				$dismissible_length = ( 0 == absint( $dismissible_length ) ) ? 1 : $dismissible_length;
				$dismissible_length = strtotime( absint( $dismissible_length ) . ' days' );
			}

			check_ajax_referer( 'dismissible-notice', 'nonce' );
			self::set_admin_notice_cache( $option_name, $dismissible_length );
			wp_die();
		}

		/**
		 * Is admin notice active?
		 *
		 * @param string $arg data-dismissible content of notice.
		 *
		 * @return bool
		 */
		public static function is_admin_notice_active( $arg ) {
			$array       = explode( '-', $arg );
			$length      = array_pop( $array );
			$option_name = implode( '-', $array );
			$db_record   = self::get_admin_notice_cache( $option_name );
			if ( 'forever' == $db_record ) {
				return false;
			} elseif ( absint( $db_record ) >= time() ) {
				return false;
			} else {
				return true;
			}
		}

		/**
		 * Returns admin notice cached timeout.
		 *
		 * @access public
		 *
		 * @param string|bool $id admin notice name or false.
		 *
		 * @return array|bool The timeout. False if expired.
		 */
		public static function get_admin_notice_cache( $id = false ) {
			if ( ! $id ) {
				return false;
			}
			$cache_key = 'pand-' . md5( $id );
			$timeout   = get_site_option( $cache_key );
			$timeout   = 'forever' === $timeout ? time() + 60 : $timeout;

			if ( empty( $timeout ) || time() > $timeout ) {
				return false;
			}

			return $timeout;
		}

		/**
		 * Sets admin notice timeout in site option.
		 *
		 * @access public
		 *
		 * @param string      $id       Data Identifier.
		 * @param string|bool $timeout  Timeout for admin notice.
		 *
		 * @return bool
		 */
		public static function set_admin_notice_cache( $id, $timeout ) {
			$cache_key = 'pand-' . md5( $id );
			update_site_option( $cache_key, $timeout );

			return true;
		}

	}

}
PK.�1\ZW��6external/php/persist-admin-notices-dismissal/README.mdnu�[���# Persist Admin notice Dismissals
[![Latest Stable Version](https://poser.pugx.org/collizo4sky/persist-admin-notices-dismissal/v/stable)](https://packagist.org/packages/collizo4sky/persist-admin-notices-dismissal)
[![Total Downloads](https://poser.pugx.org/collizo4sky/persist-admin-notices-dismissal/downloads)](https://packagist.org/packages/collizo4sky/persist-admin-notices-dismissal)

Simple framework library that persists the dismissal of admin notices across pages in WordPress dashboard.

## Installation

Run `composer require collizo4sky/persist-admin-notices-dismissal`

Alternatively, clone or download this repo into the `vendor/` folder in your plugin, and include/require the `persist-admin-notices-dismissal.php` file like so

```php
require  __DIR__ . '/vendor/persist-admin-notices-dismissal/persist-admin-notices-dismissal.php';
add_action( 'admin_init', array( 'PAnD', 'init' ) );
```

or let Composer's autoloader do the work.

## How to Use
Firstly, install and activate this library within a plugin.

Say you have the following markup as your admin notice,


```php
function sample_admin_notice__success() {
	?>
	<div class="updated notice notice-success is-dismissible">
    	<p><?php _e( 'Done!', 'sample-text-domain' ); ?></p>
	</div>
	<?php
}
add_action( 'admin_notices', 'sample_admin_notice__success' );
```

To make it hidden forever when dismissed, add the following data attribute `data-dismissible="disable-done-notice-forever"` to the div markup like so:


```php
function sample_admin_notice__success() {
	if ( ! PAnD::is_admin_notice_active( 'disable-done-notice-forever' ) ) {
		return;
	}

	?>
	<div data-dismissible="disable-done-notice-forever" class="updated notice notice-success is-dismissible">
		<p><?php _e( 'Done!', 'sample-text-domain' ); ?></p>
	</div>
	<?php
}
add_action( 'admin_init', array( 'PAnD', 'init' ) );
add_action( 'admin_notices', 'sample_admin_notice__success' );
```

## Autoloaders
When using the framework with an autoloader you **must** also load the class outside of the `admin_notices` or `network_admin_notices` hooks. The reason is that these hooks come after the `admin_enqueue_script` hook that loads the javascript.

Just add the following in your main plugin file.

```php
add_action( 'admin_init', array( 'PAnD', 'init' ) );
```

#### Usage Instructions and Examples
If you have two notices displayed when certain actions are triggered; firstly, choose a string to uniquely identify them, e.g. `notice-one` and `notice-two`

To make the first notice never appear once dismissed, its `data-dismissible` attribute will be `data-dismissible="notice-one-forever"` where `notice-one` is its unique identifier and `forever` is the dismissal time period.

To make the second notice only hidden for 2 days, its `data-dismissible` attribute will be `data-dismissible="notice-two-2"` where `notice-two` is its unique identifier and the `2`, the number of days it will be hidden is the dismissal time period.

You **must** append the dismissal time period to the end of your unique identifier with a hyphen (`-`) and this value must be an integer. The only exception is the string `forever`.

To actually make the dismissed admin notice not to appear, use the `is_admin_notice_active()` function like so:


```php
function sample_admin_notice__success1() {
	if ( ! PAnD::is_admin_notice_active( 'notice-one-forever' ) ) {
		return;
	}

	?>
	<div data-dismissible="notice-one-forever" class="updated notice notice-success is-dismissible">
		<p><?php _e( 'Done 1!', 'sample-text-domain' ); ?></p>
	</div>
	<?php
}

function sample_admin_notice__success2() {
	if ( ! PAnD::is_admin_notice_active( 'notice-two-2' ) ) {
		return;
	}

	?>
	<div data-dismissible="notice-two-2" class="updated notice notice-success is-dismissible">
		<p><?php _e( 'Done 2!', 'sample-text-domain' ); ?></p>
	</div>
	<?php
}

add_action( 'admin_init', array( 'PAnD', 'init' ) );
add_action( 'admin_notices', 'sample_admin_notice__success1' );
add_action( 'admin_notices', 'sample_admin_notice__success2' );
```

You should be a good developer and add the following to your `uninstall.php` file so that we can clean up after ourselves and not leave unnecessary stuff in the options table.

```php
global $wpdb;
$table         = is_multisite() ? $wpdb->base_prefix . 'sitemeta' : $wpdb->base_prefix . 'options';
$column        = is_multisite() ? 'meta_key' : 'option_name';
$delete_string = 'DELETE FROM ' . $table . ' WHERE ' . $column . ' LIKE %s LIMIT 1000';
$wpdb->query( $wpdb->prepare( $delete_string, array( '%pand-%' ) ) );
```

Cool beans. Isn't it?
PK.�1\}�dd>external/php/persist-admin-notices-dismissal/dismiss-notice.jsnu�[���(function ($) {
	//shorthand for ready event.
	$(
		function () {
			$( 'div[data-dismissible] button.notice-dismiss' ).on('click',
				function (event) {
					event.preventDefault();
					var $this = $( this );

					var attr_value, option_name, dismissible_length, data;

					attr_value = $this.parent().attr( 'data-dismissible' ).split( '-' );

					// remove the dismissible length from the attribute value and rejoin the array.
					dismissible_length = attr_value.pop();

					option_name = attr_value.join( '-' );

					data = {
						'action': 'dismiss_admin_notice',
						'option_name': option_name,
						'dismissible_length': dismissible_length,
						'nonce': dismissible_notice.nonce
					};

					// We can also pass the url value separately from ajaxurl for front end AJAX implementations
					$.post( ajaxurl, data );
				}
			);
		}
	)

}(jQuery));
PK.�1\��I��?�?$critcss-inc/admin_settings_rules.phpnu�[���<?php
/**
 * Render the rules panel.
 */

/**
 * Main function to render the rules panel.
 */
function ao_ccss_render_rules() {
    // Attach required arrays.
    $criticalcss   = autoptimize()->criticalcss();
    $ao_ccss_rules = sanitize_rules( $criticalcss->get_option( 'rules' ) );
    $ao_ccss_types = $criticalcss->get_types();

    if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
        $ao_ccss_types = array( 'No conditionals, check CSS optimization settings.' );
    }
    ?>
    <ul id="rules-panel">
        <li class="itemDetail">
            <h2 class="itemTitle"><?php esc_html_e( 'Rules', 'autoptimize' ); ?></h2>

            <!-- BEGIN Rule dialogs -->
            <!-- Unsaved dialog -->
            <div id="unSavedWarning" class="hidden updated settings-error notice notice-warning is-dismissible">
                <p><?php _e( "<strong>Rules or Queue changed!</strong> Don't forget to save your changes!", 'autoptimize' ); ?></p>
            </div>

            <!-- Create/edit rule dialog -->
            <div id="addEditCritCss" class="hidden">
                <table class="form-table rules">
                    <tr id="critcss_addedit_type_wrapper">
                        <th scope="row">
                            <?php esc_html_e( 'Rule Type', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <select id="critcss_addedit_type" style="width:100%;">
                                <option value="paths"><?php esc_html_e( 'Path', 'autoptimize' ); ?></option>
                                <option value="types"><?php esc_html_e( 'Conditional Tag', 'autoptimize' ); ?></option>
                            </select>
                        </td>
                    </tr>
                    <tr id="critcss_addedit_path_wrapper">
                        <th scope="row">
                            <?php esc_html_e( 'String in Path', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <input type="text" id="critcss_addedit_path" placeholder="<?php esc_html_e( "Enter a part of the URL that identifies the page(s) you're targetting.", 'autoptimize' ); ?>" style="width:100%;" value="">
                        </td>
                    </tr>
                    <tr id="critcss_addedit_pagetype_wrapper">
                        <th scope="row">
                            <?php esc_html_e( 'Conditional Tag, Custom Post Type or Page Template', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <select id="critcss_addedit_pagetype" style="width:100%;">
                                <option value="" disabled selected><?php esc_html_e( 'Select from the list below...', 'autoptimize' ); ?></option>
                                <optgroup label="<?php esc_html_e( 'Standard Conditional Tags', 'autoptimize' ); ?>">
                                    <?php
                                    // Render grouped simple conditional tags.
                                    foreach ( $ao_ccss_types as $ctag ) {
                                        $optgrp = substr( $ctag, 0, 3 );
                                        if ( substr( $ctag, 0, 3 ) === 'is_' ) {
                                            echo '<option value="' . $ctag . '">' . $ctag . '</option>';
                                        }
                                        $prevgrp = substr( $ctag, 0, 3 );
                                    }

                                    // Render grouped custom post types, templates and specific conditional tags.
                                    foreach ( $ao_ccss_types as $type ) {
                                        $optgrp = substr( $type, 0, 3 );

                                        // Option groups labels.
                                        if ( $optgrp !== $prevgrp && 'is_' !== $optgrp ) {
                                            ?>
                                            </optgroup>
                                            <?php
                                            if ( substr( $type, 0, 12 ) === 'custom_post_' ) {
                                                ?>
                                                <optgroup label="<?php esc_html_e( 'Custom Post Types', 'autoptimize' ); ?>">
                                                <?php
                                            } elseif ( substr( $type, 0, 9 ) === 'template_' ) {
                                                ?>
                                                <optgroup label="<?php esc_html_e( 'Page Templates', 'autoptimize' ); ?>">
                                                <?php
                                            } elseif ( substr( $type, 0, 4 ) === 'bbp_' ) {
                                                ?>
                                                <optgroup label="<?php esc_html_e( 'BBPress Conditional Tags', 'autoptimize' ); ?>">
                                                <?php
                                            } elseif ( substr( $type, 0, 3 ) === 'bp_' ) {
                                                ?>
                                                <optgroup label="<?php esc_html_e( 'BuddyPress Conditional Tags', 'autoptimize' ); ?>">
                                                <?php
                                            } elseif ( substr( $type, 0, 4 ) === 'edd_' ) {
                                                ?>
                                                <optgroup label="<?php esc_html_e( 'Easy Digital Downloads Conditional Tags', 'autoptimize' ); ?>">
                                                <?php
                                            } elseif ( substr( $type, 0, 4 ) === 'woo_' ) {
                                                ?>
                                                <optgroup label="<?php esc_html_e( 'WooCommerce Conditional Tags', 'autoptimize' ); ?>">
                                                <?php
                                            }
                                        }

                                        // Options.
                                        if ( 'is_' !== $optgrp ) {
                                            // Remove prefix from custom post types, templates and some specific conditional tags.
                                            if ( substr( $type, 0, 12 ) === 'custom_post_' ) {
                                                $_type = str_replace( 'custom_post_', '', $type );
                                            } elseif ( substr( $type, 0, 9 ) === 'template_' ) {
                                                $_type = str_replace( 'template_', '', $type );
                                            } elseif ( 'bbp_is_bbpress' === $type ) {
                                                $_type = str_replace( 'bbp_', '', $type );
                                            } elseif ( 'bp_is_buddypress' === $type ) {
                                                $_type = str_replace( 'bp_', '', $type );
                                            } elseif ( substr( $type, 0, 4 ) === 'woo_' ) {
                                                $_type = str_replace( 'woo_', '', $type );
                                            } elseif ( substr( $type, 0, 4 ) === 'edd_' ) {
                                                $_type = str_replace( 'edd_', '', $type );
                                            } else {
                                                $_type = $type;
                                            }

                                            echo '<option value="' . $type . '">' . $_type . '</option>';
                                            $prevgrp = $optgrp;
                                        }
                                    }
                                    ?>
                                </optgroup>
                            </select>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Custom Critical CSS', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <textarea id="critcss_addedit_css" rows="13" cols="10" style="width:100%;" placeholder="<?php esc_html_e( 'Paste your specific critical CSS here and hit submit to save.', 'autoptimize' ); ?>"></textarea>
                            <input type="hidden" id="critcss_addedit_file">
                            <input type="hidden" id="critcss_addedit_id">
                        </td>
                    </tr>
                </table>
            </div>

            <!-- Remove dialog -->
            <div id="confirm-rm" title="<?php esc_html_e( 'Delete Rule', 'autoptimize' ); ?>" class="hidden">
                <p><?php _e( 'This Critical CSS rule will be deleted immediately and cannot be recovered.<br /><br /><strong>Are you sure?</strong>', 'autoptimize' ); ?></p>
            </div>

            <!-- Remove All dialog -->
            <div id="confirm-rm-all" title="<?php esc_html_e( 'Delete all Rules and Jobs', 'autoptimize' ); ?>" class="hidden">
                <p><?php _e( 'All Critical CSS rules will be deleted immediately and cannot be recovered.<br /><br /><strong>Are you sure?</strong>', 'autoptimize' ); ?></p>
            </div>

            <!-- Add/edit default critical CSS dialog -->
            <div id="default_critcss_wrapper" class="hidden">
                <textarea id="dummyDefault" rows="19" cols="10" style="width:100%;" placeholder="<?php esc_html_e( 'Paste your minified default critical CSS here and hit submit to save. This is the critical CSS to be used for every page not matching any rule.', 'autoptimize' ); ?>"></textarea>
            </div>

            <!-- Add/edit additional critical CSS dialog -->
            <div id="additional_critcss_wrapper" class="hidden">
                <textarea id="dummyAdditional" rows="19" cols="10" style="width:100%;" placeholder="<?php esc_html_e( 'Paste your minified additional critical CSS here and hit submit to save. This is the CSS to be added AT THE END of every critical CSS provided by a matching rule, or the default one.', 'autoptimize' ); ?>"></textarea>
            </div>

            <!-- BEGIN Rules UI -->
            <div class="howto">
                <div class="title-wrap">
                    <h4 class="title"><?php esc_html_e( 'How To Use Autoptimize CriticalCSS Rules', 'autoptimize' ); ?></h4>
                    <p class="subtitle"><?php esc_html_e( 'Click the side arrow to toggle instructions', 'autoptimize' ); ?></p>
                </div>
                <button type="button" class="toggle-btn">
                    <span class="toggle-indicator dashicons dashicons-arrow-up dashicons-arrow-down"></span>
                </button>
                <div class="howto-wrap hidden">
                <p><?php _e( "TL;DR:<br />Critical CSS files from <span class='badge auto'>AUTO</span> <strong>rules are updated automatically</strong> while from <span class='badge manual'>MANUAL</span> <strong>rules are not.</strong>", 'autoptimize' ); ?></p>
                    <ol>
                        <li><?php _e( 'When a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> API key is in place, Autoptimize starts to operate <strong>automatically</strong>.', 'autoptimize' ); ?></li>
                        <li><?php _e( 'Upon a request to any of the frontend pages made by a <strong>not logged in user</strong>, it will <strong>asynchronously</strong> fetch and update the critical CSS from <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> for conditional tags you have on your site (e.g. is_page, is_single, is_archive etc.)', 'autoptimize' ); ?></li>
                        <li><?php _e( 'These requests also creates an <span class="badge auto">AUTO</span> rule for you. The critical CSS files from <span class="badge auto">AUTO</span> <strong>rules are updated automatically</strong> when a CSS file in your theme or frontend plugins changes.', 'autoptimize' ); ?></li>
                        <li><?php _e( 'If you want to make any fine tunning in the critical CSS file of an <span class="badge auto">AUTO</span> rule, click on "Edit" button of that rule, change what you need, submit and save it. The rule you\'ve just edited becomes a <span class="badge manual">MANUAL</span> rule then.', 'autoptimize' ); ?></li>
                        <li><?php _e( 'You can create <span class="badge manual">MANUAL</span> rules for specific page paths (URL). Longer, more specific paths have higher priority over shorter ones, which in turn have higher priority over <span class="badge auto">AUTO</span> rules. Also, critical CSS files from <span class="badge manual">MANUAL</span> <strong>rules are NEVER updated automatically.</strong>', 'autoptimize' ); ?></li>
                        <li><?php _e( 'At any time you can delete an <span class="badge auto">AUTO</span> or <span class="badge manual">MANUAL</span> rule by cliking on "Remove" button of the desired rule and saving your changes.', 'autoptimize' ); ?></li>
                    </ol>
                </div>
            </div>
            <textarea id="autoptimize_css_defer_inline" name="autoptimize_css_defer_inline" rows="19" cols="10" style="width:100%;"><?php echo autoptimizeStyles::sanitize_css( get_option( 'autoptimize_css_defer_inline', '' ) ); ?></textarea>
            <textarea id="autoptimize_ccss_additional" name="autoptimize_ccss_additional" rows="19" cols="10" style="width:100%;"><?php echo autoptimizeStyles::sanitize_css( get_option( 'autoptimize_ccss_additional', '' ) ); ?></textarea>
            <table class="rules-list" cellspacing="0"><tbody id="rules-list"></tbody></table>
            <input class="hidden" type="text" id="critCssOrigin" name="autoptimize_ccss_rules" value='<?php echo ( json_encode( $ao_ccss_rules, JSON_FORCE_OBJECT ) ); ?>'>
            <!-- Wrapper for in screen notices -->
            <div id="rules-notices"></div>
            <!-- END Rule add/edit dialogs -->
            <div class="submit rules-btn">
                <div class="alignleft">
                    <span id="addCritCssButton" class="button-secondary"><?php esc_html_e( 'Add New Rule', 'autoptimize' ); ?></span>
                    <span id="editDefaultButton" class="button-secondary"><?php esc_html_e( 'Edit Default Rule CSS', 'autoptimize' ); ?></span>
                    <span id="editAdditionalButton" class="button-secondary"><?php esc_html_e( 'Add CSS To All Rules', 'autoptimize' ); ?></span>
                </div>
                <div class="alignright">
                    <span id="removeAllRules" class="button-secondary" style="color:red;"><?php esc_html_e( 'Remove all rules', 'autoptimize' ); ?></span>
                </div>
            </div>
            <!-- END Rules UI -->
        </li>
    </ul>
    <?php
}

/**
 * Sanitize rules before rendering.
 *
 * @param array $rules Array with rules to be sanitized.
 */
function sanitize_rules( $rules ) {
    if ( apply_filters( 'autoptimize_filter_ccss_paths_clickable', true ) ) {
        if ( array_key_exists( 'paths', $rules ) ) {
            foreach ( $rules['paths'] as $key => $value ) {
                $newkey = esc_url( $key );
                if ( $newkey !== $key ) {
                    if ( 0 === strpos( $newkey, 'http://' ) && 0 !== strpos( $key, 'http://' ) ) {
                        // esc_url adds "http://" to any string that does not start with either a protocol or a
                        // slash, see https://developer.wordpress.org/reference/functions/esc_url/#more-information
                        // this removes that unneeded protocol again.
                        $newkey = substr_replace( $newkey, '', 0, 7 );
                    }
                    unset( $rules['paths'][ $key ] );
                    $rules['paths'][ $newkey ] = $value;
                }
            }
        }
    }
    
    $rules = autoptimizeUtils::strip_tags_array( $rules );
    return $rules;
}

?>
PK.�1\5�h�m
m
$critcss-inc/admin_settings_debug.phpnu�[���<?php
/**
 * Debug panel.
 */

// Attach wpdb() object.
global $wpdb;

// Query AO's options.
$ao_options = $wpdb->get_results(
    '
    SELECT option_name  AS name,
    option_value AS value
    FROM ' . $wpdb->options . '
    WHERE option_name LIKE "autoptimize_%%"
    ORDER BY name
    ',
    ARRAY_A
);

// Query AO's transients.
$ao_trans = $wpdb->get_results(
    '
    SELECT option_name  AS name,
    option_value AS value
    FROM ' . $wpdb->options . '
    WHERE option_name LIKE "_transient_autoptimize_%%"
    OR option_name LIKE "_transient_timeout_autoptimize_%%"
    ',
    ARRAY_A
);

// Render debug panel if there's something to show.
if ( $ao_options || $ao_trans ) {
    ?>
<!-- BEGIN: Settings Debug -->
<ul>
    <li class="itemDetail">
        <h2 class="itemTitle"><?php esc_html_e( 'Debug Information', 'autoptimize' ); ?></h2>

        <?php
        // Render options.
        if ( $ao_options ) {
            ?>
            <h4><?php esc_html_e( 'Options', 'autoptimize' ); ?>:</h4>
            <table class="form-table debug">
            <?php
            foreach ( $ao_options as $option ) {
                ?>
                <tr>
                    <th scope="row">
                        <?php echo wp_strip_all_tags( $option['name'] ); ?>
                    </th>
                    <td>
                        <?php
                        if ( 'autoptimize_ccss_queue' == $option['name'] || 'autoptimize_ccss_rules' == $option['name'] ) {
                            $value = print_r( json_decode( wp_strip_all_tags( $option['value'] ), true ), true );
                            if ( $value ) {
                                echo "Raw JSON:\n<pre>" . wp_strip_all_tags( $option['value'] ) . "</pre>\n\nDecoded JSON:\n<pre>" . wp_strip_all_tags( $value ) . '</pre>';
                            } else {
                                echo 'Empty';
                            }
                        } else {
                            echo wp_strip_all_tags( $option['value'] );
                        }
                        ?>
                    </td>
                </tr>
                <?php
            }
            ?>
            </table>
            <hr />
            <?php
        }
        // Render WP-Cron intervals and scheduled events.
        ?>
        <h4><?php esc_html_e( 'WP-Cron Intervals', 'autoptimize' ); ?>:</h4>
        <pre><?php print_r( wp_get_schedules() ); ?></pre>
        <hr />
        <h4><?php esc_html_e( 'WP-Cron Scheduled Events', 'autoptimize' ); ?>:</h4>
        <pre><?php print_r( _get_cron_array() ); ?></pre>
    </li>
</ul>
<!-- END: Settings Debug -->
    <?php
}
PK.�1\�R�"critcss-inc/admin_settings_key.phpnu�[���<?php
/**
 * Render key panel.
 */

/**
 * Function that renders key panel.
 *
 * @param string $key          API Key.
 * @param string $status       API Key status.
 * @param string $status_msg   Status message.
 * @param string $message      Message.
 * @param string $color        Color to highlight message in.
 */
function ao_ccss_render_key( $key, $status, $status_msg, $message, $color ) {
    if ( defined( 'AO_PRO_VERSION' ) && has_filter( 'autoptimize_filter_ccss_key' ) ) {
        ?>
        <input type="hidden" id="autoptimize_ccss_key" name="autoptimize_ccss_key" value="">
        <?php
        return;
    }

    if ( defined( 'AUTOPTIMIZE_CRITICALCSS_API_KEY' ) ) {
        $key = esc_html__( 'API key provided by your host/ WordPress administrator, no need to enter anything here. In case of problems with the API key, contact your host/ WordPress administrator.', 'autoptimize' );
    } else if ( has_filter( 'autoptimize_filter_ccss_key' ) ) {
        $key = esc_html__( 'API Key provided by a filter, no need to enter anything here.', 'autoptimize' );
    }

    ?>
    <ul id="key-panel">
        <li class="itemDetail">
            <h2 class="itemTitle fleft"><?php esc_html_e( 'API Key', 'autoptimize' ); ?>: <span style="color:<?php echo $color; ?>;"><?php echo $status_msg; ?></span></h2>
            <button type="button" class="toggle-btn">
                <?php if ( 'valid' != $status ) { ?>
                    <span class="toggle-indicator dashicons dashicons-arrow-up"></span>
                <?php } else { ?>
                    <span class="toggle-indicator dashicons dashicons-arrow-up dashicons-arrow-down"></span>
                <?php } ?>
            </button>
            <?php if ( 'valid' != $status ) { ?>
                <div class="collapsible">
            <?php } else { ?>
                <div class="collapsible hidden">
            <?php } ?>
            <?php if ( 'valid' != $status ) { ?>
                <div style="clear:both;padding:2px 10px;border-left:solid;border-left-width:5px;border-left-color:<?php echo $color; ?>;background-color:white;">
                    <p><?php echo $message; ?></p>
                </div>
            <?php } ?>
                <table id="key" class="form-table">
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Your API Key', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <textarea id="autoptimize_ccss_key" name="autoptimize_ccss_key" rows='3' style="width:100%;" placeholder="<?php esc_html_e( 'Please enter your criticalcss.com API key here.', 'autoptimize' ); ?>"><?php echo trim( esc_textarea( $key ) ); ?></textarea>
                            <p class="notes">
                                <?php _e( 'Enter your <a href="https://criticalcss.com/account/api-keys?aff=1" target="_blank">criticalcss.com</a> API key above. The key is revalidated every time a new job is sent to it.<br />To obtain your API key, go to <a href="https://criticalcss.com/account/api-keys?aff=1" target="_blank">criticalcss.com</a> > Account > API Keys.<br />Requests to generate a critical CSS via the API are priced at £5 per domain per month.<br /><strong>Not sure yet? With the <a href="https://criticalcss.com/faq/?aff=1#trial" target="_blank">30 day money back guarantee</a>, you have nothing to lose!</strong>', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                </table>
            </div>
        </li>
    </ul>
    <?php
}
PK.�1\eQ0h��&critcss-inc/admin_settings_explain.phpnu�[���<?php
/**
 * Explain what CCSS is (visible if no API key is stored).
 */

/**
 * Actual function that explains.
 */
function ao_ccss_render_explain() {
    ?>
    <style>
        .ao_settings_div {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
        .ao_settings_div .form-table th {font-weight: normal;}
    </style>
    <script>document.title = "Autoptimize: <?php esc_html_e( 'Critical CSS', 'autoptimize' ); ?> " + document.title;</script>
    <ul id="explain-panel">
        <div class="ao_settings_div">
            <?php
            $ccss_explanation = '';

            if ( apply_filters( 'autoptimize_filter_ccss_rules_without_api', true ) ) {
                $_transient    = 'ao3_ccss_explain';
                $_explain_html = 'https://misc.optimizingmatters.com/autoptimize_ccss_explain_ao30_i18n.html?ao_ver=';
            } else {
                $_transient    = 'ao_ccss_explain';
                $_explain_html = 'https://misc.optimizingmatters.com/autoptimize_ccss_explain_i18n.html?ao_ver=';
            }

            // get the HTML with the explanation of what critical CSS is.
            if ( apply_filters( 'autoptimize_settingsscreen_remotehttp', true ) ) {
                $ccss_explanation = get_transient( $_transient );
                if ( empty( $ccss_explanation ) ) {
                    $ccss_expl_resp = wp_remote_get( $_explain_html . AUTOPTIMIZE_PLUGIN_VERSION );
                    if ( ! is_wp_error( $ccss_expl_resp ) ) {
                        if ( '200' == wp_remote_retrieve_response_code( $ccss_expl_resp ) ) {
                            $ccss_explanation = wp_kses_post( wp_remote_retrieve_body( $ccss_expl_resp ) );
                            set_transient( $_transient, $ccss_explanation, WEEK_IN_SECONDS );
                        }
                    }
                }
            }

            // placeholder text in case HTML is empty.
            if ( empty( $ccss_explanation ) ) {
                $ccss_explanation = sprintf( esc_html__( '%1$sFix render-blocking CSS!%2$s%3$sSignificantly improve your first-paint times by making CSS non-render-blocking.%4$s%3$sThe %5$snext step is to sign up at %7$shttps://criticalcss.com%8$s%6$s (this is a premium service, priced 2 GBP/month for membership and 5 GBP/month per domain) %5$sand get the API key%6$s, which you can copy from %7$sthe API-keys page%8$s and paste below.%4$s%3$sIf you have any questions or need support, head on over to %9$sour support forum%10$s and we\'ll help you get up and running in no time!%4$s', 'autoptimize' ), '<h2>', '</h2>', '<p>', '</p>', '<strong>', '</strong>', '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>', '<a href="https://wordpress.org/support/plugin/autoptimize" target="_blank">', '</a>' );
            } else {
                // we were able to fetch the explenation, so add the JS to show correct language.
                $ccss_explanation .= "<script>jQuery('.ao_i18n').hide();d=document;lang=d.getElementsByTagName('html')[0].getAttribute('lang').substring(0,2);if(d.getElementById(lang)!= null){jQuery('#'+lang).show();}else{jQuery('#default').show();}</script>";
            }

            // and echo it.
            echo $ccss_explanation;
            ?>
        </div>
        </ul>
    <?php
}
PK.�1\�Y_�/�/'critcss-inc/admin_settings_queue.js.phpnu�[���<?php
/**
 * JS code to manage queue.
 */

?>
// Hide object text box
var queueOriginEl = document.getElementById('ao-ccss-queue' );
if (queueOriginEl) {
    queueOriginEl.style.display = 'none';

    // Get queue object and call table renderer
    jQuery(document).ready(function() {
        // Instance and parse queue object
        var aoCssQueueRaw = document.getElementById('ao-ccss-queue').value;
        var aoCssQueue    = aoCssQueueRaw.indexOf('{"') === 0 ?
                                JSON.parse(aoCssQueueRaw) :
                                "";
        var aoCssQueueLog = aoCssQueue === "" ?
                                "empty" :
                                aoCssQueue;
        <?php
        if ( $ao_ccss_debug ) {
            echo "console.log( 'Queue Object:', aoCssQueueLog );\n";
        }
        ?>

        // hook up "remove all jobs" button to the JS action.
        jQuery("#removeAllJobs").click(function(){removeAllJobs();});

        // Render queue table
        drawQueueTable(aoCssQueue);

        // Make queue table sortable if there are any elements
        var queueBodyEl = jQuery('#queue > tr').length;
        if (queueBodyEl > 0) {
            jQuery('#queue-tbl').tablesorter({
                sortList: [[0,0]],
                headers: {6: {sorter: false}}
            });
        }
        
        // unhide queuerunner button conditionally (we don't want people running the queue continuously) and attach event to it.
        if (queueBodyEl > 4 || ( queueBodyEl > 0 && jQuery('#rules > tr').length < 1 ) ) {
            jQuery('#queuerunner-container').show();
            jQuery("#queuerunner").click(function(){queuerunner();});
        }
    });
}

// Render the queue in a table
function drawQueueTable(queue) {
    jQuery('#queue').empty();
    rowNumber=0;
    jQuery.each(queue, function(path, keys) {
        // Prepare commom job values
        ljid      = keys.ljid;
        targetArr = keys.rtarget.split('|' );
        target    = targetArr[1];
        type      = keys.ptype;
        ctime     = EpochToDate(keys.jctime);
        rbtn      = false;
        dbtn      = false;
        hbtn      = false;

        // don't list jobs that don't have a type, they are irrelevant and this also avoids "type.replace is not a function".
        if ( type == false ) {
            return;
        }

        // Prepare job statuses
        if (keys.jqstat === 'NEW') {
            // Status: NEW (N, sort order 1)
            status      = '<span class="hidden">1</span>N';
            statusClass = 'new';
            title       = '<?php esc_html_e( 'New', 'autoptimize' ); ?> (' + ljid + ')';
            buttons     = '<?php esc_html_e( 'None', 'autoptimize' ); ?>';
        } else if (keys.jqstat === 'JOB_QUEUED' || keys.jqstat === 'JOB_ONGOING') {
            // Status: PENDING (P, sort order 2)
            status      = '<span class="hidden">2</span>P';
            statusClass = 'pending';
            title       = '<?php esc_html_e( 'PENDING', 'autoptimize' ); ?> (' + ljid + ')';
            buttons     = '<?php esc_html_e( 'None', 'autoptimize' ); ?>';
        } else if (keys.jqstat === 'JOB_DONE' && keys.jrstat === 'GOOD' && (keys.jvstat === 'WARN' || keys.jvstat === 'BAD')) {
            // Status: REVIEW (R, sort order 5)
            status      = '<span class="hidden">5</span>R';
            statusClass = 'review';
            title       = "<?php esc_html_e( 'REVIEW', 'autoptimize' ); ?> (" + ljid + ")\n<?php esc_html_e( 'Info from criticalcss.com:', 'autoptimize' ); ?>\n<?php esc_html_e( '- Job ID: ', 'autoptimize' ); ?>" + keys.jid + "\n<?php esc_html_e( '- Status: ', 'autoptimize' ); ?>" + keys.jqstat + "\n<?php esc_html_e( '- Result: ', 'autoptimize' ); ?>" + keys.jrstat + "\n<?php esc_html_e( '- Validation: ', 'autoptimize' ); ?>" + keys.jvstat;
            buttons     = '<span class="button-secondary" id="' + ljid + '_remove" title="<?php esc_html_e( 'Delete Job', 'autoptimize' ); ?>"><span class="dashicons dashicons-trash"></span></span>';
            dbtn        = true;
        } else if (keys.jqstat === 'JOB_DONE') {
            // Status: DONE (D, sort order 6)
            status      = '<span class="hidden">6</span>D';
            statusClass = 'done';
            title       = '<?php esc_html_e( 'DONE', 'autoptimize' ); ?> (' + ljid + ')';
            buttons     = '<span class="button-secondary" id="' + ljid + '_remove" title="<?php esc_html_e( 'Delete Job', 'autoptimize' ); ?>"><span class="dashicons dashicons-trash"></span></span>';
            dbtn        = true;
        } else if (keys.jqstat === 'JOB_FAILED' || keys.jqstat === 'STATUS_JOB_BAD' || keys.jqstat === 'INVALID_JWT_TOKEN' || keys.jqstat === 'NO_CSS' || keys.jqstat === 'NO_RESPONSE') {
        // Status: ERROR (E, sort order 4)
            status      = '<span class="hidden">4</span>E';
            statusClass = 'error';
            title       = "<?php esc_html_e( 'ERROR', 'autoptimize' ); ?> (" + ljid + ")\n<?php esc_html_e( 'Info from criticalcss.com:', 'autoptimize' ); ?>\n<?php esc_html_e( '- Job ID: ', 'autoptimize' ); ?>" + keys.jid + "\n<?php esc_html_e( '- Status: ', 'autoptimize' ); ?>" + keys.jqstat + "\n<?php esc_html_e( '- Result: ', 'autoptimize' ); ?>" + keys.jrstat + "\n<?php esc_html_e( '- Validation: ', 'autoptimize' ); ?>" + keys.jvstat;
            buttons     = '<span class="button-secondary" id="' + ljid + '_retry" title="<?php esc_html_e( 'Retry Job', 'autoptimize' ); ?>"><span class="dashicons dashicons-update"></span></span><span class="button-secondary to-right" id="' + ljid + '_remove" title="<?php esc_html_e( 'Delete Job', 'autoptimize' ); ?>"><span class="dashicons dashicons-trash"></span></span><span class="button-secondary to-right" id="' + ljid + '_help" title="<?php esc_html_e( 'Get Help', 'autoptimize' ); ?>"><span class="dashicons dashicons-sos"></span></span>';
            rbtn        = true;
            dbtn        = true;
            hbtn        = true;
        } else {
            // Status: UNKNOWN (U, sort order 5)
            status      = '<span class="hidden">5</span>U';
            statusClass = 'unknown';
            title       = "<?php esc_html_e( 'UNKNOWN', 'autoptimize' ); ?> (" + ljid + ")\n<?php esc_html_e( 'Info from criticalcss.com:', 'autoptimize' ); ?>\n<?php esc_html_e( '- Job ID: ', 'autoptimize' ); ?>" + keys.jid + "\n<?php esc_html_e( '- Status: ', 'autoptimize' ); ?>" + keys.jqstat + "\n<?php esc_html_e( '- Result: ', 'autoptimize' ); ?>" + keys.jrstat + "\n<?php esc_html_e( '- Validation: ', 'autoptimize' ); ?>" + keys.jvstat;
            buttons     = '<span class="button-secondary" id="' + ljid + '_remove" title="<?php esc_html_e( 'Delete Job', 'autoptimize' ); ?>"><span class="dashicons dashicons-trash"></span></span><span class="button-secondary to-right" id="' + ljid + '_help" title="<?php esc_html_e( 'Get Help', 'autoptimize' ); ?>"><span class="dashicons dashicons-sos"></span></span>';
            dbtn        = true;
            hbtn        = true;
        }

        // Prepare job finish time
        if (keys.jftime === null) {
            ftime = '<?php esc_html_e( 'N/A', 'autoptimize' ); ?>';
        } else {
            ftime = EpochToDate(keys.jftime);
        }

        // Append job entry
        jQuery("#queue").append("<tr id='" + ljid + "' class='job " + statusClass + "'><td class='status'><span class='badge " + statusClass + "' title='<?php esc_html_e( 'Job status is ', 'autoptimize' ); ?>" + title + "'>" + status + "</span></td><td>" + target.replace(/(woo_|template_|custom_post_|edd_|bp_|bbp_)/,'') + "</td><td>" + path + "</td><td>" + type.replace(/(woo_|template_|custom_post_|edd_|bp_|bbp_)/,'') + "</td><td>" + ctime + "</td><td>" + ftime + "</td><td class='btn'>" + buttons + "</td></tr>");

        // Attach button actions
        if (rbtn) {
            jQuery('#' + ljid + '_retry').click(function(){retryJob(queue, this.id, path);});
        }
        if (dbtn) {
            jQuery('#' + ljid + '_remove').click(function(){delJob(queue, this.id, path);});
        }
        if (hbtn) {
            jQuery('#' + ljid + '_help').click(function(){jid=this.id.split('_' );window.open('https://criticalcss.com/faq?aoid=' + jid[0], '_blank' );});
        }
    });
}

// Delete a job from the queue
function delJob(queue, jid, jpath) {
    jid = jid.split('_' );
    jQuery('#queue-confirm-rm').dialog({
        resizable: false,
        height: 180,
        modal: true,
        buttons: {
            "<?php esc_html_e( 'Delete', 'autoptimize' ); ?>": function() {
                delete queue[jpath];
                updateQueue(queue);
                jQuery(this).dialog('close' );
            },
            "<?php esc_html_e( 'Cancel', 'autoptimize' ); ?>": function() {
                jQuery(this).dialog('close' );
            }
        }
    });
}

function removeAllJobs() {
    jQuery( "#queue-confirm-rm-all" ).dialog({
        resizable: false,
        height:235,
        modal: true,
        buttons: {
            "<?php esc_html_e( 'Delete all jobs?', 'autoptimize' ); ?>": function() {
                queue=[];
                updateQueue(queue);
                jQuery( this ).dialog( "close" );
            },
            "<?php esc_html_e( 'Cancel', 'autoptimize' ); ?>": function() {
                jQuery( this ).dialog( "close" );
            }
        }
    });
}

// Retry jobs with error
function retryJob(queue, jid, jpath) {
    jid = jid.split('_' );
    jQuery('#queue-confirm-retry').dialog({
        resizable: false,
        height: 180,
        modal: true,
        buttons: {
            "<?php esc_html_e( 'Retry', 'autoptimize' ); ?>": function() {
                <?php
                if ( $ao_ccss_debug ) {
                    echo "console.log( 'SHOULD retry job:', jid[0], jpath );\n";
                }
                ?>
                queue[jpath].jid = null;
                queue[jpath].jqstat = 'NEW';
                queue[jpath].jrstat = null;
                queue[jpath].jvstat = null;
                queue[jpath].jctime = (new Date).getTime() / 1000;
                queue[jpath].jftime = null;
                updateQueue(queue);
                jQuery(this).dialog('close' );
            },
            "<?php esc_html_e( 'Cancel', 'autoptimize' ); ?>": function() {
                jQuery(this).dialog('close' );
            }
        }
    });
}

// Refresh queue
function updateQueue(queue) {
    document.getElementById('ao-ccss-queue').value=JSON.stringify(queue);
    drawQueueTable(queue);
    jQuery('#unSavedWarning').show();
    document.getElementById('ao_title_and_button').scrollIntoView();  
    <?php
    if ( $ao_ccss_debug ) {
        echo "console.log('Updated Queue Object:', queue);\n";
    }
    ?>
}

// Run the queue manually (in case of cron issues/ impatient users).
function queuerunner() {
    var data = {
        'action': 'ao_ccss_queuerunner',
        'ao_ccss_queuerunner_nonce': '<?php echo wp_create_nonce( 'ao_ccss_queuerunner_nonce' ); ?>',
    };

    jQuery.post(ajaxurl, data, function(response) {
        response_array=JSON.parse(response);
        if (response_array['code'] == 200) {
            displayNotice( '<?php esc_html_e( 'Queue processed, reloading page.', 'autoptimize' ); ?>', 'success' )
            setTimeout(window.location.reload.bind(window.location), 1.5*1000);
        } else if ( response_array['code'] == 302 ) {
            displayNotice( '<?php esc_html_e( 'The queue is locked, retry in a couple of minutes. If this problem persists and the queue is not moving at all remove the <code>wp-content/uploads/ao_ccss/queue.lock</code> file.', 'autoptimize' ); ?>', 'warning' )
        } else {
            displayNotice( '<?php esc_html_e( 'Could not process queue.', 'autoptimize' ); ?>', 'error' )
        }
    });
}

// Convert epoch to date for job times
function EpochToDate(epoch) {
    if (epoch < 10000000000)
        epoch *= 1000;
    var epoch = epoch + (new Date().getTimezoneOffset() * -1); //for timeZone
    var sdate = new Date(epoch);
    var ldate = sdate.toLocaleString();
    return ldate;
}
PK.�1\�%�F__$critcss-inc/admin_settings_queue.phpnu�[���<?php
/**
 * Render the queue panel.
 */

/**
 * Function to render the queue panel.
 */
function ao_ccss_render_queue() {
    // Attach required arrays.
    $criticalcss = autoptimize()->criticalcss();
    $ao_ccss_queue = $criticalcss->get_option( 'queue' );

    // Prepare the queue object.
    if ( empty( $ao_ccss_queue ) ) {
        $ao_ccss_queue = '';
    } else {
        $ao_ccss_queue = json_encode( $ao_ccss_queue );
    }
    ?>

    <ul id="queue-panel">
        <li class="itemDetail">
            <h2 class="itemTitle fleft"><?php esc_html_e( 'Job Queue', 'autoptimize' ); ?></h2>
            <button type="button" class="toggle-btn">
                <span class="toggle-indicator dashicons dashicons-arrow-up dashicons-arrow-down"></span>
            </button>
            <?php
            if ( $criticalcss->has_autorules() ) {
                $_queue_visibility = 'hidden';
            } else {
                $_queue_visibility = 'visible';
            }
            ?>
            <div class="collapsible <?php echo $_queue_visibility; ?>">
                <!-- BEGIN Queue dialogs -->
                <!-- Retry dialog -->
                <div id="queue-confirm-retry" title="<?php esc_html_e( 'Retry Job', 'autoptimize' ); ?>" class="hidden">
                    <p><?php esc_html_e( 'Are you sure you want to retry this job?', 'autoptimize' ); ?></p>
                </div>

                <!-- Remove dialog -->
                <div id="queue-confirm-rm" title="<?php esc_html_e( 'Delete Job', 'autoptimize' ); ?>" class="hidden">
                    <p><?php esc_html_e( 'Are you sure you want to delete this job?', 'autoptimize' ); ?></p>
                </div>

                <!-- Remove all dialog -->
                <div id="queue-confirm-rm-all" title="<?php esc_html_e( 'Delete all jobs', 'autoptimize' ); ?>" class="hidden">
                    <p><?php esc_html_e( 'This will delete all jobs, are you sure?', 'autoptimize' ); ?></p>
                </div>
                <!-- END Queue dialogs -->

                <!-- BEGIN Queue UI -->
                <div class="howto">
                    <div class="title-wrap">
                        <h4 class="title"><?php esc_html_e( 'How To Use Autoptimize CriticalCSS Queue', 'autoptimize' ); ?></h4>
                        <p class="subtitle"><?php esc_html_e( 'Click the side arrow to toggle instructions', 'autoptimize' ); ?></p>
                    </div>
                    <button type="button" class="toggle-btn">
                        <span class="toggle-indicator dashicons dashicons-arrow-up dashicons-arrow-down"></span>
                    </button>
                    <div class="howto-wrap hidden">
                        <p><?php _e( 'TL;DR:<br /><strong>Queue runs every 10 minutes.</strong> Job statuses are <span class="badge new">N</span> for NEW, <span class="badge pending">P</span> for PENDING, <span class="badge error">E</span> for ERROR and <span class="badge unknown">U</span> for UNKOWN.', 'autoptimize' ); ?></p>
                        <ol>
                            <li><?php _e( 'The queue operates <strong>automatically, asynchronously and on regular intervals of 10 minutes.</strong> To view updated queue status, refresh this page.', 'autoptimize' ); ?></li>
                            <li><?php _e( 'When the conditions to create a job are met (i.e. user not logged in, no matching <span class="badge manual">MANUAL</span> rule or CSS files has changed for an <span class="badge auto">AUTO</span> rule), a <span class="badge new">N</span> job is created in the queue.', 'autoptimize' ); ?></li>
                            <li><?php _e( "Autoptimize constantly queries the queue for <span class='badge new'>N</span> jobs. When it finds one, gears spins and jobs becomes <span class='badge pending'>P</span> while they are running and <a href='https://criticalcss.com/?aff=1' target='_blank'>criticalcss.com</a> doesn't return a result.", 'autoptimize' ); ?></li>
                            <li><?php _e( 'As soon as <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> returns a valid critical CSS file, the job is then finished and removed from the queue.', 'autoptimize' ); ?></li>
                            <li><?php _e( 'When things go wrong, a job is marked as <span class="badge error">E</span>. You can retry faulty jobs, delete them or get in touch with <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> for assistance.', 'autoptimize' ); ?></li>
                            <li><?php _e( 'Sometimes an unknown condition can happen. In this case, the job status becomes <span class="badge unknown">U</span> and you may want to ask <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> for help or just delete it.', 'autoptimize' ); ?></li>
                            <li><?php _e( 'To get more information about jobs statuses, specially the ones with <span class="badge error">E</span> and <span class="badge unknown">U</span> status, hover your mouse in the status badge of that job. This information might be crucial when contacting <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> for assistance.', 'autoptimize' ); ?></li>
                            <li><?php _e( '<strong>A word about WordPress cron:</strong> Autoptimize watches the queue by using WordPress Cron (or WP-Cron for short.) It <a href="https://www.smashingmagazine.com/2013/10/schedule-events-using-wordpress-cron/#limitations-of-wordpress-cron-and-solutions-to-fix-em" target="_blank">could be faulty</a> on very light or very heavy loads. If your site receives just a few or thousands visits a day, it might be a good idea to <a href="https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/" target="_blank">turn WP-Cron off and use your system task scheduler</a> to fire it instead.', 'autoptimize' ); ?></li>
                        </ol>
                    </div>
                </div>
                <table id="queue-tbl" class="queue tablesorter" cellspacing="0">
                    <thead>
                        <tr><th class="status"><?php esc_html_e( 'Status', 'autoptimize' ); ?></th><th><?php esc_html_e( 'Target Rule', 'autoptimize' ); ?></th><th><?php esc_html_e( 'Page Path', 'autoptimize' ); ?></th><th><?php esc_html_e( 'Page Type', 'autoptimize' ); ?></th><th><?php esc_html_e( 'Creation Date', 'autoptimize' ); ?></th><th><?php esc_html_e( 'Finish Date', 'autoptimize' ); ?></th><th class="btn"><?php esc_html_e( 'Actions', 'autoptimize' ); ?></th></tr>
                    </thead>
                    <tbody id="queue"></tbody>
                </table>
                <input class="hidden" type="text" id="ao-ccss-queue" name="autoptimize_ccss_queue" value='<?php echo( $ao_ccss_queue ); ?>'>
                <div class="submit jobs-btn">
                    <div id="queuerunner-container" class="alignleft hidden">
                        <span id="queuerunner" class="button-secondary"><?php esc_html_e( 'Manually process the job queue', 'autoptimize' ); ?></span>
                    </div>
                    <div class="alignright">
                        <span id="removeAllJobs" class="button-secondary" style="color:red;"><?php esc_html_e( 'Remove all jobs', 'autoptimize' ); ?></span>
                    </div>
                </div>
            </div>
            <!-- END Queue UI -->
        </li>
    </ul>
    <?php
}
PK.�1\SQ�W0-0-"critcss-inc/admin_settings_adv.phpnu�[���<?php
/**
 * Contains the function to render the advanced panel.
 */

/**
 * Function to render the advanced panel.
 */
function ao_ccss_render_adv() {
    $criticalcss = autoptimize()->criticalcss();

    $ao_ccss_debug       = esc_attr( $criticalcss->get_option( 'debug' ) );
    $ao_ccss_finclude    = esc_textarea( $criticalcss->get_option( 'finclude' ) );
    $ao_ccss_rtimelimit  = esc_attr( $criticalcss->get_option( 'rtimelimit' ) );
    $ao_ccss_noptimize   = esc_attr( $criticalcss->get_option( 'noptimize' ) );
    $ao_ccss_loggedin    = esc_attr( $criticalcss->get_option( 'loggedin' ) );
    $ao_ccss_forcepath   = esc_attr( $criticalcss->get_option( 'forcepath' ) );
    $ao_ccss_deferjquery = esc_attr( $criticalcss->get_option( 'deferjquery' ) );
    $ao_ccss_domain      = esc_attr( $criticalcss->get_option( 'domain' ) );
    $ao_ccss_unloadccss  = esc_attr( $criticalcss->get_option( 'unloadccss' ) );

    // In case domain is not set yet (done in cron.php).
    if ( empty( $ao_ccss_domain ) ) {
        $ao_ccss_domain = get_site_url();
    }

    // Get viewport size.
    $viewport = $criticalcss->viewport();
    ?>
    <ul id="adv-panel">
        <li class="itemDetail">
            <h2 class="itemTitle fleft"><?php esc_html_e( 'Advanced Settings', 'autoptimize' ); ?></h2>
            <button type="button" class="toggle-btn">
                <span class="toggle-indicator dashicons dashicons-arrow-up dashicons-arrow-down"></span>
            </button>
            <div class="collapsible hidden">
                <table id="key" class="form-table">
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Viewport Size', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <label for="autoptimize_ccss_vw"><?php esc_html_e( 'Width', 'autoptimize' ); ?>:</label> <input type="number" id="autoptimize_ccss_vw" name="autoptimize_ccss_viewport[w]" min="800" max="4096" placeholder="1400" value="<?php echo $viewport['w']; ?>" />&nbsp;&nbsp;
                            <label for="autoptimize_ccss_vh"><?php esc_html_e( 'Height', 'autoptimize' ); ?>:</label> <input type="number" id="autoptimize_ccss_vh" name="autoptimize_ccss_viewport[h]" min="600" max="2160" placeholder="1080" value="<?php echo $viewport['h']; ?>" />
                            <p class="notes">
                                <?php _e( '<a href="https://criticalcss.com/account/api-keys?aff=1" target="_blank">criticalcss.com</a> default viewport size is 1400x1080 pixels (width x height). You can change this size by typing a desired width and height values above. Allowed value ranges are from 800 to 4096 for width and from 600 to 2160 for height.', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Force Include CSS selectors', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <textarea id="autoptimize_ccss_finclude" name="autoptimize_ccss_finclude" rows='3' maxlenght='500' style="width:100%;" placeholder="<?php esc_html_e( '.button-special,//#footer', 'autoptimize' ); ?>"><?php echo trim( esc_textarea( $ao_ccss_finclude ) ); ?></textarea>
                            <p class="notes">
                                <?php _e( 'Force include CSS selectors can be used to style dynamic content that is not part of the HTML that is seen during the Critical CSS generation. To use this feature, add comma separated values with both simple strings and/or regular expressions to match the desired selectors. Regular expressions must be preceeded by two forward slashes. For instance: <code>.button-special,//#footer</code>. In this example <code>.button-special</code> will match <code>.button-special</code> selector only, while <code>//#footer</code> will match <code>#footer</code>, <code>#footer-address</code> and <code>#footer-phone</code> selectors in case they exist.<br />Do take into account that changing this setting will only affect new/ updated rules, so you might want to remove old rules and clear your page cache to expedite the forceIncludes becoming used.', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Queue processing time limit', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <input type="number" id="autoptimize_ccss_rtimelimit" name="autoptimize_ccss_rtimelimit" min="0" max="240" placeholder="0" value="<?php echo $ao_ccss_rtimelimit; ?>" />
                            <p class="notes">
                                <?php esc_html_e( 'The cronned queue processing is an asynchronous process triggerd by (WordPress) cron. To avoid this process from running too long and potentially getting killed, you can set the number of seconds here, 0 means no limit.', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Fetch Original CSS', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <input type="checkbox" id="autoptimize_ccss_noptimize" name="autoptimize_ccss_noptimize" value="1" <?php checked( 1 == $ao_ccss_noptimize ); ?>>
                            <p class="notes">
                                <?php esc_html_e( 'In some (rare) cases the generation of critical CSS works better with the original CSS instead of the Autoptimized one, this option enables that behavior.', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Add CCSS for logged in users?', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <input type="checkbox" id="autoptimize_ccss_loggedin" name="autoptimize_ccss_loggedin" value="1" <?php checked( 1 == $ao_ccss_loggedin ); ?>>
                            <p class="notes">
                                <?php esc_html_e( 'Critical CSS is generated by criticalcss.com from your pages as seen by an "anonymous visitor". Disable this option if you don\'t want the "visitor" critical CSS to be used for logged in users.', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Force path-based rules to be generated for pages?', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <input type="checkbox" id="autoptimize_ccss_forcepath" name="autoptimize_ccss_forcepath" value="1" <?php checked( 1 == $ao_ccss_forcepath ); ?>>
                            <p class="notes">
                                <?php esc_html_e( 'By default for each page a separate rule is generated. If your pages have (semi-)identical above the fold look and feel and you want to keep the rules lean, you can disable that so one rule is created to all pages.', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                    <?php if ( 1 == $ao_ccss_deferjquery ) { ?>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Defer jQuery and other non-aggregated JS-files? (deprecated)', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <input type="checkbox" id="autoptimize_ccss_deferjquery" name="autoptimize_ccss_deferjquery" value="1" <?php checked( 1 == $ao_ccss_deferjquery ); ?>>
                            <p class="notes">
                                <?php esc_html_e( 'Defer all non-aggregated JS, including jQuery and inline JS to fix remaining render-blocking issues. Make sure to test your site thoroughly when activating this option!', 'autoptimize' ); ?>
                                <?php esc_html_e( '<b>This functionality will be removed in a next major version of Autoptimize</b>, being replaced by the combination of the "do not aggregate but defer JS" + "defer inline JS" options on the main settings page.', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                    <?php } ?>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Unload critical CSS after page load?', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <input type="checkbox" id="autoptimize_ccss_unloadccss" name="autoptimize_ccss_unloadccss" value="1" <?php checked( 1 == $ao_ccss_unloadccss ); ?>>
                            <p class="notes">
                                <?php esc_html_e( 'In rare cases the critical CSS needs to be removed once the full CSS loads, this option makes it so!', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Bound domain', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <input type="text" id="autoptimize_ccss_domain" name="autoptimize_ccss_domain" style="width:100%;" placeholder="<?php esc_html_e( 'Don\'t leave this empty, put e.g. https://example.net/ or simply \'none\' to disable domain binding.', 'autoptimize' ); ?>" value="<?php echo trim( esc_attr( $ao_ccss_domain ) ); ?>">
                            <p class="notes">
                                <?php esc_html_e( 'Only requests from this domain will be sent for Critical CSS generation (pricing is per domain/ month).', 'autoptimize' ); ?>
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">
                            <?php esc_html_e( 'Debug Mode', 'autoptimize' ); ?>
                        </th>
                        <td>
                            <input type="checkbox" id="autoptimize_ccss_debug" name="autoptimize_ccss_debug" value="1" <?php checked( 1 == $ao_ccss_debug ); ?>>
                            <p class="notes">
                                <?php
                                _e( '<strong>CAUTION! Only use debug mode on production/live environments for ad-hoc troubleshooting and remember to turn it back off after</strong>, as this generates a lot of log-data.<br />Check the box above to enable Autoptimize CriticalCSS Power-Up debug mode. It provides debug facilities in this screen, to the browser console and to this file: ', 'autoptimize' );
                                echo '<code>' . AO_CCSS_LOG . '</code>';
                                ?>
                            </p>
                        </td>
                    </tr>
                </table>
            </div>
        </li>
    </ul>
    <?php
}
?>
PK.�1\|#.+L+L'critcss-inc/admin_settings_rules.js.phpnu�[���<?php
/**
 * Outputs JS code for the rules panel.
 */

if ( $ao_ccss_debug ) {
    echo "console.log('[WARN] Autoptimize CriticalCSS is in debug mode!');\n";
    echo "console.log('[WARN] Avoid using debug mode on production/live environments unless for ad-hoc troubleshooting purposes and make sure to disable it after!');\n";
}
?>

var rulesOriginEl = document.getElementById("critCssOrigin");
var deferInlineEl = document.getElementById("autoptimize_css_defer_inline");
var additionalEl  = document.getElementById("autoptimize_ccss_additional");
if (rulesOriginEl)
    rulesOriginEl.style.display = 'none';
if (deferInlineEl)
    deferInlineEl.style.display = 'none';
if (additionalEl)
    additionalEl.style.display  = 'none';

if (rulesOriginEl) {
    jQuery(document).ready(function() {
        critCssArray=JSON.parse(document.getElementById("critCssOrigin").value);
        <?php
        if ( $ao_ccss_debug ) {
            echo "console.log('Rules Object:', critCssArray);\n";
        }
        ?>
        drawTable(critCssArray);
        jQuery("#addCritCssButton").click(function(){addEditRow();});
        jQuery("#editDefaultButton").click(function(){editDefaultCritCss();});
        jQuery("#editAdditionalButton").click(function(){editAdditionalCritCss();});
        jQuery("#removeAllRules").click(function(){removeAllRules();});
    });
}

function drawTable(critCssArray) {
    jQuery("#rules-list").empty();
    rnotice = 0;
    jQuery.each(critCssArray,function(k,v) {
        if (k=="paths") {
            kstring="<?php esc_html_e( 'Path Based Rules', 'autoptimize' ); ?>";
        } else {
            kstring="<?php esc_html_e( 'Conditional Tags, Custom Post Types and Page Templates Rules', 'autoptimize' ); ?>";
        }
        if (!(jQuery.isEmptyObject(v))) {
            jQuery("#rules-list").append("<tr><td colspan='5'><h4>" + kstring + "</h4></td></tr>");
            jQuery("#rules-list").append("<tr class='header "+k+"Rule'><th><?php esc_html_e( 'Type', 'autoptimize' ); ?></th><th><?php esc_html_e( 'Target', 'autoptimize' ); ?></th><th><?php esc_html_e( 'Critical CSS File', 'autoptimize' ); ?></th><th colspan='2'><?php esc_html_e( 'Actions', 'autoptimize' ); ?></th></tr>");
        }
        nodeNumber=0;
        jQuery.each(v,function(i,nv){
            nodeNumber++;
            nodeId=k + "_" + nodeNumber;
            hash=nv.hash;
            file=nv.file;
            filest=nv.file;
            auto_style = '';
            <?php
            $criticalcss = new autoptimizeCriticalCSSBase();
            if ( $criticalcss->is_api_active() ) {
                echo 'api_active = 1;' . "\n";
            } else {
                echo 'api_active = 0;' . "\n";
            }
            ?>
            if (file == 0) {
                file='<?php esc_html_e( 'To be fetched from criticalcss.com in the next queue run...', 'autoptimize' ); ?>';
            }
            if (nv.hash === 0 && filest != 0) {
                type='<?php esc_html_e( 'MANUAL', 'autoptimize' ); ?>';
                typeClass = 'manual';
            } else {
                type='<?php esc_html_e( 'AUTO', 'autoptimize' ); ?>';
                typeClass = 'auto';
                if ( api_active != 1 ) {
                    auto_style = ' style="opacity:.5;" '
                }
            }
            if (file && typeof file == 'string') {
                rmark_find=file.split('_');
                if (rmark_find[2] || rmark_find[2] == 'R.css') {
                    rnotice = rnotice + 1;
                }
            }
            if ( k == "paths" ) {
                <?php
                if ( apply_filters( 'autoptimize_filter_ccss_paths_clickable', true ) ) {
                    ?>
                    target = '<a href="<?php echo AUTOPTIMIZE_WP_SITE_URL; ?>' + i + '" target="_blank">' + i + '</a>';
                    <?php
                } else {
                    ?>
                    target = i;
                    <?php
                }
                ?>
            } else {
                target = i.replace(/(woo_|template_|custom_post_|edd_|bp_|bbp_)/,'');
            }

            jQuery("#rules-list").append("<tr " + auto_style + "class='rule "+k+"Rule'><td class='type'><span class='badge " + typeClass + "'>" + type + "</span></td><td class='target'>" + target + "</td><td class='file'>" + file + "</td><td class='btn edit'><span class=\"button-secondary\" id=\"" + nodeId + "_edit\"><?php esc_html_e( 'Edit', 'autoptimize' ); ?></span></td><td class='btn delete'><span class=\"button-secondary\" id=\"" + nodeId + "_remove\"><?php esc_html_e( 'Remove', 'autoptimize' ); ?></span></td></tr>");
            if ( typeClass == 'manual' || api_active == 1 ) {
                jQuery("#" + nodeId + "_edit").click(function(){addEditRow(this.id);});
            }
            jQuery("#" + nodeId + "_remove").click(function(){confirmRemove(this.id);});
        })
    });
    if ( rnotice && rnotice != 0 ) {
        // R rules were found, show a notice!
        // and add some JS magic to ensure the notice works as a notice, but is shown inline 
        // in the rules panel instead of in the notice area where it would be too prominent.
        <?php
        $_ao_ccss_review_notice_id   = 'autoptimize-ccss-review-rules-notice-30';
        // Translators: before the 1st word a number + a space will be displayed, as in e.g. "2 of above rules".
        $_ao_ccss_review_notice_copy = esc_html__( 'of the above rules got flagged by criticalcss.com as possibly needing review. This is often due to font-related issues which can be safely ignored, but in case of doubt do a visual test or check for Cumulative Layout Shift issues in e.g. Pagespeed Insights.', 'autoptimize' );
        if ( PAnD::is_admin_notice_active( $_ao_ccss_review_notice_id ) ) {
            ?>
            jQuery("#rules-notices").append( "&nbsp;<div class='rnotice notice notice-info is-dismissible hidden' data-dismissible='<?php echo $_ao_ccss_review_notice_id; ?>'><p>" + rnotice + " <?php echo $_ao_ccss_review_notice_copy; ?>" + "</p></div>");
            jQuery( document ).ready(function() {
                jQuery("div.rnotice").detach().appendTo('#rules-notices');
                jQuery("div.rnotice").show();
            });
            <?php
        } else if ( $ao_ccss_debug ) {
            ?>
            console.log( "Autoptimize: " + rnotice + " <?php echo $_ao_ccss_review_notice_copy; ?>" );
            <?php
        }
        ?>
    }
}

function confirmRemove(idToRemove) {
    jQuery( "#confirm-rm" ).dialog({
        resizable: false,
        height:235,
        modal: true,
        buttons: {
            "<?php esc_html_e( 'Delete', 'autoptimize' ); ?>": function() {
                removeRow(idToRemove);
                updateAfterChange();
                jQuery( this ).dialog( "close" );
            },
            "<?php esc_html_e( 'Cancel', 'autoptimize' ); ?>": function() {
                jQuery( this ).dialog( "close" );
            }
        }
    });
}

function removeAllRules() {
    jQuery( "#confirm-rm-all" ).dialog({
        resizable: false,
        height:235,
        modal: true,
        buttons: {
            "<?php esc_html_e( 'Delete All', 'autoptimize' ); ?>": function() {
                critCssArray={'paths':[],'types':[]};
                drawTable(critCssArray);
                updateAfterChange();
                removeAllCcssFilesOnServer();
                jQuery( this ).dialog( "close" );
            },
            "<?php esc_html_e( 'Cancel', 'autoptimize' ); ?>": function() {
                jQuery( this ).dialog( "close" );
            }
        }
    });
}

function removeRow(idToRemove) {
    splits=idToRemove.split(/_/);
    crit_type=splits[0];
    crit_item=splits[1];
    crit_key=Object.keys(critCssArray[crit_type])[crit_item-1];
    crit_file=critCssArray[crit_type][crit_key].file;
    delete critCssArray[crit_type][crit_key];

    var data = {
        'action': 'rm_critcss',
        'critcss_rm_nonce': '<?php echo wp_create_nonce( 'rm_critcss_nonce' ); ?>',
        'cachebustingtimestamp': new Date().getTime(),
        'critcssfile': crit_file
    };

    jQuery.ajaxSetup({
        async: false
    });

    jQuery.post(ajaxurl, data, function(response) {
        response_array=JSON.parse(response);
        if (response_array["code"]!=200) {
            // not displaying notice, as the end result is OK; the file isn't there
            // displayNotice(response_array["string"]);
        }
    });
}

function removeAllCcssFilesOnServer() {
    var data = {
        'action': 'rm_critcss_all',
        'critcss_rm_all_nonce': '<?php echo wp_create_nonce( 'rm_critcss_all_nonce' ); ?>',
        'cachebustingtimestamp': new Date().getTime()
    };

    jQuery.ajaxSetup({
        async: false
    });

    jQuery.post(ajaxurl, data, function(response) {
        response_array=JSON.parse(response);
        if (response_array["code"]!=200) {
        // not displaying notice, as the end result is OK; the file isn't there
        // displayNotice(response_array["string"]);
        }
    });
}

function addEditRow(idToEdit) {
    resetForm();
    if (idToEdit) {
        dialogTitle="<?php esc_html_e( 'Edit Critical CSS Rule', 'autoptimize' ); ?>";

        splits=idToEdit.split(/_/);
        crit_type=splits[0];
        crit_item=splits[1];
        crit_key=Object.keys(critCssArray[crit_type])[crit_item-1];
        crit_file=critCssArray[crit_type][crit_key].file;

        jQuery("#critcss_addedit_id").val(idToEdit);
        jQuery("#critcss_addedit_type").val(crit_type);
        jQuery("#critcss_addedit_file").val(crit_file);
        jQuery("#critcss_addedit_css").attr("placeholder", "<?php esc_html_e( 'Loading critical CSS...', 'autoptimize' ); ?>");
        jQuery("#critcss_addedit_css").attr("spellcheck",false);
        jQuery("#critcss_addedit_type").attr("disabled",true);

        if (crit_type==="paths") {
            jQuery("#critcss_addedit_path").val(crit_key);
            jQuery("#critcss_addedit_path_wrapper").show();
            jQuery("#critcss_addedit_pagetype_wrapper").hide();
        } else {
            jQuery("#critcss_addedit_pagetype").val(crit_key);
            jQuery("#critcss_addedit_pagetype_wrapper").show();
            jQuery("#critcss_addedit_path_wrapper").hide();
        }

        var data = {
            'action': 'fetch_critcss',
            'critcss_fetch_nonce': '<?php echo wp_create_nonce( 'fetch_critcss_nonce' ); ?>',
            'cachebustingtimestamp': new Date().getTime(),
            'critcssfile': crit_file
        };

        jQuery.post(ajaxurl, data, function(response) {
            response_array=JSON.parse(response);
            if (response_array["code"]==200) {
                jQuery("#critcss_addedit_css").val(response_array["string"]);
            } else {
                jQuery("#critcss_addedit_css").attr("placeholder", "").focus();
            }
        });
    } else {
        dialogTitle="<?php esc_html_e( 'Add Critical CSS Rule', 'autotimize' ); ?>";

        // default: paths, hide content type field
        jQuery("#critcss_addedit_type").val("paths");
        jQuery("#critcss_addedit_css").attr("spellcheck",false);
        jQuery("#critcss_addedit_pagetype_wrapper").hide();

        // event handler on type to switch display
        jQuery("#critcss_addedit_type").on('change', function (e) {
            if(this.value==="types") {
                jQuery("#critcss_addedit_pagetype_wrapper").show();
                jQuery("#critcss_addedit_path_wrapper").hide();
                jQuery("#critcss_addedit_css").attr("placeholder", "<?php esc_html_e( 'For type based rules, paste your specific and minified critical CSS here and hit submit to save. If you want to create a rule to exclude from critical CSS injection, enter \"none\".', 'autoptimize' ); ?>");
            } else {
                jQuery("#critcss_addedit_path_wrapper").show();
                jQuery("#critcss_addedit_pagetype_wrapper").hide();
                jQuery("#critcss_addedit_css").attr("placeholder", "<?php esc_html_e( 'For path based rules, paste your specific and minified critical CSS here or leave this empty to fetch it from criticalcss.com and hit submit to save. If you want to create a rule to exclude from critical CSS injection, enter \"none\"', 'autoptimize' ); ?>");
            }
        });
    }

    jQuery("#addEditCritCss").dialog({
        autoOpen: true,
        height: 500,
        width: 700,
        title: dialogTitle,
        modal: true,
        buttons: {
            "<?php esc_html_e( 'Submit', 'autoptimize' ); ?>": function() {
                rpath = jQuery("#critcss_addedit_path").val();
                rtype = jQuery("#critcss_addedit_pagetype option:selected").val();
                rccss = jQuery("#critcss_addedit_css").val();
                <?php if ( $ao_ccss_debug ) { ?>
                console.log('rpath: ' + rpath, 'rtype: ' + rtype, 'rccss: ' + rccss);
                <?php } ?>
                if (rpath === '' && rtype === '') {
                    alert('<?php esc_html_e( "Rule validation error:\\n\\nBased on your rule type, you should set a path or conditional tag.", 'autoptimize' ); ?>');
                } else if (rtype !== '' && rccss == '') {
                    alert('<?php esc_html_e( "Rule validation error:\\n\\nType based rules requires a minified critical CSS.", 'autoptimize' ); ?>');
                } else if (rpath !== rpath.replace(/("|\'|<|>|\[|\]|{|}|\|)/,'')) {
                    alert('<?php esc_html_e( "Path validation error:\\n\\nThe path contains characters that are not permitted, remove or encode the unsafe characters.", 'autoptimize' ); ?>');
                } else {
                    saveEditCritCss();
                    jQuery(this).dialog('close');
                }
            },
            "<?php esc_html_e( 'Cancel', 'autoptimize' ); ?>": function() {
                resetForm();
                jQuery(this).dialog("close");
            }
        }
    });
}

function editDefaultCritCss(){
    document.getElementById("dummyDefault").value=document.getElementById("autoptimize_css_defer_inline").value;
    jQuery("#dummyDefault").attr("spellcheck",false);
    jQuery("#default_critcss_wrapper").dialog({
        autoOpen: true,
        height: 505,
        width: 700,
        title: "<?php esc_html_e( 'Default Critical CSS', 'autoptimize' ); ?>",
        modal: true,
        buttons: {
            "<?php esc_html_e( 'Submit', 'autoptimize' ); ?>": function() {
                document.getElementById("autoptimize_css_defer_inline").value=document.getElementById("dummyDefault").value;
                jQuery("#unSavedWarning").show();
                jQuery("#default_critcss_wrapper").dialog( "close" );
            },
            "<?php esc_html_e( 'Cancel', 'autoptimize' ); ?>": function() {
                jQuery("#default_critcss_wrapper").dialog( "close" );
            }
        }
    });
}

function editAdditionalCritCss(){
    document.getElementById("dummyAdditional").value=document.getElementById("autoptimize_ccss_additional").value;
    jQuery("#dummyAdditional").attr("spellcheck",false);
    jQuery("#additional_critcss_wrapper").dialog({
        autoOpen: true,
        height: 505,
        width: 700,
        title: "<?php esc_html_e( 'Additional Critical CSS', 'autoptimize' ); ?>",
        modal: true,
        buttons: {
            "<?php esc_html_e( 'Submit', 'autoptimize' ); ?>": function() {
                document.getElementById("autoptimize_ccss_additional").value=document.getElementById("dummyAdditional").value;
                jQuery("#unSavedWarning").show();
                jQuery("#additional_critcss_wrapper").dialog( "close" );
            },
            "<?php esc_html_e( 'Cancel', 'autoptimize' ); ?>": function() {
                jQuery("#additional_critcss_wrapper").dialog( "close" );
            }
        }
    });
}

function saveEditCritCss(){
    critcssfile=jQuery("#critcss_addedit_file").val();
    critcsscontents=jQuery("#critcss_addedit_css").val();
    critcsstype=jQuery("#critcss_addedit_type").val();
    critcssid=jQuery("#critcss_addedit_id").val();

    if (critcssid) {
        // this was an "edit" action, so remove original
        // will also remove the file, but that will get rewritten anyway
        removeRow(critcssid);
    }
    if (critcsstype==="types") {
        critcsstarget=jQuery("#critcss_addedit_pagetype").val();
    } else {
        critcsstarget=jQuery("#critcss_addedit_path").val();
    }

    if (!critcssfile && !critcsscontents) {
        critcssfile=0;
    } else if (!critcssfile && critcsscontents) {
        critcssfile="ccss_" + md5(critcsscontents+critcsstarget) + ".css";
    }

    // Compose the rule object
    critCssArray[critcsstype][critcsstarget]={};
    critCssArray[critcsstype][critcsstarget].hash=0;
    critCssArray[critcsstype][critcsstarget].file=critcssfile;

    <?php
    if ( $ao_ccss_debug ) {
        echo "console.log('[RULE PROPERTIES] Type:', critcsstype, ', Target:', critcsstarget, ', Hash:', 0, ', File:',  critcssfile);";
    }
    ?>

    updateAfterChange();

    var data = {
        'action': 'save_critcss',
        'critcss_save_nonce': '<?php echo wp_create_nonce( 'save_critcss_nonce' ); ?>',
        'critcssfile': critcssfile,
        'critcsscontents': critcsscontents
    };

    jQuery.post(ajaxurl, data, function(response) {
        response_array=JSON.parse(response);
        if (response_array["code"]!=200) {
            displayNotice(response_array["string"]);
        }
    });
}

function updateAfterChange() {
    document.getElementById("critCssOrigin").value=JSON.stringify(critCssArray);
    drawTable(critCssArray);

    <?php
    // autosave rules is on by default, but can be disabled with a filter.
    if ( apply_filters( 'autoptimize_filter_ccss_settings_rules_autosave', true ) ) {
        ?>
    var data = {
        'action': 'ao_ccss_saverules',
        'ao_ccss_saverules_nonce': '<?php echo wp_create_nonce( 'ao_ccss_saverules_nonce' ); ?>',
        'critcssrules': document.getElementById("critCssOrigin").value
    };

    jQuery.post(ajaxurl, data, function(response) {
        response_array=JSON.parse(response);
        if (response_array["code"]!=200) {
            displayNotice(response_array["msg"]);
            jQuery("#unSavedWarning").show();
        }
    });
    <?php } else { ?>
    jQuery("#unSavedWarning").show();
    <?php } ?>

    document.getElementById('ao_title_and_button').scrollIntoView();
}

function displayNotice(textIn, level = 'error') {
    jQuery('<div class="notice notice-' + level + ' notice is-dismissible"><p>'+textIn+'</p></div>').insertBefore("#unSavedWarning");
    document.getElementById('ao_title_and_button').scrollIntoView();
}

function resetForm() {
    jQuery("#critcss_addedit_css").attr("placeholder", "<?php esc_html_e( 'For path based rules, paste your specific and minified critical CSS. If you want to create a rule to exclude from critical CSS injection, enter \"none\"', 'autoptimize' ); ?>");
    jQuery("#critcss_addedit_type").attr("disabled",false);
    jQuery("#critcss_addedit_path_wrapper").show();
    jQuery("#critcss_addedit_id").val("");
    jQuery("#critcss_addedit_path").val("");
    jQuery("#critcss_addedit_file").val("");
    jQuery("#critcss_addedit_pagetype").val("");
    jQuery("#critcss_addedit_css").val("");
}
PK.�1\Di�X��critcss-inc/js/md5.min.jsnu�[���!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function r(n,t){return n<<t|n>>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<<r%32,n[14+(r+64>>>9<<4)]=r;var e,i,a,d,h,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e<n.length;e+=16)i=l,a=g,d=v,h=m,g=f(g=f(g=f(g=f(g=c(g=c(g=c(g=c(g=u(g=u(g=u(g=u(g=o(g=o(g=o(g=o(g,v=o(v,m=o(m,l=o(l,g,v,m,n[e],7,-680876936),g,v,n[e+1],12,-389564586),l,g,n[e+2],17,606105819),m,l,n[e+3],22,-1044525330),v=o(v,m=o(m,l=o(l,g,v,m,n[e+4],7,-176418897),g,v,n[e+5],12,1200080426),l,g,n[e+6],17,-1473231341),m,l,n[e+7],22,-45705983),v=o(v,m=o(m,l=o(l,g,v,m,n[e+8],7,1770035416),g,v,n[e+9],12,-1958414417),l,g,n[e+10],17,-42063),m,l,n[e+11],22,-1990404162),v=o(v,m=o(m,l=o(l,g,v,m,n[e+12],7,1804603682),g,v,n[e+13],12,-40341101),l,g,n[e+14],17,-1502002290),m,l,n[e+15],22,1236535329),v=u(v,m=u(m,l=u(l,g,v,m,n[e+1],5,-165796510),g,v,n[e+6],9,-1069501632),l,g,n[e+11],14,643717713),m,l,n[e],20,-373897302),v=u(v,m=u(m,l=u(l,g,v,m,n[e+5],5,-701558691),g,v,n[e+10],9,38016083),l,g,n[e+15],14,-660478335),m,l,n[e+4],20,-405537848),v=u(v,m=u(m,l=u(l,g,v,m,n[e+9],5,568446438),g,v,n[e+14],9,-1019803690),l,g,n[e+3],14,-187363961),m,l,n[e+8],20,1163531501),v=u(v,m=u(m,l=u(l,g,v,m,n[e+13],5,-1444681467),g,v,n[e+2],9,-51403784),l,g,n[e+7],14,1735328473),m,l,n[e+12],20,-1926607734),v=c(v,m=c(m,l=c(l,g,v,m,n[e+5],4,-378558),g,v,n[e+8],11,-2022574463),l,g,n[e+11],16,1839030562),m,l,n[e+14],23,-35309556),v=c(v,m=c(m,l=c(l,g,v,m,n[e+1],4,-1530992060),g,v,n[e+4],11,1272893353),l,g,n[e+7],16,-155497632),m,l,n[e+10],23,-1094730640),v=c(v,m=c(m,l=c(l,g,v,m,n[e+13],4,681279174),g,v,n[e],11,-358537222),l,g,n[e+3],16,-722521979),m,l,n[e+6],23,76029189),v=c(v,m=c(m,l=c(l,g,v,m,n[e+9],4,-640364487),g,v,n[e+12],11,-421815835),l,g,n[e+15],16,530742520),m,l,n[e+2],23,-995338651),v=f(v,m=f(m,l=f(l,g,v,m,n[e],6,-198630844),g,v,n[e+7],10,1126891415),l,g,n[e+14],15,-1416354905),m,l,n[e+5],21,-57434055),v=f(v,m=f(m,l=f(l,g,v,m,n[e+12],6,1700485571),g,v,n[e+3],10,-1894986606),l,g,n[e+10],15,-1051523),m,l,n[e+1],21,-2054922799),v=f(v,m=f(m,l=f(l,g,v,m,n[e+8],6,1873313359),g,v,n[e+15],10,-30611744),l,g,n[e+6],15,-1560198380),m,l,n[e+13],21,1309151649),v=f(v,m=f(m,l=f(l,g,v,m,n[e+4],6,-145523070),g,v,n[e+11],10,-1120210379),l,g,n[e+2],15,718787259),m,l,n[e+9],21,-343485551),l=t(l,i),g=t(g,a),v=t(v,d),m=t(m,h);return[l,g,v,m]}function a(n){var t,r="",e=32*n.length;for(t=0;t<e;t+=8)r+=String.fromCharCode(n[t>>5]>>>t%32&255);return r}function d(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t<r.length;t+=1)r[t]=0;var e=8*n.length;for(t=0;t<e;t+=8)r[t>>5]|=(255&n.charCodeAt(t/8))<<t%32;return r}function h(n){return a(i(d(n),8*n.length))}function l(n,t){var r,e,o=d(n),u=[],c=[];for(u[15]=c[15]=void 0,o.length>16&&(o=i(o,8*n.length)),r=0;r<16;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(d(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="";for(r=0;r<n.length;r+=1)t=n.charCodeAt(r),e+="0123456789abcdef".charAt(t>>>4&15)+"0123456789abcdef".charAt(15&t);return e}function v(n){return unescape(encodeURIComponent(n))}function m(n){return h(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this);
//# sourceMappingURL=md5.min.js.mapPK.�1\ ��� critcss-inc/js/admin_settings.jsnu�[���// Toggle button control for collapsible elements
jQuery(".toggle-btn").click(function () {
  $header = jQuery(this);
  $content = $header.next();
  $content.slideToggle(250, "swing", function () {
      jQuery("span.toggle-indicator", $header).toggleClass('dashicons-arrow-down');
  });
});

// Attach an event to export buttons
jQuery("#exportSettings").click(function(){exportSettings();});PK.�1\m��B�@�@(critcss-inc/js/jquery.tablesorter.min.jsnu�[���
(function($){$.extend({tablesorter:new
function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1
var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if (c.dateFormat == "pt") {s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");} else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);PK.�1\����II(critcss-inc/admin_settings_impexp.js.phpnu�[���<?php
/**
 * Javascript to import and export AO CCSS settings.
 */

?>
// Export and download settings
function exportSettings( idToEdit ) {
    console.log('Exporting...');
    var data = {
        'action': 'ao_ccss_export',
        'ao_ccss_export_nonce': '<?php echo wp_create_nonce( 'ao_ccss_export_nonce' ); ?>',
    };

    jQuery.post(ajaxurl, data, function(response) {
        response_array=JSON.parse(response);
        if (response_array['code'] == 200) {
            <?php
            if ( is_multisite() ) {
                $blog_id = '/' . get_current_blog_id() . '/';
            } else {
                $blog_id = '/';
            }
            ?>
            export_url = '<?php echo content_url(); ?>/uploads/ao_ccss' + '<?php echo $blog_id; ?>' + response_array['file'];
            msg = "Download export-file from: <a href=\"" + export_url + "\" target=\"_blank\">"+ export_url + "</a>";
        } else {
            msg = response_array['msg'];
        }
        jQuery("#importdialog").html(msg);
        jQuery("#importdialog").dialog({
            autoOpen: true,
            height: 210,
            width: 700,
            title: "<?php esc_html_e( 'Export settings result', 'autoptimize' ); ?>",
            modal: true,
            buttons: {
                OK: function() {
                    jQuery( this ).dialog( "close" );
                }
            }
        });
    });
}

// Upload and import settings
function upload(){
    var fd = new FormData();
    var file = jQuery(document).find('#settingsfile');
    var settings_file = file[0].files[0];
    fd.append('file', settings_file);
    fd.append('action', 'ao_ccss_import');
    fd.append('ao_ccss_import_nonce', '<?php echo wp_create_nonce( 'ao_ccss_import_nonce' ); ?>');

    jQuery.ajax({
        url: ajaxurl,
        type: 'POST',
        data: fd,
        contentType: false,
        processData: false,
        success: function(response) {
            response_array=JSON.parse(response);
            if (response_array['code'] == 200) {
                window.location.reload();
            }
        }
    });
}
PK.�1\�{Z��� critcss-inc/css/admin_styles.cssnu�[���/* form */
.itemTitle {
  margin: 0;
}
.itemTitle.fleft {
  float: left;
}
.toggle-btn {
  float: right;
  position: relative;
  top: -10px;
  width: 36px;
  height: 36px;
  margin: 0;
  padding: 0;
  border: 0;
  background: 0 0;
  cursor: pointer;
  outline: none;
}
.toggle-indicator {
  color: #72777c;
  display: inline-block;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-decoration: none!important;
  outline: none;
}
.itemDetail {
  background: #fff;
  border: 1px solid #ccc;
  padding: 15px;
  margin: 15px 10px 10px 0;
  min-height: 15px;
  word-break: break-word;
}
.collapsible {
  clear: both;
}
.howto {
  margin: 10px 0 0;
  padding: 2px 10px;
  min-height: 45px;
  font-style: normal;
  border-left: solid;
  border-left-width: 5px;
  border-left-color: #00a0d2;
  background-color: white;
}
.howto .title-wrap {
  float: left;
  margin: 5px 0 15px;
}
.howto .title {
  margin: 0;
}
.howto .subtitle {
  margin: 0;
  padding: 0;
  font-weight: 100;
  font-style: italic;
  color: #72777c;
}
.howto .toggle-btn {
  top: -4px;
  left: 10px;
}
.howto-wrap {
  clear: both;
  width: 100%;
}
.form-table {
  margin-top: 20px;
}
.form-table tr {
  vertical-align: top;
}
.form-table p.notes,
.form-table p code {
  font-size: 12px !important;
}
.form-table p.notes {
  color: #666;
}
.form-table.rules {
  margin-top: 0;
}
.form-table.rules th {
  width: 175px;
}
.ui-dialog .form-table.rules th,
.ui-dialog .form-table.rules td {
  padding: 5px 0px;
}
.rules-list {
  width: 100%;
  margin-bottom: 10px;
}
.rules-list tr.header th {
  background-color: #f1f1f1;
}
.rules-list tbody .rule:nth-child(even) td {
  background-color: #f9f9f9;
}
#rules-list,
#queue {
  margin-bottom: 20px;
}
#rules-list h4 {
  margin: 20px 0 10px;
  font-size: 1.2em;
}
#rules-list .header th,
#rules-list .rule td {
  padding: 4px;
}
#rules-list .rule td {
  margin: 0;
  border-bottom: 1px solid #ccc;
}
#rules-list .type {
  width: 80px;
}
#rules-list .btn {
  width: 55px;
}
#rules-list .rule td.btn {
  width: 70px;
  padding: 4px 0 4px 2px;
}
.badge {
  padding: 0 5px;
  color: #fff;
  background-color: #00a0d2;
  border-radius: 5px;
  font-size: .8em;
  font-weight: bold;
  text-align: center;
}
.badge.manual {
  background-color: #46b450;
}
#rules-list .btn span {
  width: 67px;
  text-align: center;
}
p.rules-btn {
  margin: 20px 0 0;
  padding: 0;
}
.queue {
  table-layout: fixed;
  border-collapse: collapse;
}
.queue .job td {
  border-bottom: 1px solid #ccc;
}
.queue .status,
.queue .job td.status {
  width: 45px;
}
.queue td.status {
  text-align: center;
}
.queue .badge {
  border-radius: 3px;
  cursor: default;
}
.badge.new {
  background-color: #666;
}
.badge.pending {
  background-color: #00a0d2;
}
.badge.done {
  background-color: #46b450;
}
.badge.review {
  background-color: #ffb900;
}
.badge.review.rule {
  margin-left: 2px;
}
.badge.error {
  background-color: #dc3232;
}
.badge.unknown {
  color: #666;
  background-color: #ccc;
}
.queue .btn {
  width: 86px;
}
.queue .button-secondary {
  line-height: 17px;
  height: 19px;
  padding: 0 2px;
  font-size: 8pt;
}
.queue .button-secondary.to-right {
  margin-left: 2px;
}
.queue .button-secondary a {
  color: #555;
  text-decoration: none;
}
.queue .button-secondary a:hover {
  color: #23282d;
}
.queue .button-secondary .dashicons {
  position: relative;
  top: 1px;
  font-size: 15px;
}
p.submit.left {
  float: left;
}
#importSettingsForm {
  float: right;
  text-align: left;
  max-width: 100%;
  margin-top: 20px;
  padding-top: 10px;
  position: relative;
  overflow: hidden;
}
#settingsfile {
  padding: 0 2px;
}
#settingsfile {
  padding: 0 2px;
}

/* debug block */
#debug {
  clear: both;
}
#debug .debug th,
#debug .debug td {
  padding: 5px 10px;
  font-size: 13px;
}
#debug pre {
  margin: 0 0 1em;
  font-size: 12px;
  -moz-tab-size: 4;
  -o-tab-size: 4;
  tab-size: 4;
  white-space: pre-wrap;
}
#explain-panel p {
 font-size:120%
}
PK.�1\^8��@@%critcss-inc/css/ao-tablesorter/bg.gifnu�[���GIF89a	�fff���!�,	@���˫
�L��yl�ns�Y_�%S;PK.�1\�ލ�66&critcss-inc/css/ao-tablesorter/asc.gifnu�[���GIF89a�rw|���!�,
����ڛg�k$-;PK.�1\W8766'critcss-inc/css/ao-tablesorter/desc.gifnu�[���GIF89a�rw|���!�,
��ɭ���T2�Y;PK.�1\��2k(critcss-inc/css/ao-tablesorter/style.cssnu�[���/* tables */
table.tablesorter {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
  margin: 30px 0 5px;
  font-size: 8pt;
  width: 100%;
  text-align: left;
}
table.tablesorter thead tr th,
table.tablesorter tfoot tr th {
  background-color: #f1f1f1;
  font-size: 8pt;
  padding: 2px;
}
table.tablesorter thead tr .header {
  background-image: url(bg.gif);
  background-repeat: no-repeat;
  background-position: center right;
  cursor: pointer;
}
table.tablesorter tbody td {
  color: #444;
  padding: 4px;
  background-color: #fff;
  vertical-align: middle;
}
table.tablesorter tbody tr.even td,
table.tablesorter tbody tr:nth-child(even) td {
  background-color: #f9f9f9;
}
table.tablesorter thead tr .headerSortUp {
  background-image: url(asc.gif);
}
table.tablesorter thead tr .headerSortDown {
  background-image: url(desc.gif);
}
table.tablesorter thead tr .headerSortDown,
table.tablesorter thead tr .headerSortUp {
  background-color: #ccc;
}
PK.�1\���;;autoptimizeMetabox.phpnu�[���<?php
/**
 * Handles meta box to disable optimizations.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeMetabox
{
    public function __construct()
    {
        $this->run();
    }

    public function run()
    {
        add_action( 'add_meta_boxes', array( $this, 'ao_metabox_add_box' ) );
        add_action( 'save_post', array( $this, 'ao_metabox_save' ) );
        add_action( 'wp_ajax_ao_metabox_ccss_addjob', array( $this, 'ao_metabox_generateccss_callback' ) );
    }

    public function ao_metabox_add_box()
    {
        $screens = array(
            'post',
            'page',
            // add extra types e.g. product or ... ?
        );

        $screens = apply_filters( 'autoptimize_filter_metabox_screens', $screens );

        foreach ( $screens as $screen ) {
            add_meta_box(
                'ao_metabox',
                esc_html__( 'Autoptimize this page', 'autoptimize' ),
                array( $this, 'ao_metabox_content' ),
                $screen,
                'side'
            );
        }
    }

    /**
     * Prints the box content.
     *
     * @param WP_Post $post The object for the current post/page.
     */
    function ao_metabox_content( $post )
    {
        // phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace

        wp_nonce_field( 'ao_metabox', 'ao_metabox_nonce' );

        $ao_opt_value = $this->check_ao_opt_sanity( get_post_meta( $post->ID, 'ao_post_optimize', true ) );

        $_ao_meta_sub_opacity = '';
        if ( 'on' !== $ao_opt_value['ao_post_optimize'] ) {
            $_ao_meta_sub_opacity = 'opacity:.33;';
        }
        ?>
        <p >
            <input type="checkbox" id="autoptimize_post_optimize" class="ao_meta_main" name="ao_post_optimize" <?php echo 'on' !== $ao_opt_value['ao_post_optimize'] ? '' : 'checked="checked" '; ?> />
            <label for="autoptimize_post_optimize">
                 <?php esc_html_e( 'Optimize this page?', 'autoptimize' ); ?>
            </label>
        </p>
        <?php
        $_ao_meta_js_style = '';
        if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_js', false ) ) {
            $_ao_meta_js_style = 'display:none;';
        }
        echo '<p class="ao_meta_sub" style="' . $_ao_meta_sub_opacity . $_ao_meta_js_style . '">';
        ?>
        <input type="checkbox" id="autoptimize_post_optimize_js" name="ao_post_js_optimize" <?php echo 'on' !== $ao_opt_value['ao_post_js_optimize'] ? '' : 'checked="checked" '; ?> />
            <label for="autoptimize_post_optimize_js">
                 <?php esc_html_e( 'Optimize JS?', 'autoptimize' ); ?>
            </label>
        </p>
        <?php
        $_ao_meta_css_style = '';
        if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css', false ) ) {
            $_ao_meta_css_style = 'display:none;';
        }
        echo '<p class="ao_meta_sub" style="' . $_ao_meta_sub_opacity . $_ao_meta_css_style . '">';
        ?>
        <input type="checkbox" id="autoptimize_post_optimize_css" name="ao_post_css_optimize" <?php echo 'on' !== $ao_opt_value['ao_post_css_optimize'] ? '' : 'checked="checked" '; ?> />
            <label for="autoptimize_post_optimize_css">
                 <?php esc_html_e( 'Optimize CSS?', 'autoptimize' ); ?>
            </label>
        </p>
        <?php
        $_ao_meta_ccss_style = '';
        if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer', false ) || 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css', false ) ) {
            $_ao_meta_ccss_style = 'display:none;';
        }
        if ( 'on' !== $ao_opt_value['ao_post_css_optimize'] ) {
            $_ao_meta_ccss_style .= 'opacity:.33;';
        }
        echo '<p class="ao_meta_sub ao_meta_sub_css" style="' . $_ao_meta_sub_opacity . $_ao_meta_ccss_style . '">';
        ?>
            <input type="checkbox" id="autoptimize_post_ccss" name="ao_post_ccss" <?php echo 'on' !== $ao_opt_value['ao_post_ccss'] ? '' : 'checked="checked" '; ?> />
            <label for="autoptimize_post_ccss">
                 <?php esc_html_e( 'Inline critical CSS?', 'autoptimize' ); ?>
            </label>
        </p>
        <?php
        $_ao_meta_lazyload_style = '';
        if ( false === autoptimizeImages::should_lazyload_wrapper( true ) ) {
            $_ao_meta_lazyload_style = 'display:none;';
        }
        echo '<p class="ao_meta_sub" style="' . $_ao_meta_sub_opacity . $_ao_meta_lazyload_style . '">';
        ?>
            <input type="checkbox" id="autoptimize_post_lazyload" name="ao_post_lazyload" <?php echo 'on' !== $ao_opt_value['ao_post_lazyload'] ? '' : 'checked="checked" '; ?> />
            <label for="autoptimize_post_lazyload">
                 <?php esc_html_e( 'Lazyload images?', 'autoptimize' ); ?>
            </label>
        </p>
        <?php
        $_ao_meta_preload_style = '';
        if ( false === autoptimizeImages::should_lazyload_wrapper() && false === autoptimizeImages::imgopt_active() ) {
            // img preload requires imgopt and/ or lazyload to be active.
            $_ao_meta_preload_style = 'opacity:.33;';
        }
        ?>
        <p class="ao_meta_sub ao_meta_preload" style="<?php echo $_ao_meta_sub_opacity . $_ao_meta_preload_style; ?>">
            <label for="autoptimize_post_preload">
                 <?php esc_html_e( 'LCP Image to preload', 'autoptimize' ); ?>
            </label>
            <?php
            if ( is_array( $ao_opt_value ) && array_key_exists( 'ao_post_preload', $ao_opt_value ) ) {
                $_preload_img = esc_attr( $ao_opt_value['ao_post_preload'] );
            } else {
                $_preload_img = '';
            }
            ?>
            <input type="text" id="autoptimize_post_preload" name="ao_post_preload" value="<?php echo $_preload_img; ?>">
        </p>
        <?php
            echo apply_filters( 'autoptimize_filter_metabox_extra_ui', '');
        ?>
        <p>&nbsp;</p>
        <p>
            <?php
            // Get path + check if button should be enabled or disabled.
            $_generate_disabled = true;
            $_slug              = false;
            $_type              = 'is_single';

            // harvest post ID from URL, get permalink from that and extract path from that.
            if ( array_key_exists( 'post', $_GET ) ) {
                $_slug = str_replace( AUTOPTIMIZE_WP_SITE_URL, '', get_permalink( $_GET['post'] ) );
            }

            // override the default 'is_single' if post.
            global $post;
            if ( 'page' === $post->post_type ) {
                $_type = 'is_page';
            }

            // if CSS opt and inline & defer are on and if we have a slug, the button can be active.
            if ( false !== $_slug && 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_css', false ) && 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer', false ) && ! empty( apply_filters( 'autoptimize_filter_ccss_key', autoptimizeOptionWrapper::get_option( 'autoptimize_ccss_key', false ) ) ) && '2' === autoptimizeOptionWrapper::get_option( 'autoptimize_ccss_keyst', false ) ) {
                $_generate_disabled = false;
            }
            ?>
            <button class="button ao_meta_sub ao_meta_sub_css" id="generateccss" style="<?php echo $_ao_meta_sub_opacity . $_ao_meta_ccss_style; ?>" <?php if ( true === $_generate_disabled ) { echo 'disabled'; } ?>><?php esc_html_e( 'Generate Critical CSS', 'autoptimize' ); ?></button>
        </p>
        <script>
            jQuery(document).ready(function() {
                jQuery( "#autoptimize_post_optimize" ).change(function() {
                    if (this.checked) {
                        jQuery(".ao_meta_sub:visible").fadeTo("fast",1);
                    } else {
                        jQuery(".ao_meta_sub:visible").fadeTo("fast",.33);
                    }
                });
                jQuery( "#autoptimize_post_optimize_css" ).change(function() {
                    if (this.checked) {
                        jQuery(".ao_meta_sub_css:visible").fadeTo("fast",1);
                    } else {
                        jQuery(".ao_meta_sub_css:visible").fadeTo("fast",.33);
                    }
                });
                jQuery( "#autoptimize_post_ccss" ).change(function() {
                    if (this.checked) {
                        jQuery("#generateccss:visible").fadeTo("fast",1);
                    } else {
                        jQuery("#generateccss:visible").fadeTo("fast",.33);
                    }
                });
                <?php
                if ( true === autoptimizeImages::should_lazyload_wrapper() && false === autoptimizeImages::imgopt_active() ) {
                ?>
                    jQuery( "#autoptimize_post_lazyload" ).change(function() {
                        if (this.checked) {
                            jQuery(".ao_meta_preload:visible").fadeTo("fast",1);
                        } else {
                            jQuery(".ao_meta_preload:visible").fadeTo("fast",.33);
                        }                    
                    });
                <?php
                }
                ?>
                jQuery("#generateccss").click(function(e){
                    e.preventDefault();
                    // disable button to avoid it being clicked several times.
                    jQuery("#generateccss").prop('disabled', true);
                    var data = {
                        'action': 'ao_metabox_ccss_addjob',
                        'path'  : '<?php echo $_slug; ?>',
                        'type'  : '<?php echo $_type; ?>',
                        'ao_ccss_addjob_nonce': '<?php echo wp_create_nonce( 'ao_ccss_addjob_nonce' ); ?>',
                    };

                    jQuery.post(ajaxurl, data, function(response) {
                        response_array=JSON.parse(response);
                        if (response_array['code'] == 200) {
                            setCritCSSbutton("<?php esc_html_e( 'Added to CCSS job queue.', 'autoptimize' ); ?>", "green");
                        } else {
                            setCritCSSbutton("<?php esc_html_e( 'Could not add to CCSS job queue.', 'autoptimize' ); ?>", "orange");
                        }
                    }).fail(function() {
                        setCritCSSbutton("<?php esc_html_e( 'Sorry, something went wrong.', 'autoptimize' ); ?>", "orange");
                    });
                });
            });

            function setCritCSSbutton( message, color) {
                jQuery("#generateccss").html(message);
                jQuery("#generateccss").prop("style","border-color:" + color + "!important; color:" + color + "!important");
            }
        </script>
        <?php
    }

    /**
     * When the post is saved, saves our custom data.
     *
     * @param int $post_id The ID of the post being saved.
     */
    public function ao_metabox_save( $post_id )
    {
        // If this is an autosave, our form has not been submitted, so we don't want to do anything.
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
            return $post_id;
        }

        // check if from our own data.
        if ( ! isset( $_POST['ao_metabox_nonce'] ) ) {
            return $post_id;
        }

        // Check if our nonce is set and verify if valid.
        $nonce = $_POST['ao_metabox_nonce'];
        if ( ! wp_verify_nonce( $nonce, 'ao_metabox' ) ) {
            return $post_id;
        }

        // Check the user's permissions.
        if ( 'page' === $_POST['post_type'] ) {
            if ( ! current_user_can( 'edit_page', $post_id ) ) {
                return $post_id;
            }
        } else {
            if ( ! current_user_can( 'edit_post', $post_id ) ) {
                return $post_id;
            }
        }

        // OK, we can have a look at the actual data now.
        // Sanitize user input.
        foreach ( apply_filters( 'autoptimize_filter_meta_valid_optims', array( 'ao_post_optimize', 'ao_post_js_optimize', 'ao_post_css_optimize', 'ao_post_ccss', 'ao_post_lazyload', 'ao_post_preload' ) ) as $opti_type ) {
            if ( in_array( $opti_type, apply_filters( 'autoptimize_filter_meta_optim_nonbool', array( 'ao_post_preload' ) ) ) ) {
                if ( isset( $_POST[ $opti_type ] ) ) {
                    $ao_meta_result[ $opti_type ] = $_POST[ $opti_type ];
                } else {
                    $ao_meta_result[ $opti_type ] = false;
                }
            } else if ( ! isset( $_POST[ $opti_type ] ) ) {
                $ao_meta_result[ $opti_type ] = '';
            } else if ( 'on' === $_POST[ $opti_type ] ) {
                $ao_meta_result[ $opti_type ] = 'on';
            } 
        }

        // Update the meta field in the database.
        update_post_meta( $post_id, 'ao_post_optimize', $ao_meta_result );
    }

    public function ao_metabox_generateccss_callback()
    {
        check_ajax_referer( 'ao_ccss_addjob_nonce', 'ao_ccss_addjob_nonce' );

        if ( current_user_can( 'manage_options' ) && array_key_exists( 'path', $_POST ) && ! empty( $_POST['path'] ) ) {
            if ( array_key_exists( 'type', $_POST ) && 'is_page' === $_POST['type'] ) {
                $type = 'is_page';
            } else {
                $type = 'is_single';
            }

            $path = wp_strip_all_tags( $_POST['path'] );
            $criticalcss = autoptimize()->criticalcss();
            $_result = $criticalcss->enqueue( '', $path, $type );

            if ( $_result ) {
                $response['code']   = '200';
                $response['string'] = $path . ' added to job queue.';
            } else {
                $response['code']   = '404';
                $response['string'] = 'could not add ' . $path . ' to job queue.';
            }
        } else {
            $response['code']   = '500';
            $response['string'] = 'nok';
        }

        // Dispatch respose.
        echo json_encode( $response );

        // Close ajax request.
        wp_die();
    }

    public function get_metabox_default_values()
    {
        $ao_metabox_defaults = array(
            'ao_post_optimize'     => 'on',
            'ao_post_js_optimize'  => 'on',
            'ao_post_css_optimize' => 'on',
            'ao_post_ccss'         => 'on',
            'ao_post_lazyload'     => 'on',
            'ao_post_preload'      => '',
        );
        return $ao_metabox_defaults;
    }

    public function check_ao_opt_sanity( $ao_opt_val ) {
        if ( empty( $ao_opt_val ) || ! is_array( $ao_opt_val ) ) {
            $ao_opt_val = $this->get_metabox_default_values();
        } else {
            foreach ( array( 'ao_post_optimize', 'ao_post_js_optimize', 'ao_post_css_optimize', 'ao_post_ccss', 'ao_post_lazyload' ) as $key ) {
                if ( ! array_key_exists( $key, $ao_opt_val ) ) {
                    $ao_opt_val[ $key ] = 'off';
                }
            }
        }

        return $ao_opt_val;
    }
}
PK.�1\X��ooautoptimizeProTab.phpnu�[���<?php
/**
 * Handles adding "more tools" tab in AO admin settings page which promotes (future) AO
 * addons and/or affiliate services.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeProTab
{
    /**
     * Random title string.
     *
     * @var string
     */
    protected $rnd_title = null;

    public function __construct()
    {
        // alternate between tab title every 5 minutes.
        if ( floor( date( "i", time() ) / 5 ) %2 === 0 ) {
            $this->rnd_title = esc_html__( 'Page Cache', 'autoptimize' );
        } else {
            $this->rnd_title = esc_html__( 'Pro Boosters', 'autoptimize' );
        }

        $this->run();
    }

    public function run()
    {
        if ( $this->enabled() ) {
            add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_pro_tabs' ), 10, 1 );
        }
        if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
            add_action( 'network_admin_menu', array( $this, 'add_admin_menu' ) );
        } else {
            add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        }
    }

    protected function enabled()
    {
        return apply_filters( 'autoptimize_filter_show_partner_tabs', true );
    }

    public function add_pro_tabs( $in )
    {
        $in = array_merge(
            $in,
            array(
                'ao_protab' => '&#x1F680; ' . $this->rnd_title
            )
        );

        return $in;
    }

    public function add_admin_menu()
    {
        if ( $this->enabled() ) {
            add_submenu_page( '', 'AO pro', 'AO pro', 'manage_options', 'ao_protab', array( $this, 'ao_pro_page' ) );
        }
    }

    public function ao_pro_page()
    {
        ?>
    <style>
        .ao_settings_div {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;font-size: 120% !important; padding-bottom:20px;}
        .ao_settings_div p {font-size:110%;}
        
        #aoprocontainer{width:100%;overflow:hidden;}
        #aoprotxt{width:68%;float:left;}
        #aoprobuy { background:#ba4102;text-align:center;border-radius:25px; }
        #aoprobuy p {margin:.25em 1em}
        #aoprobuy p#cta {font-size:150%;}
        #aoproimg {width:28%;float:right;}
        
        @media (max-width:699px) { 
            #aoproimg{display:none;}
            #aoprotxt{width:100% !important;}
            #aoprobuy{font-size:70%;}
        }
    </style>
    <script>document.title = "Autoptimize: <?php echo $this->rnd_title ?> " + document.title;</script>
    <div class="wrap">
        <h1><?php apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html_e( 'Autoptimize Pro Settings', 'autoptimize' ) : esc_html_e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
        <?php
            echo autoptimizeConfig::ao_admin_tabs();
            $aopro_explanation = '';

            $_transient    = 'aopro_explain';
            $_explain_html = 'https://misc.optimizingmatters.com/aopro_explain.html?ao_ver=';
            
            // get the HTML with the explanation of what AOPro is.
            if ( apply_filters( 'autoptimize_settingsscreen_remotehttp', true ) ) {
                $aopro_explanation = get_transient( $_transient );
                if ( empty( $aopro_explanation ) ) {
                    $ccss_expl_resp = wp_remote_get( $_explain_html . AUTOPTIMIZE_PLUGIN_VERSION );
                    if ( ! is_wp_error( $ccss_expl_resp ) ) {
                        if ( '200' == wp_remote_retrieve_response_code( $ccss_expl_resp ) ) {
                            $aopro_explanation = wp_kses_post( wp_remote_retrieve_body( $ccss_expl_resp ) );
                            set_transient( $_transient, $aopro_explanation, WEEK_IN_SECONDS );
                        }
                    }
                }
            }

            // placeholder text in case HTML is empty.
            if ( empty( $aopro_explanation ) ) {
                // translators: h2, strong but also 2 links.
                $aopro_explanation = sprintf( esc_html__( '%1$sAdd more power to Autoptimize with Pro!%2$s%3$sAs a user of Autoptimize you understand %5$sthe importance of having a fast site%6$s. Autoptimize Pro is a premium Power-Up extending AO by adding %5$simage optimization, CDN, automatic critical CSS rules generation and page caching but also providing extra “booster” options%6$s, all in one handy subscription to make your site even faster!%4$s%3$sHave a look at %7$shttps://autoptimize.com/pro/%8$s for more info or %9$sclick here to buy now%10$s!%4$s', 'autoptimize' ), '<h2>', '</h2>', '<p>', '</p>', '<strong>', '</strong>', '<a href="https://autoptimize.com/pro/" target="_blank">', '</a>', '<a href="https://checkout.freemius.com/mode/dialog/plugin/10906/plan/18508/?currency=auto" target="_blank">', '</a>' );
            } else {
                // we were able to fetch the explenation, so add the JS to show correct language.
                $aopro_explanation .= "<script>jQuery('.ao_i18n').hide();d=document;lang=d.getElementsByTagName('html')[0].getAttribute('lang').substring(0,2);if(d.getElementById(lang)!= null){jQuery('#'+lang).show();}else{jQuery('#default').show();}</script>";
            }
            ?>
            <div class="ao_settings_div">
            <?php
                // and echo it.
                echo $aopro_explanation;
            ?>
            </div>
    </div>
        <?php
    }
}
PK.�1\rJ#�\�\"autoptimizeCriticalCSSSettings.phpnu�[���<?php
/**
 * Critical CSS Options page.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCriticalCSSSettings {
    /**
     * Options.
     *
     * @var bool
     */
    private $settings_screen_do_remote_http = true;

    /**
     * Critical CSS object.
     *
     * @var object
     */
    protected $criticalcss;

    public function __construct() {
        $this->criticalcss = autoptimize()->criticalcss();
        $this->settings_screen_do_remote_http = apply_filters( 'autoptimize_settingsscreen_remotehttp', $this->settings_screen_do_remote_http );
        $this->run();
    }

    protected function enabled()
    {
        return apply_filters( 'autoptimize_filter_show_criticalcss_tabs', true );
    }

    public function run()
    {
        if ( $this->enabled() ) {
            add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_critcss_tabs' ), 10, 1 );
            add_action( 'admin_enqueue_scripts', array( $this, 'admin_assets' ) );

            if ( $this->is_multisite_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
                add_action( 'network_admin_menu', array( $this, 'add_critcss_admin_menu' ) );
            } else {
                add_action( 'admin_menu', array( $this, 'add_critcss_admin_menu' ) );
            }

            $criticalcss_ajax = new autoptimizeCriticalCSSSettingsAjax();

            // if debug logging is off but the file is present, then remove the debug log file.
            if ( empty( $this->criticalcss->get_option( 'debug' ) ) && file_exists( AO_CCSS_LOG ) ) {
                unlink( AO_CCSS_LOG );
            }
        }
    }

    public function add_critcss_tabs( $in )
    {
        $in = array_merge( $in, array( 'ao_critcss' => apply_filters( 'autoptimize_filter_ccss_tab_text', '⚡ ' . esc_html__( 'Critical CSS', 'autoptimize' ) ) ) );

        return $in;
    }

    public function add_critcss_admin_menu()
    {
        // Register settings.
        register_setting( 'ao_ccss_options_group', 'autoptimize_css_defer_inline' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_rules' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_additional' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_queue' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_viewport' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_finclude' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_rtimelimit' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_noptimize' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_debug' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_key' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_keyst' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_loggedin' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_forcepath' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_deferjquery' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_domain' );
        register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_unloadccss' );

        // And add submenu-page.
        add_submenu_page( '', 'Critical CSS', 'Critical CSS', 'manage_options', 'ao_critcss', array( $this, 'ao_criticalcsssettings_page' ) );
    }

    public function admin_assets( $hook ) {
        // Return if plugin is not hooked.
        if ( 'settings_page_ao_critcss' != $hook && 'admin_page_ao_critcss' != $hook ) {
            return;
        }

        // Stylesheets to add.
        wp_enqueue_style( 'wp-jquery-ui-dialog' );
        wp_enqueue_style( 'ao-tablesorter', plugins_url( 'critcss-inc/css/ao-tablesorter/style.css', __FILE__ ), null, AUTOPTIMIZE_PLUGIN_VERSION );
        wp_enqueue_style( 'ao-ccss-admin-css', plugins_url( 'critcss-inc/css/admin_styles.css', __FILE__ ), null, AUTOPTIMIZE_PLUGIN_VERSION );

        // Scripts to add.
        wp_enqueue_script( 'jquery-ui-dialog', '', array( 'jquery' ), null, true );
        wp_enqueue_script( 'md5', plugins_url( 'critcss-inc/js/md5.min.js', __FILE__ ), null, AUTOPTIMIZE_PLUGIN_VERSION, true );
        wp_enqueue_script( 'tablesorter', plugins_url( 'critcss-inc/js/jquery.tablesorter.min.js', __FILE__ ), array( 'jquery' ), AUTOPTIMIZE_PLUGIN_VERSION, true );
        wp_enqueue_script( 'ao-ccss-admin-license', plugins_url( 'critcss-inc/js/admin_settings.js', __FILE__ ), array( 'jquery' ), AUTOPTIMIZE_PLUGIN_VERSION, true );
    }

    public function ao_criticalcsssettings_page()
    {
        // these are not OO yet, simply require for now.
        require_once( 'critcss-inc/admin_settings_rules.php' );
        require_once( 'critcss-inc/admin_settings_queue.php' );
        require_once( 'critcss-inc/admin_settings_key.php' );
        require_once( 'critcss-inc/admin_settings_adv.php' );
        require_once( 'critcss-inc/admin_settings_explain.php' );

        $ao_ccss_key           = $this->criticalcss->get_option( 'key' );
        $ao_ccss_keyst         = $this->criticalcss->get_option( 'keyst' );
        $ao_css_defer          = $this->criticalcss->get_option( 'css_defer' );
        $ao_ccss_deferjquery   = $this->criticalcss->get_option( 'deferjquery' );
        $ao_ccss_queue         = $this->criticalcss->get_option( 'queue' );
        $ao_ccss_rules         = $this->criticalcss->get_option( 'rules' );
        $ao_ccss_servicestatus = $this->criticalcss->get_option( 'servicestatus' );
        $ao_ccss_finclude      = $this->criticalcss->get_option( 'finclude' );
        $ao_ccss_rtimelimit    = $this->criticalcss->get_option( 'rtimelimit' );
        $ao_ccss_debug         = $this->criticalcss->get_option( 'debug' );
        $ao_ccss_noptimize     = $this->criticalcss->get_option( 'noptimize' );
        $ao_css_defer_inline   = $this->criticalcss->get_option( 'css_defer_inline' );
        $ao_ccss_loggedin      = $this->criticalcss->get_option( 'loggedin' );
        $ao_ccss_forcepath     = $this->criticalcss->get_option( 'forcepath' );
        $ao_ccss_domain        = $this->criticalcss->get_option( 'domain' );
        ?>
        <script>document.title = "Autoptimize: <?php esc_html_e( 'Critical CSS', 'autoptimize' ); ?> " + document.title;</script>
        <div class="wrap">
            <div id="autoptimize_main">
                <div id="ao_title_and_button">
                    <h1><?php apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html_e( 'Autoptimize Pro Settings', 'autoptimize' ) : esc_html_e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
                </div>

                <?php
                // Print AO settings tabs.
                echo autoptimizeConfig::ao_admin_tabs();

                if ( autoptimizeUtils::is_local_server() && isset( $ao_ccss_key ) ) { ?>
                    <div class="notice-warning notice"><p>
                    <?php
                    echo esc_html__( 'The Critical CSS service does not work on locally hosted sites or when the server is on a private network.', 'autoptimize' );
                    ?>
                    </p></div>
                <?php }

                $mkdirresult = $this->criticalcss->create_ao_ccss_dir();

                // Warn if we could not create those files.
                if ( ( true !== $mkdirresult ) ) {
                    ?>
                    <div class="notice-error notice"><p>
                    <?php
                    esc_html_e( 'Could not create the required directory. Make sure the webserver can write to the wp-content/uploads directory.', 'autoptimize' );
                    ?>
                    </p></div>
                    <?php
                }

                // Check if CSS optimization is on.
                if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) || 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer' ) ) {
                    ?>
                    <div class="notice-info notice"><p>
                    <?php
                    esc_html_e( 'To be able to use Critical CSS you will have to enable CSS optimization and make sure "eliminate render-blocking CSS" is active on the main Autoptimize settings page.', 'autoptimize' );
                    ?>
                    </p></div>
                    <?php
                }

                // Check for "inline & defer CSS" being active in Autoptimize.
                if ( ! empty( $ao_ccss_key ) && ! $ao_css_defer && empty( $ao_ccss_keyst ) ) {
                        // no keystate so likely in activation-process of CCSS, let's enable "inline & defer CSS" immediately to make things easier!
                        autoptimizeOptionWrapper::update_option( 'autoptimize_css_defer', 'on' );
                    ?>
                        <div class="notice-info notice"><p>
                        <?php
                        esc_html_e( 'The "Eliminate render-blocking CSS" option was activated to allow critical CSS to be used.', 'autoptimize' );
                        ?>
                        </p></div>
                        <?php
                }

                // check if defer jQuery is active and warn if so.
                if ( 1 == $ao_ccss_deferjquery && PAnD::is_admin_notice_active( 'i-know-about-defer-inline-forever' ) ) {
                    ?>
                    <div data-dismissible="i-know-about-defer-inline-forever" class="notice-warning notice is-dismissible"><p>
                    <?php
                    esc_html_e( 'You have "defer jQuery and other non-aggregated JS-files" active (under Advanced Settings), but that functionality is deprecated and will be removed in the next major version of Autoptimize. Consider using the new "Do not aggregate but defer" and "Also defer inline JS" options on the main settings page instead.', 'autoptimize' );
                    ?>
                    </p></div>
                    <?php
                }

                // warn if it looks as though the queue processing job looks isn't running
                // but store result in transient as to not to have to go through 2 arrays each and every time.
                $_warn_cron = get_transient( 'ao_ccss_cronwarning' );
                if ( ! empty( $ao_ccss_key ) && false === $_warn_cron ) {
                    $_jobs_all_new         = true;
                    $_oldest_job_timestamp = microtime( true ); // now.
                    $_jobs_too_old         = true;

                    // go over queue array.
                    if ( empty( $ao_ccss_queue ) ) {
                        // no jobs, then no warning.
                        $_jobs_all_new = false;
                    } else {
                        foreach ( $ao_ccss_queue as $job ) {
                            if ( $job['jctime'] < $_oldest_job_timestamp ) {
                                // we need to catch the oldest job's timestamp.
                                $_oldest_job_timestamp = $job['jctime'];
                            }

                            if ( 'NEW' !== $job['jqstat'] && 'firstrun' !== $job['ljid'] ) {
                                // we have a non-"NEW" job which is not our pending firstrun job either, break the loop.
                                $_jobs_all_new = false;
                                break;
                            }
                        }
                    }

                    // is the oldest job too old (4h)?
                    if ( $_oldest_job_timestamp > microtime( true ) - 60 * 60 * 4 ) {
                        $_jobs_too_old = false;
                    }

                    if ( $_jobs_all_new && ! $this->ao_ccss_has_autorules() && $_jobs_too_old ) {
                        $_warn_cron            = 'on';
                        $_transient_multiplier = 1; // store for 1 hour.
                    } else {
                        $_warn_cron            = 'off';
                        $_transient_multiplier = 4; // store for 4 hours.
                    }
                    // and set transient.
                    set_transient( 'ao_ccss_cronwarning', $_warn_cron, $_transient_multiplier * HOUR_IN_SECONDS );
                }

                if ( ! empty( $ao_ccss_key ) && 'on' == $_warn_cron && PAnD::is_admin_notice_active( 'i-know-about-cron-30' ) ) {
                    ?>
                    <div data-dismissible="i-know-about-cron-30" class="notice-warning notice is-dismissible"><p>
                    <?php
                    _e( 'It looks like there might be a problem with WordPress cron (task scheduling). Have a look at <a href="https://blog.futtta.be/2023/03/17/how-to-fix-autoptimize-critical-css-cron-issue/" target="_blank">the FAQ</a> or the info in the Job Queue instructions if all jobs remain in "N" status and no rules are created.', 'autoptimize' );
                    ?>
                    </p></div>
                    <?php
                } elseif ( ! empty( $ao_ccss_key ) && '2' == $ao_ccss_keyst && 'on' != $_warn_cron && ! $this->ao_ccss_has_autorules() ) {
                    ?>
                    <div class="notice-success notice"><p>
                    <?php
                    esc_html_e( 'Great, Autoptimize will now automatically start creating new critical CSS rules, you should see those appearing below in the next couple of hours.', 'autoptimize' );
                    echo ' ';
                    _e( 'In the meantime you might want to <strong>edit default rule CSS now</strong>, to avoid all CSS being inlined when no (applicable) rules are found.', 'autoptimize' );
                    ?>
                    </p></div>
                    <?php
                }

                // warn if service is down.
                if ( ! empty( $ao_ccss_key ) && ! empty( $ao_ccss_servicestatus ) && is_array( $ao_ccss_servicestatus ) && 'down' === $ao_ccss_servicestatus['critcss']['status'] ) {
                    ?>
                    <div class="notice-warning notice"><p>
                    <?php
                    esc_html_e( 'The critical CSS service has been reported to be down. Although no new rules will be created for now, this does not prevent existing rules from being applied.', 'autoptimize' );
                    ?>
                    </p></div>
                    <?php
                }

                // warn if too many rules (based on length of ao_ccss_rules option) as that might cause issues at e.g. wpengine
                // see https://wpengine.com/support/database-optimization-best-practices/#Autoloaded_Data .
                $_raw_rules_length = strlen( get_option( 'autoptimize_ccss_rules', '' ) );
                if ( $_raw_rules_length > apply_filters( 'autoptimize_ccss_rules_length_warning', 500000 ) ) {
                    ?>
                    <div class="notice-warning notice"><p>
                    <?php
                    esc_html_e( 'It looks like the amount of Critical CSS rules is very high, it is recommended to reconfigure Autoptimize (e.g. by manually creating broader rules) to ensure less rules are created.', 'autoptimize' );
                    ?>
                    </p></div>
                    <?php
                }

                // Settings Form.
                ?>
                <form id="settings" method="post" action="options.php">
                    <?php
                    settings_fields( 'ao_ccss_options_group' );

                    // Get API key status.
                    $key = $this->criticalcss->key_status( true );

                    if ( $this->is_multisite_network_admin() ) {
                        ?>
                        <ul id="key-panel">
                            <li class="itemDetail">
                            <?php
                                // translators: the placesholder is for a line of code in wp-config.php.
                                echo sprintf( esc_html__( '<p>Critical CSS settings cannot be set at network level as critical CSS is specific to each sub-site.</p><p>You can however provide the critical CSS API key for use by all sites by adding this your wp-config.php as %s</p>', 'autoptimize' ), '<br/><code>define(\'AUTOPTIMIZE_CRITICALCSS_API_KEY\', \'eyJhbGmorestringsherexHa7MkOQFtDFkZgLmBLe-LpcHx4\');</code>' );
                            ?>
                            </li>
                        </ul>
                        <?php
                    } else {
                        if ( 'valid' == $key['status'] || ( defined( 'AO_PRO_VERSION' ) && has_filter( 'autoptimize_filter_ccss_key' ) ) ) {
                            // If key status is valid, render other panels.
                            // Render rules section.
                            ao_ccss_render_rules();
                            // Render queue section.
                            ao_ccss_render_queue();
                            // Render advanced panel.
                            ao_ccss_render_adv();
                        } else {
                            if ( apply_filters( 'autoptimize_filter_ccss_rules_without_api', true ) ) {
                                // Render rules section for manual rules.
                                ao_ccss_render_rules();
                            } else {
                                echo "<input class='hidden' name='autoptimize_ccss_rules' value='" . json_encode( $ao_ccss_rules, JSON_FORCE_OBJECT ) . "'>";
                            }

                            // But if key is other than valid, add hidden fields to persist settings when submitting form
                            // Show explanation of why and how to get a API key.
                            ao_ccss_render_explain();

                            // Get viewport size.
                            $viewport = $this->criticalcss->viewport();

                            // Add hidden fields.
                            echo "<input class='hidden' name='autoptimize_ccss_queue' value='" . json_encode( $ao_ccss_queue, JSON_FORCE_OBJECT ) . "'>";
                            echo '<input class="hidden" name="autoptimize_ccss_viewport[w]" value="' . esc_attr( $viewport['w'] ) . '">';
                            echo '<input class="hidden" name="autoptimize_ccss_viewport[h]" value="' . esc_attr( $viewport['h'] ) . '">';
                            echo '<input class="hidden" name="autoptimize_ccss_finclude" value="' . esc_attr( $ao_ccss_finclude ) . '">';
                            echo '<input class="hidden" name="autoptimize_ccss_rtimelimit" value="' . esc_attr( $ao_ccss_rtimelimit ) . '">';
                            echo '<input class="hidden" name="autoptimize_ccss_debug" value="' . esc_attr( $ao_ccss_debug ) . '">';
                            echo '<input class="hidden" name="autoptimize_ccss_noptimize" value="' . esc_attr( $ao_ccss_noptimize ) . '">';
                            echo '<input class="hidden" name="autoptimize_css_defer_inline" value="' . esc_attr( $ao_css_defer_inline ) . '">';
                            echo '<input class="hidden" name="autoptimize_ccss_loggedin" value="' . esc_attr( $ao_ccss_loggedin ) . '">';
                            echo '<input class="hidden" name="autoptimize_ccss_forcepath" value="' . esc_attr( $ao_ccss_forcepath ) . '">';
                            echo '<input class="hidden" name="autoptimize_ccss_domain" id="autoptimize_ccss_domain" value="' . esc_attr( $ao_ccss_domain ) . '">';
                        }
                        // Render key panel unconditionally.
                        ao_ccss_render_key( $ao_ccss_key, $key['status'], $key['stmsg'], $key['msg'], $key['color'] );
                        ?>
                        <p class="submit left">
                            <input type="submit" class="button-primary" value="<?php esc_html_e( 'Save Changes', 'autoptimize' ); ?>" />
                        </p>
                        <?php
                    }
                    ?>
                </form>
                <script>
                jQuery("form#settings").submit(function(){
                    var input = jQuery("#autoptimize_ccss_domain");
                    input.val(rot(input.val(), 13));
                });
                // rot JS from http://stackoverflow.com/a/617685/987044 .
                function rot(domainstring, itype) {
                    return domainstring.toString().replace(/[a-zA-Z]/g, function (letter) {
                        return String.fromCharCode((letter <= 'Z' ? 90 : 122) >= (letter = letter.charCodeAt(0) + itype) ? letter : letter   - 26);
                    });
                }
                </script>
                <form id="importSettingsForm"<?php if ( $this->is_multisite_network_admin() ) { echo ' class="hidden"'; } // @codingStandardsIgnoreLine ?>>
                    <span id="exportSettings" class="button-secondary"><?php esc_html_e( 'Export Settings', 'autoptimize' ); ?></span>
                    <input class="button-secondary" id="importSettings" type="button" value="<?php esc_html_e( 'Import Settings', 'autoptimize' ); ?>" onclick="upload();return false;" />
                    <input class="button-secondary" id="settingsfile" name="settingsfile" type="file" />
                </form>
                <div id="importdialog"></div>
            </div><!-- /#autoptimize_main -->
        </div><!-- /#wrap -->
        <?php
        if ( ! $this->is_multisite_network_admin() ) {
            // Include debug panel if debug mode is enable.
            if ( $ao_ccss_debug ) {
                ?>
                <div id="debug">
                    <?php
                    // Include debug panel.
                    if ( true === apply_filters( 'autoptimize_filter_ccss_debug_browser_output', true ) ) {
                        include( 'critcss-inc/admin_settings_debug.php' );
                    }
                    ?>
                </div><!-- /#debug -->
                <?php
            }
            echo '<script>';
            include( 'critcss-inc/admin_settings_rules.js.php' );
            include( 'critcss-inc/admin_settings_queue.js.php' );
            include( 'critcss-inc/admin_settings_impexp.js.php' );
            echo '</script>';
        }
    }

    public function ao_ccss_has_autorules() {
        static $_has_auto_rules = null;

        if ( null === $_has_auto_rules ) {
            $ao_ccss_rules = $this->criticalcss->get_option( 'rules' );
            $_has_auto_rules = false;
            if ( ! empty( $ao_ccss_rules ) ) {
                foreach ( array( 'types', 'paths' ) as $_typat ) {
                    foreach ( $ao_ccss_rules[ $_typat ] as $rule ) {
                        if ( ! empty( $rule['hash'] ) ) {
                            // we have at least one AUTO job, so all is fine.
                            $_has_auto_rules = true;
                            break;
                        }
                    }
                    if ( $_has_auto_rules ) {
                        break;
                    }
                }
            }
        }

        return $_has_auto_rules;
    }

    public static function is_multisite_network_admin() {
        static $_multisite_network_admin = null;

        if ( null === $_multisite_network_admin ) {
            if ( is_multisite() && is_network_admin() ) {
                $_multisite_network_admin = true;
            } else {
                $_multisite_network_admin = false;
            }
        }

        return $_multisite_network_admin;
    }
}
PK.�1\Ki����autoptimizeExtra.phpnu�[���<?php
/**
 * Handles autoptimizeExtra frontend features + admin options page
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeExtra
{
    /**
     * Options
     *
     * @var array
     */
    protected $options = array();

    /**
     * Singleton instance.
     *
     * @var self|null
     */
    protected static $instance = null;

    /**
     * Creates an instance and calls run().
     *
     * @param array $options Optional. Allows overriding options without having to specify them via admin options page.
     */
    public function __construct( $options = array() )
    {
        if ( empty( $options ) ) {
            $options = self::fetch_options();
        }

        $this->options = $options;
    }

    /**
     * Helper for getting a singleton instance. While being an
     * anti-pattern generally, it comes in handy for now from a
     * readability/maintainability perspective, until we get some
     * proper dependency injection going.
     *
     * @return self
     */
    public static function instance()
    {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    public function run()
    {
        if ( is_admin() ) {
            if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
                add_action( 'network_admin_menu', array( $this, 'admin_menu' ) );
            } else {
                add_action( 'admin_menu', array( $this, 'admin_menu' ) );
            }
            add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_extra_tab' ) );
        } else {
            add_action( 'wp', array( $this, 'run_on_frontend' ) );
        }
    }

    public function set_options( array $options )
    {
        $this->options = $options;

        return $this;
    }

    public static function fetch_options()
    {
        $value = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings' );
        if ( empty( $value ) ) {
            // Fallback to returning defaults when no stored option exists yet.
            $value = autoptimizeConfig::get_ao_extra_default_options();
        }

        return $value;
    }

    public function disable_emojis()
    {
        // Removing all actions related to emojis!
        remove_action( 'admin_print_styles', 'print_emoji_styles' );
        remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
        remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
        remove_action( 'wp_print_styles', 'print_emoji_styles' );
        remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
        remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
        remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );

        // Removes TinyMCE emojis.
        add_filter( 'tiny_mce_plugins', array( $this, 'filter_disable_emojis_tinymce' ) );

        // Removes emoji dns-preftech.
        add_filter( 'emoji_svg_url', '__return_false' );
    }

    public function filter_disable_emojis_tinymce( $plugins )
    {
        if ( is_array( $plugins ) ) {
            return array_diff( $plugins, array( 'wpemoji' ) );
        } else {
            return array();
        }
    }

    public function filter_remove_qs( $src )
    {
        if ( ! empty( $src ) ) {
            if ( strpos( $src, '?ver=' ) ) {
                $src = remove_query_arg( 'ver', $src );
            } elseif ( strpos( $src, '?v=' ) ) {
                $src = remove_query_arg( 'v', $src );
            }
        }

        return $src;
    }

    public function extra_async_js( $in )
    {
        $exclusions = array();
        if ( ! empty( $in ) ) {
            $exclusions = array_fill_keys( array_filter( array_map( 'trim', explode( ',', $in ) ) ), '' );
        }

        $settings = wp_strip_all_tags( $this->options['autoptimize_extra_text_field_3'] );
        $async    = array_fill_keys( array_filter( array_map( 'trim', explode( ',', $settings ) ) ), '' );
        $attr     = apply_filters( 'autoptimize_filter_extra_async', 'async' );
        foreach ( $async as $k => $v ) {
            $async[ $k ] = $attr;
        }

        // Merge exclusions & asyncs in one array and return to AO API.
        $merged = array_merge( $exclusions, $async );

        return $merged;
    }

    public function run_on_frontend()
    {
        // only run the Extra optimizations on frontend if general conditions
        // for optimizations are met, this to ensure e.g. removing querystrings
        // is not done when optimizing for logged in users is off, breaking
        // some pagebuilders (Divi & Elementor).
        if ( false === autoptimizeMain::should_buffer() ) {
            return;
        }

        $options = $this->options;

        // Disable emojis if specified.
        if ( ! empty( $options['autoptimize_extra_checkbox_field_1'] ) ) {
            $this->disable_emojis();
        }

        // Remove version query parameters.
        if ( ! empty( $options['autoptimize_extra_checkbox_field_0'] ) ) {
            add_filter( 'script_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
            add_filter( 'style_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
        }

        // Avoiding conflicts of interest when async-javascript plugin is active!
        $async_js_plugin_active = autoptimizeUtils::is_plugin_active( 'async-javascript/async-javascript.php' );
        if ( ! empty( $options['autoptimize_extra_text_field_3'] ) && ! $async_js_plugin_active ) {
            add_filter( 'autoptimize_filter_js_exclude', array( $this, 'extra_async_js' ), 10, 1 );
        }

        // Optimize google fonts!
        if ( ! empty( $options['autoptimize_extra_radio_field_4'] ) && ( '1' !== $options['autoptimize_extra_radio_field_4'] ) ) {
            add_filter( 'wp_resource_hints', array( $this, 'filter_remove_gfonts_dnsprefetch' ), 10, 2 );
            add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_optimize_google_fonts' ), 10, 1 );
            add_filter( 'autoptimize_extra_filter_tobepreconn', array( $this, 'filter_preconnect_google_fonts' ), 10, 1 );

            if ( '2' === $options['autoptimize_extra_radio_field_4'] ) {
                // remove Google Fonts, adding filters to also remove Google Fonts from 3rd party themes/ plugins.
                // inspired by https://wordpress.org/plugins/disable-remove-google-fonts/.
                remove_action( 'wp_footer', 'et_builder_print_font' ); // Divi.
                remove_action( 'wp_footer', array( 'RevSliderFront', 'load_google_fonts' ) ); // Revslider.
                add_filter( 'elementor/frontend/print_google_fonts', '__return_false' ); // Elementor.
                add_filter( 'fl_builder_google_fonts_pre_enqueue', '__return_empty_array' ); // Beaver Builder.
            }
        }

        // Preconnect!
        if ( ! empty( $options['autoptimize_extra_text_field_2'] ) || has_filter( 'autoptimize_extra_filter_tobepreconn' ) ) {
            add_filter( 'wp_resource_hints', array( $this, 'filter_preconnect' ), 10, 2 );
        }

        // Preload!
        if ( ! empty( $options['autoptimize_extra_text_field_7'] ) || has_filter( 'autoptimize_filter_extra_tobepreloaded' ) || ! empty( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) {
            add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_preload' ), 10, 2 );
        }

        // Remove global styles.
        if ( ! empty( $options['autoptimize_extra_checkbox_field_8'] ) ) {
            $this->disable_global_styles();
        }
    }

    public function filter_remove_gfonts_dnsprefetch( $urls, $relation_type )
    {
        return $this->filter_remove_dns_prefetch( $urls, $relation_type, 'fonts.googleapis.com' );
    }

    public function filter_remove_dns_prefetch( $urls, $relation_type, $url_to_remove )
    {
        $url_to_remove = (string) $url_to_remove;

        if ( ! empty( $url_to_remove ) && 'dns-prefetch' === $relation_type ) {
            $cnt = 0;
            foreach ( $urls as $url ) {
                if ( false !== strpos( $url, $url_to_remove ) ) {
                    unset( $urls[ $cnt ] );
                }
                $cnt++;
            }
        }

        return $urls;
    }

    public function filter_optimize_google_fonts( $in )
    {
        // Extract fonts, partly based on wp rocket's extraction code.
        $markup = preg_replace( '/<!--(.*)-->/Uis', '', $in );
        preg_match_all( '#<link(?:\s+(?:(?!href\s*=\s*)[^>])+)?(?:\s+href\s*=\s*([\'"])((?:https?:)?\/\/fonts\.googleapis\.com\/css(?:(?!\1).)+)\1)(?:\s+[^>]*)?>#iU', $markup, $matches );

        $fonts_collection = array();
        if ( ! $matches[2] ) {
            return $in;
        }

        // Store them in $fonts array.
        $i = 0;
        foreach ( $matches[2] as $font ) {
            if ( ! preg_match( '/rel=["\']dns-prefetch["\']/', $matches[0][ $i ] ) ) {
                // Get fonts name.
                $font = str_replace( array( '%7C', '%7c' ), '|', $font );
                if ( strpos( $font, 'fonts.googleapis.com/css2' ) !== false ) {
                    // (Somewhat) change Google Fonts APIv2 syntax back to v1.
                    // todo: support for 100..900
                    $font = rawurldecode( $font );
                    $font = str_replace( array( 'css2?', 'ital,wght@', 'wght@', 'ital@', '0,', '1,', ':1', ';', '&family=' ), array( 'css?', '', '', '', '', 'italic', ':italic', ',', '%7C' ), $font );
                }
                $font = explode( 'family=', $font );
                $font = ( isset( $font[1] ) ) ? explode( '&', $font[1] ) : array();
                // Add font to $fonts[$i] but make sure not to pollute with an empty family!
                $_thisfont = array_values( array_filter( explode( '|', reset( $font ) ) ) );
                if ( ! empty( $_thisfont ) ) {
                    $fonts_collection[ $i ]['fonts'] = $_thisfont;
                    // And add subset if any!
                    $subset = ( is_array( $font ) ) ? end( $font ) : '';
                    if ( false !== strpos( $subset, 'subset=' ) ) {
                        $subset                            = str_replace( array( '%2C', '%2c' ), ',', $subset );
                        $subset                            = explode( 'subset=', $subset );
                        $fonts_collection[ $i ]['subsets'] = explode( ',', $subset[1] );
                    }
                }
                // And remove Google Fonts.
                $in = str_replace( $matches[0][ $i ], '', $in );
            }
            $i++;
        }

        $options      = $this->options;
        $fonts_markup = '';
        if ( '2' === $options['autoptimize_extra_radio_field_4'] ) {
            // Remove Google Fonts.
            unset( $fonts_collection );
            return $in;
        } elseif ( '3' === $options['autoptimize_extra_radio_field_4'] || '5' === $options['autoptimize_extra_radio_field_4'] ) {
            // Aggregate & link!
            $fonts_string  = '';
            $subset_string = '';
            foreach ( $fonts_collection as $font ) {
                $fonts_string .= '|' . trim( implode( '|', $font['fonts'] ), '|' );
                if ( ! empty( $font['subsets'] ) ) {
                    $subset_string .= ',' . trim( implode( ',', $font['subsets'] ), ',' );
                }
            }

            if ( ! empty( $subset_string ) ) {
                $subset_string = str_replace( ',', '%2C', ltrim( $subset_string, ',' ) );
                $fonts_string  = $fonts_string . '&#038;subset=' . $subset_string;
            }

            $fonts_string = apply_filters( 'autoptimize_filter_extra_gfont_fontstring', str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) ) );
            // only add display parameter if there is none in $fonts_string (by virtue of the filter).
            if ( strpos( $fonts_string, 'display=' ) === false ) {
                $fonts_string .= apply_filters( 'autoptimize_filter_extra_gfont_display', '&amp;display=swap' );
            }

            if ( ! empty( $fonts_string ) ) {
                if ( '5' === $options['autoptimize_extra_radio_field_4'] ) {
                    $rel_string = 'rel="stylesheet" media="print" onload="' . autoptimizeConfig::get_ao_css_preload_onload() . '"';
                } else {
                    $rel_string = 'rel="stylesheet"';
                }
                $fonts_markup = '<link ' . $rel_string . ' id="ao_optimized_gfonts" href="https://fonts.googleapis.com/css?family=' . $fonts_string . '">';
            }
        } elseif ( '4' === $options['autoptimize_extra_radio_field_4'] ) {
            // Aggregate & load async (webfont.js impl.)!
            $fonts_array = array();
            foreach ( $fonts_collection as $_fonts ) {
                if ( ! empty( $_fonts['subsets'] ) ) {
                    $_subset = implode( ',', $_fonts['subsets'] );
                    foreach ( $_fonts['fonts'] as $key => $_one_font ) {
                        $_one_font               = $_one_font . ':' . $_subset;
                        $_fonts['fonts'][ $key ] = $_one_font;
                    }
                }
                $fonts_array = array_merge( $fonts_array, $_fonts['fonts'] );
            }

            $fonts_array = array_map( 'urldecode', $fonts_array );
            $fonts_array = array_map(
                function( $_f ) {
                    return trim( $_f, ',' );
                },
                $fonts_array
            );

            // type attrib on <script not added by default.
            $type_js = '';
            if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
                $type_js = 'type="text/javascript" ';
            }

            $fonts_markup         = '<script ' . $type_js . 'data-cfasync="false" id="ao_optimized_gfonts_config">WebFontConfig={google:{families:' . wp_json_encode( $fonts_array ) . ' },classes:false, events:false, timeout:1500};</script>';
            $fonts_library_markup = '<script ' . $type_js . 'data-cfasync="false" id="ao_optimized_gfonts_webfontloader">(function() {var wf = document.createElement(\'script\');wf.src=\'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js\';wf.type=\'text/javascript\';wf.async=\'true\';var s=document.getElementsByTagName(\'script\')[0];s.parentNode.insertBefore(wf, s);})();</script>';
            $in                   = substr_replace( $in, $fonts_library_markup . '</head>', strpos( $in, '</head>' ), strlen( '</head>' ) );
        }

        // Replace back in markup.
        $inject_point = apply_filters( 'autoptimize_filter_extra_gfont_injectpoint', '<link' );
        $out          = substr_replace( $in, $fonts_markup . $inject_point, strpos( $in, $inject_point ), strlen( $inject_point ) );
        unset( $fonts_collection );

        return $out;
    }

    public function filter_preconnect( $hints, $relation_type )
    {
        $options  = $this->options;
        $preconns = array();

        // Get settings and store in array.
        if ( array_key_exists( 'autoptimize_extra_text_field_2', $options ) ) {
            $preconns = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( $options['autoptimize_extra_text_field_2'] ) ) ) );
        }
        $preconns = apply_filters( 'autoptimize_extra_filter_tobepreconn', $preconns );

        // Walk array, extract domain and add to new array with crossorigin attribute.
        foreach ( $preconns as $preconn ) {
            $domain = '';
            $parsed = parse_url( $preconn );
            if ( is_array( $parsed ) && ! empty( $parsed['host'] ) && empty( $parsed['scheme'] ) ) {
                $domain = '//' . $parsed['host'];
            } elseif ( is_array( $parsed ) && ! empty( $parsed['host'] ) ) {
                $domain = $parsed['scheme'] . '://' . $parsed['host'];
            }

            if ( ! empty( $domain ) ) {
                $hint = array( 'href' => $domain );
                // Fonts don't get preconnected unless crossorigin flag is set, non-fonts don't get preconnected if origin flag is set
                // so hardcode fonts.gstatic.com to come with crossorigin and have filter to add other domains if needed.
                $crossorigins = apply_filters( 'autoptimize_extra_filter_preconn_crossorigin', array( 'https://fonts.gstatic.com' ) );
                if ( in_array( $domain, $crossorigins ) ) {
                    $hint['crossorigin'] = 'anonymous';
                }
                $new_hints[] = $hint;
            }
        }

        // Merge in WP's preconnect hints.
        if ( 'preconnect' === $relation_type && ! empty( $new_hints ) ) {
            $hints = array_merge( $hints, $new_hints );
        }

        return $hints;
    }

    public function filter_preconnect_google_fonts( $in )
    {
        if ( '2' !== $this->options['autoptimize_extra_radio_field_4'] ) {
            // Preconnect to fonts.gstatic.com unless we remove gfonts.
            $in[] = 'https://fonts.gstatic.com';
        }

        if ( '4' === $this->options['autoptimize_extra_radio_field_4'] ) {
            // Preconnect even more hosts for webfont.js!
            $in[] = 'https://ajax.googleapis.com';
            $in[] = 'https://fonts.googleapis.com';
        }

        return $in;
    }

    public function filter_preload( $in ) {
        // make array from comma separated list.
        $options  = $this->options;
        $preloads = array();
        if ( array_key_exists( 'autoptimize_extra_text_field_7', $options ) ) {
            $preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( $options['autoptimize_extra_text_field_7'] ) ) ) );
        }

        if ( false === autoptimizeImages::imgopt_active() && false === autoptimizeImages::should_lazyload_wrapper() ) {
            // only do this here if imgopt/ lazyload are not active?
            $metabox_preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );
            if ( ! empty( $metabox_preloads ) ) {
                $preloads = array_merge( $preloads, $metabox_preloads );
            }
        }

        $preloads = apply_filters( 'autoptimize_filter_extra_tobepreloaded', $preloads );

        // immediately return if nothing to be preloaded.
        if ( empty( $preloads ) ) {
            return $in;
        }

        // iterate through array and add preload link to tmp string.
        $preload_output = '';
        foreach ( $preloads as $preload ) {
            if ( filter_var( $preload, FILTER_VALIDATE_URL ) !== $preload ) {
                continue;
            }
            $preload     = esc_url_raw( $preload );
            $crossorigin = '';
            $preload_as  = '';
            $mime_type   = '';
            $_preload    = strtok( $preload, '?' );

            if ( autoptimizeUtils::str_ends_in( $_preload, '.css' ) ) {
                $preload_as = 'style';
            } elseif ( autoptimizeUtils::str_ends_in( $_preload, '.js' ) ) {
                $preload_as = 'script';
            } elseif ( autoptimizeUtils::str_ends_in( $_preload, '.woff' ) || autoptimizeUtils::str_ends_in( $_preload, '.woff2' ) || autoptimizeUtils::str_ends_in( $_preload, '.ttf' ) || autoptimizeUtils::str_ends_in( $_preload, '.eot' ) || autoptimizeUtils::str_ends_in( $_preload, '.otf' ) ) {
                $preload_as  = 'font';
                $crossorigin = ' crossorigin';
                $mime_type   = ' type="font/' . pathinfo( $_preload, PATHINFO_EXTENSION ) . '"';
                if ( ' type="font/eot"' === $mime_type ) {
                    $mime_type = 'application/vnd.ms-fontobject';
                }
            } elseif ( autoptimizeUtils::str_ends_in( $_preload, '.jpeg' ) || autoptimizeUtils::str_ends_in( $_preload, '.jpg' ) || autoptimizeUtils::str_ends_in( $_preload, '.webp' ) || autoptimizeUtils::str_ends_in( $_preload, '.png' ) || autoptimizeUtils::str_ends_in( $_preload, '.gif' ) || autoptimizeUtils::str_ends_in( $_preload, '.svg' ) ) {
                $preload_as = 'image';
            } else {
                $preload_as = 'other';
            }

            $preload_output .= '<link rel="preload" href="' . $preload . '" as="' . $preload_as . '"' . $mime_type . $crossorigin . '>';
        }
        $preload_output = apply_filters( 'autoptimize_filter_extra_preload_output', $preload_output );

        return $this->inject_preloads( $preload_output, $in );
    }

    public static function inject_preloads( $preloads, $html ) {
        // add string to head (before first link node by default).
        $preload_inject = apply_filters( 'autoptimize_filter_extra_preload_inject', '<link' );
        $position       = autoptimizeUtils::strpos( $html, $preload_inject );

        return autoptimizeUtils::substr_replace( $html, $preloads . $preload_inject, $position, strlen( $preload_inject ) );
    }

    public function disable_global_styles()
    {
        remove_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles' );
        remove_action( 'wp_footer', 'wp_enqueue_global_styles', 1 );
        remove_action( 'wp_body_open', 'wp_global_styles_render_svg_filters' );

        if ( true === apply_filters( 'autoptimize_filter_extra_global_styles_and_block_css', true ) ) {
            add_action(
                'wp_enqueue_scripts',
                function() {
                    wp_dequeue_style( 'wp-block-library' );
                    wp_dequeue_style( 'wp-block-library-theme' );
                }
            );
        }

        if ( true === apply_filters( 'autoptimize_filter_extra_remove_woocommerce_block_css', true ) ) {
            add_action(
                'wp_enqueue_scripts',
                function() {
                    wp_dequeue_style( 'wc-blocks-style' );
                }
            );
        }
    }

    public function admin_menu()
    {
        // no acces if multisite and not network admin and no site config allowed.
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
            add_submenu_page(
                '',
                'autoptimize_extra',
                'autoptimize_extra',
                'manage_options',
                'autoptimize_extra',
                array( $this, 'options_page' )
            );
        }
        register_setting( 'autoptimize_extra_settings', 'autoptimize_extra_settings' );
    }

    public function add_extra_tab( $in )
    {
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
            $in = array_merge( $in, array( 'autoptimize_extra' => esc_html__( 'Extra', 'autoptimize' ) ) );
        }

        return $in;
    }

    public function options_page()
    {
        // phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace

        // Working with actual option values from the database here.
        // That way any saves are still processed as expected, but we can still
        // override behavior by using `new autoptimizeExtra($custom_options)` and not have that custom
        // behavior being persisted in the DB even if save is done here.
        $options = $this->fetch_options();
        $gfonts  = $options['autoptimize_extra_radio_field_4'];
        ?>
    <style>
        #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
        #ao_settings_form .form-table th {font-weight: normal;}
        #autoptimize_extra_descr{font-size: 120%;}
    </style>
    <script>document.title = "Autoptimize: <?php esc_html_e( 'Extra', 'autoptimize' ); ?> " + document.title;</script>
    <div class="wrap">
    <h1><?php apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html_e( 'Autoptimize Pro Settings', 'autoptimize' ) : esc_html_e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
        <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
        <?php if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_js' ) && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_html' ) && ! autoptimizeImages::imgopt_active() ) { ?>
            <div class="notice-warning notice"><p>
            <?php esc_html_e( 'Most of below Extra optimizations require at least one of HTML, JS, CSS or Image autoptimizations being active.', 'autoptimize' ); ?>
            </p></div>
        <?php } ?>

    <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
        <?php settings_fields( 'autoptimize_extra_settings' ); ?>
        <h2><?php esc_html_e( 'Extra Auto-Optimizations', 'autoptimize' ); ?></h2>
        <span id='autoptimize_extra_descr'><?php esc_html_e( 'The following settings can improve your site\'s performance even more.', 'autoptimize' ); ?></span>
        <table class="form-table">
            <tr>
                <th scope="row"><?php esc_html_e( 'Google Fonts', 'autoptimize' ); ?></th>
                <td>
                    <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="1" <?php if ( ! in_array( $gfonts, array( 2, 3, 4, 5 ) ) ) { echo 'checked'; } ?> ><?php esc_html_e( 'Leave as is', 'autoptimize' ); ?><br/>
                    <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="2" <?php checked( 2, $gfonts, true ); ?> ><?php esc_html_e( 'Remove Google Fonts', 'autoptimize' ); ?><br/>
                    <?php // translators: "display:swap" should remain untranslated, will be shown in code tags. ?>
                    <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="3" <?php checked( 3, $gfonts, true ); ?> ><?php echo esc_html__( 'Combine and link in head (fonts load fast but are render-blocking)', 'autoptimize' ) . ', ' . sprintf( esc_html__( 'includes %1$sdisplay:swap%2$s.', 'autoptimize' ), '<code>', '</code>' ); ?><br/>
                    <?php // translators: "display:swap" should remain untranslated, will be shown in code tags. ?>
                    <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="5" <?php checked( 5, $gfonts, true ); ?> ><?php echo esc_html__( 'Combine and link deferred in head (fonts load late, but are not render-blocking)', 'autoptimize' ) . ', ' . sprintf( esc_html__( 'includes %1$sdisplay:swap%2$s.', 'autoptimize' ), '<code>', '</code>' ); ?>
                    <span <?php if ( '4' !== $gfonts ){ echo "style='display:none;' "; } ?> ><br/><input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="4" <?php checked( 4, $gfonts, true ); ?> ><?php echo sprintf( esc_html__( 'Combine and load fonts asynchronously with %1$swebfont.js%2$s', 'autoptimize' ), '<a href="https://github.com/typekit/webfontloader#readme" target="_blank">', '</a>' ) . ' ' . esc_html__( '(deprecated)', 'autoptimize' ); ?></span><br/>
                </td>
            </tr>
            <tr>
                <th scope="row"><?php esc_html_e( 'Remove emojis', 'autoptimize' ); ?></th>
                <td>
                    <label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_1'] ) && '1' === $options['autoptimize_extra_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php esc_html_e( 'Removes WordPress\' core emojis\' inline CSS, inline JavaScript, and an otherwise un-autoptimized JavaScript file.', 'autoptimize' ); ?></label>
                </td>
            </tr>
            <tr>
                <th scope="row"><?php esc_html_e( 'Remove query strings from static resources', 'autoptimize' ); ?></th>
                <td>
                    <label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_0]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_0'] ) && '1' === $options['autoptimize_extra_checkbox_field_0'] ) { echo 'checked="checked"'; } ?> value='1'>
                    <?php 
                    // translators: just a code tag around "ver" which is the parameter added to CSS/ JS URL's by wordpress.
                    printf( esc_html__( 'Removing query strings (or more specifically the %1$sver%2$s parameter) will not improve load time, but might improve performance scores.', 'autoptimize' ), '<code>', '</code>' );
                    ?>
                    </label>
                </td>
            </tr>
            <tr>
                <th scope="row"><?php esc_html_e( 'Remove WordPress block CSS', 'autoptimize' ); ?></th>
                <td>
                    <label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_8]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_8'] ) && '1' === $options['autoptimize_extra_checkbox_field_8'] ) { echo 'checked="checked"'; } ?> value='1'><?php esc_html_e( 'WordPress adds block CSS and global styles to improve easy styling of block-based sites, but which can add a significant amount of CSS and SVG. If you are sure your site can do without the block CSS and "global styles", you can disable them here.', 'autoptimize' ); ?></label>
                </td>
            </tr>
            <tr>
                <th scope="row"><?php esc_html_e( 'Preconnect to 3rd party domains (advanced users)', 'autoptimize' ); ?></th>
                <td>
                    <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_2]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_2', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_2'] ); } ?>'><br />
                    <?php
                    // Translators; link to a page on keycdn blog about preconnecting.
                    printf( esc_html__( 'Add 3rd party domains you want the browser to %1$spreconnect%2$s to, separated by comma\'s. Make sure to include the correct protocol (HTTP or HTTPS).', 'autoptimize' ), '<a href="https://www.keycdn.com/support/preconnect/#primary" target="_blank">', '</a>' );
                    ?>
                    </label>
                </td>
            </tr>
            <tr>
                <th scope="row"><?php esc_html_e( 'Preload specific requests (advanced users)', 'autoptimize' ); ?></th>
                <td>
                    <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_7]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_7', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_7'] ); } ?>'><br /><?php esc_html_e( 'Comma-separated list with full URL\'s of to to-be-preloaded resources. To be used sparingly!', 'autoptimize' ); ?></label>
                </td>
            </tr>
            <tr>
                <th scope="row"><?php esc_html_e( 'Async Javascript-files (advanced users)', 'autoptimize' ); ?></th>
                <td>
                    <?php
                    if ( autoptimizeUtils::is_plugin_active( 'async-javascript/async-javascript.php' ) ) {
                        // translators: link points Async Javascript settings page.
                        printf( esc_html__( 'You have "Async JavaScript" installed, %1$sconfiguration of async javascript is best done there%2$s.', 'autoptimize' ), '<a href="' . 'options-general.php?page=async-javascript' . '">', '</a>' );
                    } else {
                        ?>
                        <input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_3]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_3', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_3'] ); } ?>'>
                        <br />
                        <?php
                            printf( esc_html__( 'Comma-separated list of local or 3rd party JS-files that should loaded with the %1$sasync%2$s flag. JS-files from your own site will be automatically excluded if added here. ', 'autoptimize' ), '<code>', '</code>' );
                            // translators: %s will be replaced by a link to the "async javascript" plugin.
                            echo sprintf( esc_html__( 'Configuration of async javascript is easier and more flexible using the %s plugin.', 'autoptimize' ), '"<a href="https://wordpress.org/plugins/async-javascript" target="_blank">Async Javascript</a>"' );
                            $asj_install_url = network_admin_url() . 'plugin-install.php?s=async+javascript&tab=search&type=term';
                            echo sprintf( ' <a href="' . $asj_install_url . '">%s</a>', esc_html__( 'Click here to install and activate it.', 'autoptimize' ) );
                    }
                    ?>
                </td>
            </tr>
            <tr>
                <th scope="row"><?php esc_html_e( 'Optimize YouTube videos', 'autoptimize' ); ?></th>
                <td>
                    <?php
                    if ( autoptimizeUtils::is_plugin_active( 'wp-youtube-lyte/wp-youtube-lyte.php' ) ) {
                        esc_html_e( 'Great, you have WP YouTube Lyte installed.', 'autoptimize' );
                        $lyte_config_url = 'options-general.php?page=lyte_settings_page';
                        echo sprintf( ' <a href="' . $lyte_config_url . '">%s</a>', esc_html__( 'Click here to configure it.', 'autoptimize' ) );
                    } else {
                        // translators: %s will be replaced by a link to "wp youtube lyte" plugin.
                        echo sprintf( esc_html__( '%s allows you to “lazy load” your videos, by inserting responsive “Lite YouTube Embeds". ', 'autoptimize' ), '<a href="https://wordpress.org/plugins/wp-youtube-lyte" target="_blank">WP YouTube Lyte</a>' );
                        $lyte_install_url = network_admin_url() . 'plugin-install.php?s=lyte&tab=search&type=term';
                        echo sprintf( ' <a href="' . $lyte_install_url . '">%s</a>', esc_html__( 'Click here to install and activate it.', 'autoptimize' ) );
                    }
                    ?>
                </td>
            </tr>
        </table>
        <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php esc_html_e( 'Save Changes', 'autoptimize' ); ?>" /></p>
    </form>
        <?php
    }
}
PK.�1\�v�vautoptimizeCache.phpnu�[���<?php
/**
 * Handles disk-cache-related operations.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeCache
{
    /**
     * Cache filename.
     *
     * @var string
     */
    private $filename;

    /**
     * Cache directory path (with a trailing slash).
     *
     * @var string
     */
    private $cachedir;

    /**
     * Whether gzipping is done by the web server or us.
     * True => we don't gzip, the web server does it.
     * False => we do it ourselves.
     *
     * @var bool
     */
    private $nogzip;

    /**
     * Ctor.
     *
     * @param string $md5 Hash.
     * @param string $ext Extension.
     */
    public function __construct( $md5, $ext = 'php' )
    {
        $_min_ext = '';
        if ( apply_filters( 'autoptimize_filter_cache_url_add_min_ext', false ) ) {
            $_min_ext = '.min';
        }

        $this->cachedir = AUTOPTIMIZE_CACHE_DIR;
        $this->nogzip   = AUTOPTIMIZE_CACHE_NOGZIP;
        if ( ! $this->nogzip ) {
            $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . $_min_ext . '.php';
        } else {
            if ( in_array( $ext, array( 'js', 'css' ) ) ) {
                $this->filename = $ext . '/' . AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . $_min_ext . '.' . $ext;
            } else {
                $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . $_min_ext . '.' . $ext;
            }
        }
    }

    /**
     * Returns true if the cached file exists on disk.
     *
     * @return bool
     */
    public function check()
    {
        return file_exists( $this->cachedir . $this->filename );
    }

    /**
     * Returns cache contents if they exist, false otherwise.
     *
     * @return string|false
     */
    public function retrieve()
    {
        if ( $this->check() ) {
            if ( false == $this->nogzip ) {
                return file_get_contents( $this->cachedir . $this->filename . '.none' );
            } else {
                return file_get_contents( $this->cachedir . $this->filename );
            }
        }
        return false;
    }

    /**
     * Stores given $data in cache.
     *
     * @param string $data Data to cache.
     * @param string $mime Mimetype.
     *
     * @return void|bool
     */
    public function cache( $data, $mime )
    {
        // readonly FS explicitly OK'ed by developer, so just pretend all is OK.
        if ( defined( 'AUTOPTIMIZE_CACHE_READONLY' ) ) {
            return true;
        }

        // off by default; check if cachedirs exist every time before caching
        //
        // to be activated for users that experience these ugly errors;
        // PHP Warning: file_put_contents failed to open stream: No such file or directory.
        if ( apply_filters( 'autoptimize_filter_cache_checkdirs_on_write', false ) ) {
            $this->check_and_create_dirs();
        }

        if ( false === $this->nogzip ) {
            // We handle gzipping ourselves.
            $file    = 'default.php';
            $phpcode = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $file );
            $phpcode = str_replace( array( '%%CONTENT%%', 'exit;' ), array( $mime, '' ), $phpcode );

            file_put_contents( $this->cachedir . $this->filename, $phpcode );
            file_put_contents( $this->cachedir . $this->filename . '.none', $data );
        } else {
            // Write code to cache without doing anything else.
            file_put_contents( $this->cachedir . $this->filename, $data );

            // save fallback .js or .css file if filter true (to be false by default) but not if snippet or single.
            if ( self::do_fallback() && strpos( $this->filename, '_snippet_' ) === false && strpos( $this->filename, '_single_' ) === false ) {
                $_extension     = pathinfo( $this->filename, PATHINFO_EXTENSION );
                $_fallback_file = AUTOPTIMIZE_CACHEFILE_PREFIX . 'fallback.' . $_extension;
                if ( ( 'css' === $_extension || 'js' === $_extension ) && ! file_exists( $this->cachedir . $_extension . '/' . $_fallback_file ) ) {
                    file_put_contents( $this->cachedir . $_extension . '/' . $_fallback_file, $data );
                }
            }

            if ( apply_filters( 'autoptimize_filter_cache_create_static_gzip', false ) ) {
                // Create an additional cached gzip file.
                file_put_contents( $this->cachedir . $this->filename . '.gz', gzencode( $data, 9, FORCE_GZIP ) );
                // If PHP Brotli extension is installed, create an additional cached Brotli file.
                if ( function_exists( 'brotli_compress' ) ) {
                    file_put_contents( $this->cachedir . $this->filename . '.br', brotli_compress( $data, 11, BROTLI_GENERIC ) );
                }
            }
        }

        // Provide 3rd party action hook for every cache file that is created.
        // This hook can for example be used to inject a copy of the created cache file to a other domain.
        do_action( 'autoptimize_action_cache_file_created', $this->cachedir . $this->filename );
    }

    /**
     * Get cache filename.
     *
     * @return string
     */
    public function getname()
    {
        // NOTE: This could've maybe been a do_action() instead, however,
        // that ship has sailed.
        // The original idea here was to provide 3rd party code a hook so that
        // it can "listen" to all the complete autoptimized-urls that the page
        // will emit... Or something to that effect I think?
        apply_filters( 'autoptimize_filter_cache_getname', AUTOPTIMIZE_CACHE_URL . $this->filename );

        return $this->filename;
    }

    /**
     * Returns true if given `$file` is considered a valid Autoptimize cache file,
     * false otherwise.
     *
     * @param string $dir Directory name (with a trailing slash).
     * @param string $file Filename.
     * @return bool
     */
    protected static function is_valid_cache_file( $dir, $file )
    {
        if ( '.' !== $file && '..' !== $file &&
            false !== strpos( $file, AUTOPTIMIZE_CACHEFILE_PREFIX ) &&
            is_file( $dir . $file ) ) {

            // It's a valid file!
            return true;
        }

        // Everything else is considered invalid!
        return false;
    }

    /**
     * Clears contents of AUTOPTIMIZE_CACHE_DIR.
     *
     * @return void
     */
    protected static function clear_cache_classic()
    {
        $contents = self::get_cache_contents();
        foreach ( $contents as $name => $files ) {
            $dir = rtrim( AUTOPTIMIZE_CACHE_DIR . $name, '/' ) . '/';
            foreach ( $files as $file ) {
                if ( self::is_valid_cache_file( $dir, $file ) ) {
                    @unlink( $dir . $file ); // @codingStandardsIgnoreLine
                }
            }
        }

        @unlink( AUTOPTIMIZE_CACHE_DIR . '/.htaccess' ); // @codingStandardsIgnoreLine
    }

    /**
     * Recursively deletes the specified pathname (file/directory) if possible.
     * Returns true on success, false otherwise.
     *
     * @param string $pathname Pathname to remove.
     *
     * @return bool
     */
    protected static function rmdir( $pathname )
    {
        $files = self::get_dir_contents( $pathname );
        foreach ( $files as $file ) {
            $path = $pathname . '/' . $file;
            if ( is_dir( $path ) ) {
                self::rmdir( $path );
            } else {
                unlink( $path );
            }
        }

        return rmdir( $pathname );
    }

    /**
     * Clears contents of AUTOPTIMIZE_CACHE_DIR by renaming the current
     * cache directory into a new one with a unique name and then
     * re-creating the default (empty) cache directory.
     *
     * Important/ Fixme: this does not take multisite into account, so
     * if advanced_cache_clear_enabled is true (it is not by default)
     * then the content for all subsites is zapped!
     *
     * @return bool Returns true when everything is done successfully, false otherwise.
     */
    protected static function clear_cache_via_rename()
    {
        $ok       = false;
        $dir      = self::get_pathname_base();
        $new_name = self::get_unique_name();

        // Makes sure the new pathname is on the same level...
        $new_pathname = dirname( $dir ) . '/' . $new_name;
        $renamed      = @rename( $dir, $new_pathname ); // @codingStandardsIgnoreLine

        // When renamed, re-create the default cache directory back so it's
        // available again...
        if ( $renamed ) {
            $ok = self::cacheavail();
        }

        return $ok;
    }

    /**
     * Returns true when advanced cache clearing is enabled.
     *
     * @return bool
     */
    public static function advanced_cache_clear_enabled()
    {
        return apply_filters( 'autoptimize_filter_cache_clear_advanced', false );
    }

    /**
     * Returns a (hopefully) unique new cache folder name for renaming purposes.
     *
     * @return string
     */
    protected static function get_unique_name()
    {
        $prefix   = self::get_advanced_cache_clear_prefix();
        $new_name = uniqid( $prefix, true );

        return $new_name;
    }

    /**
     * Get cache prefix name used in advanced cache clearing mode.
     *
     * @return string
     */
    protected static function get_advanced_cache_clear_prefix()
    {
        $pathname = self::get_pathname_base();
        $basename = basename( $pathname );
        $prefix   = $basename . '-artifact-';

        return $prefix;
    }

    /**
     * Returns an array of file and directory names found within
     * the given $pathname without '.' and '..' elements.
     *
     * @param string $pathname Pathname.
     *
     * @return array
     */
    protected static function get_dir_contents( $pathname )
    {
        return array_slice( scandir( $pathname ), 2 );
    }

    /**
     * Wipes directories which were created as part of the fast cache clearing
     * routine (which renames the current cache directory into a new one with
     * a custom-prefixed unique name).
     *
     * @return bool
     */
    public static function delete_advanced_cache_clear_artifacts()
    {
        // Don't go through these motions (called from the cachechecker) if advanced cache clear isn't even active.
        if ( ! self::advanced_cache_clear_enabled() ) {
            return false;
        }

        $dir    = self::get_pathname_base();
        $prefix = self::get_advanced_cache_clear_prefix();
        $parent = dirname( $dir );
        $ok     = false;

        // Returns the list of files without '.' and '..' elements.
        $files = self::get_dir_contents( $parent );
        if ( is_array( $files ) && ! empty( $files ) ) {
            foreach ( $files as $file ) {
                $path     = $parent . '/' . $file;
                $prefixed = ( false !== strpos( $path, $prefix ) );
                // Removing only our own (prefixed) directories...
                if ( is_dir( $path ) && $prefixed ) {
                    $ok = self::rmdir( $path );
                }
            }
        }

        return $ok;
    }

    /**
     * Returns the cache directory pathname used.
     * Done as a function so we canSlightly different
     * if multisite is used and `autoptimize_separate_blog_caches` filter
     * is used.
     *
     * @return string
     */
    public static function get_pathname()
    {
        $pathname = self::get_pathname_base();

        if ( is_multisite() && apply_filters( 'autoptimize_separate_blog_caches', true ) ) {
            $blog_id   = get_current_blog_id();
            $pathname .= $blog_id . '/';
        }

        return $pathname;
    }

    /**
     * Returns the base path of our cache directory.
     *
     * @return string
     */
    protected static function get_pathname_base()
    {
        $pathname = WP_CONTENT_DIR . AUTOPTIMIZE_CACHE_CHILD_DIR;

        return $pathname;
    }

    /**
     * Deletes everything from the cache directories.
     *
     * @param bool $propagate Whether to trigger additional actions when cache is purged.
     *
     * @return bool
     */
    public static function clearall( $propagate = true )
    {
        if ( defined( 'ET_CORE_VERSION' ) && 'Divi' === get_template() ) {
            // see https://blog.futtta.be/2018/11/17/warning-divi-purging-autoptimizes-cache/ .
            $dbt    = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
            $caller = isset( $dbt[1]['function'] ) ? $dbt[1]['function'] : null;
            if ( 'et_core_clear_wp_cache' === $caller ) {
                if ( apply_filters( 'autoptimize_filter_cache_divi_wrong_complain', true ) ) {
                    _doing_it_wrong( 'autoptimizeCache::clearall', 'Divi devs: please don\'t clear Autoptimize\'s cache, it is unneeded and can break sites. You can contact me at futtta@gmail.com to discuss.', 'Autoptimize 2.9.6' );
                }
                return false;
            }
        }

        if ( ! self::cacheavail() || true === apply_filters( 'autoptimize_filter_cache_clearall_disabled', false ) ) {
            return false;
        }

        // TODO/FIXME: If cache is big, switch to advanced/new cache clearing automatically?
        if ( self::advanced_cache_clear_enabled() ) {
            self::clear_cache_via_rename();
        } else {
            self::clear_cache_classic();
        }

        // Remove 404 handler if required.
        if ( self::do_fallback() ) {
            $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php';
            @unlink( $_fallback_php ); // @codingStandardsIgnoreLine
        }

        // Remove the transient so it gets regenerated...
        delete_transient( 'autoptimize_stats' );

        // Cache was just purged, clear page cache and allow others to hook into our purging...
        if ( true === $propagate ) {
            if ( ! function_exists( 'autoptimize_do_cachepurged_action' ) ) {
                function autoptimize_do_cachepurged_action() {
                    do_action( 'autoptimize_action_cachepurged' );
                }
            }
            add_action( 'shutdown', 'autoptimize_do_cachepurged_action', 11 );
            add_action( 'autoptimize_action_cachepurged', array( 'autoptimizeCache', 'flushPageCache' ), 10, 0 );
        }

        // Warm cache (part of speedupper)!
        if ( apply_filters( 'autoptimize_filter_speedupper', true ) && false == get_transient( 'autoptimize_cache_warmer_protector' ) ) {
            set_transient( 'autoptimize_cache_warmer_protector', 'I shall not warm cache for another 10 minutes.', 60 * 10 );
            $url   = site_url() . '/?ao_speedup_cachebuster=' . rand( 1, 100000 );
            $url   = apply_filters( 'autoptimize_filter_cache_warmer_url', $url );
            $cache = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
            unset( $cache );
        }

        return true;
    }

    /**
     * Wrapper for clearall but with false param
     * to ensure the event is not propagated to others
     * through our own hooks (to avoid infinite loops).
     *
     * @return bool
     */
    public static function clearall_actionless()
    {
        return self::clearall( false );
    }

    /**
     * Returns the contents of our cache dirs.
     *
     * @return array
     */
    protected static function get_cache_contents()
    {
        $contents = array();

        foreach ( array( '', 'js', 'css' ) as $dir ) {
            $contents[ $dir ] = scandir( AUTOPTIMIZE_CACHE_DIR . $dir );
        }

        return $contents;
    }

    /**
     * Returns stats about cached contents.
     *
     * @return array
     */
    public static function stats()
    {
        $stats = get_transient( 'autoptimize_stats' );

        // If no transient, do the actual scan!
        if ( ! is_array( $stats ) ) {
            if ( ! self::cacheavail() ) {
                return 0;
            }
            $stats = self::stats_scan();
            $count = $stats[0];
            if ( $count > 100 ) {
                // Store results in transient.
                set_transient(
                    'autoptimize_stats',
                    $stats,
                    apply_filters( 'autoptimize_filter_cache_statsexpiry', HOUR_IN_SECONDS )
                );
            }
        }

        return $stats;
    }

    /**
     * Performs a scan of cache directory contents and returns an array
     * with 3 values: count, size, timestamp.
     * count = total number of found files
     * size = total filesize (in bytes) of found files
     * timestamp = unix timestamp when the scan was last performed/finished.
     *
     * @return array
     */
    protected static function stats_scan()
    {
        $count = 0;
        $size  = 0;

        // Scan everything in our cache directories.
        foreach ( self::get_cache_contents() as $name => $files ) {
            $dir = rtrim( AUTOPTIMIZE_CACHE_DIR . $name, '/' ) . '/';
            foreach ( $files as $file ) {
                if ( self::is_valid_cache_file( $dir, $file ) ) {
                    if ( AUTOPTIMIZE_CACHE_NOGZIP &&
                        (
                            false !== strpos( $file, '.js' ) ||
                            false !== strpos( $file, '.css' ) ||
                            false !== strpos( $file, '.img' ) ||
                            false !== strpos( $file, '.txt' )
                        )
                    ) {
                        // Web server is gzipping, we count .js|.css|.img|.txt files.
                        $count++;
                    } elseif ( ! AUTOPTIMIZE_CACHE_NOGZIP && false !== strpos( $file, '.none' ) ) {
                        // We are gzipping ourselves via php, counting only .none files.
                        $count++;
                    }
                    $size += filesize( $dir . $file );
                }
            }
        }

        $stats = array( $count, $size, time() );

        return $stats;
    }

    /**
     * Ensures the cache directory exists, is writeable and contains the
     * required .htaccess files.
     * Returns false in case it fails to ensure any of those things.
     *
     * @return bool
     */
    public static function cacheavail()
    {
        // readonly FS explicitly OK'ed by dev, let's assume the cache dirs are there!
        if ( defined( 'AUTOPTIMIZE_CACHE_READONLY' ) ) {
            return true;
        }

        if ( false === autoptimizeCache::check_and_create_dirs() ) {
            return false;
        }

        // Using .htaccess inside our cache folder to overrule wp-super-cache.
        $htaccess = AUTOPTIMIZE_CACHE_DIR . '/.htaccess';
        if ( ! is_file( $htaccess ) ) {
            /**
             * Create `wp-content/AO_htaccess_tmpl` file with
             * whatever htaccess rules you might need
             * if you want to override default AO htaccess
             */
            $htaccess_tmpl = WP_CONTENT_DIR . '/AO_htaccess_tmpl';
            if ( is_file( $htaccess_tmpl ) ) {
                $content = file_get_contents( $htaccess_tmpl );
            } elseif ( is_multisite() || ! AUTOPTIMIZE_CACHE_NOGZIP ) {
                $content = '<IfModule mod_expires.c>
        ExpiresActive On
        ExpiresByType text/css A30672000
        ExpiresByType text/javascript A30672000
        ExpiresByType application/javascript A30672000
</IfModule>
<IfModule mod_headers.c>
    Header append Cache-Control "public, immutable"
</IfModule>
<IfModule mod_deflate.c>
        <FilesMatch "\.(js|css)$">
        SetOutputFilter DEFLATE
    </FilesMatch>
</IfModule>
<IfModule mod_authz_core.c>
    <Files *.php>
        Require all granted
    </Files>
</IfModule>
<IfModule !mod_authz_core.c>
    <Files *.php>
        Order allow,deny
        Allow from all
    </Files>
</IfModule>';
            } else {
                $content = '<IfModule mod_expires.c>
        ExpiresActive On
        ExpiresByType text/css A30672000
        ExpiresByType text/javascript A30672000
        ExpiresByType application/javascript A30672000
</IfModule>
<IfModule mod_headers.c>
    Header append Cache-Control "public, immutable"
</IfModule>
<IfModule mod_deflate.c>
    <FilesMatch "\.(js|css)$">
        SetOutputFilter DEFLATE
    </FilesMatch>
</IfModule>
<IfModule mod_authz_core.c>
    <Files *.php>
        Require all denied
    </Files>
</IfModule>
<IfModule !mod_authz_core.c>
    <Files *.php>
        Order deny,allow
        Deny from all
    </Files>
</IfModule>';
            }

            if ( self::do_fallback() === true ) {
                $content .= "\nErrorDocument 404 " . trailingslashit( parse_url( content_url(), PHP_URL_PATH ) ) . 'autoptimize_404_handler.php';
            }
            @file_put_contents( $htaccess, $content ); // @codingStandardsIgnoreLine
        }

        if ( self::do_fallback() ) {
            self::check_fallback_php();
        }

        // All OK!
        return true;
    }

    /**
     * Checks if fallback-php file exists and create it if not.
     *
     * Return bool
     */
    public static function check_fallback_php() {
        $_fallback_filename = 'autoptimize_404_handler.php';
        $_fallback_php      = trailingslashit( WP_CONTENT_DIR ) . $_fallback_filename;
        $_fallback_status   = true;

        if ( ! file_exists( $_fallback_php ) && is_writable( WP_CONTENT_DIR ) ) {
            $_fallback_php_contents = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $_fallback_filename );
            $_fallback_php_contents = str_replace( '<?php exit;', '<?php', $_fallback_php_contents );
            $_fallback_php_contents = str_replace( '<!--ao-cache-dir-->', AUTOPTIMIZE_CACHE_DIR, $_fallback_php_contents );
            $_fallback_php_contents = str_replace( '<!--ao-cachefile-prefix-->', AUTOPTIMIZE_CACHEFILE_PREFIX, $_fallback_php_contents );
            if ( is_multisite() ) {
                $_fallback_php_contents = str_replace( '$multisite = false;', '$multisite = true;', $_fallback_php_contents );
            }
            if ( apply_filters( 'autoptimize_filter_cache_fallback_log_errors', false ) ) {
                $_fallback_php_contents = str_replace( '// error_log', 'error_log', $_fallback_php_contents );
            }
            $_fallback_status = file_put_contents( $_fallback_php, $_fallback_php_contents );
        }

        return $_fallback_status;
    }

    /**
     * Tells if AO should try to avoid 404's by creating fallback filesize
     * and create a php 404 handler and tell .htaccess to redirect to said handler
     * and hook into WordPress to redirect 404 to said handler as well. NGINX users
     * are smart enough to get this working, no? ;-)
     *
     * Return bool
     */
    public static function do_fallback() {
        static $_do_fallback = null;

        if ( null === $_do_fallback ) {
            $_do_fallback = (bool) apply_filters( 'autoptimize_filter_cache_do_fallback', autoptimizeOptionWrapper::get_option( 'autoptimize_cache_fallback', '1' ) );
        }

        return $_do_fallback;
    }

    /**
     * Hooks into template_redirect, will act on 404-ing requests for
     * Autoptimized files and redirects to the fallback CSS/ JS if available
     * and 410'ing ("Gone") if fallback not available.
     */
    public static function wordpress_notfound_fallback() {
        $original_request = strtok( $_SERVER['REQUEST_URI'], '?' );
        if ( strpos( $original_request, wp_basename( WP_CONTENT_DIR ) . AUTOPTIMIZE_CACHE_CHILD_DIR ) !== false && is_404() ) {
            // make sure this is not considered a 404.
            global $wp_query;
            $wp_query->is_404 = false;

            // set fallback path.
            $js_or_css     = pathinfo( $original_request, PATHINFO_EXTENSION );
            $fallback_path = AUTOPTIMIZE_CACHE_DIR . $js_or_css . '/autoptimize_fallback.' . $js_or_css;

            // prepare for Shakeeb's Unused CSS files to be 404-handled as well.
            if ( strpos( $original_request, 'uucss/uucss-' ) !== false ) {
                $original_request = preg_replace( '/uucss\/uucss-[a-z0-9]{32}-/', 'css/', $original_request );
            }

            // set fallback URL.
            $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );

            // redirect to fallback if possible.
            if ( $original_request !== $fallback_target && file_exists( $fallback_path ) ) {
                // redirect to fallback.
                wp_redirect( $fallback_target, 302 );
            } else {
                // return HTTP 410 (gone) reponse.
                status_header( 410 );
            }
        }
    }

    /**
     * Checks if cache dirs exist and create if not.
     * Returns false if not succesful.
     *
     * @return bool
     */
    public static function check_and_create_dirs() {
        if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
            // We didn't set a cache.
            return false;
        }

        foreach ( array( '', 'js', 'css' ) as $dir ) {
            if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
                return false;
            }
        }
        return true;
    }

    /**
     * Ensures the specified `$dir` exists and is writeable.
     * Returns false if that's not the case.
     *
     * @param string $dir Directory to check/create.
     *
     * @return bool
     */
    protected static function check_cache_dir( $dir )
    {
        // Try creating the dir if it doesn't exist.
        if ( ! file_exists( $dir ) ) {
            @mkdir( $dir, 0775, true ); // @codingStandardsIgnoreLine
            if ( ! file_exists( $dir ) ) {
                return false;
            }
        }

        // If we still cannot write, bail.
        if ( ! is_writable( $dir ) ) {
            return false;
        }

        // Create an index.html in there to avoid prying eyes!
        $idx_file = rtrim( $dir, '/\\' ) . '/index.html';
        if ( ! is_file( $idx_file ) ) {
            @file_put_contents( $idx_file, '<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>' ); // @codingStandardsIgnoreLine
        }

        return true;
    }

    /**
     * Flushes as many page cache plugin's caches as possible.
     *
     * @return void
     */
    // @codingStandardsIgnoreStart
    public static function flushPageCache()
    {
        if ( function_exists( 'wp_cache_clear_cache' ) ) {
            if ( is_multisite() ) {
                $blog_id = get_current_blog_id();
                wp_cache_clear_cache( $blog_id );
            } else {
                wp_cache_clear_cache();
            }
        } elseif ( has_action( 'cachify_flush_cache' ) ) {
            do_action( 'cachify_flush_cache' );
        } elseif ( function_exists( 'w3tc_pgcache_flush' ) ) {
            w3tc_pgcache_flush();
        } elseif ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
            wp_fast_cache_bulk_delete_all();
        } elseif ( function_exists( 'rapidcache_clear_cache' ) ) {
            rapidcache_clear_cache();
        } elseif ( class_exists( 'Swift_Performance_Cache' ) ) {
            Swift_Performance_Cache::clear_all_cache();
        } elseif ( class_exists( 'WpFastestCache' ) ) {
            $wpfc = new WpFastestCache();
            $wpfc->deleteCache();
        } elseif ( class_exists( 'c_ws_plugin__qcache_purging_routines' ) ) {
            c_ws_plugin__qcache_purging_routines::purge_cache_dir(); // quick cache
        } elseif ( class_exists( 'zencache' ) ) {
            zencache::clear();
        } elseif ( class_exists( 'comet_cache' ) ) {
            comet_cache::clear();
        } elseif ( class_exists( 'WpeCommon' ) ) {
            // WPEngine cache purge/flush methods to call by default
            $wpe_methods = array(
                'purge_varnish_cache',
            );

            // More agressive clear/flush/purge behind a filter
            if ( apply_filters( 'autoptimize_flush_wpengine_aggressive', false ) ) {
                $wpe_methods = array_merge( $wpe_methods, array( 'purge_memcached', 'clear_maxcdn_cache' ) );
            }

            // Filtering the entire list of WpeCommon methods to be called (for advanced usage + easier testing)
            $wpe_methods = apply_filters( 'autoptimize_flush_wpengine_methods', $wpe_methods );

            foreach ( $wpe_methods as $wpe_method ) {
                if ( method_exists( 'WpeCommon', $wpe_method ) ) {
                    WpeCommon::$wpe_method();
                }
            }
        } elseif ( function_exists( 'sg_cachepress_purge_cache' ) ) {
            sg_cachepress_purge_cache();
        } elseif ( array_key_exists( 'KINSTA_CACHE_ZONE', $_SERVER ) ) {
            $_kinsta_clear_cache_url = 'https://localhost/kinsta-clear-cache-all';
            $_kinsta_response        = wp_remote_get(
                $_kinsta_clear_cache_url,
                array(
                    'sslverify' => false,
                    'timeout' => 5,
                    )
            );
        } elseif ( class_exists( 'RaidboxesNginxCacheFunctions' ) ) {
            $rb_cache_helper = new RaidboxesNginxCacheFunctions();
            $rb_cache_helper->purge_cache();
        } elseif ( defined('NGINX_HELPER_BASENAME') ) {
            do_action( 'rt_nginx_helper_purge_all' );
        } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
            // fallback for WP-Super-Cache
            global $cache_path;
            if ( is_multisite() ) {
                $blog_id = get_current_blog_id();
                prune_super_cache( get_supercache_dir( $blog_id ), true );
                prune_super_cache( $cache_path . 'blogs/', true );
            } else {
                prune_super_cache( $cache_path . 'supercache/', true );
                prune_super_cache( $cache_path, true );
            }
        } elseif ( class_exists( 'NginxCache' ) ) {
            $nginx_cache = new NginxCache();
            $nginx_cache->purge_zone_once();
        }
    }
    // @codingStandardsIgnoreEnd
}
PK.�1\@9����autoptimizeExitSurvey.phpnu�[���<?php
/**
 * Add exit-survey logic to plugins-page.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class autoptimizeExitSurvey
{
    function __construct() {
        global $pagenow;

        if ( 'plugins.php' != $pagenow ) {
            return;
        }

        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_survey_scripts' ) );
        add_action( 'admin_footer', array( $this, 'render_survey_model' ) );
    }

    function enqueue_survey_scripts() {
        wp_enqueue_script( 'ao_exit_survey', plugins_url( '/static/exit-survey/exit-survey.js', __FILE__ ), array( 'jquery' ), AUTOPTIMIZE_PLUGIN_VERSION, true );
        wp_enqueue_style( 'ao_exit_survey', plugins_url( '/static/exit-survey/exit-survey.css', __FILE__ ), null, AUTOPTIMIZE_PLUGIN_VERSION );
    }

    function render_survey_model() {
        global $wp_version;

        $data = array(
            'home' => home_url(),
            'dest' => 'aHR0cHM6Ly9taXNjLm9wdGltaXppbmdtYXR0ZXJzLmNvbS9hb19leGl0X3N1cnZleS9pbmRleC5waHA=',
        );
        ?>

        <div class="ao-plugin-uninstall-feedback-popup ao-feedback" id="ao_uninstall_feedback_popup" data-modal="<?php echo base64_encode( json_encode( $data ) ); ?>">
            <div class="popup--header">
                <h5><?php esc_html_e( 'Sorry to see you go!', 'autoptimize' ); ?></h5>
            </div><!--/.popup--header-->
            <div class="popup--body">
                <p><strong><?php esc_html_e( 'We would appreciate if you let us know why you\'re deactivating Autoptimize!', 'autoptimize' ); ?></strong></p>
                <ul class="popup--form">
                    <li ao-option-id="5">
                        <input type="radio" name="ao-deactivate-option" id="ao_feedback5">
                        <label for="ao_feedback5">
                            <?php esc_html_e( 'I don\'t see a performance improvement.', 'autoptimize' ); ?>
                        </label>
                        <p class="last-attempt"><?php _e( 'As Autoptimize does not do page caching, you might have to install e.g. KeyCDN Cache Enabler or WP Super Cache as well. Feel free to create a topic on <a href="https://wordpress.org/support/plugin/autoptimize/#new-topic-0" target="_blank">the support forum here</a> to get pointers on how get the most out of Autoptimize!', 'autoptimize' ); ?></p>
                    </li>
                    <li ao-option-id="6">
                        <input type="radio" name="ao-deactivate-option" id="ao_feedback6">
                        <label for="ao_feedback6" data-reason="broke site">
                            <?php esc_html_e( 'It broke my site.', 'autoptimize' ); ?>
                        </label>
                        <p class="last-attempt"><?php _e( 'Ouch, sorry about that! But almost all problems can be fixed with the right configuration, have a look at <a href="https://blog.futtta.be/2022/05/05/what-to-do-when-autoptimize-breaks-your-site/" target="_blank">this short troubleshooting howto</a> or create a topic on <a href="https://wordpress.org/support/plugin/autoptimize/#new-topic-0" target="_blank">the support forum here</a>!', 'autoptimize' ); ?></p>
                    <li ao-option-id="4">
                        <input type="radio" name="ao-deactivate-option" id="ao_feedback4">
                        <label for="ao_feedback4" data-reason="found better">
                            <?php esc_html_e( 'I found a better solution.', 'autoptimize' ); ?>
                        </label>
                    <li ao-option-id="3">
                        <input type="radio" name="ao-deactivate-option" id="ao_feedback3">
                        <label for="ao_feedback3" data-reason="just temporarily">
                            <?php esc_html_e( 'I\'m just disabling temporarily.', 'autoptimize' ); ?>
                        </label>
                    <li ao-option-id="999">
                        <input type="radio" name="ao-deactivate-option" id="ao_feedback999">
                        <label for="ao_feedback999" data-reason="other">
                            <?php esc_html_e( 'Other (please specify below)', 'autoptimize' ); ?>
                        </label>
                        <textarea width="100%" rows="2" name="comments" placeholder="What can we do better?"></textarea></li>
                    <hr />
                    <li ao-option-id="998">
                        <label for="ao_feedback_email_toggle" data-reason="other detail">
                            <input type="checkbox" id="ao_feedback_email_toggle" name="ao_feedback_email_toggle" />
                            <?php esc_html_e( 'I would like be contacted about my experience with Autoptimize.', 'autoptimize' ); ?>
                        </label>
                        <input type="email" name="ao-deactivate-option" id="ao_feedback998" placeholder="mymail@domain.xyz" class="hidden">
                    </li>
                </ul>
            </div><!--/.popup--body-->
            <div class="popup--footer">
                <div class="actions">
                    <a href="#" class="info-disclosure-link"><?php esc_html_e( 'What info do we collect?', 'autoptimize' ); ?></a>
                    <div class="info-disclosure-content"><p><?php esc_html_e( 'Below is a detailed view of all data that Optimizing Matters will receive if you fill in this survey. Your email address is only shared if you explicitly fill it in, your IP addres is never sent.', 'autoptimize' ); ?></p>
                        <ul>
                            <li><strong><?php esc_html_e( 'Plugin version', 'autoptimize' ); ?> </strong> <code id="ao_plugin_version"> <?php echo AUTOPTIMIZE_PLUGIN_VERSION; ?> </code></li>
                            <li><strong><?php esc_html_e( 'WordPress version', 'autoptimize' ); ?> </strong> <code id="core_version"> <?php echo $wp_version; ?> </code></li>
                            <li><strong><?php esc_html_e( 'Current website:', 'autoptimize' ); ?></strong> <code> <?php echo trailingslashit( get_site_url() ); ?> </code></li>
                            <li><strong><?php esc_html_e( 'Uninstall reason', 'autoptimize' ); ?> </strong> <i> <?php esc_html_e( 'Selected reason from the above survey', 'autoptimize' ); ?> </i></li>
                        </ul>
                    </div>
                    <div class="buttons">
                        <input type="submit"
                               name="ao-deactivate-no"
                               id="ao-deactivate-no"
                               class="button"
                               value="Just Deactivate">
                        <input type="submit"
                               name="ao-deactivate-cancel"
                               id="ao-deactivate-cancel"
                               class="button"
                               value="Cancel">
                        <input type="submit"
                               name="ao-deactivate-yes"
                               id="ao-deactivate-yes"
                               class="button button-primary"
                               value="Submit &amp; Deactivate"
                               data-after-text="Submit &amp; Deactivate"
                               disabled="1"></div>

                </div><!--/.actions-->
            </div><!--/.popup--footer-->
        </div>
        <?php
    }
}
PKg2\�E_^^ class-twentytwenty-svg-icons.phpnu&1i�<?php
/**
 * Custom icons for this theme.
 *
 * @package WordPress
 * @subpackage Twenty_Twenty
 * @since Twenty Twenty 1.0
 */

if ( ! class_exists( 'TwentyTwenty_SVG_Icons' ) ) {
	/**
	 * SVG ICONS CLASS
	 * Retrieve the SVG code for the specified icon. Based on a solution in Twenty Nineteen.
	 *
	 * @since Twenty Twenty 1.0
	 */
	class TwentyTwenty_SVG_Icons {
		/**
		 * GET SVG CODE
		 * Get the SVG code for the specified icon
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @param string $icon  Icon name.
		 * @param string $group Icon group.
		 * @param string $color Color.
		 */
		public static function get_svg( $icon, $group = 'ui', $color = '#1A1A1B' ) {
			if ( 'ui' === $group ) {
				$arr = self::$ui_icons;
			} elseif ( 'social' === $group ) {
				$arr = self::$social_icons;
			} else {
				$arr = array();
			}

			/**
			 * Filters Twenty Twenty's array of icons.
			 *
			 * The dynamic portion of the hook name, `$group`, refers to
			 * the name of the group of icons, either "ui" or "social".
			 *
			 * @since Twenty Twenty 1.5
			 *
			 * @param array $arr Array of icons.
			 */
			$arr = apply_filters( "twentytwenty_svg_icons_{$group}", $arr );

			/**
			 * Filters an SVG icon's color.
			 *
			 * @since Twenty Twenty 1.5
			 *
			 * @param string $color The icon color.
			 * @param string $icon  The icon name.
			 * @param string $group The icon group.
			 */
			$color = apply_filters( 'twentytwenty_svg_icon_color', $color, $icon, $group );

			if ( array_key_exists( $icon, $arr ) ) {
				$repl = '<svg class="svg-icon" aria-hidden="true" role="img" focusable="false" ';
				$svg  = preg_replace( '/^<svg /', $repl, trim( $arr[ $icon ] ) ); // Add extra attributes to SVG code.
				$svg  = str_replace( '#1A1A1B', $color, $svg );   // Replace the color.
				$svg  = str_replace( '#', '%23', $svg );          // Urlencode hashes.
				$svg  = preg_replace( "/([\n\t]+)/", ' ', $svg ); // Remove newlines & tabs.
				$svg  = preg_replace( '/>\s*</', '><', $svg );    // Remove whitespace between SVG tags.
				return $svg;
			}
			return null;
		}

		/**
		 * GET SOCIAL LINK SVG
		 * Detects the social network from a URL and returns the SVG code for its icon.
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @param string $uri The URL to retrieve SVG for.
		 */
		public static function get_social_link_svg( $uri ) {
			static $regex_map; // Only compute regex map once, for performance.
			if ( ! isset( $regex_map ) ) {
				$regex_map = array();

				/**
				 * Filters Twenty Twenty's array of domain mappings for social icons.
				 *
				 * By default, each Icon ID is matched against a .com TLD. To override this behavior,
				 * specify all the domains it covers (including the .com TLD too, if applicable).
				 *
				 * @since Twenty Twenty 1.5
				 *
				 * @param array $social_icons_map Array of default social icons.
				 */
				$map = apply_filters( 'twentytwenty_social_icons_map', self::$social_icons_map );

				/**
				 * Filters Twenty Twenty's array of social icons.
				 *
				 * @since Twenty Twenty 1.5
				 *
				 * @param array $social_icons Array of default social icons.
				 */
				$social_icons = apply_filters( 'twentytwenty_svg_icons_social', self::$social_icons );

				foreach ( array_keys( $social_icons ) as $icon ) {
					$domains            = array_key_exists( $icon, $map ) ? $map[ $icon ] : array( sprintf( '%s.com', $icon ) );
					$domains            = array_map( 'trim', $domains ); // Remove leading/trailing spaces, to prevent regex from failing to match.
					$domains            = array_map( 'preg_quote', $domains );
					$regex_map[ $icon ] = sprintf( '/(%s)/i', implode( '|', $domains ) );
				}
			}
			foreach ( $regex_map as $icon => $regex ) {
				if ( preg_match( $regex, $uri ) ) {
					return twentytwenty_get_theme_svg( $icon, 'social' );
				}
			}
			return null;
		}

		/**
		 * ICON STORAGE
		 * Store the code for all SVGs in an array.
		 *
		 * @since Twenty Twenty 1.0
		 * @var array
		 */
		public static $ui_icons = array(
			'arrow-down'         => '<svg xmlns="http://www.w3.org/2000/svg" width="22" height="24" viewBox="0 0 22 24">
		<polygon fill="#FFF" points="721.105 856 721.105 874.315 728.083 867.313 730.204 869.41 719.59 880 709 869.41 711.074 867.313 718.076 874.315 718.076 856" transform="translate(-709 -856)"/>
		</svg>',
			'arrow-down-circled' => '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
		<path fill="#FFF" fill-rule="evenodd" d="M16,32 C7.163444,32 0,24.836556 0,16 C0,7.163444 7.163444,0 16,0 C24.836556,0 32,7.163444 32,16 C32,24.836556 24.836556,32 16,32 Z M16.7934656,8 L15.4886113,8 L15.4886113,21.5300971 L10.082786,16.1242718 L9.18181515,17.0407767 L16.1410384,24 L23.1157957,17.0407767 L22.1915239,16.1242718 L16.7934656,21.5300971 L16.7934656,8 Z"/>
		</svg>',
			'bookmark'           => '<svg xmlns="http://www.w3.org/2000/svg" width="15" height="18" viewBox="0 0 15 18">
		<path d="M12.598889,2.699762 C12.598889,2.20275 12.195981,1.799841 11.698969,1.799841 L2.699762,1.799841 C2.20275,1.799841 1.799841,2.20275 1.799841,2.699762 L1.799841,15.349777 L6.676297,11.866594 C6.989197,11.643094 7.409533,11.643094 7.722434,11.866594 L12.598889,15.349777 L12.598889,2.699762 Z M1.422989,17.830788 C0.82736,18.256238 0,17.830464 0,17.098493 L0,2.699762 C0,1.208725 1.208725,0 2.699762,0 L11.698969,0 C13.190006,0 14.398731,1.208725 14.398731,2.699762 L14.398731,17.098493 C14.398731,17.830464 13.571371,18.256238 12.975742,17.830788 L7.199365,13.704805 L1.422989,17.830788 Z"/>
		</svg>',
			'calendar'           => '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="19" viewBox="0 0 18 19">
		<path fill="#1A1A1B" d="M4.60069444,4.09375 L3.25,4.09375 C2.47334957,4.09375 1.84375,4.72334957 1.84375,5.5 L1.84375,7.26736111 L16.15625,7.26736111 L16.15625,5.5 C16.15625,4.72334957 15.5266504,4.09375 14.75,4.09375 L13.3993056,4.09375 L13.3993056,4.55555556 C13.3993056,5.02154581 13.0215458,5.39930556 12.5555556,5.39930556 C12.0895653,5.39930556 11.7118056,5.02154581 11.7118056,4.55555556 L11.7118056,4.09375 L6.28819444,4.09375 L6.28819444,4.55555556 C6.28819444,5.02154581 5.9104347,5.39930556 5.44444444,5.39930556 C4.97845419,5.39930556 4.60069444,5.02154581 4.60069444,4.55555556 L4.60069444,4.09375 Z M6.28819444,2.40625 L11.7118056,2.40625 L11.7118056,1 C11.7118056,0.534009742 12.0895653,0.15625 12.5555556,0.15625 C13.0215458,0.15625 13.3993056,0.534009742 13.3993056,1 L13.3993056,2.40625 L14.75,2.40625 C16.4586309,2.40625 17.84375,3.79136906 17.84375,5.5 L17.84375,15.875 C17.84375,17.5836309 16.4586309,18.96875 14.75,18.96875 L3.25,18.96875 C1.54136906,18.96875 0.15625,17.5836309 0.15625,15.875 L0.15625,5.5 C0.15625,3.79136906 1.54136906,2.40625 3.25,2.40625 L4.60069444,2.40625 L4.60069444,1 C4.60069444,0.534009742 4.97845419,0.15625 5.44444444,0.15625 C5.9104347,0.15625 6.28819444,0.534009742 6.28819444,1 L6.28819444,2.40625 Z M1.84375,8.95486111 L1.84375,15.875 C1.84375,16.6516504 2.47334957,17.28125 3.25,17.28125 L14.75,17.28125 C15.5266504,17.28125 16.15625,16.6516504 16.15625,15.875 L16.15625,8.95486111 L1.84375,8.95486111 Z"/>
		</svg>',
			'chevron-down'       => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="12" viewBox="0 0 20 12">
		<polygon fill="#1A1A1B" fill-rule="evenodd" points="1319.899 365.778 1327.678 358 1329.799 360.121 1319.899 370.021 1310 360.121 1312.121 358" transform="translate(-1310 -358)"/>
		</svg>',
			'comment'            => '<svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 19 19">
		<path d="M9.43016863,13.2235931 C9.58624731,13.094699 9.7823475,13.0241935 9.98476849,13.0241935 L15.0564516,13.0241935 C15.8581553,13.0241935 16.5080645,12.3742843 16.5080645,11.5725806 L16.5080645,3.44354839 C16.5080645,2.64184472 15.8581553,1.99193548 15.0564516,1.99193548 L3.44354839,1.99193548 C2.64184472,1.99193548 1.99193548,2.64184472 1.99193548,3.44354839 L1.99193548,11.5725806 C1.99193548,12.3742843 2.64184472,13.0241935 3.44354839,13.0241935 L5.76612903,13.0241935 C6.24715123,13.0241935 6.63709677,13.4141391 6.63709677,13.8951613 L6.63709677,15.5301903 L9.43016863,13.2235931 Z M3.44354839,14.766129 C1.67980032,14.766129 0.25,13.3363287 0.25,11.5725806 L0.25,3.44354839 C0.25,1.67980032 1.67980032,0.25 3.44354839,0.25 L15.0564516,0.25 C16.8201997,0.25 18.25,1.67980032 18.25,3.44354839 L18.25,11.5725806 C18.25,13.3363287 16.8201997,14.766129 15.0564516,14.766129 L10.2979143,14.766129 L6.32072889,18.0506004 C5.75274472,18.5196577 4.89516129,18.1156602 4.89516129,17.3790323 L4.89516129,14.766129 L3.44354839,14.766129 Z"/>
		</svg>',
			'cross'              => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
		<polygon fill="#1A1A1B" fill-rule="evenodd" points="6.852 7.649 .399 1.195 1.445 .149 7.899 6.602 14.352 .149 15.399 1.195 8.945 7.649 15.399 14.102 14.352 15.149 7.899 8.695 1.445 15.149 .399 14.102"/>
		</svg>',
			'ellipsis'           => '<svg xmlns="http://www.w3.org/2000/svg" width="26" height="7" viewBox="0 0 26 7">
		<path fill-rule="evenodd" d="M332.5,45 C330.567003,45 329,43.4329966 329,41.5 C329,39.5670034 330.567003,38 332.5,38 C334.432997,38 336,39.5670034 336,41.5 C336,43.4329966 334.432997,45 332.5,45 Z M342,45 C340.067003,45 338.5,43.4329966 338.5,41.5 C338.5,39.5670034 340.067003,38 342,38 C343.932997,38 345.5,39.5670034 345.5,41.5 C345.5,43.4329966 343.932997,45 342,45 Z M351.5,45 C349.567003,45 348,43.4329966 348,41.5 C348,39.5670034 349.567003,38 351.5,38 C353.432997,38 355,39.5670034 355,41.5 C355,43.4329966 353.432997,45 351.5,45 Z" transform="translate(-329 -38)"/>
		</svg>',
			'edit'               => '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
		<path fill="#1A1A1B" d="M14.7272727,11.1763636 C14.7272727,10.7244943 15.0935852,10.3581818 15.5454545,10.3581818 C15.9973239,10.3581818 16.3636364,10.7244943 16.3636364,11.1763636 L16.3636364,15.5454545 C16.3636364,16.9010626 15.2646989,18 13.9090909,18 L2.45454545,18 C1.09893743,18 0,16.9010626 0,15.5454545 L0,4.09090909 C0,2.73530107 1.09893743,1.63636364 2.45454545,1.63636364 L6.82363636,1.63636364 C7.2755057,1.63636364 7.64181818,2.00267611 7.64181818,2.45454545 C7.64181818,2.9064148 7.2755057,3.27272727 6.82363636,3.27272727 L2.45454545,3.27272727 C2.00267611,3.27272727 1.63636364,3.63903975 1.63636364,4.09090909 L1.63636364,15.5454545 C1.63636364,15.9973239 2.00267611,16.3636364 2.45454545,16.3636364 L13.9090909,16.3636364 C14.3609602,16.3636364 14.7272727,15.9973239 14.7272727,15.5454545 L14.7272727,11.1763636 Z M6.54545455,9.33890201 L6.54545455,11.4545455 L8.66109799,11.4545455 L16.0247344,4.09090909 L13.9090909,1.97526564 L6.54545455,9.33890201 Z M14.4876328,0.239639906 L17.7603601,3.51236718 C18.07988,3.83188705 18.07988,4.34993113 17.7603601,4.669451 L9.57854191,12.8512692 C9.42510306,13.004708 9.21699531,13.0909091 9,13.0909091 L5.72727273,13.0909091 C5.27540339,13.0909091 4.90909091,12.7245966 4.90909091,12.2727273 L4.90909091,9 C4.90909091,8.78300469 4.99529196,8.57489694 5.14873082,8.42145809 L13.330549,0.239639906 C13.6500689,-0.0798799688 14.1681129,-0.0798799688 14.4876328,0.239639906 Z"/>
		</svg>',
			'folder'             => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="19" viewBox="0 0 20 19">
		<path fill="#1A1A1B" d="M2.8,1.85 C2.275329,1.85 1.85,2.27532949 1.85,2.8 L1.85,15.4 C1.85,15.9246705 2.275329,16.35 2.8,16.35 L17.2,16.35 C17.724671,16.35 18.15,15.9246705 18.15,15.4 L18.15,5.5 C18.15,4.97532949 17.724671,4.55 17.2,4.55 L9.1,4.55 C8.8158,4.55 8.550403,4.40796403 8.392757,4.17149517 L6.845094,1.85 L2.8,1.85 Z M17.2,2.85 C18.663555,2.85 19.85,4.03644541 19.85,5.5 L19.85,15.4 C19.85,16.8635546 18.663555,18.05 17.2,18.05 L2.8,18.05 C1.336445,18.05 0.15,16.8635546 0.15,15.4 L0.15,2.8 C0.15,1.33644541 1.336445,0.15 2.8,0.15 L7.3,0.15 C7.5842,0.15 7.849597,0.292035965 8.007243,0.528504833 L9.554906,2.85 L17.2,2.85 Z"/>
		</svg>',
			'link'               => '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
		<path d="M6.70846497,10.3082552 C6.43780491,9.94641406 6.5117218,9.43367048 6.87356298,9.16301045 C7.23540415,8.89235035 7.74814771,8.96626726 8.01880776,9.32810842 C8.5875786,10.0884893 9.45856383,10.5643487 10.4057058,10.6321812 C11.3528479,10.7000136 12.2827563,10.3531306 12.9541853,9.68145807 L15.3987642,7.23705399 C16.6390369,5.9529049 16.6212992,3.91168563 15.3588977,2.6492841 C14.0964962,1.38688258 12.0552769,1.36914494 10.77958,2.60113525 L9.37230725,4.00022615 C9.05185726,4.31881314 8.53381538,4.31730281 8.21522839,3.99685275 C7.89664141,3.67640269 7.89815174,3.15836082 8.21860184,2.83977385 L9.63432671,1.43240056 C11.5605503,-0.42800847 14.6223793,-0.401402004 16.5159816,1.49220028 C18.4095838,3.38580256 18.4361903,6.44763148 16.5658147,8.38399647 L14.1113741,10.838437 C13.1043877,11.8457885 11.7095252,12.366113 10.2888121,12.2643643 C8.86809903,12.1626156 7.56162126,11.4488264 6.70846497,10.3082552 Z M11.291535,7.6917448 C11.5621951,8.05358597 11.4882782,8.56632952 11.126437,8.83698955 C10.7645959,9.10764965 10.2518523,9.03373274 9.98119227,8.67189158 C9.4124214,7.91151075 8.54143617,7.43565129 7.59429414,7.36781884 C6.6471521,7.29998638 5.71724372,7.64686937 5.04581464,8.31854193 L2.60123581,10.762946 C1.36096312,12.0470951 1.37870076,14.0883144 2.64110228,15.3507159 C3.90350381,16.6131174 5.94472309,16.630855 7.21873082,15.400549 L8.61782171,14.0014581 C8.93734159,13.6819382 9.45538568,13.6819382 9.77490556,14.0014581 C10.0944254,14.320978 10.0944254,14.839022 9.77490556,15.1585419 L8.36567329,16.5675994 C6.43944966,18.4280085 3.37762074,18.401402 1.48401846,16.5077998 C-0.409583822,14.6141975 -0.436190288,11.5523685 1.43418536,9.61600353 L3.88862594,7.16156298 C4.89561225,6.15421151 6.29047483,5.63388702 7.71118789,5.7356357 C9.13190097,5.83738438 10.4383788,6.55117356 11.291535,7.6917448 Z"/>
		</svg>',
			'search'             => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23">
		<path d="M38.710696,48.0601792 L43,52.3494831 L41.3494831,54 L37.0601792,49.710696 C35.2632422,51.1481185 32.9839107,52.0076499 30.5038249,52.0076499 C24.7027226,52.0076499 20,47.3049272 20,41.5038249 C20,35.7027226 24.7027226,31 30.5038249,31 C36.3049272,31 41.0076499,35.7027226 41.0076499,41.5038249 C41.0076499,43.9839107 40.1481185,46.2632422 38.710696,48.0601792 Z M36.3875844,47.1716785 C37.8030221,45.7026647 38.6734666,43.7048964 38.6734666,41.5038249 C38.6734666,36.9918565 35.0157934,33.3341833 30.5038249,33.3341833 C25.9918565,33.3341833 22.3341833,36.9918565 22.3341833,41.5038249 C22.3341833,46.0157934 25.9918565,49.6734666 30.5038249,49.6734666 C32.7048964,49.6734666 34.7026647,48.8030221 36.1716785,47.3875844 C36.2023931,47.347638 36.2360451,47.3092237 36.2726343,47.2726343 C36.3092237,47.2360451 36.347638,47.2023931 36.3875844,47.1716785 Z" transform="translate(-20 -31)"/>
		</svg>',
			'tag'                => '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
		<path fill="#1A1A1B" d="M15.4496399,8.42490555 L8.66109799,1.63636364 L1.63636364,1.63636364 L1.63636364,8.66081885 L8.42522727,15.44178 C8.57869221,15.5954158 8.78693789,15.6817418 9.00409091,15.6817418 C9.22124393,15.6817418 9.42948961,15.5954158 9.58327627,15.4414581 L15.4486339,9.57610048 C15.7651495,9.25692435 15.7649133,8.74206554 15.4496399,8.42490555 Z M16.6084423,10.7304545 L10.7406818,16.59822 C10.280287,17.0591273 9.65554997,17.3181054 9.00409091,17.3181054 C8.35263185,17.3181054 7.72789481,17.0591273 7.26815877,16.5988788 L0.239976954,9.57887876 C0.0863319284,9.4254126 0,9.21716044 0,9 L0,0.818181818 C0,0.366312477 0.366312477,0 0.818181818,0 L9,0 C9.21699531,0 9.42510306,0.0862010512 9.57854191,0.239639906 L16.6084423,7.26954545 C17.5601275,8.22691012 17.5601275,9.77308988 16.6084423,10.7304545 Z M5,6 C4.44771525,6 4,5.55228475 4,5 C4,4.44771525 4.44771525,4 5,4 C5.55228475,4 6,4.44771525 6,5 C6,5.55228475 5.55228475,6 5,6 Z"/>
		</svg>',
			'user'               => '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" viewBox="0 0 18 20">
		<path fill="#1A1A1B" d="M18,19 C18,19.5522847 17.5522847,20 17,20 C16.4477153,20 16,19.5522847 16,19 L16,17 C16,15.3431458 14.6568542,14 13,14 L5,14 C3.34314575,14 2,15.3431458 2,17 L2,19 C2,19.5522847 1.55228475,20 1,20 C0.44771525,20 0,19.5522847 0,19 L0,17 C0,14.2385763 2.23857625,12 5,12 L13,12 C15.7614237,12 18,14.2385763 18,17 L18,19 Z M9,10 C6.23857625,10 4,7.76142375 4,5 C4,2.23857625 6.23857625,0 9,0 C11.7614237,0 14,2.23857625 14,5 C14,7.76142375 11.7614237,10 9,10 Z M9,8 C10.6568542,8 12,6.65685425 12,5 C12,3.34314575 10.6568542,2 9,2 C7.34314575,2 6,3.34314575 6,5 C6,6.65685425 7.34314575,8 9,8 Z"/>
		</svg>',
		);

		/**
		 * Social Icons – domain mappings.
		 *
		 * By default, each Icon ID is matched against a .com TLD. To override this behavior,
		 * specify all the domains it covers (including the .com TLD too, if applicable).
		 *
		 * @since Twenty Twenty 1.0
		 * @var array
		 */
		public static $social_icons_map = array(
			'amazon'    => array(
				'amazon.com',
				'amazon.cn',
				'amazon.in',
				'amazon.fr',
				'amazon.de',
				'amazon.it',
				'amazon.nl',
				'amazon.es',
				'amazon.co',
				'amazon.ca',
			),
			'behance'   => array(
				'behance.net',
			),
			'codepen'   => array(
				'codepen.io',
			),
			'facebook'  => array(
				'facebook.com',
				'fb.me',
			),
			'feed'      => array(
				'feed',
			),
			'google'    => array(
				'g.page',
			),
			'lastfm'    => array(
				'last.fm',
			),
			'mail'      => array(
				'mailto:',
			),
			'pocket'    => array(
				'getpocket.com',
			),
			'tiktok'    => array(
				'tiktok.com',
			),
			'twitch'    => array(
				'twitch.tv',
			),
			'wordpress' => array(
				'wordpress.com',
				'wordpress.org',
			),
		);

		/**
		 * Social Icons – svg sources.
		 *
		 * @since Twenty Twenty 1.0
		 * @var array
		 */
		public static $social_icons = array(
			'500px'      => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M6.94026,15.1412c.00437.01213.108.29862.168.44064a6.55008,6.55008,0,1,0,6.03191-9.09557,6.68654,6.68654,0,0,0-2.58357.51467A8.53914,8.53914,0,0,0,8.21268,8.61344L8.209,8.61725V3.22948l9.0504-.00008c.32934-.0036.32934-.46353.32934-.61466s0-.61091-.33035-.61467L7.47248,2a.43.43,0,0,0-.43131.42692v7.58355c0,.24466.30476.42131.58793.4819.553.11812.68074-.05864.81617-.2457l.018-.02481A10.52673,10.52673,0,0,1,9.32258,9.258a5.35268,5.35268,0,1,1,7.58985,7.54976,5.417,5.417,0,0,1-3.80867,1.56365,5.17483,5.17483,0,0,1-2.69822-.74478l.00342-4.61111a2.79372,2.79372,0,0,1,.71372-1.78792,2.61611,2.61611,0,0,1,1.98282-.89477,2.75683,2.75683,0,0,1,1.95525.79477,2.66867,2.66867,0,0,1,.79656,1.909,2.724,2.724,0,0,1-2.75849,2.748,4.94651,4.94651,0,0,1-.86254-.13719c-.31234-.093-.44519.34058-.48892.48349-.16811.54966.08453.65862.13687.67489a3.75751,3.75751,0,0,0,1.25234.18375,3.94634,3.94634,0,1,0-2.82444-6.742,3.67478,3.67478,0,0,0-1.13028,2.584l-.00041.02323c-.0035.11667-.00579,2.881-.00644,3.78811l-.00407-.00451a6.18521,6.18521,0,0,1-1.0851-1.86092c-.10544-.27856-.34358-.22925-.66857-.12917-.14192.04372-.57386.17677-.47833.489Zm4.65165-1.08338a.51346.51346,0,0,0,.19513.31818l.02276.022a.52945.52945,0,0,0,.3517.18416.24242.24242,0,0,0,.16577-.0611c.05473-.05082.67382-.67812.73287-.738l.69041.68819a.28978.28978,0,0,0,.21437.11032.53239.53239,0,0,0,.35708-.19486c.29792-.30419.14885-.46821.07676-.54751l-.69954-.69975.72952-.73469c.16-.17311.01874-.35708-.12218-.498-.20461-.20461-.402-.25742-.52855-.14083l-.7254.72665-.73354-.73375a.20128.20128,0,0,0-.14179-.05695.54135.54135,0,0,0-.34379.19648c-.22561.22555-.274.38149-.15656.5059l.73374.7315-.72942.73072A.26589.26589,0,0,0,11.59191,14.05782Zm1.59866-9.915A8.86081,8.86081,0,0,0,9.854,4.776a.26169.26169,0,0,0-.16938.22759.92978.92978,0,0,0,.08619.42094c.05682.14524.20779.531.50006.41955a8.40969,8.40969,0,0,1,2.91968-.55484,7.87875,7.87875,0,0,1,3.086.62286,8.61817,8.61817,0,0,1,2.30562,1.49315.2781.2781,0,0,0,.18318.07586c.15529,0,.30425-.15253.43167-.29551.21268-.23861.35873-.4369.1492-.63538a8.50425,8.50425,0,0,0-2.62312-1.694A9.0177,9.0177,0,0,0,13.19058,4.14283ZM19.50945,18.6236h0a.93171.93171,0,0,0-.36642-.25406.26589.26589,0,0,0-.27613.06613l-.06943.06929A7.90606,7.90606,0,0,1,7.60639,18.505a7.57284,7.57284,0,0,1-1.696-2.51537,8.58715,8.58715,0,0,1-.5147-1.77754l-.00871-.04864c-.04939-.25873-.28755-.27684-.62981-.22448-.14234.02178-.5755.088-.53426.39969l.001.00712a9.08807,9.08807,0,0,0,15.406,4.99094c.00193-.00192.04753-.04718.0725-.07436C19.79425,19.16234,19.87422,18.98728,19.50945,18.6236Z"></path></svg>',

			'amazon'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M13.582,8.182C11.934,8.367,9.78,8.49,8.238,9.166c-1.781,0.769-3.03,2.337-3.03,4.644 c0,2.953,1.86,4.429,4.253,4.429c2.02,0,3.125-0.477,4.685-2.065c0.516,0.747,0.685,1.109,1.629,1.894 c0.212,0.114,0.483,0.103,0.672-0.066l0.006,0.006c0.567-0.505,1.599-1.401,2.18-1.888c0.231-0.188,0.19-0.496,0.009-0.754 c-0.52-0.718-1.072-1.303-1.072-2.634V8.305c0-1.876,0.133-3.599-1.249-4.891C15.23,2.369,13.422,2,12.04,2 C9.336,2,6.318,3.01,5.686,6.351C5.618,6.706,5.877,6.893,6.109,6.945l2.754,0.298C9.121,7.23,9.308,6.977,9.357,6.72 c0.236-1.151,1.2-1.706,2.284-1.706c0.584,0,1.249,0.215,1.595,0.738c0.398,0.584,0.346,1.384,0.346,2.061V8.182z M13.049,14.088 c-0.451,0.8-1.169,1.291-1.967,1.291c-1.09,0-1.728-0.83-1.728-2.061c0-2.42,2.171-2.86,4.227-2.86v0.615 C13.582,12.181,13.608,13.104,13.049,14.088z M20.683,19.339C18.329,21.076,14.917,22,11.979,22c-4.118,0-7.826-1.522-10.632-4.057 c-0.22-0.199-0.024-0.471,0.241-0.317c3.027,1.762,6.771,2.823,10.639,2.823c2.608,0,5.476-0.541,8.115-1.66 C20.739,18.62,21.072,19.051,20.683,19.339z M21.336,21.043c-0.194,0.163-0.379,0.076-0.293-0.139 c0.284-0.71,0.92-2.298,0.619-2.684c-0.301-0.386-1.99-0.183-2.749-0.092c-0.23,0.027-0.266-0.173-0.059-0.319 c1.348-0.946,3.555-0.673,3.811-0.356C22.925,17.773,22.599,19.986,21.336,21.043z"></path></svg>',

			'bandcamp'   => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M15.27 17.289 3 17.289 8.73 6.711 21 6.711 15.27 17.289"></path></svg>',

			'behance'    => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M7.799,5.698c0.589,0,1.12,0.051,1.606,0.156c0.482,0.102,0.894,0.273,1.241,0.507c0.344,0.235,0.612,0.546,0.804,0.938 c0.188,0.387,0.281,0.871,0.281,1.443c0,0.619-0.141,1.137-0.421,1.551c-0.284,0.413-0.7,0.751-1.255,1.014 c0.756,0.218,1.317,0.601,1.689,1.146c0.374,0.549,0.557,1.205,0.557,1.975c0,0.623-0.12,1.161-0.359,1.612 c-0.241,0.457-0.569,0.828-0.973,1.114c-0.408,0.288-0.876,0.5-1.399,0.637C9.052,17.931,8.514,18,7.963,18H2V5.698H7.799 M7.449,10.668c0.481,0,0.878-0.114,1.192-0.345c0.311-0.228,0.463-0.603,0.463-1.119c0-0.286-0.051-0.523-0.152-0.707 C8.848,8.315,8.711,8.171,8.536,8.07C8.362,7.966,8.166,7.894,7.94,7.854c-0.224-0.044-0.457-0.06-0.697-0.06H4.709v2.874H7.449z M7.6,15.905c0.267,0,0.521-0.024,0.759-0.077c0.243-0.053,0.457-0.137,0.637-0.261c0.182-0.12,0.332-0.283,0.441-0.491 C9.547,14.87,9.6,14.602,9.6,14.278c0-0.633-0.18-1.084-0.533-1.357c-0.356-0.27-0.83-0.404-1.413-0.404H4.709v3.388L7.6,15.905z M16.162,15.864c0.367,0.358,0.897,0.538,1.583,0.538c0.493,0,0.92-0.125,1.277-0.374c0.354-0.248,0.571-0.514,0.654-0.79h2.155 c-0.347,1.072-0.872,1.838-1.589,2.299C19.534,18,18.67,18.23,17.662,18.23c-0.701,0-1.332-0.113-1.899-0.337 c-0.567-0.227-1.041-0.544-1.439-0.958c-0.389-0.415-0.689-0.907-0.904-1.484c-0.213-0.574-0.32-1.21-0.32-1.899 c0-0.666,0.11-1.288,0.329-1.863c0.222-0.577,0.529-1.075,0.933-1.492c0.406-0.42,0.885-0.751,1.444-0.994 c0.558-0.241,1.175-0.363,1.857-0.363c0.754,0,1.414,0.145,1.98,0.44c0.563,0.291,1.026,0.686,1.389,1.181 c0.363,0.493,0.622,1.057,0.783,1.69c0.16,0.632,0.217,1.292,0.171,1.983h-6.428C15.557,14.84,15.795,15.506,16.162,15.864 M18.973,11.184c-0.291-0.321-0.783-0.496-1.384-0.496c-0.39,0-0.714,0.066-0.973,0.2c-0.254,0.132-0.461,0.297-0.621,0.491 c-0.157,0.197-0.265,0.405-0.328,0.628c-0.063,0.217-0.101,0.413-0.111,0.587h3.98C19.478,11.969,19.265,11.509,18.973,11.184z M15.057,7.738h4.985V6.524h-4.985L15.057,7.738z"></path></svg>',

			'codepen'    => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M22.016,8.84c-0.002-0.013-0.005-0.025-0.007-0.037c-0.005-0.025-0.008-0.048-0.015-0.072 c-0.003-0.015-0.01-0.028-0.013-0.042c-0.008-0.02-0.015-0.04-0.023-0.062c-0.007-0.015-0.013-0.028-0.02-0.042 c-0.008-0.02-0.018-0.037-0.03-0.057c-0.007-0.013-0.017-0.027-0.025-0.038c-0.012-0.018-0.023-0.035-0.035-0.052 c-0.01-0.013-0.02-0.025-0.03-0.037c-0.015-0.017-0.028-0.032-0.043-0.045c-0.01-0.012-0.022-0.023-0.035-0.035 c-0.015-0.015-0.032-0.028-0.048-0.04c-0.012-0.01-0.025-0.02-0.037-0.03c-0.005-0.003-0.01-0.008-0.015-0.012l-9.161-6.096 c-0.289-0.192-0.666-0.192-0.955,0L2.359,8.237C2.354,8.24,2.349,8.245,2.344,8.249L2.306,8.277 c-0.017,0.013-0.033,0.027-0.048,0.04C2.246,8.331,2.234,8.342,2.222,8.352c-0.015,0.015-0.028,0.03-0.042,0.047 c-0.012,0.013-0.022,0.023-0.03,0.037C2.139,8.453,2.125,8.471,2.115,8.488C2.107,8.501,2.099,8.514,2.09,8.526 C2.079,8.548,2.069,8.565,2.06,8.585C2.054,8.6,2.047,8.613,2.04,8.626C2.032,8.648,2.025,8.67,2.019,8.69 c-0.005,0.013-0.01,0.027-0.013,0.042C1.999,8.755,1.995,8.778,1.99,8.803C1.989,8.817,1.985,8.828,1.984,8.84 C1.978,8.879,1.975,8.915,1.975,8.954v6.093c0,0.037,0.003,0.075,0.008,0.112c0.002,0.012,0.005,0.025,0.007,0.038 c0.005,0.023,0.008,0.047,0.015,0.072c0.003,0.015,0.008,0.028,0.013,0.04c0.007,0.022,0.013,0.042,0.022,0.063 c0.007,0.015,0.013,0.028,0.02,0.04c0.008,0.02,0.018,0.038,0.03,0.058c0.007,0.013,0.015,0.027,0.025,0.038 c0.012,0.018,0.023,0.035,0.035,0.052c0.01,0.013,0.02,0.025,0.03,0.037c0.013,0.015,0.028,0.032,0.042,0.045 c0.012,0.012,0.023,0.023,0.035,0.035c0.015,0.013,0.032,0.028,0.048,0.04l0.038,0.03c0.005,0.003,0.01,0.007,0.013,0.01 l9.163,6.095C11.668,21.953,11.833,22,12,22c0.167,0,0.332-0.047,0.478-0.144l9.163-6.095l0.015-0.01 c0.013-0.01,0.027-0.02,0.037-0.03c0.018-0.013,0.035-0.028,0.048-0.04c0.013-0.012,0.025-0.023,0.035-0.035 c0.017-0.015,0.03-0.032,0.043-0.045c0.01-0.013,0.02-0.025,0.03-0.037c0.013-0.018,0.025-0.035,0.035-0.052 c0.008-0.013,0.018-0.027,0.025-0.038c0.012-0.02,0.022-0.038,0.03-0.058c0.007-0.013,0.013-0.027,0.02-0.04 c0.008-0.022,0.015-0.042,0.023-0.063c0.003-0.013,0.01-0.027,0.013-0.04c0.007-0.025,0.01-0.048,0.015-0.072 c0.002-0.013,0.005-0.027,0.007-0.037c0.003-0.042,0.007-0.079,0.007-0.117V8.954C22.025,8.915,22.022,8.879,22.016,8.84z M12.862,4.464l6.751,4.49l-3.016,2.013l-3.735-2.492V4.464z M11.138,4.464v4.009l-3.735,2.494L4.389,8.954L11.138,4.464z M3.699,10.562L5.853,12l-2.155,1.438V10.562z M11.138,19.536l-6.749-4.491l3.015-2.011l3.735,2.492V19.536z M12,14.035L8.953,12 L12,9.966L15.047,12L12,14.035z M12.862,19.536v-4.009l3.735-2.492l3.016,2.011L12.862,19.536z M20.303,13.438L18.147,12 l2.156-1.438L20.303,13.438z"></path></svg>',

			'deviantart' => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M 18.19 5.636 18.19 2 18.188 2 14.553 2 14.19 2.366 12.474 5.636 11.935 6 5.81 6 5.81 10.994 9.177 10.994 9.477 11.357 5.81 18.363 5.81 22 5.811 22 9.447 22 9.81 21.634 11.526 18.364 12.065 18 18.19 18 18.19 13.006 14.823 13.006 14.523 12.641 18.19 5.636z"></path></svg>',

			'dribbble'   => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12,22C6.486,22,2,17.514,2,12S6.486,2,12,2c5.514,0,10,4.486,10,10S17.514,22,12,22z M20.434,13.369 c-0.292-0.092-2.644-0.794-5.32-0.365c1.117,3.07,1.572,5.57,1.659,6.09C18.689,17.798,20.053,15.745,20.434,13.369z M15.336,19.876c-0.127-0.749-0.623-3.361-1.822-6.477c-0.019,0.006-0.038,0.013-0.056,0.019c-4.818,1.679-6.547,5.02-6.701,5.334 c1.448,1.129,3.268,1.803,5.243,1.803C13.183,20.555,14.311,20.313,15.336,19.876z M5.654,17.724 c0.193-0.331,2.538-4.213,6.943-5.637c0.111-0.036,0.224-0.07,0.337-0.102c-0.214-0.485-0.448-0.971-0.692-1.45 c-4.266,1.277-8.405,1.223-8.778,1.216c-0.003,0.087-0.004,0.174-0.004,0.261C3.458,14.207,4.29,16.21,5.654,17.724z M3.639,10.264 c0.382,0.005,3.901,0.02,7.897-1.041c-1.415-2.516-2.942-4.631-3.167-4.94C5.979,5.41,4.193,7.613,3.639,10.264z M9.998,3.709 c0.236,0.316,1.787,2.429,3.187,5c3.037-1.138,4.323-2.867,4.477-3.085C16.154,4.286,14.17,3.471,12,3.471 C11.311,3.471,10.641,3.554,9.998,3.709z M18.612,6.612C18.432,6.855,17,8.69,13.842,9.979c0.199,0.407,0.389,0.821,0.567,1.237 c0.063,0.148,0.124,0.295,0.184,0.441c2.842-0.357,5.666,0.215,5.948,0.275C20.522,9.916,19.801,8.065,18.612,6.612z"></path></svg>',

			'dropbox'    => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12,6.134L6.069,9.797L2,6.54l5.883-3.843L12,6.134z M2,13.054l5.883,3.843L12,13.459L6.069,9.797L2,13.054z M12,13.459 l4.116,3.439L22,13.054l-4.069-3.257L12,13.459z M22,6.54l-5.884-3.843L12,6.134l5.931,3.663L22,6.54z M12.011,14.2l-4.129,3.426 l-1.767-1.153v1.291l5.896,3.539l5.897-3.539v-1.291l-1.769,1.153L12.011,14.2z"></path></svg>',

			'etsy'       => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M9.16033,4.038c0-.27174.02717-.43478.48913-.43478h6.22283c1.087,0,1.68478.92391,2.11957,2.663l.35326,1.38587h1.05978C19.59511,3.712,19.75815,2,19.75815,2s-2.663.29891-4.23913.29891h-7.962L3.29076,2.163v1.1413L4.731,3.57609c1.00543.19022,1.25.40761,1.33152,1.33152,0,0,.08152,2.71739.08152,7.20109s-.08152,7.17391-.08152,7.17391c0,.81522-.32609,1.11413-1.33152,1.30435l-1.44022.27174V22l4.2663-.13587h7.11957c1.60326,0,5.32609.13587,5.32609.13587.08152-.97826.625-5.40761.70652-5.89674H19.7038L18.644,18.52174c-.84239,1.90217-2.06522,2.038-3.42391,2.038H11.1712c-1.3587,0-2.01087-.54348-2.01087-1.712V12.65217s3.0163,0,3.99457.08152c.76087.05435,1.22283.27174,1.46739,1.33152l.32609,1.413h1.16848l-.08152-3.55978.163-3.587H15.02989l-.38043,1.57609c-.24457,1.03261-.40761,1.22283-1.46739,1.33152-1.38587.13587-4.02174.1087-4.02174.1087Z"></path></svg>',

			'facebook'   => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg>',

			'feed'       => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M2,8.667V12c5.515,0,10,4.485,10,10h3.333C15.333,14.637,9.363,8.667,2,8.667z M2,2v3.333 c9.19,0,16.667,7.477,16.667,16.667H22C22,10.955,13.045,2,2,2z M4.5,17C3.118,17,2,18.12,2,19.5S3.118,22,4.5,22S7,20.88,7,19.5 S5.882,17,4.5,17z"></path></svg>',

			'flickr'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M6.5,7c-2.75,0-5,2.25-5,5s2.25,5,5,5s5-2.25,5-5S9.25,7,6.5,7z M17.5,7c-2.75,0-5,2.25-5,5s2.25,5,5,5s5-2.25,5-5 S20.25,7,17.5,7z"></path></svg>',

			'foursquare' => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M17.573,2c0,0-9.197,0-10.668,0S5,3.107,5,3.805s0,16.948,0,16.948c0,0.785,0.422,1.077,0.66,1.172 c0.238,0.097,0.892,0.177,1.285-0.275c0,0,5.035-5.843,5.122-5.93c0.132-0.132,0.132-0.132,0.262-0.132h3.26 c1.368,0,1.588-0.977,1.732-1.552c0.078-0.318,0.692-3.428,1.225-6.122l0.675-3.368C19.56,2.893,19.14,2,17.573,2z M16.495,7.22 c-0.053,0.252-0.372,0.518-0.665,0.518c-0.293,0-4.157,0-4.157,0c-0.467,0-0.802,0.318-0.802,0.787v0.508 c0,0.467,0.337,0.798,0.805,0.798c0,0,3.197,0,3.528,0s0.655,0.362,0.583,0.715c-0.072,0.353-0.407,2.102-0.448,2.295 c-0.04,0.193-0.262,0.523-0.655,0.523c-0.33,0-2.88,0-2.88,0c-0.523,0-0.683,0.068-1.033,0.503 c-0.35,0.437-3.505,4.223-3.505,4.223c-0.032,0.035-0.063,0.027-0.063-0.015V4.852c0-0.298,0.26-0.648,0.648-0.648 c0,0,8.228,0,8.562,0c0.315,0,0.61,0.297,0.528,0.683L16.495,7.22z"></path></svg>',

			'goodreads'  => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M17.3,17.5c-0.2,0.8-0.5,1.4-1,1.9c-0.4,0.5-1,0.9-1.7,1.2C13.9,20.9,13.1,21,12,21c-0.6,0-1.3-0.1-1.9-0.2 c-0.6-0.1-1.1-0.4-1.6-0.7c-0.5-0.3-0.9-0.7-1.2-1.2c-0.3-0.5-0.5-1.1-0.5-1.7h1.5c0.1,0.5,0.2,0.9,0.5,1.2 c0.2,0.3,0.5,0.6,0.9,0.8c0.3,0.2,0.7,0.3,1.1,0.4c0.4,0.1,0.8,0.1,1.2,0.1c1.4,0,2.5-0.4,3.1-1.2c0.6-0.8,1-2,1-3.5v-1.7h0 c-0.4,0.8-0.9,1.4-1.6,1.9c-0.7,0.5-1.5,0.7-2.4,0.7c-1,0-1.9-0.2-2.6-0.5C8.7,15,8.1,14.5,7.7,14c-0.5-0.6-0.8-1.3-1-2.1 c-0.2-0.8-0.3-1.6-0.3-2.5c0-0.9,0.1-1.7,0.4-2.5c0.3-0.8,0.6-1.5,1.1-2c0.5-0.6,1.1-1,1.8-1.4C10.3,3.2,11.1,3,12,3 c0.5,0,0.9,0.1,1.3,0.2c0.4,0.1,0.8,0.3,1.1,0.5c0.3,0.2,0.6,0.5,0.9,0.8c0.3,0.3,0.5,0.6,0.6,1h0V3.4h1.5V15 C17.6,15.9,17.5,16.7,17.3,17.5z M13.8,14.1c0.5-0.3,0.9-0.7,1.3-1.1c0.3-0.5,0.6-1,0.8-1.6c0.2-0.6,0.3-1.2,0.3-1.9 c0-0.6-0.1-1.2-0.2-1.9c-0.1-0.6-0.4-1.2-0.7-1.7c-0.3-0.5-0.7-0.9-1.3-1.2c-0.5-0.3-1.1-0.5-1.9-0.5s-1.4,0.2-1.9,0.5 c-0.5,0.3-1,0.7-1.3,1.2C8.5,6.4,8.3,7,8.1,7.6C8,8.2,7.9,8.9,7.9,9.5c0,0.6,0.1,1.3,0.2,1.9C8.3,12,8.6,12.5,8.9,13 c0.3,0.5,0.8,0.8,1.3,1.1c0.5,0.3,1.1,0.4,1.9,0.4C12.7,14.5,13.3,14.4,13.8,14.1z"></path></svg>',

			'google'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12.02,10.18v3.72v0.01h5.51c-0.26,1.57-1.67,4.22-5.5,4.22c-3.31,0-6.01-2.75-6.01-6.12s2.7-6.12,6.01-6.12 c1.87,0,3.13,0.8,3.85,1.48l2.84-2.76C16.99,2.99,14.73,2,12.03,2c-5.52,0-10,4.48-10,10s4.48,10,10,10c5.77,0,9.6-4.06,9.6-9.77 c0-0.83-0.11-1.42-0.25-2.05H12.02z"></path></svg>',

			'github'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg>',

			'instagram'  => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12,4.622c2.403,0,2.688,0.009,3.637,0.052c0.877,0.04,1.354,0.187,1.671,0.31c0.42,0.163,0.72,0.358,1.035,0.673 c0.315,0.315,0.51,0.615,0.673,1.035c0.123,0.317,0.27,0.794,0.31,1.671c0.043,0.949,0.052,1.234,0.052,3.637 s-0.009,2.688-0.052,3.637c-0.04,0.877-0.187,1.354-0.31,1.671c-0.163,0.42-0.358,0.72-0.673,1.035 c-0.315,0.315-0.615,0.51-1.035,0.673c-0.317,0.123-0.794,0.27-1.671,0.31c-0.949,0.043-1.233,0.052-3.637,0.052 s-2.688-0.009-3.637-0.052c-0.877-0.04-1.354-0.187-1.671-0.31c-0.42-0.163-0.72-0.358-1.035-0.673 c-0.315-0.315-0.51-0.615-0.673-1.035c-0.123-0.317-0.27-0.794-0.31-1.671C4.631,14.688,4.622,14.403,4.622,12 s0.009-2.688,0.052-3.637c0.04-0.877,0.187-1.354,0.31-1.671c0.163-0.42,0.358-0.72,0.673-1.035 c0.315-0.315,0.615-0.51,1.035-0.673c0.317-0.123,0.794-0.27,1.671-0.31C9.312,4.631,9.597,4.622,12,4.622 M12,3 C9.556,3,9.249,3.01,8.289,3.054C7.331,3.098,6.677,3.25,6.105,3.472C5.513,3.702,5.011,4.01,4.511,4.511 c-0.5,0.5-0.808,1.002-1.038,1.594C3.25,6.677,3.098,7.331,3.054,8.289C3.01,9.249,3,9.556,3,12c0,2.444,0.01,2.751,0.054,3.711 c0.044,0.958,0.196,1.612,0.418,2.185c0.23,0.592,0.538,1.094,1.038,1.594c0.5,0.5,1.002,0.808,1.594,1.038 c0.572,0.222,1.227,0.375,2.185,0.418C9.249,20.99,9.556,21,12,21s2.751-0.01,3.711-0.054c0.958-0.044,1.612-0.196,2.185-0.418 c0.592-0.23,1.094-0.538,1.594-1.038c0.5-0.5,0.808-1.002,1.038-1.594c0.222-0.572,0.375-1.227,0.418-2.185 C20.99,14.751,21,14.444,21,12s-0.01-2.751-0.054-3.711c-0.044-0.958-0.196-1.612-0.418-2.185c-0.23-0.592-0.538-1.094-1.038-1.594 c-0.5-0.5-1.002-0.808-1.594-1.038c-0.572-0.222-1.227-0.375-2.185-0.418C14.751,3.01,14.444,3,12,3L12,3z M12,7.378 c-2.552,0-4.622,2.069-4.622,4.622S9.448,16.622,12,16.622s4.622-2.069,4.622-4.622S14.552,7.378,12,7.378z M12,15 c-1.657,0-3-1.343-3-3s1.343-3,3-3s3,1.343,3,3S13.657,15,12,15z M16.804,6.116c-0.596,0-1.08,0.484-1.08,1.08 s0.484,1.08,1.08,1.08c0.596,0,1.08-0.484,1.08-1.08S17.401,6.116,16.804,6.116z"></path></svg>',

			'lastfm'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M10.5002,0 C4.7006,0 0,4.70109753 0,10.4998496 C0,16.2989526 4.7006,21 10.5002,21 C16.299,21 21,16.2989526 21,10.4998496 C21,4.70109753 16.299,0 10.5002,0 Z M14.69735,14.7204413 C13.3164,14.7151781 12.4346,14.0870017 11.83445,12.6859357 L11.6816001,12.3451305 L10.35405,9.31011397 C9.92709997,8.26875064 8.85260001,7.57120012 7.68010001,7.57120012 C6.06945001,7.57120012 4.75925001,8.88509738 4.75925001,10.5009524 C4.75925001,12.1164565 6.06945001,13.4303036 7.68010001,13.4303036 C8.77200001,13.4303036 9.76514999,12.827541 10.2719501,11.8567047 C10.2893,11.8235214 10.3239,11.8019673 10.36305,11.8038219 C10.4007,11.8053759 10.43535,11.8287847 10.4504,11.8631709 L10.98655,13.1045863 C11.0016,13.1389726 10.9956,13.17782 10.97225,13.2068931 C10.1605001,14.1995341 8.96020001,14.7683115 7.68010001,14.7683115 C5.33305,14.7683115 3.42340001,12.8535563 3.42340001,10.5009524 C3.42340001,8.14679459 5.33300001,6.23203946 7.68010001,6.23203946 C9.45720002,6.23203946 10.8909,7.19074535 11.6138,8.86359341 C11.6205501,8.88018505 12.3412,10.5707777 12.97445,12.0190621 C13.34865,12.8739575 13.64615,13.3959676 14.6288,13.4291508 C15.5663001,13.4612814 16.25375,12.9121534 16.25375,12.1484869 C16.25375,11.4691321 15.8320501,11.3003585 14.8803,10.98216 C13.2365,10.4397989 12.34495,9.88605929 12.34495,8.51817658 C12.34495,7.1809207 13.26665,6.31615054 14.692,6.31615054 C15.62875,6.31615054 16.3155,6.7286858 16.79215,7.5768142 C16.80495,7.60062396 16.8079001,7.62814302 16.8004001,7.65420843 C16.7929,7.68027384 16.7748,7.70212868 16.7507001,7.713808 L15.86145,8.16900031 C15.8178001,8.19200805 15.7643,8.17807308 15.73565,8.13847371 C15.43295,7.71345711 15.0956,7.52513451 14.6423,7.52513451 C14.05125,7.52513451 13.6220001,7.92899802 13.6220001,8.48649708 C13.6220001,9.17382194 14.1529001,9.34144259 15.0339,9.61923972 C15.14915,9.65578139 15.26955,9.69397731 15.39385,9.73432853 C16.7763,10.1865133 17.57675,10.7311301 17.57675,12.1836251 C17.57685,13.629654 16.3389,14.7204413 14.69735,14.7204413 Z"></path></svg>',

			'linkedin'   => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg>',

			'mail'       => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M20,4H4C2.895,4,2,4.895,2,6v12c0,1.105,0.895,2,2,2h16c1.105,0,2-0.895,2-2V6C22,4.895,21.105,4,20,4z M20,8.236l-8,4.882 L4,8.236V6h16V8.236z"></path></svg>',

			'mastodon'   => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M23.193 7.879c0-5.206-3.411-6.732-3.411-6.732C18.062.357 15.108.025 12.041 0h-.076c-3.068.025-6.02.357-7.74 1.147 0 0-3.411 1.526-3.411 6.732 0 1.192-.023 2.618.015 4.129.124 5.092.934 10.109 5.641 11.355 2.17.574 4.034.695 5.535.612 2.722-.15 4.25-.972 4.25-.972l-.09-1.975s-1.945.613-4.129.539c-2.165-.074-4.449-.233-4.799-2.891a5.499 5.499 0 0 1-.048-.745s2.125.52 4.817.643c1.646.075 3.19-.097 4.758-.283 3.007-.359 5.625-2.212 5.954-3.905.517-2.665.475-6.507.475-6.507zm-4.024 6.709h-2.497V8.469c0-1.29-.543-1.944-1.628-1.944-1.2 0-1.802.776-1.802 2.312v3.349h-2.483v-3.35c0-1.536-.602-2.312-1.802-2.312-1.085 0-1.628.655-1.628 1.944v6.119H4.832V8.284c0-1.289.328-2.313.987-3.07.68-.758 1.569-1.146 2.674-1.146 1.278 0 2.246.491 2.886 1.474L12 6.585l.622-1.043c.64-.983 1.608-1.474 2.886-1.474 1.104 0 1.994.388 2.674 1.146.658.757.986 1.781.986 3.07v6.304z"/></svg>',

			'medium'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M20.962,7.257l-5.457,8.867l-3.923-6.375l3.126-5.08c0.112-0.182,0.319-0.286,0.527-0.286c0.05,0,0.1,0.008,0.149,0.02 c0.039,0.01,0.078,0.023,0.114,0.041l5.43,2.715l0.006,0.003c0.004,0.002,0.007,0.006,0.011,0.008 C20.971,7.191,20.98,7.227,20.962,7.257z M9.86,8.592v5.783l5.14,2.57L9.86,8.592z M15.772,17.331l4.231,2.115 C20.554,19.721,21,19.529,21,19.016V8.835L15.772,17.331z M8.968,7.178L3.665,4.527C3.569,4.479,3.478,4.456,3.395,4.456 C3.163,4.456,3,4.636,3,4.938v11.45c0,0.306,0.224,0.669,0.498,0.806l4.671,2.335c0.12,0.06,0.234,0.088,0.337,0.088 c0.29,0,0.494-0.225,0.494-0.602V7.231C9,7.208,8.988,7.188,8.968,7.178z"></path></svg>',

			'meetup'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M19.24775,14.722a3.57032,3.57032,0,0,1-2.94457,3.52073,3.61886,3.61886,0,0,1-.64652.05634c-.07314-.0008-.10187.02846-.12507.09547A2.38881,2.38881,0,0,1,13.49453,20.094a2.33092,2.33092,0,0,1-1.827-.50716.13635.13635,0,0,0-.19878-.00408,3.191,3.191,0,0,1-2.104.60248,3.26309,3.26309,0,0,1-3.00324-2.71993,2.19076,2.19076,0,0,1-.03512-.30865c-.00156-.08579-.03413-.1189-.11608-.13493a2.86421,2.86421,0,0,1-1.23189-.56111,2.945,2.945,0,0,1-1.166-2.05749,2.97484,2.97484,0,0,1,.87524-2.50774.112.112,0,0,0,.02091-.16107,2.7213,2.7213,0,0,1-.36648-1.48A2.81256,2.81256,0,0,1,6.57673,7.58838a.35764.35764,0,0,0,.28869-.22819,4.2208,4.2208,0,0,1,6.02892-1.90111.25161.25161,0,0,0,.22023.0243,3.65608,3.65608,0,0,1,3.76031.90678A3.57244,3.57244,0,0,1,17.95918,8.626a2.97339,2.97339,0,0,1,.01829.57356.10637.10637,0,0,0,.0853.12792,1.97669,1.97669,0,0,1,1.27939,1.33733,2.00266,2.00266,0,0,1-.57112,2.12652c-.05284.05166-.04168.08328-.01173.13489A3.51189,3.51189,0,0,1,19.24775,14.722Zm-6.35959-.27836a1.6984,1.6984,0,0,0,1.14556,1.61113,3.82039,3.82039,0,0,0,1.036.17935,1.46888,1.46888,0,0,0,.73509-.12255.44082.44082,0,0,0,.26057-.44274.45312.45312,0,0,0-.29211-.43375.97191.97191,0,0,0-.20678-.063c-.21326-.03806-.42754-.0701-.63973-.11215a.54787.54787,0,0,1-.50172-.60926,2.75864,2.75864,0,0,1,.1773-.901c.1763-.535.414-1.045.64183-1.55913A12.686,12.686,0,0,0,15.85,10.47863a1.58461,1.58461,0,0,0,.04861-.87208,1.04531,1.04531,0,0,0-.85432-.83981,1.60658,1.60658,0,0,0-1.23654.16594.27593.27593,0,0,1-.36286-.03413c-.085-.0747-.16594-.15379-.24918-.23055a.98682.98682,0,0,0-1.33577-.04933,6.1468,6.1468,0,0,1-.4989.41615.47762.47762,0,0,1-.51535.03566c-.17448-.09307-.35512-.175-.53531-.25665a1.74949,1.74949,0,0,0-.56476-.2016,1.69943,1.69943,0,0,0-1.61654.91787,8.05815,8.05815,0,0,0-.32952.80126c-.45471,1.2557-.82507,2.53825-1.20838,3.81639a1.24151,1.24151,0,0,0,.51532,1.44389,1.42659,1.42659,0,0,0,1.22008.17166,1.09728,1.09728,0,0,0,.66994-.69764c.44145-1.04111.839-2.09989,1.25981-3.14926.11581-.28876.22792-.57874.35078-.86438a.44548.44548,0,0,1,.69189-.19539.50521.50521,0,0,1,.15044.43836,1.75625,1.75625,0,0,1-.14731.50453c-.27379.69219-.55265,1.38236-.82766,2.074a2.0836,2.0836,0,0,0-.14038.42876.50719.50719,0,0,0,.27082.57722.87236.87236,0,0,0,.66145.02739.99137.99137,0,0,0,.53406-.532q.61571-1.20914,1.228-2.42031.28423-.55863.57585-1.1133a.87189.87189,0,0,1,.29055-.35253.34987.34987,0,0,1,.37634-.01265.30291.30291,0,0,1,.12434.31459.56716.56716,0,0,1-.04655.1915c-.05318.12739-.10286.25669-.16183.38156-.34118.71775-.68754,1.43273-1.02568,2.152A2.00213,2.00213,0,0,0,12.88816,14.44366Zm4.78568,5.28972a.88573.88573,0,0,0-1.77139.00465.8857.8857,0,0,0,1.77139-.00465Zm-14.83838-7.296a.84329.84329,0,1,0,.00827-1.68655.8433.8433,0,0,0-.00827,1.68655Zm10.366-9.43673a.83506.83506,0,1,0-.0091,1.67.83505.83505,0,0,0,.0091-1.67Zm6.85014,5.22a.71651.71651,0,0,0-1.433.0093.71656.71656,0,0,0,1.433-.0093ZM5.37528,6.17908A.63823.63823,0,1,0,6.015,5.54483.62292.62292,0,0,0,5.37528,6.17908Zm6.68214,14.80843a.54949.54949,0,1,0-.55052.541A.54556.54556,0,0,0,12.05742,20.98752Zm8.53235-8.49689a.54777.54777,0,0,0-.54027.54023.53327.53327,0,0,0,.532.52293.51548.51548,0,0,0,.53272-.5237A.53187.53187,0,0,0,20.58977,12.49063ZM7.82846,2.4715a.44927.44927,0,1,0,.44484.44766A.43821.43821,0,0,0,7.82846,2.4715Zm13.775,7.60492a.41186.41186,0,0,0-.40065.39623.40178.40178,0,0,0,.40168.40168A.38994.38994,0,0,0,22,10.48172.39946.39946,0,0,0,21.60349,10.07642ZM5.79193,17.96207a.40469.40469,0,0,0-.397-.39646.399.399,0,0,0-.396.405.39234.39234,0,0,0,.39939.389A.39857.39857,0,0,0,5.79193,17.96207Z"></path></svg>',

			'pinterest'  => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12.289,2C6.617,2,3.606,5.648,3.606,9.622c0,1.846,1.025,4.146,2.666,4.878c0.25,0.111,0.381,0.063,0.439-0.169 c0.044-0.175,0.267-1.029,0.365-1.428c0.032-0.128,0.017-0.237-0.091-0.362C6.445,11.911,6.01,10.75,6.01,9.668 c0-2.777,2.194-5.464,5.933-5.464c3.23,0,5.49,2.108,5.49,5.122c0,3.407-1.794,5.768-4.13,5.768c-1.291,0-2.257-1.021-1.948-2.277 c0.372-1.495,1.089-3.112,1.089-4.191c0-0.967-0.542-1.775-1.663-1.775c-1.319,0-2.379,1.309-2.379,3.059 c0,1.115,0.394,1.869,0.394,1.869s-1.302,5.279-1.54,6.261c-0.405,1.666,0.053,4.368,0.094,4.604 c0.021,0.126,0.167,0.169,0.25,0.063c0.129-0.165,1.699-2.419,2.142-4.051c0.158-0.59,0.817-2.995,0.817-2.995 c0.43,0.784,1.681,1.446,3.013,1.446c3.963,0,6.822-3.494,6.822-7.833C20.394,5.112,16.849,2,12.289,2"></path></svg>',

			'pocket'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M21.927,4.194C21.667,3.48,20.982,3,20.222,3h-0.01h-1.721H3.839C3.092,3,2.411,3.47,2.145,4.17 C2.066,4.378,2.026,4.594,2.026,4.814v6.035l0.069,1.2c0.29,2.73,1.707,5.115,3.899,6.778c0.039,0.03,0.079,0.059,0.119,0.089 l0.025,0.018c1.175,0.859,2.491,1.441,3.91,1.727c0.655,0.132,1.325,0.2,1.991,0.2c0.615,0,1.232-0.057,1.839-0.17 c0.073-0.014,0.145-0.028,0.219-0.044c0.02-0.004,0.042-0.012,0.064-0.023c1.359-0.297,2.621-0.864,3.753-1.691l0.025-0.018 c0.04-0.029,0.08-0.058,0.119-0.089c2.192-1.664,3.609-4.049,3.898-6.778l0.069-1.2V4.814C22.026,4.605,22,4.398,21.927,4.194z M17.692,10.481l-4.704,4.512c-0.266,0.254-0.608,0.382-0.949,0.382c-0.342,0-0.684-0.128-0.949-0.382l-4.705-4.512 C5.838,9.957,5.82,9.089,6.344,8.542c0.524-0.547,1.392-0.565,1.939-0.04l3.756,3.601l3.755-3.601 c0.547-0.524,1.415-0.506,1.939,0.04C18.256,9.089,18.238,9.956,17.692,10.481z"></path></svg>',

			'reddit'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M22,11.816c0-1.256-1.021-2.277-2.277-2.277c-0.593,0-1.122,0.24-1.526,0.614c-1.481-0.965-3.455-1.594-5.647-1.69 l1.171-3.702l3.18,0.748c0.008,1.028,0.846,1.862,1.876,1.862c1.035,0,1.877-0.842,1.877-1.878c0-1.035-0.842-1.877-1.877-1.877 c-0.769,0-1.431,0.466-1.72,1.13l-3.508-0.826c-0.203-0.047-0.399,0.067-0.46,0.261l-1.35,4.268 c-2.316,0.038-4.411,0.67-5.97,1.671C5.368,9.765,4.853,9.539,4.277,9.539C3.021,9.539,2,10.56,2,11.816 c0,0.814,0.433,1.523,1.078,1.925c-0.037,0.221-0.061,0.444-0.061,0.672c0,3.292,4.011,5.97,8.941,5.97s8.941-2.678,8.941-5.97 c0-0.214-0.02-0.424-0.053-0.632C21.533,13.39,22,12.661,22,11.816z M18.776,4.394c0.606,0,1.1,0.493,1.1,1.1s-0.493,1.1-1.1,1.1 s-1.1-0.494-1.1-1.1S18.169,4.394,18.776,4.394z M2.777,11.816c0-0.827,0.672-1.5,1.499-1.5c0.313,0,0.598,0.103,0.838,0.269 c-0.851,0.676-1.477,1.479-1.812,2.36C2.983,12.672,2.777,12.27,2.777,11.816z M11.959,19.606c-4.501,0-8.164-2.329-8.164-5.193 S7.457,9.22,11.959,9.22s8.164,2.329,8.164,5.193S16.46,19.606,11.959,19.606z M20.636,13.001c-0.326-0.89-0.948-1.701-1.797-2.384 c0.248-0.186,0.55-0.301,0.883-0.301c0.827,0,1.5,0.673,1.5,1.5C21.223,12.299,20.992,12.727,20.636,13.001z M8.996,14.704 c-0.76,0-1.397-0.616-1.397-1.376c0-0.76,0.637-1.397,1.397-1.397c0.76,0,1.376,0.637,1.376,1.397 C10.372,14.088,9.756,14.704,8.996,14.704z M16.401,13.328c0,0.76-0.616,1.376-1.376,1.376c-0.76,0-1.399-0.616-1.399-1.376 c0-0.76,0.639-1.397,1.399-1.397C15.785,11.931,16.401,12.568,16.401,13.328z M15.229,16.708c0.152,0.152,0.152,0.398,0,0.55 c-0.674,0.674-1.727,1.002-3.219,1.002c-0.004,0-0.007-0.002-0.011-0.002c-0.004,0-0.007,0.002-0.011,0.002 c-1.492,0-2.544-0.328-3.218-1.002c-0.152-0.152-0.152-0.398,0-0.55c0.152-0.152,0.399-0.151,0.55,0 c0.521,0.521,1.394,0.775,2.669,0.775c0.004,0,0.007,0.002,0.011,0.002c0.004,0,0.007-0.002,0.011-0.002 c1.275,0,2.148-0.253,2.669-0.775C14.831,16.556,15.078,16.556,15.229,16.708z"></path></svg>',

			'skype'      => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M10.113,2.699c0.033-0.006,0.067-0.013,0.1-0.02c0.033,0.017,0.066,0.033,0.098,0.051L10.113,2.699z M2.72,10.223 c-0.006,0.034-0.011,0.069-0.017,0.103c0.018,0.032,0.033,0.064,0.051,0.095L2.72,10.223z M21.275,13.771 c0.007-0.035,0.011-0.071,0.018-0.106c-0.018-0.031-0.033-0.064-0.052-0.095L21.275,13.771z M13.563,21.199 c0.032,0.019,0.065,0.035,0.096,0.053c0.036-0.006,0.071-0.011,0.105-0.017L13.563,21.199z M22,16.386 c0,1.494-0.581,2.898-1.637,3.953c-1.056,1.057-2.459,1.637-3.953,1.637c-0.967,0-1.914-0.251-2.75-0.725 c0.036-0.006,0.071-0.011,0.105-0.017l-0.202-0.035c0.032,0.019,0.065,0.035,0.096,0.053c-0.543,0.096-1.099,0.147-1.654,0.147 c-1.275,0-2.512-0.25-3.676-0.743c-1.125-0.474-2.135-1.156-3.002-2.023c-0.867-0.867-1.548-1.877-2.023-3.002 c-0.493-1.164-0.743-2.401-0.743-3.676c0-0.546,0.049-1.093,0.142-1.628c0.018,0.032,0.033,0.064,0.051,0.095L2.72,10.223 c-0.006,0.034-0.011,0.069-0.017,0.103C2.244,9.5,2,8.566,2,7.615c0-1.493,0.582-2.898,1.637-3.953 c1.056-1.056,2.46-1.638,3.953-1.638c0.915,0,1.818,0.228,2.622,0.655c-0.033,0.007-0.067,0.013-0.1,0.02l0.199,0.031 c-0.032-0.018-0.066-0.034-0.098-0.051c0.002,0,0.003-0.001,0.004-0.001c0.586-0.112,1.187-0.169,1.788-0.169 c1.275,0,2.512,0.249,3.676,0.742c1.124,0.476,2.135,1.156,3.002,2.024c0.868,0.867,1.548,1.877,2.024,3.002 c0.493,1.164,0.743,2.401,0.743,3.676c0,0.575-0.054,1.15-0.157,1.712c-0.018-0.031-0.033-0.064-0.052-0.095l0.034,0.201 c0.007-0.035,0.011-0.071,0.018-0.106C21.754,14.494,22,15.432,22,16.386z M16.817,14.138c0-1.331-0.613-2.743-3.033-3.282 l-2.209-0.49c-0.84-0.192-1.807-0.444-1.807-1.237c0-0.794,0.679-1.348,1.903-1.348c2.468,0,2.243,1.696,3.468,1.696 c0.645,0,1.209-0.379,1.209-1.031c0-1.521-2.435-2.663-4.5-2.663c-2.242,0-4.63,0.952-4.63,3.488c0,1.221,0.436,2.521,2.839,3.123 l2.984,0.745c0.903,0.223,1.129,0.731,1.129,1.189c0,0.762-0.758,1.507-2.129,1.507c-2.679,0-2.307-2.062-3.743-2.062 c-0.645,0-1.113,0.444-1.113,1.078c0,1.236,1.501,2.886,4.856,2.886C15.236,17.737,16.817,16.199,16.817,14.138z"></path></svg>',

			'snapchat'   => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12.065,2a5.526,5.526,0,0,1,3.132.892A5.854,5.854,0,0,1,17.326,5.4a5.821,5.821,0,0,1,.351,2.33q0,.612-.117,2.487a.809.809,0,0,0,.365.091,1.93,1.93,0,0,0,.664-.176,1.93,1.93,0,0,1,.664-.176,1.3,1.3,0,0,1,.729.234.7.7,0,0,1,.351.6.839.839,0,0,1-.41.7,2.732,2.732,0,0,1-.9.41,3.192,3.192,0,0,0-.9.378.728.728,0,0,0-.41.618,1.575,1.575,0,0,0,.156.56,6.9,6.9,0,0,0,1.334,1.953,5.6,5.6,0,0,0,1.881,1.315,5.875,5.875,0,0,0,1.042.3.42.42,0,0,1,.365.456q0,.911-2.852,1.341a1.379,1.379,0,0,0-.143.507,1.8,1.8,0,0,1-.182.605.451.451,0,0,1-.429.241,5.878,5.878,0,0,1-.807-.085,5.917,5.917,0,0,0-.833-.085,4.217,4.217,0,0,0-.807.065,2.42,2.42,0,0,0-.82.293,6.682,6.682,0,0,0-.755.5q-.351.267-.755.527a3.886,3.886,0,0,1-.989.436A4.471,4.471,0,0,1,11.831,22a4.307,4.307,0,0,1-1.256-.176,3.784,3.784,0,0,1-.976-.436q-.4-.26-.749-.527a6.682,6.682,0,0,0-.755-.5,2.422,2.422,0,0,0-.807-.293,4.432,4.432,0,0,0-.82-.065,5.089,5.089,0,0,0-.853.1,5,5,0,0,1-.762.1.474.474,0,0,1-.456-.241,1.819,1.819,0,0,1-.182-.618,1.411,1.411,0,0,0-.143-.521q-2.852-.429-2.852-1.341a.42.42,0,0,1,.365-.456,5.793,5.793,0,0,0,1.042-.3,5.524,5.524,0,0,0,1.881-1.315,6.789,6.789,0,0,0,1.334-1.953A1.575,1.575,0,0,0,6,12.9a.728.728,0,0,0-.41-.618,3.323,3.323,0,0,0-.9-.384,2.912,2.912,0,0,1-.9-.41.814.814,0,0,1-.41-.684.71.71,0,0,1,.338-.593,1.208,1.208,0,0,1,.716-.241,1.976,1.976,0,0,1,.625.169,2.008,2.008,0,0,0,.69.169.919.919,0,0,0,.416-.091q-.117-1.849-.117-2.474A5.861,5.861,0,0,1,6.385,5.4,5.516,5.516,0,0,1,8.625,2.819,7.075,7.075,0,0,1,12.062,2Z"></path></svg>',

			'soundcloud' => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M8.9,16.1L9,14L8.9,9.5c0-0.1,0-0.1-0.1-0.1c0,0-0.1-0.1-0.1-0.1c-0.1,0-0.1,0-0.1,0.1c0,0-0.1,0.1-0.1,0.1L8.3,14l0.1,2.1 c0,0.1,0,0.1,0.1,0.1c0,0,0.1,0.1,0.1,0.1C8.8,16.3,8.9,16.3,8.9,16.1z M11.4,15.9l0.1-1.8L11.4,9c0-0.1,0-0.2-0.1-0.2 c0,0-0.1,0-0.1,0s-0.1,0-0.1,0c-0.1,0-0.1,0.1-0.1,0.2l0,0.1l-0.1,5c0,0,0,0.7,0.1,2v0c0,0.1,0,0.1,0.1,0.1c0.1,0.1,0.1,0.1,0.2,0.1 c0.1,0,0.1,0,0.2-0.1c0.1,0,0.1-0.1,0.1-0.2L11.4,15.9z M2.4,12.9L2.5,14l-0.2,1.1c0,0.1,0,0.1-0.1,0.1c0,0-0.1,0-0.1-0.1L2.1,14 l0.1-1.1C2.2,12.9,2.3,12.9,2.4,12.9C2.3,12.9,2.4,12.9,2.4,12.9z M3.1,12.2L3.3,14l-0.2,1.8c0,0.1,0,0.1-0.1,0.1 c-0.1,0-0.1,0-0.1-0.1L2.8,14L3,12.2C3,12.2,3,12.2,3.1,12.2C3.1,12.2,3.1,12.2,3.1,12.2z M3.9,11.9L4.1,14l-0.2,2.1 c0,0.1,0,0.1-0.1,0.1c-0.1,0-0.1,0-0.1-0.1L3.5,14l0.2-2.1c0-0.1,0-0.1,0.1-0.1C3.9,11.8,3.9,11.8,3.9,11.9z M4.7,11.9L4.9,14 l-0.2,2.1c0,0.1-0.1,0.1-0.1,0.1c-0.1,0-0.1,0-0.1-0.1L4.3,14l0.2-2.2c0-0.1,0-0.1,0.1-0.1C4.7,11.7,4.7,11.8,4.7,11.9z M5.6,12 l0.2,2l-0.2,2.1c0,0.1-0.1,0.1-0.1,0.1c0,0-0.1,0-0.1,0c0,0,0-0.1,0-0.1L5.1,14l0.2-2c0,0,0-0.1,0-0.1s0.1,0,0.1,0 C5.5,11.9,5.5,11.9,5.6,12L5.6,12z M6.4,10.7L6.6,14l-0.2,2.1c0,0,0,0.1,0,0.1c0,0-0.1,0-0.1,0c-0.1,0-0.1-0.1-0.2-0.2L5.9,14 l0.2-3.3c0-0.1,0.1-0.2,0.2-0.2c0,0,0.1,0,0.1,0C6.4,10.7,6.4,10.7,6.4,10.7z M7.2,10l0.2,4.1l-0.2,2.1c0,0,0,0.1,0,0.1 c0,0-0.1,0-0.1,0c-0.1,0-0.2-0.1-0.2-0.2l-0.1-2.1L6.8,10c0-0.1,0.1-0.2,0.2-0.2c0,0,0.1,0,0.1,0S7.2,9.9,7.2,10z M8,9.6L8.2,14 L8,16.1c0,0.1-0.1,0.2-0.2,0.2c-0.1,0-0.2-0.1-0.2-0.2L7.5,14l0.1-4.4c0-0.1,0-0.1,0.1-0.1c0,0,0.1-0.1,0.1-0.1c0.1,0,0.1,0,0.1,0.1 C8,9.6,8,9.6,8,9.6z M11.4,16.1L11.4,16.1L11.4,16.1z M9.7,9.6L9.8,14l-0.1,2.1c0,0.1,0,0.1-0.1,0.2s-0.1,0.1-0.2,0.1 c-0.1,0-0.1,0-0.1-0.1s-0.1-0.1-0.1-0.2L9.2,14l0.1-4.4c0-0.1,0-0.1,0.1-0.2s0.1-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0.1S9.7,9.5,9.7,9.6 L9.7,9.6z M10.6,9.8l0.1,4.3l-0.1,2c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0.1-0.2,0.1c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2L10,14 l0.1-4.3c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0.1S10.6,9.7,10.6,9.8z M12.4,14l-0.1,2c0,0.1,0,0.1-0.1,0.2 c-0.1,0.1-0.1,0.1-0.2,0.1c-0.1,0-0.1,0-0.2-0.1c-0.1-0.1-0.1-0.1-0.1-0.2l-0.1-1l-0.1-1l0.1-5.5v0c0-0.1,0-0.2,0.1-0.2 c0.1,0,0.1-0.1,0.2-0.1c0,0,0.1,0,0.1,0c0.1,0,0.1,0.1,0.1,0.2L12.4,14z M22.1,13.9c0,0.7-0.2,1.3-0.7,1.7c-0.5,0.5-1.1,0.7-1.7,0.7 h-6.8c-0.1,0-0.1,0-0.2-0.1c-0.1-0.1-0.1-0.1-0.1-0.2V8.2c0-0.1,0.1-0.2,0.2-0.3c0.5-0.2,1-0.3,1.6-0.3c1.1,0,2.1,0.4,2.9,1.1 c0.8,0.8,1.3,1.7,1.4,2.8c0.3-0.1,0.6-0.2,1-0.2c0.7,0,1.3,0.2,1.7,0.7C21.8,12.6,22.1,13.2,22.1,13.9L22.1,13.9z"></path></svg>',

			'spotify'    => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12,2C6.477,2,2,6.477,2,12c0,5.523,4.477,10,10,10c5.523,0,10-4.477,10-10C22,6.477,17.523,2,12,2 M16.586,16.424 c-0.18,0.295-0.563,0.387-0.857,0.207c-2.348-1.435-5.304-1.76-8.785-0.964c-0.335,0.077-0.67-0.133-0.746-0.469 c-0.077-0.335,0.132-0.67,0.469-0.746c3.809-0.871,7.077-0.496,9.713,1.115C16.673,15.746,16.766,16.13,16.586,16.424 M17.81,13.7 c-0.226,0.367-0.706,0.482-1.072,0.257c-2.687-1.652-6.785-2.131-9.965-1.166C6.36,12.917,5.925,12.684,5.8,12.273 C5.675,11.86,5.908,11.425,6.32,11.3c3.632-1.102,8.147-0.568,11.234,1.328C17.92,12.854,18.035,13.335,17.81,13.7 M17.915,10.865 c-3.223-1.914-8.54-2.09-11.618-1.156C5.804,9.859,5.281,9.58,5.131,9.086C4.982,8.591,5.26,8.069,5.755,7.919 c3.532-1.072,9.404-0.865,13.115,1.338c0.445,0.264,0.59,0.838,0.327,1.282C18.933,10.983,18.359,11.129,17.915,10.865"></path></svg>',

			'tumblr'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M16.749,17.396c-0.357,0.17-1.041,0.319-1.551,0.332c-1.539,0.041-1.837-1.081-1.85-1.896V9.847h3.861V6.937h-3.847V2.039 c0,0-2.77,0-2.817,0c-0.046,0-0.127,0.041-0.138,0.144c-0.165,1.499-0.867,4.13-3.783,5.181v2.484h1.945v6.282 c0,2.151,1.587,5.206,5.775,5.135c1.413-0.024,2.982-0.616,3.329-1.126L16.749,17.396z"></path></svg>',

			'tiktok'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M22.601273,3.99398422 C22.3543419,3.81786432 22.035286,3.77722295 21.7519316,3.88579503 C20.7673827,4.3053476 19.6367473,4.19781761 18.749359,3.60023293 C17.8619707,3.00264826 17.3390468,1.99663905 17.3606558,0.92862387 C17.366873,0.687165991 17.2757319,0.453316101 17.1076605,0.279488738 C16.937965,0.101864383 16.7030375,0.000936521363 16.4571011,0 L12.8428824,0 C12.343863,0 11.9393277,0.403649581 11.9393277,0.901576573 L11.9393277,16.3365675 C11.9393277,17.581385 10.9279897,18.5905089 9.68044104,18.5905089 C8.43289237,18.5905089 7.42155434,17.581385 7.42155434,16.3365675 C7.42155434,15.09175 8.43289237,14.0826261 9.68044104,14.0826261 C10.1794605,14.0826261 10.5839957,13.6789765 10.5839957,13.1810495 L10.5839957,9.57474321 C10.5839957,9.07681622 10.1794605,8.67316663 9.68044104,8.67316663 C5.44083962,8.67813472 2.00520525,12.1062476 2.00022626,16.3365675 C1.98771579,17.9546233 2.49430163,19.5342383 3.44591375,20.8444504 C3.61458557,21.0808398 5.05966461,19.9481604 4.91870788,19.7535427 C4.19555103,18.760421 3.80646351,17.5641499 3.80733562,16.3365675 C3.81165144,13.4484211 5.91803869,10.991254 8.77688636,10.5394301 L8.77688636,12.3425833 C6.70706914,12.8134053 5.34862709,14.7938709 5.65804701,16.8895228 C5.96746693,18.9851748 7.84059425,20.4905907 9.95854931,20.3457987 C12.0765044,20.2010067 13.7263573,18.4547454 13.7464371,16.3365675 L13.7464371,1.80315315 L15.6167953,1.80315315 C16.0103639,4.20249558 18.0864975,5.96506252 20.5230972,5.96843692 C20.7496837,5.96688212 20.975975,5.95182905 21.2007632,5.92335809 L21.2007632,7.67241664 C19.7293748,7.92650626 18.2152284,7.66195137 16.917914,6.92410808 C16.6383532,6.76305694 16.2939201,6.76305694 16.0143593,6.92410808 C15.7336844,7.0857977 15.5612953,7.38509793 15.562582,7.7084797 L15.562582,16.3365675 C15.5580692,18.2941869 14.5756972,20.1206948 12.9432814,21.2065896 C11.9060301,21.8965766 10.6930991,22.229824 9.48148608,22.189012 C8.78627447,22.1655944 8.08246121,22.0477035 7.42155434,21.774707 C7.12504167,21.5395062 5.484375,20.8444504 4.91870788,19.7535427 C4.66351437,19.2613936 3.0238563,20.6306805 3.44591375,20.8444504 C4.91870788,23.1351563 7.99726562,23.9999684 9.69851213,23.9999684 C13.7074483,23.9999684 17.0406982,20.9190082 17.3606558,16.931608 C17.3657425,16.8836579 17.3657425,16.8353059 17.3606558,16.7873558 L17.3606558,9.10592339 C18.9684136,9.66657941 20.7106733,9.71696914 22.3482777,9.25017564 C22.7469793,9.13449784 23.0153499,8.76257691 22.998837,8.34859907 L22.998837,4.7873716 C23.0148813,4.47165088 22.8640303,4.17060935 22.601273,3.99398422 Z" /></svg>',

			'twitch'     => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M16.499,8.089h-1.636v4.91h1.636V8.089z M12,8.089h-1.637v4.91H12V8.089z M4.228,3.178L3,6.451v13.092h4.499V22h2.456 l2.454-2.456h3.681L21,14.636V3.178H4.228z M19.364,13.816l-2.864,2.865H12l-2.453,2.453V16.68H5.863V4.814h13.501V13.816z"></path></svg>',

			'twitter'    => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M22.23,5.924c-0.736,0.326-1.527,0.547-2.357,0.646c0.847-0.508,1.498-1.312,1.804-2.27 c-0.793,0.47-1.671,0.812-2.606,0.996C18.324,4.498,17.257,4,16.077,4c-2.266,0-4.103,1.837-4.103,4.103 c0,0.322,0.036,0.635,0.106,0.935C8.67,8.867,5.647,7.234,3.623,4.751C3.27,5.357,3.067,6.062,3.067,6.814 c0,1.424,0.724,2.679,1.825,3.415c-0.673-0.021-1.305-0.206-1.859-0.513c0,0.017,0,0.034,0,0.052c0,1.988,1.414,3.647,3.292,4.023 c-0.344,0.094-0.707,0.144-1.081,0.144c-0.264,0-0.521-0.026-0.772-0.074c0.522,1.63,2.038,2.816,3.833,2.85 c-1.404,1.1-3.174,1.756-5.096,1.756c-0.331,0-0.658-0.019-0.979-0.057c1.816,1.164,3.973,1.843,6.29,1.843 c7.547,0,11.675-6.252,11.675-11.675c0-0.178-0.004-0.355-0.012-0.531C20.985,7.47,21.68,6.747,22.23,5.924z"></path></svg>',

			'vimeo'      => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M22.396,7.164c-0.093,2.026-1.507,4.799-4.245,8.32C15.322,19.161,12.928,21,10.97,21c-1.214,0-2.24-1.119-3.079-3.359 c-0.56-2.053-1.119-4.106-1.68-6.159C5.588,9.243,4.921,8.122,4.206,8.122c-0.156,0-0.701,0.328-1.634,0.98L1.594,7.841 c1.027-0.902,2.04-1.805,3.037-2.708C6.001,3.95,7.03,3.327,7.715,3.264c1.619-0.156,2.616,0.951,2.99,3.321 c0.404,2.557,0.685,4.147,0.841,4.769c0.467,2.121,0.981,3.181,1.542,3.181c0.435,0,1.09-0.688,1.963-2.065 c0.871-1.376,1.338-2.422,1.401-3.142c0.125-1.187-0.343-1.782-1.401-1.782c-0.498,0-1.012,0.115-1.541,0.341 c1.023-3.35,2.977-4.977,5.862-4.884C21.511,3.066,22.52,4.453,22.396,7.164z"></path></svg>',

			'vk'         => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M22,7.1c0.2,0.4-0.4,1.5-1.6,3.1c-0.2,0.2-0.4,0.5-0.7,0.9c-0.5,0.7-0.9,1.1-0.9,1.4c-0.1,0.3-0.1,0.6,0.1,0.8 c0.1,0.1,0.4,0.4,0.8,0.9h0l0,0c1,0.9,1.6,1.7,2,2.3c0,0,0,0.1,0.1,0.1c0,0.1,0,0.1,0.1,0.3c0,0.1,0,0.2,0,0.4 c0,0.1-0.1,0.2-0.3,0.3c-0.1,0.1-0.4,0.1-0.6,0.1l-2.7,0c-0.2,0-0.4,0-0.6-0.1c-0.2-0.1-0.4-0.1-0.5-0.2l-0.2-0.1 c-0.2-0.1-0.5-0.4-0.7-0.7s-0.5-0.6-0.7-0.8c-0.2-0.2-0.4-0.4-0.6-0.6C14.8,15,14.6,15,14.4,15c0,0,0,0-0.1,0c0,0-0.1,0.1-0.2,0.2 c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.1-0.1,0.3-0.2,0.5c-0.1,0.2-0.1,0.5-0.1,0.8c0,0.1,0,0.2,0,0.3c0,0.1-0.1,0.2-0.1,0.2l0,0.1 c-0.1,0.1-0.3,0.2-0.6,0.2h-1.2c-0.5,0-1,0-1.5-0.2c-0.5-0.1-1-0.3-1.4-0.6s-0.7-0.5-1.1-0.7s-0.6-0.4-0.7-0.6l-0.3-0.3 c-0.1-0.1-0.2-0.2-0.3-0.3s-0.4-0.5-0.7-0.9s-0.7-1-1.1-1.6c-0.4-0.6-0.8-1.3-1.3-2.2C2.9,9.4,2.5,8.5,2.1,7.5C2,7.4,2,7.3,2,7.2 c0-0.1,0-0.1,0-0.2l0-0.1c0.1-0.1,0.3-0.2,0.6-0.2l2.9,0c0.1,0,0.2,0,0.2,0.1S5.9,6.9,5.9,7L6,7c0.1,0.1,0.2,0.2,0.3,0.3 C6.4,7.7,6.5,8,6.7,8.4C6.9,8.8,7,9,7.1,9.2l0.2,0.3c0.2,0.4,0.4,0.8,0.6,1.1c0.2,0.3,0.4,0.5,0.5,0.7s0.3,0.3,0.4,0.4 c0.1,0.1,0.3,0.1,0.4,0.1c0.1,0,0.2,0,0.3-0.1c0,0,0,0,0.1-0.1c0,0,0.1-0.1,0.1-0.2c0.1-0.1,0.1-0.3,0.1-0.5c0-0.2,0.1-0.5,0.1-0.8 c0-0.4,0-0.8,0-1.3c0-0.3,0-0.5-0.1-0.8c0-0.2-0.1-0.4-0.1-0.5L9.6,7.6C9.4,7.3,9.1,7.2,8.7,7.1C8.6,7.1,8.6,7,8.7,6.9 C8.9,6.7,9,6.6,9.1,6.5c0.4-0.2,1.2-0.3,2.5-0.3c0.6,0,1,0.1,1.4,0.1c0.1,0,0.3,0.1,0.3,0.1c0.1,0.1,0.2,0.1,0.2,0.3 c0,0.1,0.1,0.2,0.1,0.3s0,0.3,0,0.5c0,0.2,0,0.4,0,0.6c0,0.2,0,0.4,0,0.7c0,0.3,0,0.6,0,0.9c0,0.1,0,0.2,0,0.4c0,0.2,0,0.4,0,0.5 c0,0.1,0,0.3,0,0.4s0.1,0.3,0.1,0.4c0.1,0.1,0.1,0.2,0.2,0.3c0.1,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3-0.1c0.1-0.1,0.2-0.2,0.4-0.4 s0.3-0.4,0.5-0.7c0.2-0.3,0.5-0.7,0.7-1.1c0.4-0.7,0.8-1.5,1.1-2.3c0-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1-0.1,0.1-0.1l0,0l0.1,0 c0,0,0,0,0.1,0s0.2,0,0.2,0l3,0c0.3,0,0.5,0,0.7,0S21.9,7,21.9,7L22,7.1z"></path></svg>',

			'whatsapp'   => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M16.75,13.96C17,14.09 17.16,14.16 17.21,14.26C17.27,14.37 17.25,14.87 17,15.44C16.8,16 15.76,16.54 15.3,16.56C14.84,16.58 14.83,16.92 12.34,15.83C9.85,14.74 8.35,12.08 8.23,11.91C8.11,11.74 7.27,10.53 7.31,9.3C7.36,8.08 8,7.5 8.26,7.26C8.5,7 8.77,6.97 8.94,7H9.41C9.56,7 9.77,6.94 9.96,7.45L10.65,9.32C10.71,9.45 10.75,9.6 10.66,9.76L10.39,10.17L10,10.59C9.88,10.71 9.74,10.84 9.88,11.09C10,11.35 10.5,12.18 11.2,12.87C12.11,13.75 12.91,14.04 13.15,14.17C13.39,14.31 13.54,14.29 13.69,14.13L14.5,13.19C14.69,12.94 14.85,13 15.08,13.08L16.75,13.96M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C10.03,22 8.2,21.43 6.65,20.45L2,22L3.55,17.35C2.57,15.8 2,13.97 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12C4,13.72 4.54,15.31 5.46,16.61L4.5,19.5L7.39,18.54C8.69,19.46 10.28,20 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
</svg>',

			// phpcs:disable WordPress.WP.CapitalPDangit.Misspelled
			'wordpress'  => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12.158,12.786L9.46,20.625c0.806,0.237,1.657,0.366,2.54,0.366c1.047,0,2.051-0.181,2.986-0.51 c-0.024-0.038-0.046-0.079-0.065-0.124L12.158,12.786z M3.009,12c0,3.559,2.068,6.634,5.067,8.092L3.788,8.341 C3.289,9.459,3.009,10.696,3.009,12z M18.069,11.546c0-1.112-0.399-1.881-0.741-2.48c-0.456-0.741-0.883-1.368-0.883-2.109 c0-0.826,0.627-1.596,1.51-1.596c0.04,0,0.078,0.005,0.116,0.007C16.472,3.904,14.34,3.009,12,3.009 c-3.141,0-5.904,1.612-7.512,4.052c0.211,0.007,0.41,0.011,0.579,0.011c0.94,0,2.396-0.114,2.396-0.114 C7.947,6.93,8.004,7.642,7.52,7.699c0,0-0.487,0.057-1.029,0.085l3.274,9.739l1.968-5.901l-1.401-3.838 C9.848,7.756,9.389,7.699,9.389,7.699C8.904,7.67,8.961,6.93,9.446,6.958c0,0,1.484,0.114,2.368,0.114 c0.94,0,2.397-0.114,2.397-0.114c0.485-0.028,0.542,0.684,0.057,0.741c0,0-0.488,0.057-1.029,0.085l3.249,9.665l0.897-2.996 C17.841,13.284,18.069,12.316,18.069,11.546z M19.889,7.686c0.039,0.286,0.06,0.593,0.06,0.924c0,0.912-0.171,1.938-0.684,3.22 l-2.746,7.94c2.673-1.558,4.47-4.454,4.47-7.771C20.991,10.436,20.591,8.967,19.889,7.686z M12,22C6.486,22,2,17.514,2,12 C2,6.486,6.486,2,12,2c5.514,0,10,4.486,10,10C22,17.514,17.514,22,12,22z"></path></svg>',

			'yelp'       => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12.271,16.718v1.417q-.011,3.257-.067,3.4a.707.707,0,0,1-.569.446,4.637,4.637,0,0,1-2.024-.424A4.609,4.609,0,0,1,7.8,20.565a.844.844,0,0,1-.19-.4.692.692,0,0,1,.044-.29,3.181,3.181,0,0,1,.379-.524q.335-.412,2.019-2.409.011,0,.669-.781a.757.757,0,0,1,.44-.274.965.965,0,0,1,.552.039.945.945,0,0,1,.418.324.732.732,0,0,1,.139.468Zm-1.662-2.8a.783.783,0,0,1-.58.781l-1.339.435q-3.067.981-3.257.981a.711.711,0,0,1-.6-.4,2.636,2.636,0,0,1-.19-.836,9.134,9.134,0,0,1,.011-1.857,3.559,3.559,0,0,1,.335-1.389.659.659,0,0,1,.625-.357,22.629,22.629,0,0,1,2.253.859q.781.324,1.283.524l.937.379a.771.771,0,0,1,.4.34A.982.982,0,0,1,10.609,13.917Zm9.213,3.313a4.467,4.467,0,0,1-1.021,1.8,4.559,4.559,0,0,1-1.512,1.417.671.671,0,0,1-.7-.078q-.156-.112-2.052-3.2l-.524-.859a.761.761,0,0,1-.128-.513.957.957,0,0,1,.217-.513.774.774,0,0,1,.926-.29q.011.011,1.327.446,2.264.736,2.7.887a2.082,2.082,0,0,1,.524.229.673.673,0,0,1,.245.68Zm-7.5-7.049q.056,1.137-.6,1.361-.647.19-1.272-.792L6.237,4.08a.7.7,0,0,1,.212-.691,5.788,5.788,0,0,1,2.314-1,5.928,5.928,0,0,1,2.5-.352.681.681,0,0,1,.547.5q.034.2.245,3.407T12.327,10.181Zm7.384,1.2a.679.679,0,0,1-.29.658q-.167.112-3.67.959-.747.167-1.015.257l.011-.022a.769.769,0,0,1-.513-.044.914.914,0,0,1-.413-.357.786.786,0,0,1,0-.971q.011-.011.836-1.137,1.394-1.908,1.673-2.275a2.423,2.423,0,0,1,.379-.435A.7.7,0,0,1,17.435,8a4.482,4.482,0,0,1,1.372,1.489,4.81,4.81,0,0,1,.9,1.868v.034Z"></path></svg>',

			'youtube'    => '<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg>',

		);

	}
}
PKg2\�@�Ĝ�"class-twentytwenty-walker-page.phpnu&1i�<?php
/**
 * Custom page walker for this theme.
 *
 * @package WordPress
 * @subpackage Twenty_Twenty
 * @since Twenty Twenty 1.0
 */

if ( ! class_exists( 'TwentyTwenty_Walker_Page' ) ) {
	/**
	 * CUSTOM PAGE WALKER
	 * A custom walker for pages.
	 *
	 * @since Twenty Twenty 1.0
	 */
	class TwentyTwenty_Walker_Page extends Walker_Page {

		/**
		 * Outputs the beginning of the current element in the tree.
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @see Walker::start_el()
		 *
		 * @param string  $output       Used to append additional content. Passed by reference.
		 * @param WP_Post $page         Page data object.
		 * @param int     $depth        Optional. Depth of page. Used for padding. Default 0.
		 * @param array   $args         Optional. Array of arguments. Default empty array.
		 * @param int     $current_page Optional. Page ID. Default 0.
		 */
		public function start_el( &$output, $page, $depth = 0, $args = array(), $current_page = 0 ) {

			if ( isset( $args['item_spacing'] ) && 'preserve' === $args['item_spacing'] ) {
				$t = "\t";
				$n = "\n";
			} else {
				$t = '';
				$n = '';
			}
			if ( $depth ) {
				$indent = str_repeat( $t, $depth );
			} else {
				$indent = '';
			}

			$css_class = array( 'page_item', 'page-item-' . $page->ID );

			if ( isset( $args['pages_with_children'][ $page->ID ] ) ) {
				$css_class[] = 'page_item_has_children';
			}

			if ( ! empty( $current_page ) ) {
				$_current_page = get_post( $current_page );
				if ( $_current_page && in_array( $page->ID, $_current_page->ancestors, true ) ) {
					$css_class[] = 'current_page_ancestor';
				}
				if ( $page->ID === $current_page ) {
					$css_class[] = 'current_page_item';
				} elseif ( $_current_page && $page->ID === $_current_page->post_parent ) {
					$css_class[] = 'current_page_parent';
				}
			} elseif ( get_option( 'page_for_posts' ) === $page->ID ) {
				$css_class[] = 'current_page_parent';
			}

			/** This filter is documented in wp-includes/class-walker-page.php */
			$css_classes = implode( ' ', apply_filters( 'page_css_class', $css_class, $page, $depth, $args, $current_page ) );
			$css_classes = $css_classes ? ' class="' . esc_attr( $css_classes ) . '"' : '';

			if ( '' === $page->post_title ) {
				/* translators: %d: ID of a post. */
				$page->post_title = sprintf( __( '#%d (no title)', 'twentytwenty' ), $page->ID );
			}

			$args['link_before'] = empty( $args['link_before'] ) ? '' : $args['link_before'];
			$args['link_after']  = empty( $args['link_after'] ) ? '' : $args['link_after'];

			$atts                 = array();
			$atts['href']         = get_permalink( $page->ID );
			$atts['aria-current'] = ( $page->ID === $current_page ) ? 'page' : '';

			/** This filter is documented in wp-includes/class-walker-page.php */
			$atts = apply_filters( 'page_menu_link_attributes', $atts, $page, $depth, $args, $current_page );

			$attributes = '';
			foreach ( $atts as $attr => $value ) {
				if ( ! empty( $value ) ) {
					$value       = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
					$attributes .= ' ' . $attr . '="' . $value . '"';
				}
			}

			$args['list_item_before'] = '';
			$args['list_item_after']  = '';

			// Wrap the link in a div and append a sub menu toggle.
			if ( isset( $args['show_toggles'] ) && true === $args['show_toggles'] ) {
				// Wrap the menu item link contents in a div, used for positioning.
				$args['list_item_before'] = '<div class="ancestor-wrapper">';
				$args['list_item_after']  = '';

				// Add a toggle to items with children.
				if ( isset( $args['pages_with_children'][ $page->ID ] ) ) {

					$toggle_target_string = '.menu-modal .page-item-' . $page->ID . ' > ul';
					$toggle_duration      = twentytwenty_toggle_duration();

					// Add the sub menu toggle.
					$args['list_item_after'] .= '<button class="toggle sub-menu-toggle fill-children-current-color" data-toggle-target="' . $toggle_target_string . '" data-toggle-type="slidetoggle" data-toggle-duration="' . absint( $toggle_duration ) . '" aria-expanded="false"><span class="screen-reader-text">' . __( 'Show sub menu', 'twentytwenty' ) . '</span>' . twentytwenty_get_theme_svg( 'chevron-down' ) . '</button>';

				}

				// Close the wrapper.
				$args['list_item_after'] .= '</div><!-- .ancestor-wrapper -->';
			}

			// Add icons to menu items with children.
			if ( isset( $args['show_sub_menu_icons'] ) && true === $args['show_sub_menu_icons'] ) {
				if ( isset( $args['pages_with_children'][ $page->ID ] ) ) {
					$args['list_item_after'] = '<span class="icon"></span>';
				}
			}

			$output .= $indent . sprintf(
				'<li%s>%s<a%s>%s%s%s</a>%s',
				$css_classes,
				$args['list_item_before'],
				$attributes,
				$args['link_before'],
				/** This filter is documented in wp-includes/post-template.php */
				apply_filters( 'the_title', $page->post_title, $page->ID ),
				$args['link_after'],
				$args['list_item_after']
			);

			if ( ! empty( $args['show_date'] ) ) {
				if ( 'modified' === $args['show_date'] ) {
					$time = $page->post_modified;
				} else {
					$time = $page->post_date;
				}

				$date_format = empty( $args['date_format'] ) ? '' : $args['date_format'];
				$output     .= ' ' . mysql2date( $date_format, $time );
			}
		}
	}
}
PKg2\�+ɿ�%class-twentytwenty-walker-comment.phpnu&1i�<?php
/**
 * Custom comment walker for this theme.
 *
 * @package WordPress
 * @subpackage Twenty_Twenty
 * @since Twenty Twenty 1.0
 */

if ( ! class_exists( 'TwentyTwenty_Walker_Comment' ) ) {
	/**
	 * CUSTOM COMMENT WALKER
	 * A custom walker for comments, based on the walker in Twenty Nineteen.
	 *
	 * @since Twenty Twenty 1.0
	 */
	class TwentyTwenty_Walker_Comment extends Walker_Comment {

		/**
		 * Outputs a comment in the HTML5 format.
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @see wp_list_comments()
		 * @see https://developer.wordpress.org/reference/functions/get_comment_author_url/
		 * @see https://developer.wordpress.org/reference/functions/get_comment_author/
		 * @see https://developer.wordpress.org/reference/functions/get_avatar/
		 * @see https://developer.wordpress.org/reference/functions/get_comment_reply_link/
		 * @see https://developer.wordpress.org/reference/functions/get_edit_comment_link/
		 *
		 * @param WP_Comment $comment Comment to display.
		 * @param int        $depth   Depth of the current comment.
		 * @param array      $args    An array of arguments.
		 */
		protected function html5_comment( $comment, $depth, $args ) {

			$tag = ( 'div' === $args['style'] ) ? 'div' : 'li';

			?>
			<<?php echo $tag; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static output ?> id="comment-<?php comment_ID(); ?>" <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?>>
				<article id="div-comment-<?php comment_ID(); ?>" class="comment-body">
					<footer class="comment-meta">
						<div class="comment-author vcard">
							<?php
							$comment_author_url = get_comment_author_url( $comment );
							$comment_author     = get_comment_author( $comment );
							$avatar             = get_avatar( $comment, $args['avatar_size'] );
							if ( 0 !== $args['avatar_size'] ) {
								if ( empty( $comment_author_url ) ) {
									echo wp_kses_post( $avatar );
								} else {
									printf( '<a href="%s" rel="external nofollow" class="url">', $comment_author_url ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped --Escaped in https://developer.wordpress.org/reference/functions/get_comment_author_url/
									echo wp_kses_post( $avatar );
								}
							}

							printf(
								'<span class="fn">%1$s</span><span class="screen-reader-text says">%2$s</span>',
								esc_html( $comment_author ),
								__( 'says:', 'twentytwenty' )
							);

							if ( ! empty( $comment_author_url ) ) {
								echo '</a>';
							}
							?>
						</div><!-- .comment-author -->

						<div class="comment-metadata">
							<?php
							/* translators: 1: Comment date, 2: Comment time. */
							$comment_timestamp = sprintf( __( '%1$s at %2$s', 'twentytwenty' ), get_comment_date( '', $comment ), get_comment_time() );

							printf(
								'<a href="%s"><time datetime="%s" title="%s">%s</time></a>',
								esc_url( get_comment_link( $comment, $args ) ),
								get_comment_time( 'c' ),
								esc_attr( $comment_timestamp ),
								esc_html( $comment_timestamp )
							);

							if ( get_edit_comment_link() ) {
								printf(
									' <span aria-hidden="true">&bull;</span> <a class="comment-edit-link" href="%s">%s</a>',
									esc_url( get_edit_comment_link() ),
									__( 'Edit', 'twentytwenty' )
								);
							}
							?>
						</div><!-- .comment-metadata -->

					</footer><!-- .comment-meta -->

					<div class="comment-content entry-content">

						<?php

						comment_text();

						if ( '0' === $comment->comment_approved ) {
							?>
							<p class="comment-awaiting-moderation"><?php _e( 'Your comment is awaiting moderation.', 'twentytwenty' ); ?></p>
							<?php
						}

						?>

					</div><!-- .comment-content -->

					<?php

					$comment_reply_link = get_comment_reply_link(
						array_merge(
							$args,
							array(
								'add_below' => 'div-comment',
								'depth'     => $depth,
								'max_depth' => $args['max_depth'],
								'before'    => '<span class="comment-reply">',
								'after'     => '</span>',
							)
						)
					);

					$by_post_author = twentytwenty_is_comment_by_post_author( $comment );

					if ( $comment_reply_link || $by_post_author ) {
						?>

						<footer class="comment-footer-meta">

							<?php
							if ( $comment_reply_link ) {
								echo $comment_reply_link; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Link is escaped in https://developer.wordpress.org/reference/functions/get_comment_reply_link/
							}
							if ( $by_post_author ) {
								echo '<span class="by-post-author">' . __( 'By Post Author', 'twentytwenty' ) . '</span>';
							}
							?>

						</footer>

						<?php
					}
					?>

				</article><!-- .comment-body -->

			<?php
		}
	}
}
PKg2\r�y99 class-twentytwenty-customize.phpnu&1i�<?php
/**
 * Customizer settings for this theme.
 *
 * @package WordPress
 * @subpackage Twenty_Twenty
 * @since Twenty Twenty 1.0
 */

if ( ! class_exists( 'TwentyTwenty_Customize' ) ) {
	/**
	 * CUSTOMIZER SETTINGS
	 *
	 * @since Twenty Twenty 1.0
	 */
	class TwentyTwenty_Customize {

		/**
		 * Register customizer options.
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @param WP_Customize_Manager $wp_customize Theme Customizer object.
		 */
		public static function register( $wp_customize ) {

			/**
			 * Site Title & Description.
			 * */
			$wp_customize->get_setting( 'blogname' )->transport        = 'postMessage';
			$wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage';

			$wp_customize->selective_refresh->add_partial(
				'blogname',
				array(
					'selector'        => '.site-title a',
					'render_callback' => 'twentytwenty_customize_partial_blogname',
				)
			);

			$wp_customize->selective_refresh->add_partial(
				'blogdescription',
				array(
					'selector'        => '.site-description',
					'render_callback' => 'twentytwenty_customize_partial_blogdescription',
				)
			);

			$wp_customize->selective_refresh->add_partial(
				'custom_logo',
				array(
					'selector'        => '.header-titles [class*=site-]:not(.site-description)',
					'render_callback' => 'twentytwenty_customize_partial_site_logo',
				)
			);

			$wp_customize->selective_refresh->add_partial(
				'retina_logo',
				array(
					'selector'        => '.header-titles [class*=site-]:not(.site-description)',
					'render_callback' => 'twentytwenty_customize_partial_site_logo',
				)
			);

			/**
			 * Site Identity
			 */

			/* 2X Header Logo ---------------- */
			$wp_customize->add_setting(
				'retina_logo',
				array(
					'capability'        => 'edit_theme_options',
					'sanitize_callback' => array( __CLASS__, 'sanitize_checkbox' ),
					'transport'         => 'postMessage',
				)
			);

			$wp_customize->add_control(
				'retina_logo',
				array(
					'type'        => 'checkbox',
					'section'     => 'title_tagline',
					'priority'    => 10,
					'label'       => __( 'Retina logo', 'twentytwenty' ),
					'description' => __( 'Scales the logo to half its uploaded size, making it sharp on high-res screens.', 'twentytwenty' ),
				)
			);

			// Header & Footer Background Color.
			$wp_customize->add_setting(
				'header_footer_background_color',
				array(
					'default'           => '#ffffff',
					'sanitize_callback' => 'sanitize_hex_color',
					'transport'         => 'postMessage',
				)
			);

			$wp_customize->add_control(
				new WP_Customize_Color_Control(
					$wp_customize,
					'header_footer_background_color',
					array(
						'label'   => __( 'Header &amp; Footer Background Color', 'twentytwenty' ),
						'section' => 'colors',
					)
				)
			);

			// Enable picking an accent color.
			$wp_customize->add_setting(
				'accent_hue_active',
				array(
					'capability'        => 'edit_theme_options',
					'sanitize_callback' => array( __CLASS__, 'sanitize_select' ),
					'transport'         => 'postMessage',
					'default'           => 'default',
				)
			);

			$wp_customize->add_control(
				'accent_hue_active',
				array(
					'type'    => 'radio',
					'section' => 'colors',
					'label'   => __( 'Primary Color', 'twentytwenty' ),
					'choices' => array(
						'default' => _x( 'Default', 'color', 'twentytwenty' ),
						'custom'  => _x( 'Custom', 'color', 'twentytwenty' ),
					),
				)
			);

			/**
			 * Implementation for the accent color.
			 * This is different to all other color options because of the accessibility enhancements.
			 * The control is a hue-only colorpicker, and there is a separate setting that holds values
			 * for other colors calculated based on the selected hue and various background-colors on the page.
			 *
			 * @since Twenty Twenty 1.0
			 */

			// Add the setting for the hue colorpicker.
			$wp_customize->add_setting(
				'accent_hue',
				array(
					'default'           => 344,
					'type'              => 'theme_mod',
					'sanitize_callback' => 'absint',
					'transport'         => 'postMessage',
				)
			);

			// Add setting to hold colors derived from the accent hue.
			$wp_customize->add_setting(
				'accent_accessible_colors',
				array(
					'default'           => array(
						'content'       => array(
							'text'      => '#000000',
							'accent'    => '#cd2653',
							'secondary' => '#6d6d6d',
							'borders'   => '#dcd7ca',
						),
						'header-footer' => array(
							'text'      => '#000000',
							'accent'    => '#cd2653',
							'secondary' => '#6d6d6d',
							'borders'   => '#dcd7ca',
						),
					),
					'type'              => 'theme_mod',
					'transport'         => 'postMessage',
					'sanitize_callback' => array( __CLASS__, 'sanitize_accent_accessible_colors' ),
				)
			);

			// Add the hue-only colorpicker for the accent color.
			$wp_customize->add_control(
				new WP_Customize_Color_Control(
					$wp_customize,
					'accent_hue',
					array(
						'section'         => 'colors',
						'settings'        => 'accent_hue',
						'description'     => __( 'Apply a custom color for links, buttons, featured images.', 'twentytwenty' ),
						'mode'            => 'hue',
						'active_callback' => function() use ( $wp_customize ) {
							return ( 'custom' === $wp_customize->get_setting( 'accent_hue_active' )->value() );
						},
					)
				)
			);

			// Update background color with postMessage, so inline CSS output is updated as well.
			$wp_customize->get_setting( 'background_color' )->transport = 'postMessage';

			/**
			 * Theme Options
			 */

			$wp_customize->add_section(
				'options',
				array(
					'title'      => __( 'Theme Options', 'twentytwenty' ),
					'priority'   => 40,
					'capability' => 'edit_theme_options',
				)
			);

			/* Enable Header Search ----------------------------------------------- */

			$wp_customize->add_setting(
				'enable_header_search',
				array(
					'capability'        => 'edit_theme_options',
					'default'           => true,
					'sanitize_callback' => array( __CLASS__, 'sanitize_checkbox' ),
				)
			);

			$wp_customize->add_control(
				'enable_header_search',
				array(
					'type'     => 'checkbox',
					'section'  => 'options',
					'priority' => 10,
					'label'    => __( 'Show search in header', 'twentytwenty' ),
				)
			);

			/* Show author bio ---------------------------------------------------- */

			$wp_customize->add_setting(
				'show_author_bio',
				array(
					'capability'        => 'edit_theme_options',
					'default'           => true,
					'sanitize_callback' => array( __CLASS__, 'sanitize_checkbox' ),
				)
			);

			$wp_customize->add_control(
				'show_author_bio',
				array(
					'type'     => 'checkbox',
					'section'  => 'options',
					'priority' => 10,
					'label'    => __( 'Show author bio', 'twentytwenty' ),
				)
			);

			/* Display full content or excerpts on the blog and archives --------- */

			$wp_customize->add_setting(
				'blog_content',
				array(
					'capability'        => 'edit_theme_options',
					'default'           => 'full',
					'sanitize_callback' => array( __CLASS__, 'sanitize_select' ),
				)
			);

			$wp_customize->add_control(
				'blog_content',
				array(
					'type'     => 'radio',
					'section'  => 'options',
					'priority' => 10,
					'label'    => __( 'On archive pages, posts show:', 'twentytwenty' ),
					'choices'  => array(
						'full'    => __( 'Full text', 'twentytwenty' ),
						'summary' => __( 'Summary', 'twentytwenty' ),
					),
				)
			);

			/**
			 * Template: Cover Template.
			 */
			$wp_customize->add_section(
				'cover_template_options',
				array(
					'title'       => __( 'Cover Template', 'twentytwenty' ),
					'capability'  => 'edit_theme_options',
					'description' => __( 'Settings for the "Cover Template" page template. Add a featured image to use as background.', 'twentytwenty' ),
					'priority'    => 42,
				)
			);

			/* Overlay Fixed Background ------ */

			$wp_customize->add_setting(
				'cover_template_fixed_background',
				array(
					'capability'        => 'edit_theme_options',
					'default'           => true,
					'sanitize_callback' => array( __CLASS__, 'sanitize_checkbox' ),
					'transport'         => 'postMessage',
				)
			);

			$wp_customize->add_control(
				'cover_template_fixed_background',
				array(
					'type'        => 'checkbox',
					'section'     => 'cover_template_options',
					'label'       => __( 'Fixed Background Image', 'twentytwenty' ),
					'description' => __( 'Creates a parallax effect when the visitor scrolls.', 'twentytwenty' ),
				)
			);

			$wp_customize->selective_refresh->add_partial(
				'cover_template_fixed_background',
				array(
					'selector' => '.cover-header',
					'type'     => 'cover_fixed',
				)
			);

			/* Separator --------------------- */

			$wp_customize->add_setting(
				'cover_template_separator_1',
				array(
					'sanitize_callback' => 'wp_filter_nohtml_kses',
				)
			);

			$wp_customize->add_control(
				new TwentyTwenty_Separator_Control(
					$wp_customize,
					'cover_template_separator_1',
					array(
						'section' => 'cover_template_options',
					)
				)
			);

			/* Overlay Background Color ------ */

			$wp_customize->add_setting(
				'cover_template_overlay_background_color',
				array(
					'default'           => twentytwenty_get_color_for_area( 'content', 'accent' ),
					'sanitize_callback' => 'sanitize_hex_color',
				)
			);

			$wp_customize->add_control(
				new WP_Customize_Color_Control(
					$wp_customize,
					'cover_template_overlay_background_color',
					array(
						'label'       => __( 'Overlay Background Color', 'twentytwenty' ),
						'description' => __( 'The color used for the overlay. Defaults to the accent color.', 'twentytwenty' ),
						'section'     => 'cover_template_options',
					)
				)
			);

			/* Overlay Text Color ------------ */

			$wp_customize->add_setting(
				'cover_template_overlay_text_color',
				array(
					'default'           => '#ffffff',
					'sanitize_callback' => 'sanitize_hex_color',
				)
			);

			$wp_customize->add_control(
				new WP_Customize_Color_Control(
					$wp_customize,
					'cover_template_overlay_text_color',
					array(
						'label'       => __( 'Overlay Text Color', 'twentytwenty' ),
						'description' => __( 'The color used for the text in the overlay.', 'twentytwenty' ),
						'section'     => 'cover_template_options',
					)
				)
			);

			/* Overlay Color Opacity --------- */

			$wp_customize->add_setting(
				'cover_template_overlay_opacity',
				array(
					'default'           => 80,
					'sanitize_callback' => 'absint',
					'transport'         => 'postMessage',
				)
			);

			$wp_customize->add_control(
				'cover_template_overlay_opacity',
				array(
					'label'       => __( 'Overlay Opacity', 'twentytwenty' ),
					'description' => __( 'Make sure that the contrast is high enough so that the text is readable.', 'twentytwenty' ),
					'section'     => 'cover_template_options',
					'type'        => 'range',
					'input_attrs' => twentytwenty_customize_opacity_range(),
				)
			);

			$wp_customize->selective_refresh->add_partial(
				'cover_template_overlay_opacity',
				array(
					'selector' => '.cover-color-overlay',
					'type'     => 'cover_opacity',
				)
			);
		}

		/**
		 * Sanitization callback for the "accent_accessible_colors" setting.
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @param array $value The value we want to sanitize.
		 * @return array Returns sanitized value. Each item in the array gets sanitized separately.
		 */
		public static function sanitize_accent_accessible_colors( $value ) {

			// Make sure the value is an array. Do not typecast, use empty array as fallback.
			$value = is_array( $value ) ? $value : array();

			// Loop values.
			foreach ( $value as $area => $values ) {
				foreach ( $values as $context => $color_val ) {
					$value[ $area ][ $context ] = sanitize_hex_color( $color_val );
				}
			}

			return $value;
		}

		/**
		 * Sanitize select.
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @param string $input   The input from the setting.
		 * @param object $setting The selected setting.
		 * @return string The input from the setting or the default setting.
		 */
		public static function sanitize_select( $input, $setting ) {
			$input   = sanitize_key( $input );
			$choices = $setting->manager->get_control( $setting->id )->choices;
			return ( array_key_exists( $input, $choices ) ? $input : $setting->default );
		}

		/**
		 * Sanitize boolean for checkbox.
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @param bool $checked Whether or not a box is checked.
		 * @return bool
		 */
		public static function sanitize_checkbox( $checked ) {
			return ( ( isset( $checked ) && true === $checked ) ? true : false );
		}

	}

	// Setup the Theme Customizer settings and controls.
	add_action( 'customize_register', array( 'TwentyTwenty_Customize', 'register' ) );

}

/**
 * PARTIAL REFRESH FUNCTIONS
 * */
if ( ! function_exists( 'twentytwenty_customize_partial_blogname' ) ) {
	/**
	 * Render the site title for the selective refresh partial.
	 *
	 * @since Twenty Twenty 1.0
	 */
	function twentytwenty_customize_partial_blogname() {
		bloginfo( 'name' );
	}
}

if ( ! function_exists( 'twentytwenty_customize_partial_blogdescription' ) ) {
	/**
	 * Render the site description for the selective refresh partial.
	 *
	 * @since Twenty Twenty 1.0
	 */
	function twentytwenty_customize_partial_blogdescription() {
		bloginfo( 'description' );
	}
}

if ( ! function_exists( 'twentytwenty_customize_partial_site_logo' ) ) {
	/**
	 * Render the site logo for the selective refresh partial.
	 *
	 * Doing it this way so we don't have issues with `render_callback`'s arguments.
	 *
	 * @since Twenty Twenty 1.0
	 */
	function twentytwenty_customize_partial_site_logo() {
		twentytwenty_site_logo();
	}
}


/**
 * Input attributes for cover overlay opacity option.
 *
 * @since Twenty Twenty 1.0
 *
 * @return array Array containing attribute names and their values.
 */
function twentytwenty_customize_opacity_range() {
	/**
	 * Filters the input attributes for opacity.
	 *
	 * @since Twenty Twenty 1.0
	 *
	 * @param array $attrs {
	 *     The attributes.
	 *
	 *     @type int $min  Minimum value.
	 *     @type int $max  Maximum value.
	 *     @type int $step Interval between numbers.
	 * }
	 */
	return apply_filters(
		'twentytwenty_customize_opacity_range',
		array(
			'min'  => 0,
			'max'  => 90,
			'step' => 5,
		)
	);
}
PKg2\��!!$class-twentytwenty-script-loader.phpnu&1i�<?php
/**
 * Javascript Loader Class
 *
 * Allow `async` and `defer` while enqueuing Javascript.
 *
 * Based on a solution in WP Rig.
 *
 * @package WordPress
 * @subpackage Twenty_Twenty
 * @since Twenty Twenty 1.0
 */

if ( ! class_exists( 'TwentyTwenty_Script_Loader' ) ) {
	/**
	 * A class that provides a way to add `async` or `defer` attributes to scripts.
	 *
	 * @since Twenty Twenty 1.0
	 */
	class TwentyTwenty_Script_Loader {

		/**
		 * Adds async/defer attributes to enqueued / registered scripts.
		 *
		 * If #12009 lands in WordPress, this function can no-op since it would be handled in core.
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @link https://core.trac.wordpress.org/ticket/12009
		 *
		 * @param string $tag    The script tag.
		 * @param string $handle The script handle.
		 * @return string Script HTML string.
		 */
		public function filter_script_loader_tag( $tag, $handle ) {
			foreach ( array( 'async', 'defer' ) as $attr ) {
				if ( ! wp_scripts()->get_data( $handle, $attr ) ) {
					continue;
				}
				// Prevent adding attribute when already added in #12009.
				if ( ! preg_match( ":\s$attr(=|>|\s):", $tag ) ) {
					$tag = preg_replace( ':(?=></script>):', " $attr", $tag, 1 );
				}
				// Only allow async or defer, not both.
				break;
			}
			return $tag;
		}

	}
}
PKg2\J���$$(class-twentytwenty-separator-control.phpnu&1i�<?php
/**
 * Customizer Separator Control settings for this theme.
 *
 * @package WordPress
 * @subpackage Twenty_Twenty
 * @since Twenty Twenty 1.0
 */

if ( class_exists( 'WP_Customize_Control' ) ) {

	if ( ! class_exists( 'TwentyTwenty_Separator_Control' ) ) {
		/**
		 * Separator Control.
		 *
		 * @since Twenty Twenty 1.0
		 */
		class TwentyTwenty_Separator_Control extends WP_Customize_Control {
			/**
			 * Render the hr.
			 *
			 * @since Twenty Twenty 1.0
			 */
			public function render_content() {
				echo '<hr/>';
			}

		}
	}
}
PKg2\��Bii*class-twentytwenty-non-latin-languages.phpnu&1i�<?php
/**
 * Non-latin language handling.
 *
 * Handle non-latin language styles.
 *
 * @package WordPress
 * @subpackage Twenty_Twenty
 * @since Twenty Twenty 1.0
 */

if ( ! class_exists( 'TwentyTwenty_Non_Latin_Languages' ) ) {
	/**
	 * Language handling.
	 *
	 * @since Twenty Twenty 1.0
	 */
	class TwentyTwenty_Non_Latin_Languages {

		/**
		 * Get custom CSS.
		 *
		 * Return CSS for non-latin language, if available, or null
		 *
		 * @since Twenty Twenty 1.0
		 *
		 * @param string $type Whether to return CSS for the "front-end", "block-editor", or "classic-editor".
		 * @return void
		 */
		public static function get_non_latin_css( $type = 'front-end' ) {

			// Fetch site locale.
			$locale = get_bloginfo( 'language' );

			/**
			 * Filters the fallback fonts for non-latin languages.
			 *
			 * @since Twenty Twenty 1.0
			 *
			 * @param array $font_family An array of locales and font families.
			 */
			$font_family = apply_filters(
				'twentytwenty_get_localized_font_family_types',
				array(

					// Arabic.
					'ar'    => array( 'Tahoma', 'Arial', 'sans-serif' ),
					'ary'   => array( 'Tahoma', 'Arial', 'sans-serif' ),
					'azb'   => array( 'Tahoma', 'Arial', 'sans-serif' ),
					'ckb'   => array( 'Tahoma', 'Arial', 'sans-serif' ),
					'fa-IR' => array( 'Tahoma', 'Arial', 'sans-serif' ),
					'haz'   => array( 'Tahoma', 'Arial', 'sans-serif' ),
					'ps'    => array( 'Tahoma', 'Arial', 'sans-serif' ),

					// Chinese Simplified (China) - Noto Sans SC.
					'zh-CN' => array( '\'PingFang SC\'', '\'Helvetica Neue\'', '\'Microsoft YaHei New\'', '\'STHeiti Light\'', 'sans-serif' ),

					// Chinese Traditional (Taiwan) - Noto Sans TC.
					'zh-TW' => array( '\'PingFang TC\'', '\'Helvetica Neue\'', '\'Microsoft YaHei New\'', '\'STHeiti Light\'', 'sans-serif' ),

					// Chinese (Hong Kong) - Noto Sans HK.
					'zh-HK' => array( '\'PingFang HK\'', '\'Helvetica Neue\'', '\'Microsoft YaHei New\'', '\'STHeiti Light\'', 'sans-serif' ),

					// Cyrillic.
					'bel'   => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
					'bg-BG' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
					'kk'    => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
					'mk-MK' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
					'mn'    => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
					'ru-RU' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
					'sah'   => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
					'sr-RS' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
					'tt-RU' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
					'uk'    => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),

					// Devanagari.
					'bn-BD' => array( 'Arial', 'sans-serif' ),
					'hi-IN' => array( 'Arial', 'sans-serif' ),
					'mr'    => array( 'Arial', 'sans-serif' ),
					'ne-NP' => array( 'Arial', 'sans-serif' ),

					// Greek.
					'el'    => array( '\'Helvetica Neue\', Helvetica, Arial, sans-serif' ),

					// Gujarati.
					'gu'    => array( 'Arial', 'sans-serif' ),

					// Hebrew.
					'he-IL' => array( '\'Arial Hebrew\'', 'Arial', 'sans-serif' ),

					// Japanese.
					'ja'    => array( 'sans-serif' ),

					// Korean.
					'ko-KR' => array( '\'Apple SD Gothic Neo\'', '\'Malgun Gothic\'', '\'Nanum Gothic\'', 'Dotum', 'sans-serif' ),

					// Thai.
					'th'    => array( '\'Sukhumvit Set\'', '\'Helvetica Neue\'', 'Helvetica', 'Arial', 'sans-serif' ),

					// Vietnamese.
					'vi'    => array( '\'Libre Franklin\'', 'sans-serif' ),

				)
			);

			// Return if the selected language has no fallback fonts.
			if ( empty( $font_family[ $locale ] ) ) {
				return;
			}

			/**
			 * Filters the elements to apply fallback fonts to.
			 *
			 * @since Twenty Twenty 1.0
			 *
			 * @param array $elements An array of elements for "front-end", "block-editor", or "classic-editor".
			 */
			$elements = apply_filters(
				'twentytwenty_get_localized_font_family_elements',
				array(
					'front-end'      => array( 'body', 'input', 'textarea', 'button', '.button', '.faux-button', '.wp-block-button__link', '.wp-block-file__button', '.has-drop-cap:not(:focus)::first-letter', '.has-drop-cap:not(:focus)::first-letter', '.entry-content .wp-block-archives', '.entry-content .wp-block-categories', '.entry-content .wp-block-cover-image', '.entry-content .wp-block-latest-comments', '.entry-content .wp-block-latest-posts', '.entry-content .wp-block-pullquote', '.entry-content .wp-block-quote.is-large', '.entry-content .wp-block-quote.is-style-large', '.entry-content .wp-block-archives *', '.entry-content .wp-block-categories *', '.entry-content .wp-block-latest-posts *', '.entry-content .wp-block-latest-comments *', '.entry-content p', '.entry-content ol', '.entry-content ul', '.entry-content dl', '.entry-content dt', '.entry-content cite', '.entry-content figcaption', '.entry-content .wp-caption-text', '.comment-content p', '.comment-content ol', '.comment-content ul', '.comment-content dl', '.comment-content dt', '.comment-content cite', '.comment-content figcaption', '.comment-content .wp-caption-text', '.widget_text p', '.widget_text ol', '.widget_text ul', '.widget_text dl', '.widget_text dt', '.widget-content .rssSummary', '.widget-content cite', '.widget-content figcaption', '.widget-content .wp-caption-text' ),
					'block-editor'   => array( '.editor-styles-wrapper > *', '.editor-styles-wrapper p', '.editor-styles-wrapper ol', '.editor-styles-wrapper ul', '.editor-styles-wrapper dl', '.editor-styles-wrapper dt', '.editor-post-title__block .editor-post-title__input', '.editor-styles-wrapper .wp-block h1', '.editor-styles-wrapper .wp-block h2', '.editor-styles-wrapper .wp-block h3', '.editor-styles-wrapper .wp-block h4', '.editor-styles-wrapper .wp-block h5', '.editor-styles-wrapper .wp-block h6', '.editor-styles-wrapper .has-drop-cap:not(:focus)::first-letter', '.editor-styles-wrapper cite', '.editor-styles-wrapper figcaption', '.editor-styles-wrapper .wp-caption-text' ),
					'classic-editor' => array( 'body#tinymce.wp-editor', 'body#tinymce.wp-editor p', 'body#tinymce.wp-editor ol', 'body#tinymce.wp-editor ul', 'body#tinymce.wp-editor dl', 'body#tinymce.wp-editor dt', 'body#tinymce.wp-editor figcaption', 'body#tinymce.wp-editor .wp-caption-text', 'body#tinymce.wp-editor .wp-caption-dd', 'body#tinymce.wp-editor cite', 'body#tinymce.wp-editor table' ),
				)
			);

			// Return if the specified type doesn't exist.
			if ( empty( $elements[ $type ] ) ) {
				return;
			}

			// Return the specified styles.
			return twentytwenty_generate_css( implode( ',', $elements[ $type ] ), 'font-family', implode( ',', $font_family[ $locale ] ), null, null, false );

		}
	}
}
PK�{2\k�fNpackage/index.phpnu�[���<?php
//silentPK�{2\�S���p�ppackage/class.pack.archive.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
// Exit if accessed directly
if (!defined('DUPLICATOR_VERSION')) exit;

require_once (DUPLICATOR_PLUGIN_PATH.'classes/package/duparchive/class.pack.archive.duparchive.php');
require_once (DUPLICATOR_PLUGIN_PATH.'classes/package/class.pack.archive.filters.php');
require_once (DUPLICATOR_PLUGIN_PATH.'classes/package/class.pack.archive.zip.php');
require_once (DUPLICATOR_PLUGIN_PATH.'lib/forceutf8/Encoding.php');

/**
 * Class for handling archive setup and build process
 *
 * Standard: PSR-2 (almost)
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package DUP
 * @subpackage classes/package
 * @copyright (c) 2017, Snapcreek LLC
 * @license	https://opensource.org/licenses/GPL-3.0 GNU Public License
 *
 */
class DUP_Archive
{
    //PUBLIC
    public $FilterDirs;
    public $FilterFiles;
    public $FilterExts;
    public $FilterDirsAll     = array();
    public $FilterFilesAll    = array();
    public $FilterExtsAll     = array();
    public $FilterOn;
    public $ExportOnlyDB;
    public $File;
    public $Format;
    public $PackDir;
    public $Size              = 0;
    public $Dirs              = array();
    public $Files             = array();

    /**
     *
     * @var DUP_Archive_Filter_Info
     */
    public $FilterInfo        = null;
    public $RecursiveLinks    = array();
    public $file_count        = -1;
    //PROTECTED
    protected $Package;
    private $tmpFilterDirsAll = array();
    private $wpCorePaths      = array();
    private $wpCoreExactPaths = array();

    /**
	 *  Init this object
	 */
	public function __construct($package)
	{
		$this->Package		 = $package;
		$this->FilterOn		 = false;
		$this->ExportOnlyDB	 = false;
		$this->FilterInfo	 = new DUP_Archive_Filter_Info();

		$homePath = duplicator_get_home_path();

		$this->wpCorePaths[] = DUP_Util::safePath("{$homePath}/wp-admin");
		$this->wpCorePaths[] = DUP_Util::safePath(WP_CONTENT_DIR."/languages");
		$this->wpCorePaths[] = DUP_Util::safePath("{$homePath}/wp-includes");

		$this->wpCoreExactPaths[] = DUP_Util::safePath("{$homePath}");
		$this->wpCoreExactPaths[] = DUP_Util::safePath(WP_CONTENT_DIR);
		$this->wpCoreExactPaths[] = DUP_Util::safePath(WP_CONTENT_DIR."/uploads");
		$this->wpCoreExactPaths[] = DUP_Util::safePath(WP_CONTENT_DIR."/plugins");
		$this->wpCoreExactPaths[] = DUP_Util::safePath(get_theme_root());
	}

	/**
	 * Builds the archive based on the archive type
	 *
	 * @param obj $package The package object that started this process
	 *
	 * @return null
	 */
	public function build($package, $rethrow_exception = false)
	{
		DUP_LOG::trace("b1");
		$this->Package = $package;
		if (!isset($this->PackDir) && !is_dir($this->PackDir)) throw new Exception("The 'PackDir' property must be a valid directory.");
		if (!isset($this->File)) throw new Exception("A 'File' property must be set.");

		DUP_LOG::trace("b2");
		$completed = false;

		switch ($this->Format) {
			case 'TAR': break;
			case 'TAR-GZIP': break;
			case 'DAF':
				$completed = DUP_DupArchive::create($this, $this->Package->BuildProgress, $this->Package);

				$this->Package->Update();
				break;

			default:
				if (class_exists('ZipArchive')) {
					$this->Format	 = 'ZIP';
					DUP_Zip::create($this, $this->Package->BuildProgress);
					$completed		 = true;
				}
				break;
		}

		DUP_LOG::Trace("Completed build or build thread");

		if ($this->Package->BuildProgress === null) {
			// Zip path
			DUP_LOG::Trace("Completed Zip");
			$storePath	 = DUP_Settings::getSsdirTmpPath()."/{$this->File}";
			$this->Size	 = @filesize($storePath);
			$this->Package->setStatus(DUP_PackageStatus::ARCDONE);
		} else if ($completed) {
			// Completed DupArchive path
			DUP_LOG::Trace("Completed DupArchive build");
			if ($this->Package->BuildProgress->failed) {
				DUP_LOG::Trace("Error building DupArchive");
				$this->Package->setStatus(DUP_PackageStatus::ERROR);
			} else {
				$filepath	 = DUP_Settings::getSsdirTmpPath()."/{$this->File}";
				$this->Size	 = @filesize($filepath);
				$this->Package->setStatus(DUP_PackageStatus::ARCDONE);
				DUP_LOG::Trace("Done building archive");
			}
		} else {
			DUP_Log::trace("DupArchive chunk done but package not completed yet");
		}
	}

    /**
     *
     * @return int return  DUP_Archive_Build_Mode
     */
    public function getBuildMode()
    {
        switch ($this->Format) {
            case 'TAR': break;
            case 'TAR-GZIP': break;
            case 'DAF':
                return DUP_Archive_Build_Mode::DupArchive;
            default:
                if (class_exists('ZipArchive')) {
                    return DUP_Archive_Build_Mode::ZipArchive;
                } else {
                    return DUP_Archive_Build_Mode::Unconfigured;
                }
                break;
        }
    }

    /**
	 *  Builds a list of files and directories to be included in the archive
	 *
	 *  Get the directory size recursively, but don't calc the snapshot directory, exclusion directories
	 *  @link http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx Windows filename restrictions
	 *
	 *  @return obj Returns a DUP_Archive object
	 */
	public function getScannerData()
	{
		$this->createFilterInfo();
		$rootPath	 = duplicator_get_abs_path();

		$this->RecursiveLinks = array();
		//If the root directory is a filter then skip it all
		if (in_array($this->PackDir, $this->FilterDirsAll) || $this->Package->Archive->ExportOnlyDB) {
			$this->Dirs = array();
		} else {
			$this->Dirs[] = $this->PackDir;

			$this->getFileLists($rootPath);

			if ($this->isOuterWPContentDir()) {
				$this->Dirs[] = WP_CONTENT_DIR;
				$this->getFileLists(WP_CONTENT_DIR);
			}

			$this->setDirFilters();
			$this->setFileFilters();
			$this->setTreeFilters();
		}

		$this->FilterDirsAll	 = array_merge($this->FilterDirsAll, $this->FilterInfo->Dirs->Unreadable);
		$this->FilterFilesAll	 = array_merge($this->FilterFilesAll, $this->FilterInfo->Files->Unreadable);
		sort($this->FilterDirsAll);

		return $this;
	}

	/**
	 * Save any property of this class through reflection
	 *
	 * @param $property     A valid public property in this class
	 * @param $value        The value for the new dynamic property
	 *
	 * @return bool	Returns true if the value has changed.
	 */
	public function saveActiveItem($package, $property, $value)
	{
		$package		 = DUP_Package::getActive();
		$reflectionClass = new ReflectionClass($package->Archive);
		$reflectionClass->getProperty($property)->setValue($package->Archive, $value);
		return update_option(DUP_Package::OPT_ACTIVE, $package);
	}

    /**
     *  Properly creates the directory filter list that is used for filtering directories
     *
     * @param string $dirs A semi-colon list of dir paths
     *  /path1_/path/;/path1_/path2/;
     *
     * @returns string A cleaned up list of directory filters
     * @return string
     */
	public function parseDirectoryFilter($dirs = "")
	{
		$filters	 = "";
		$dir_array	 = array_unique(explode(";", $dirs));
		$clean_array = array();
		foreach ($dir_array as $val) {
		    $val = DupLiteSnapLibIOU::safePathUntrailingslashit(DupLiteSnapLibUtil::sanitize_non_stamp_chars_newline_and_trim($val));
			if (strlen($val) >= 2 && is_dir($val)) {
				$clean_array[] = $val;
			}
		}

		if (count($clean_array)) {
			$clean_array = array_unique($clean_array);
			sort($clean_array);
			$filters	 = implode(';', $clean_array).';';
		}
		return $filters;
	}

    /**
     *  Properly creates the file filter list that is used for filtering files
     *
     * @param string $files A semi-colon list of file paths
     *  /path1_/path/file1.ext;/path1_/path2/file2.ext;
     *
     * @returns string A cleaned up list of file filters
     * @return string
     */
	public function parseFileFilter($files = "")
	{
		$filters	 = "";
		$file_array	 = array_unique(explode(";", $files));
		$clean_array = array();
		foreach ($file_array as $val) {
            $val = DupLiteSnapLibIOU::safePathUntrailingslashit(DupLiteSnapLibUtil::sanitize_non_stamp_chars_newline_and_trim($val));
            if (strlen($val) >= 2 && file_exists($val)) {
				$clean_array[] = $val;
			}
		}

		if (count($clean_array)) {
			$clean_array = array_unique($clean_array);
			sort($clean_array);
			$filters	 = implode(';', $clean_array).';';
		}
		return $filters;
	}

	/**
	 *  Properly creates the extension filter list that is used for filtering extensions
	 *
	 *  @param string $dirs A semi-colon list of dir paths
	 *  .jpg;.zip;.gif;
	 *
	 *  @returns string A cleaned up list of extension filters
	 */
	public function parseExtensionFilter($extensions = "")
	{
		$filter_exts = "";
		if (strlen($extensions) >= 1 && $extensions != ";") {
			$filter_exts = str_replace(array(' ', '.'), '', $extensions);
			$filter_exts = str_replace(",", ";", $filter_exts);
			$filter_exts = DUP_Util::appendOnce($extensions, ";");
		}
		return $filter_exts;
	}

	/**
	 * Creates the filter info setup data used for filtering the archive
	 *
	 * @return null
	 */
	private function createFilterInfo()
	{
		//FILTER: INSTANCE ITEMS
		//Add the items generated at create time
		if ($this->FilterOn) {
			$this->FilterInfo->Dirs->Instance	 = array_map('DUP_Util::safePath', explode(";", $this->FilterDirs, -1));
			$this->FilterInfo->Files->Instance	 = array_map('DUP_Util::safePath', explode(";", $this->FilterFiles, -1));
			$this->FilterInfo->Exts->Instance	 = explode(";", $this->FilterExts, -1);
		}

		//FILTER: CORE ITMES
		//Filters Duplicator free packages & All pro local directories
		$wp_root						 = duplicator_get_abs_path();
		$upload_dir						 = wp_upload_dir();
		$upload_dir						 = isset($upload_dir['basedir']) ? basename($upload_dir['basedir']) : 'uploads';
		$wp_content						 = str_replace("\\", "/", WP_CONTENT_DIR);
		$wp_content_upload				 = "{$wp_content}/{$upload_dir}";
		$this->FilterInfo->Dirs->Core	 = array(
			//WP-ROOT
			DUP_Settings::getSsdirPathLegacy(),
            DUP_Settings::getSsdirPathWpCont(),
            $wp_root.'/.opcache',
			//WP-CONTENT
			$wp_content.'/backups-dup-pro',
			$wp_content.'/ai1wm-backups',
			$wp_content.'/backupwordpress',
			$wp_content.'/content/cache',
			$wp_content.'/contents/cache',
			$wp_content.'/infinitewp/backups',
			$wp_content.'/managewp/backups',
			$wp_content.'/old-cache',
			$wp_content.'/plugins/all-in-one-wp-migration/storage',
			$wp_content.'/updraft',
			$wp_content.'/wishlist-backup',
			$wp_content.'/wfcache',
			$wp_content.'/bps-backup', // BulletProof Security backup folder
			$wp_content.'/cache',
			//WP-CONTENT-UPLOADS
			$wp_content_upload.'/aiowps_backups',
			$wp_content_upload.'/backupbuddy_temp',
			$wp_content_upload.'/backupbuddy_backups',
			$wp_content_upload.'/ithemes-security/backups',
			$wp_content_upload.'/mainwp/backup',
			$wp_content_upload.'/pb_backupbuddy',
			$wp_content_upload.'/snapshots',
			$wp_content_upload.'/sucuri',
			$wp_content_upload.'/wp-clone',
			$wp_content_upload.'/wp_all_backup',
			$wp_content_upload.'/wpbackitup_backups'
		);

		if (class_exists('BackWPup')) {
			$upload_dir = wp_upload_dir(null, false, true);
			$this->FilterInfo->Dirs->Core[] = trailingslashit(str_replace( '\\',
					'/',
					$upload_dir['basedir'])).'backwpup-'.BackWPup::get_plugin_data('hash').'-backups/';
			
			$backwpup_cfg_logfolder = get_site_option('backwpup_cfg_logfolder');
			if (false !== $backwpup_cfg_logfolder) {
				$this->FilterInfo->Dirs->Core[] = $wp_content.'/'.$backwpup_cfg_logfolder;
			}
		}
		$duplicator_global_file_filters_on = apply_filters('duplicator_global_file_filters_on', $GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS_ON']);
		if ($GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS_ON']) {
			$duplicator_global_file_filters = apply_filters('duplicator_global_file_filters', $GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS']);
			$this->FilterInfo->Files->Global = $duplicator_global_file_filters;
		}

		// Prevent adding double wp-content dir conflicts
        if ($this->isOuterWPContentDir()) {
            $default_wp_content_dir_path = DUP_Util::safePath(ABSPATH.'wp-content');
            if (file_exists($default_wp_content_dir_path)) {
                if (is_dir($default_wp_content_dir_path)) {
                    $this->FilterInfo->Dirs->Core[] = $default_wp_content_dir_path;
                } else {
                    $this->FilterInfo->Files->Core[] = $default_wp_content_dir_path;
                }
            }
        }        

		$this->FilterDirsAll	 = array_merge($this->FilterInfo->Dirs->Instance, $this->FilterInfo->Dirs->Core);
		$this->FilterExtsAll	 = array_merge($this->FilterInfo->Exts->Instance, $this->FilterInfo->Exts->Core);
		$this->FilterFilesAll	 = array_merge($this->FilterInfo->Files->Instance, $this->FilterInfo->Files->Global);

		$abs_path = duplicator_get_abs_path();
		$this->FilterFilesAll[]	 = $abs_path.'/.htaccess';
		$this->FilterFilesAll[]	 = $abs_path.'/web.config';
		$this->FilterFilesAll[]	 = $abs_path.'/wp-config.php';
		$this->tmpFilterDirsAll	 = $this->FilterDirsAll;

		//PHP 5 on windows decode patch
		if (!DUP_Util::$PHP7_plus && DUP_Util::isWindows()) {
			foreach ($this->tmpFilterDirsAll as $key => $value) {
				if (preg_match('/[^\x20-\x7f]/', $value)) {
					$this->tmpFilterDirsAll[$key] = utf8_decode($value);
				}
			}
		}
	}

	/**
     * Get All Directories then filter
     *
     * @return null
     */
    private function setDirFilters()
    {
        $this->FilterInfo->Dirs->Warning    = array();
        $this->FilterInfo->Dirs->Unreadable = array();
        $this->FilterInfo->Dirs->AddonSites = array();
        $skip_archive_scan                  = DUP_Settings::Get('skip_archive_scan');

        $utf8_key_list  = array();
        $unset_key_list = array();

        //Filter directories invalid test checks for:
        // - characters over 250
        // - invlaid characters
        // - empty string
        // - directories ending with period (Windows incompatable)
        foreach ($this->Dirs as $key => $val) {
            $name = basename($val);

            //Dir is not readble remove flag for removal
            if (!is_readable($this->Dirs[$key])) {
                $unset_key_list[]                     = $key;
                $this->FilterInfo->Dirs->Unreadable[] = DUP_Encoding::toUTF8($val);
            }

            if (!$skip_archive_scan) {
                //Locate invalid directories and warn
                $invalid_test = (defined('PHP_MAXPATHLEN') && (strlen($val) > PHP_MAXPATHLEN)) || preg_match('/(\/|\*|\?|\>|\<|\:|\\|\|)/', $name) || trim($name) == '' || (strrpos($name, '.') == strlen($name) - 1 && substr($name, -1)
                    == '.') || preg_match('/[^\x20-\x7f]/', $name);

                if ($invalid_test) {
                    $utf8_key_list[]                   = $key;
                    $this->FilterInfo->Dirs->Warning[] = DUP_Encoding::toUTF8($val);
                }
            }

            //Check for other WordPress installs
            if ($name === 'wp-admin') {
                $parent_dir = realpath(dirname($this->Dirs[$key]));
                if ($parent_dir != realpath(duplicator_get_abs_path())) {
                    if (file_exists("$parent_dir/wp-includes")) {
                        if (file_exists("$parent_dir/wp-config.php")) {
                            // Ensure we aren't adding any critical directories
                            $parent_name = basename($parent_dir);
                            if (($parent_name != 'wp-includes') && ($parent_name != 'wp-content') && ($parent_name != 'wp-admin')) {
                                $this->FilterInfo->Dirs->AddonSites[] = str_replace("\\", '/', $parent_dir);
                            }
                        }
                    }
                }
            }
        }

        //Try to repair utf8 paths
        foreach ($utf8_key_list as $key) {
            $this->Dirs[$key] = DUP_Encoding::toUTF8($this->Dirs[$key]);
        }

        //Remove unreadable items outside of main loop for performance
        if (count($unset_key_list)) {
            foreach ($unset_key_list as $key) {
                unset($this->Dirs[$key]);
            }
            $this->Dirs = array_values($this->Dirs);
        }
    }

    /**
     * Get all files and filter out error prone subsets
     *
     * @return null
     */
    private function setFileFilters()
    {
        //Init for each call to prevent concatination from stored entity objects
        $this->Size                          = 0;
        $this->FilterInfo->Files->Size       = array();
        $this->FilterInfo->Files->Warning    = array();
        $this->FilterInfo->Files->Unreadable = array();
        $skip_archive_scan                   = DUP_Settings::Get('skip_archive_scan');

        $utf8_key_list  = array();
        $unset_key_list = array();

        $wpconfig_filepath = $this->getWPConfigFilePath();
        if (!is_readable($wpconfig_filepath)) {
            $this->FilterInfo->Files->Unreadable[] = $wpconfig_filepath;
        }

        foreach ($this->Files as $key => $filePath) {

            $fileName = basename($filePath);

            if (!is_readable($filePath)) {
                $unset_key_list[]                      = $key;
                $this->FilterInfo->Files->Unreadable[] = $filePath;
                continue;
            }

            $fileSize   = @filesize($filePath);
            $fileSize   = empty($fileSize) ? 0 : $fileSize;
            $this->Size += $fileSize;

            if (!$skip_archive_scan) {
                $invalid_test = (defined('PHP_MAXPATHLEN') && (strlen($filePath) > PHP_MAXPATHLEN)) || preg_match('/(\/|\*|\?|\>|\<|\:|\\|\|)/', $fileName) || trim($fileName) == "" || preg_match('/[^\x20-\x7f]/', $fileName);

                if ($invalid_test) {
                    $utf8_key_list[]                    = $key;
                    $filePath                           = DUP_Encoding::toUTF8($filePath);
                    $fileName                           = basename($filePath);
                    $this->FilterInfo->Files->Warning[] = array(
                        'name' => $fileName,
                        'dir' => pathinfo($filePath, PATHINFO_DIRNAME),
                        'path' => $filePath);
                }

                if ($fileSize > DUPLICATOR_SCAN_WARNFILESIZE) {
                    //$ext = pathinfo($filePath, PATHINFO_EXTENSION);
                    $this->FilterInfo->Files->Size[] = array(
                        'ubytes' => $fileSize,
                        'bytes' => DUP_Util::byteSize($fileSize, 0),
                        'name' => $fileName,
                        'dir' => pathinfo($filePath, PATHINFO_DIRNAME),
                        'path' => $filePath);
                }
            }
        }

        //Try to repair utf8 paths
        foreach ($utf8_key_list as $key) {
            $this->Files[$key] = DUP_Encoding::toUTF8($this->Files[$key]);
        }

        //Remove unreadable items outside of main loop for performance
        if (count($unset_key_list)) {
            foreach ($unset_key_list as $key) {
                unset($this->Files[$key]);
            }
            $this->Files = array_values($this->Files);
        }
    }

    /**
	 * Recursive function to get all directories in a wp install
	 *
	 * @notes:
	 * 	Older PHP logic which is more stable on older version of PHP
	 * 	NOTE RecursiveIteratorIterator is problematic on some systems issues include:
	 *  - error 'too many files open' for recursion
	 *  - $file->getExtension() is not reliable as it silently fails at least in php 5.2.17
	 *  - issues with when a file has a permission such as 705 and trying to get info (had to fallback to pathinfo)
	 *  - basic conclusion wait on the SPL libs until after php 5.4 is a requirments
	 *  - tight recursive loop use caution for speed
	 *
	 * @return array	Returns an array of directories to include in the archive
	 */
	private function getFileLists($path) {
		$handle = @opendir($path);

		if ($handle) {
			while (($file = readdir($handle)) !== false) {

				if ($file == '.' || $file == '..') {
					continue;
				}

				$fullPath = str_replace("\\", '/', "{$path}/{$file}");

				// @todo: Don't leave it like this. Convert into an option on the package to not follow symbolic links
				// if (is_dir($fullPath) && (is_link($fullPath) == false))
				if (is_dir($fullPath)) {

                    $add = true;
                    if (!is_link($fullPath)){
                        foreach ($this->tmpFilterDirsAll as $key => $val) {
                            $trimmedFilterDir = rtrim($val, '/');
                            if ($fullPath == $trimmedFilterDir || strpos($fullPath, $trimmedFilterDir . '/') !== false) {
                                $add = false;
                                unset($this->tmpFilterDirsAll[$key]);
                                break;
                            }
                        }
                    } else{
                        //Convert relative path of link to absolute path
                        chdir($fullPath);
						$link_path = str_replace("\\", '/', realpath(readlink($fullPath)));
                        chdir(dirname(__FILE__));

                        $link_pos = strpos($fullPath,$link_path);
                        if($link_pos === 0 && (strlen($link_path) <  strlen($fullPath))){
                            $add = false;
                            $this->RecursiveLinks[] = $fullPath;
                            $this->FilterDirsAll[] = $fullPath;
                        } else {
							foreach ($this->tmpFilterDirsAll as $key => $val) {
								$trimmedFilterDir = rtrim($val, '/');
								if ($fullPath == $trimmedFilterDir || strpos($fullPath, $trimmedFilterDir . '/') !== false) {
									$add = false;
									unset($this->tmpFilterDirsAll[$key]);
									break;
								}
							}
						}
                    }

                    if ($add) {
                        $this->getFileLists($fullPath);
                        $this->Dirs[] = $fullPath;
                    }
				} else {
					if ( ! (in_array(pathinfo($file, PATHINFO_EXTENSION), $this->FilterExtsAll)
						|| in_array($fullPath, $this->FilterFilesAll)
						|| in_array($file, $this->FilterFilesAll))) {
						$this->Files[] = $fullPath;
					}
				}
			}
			closedir($handle);
		}
		return $this->Dirs;
	}

	/**
	 *  Builds a tree for both file size warnings and name check warnings
	 *  The trees are used to apply filters from the scan screen
	 *
	 *  @return null
	 */
	private function setTreeFilters()
	{
		//-------------------------
		//SIZE TREE
		//BUILD: File Size tree
		$dir_group = DUP_Util::array_group_by($this->FilterInfo->Files->Size, "dir");
		ksort($dir_group);
		foreach ($dir_group as $dir => $files) {
			$sum = 0;
			foreach ($files as $key => $value) {
				$sum += $value['ubytes'];
			}

			//Locate core paths, wp-admin, wp-includes, etc.
			$iscore = 0;
			foreach ($this->wpCorePaths as $core_dir) {
				if (strpos(DUP_Util::safePath($dir), DUP_Util::safePath($core_dir)) !== false) {
					$iscore = 1;
					break;
				}
			}
			// Check root and content exact dir
			if (!$iscore) {
				if (in_array($dir, $this->wpCoreExactPaths)) {
					$iscore = 1;
				}
			}

			$this->FilterInfo->TreeSize[] = array(
				'size' => DUP_Util::byteSize($sum, 0),
				'dir' => $dir,
				'sdir' => str_replace(duplicator_get_abs_path(), '/', $dir),
				'iscore' => $iscore,
				'files' => $files
			);
		}

		//-------------------------
		//NAME TREE
		//BUILD: Warning tree for file names
		$dir_group = DUP_Util::array_group_by($this->FilterInfo->Files->Warning, "dir");
		ksort($dir_group);
		foreach ($dir_group as $dir => $files) {

			//Locate core paths, wp-admin, wp-includes, etc.
			$iscore = 0;
			foreach ($this->wpCorePaths as $core_dir) {
				if (strpos($dir, $core_dir) !== false) {
					$iscore = 1;
					break;
				}
			}
			// Check root and content exact dir
			if (!$iscore) {
				if (in_array($dir, $this->wpCoreExactPaths)) {
					$iscore = 1;
				}
			}

			$this->FilterInfo->TreeWarning[] = array(
				'dir' => $dir,
				'sdir' => str_replace(duplicator_get_abs_path(), '/', $dir),
				'iscore' => $iscore,
				'count' => count($files),
				'files' => $files);
		}

		//BUILD: Warning tree for dir names
		foreach ($this->FilterInfo->Dirs->Warning as $dir) {
			$add_dir = true;
			foreach ($this->FilterInfo->TreeWarning as $key => $value) {
				if ($value['dir'] == $dir) {
					$add_dir = false;
					break;
				}
			}
			if ($add_dir) {

				//Locate core paths, wp-admin, wp-includes, etc.
				$iscore = 0;
				foreach ($this->wpCorePaths as $core_dir) {
					if (strpos(DUP_Util::safePath($dir), DUP_Util::safePath($core_dir)) !== false) {
						$iscore = 1;
						break;
					}
				}
				// Check root and content exact dir
				if (!$iscore) {
					if (in_array($dir, $this->wpCoreExactPaths)) {
						$iscore = 1;
					}
				}

				$this->FilterInfo->TreeWarning[] = array(
					'dir' => $dir,
					'sdir' => str_replace(duplicator_get_abs_path(), '/', $dir),
					'iscore' => $iscore,
					'count' => 0);
			}
		}

		function _sortDir($a, $b)
		{
			return strcmp($a["dir"], $b["dir"]);
		}
		usort($this->FilterInfo->TreeWarning, "_sortDir");
	}

	public function getWPConfigFilePath()
	{
		$wpconfig_filepath = '';
		$abs_path = duplicator_get_abs_path();
		if (file_exists($abs_path.'/wp-config.php')) {
			$wpconfig_filepath = $abs_path.'/wp-config.php';
		} elseif (@file_exists(dirname($abs_path).'/wp-config.php') && !@file_exists(dirname($abs_path).'/wp-settings.php')) {
			$wpconfig_filepath = dirname($abs_path).'/wp-config.php';
		}
		return $wpconfig_filepath;
	}

	public function isOuterWPContentDir()
	{
		if (!isset($this->isOuterWPContentDir)) {
			$abspath_normalize			 = wp_normalize_path(ABSPATH);
			$wp_content_dir_normalize	 = wp_normalize_path(WP_CONTENT_DIR);
			if (0 !== strpos($wp_content_dir_normalize, $abspath_normalize)) {
				$this->isOuterWPContentDir = true;
			} else {
				$this->isOuterWPContentDir = false;
			}
		}
		return $this->isOuterWPContentDir;
	}

	public function wpContentDirNormalizePath()
	{
		if (!isset($this->wpContentDirNormalizePath)) {
			$this->wpContentDirNormalizePath = trailingslashit(wp_normalize_path(WP_CONTENT_DIR));
		}
		return $this->wpContentDirNormalizePath;
	}

	public function getUrl()
    {
        return DUP_Settings::getSsdirUrl()."/".$this->File;
    }

	public function getLocalDirPath($dir, $basePath = '')
	{
		$isOuterWPContentDir		 = $this->isOuterWPContentDir();
		$wpContentDirNormalizePath	 = $this->wpContentDirNormalizePath();
		$compressDir				 = rtrim(wp_normalize_path(DUP_Util::safePath($this->PackDir)), '/');

		$dir = trailingslashit(wp_normalize_path($dir));
		if ($isOuterWPContentDir && 0 === strpos($dir, $wpContentDirNormalizePath)) {
			$newWPContentDirPath = empty($basePath) ? 'wp-content/' : $basePath.'wp-content/';
			$emptyDir			 = ltrim(str_replace($wpContentDirNormalizePath, $newWPContentDirPath, $dir), '/');
		} else {
            $emptyDir = ltrim($basePath.preg_replace('/^'.preg_quote($compressDir, '/').'(.*)/m', '$1', $dir), '/');
		}
		return $emptyDir;
	}

	public function getLocalFilePath($file, $basePath = '')
	{
		$isOuterWPContentDir		 = $this->isOuterWPContentDir();
		$wpContentDirNormalizePath	 = $this->wpContentDirNormalizePath();
		$compressDir				 = rtrim(wp_normalize_path(DUP_Util::safePath($this->PackDir)), '/');

		$file = wp_normalize_path($file);
		if ($isOuterWPContentDir && 0 === strpos($file, $wpContentDirNormalizePath)) {
			$newWPContentDirPath = empty($basePath) ? 'wp-content/' : $basePath.'wp-content/';
			$localFileName		 = ltrim(str_replace($wpContentDirNormalizePath, $newWPContentDirPath, $file), '/');
		} else {            
			$localFileName = ltrim($basePath.preg_replace('/^'.preg_quote($compressDir, '/').'(.*)/m', '$1', $file), '/');
		}
		return $localFileName;
	}
}PK�{2\�}���y�ypackage/class.pack.database.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
// Exit if accessed directly
if (!defined('DUPLICATOR_VERSION'))
    exit;

/**
 * Class for gathering system information about a database
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 */
class DUP_DatabaseInfo
{

    /**
     * The SQL file was built with mysqldump or PHP
     */
    public $buildMode;

    /**
     * A unique list of all the collation table types used in the database
     */
    public $collationList;

    /**
     * Does any filtered table have an upper case character in it
     */
    public $isTablesUpperCase;

    /**
     * Does the database name have any filtered characters in it
     */
    public $isNameUpperCase;

    /**
     * The real name of the database
     */
    public $name;

    /**
     * The full count of all tables in the database
     */
    public $tablesBaseCount;

    /**
     * The count of tables after the tables filter has been applied
     */
    public $tablesFinalCount;

    /**
     * The number of rows from all filtered tables in the database
     */
    public $tablesRowCount;

    /**
     * The estimated data size on disk from all filtered tables in the database
     */
    public $tablesSizeOnDisk;

    /**
     * Gets the server variable lower_case_table_names
     *
     * 0 store=lowercase;	compare=sensitive	(works only on case sensitive file systems )
     * 1 store=lowercase;	compare=insensitive
     * 2 store=exact;		compare=insensitive	(works only on case INsensitive file systems )
     * default is 0/Linux ; 1/Windows
     */
    public $varLowerCaseTables;

    /**
     * The simple numeric version number of the database server
     * @exmaple: 5.5
     */
    public $version;

    /**
     * The full text version number of the database server
     * @exmaple: 10.2 mariadb.org binary distribution
     */
    public $versionComment;

    /**
     * table wise row counts array, Key as table name and value as row count
     *  table name => row count
     */
    public $tableWiseRowCounts;

    /**
     * @var array List of triggers included in the database
     */
    public $triggerList = array();

    /**
     * Integer field file structure of table, table name as key
     */
    private $intFieldsStruct = array();

    /**
     * $currentIndex => processedSchemaSize
     */
    private $indexProcessedSchemaSize = array();

    //CONSTRUCTOR
    function __construct()
    {
        $this->collationList      = array();
        $this->tableWiseRowCounts = array();
    }

    public function addTriggers()
    {
        global $wpdb;

        if (!is_array($triggers = $wpdb->get_results("SHOW TRIGGERS", ARRAY_A))) {
            return;
        }

        foreach ($triggers as $trigger) {
            $name   = $trigger["Trigger"];
            $create = $wpdb->get_row("SHOW CREATE TRIGGER `{$name}`", ARRAY_N);
            $this->triggerList[$name] = array(
                "create" => "DELIMITER ;;\n".$create[2].";;\nDELIMITER ;"
            );
        }
    }
}

class DUP_Database
{
    //PUBLIC
    public $Type = 'MySQL';
    public $Size;
    public $File;
    public $Path;
    public $FilterTables;
    public $FilterOn;
    public $Name;
    public $Compatible;
    public $Comments;

    /**
     *
     * @var DUP_DatabaseInfo 
     */
    public $info = null;
    //PROTECTED
    protected $Package;
    //PRIVATE
    private $tempDbPath;
    private $EOFMarker;
    private $networkFlush;

    /**
     *  Init this object
     */
    function __construct($package)
    {
        $this->Package                  = $package;
        $this->EOFMarker                = "";
        $package_zip_flush              = DUP_Settings::Get('package_zip_flush');
        $this->networkFlush             = empty($package_zip_flush) ? false : $package_zip_flush;
        $this->info                     = new DUP_DatabaseInfo();
        $this->info->varLowerCaseTables = DUP_Util::isWindows() ? 1 : 0;
    }

    /**
     *  Build the database script
     *
     *  @param DUP_Package $package A reference to the package that this database object belongs in
     *
     *  @return null
     */
    public function build($package, $errorBehavior = Dup_ErrorBehavior::ThrowException)
    {
        try {

            $this->Package = $package;
            do_action('duplicator_lite_build_database_before_start', $package);

            $time_start       = DUP_Util::getMicrotime();
            $this->Package->setStatus(DUP_PackageStatus::DBSTART);
            $this->tempDbPath = DUP_Settings::getSsdirTmpPath()."/{$this->File}";

            $package_mysqldump        = DUP_Settings::Get('package_mysqldump');
            $package_phpdump_qrylimit = DUP_Settings::Get('package_phpdump_qrylimit');

            $mysqlDumpPath        = DUP_DB::getMySqlDumpPath();
            $mode                 = DUP_DB::getBuildMode();
            $reserved_db_filepath = duplicator_get_abs_path().'/database.sql';

            $log = "\n********************************************************************************\n";
            $log .= "DATABASE:\n";
            $log .= "********************************************************************************\n";
            $log .= "BUILD MODE:   {$mode}";
            $log .= ($mode == 'PHP') ? "(query limit - {$package_phpdump_qrylimit})\n" : "\n";
            $log .= "MYSQLTIMEOUT: ".DUPLICATOR_DB_MAX_TIME."\n";
            $log .= "MYSQLDUMP:    ";
            $log .= ($mysqlDumpPath) ? "Is Supported" : "Not Supported";
            DUP_Log::Info($log);
            $log = null;

            do_action('duplicator_lite_build_database_start', $package);

            switch ($mode) {
                case 'MYSQLDUMP':
                    $this->mysqlDump($mysqlDumpPath);
                    break;
                case 'PHP' :
                    $this->phpDump($package);
                    break;
            }

            DUP_Log::Info("SQL CREATED: {$this->File}");
            $time_end = DUP_Util::getMicrotime();
            $time_sum = DUP_Util::elapsedTime($time_end, $time_start);

            //File below 10k considered incomplete
            $sql_file_size = is_file($this->tempDbPath) ? @filesize($this->tempDbPath) : 0;
            DUP_Log::Info("SQL FILE SIZE: ".DUP_Util::byteSize($sql_file_size)." ({$sql_file_size})");

            if ($sql_file_size < 1350) {
                $error_message   = "SQL file size too low.";
                $package->BuildProgress->set_failed($error_message);
                $package->Status = DUP_PackageStatus::ERROR;
                $package->Update();
                DUP_Log::error($error_message, "File does not look complete.  Check permission on file and parent directory at [{$this->tempDbPath}]", $errorBehavior);
                do_action('duplicator_lite_build_database_fail', $package);
            } else {
                do_action('duplicator_lite_build_database_completed', $package);
            }

            DUP_Log::Info("SQL FILE TIME: ".date("Y-m-d H:i:s"));
            DUP_Log::Info("SQL RUNTIME: {$time_sum}");

            $this->Size = is_file($this->tempDbPath) ? @filesize($this->tempDbPath) : 0;

            $this->Package->setStatus(DUP_PackageStatus::DBDONE);
        }
        catch (Exception $e) {
            do_action('duplicator_lite_build_database_fail', $package);
            DUP_Log::error("Runtime error in DUP_Database::Build. ".$e->getMessage(), "Exception: {$e}", $errorBehavior);
        }
    }

    /**
     *  Get the database meta-data such as tables as all there details
     *
     *  @return array Returns an array full of meta-data about the database
     */
    public function getScannerData()
    {
        global $wpdb;

        $filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : array();
        $tblBaseCount = 0;
        $tblCount     = 0;

        $tables                     = $wpdb->get_results("SHOW TABLE STATUS", ARRAY_A);
        $info                       = array();
        $info['Status']['Success']  = is_null($tables) ? false : true;
        //DB_Case for the database name is never checked on
        $info['Status']['DB_Case']  = 'Good';
        $info['Status']['DB_Rows']  = 'Good';
        $info['Status']['DB_Size']  = 'Good';
        $info['Status']['TBL_Case'] = 'Good';
        $info['Status']['TBL_Rows'] = 'Good';
        $info['Status']['TBL_Size'] = 'Good';

        $info['Size']       = 0;
        $info['Rows']       = 0;
        $info['TableCount'] = 0;
        $info['TableList']  = array();
        $tblCaseFound       = 0;
        $tblRowsFound       = 0;
        $tblSizeFound       = 0;

        //Grab Table Stats
        $filteredTables = array();
        foreach ($tables as $table) {
            $tblBaseCount++;
            $name = $table["Name"];
            if ($this->FilterOn && is_array($filterTables)) {
                if (in_array($name, $filterTables)) {
                    continue;
                }
            }

            $size = ($table["Data_length"] + $table["Index_length"]);
            $rows = empty($table["Rows"]) ? '0' : $table["Rows"];

            $info['Size']                      += $size;
            $info['Rows']                      += ($table["Rows"]);
            $info['TableList'][$name]['Case']  = preg_match('/[A-Z]/', $name) ? 1 : 0;
            $info['TableList'][$name]['Rows']  = number_format($rows);
            $info['TableList'][$name]['Size']  = DUP_Util::byteSize($size);
            $info['TableList'][$name]['USize'] = $size;
            $filteredTables[] = $name;
            $tblCount++;

            //Table Uppercase
            if ($info['TableList'][$name]['Case']) {
                if (!$tblCaseFound) {
                    $tblCaseFound = 1;
                }
            }

            //Table Row Count
            if ($rows > DUPLICATOR_SCAN_DB_TBL_ROWS) {
                if (!$tblRowsFound) {
                    $tblRowsFound = 1;
                }
            }

            //Table Size
            if ($size > DUPLICATOR_SCAN_DB_TBL_SIZE) {
                if (!$tblSizeFound) {
                    $tblSizeFound = 1;
                }
            }
        }
        $this->setInfoObj($filteredTables);
        $this->info->addTriggers();

        $info['Status']['DB_Case'] = preg_match('/[A-Z]/', $wpdb->dbname) ? 'Warn' : 'Good';
        $info['Status']['DB_Rows'] = ($info['Rows'] > DUPLICATOR_SCAN_DB_ALL_ROWS) ? 'Warn' : 'Good';
        $info['Status']['DB_Size'] = ($info['Size'] > DUPLICATOR_SCAN_DB_ALL_SIZE) ? 'Warn' : 'Good';

        $info['Status']['TBL_Case'] = ($tblCaseFound) ? 'Warn' : 'Good';
        $info['Status']['TBL_Rows'] = ($tblRowsFound) ? 'Warn' : 'Good';
        $info['Status']['TBL_Size'] = ($tblSizeFound) ? 'Warn' : 'Good';
        $info['Status']['Triggers'] = count($this->info->triggerList) > 0 ? 'Warn' : 'Good';

        $info['RawSize']    = $info['Size'];
        $info['Size']       = DUP_Util::byteSize($info['Size']) or "unknown";
        $info['Rows']       = number_format($info['Rows']) or "unknown";
        $info['TableList']  = $info['TableList'] or "unknown";
        $info['TableCount'] = $tblCount;

        $this->info->isTablesUpperCase = $tblCaseFound;
        $this->info->tablesBaseCount   = $tblBaseCount;
        $this->info->tablesFinalCount  = $tblCount;
        $this->info->tablesRowCount    = $info['Rows'];
        $this->info->tablesSizeOnDisk  = $info['Size'];

        return $info;
    }

    /**
     * @param array &$filteredTables Filtered names of tables to include in collation search.
     *        Parameter does not change in the function, is passed by reference only to avoid copying.
     *
     * @return void
     */
    public function setInfoObj(&$filteredTables)
    {
        global $wpdb;
 
        $this->info->buildMode          = DUP_DB::getBuildMode();
        $this->info->version            = DUP_DB::getVersion();
        $this->info->versionComment     = DUP_DB::getVariable('version_comment');
        $this->info->varLowerCaseTables = DUP_DB::getVariable('lower_case_table_names');
        $this->info->name               = $wpdb->dbname;
        $this->info->isNameUpperCase    = preg_match('/[A-Z]/', $wpdb->dbname) ? 1 : 0;
        $this->info->collationList      = DUP_DB::getTableCollationList($filteredTables);
    }

    /**
     * Unset tableWiseRowCounts table key for which row count is unstable
     *
     * @param object $package The reference to the current package being built     *
     * @return void
     */
    public function validateTableWiseRowCounts()
    {
        foreach ($this->Package->Database->info->tableWiseRowCounts as $rewriteTableAs => $rowCount) {
            $newRowCount = $GLOBALS['wpdb']->get_var("SELECT Count(*) FROM `{$rewriteTableAs}`");
            if ($rowCount != $newRowCount) {
                unset($this->Package->Database->info->tableWiseRowCounts[$rewriteTableAs]);
            }
        }
    }

    /**
     *  Build the database script using mysqldump
     *
     *  @return bool  Returns true if the sql script was successfully created
     */
    private function mysqlDump($exePath)
    {
        global $wpdb;
        require_once (DUPLICATOR_PLUGIN_PATH.'classes/utilities/class.u.shell.php');

        $host           = explode(':', DB_HOST);
        $host           = reset($host);
        $port           = strpos(DB_HOST, ':') ? end(explode(':', DB_HOST)) : '';
        $name           = DB_NAME;
        $mysqlcompat_on = isset($this->Compatible) && strlen($this->Compatible);

        //Build command
        $cmd = escapeshellarg($exePath);
        $cmd .= ' --no-create-db';
        $cmd .= ' --single-transaction';
        $cmd .= ' --hex-blob';
        $cmd .= ' --skip-add-drop-table';
        $cmd .= ' --routines';
        $cmd .= ' --quote-names';
        $cmd .= ' --skip-comments';
        $cmd .= ' --skip-set-charset';
        $cmd .= ' --skip-triggers';
        $cmd .= ' --allow-keywords';
        $cmd .= ' --no-tablespaces';

        //Compatibility mode
        if ($mysqlcompat_on) {
            DUP_Log::Info("COMPATIBLE: [{$this->Compatible}]");
            $cmd .= " --compatible={$this->Compatible}";
        }

        //Filter tables
        $res        = $wpdb->get_results('SHOW FULL TABLES', ARRAY_N);
        $tables     = array();
        $baseTables = array();
        foreach ($res as $row) {
            if (DUP_Util::isTableExists($row[0])) {
                $tables[] = $row[0];
                if ('BASE TABLE' == $row[1]) {
                    $baseTables[] = $row[0];
                }
            }
        }
        $filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : null;
        $tblAllCount  = count($tables);

        foreach ($tables as $table) {
            if (in_array($table, $baseTables)) {
                $row_count                                                            = $GLOBALS['wpdb']->get_var("SELECT Count(*) FROM `{$table}`");
                $rewrite_table_as                                                     = $this->rewriteTableNameAs($table);
                $this->Package->Database->info->tableWiseRowCounts[$rewrite_table_as] = $row_count;
            }
        }
        //$tblFilterOn  = ($this->FilterOn) ? 'ON' : 'OFF';

        if (is_array($filterTables) && $this->FilterOn) {
            foreach ($tables as $key => $val) {
                if (in_array($tables[$key], $filterTables)) {
                    $cmd .= " --ignore-table={$name}.{$tables[$key]} ";
                    unset($tables[$key]);
                }
            }
        }

        $cmd .= ' -u '.escapeshellarg(DB_USER);
        $cmd .= (DB_PASSWORD) ?
            ' -p'.DUP_Shell_U::escapeshellargWindowsSupport(DB_PASSWORD) : '';

        $cmd .= ' -h '.escapeshellarg($host);
        $cmd .= (!empty($port) && is_numeric($port) ) ?
            ' -P '.$port : '';

        $isPopenEnabled = DUP_Shell_U::isPopenEnabled();

        if (!$isPopenEnabled) {
            $cmd .= ' -r '.escapeshellarg($this->tempDbPath);
        }

        $cmd .= ' '.escapeshellarg(DB_NAME);
        $cmd .= ' 2>&1';

        if ($isPopenEnabled) {
            $needToRewrite = false;
            foreach ($tables as $tableName) {
                $rewriteTableAs = $this->rewriteTableNameAs($tableName);
                if ($tableName != $rewriteTableAs) {
                    $needToRewrite = true;
                    break;
                }
            }

            if ($needToRewrite) {
                $findReplaceTableNames = array(); // orignal table name => rewrite table name

                foreach ($tables as $tableName) {
                    $rewriteTableAs = $this->rewriteTableNameAs($tableName);
                    if ($tableName != $rewriteTableAs) {
                        $findReplaceTableNames[$tableName] = $rewriteTableAs;
                    }
                }
            }

            $firstLine = '';
            DUP_LOG::trace("Executing mysql dump command by popen: $cmd");
            $handle    = popen($cmd, "r");
            if ($handle) {
                $sql_header = "/* DUPLICATOR-LITE (MYSQL-DUMP BUILD MODE) MYSQL SCRIPT CREATED ON : ".@date("Y-m-d H:i:s")." */\n\n";
                file_put_contents($this->tempDbPath, $sql_header, FILE_APPEND);
                while (!feof($handle)) {
                    $line = fgets($handle); //get ony one line
                    if ($line) {
                        if (empty($firstLine)) {
                            $firstLine = $line;
                            if (false !== stripos($line, 'Using a password on the command line interface can be insecure'))
                                continue;
                        }

                        if ($needToRewrite) {
                            $replaceCount = 1;

                            if (preg_match('/CREATE TABLE `(.*?)`/', $line, $matches)) {
                                $tableName = $matches[1];
                                if (isset($findReplaceTableNames[$tableName])) {
                                    $rewriteTableAs = $findReplaceTableNames[$tableName];
                                    $line           = str_replace('CREATE TABLE `'.$tableName.'`', 'CREATE TABLE `'.$rewriteTableAs.'`', $line, $replaceCount);
                                }
                            } elseif (preg_match('/INSERT INTO `(.*?)`/', $line, $matches)) {
                                $tableName = $matches[1];
                                if (isset($findReplaceTableNames[$tableName])) {
                                    $rewriteTableAs = $findReplaceTableNames[$tableName];
                                    $line           = str_replace('INSERT INTO `'.$tableName.'`', 'INSERT INTO `'.$rewriteTableAs.'`', $line, $replaceCount);
                                }
                            } elseif (preg_match('/LOCK TABLES `(.*?)`/', $line, $matches)) {
                                $tableName = $matches[1];
                                if (isset($findReplaceTableNames[$tableName])) {
                                    $rewriteTableAs = $findReplaceTableNames[$tableName];
                                    $line           = str_replace('LOCK TABLES `'.$tableName.'`', 'LOCK TABLES `'.$rewriteTableAs.'`', $line, $replaceCount);
                                }
                            }
                        }

                        file_put_contents($this->tempDbPath, $line, FILE_APPEND);
                        $output = "Ran from {$exePath}";
                    }
                }
                $mysqlResult = pclose($handle);
            } else {
                $output = '';
            }

            // Password bug > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
            if (empty($output) && trim($firstLine) === 'Warning: Using a password on the command line interface can be insecure.') {
                $output = '';
            }
        } else {
            DUP_LOG::trace("Executing mysql dump command $cmd");
            exec($cmd, $output, $mysqlResult);
            $output = implode("\n", $output);

            // Password bug > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
            if (trim($output) === 'Warning: Using a password on the command line interface can be insecure.') {
                $output = '';
            }
            $output = (strlen($output)) ? $output : "Ran from {$exePath}";

            $tblCreateCount = count($tables);
            $tblFilterCount = $tblAllCount - $tblCreateCount;

            //DEBUG
            //DUP_Log::Info("COMMAND: {$cmd}");
            DUP_Log::Info("FILTERED: [{$this->FilterTables}]");
            DUP_Log::Info("RESPONSE: {$output}");
            DUP_Log::Info("TABLES: total:{$tblAllCount} | filtered:{$tblFilterCount} | create:{$tblCreateCount}");
        }

        $sql_footer = "\n\n/* Duplicator WordPress Timestamp: ".date("Y-m-d H:i:s")."*/\n";
        $sql_footer .= "/* ".DUPLICATOR_DB_EOF_MARKER." */\n";
        file_put_contents($this->tempDbPath, $sql_footer, FILE_APPEND);
        if ($mysqlResult !== 0) {
            /**
             * -1 error command shell 
             * mysqldump return 
             * 0 - Success
             * 1 - Warning
             * 2 - Exception
             */
            DUP_Log::Info('MYSQL DUMP ERROR '.print_r($mysqlResult, true));
            DUP_Log::error(__('Shell mysql dump error. Change SQL Mode to the "PHP Code" in the Duplicator > Settings > Packages.', 'duplicator'), implode("\n", DupLiteSnapLibIOU::getLastLinesOfFile($this->tempDbPath,
                DUPLICATOR_DB_MYSQLDUMP_ERROR_CONTAINING_LINE_COUNT, DUPLICATOR_DB_MYSQLDUMP_ERROR_CHARS_IN_LINE_COUNT)), Dup_ErrorBehavior::ThrowException);
            return false;
        }

        return true;
    }

    /**
     *  Build the database script using php
     *
     *  @return bool  Returns true if the sql script was successfully created
     */
    private function phpDump($package)
    {
        global $wpdb;

        $wpdb->query("SET session wait_timeout = ".DUPLICATOR_DB_MAX_TIME);
        if (($handle = fopen($this->tempDbPath, 'w+')) == false) {
            DUP_Log::error('[PHP DUMP] ERROR Can\'t open sbStorePath "'.$this->tempDbPath.'"', Dup_ErrorBehavior::ThrowException);
        }
        $tables = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type != 'VIEW'");

        $filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : null;
        $tblAllCount  = count($tables);
        //$tblFilterOn  = ($this->FilterOn) ? 'ON' : 'OFF';
        $qryLimit     = DUP_Settings::Get('package_phpdump_qrylimit');

        if (is_array($filterTables) && $this->FilterOn) {
            foreach ($tables as $key => $val) {
                if (in_array($tables[$key], $filterTables)) {
                    unset($tables[$key]);
                }
            }
        }
        $tblCreateCount = count($tables);
        $tblFilterCount = $tblAllCount - $tblCreateCount;

        DUP_Log::Info("TABLES: total:{$tblAllCount} | filtered:{$tblFilterCount} | create:{$tblCreateCount}");
        DUP_Log::Info("FILTERED: [{$this->FilterTables}]");

        //Added 'NO_AUTO_VALUE_ON_ZERO' at plugin version 1.2.12 to fix :
        //**ERROR** database error write 'Invalid default value for for older mysql versions
        $sql_header = "/* DUPLICATOR-LITE (PHP BUILD MODE) MYSQL SCRIPT CREATED ON : ".@date("Y-m-d H:i:s")." */\n\n";
        $sql_header .= "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;\n\n";
        $sql_header .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
        fwrite($handle, $sql_header);

        //BUILD CREATES:
        //All creates must be created before inserts do to foreign key constraints
        foreach ($tables as $table) {
            $rewrite_table_as   = $this->rewriteTableNameAs($table);
            $create             = $wpdb->get_row("SHOW CREATE TABLE `{$table}`", ARRAY_N);
            $count              = 1;
            $create_table_query = str_replace($table, $rewrite_table_as, $create[1], $count);
            @fwrite($handle, "{$create_table_query};\n\n");
        }

        $procedures = $wpdb->get_col("SHOW PROCEDURE STATUS WHERE `Db` = '{$wpdb->dbname}'", 1);
        if (count($procedures)) {
            foreach ($procedures as $procedure) {
                @fwrite($handle, "DELIMITER ;;\n");
                $create = $wpdb->get_row("SHOW CREATE PROCEDURE `{$procedure}`", ARRAY_N);
                @fwrite($handle, "{$create[2]} ;;\n");
                @fwrite($handle, "DELIMITER ;\n\n");
            }
        }

        $functions = $wpdb->get_col("SHOW FUNCTION STATUS WHERE `Db` = '{$wpdb->dbname}'", 1);
        if (count($functions)) {
            foreach ($functions as $function) {
                @fwrite($handle, "DELIMITER ;;\n");
                $create = $wpdb->get_row("SHOW CREATE FUNCTION `{$function}`", ARRAY_N);
                @fwrite($handle, "{$create[2]} ;;\n");
                @fwrite($handle, "DELIMITER ;\n\n");
            }
        }

        $views = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type = 'VIEW'");
        if (count($views)) {
            foreach ($views as $view) {
                $create = $wpdb->get_row("SHOW CREATE VIEW `{$view}`", ARRAY_N);
                @fwrite($handle, "{$create[1]};\n\n");
            }
        }

        $table_count  = count($tables);
        $table_number = 0;

        //BUILD INSERTS:
        //Create Insert in 100 row increments to better handle memory
        foreach ($tables as $table) {

            $table_number++;
            if ($table_number % 2 == 0) {
                $this->Package->Status = DupLiteSnapLibUtil::getWorkPercent(DUP_PackageStatus::DBSTART, DUP_PackageStatus::DBDONE, $table_count, $table_number);
                $this->Package->update();
            }

            $row_count        = $wpdb->get_var("SELECT Count(*) FROM `{$table}`");
            $rewrite_table_as = $this->rewriteTableNameAs($table);

            $this->Package->Database->info->tableWiseRowCounts[$rewrite_table_as] = $row_count;

            if ($row_count > $qryLimit) {
                $row_count = ceil($row_count / $qryLimit);
            } else if ($row_count > 0) {
                $row_count = 1;
            }

            if ($row_count >= 1) {
                fwrite($handle, "\n/* INSERT TABLE DATA: {$table} */\n");
            }

            for ($i = 0; $i < $row_count; $i++) {
                $sql   = "";
                $limit = $i * $qryLimit;
                $query = "SELECT * FROM `{$table}` LIMIT {$limit}, {$qryLimit}";
                $rows  = $wpdb->get_results($query, ARRAY_A);

                $select_last_error = $wpdb->last_error;
                if ('' !== $select_last_error) {
                    $fix                            = esc_html__('Please contact your DataBase administrator to fix the error.', 'duplicator');
                    $errorMessage                   = $select_last_error.' '.$fix.'.';
                    $package->BuildProgress->set_failed($errorMessage);
                    $package->BuildProgress->failed = true;
                    $package->failed                = true;
                    $package->Status                = DUP_PackageStatus::ERROR;
                    $package->Update();
                    DUP_Log::error($select_last_error, $fix, Dup_ErrorBehavior::ThrowException);
                    return;
                }

                if (is_array($rows)) {
                    foreach ($rows as $row) {
                        $sql         .= "INSERT INTO `{$rewrite_table_as}` VALUES(";
                        $num_values  = count($row);
                        $num_counter = 1;
                        foreach ($row as $value) {
                            if (is_null($value) || !isset($value)) {
                                ($num_values == $num_counter) ? $sql .= 'NULL' : $sql .= 'NULL, ';
                            } else {
                                ($num_values == $num_counter) ? $sql .= '"'.DUP_DB::escSQL($value, true).'"' : $sql .= '"'.DUP_DB::escSQL($value, true).'", ';
                            }
                            $num_counter++;
                        }
                        $sql .= ");\n";
                    }
                    fwrite($handle, $sql);
                }
            }

            //Flush buffer if enabled
            if ($this->networkFlush) {
                DUP_Util::fcgiFlush();
            }
            $sql  = null;
            $rows = null;
        }

        $sql_footer = "\nSET FOREIGN_KEY_CHECKS = 1; \n\n";
        $sql_footer .= "/* Duplicator WordPress Timestamp: ".date("Y-m-d H:i:s")."*/\n";
        $sql_footer .= "/* ".DUPLICATOR_DB_EOF_MARKER." */\n";
        fwrite($handle, $sql_footer);
        $wpdb->flush();
        fclose($handle);
    }

    private function rewriteTableNameAs($table)
    {
        $table_prefix = $this->getTablePrefix();
        if (!isset($this->sameNameTableExists)) {
            global $wpdb;
            $this->sameNameTableExists = false;
            $all_tables                = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type != 'VIEW'");
            foreach ($all_tables as $table_name) {
                if (strtolower($table_name) != $table_name && in_array(strtolower($table_name), $all_tables)) {
                    $this->sameNameTableExists = true;
                    break;
                }
            }
        }
        if (false === $this->sameNameTableExists && 0 === stripos($table, $table_prefix) && 0 !== strpos($table, $table_prefix)) {
            $post_fix           = substr($table, strlen($table_prefix));
            $rewrite_table_name = $table_prefix.$post_fix;
        } else {
            $rewrite_table_name = $table;
        }
        return $rewrite_table_name;
    }

    private function getTablePrefix()
    {
        global $wpdb;
        $table_prefix = (is_multisite() && !defined('MULTISITE')) ? $wpdb->base_prefix : $wpdb->get_blog_prefix(0);
        return $table_prefix;
    }

    public function getUrl()
    {
        return DUP_Settings::getSsdirUrl()."/".$this->File;
    }
}PK�{2\t]��R�R package/class.pack.installer.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
// Exit if accessed directly
/* @var $global DUP_Global_Entity */

require_once(DUPLICATOR_PLUGIN_PATH.'/classes/class.archive.config.php');
require_once(DUPLICATOR_PLUGIN_PATH.'/classes/utilities/class.u.zip.php');
require_once(DUPLICATOR_PLUGIN_PATH.'/classes/utilities/class.u.multisite.php');
require_once(DUPLICATOR_PLUGIN_PATH.'/classes/class.password.php');

class DUP_Installer
{

	const DEFAULT_INSTALLER_FILE_NAME_WITHOUT_HASH = 'installer.php';

	//PUBLIC
	public $File;
	public $Size			 = 0;
	public $OptsDBHost;
	public $OptsDBPort;
	public $OptsDBName;
	public $OptsDBUser;
	public $OptsDBCharset;
	public $OptsDBCollation;
	public $OptsSecureOn	 = 0;
	public $OptsSecurePass;
	public $numFilesAdded	 = 0;
	public $numDirsAdded	 = 0;
	//PROTECTED
	protected $Package;

	/**
	 *  Init this object
	 */
	function __construct($package)
	{
		$this->Package = $package;
	}

	public function build($package, $error_behavior = Dup_ErrorBehavior::Quit)
	{
		DUP_Log::Info("building installer");

		$this->Package	 = $package;
		$success		 = false;

		if ($this->create_enhanced_installer_files()) {
			$success = $this->add_extra_files($package);
		} else {
			DUP_Log::Info("error creating enhanced installer files");
		}


		if ($success) {
			// No longer need to store wp-config.txt file in main storage area
			$temp_conf_ark_file_path = $this->getTempWPConfArkFilePath();
			@unlink($temp_conf_ark_file_path);

			$package->BuildProgress->installer_built = true;
		} else {
			$error_message	 = 'Error adding installer';
			$package->BuildProgress->set_failed($error_message);
			$package->Status = DUP_PackageStatus::ERROR;
			$package->Update();

			DUP_Log::error($error_message, "Marking build progress as failed because couldn't add installer files", $error_behavior);
			//$package->BuildProgress->failed = true;
			//$package->setStatus(DUP_PackageStatus::ERROR);
		}

		return $success;
	}

	private function create_enhanced_installer_files()
	{
		$success = false;
		if ($this->create_enhanced_installer()) {
			$success = $this->create_archive_config_file();
		}
		return $success;
	}

	private function create_enhanced_installer()
	{
		$success				 = true;
		$archive_filepath		 = DUP_Settings::getSsdirTmpPath()."/{$this->Package->Archive->File}";
		$installer_filepath		 = apply_filters('duplicator_installer_file_path', DUP_Settings::getSsdirTmpPath()."/{$this->Package->NameHash}_installer.php");
		$template_filepath		 = DUPLICATOR_PLUGIN_PATH.'/installer/installer.tpl';
		$mini_expander_filepath	 = DUPLICATOR_PLUGIN_PATH.'/lib/dup_archive/classes/class.duparchive.mini.expander.php';

		// Replace the @@ARCHIVE@@ token
		$installer_contents = file_get_contents($template_filepath);

		if (DUP_Settings::Get('archive_build_mode') == DUP_Archive_Build_Mode::DupArchive) {
			$mini_expander_string = file_get_contents($mini_expander_filepath);

			if ($mini_expander_string === false) {
				DUP_Log::error(DUP_U::__('Error reading DupArchive mini expander'), DUP_U::__('Error reading DupArchive mini expander'), Dup_ErrorBehavior::LogOnly);
				return false;
			}
		} else {
			$mini_expander_string = '';
		}

		$search_array			 = array('@@ARCHIVE@@', '@@VERSION@@', '@@ARCHIVE_SIZE@@', '@@PACKAGE_HASH@@', '@@SECONDARY_PACKAGE_HASH@@', '@@DUPARCHIVE_MINI_EXPANDER@@');
		$package_hash			 = $this->Package->getPackageHash();
		$secondary_package_hash	 = $this->Package->getSecondaryPackageHash();
		$replace_array			 = array($this->Package->Archive->File, DUPLICATOR_VERSION, @filesize($archive_filepath), $package_hash, $secondary_package_hash, $mini_expander_string);
		$installer_contents		 = str_replace($search_array, $replace_array, $installer_contents);

		if (@file_put_contents($installer_filepath, $installer_contents) === false) {
			DUP_Log::error(esc_html__('Error writing installer contents', 'duplicator'), esc_html__("Couldn't write to $installer_filepath", 'duplicator'));
			$success = false;
		}

		if ($success) {
			$storePath	 = DUP_Settings::getSsdirTmpPath()."/{$this->File}";
			$this->Size	 = @filesize($storePath);
		}

		return $success;
	}

	/**
	 * Create archive.txt file */
	private function create_archive_config_file()
	{
		global $wpdb;

		$success				 = true;
		$archive_config_filepath = DUP_Settings::getSsdirTmpPath()."/{$this->Package->NameHash}_archive.txt";
		$ac						 = new DUP_Archive_Config();
		$extension				 = strtolower($this->Package->Archive->Format);

		$hasher		 = new DUP_PasswordHash(8, FALSE);
		$pass_hash	 = $hasher->HashPassword($this->Package->Installer->OptsSecurePass);

		$this->Package->Database->getScannerData();

		//READ-ONLY: COMPARE VALUES
		$ac->created	 = $this->Package->Created;
		$ac->version_dup = DUPLICATOR_VERSION;
		$ac->version_wp	 = $this->Package->VersionWP;
		$ac->version_db	 = $this->Package->VersionDB;
		$ac->version_php = $this->Package->VersionPHP;
		$ac->version_os	 = $this->Package->VersionOS;
		$ac->dup_type 	 = 'lite';
		$ac->dbInfo		 = $this->Package->Database->info;

		//READ-ONLY: GENERAL
		// $ac->installer_base_name  = $global->installer_base_name;
		$ac->installer_base_name = 'installer.php';
		$ac->installer_backup_name = $this->Package->NameHash.'_installer-backup.php';
		$ac->package_name		 = "{$this->Package->NameHash}_archive.{$extension}";
		$ac->package_hash		 = $this->Package->getPackageHash();
		$ac->package_notes		 = $this->Package->Notes;
		$ac->url_old			 = get_option('siteurl');
		$ac->opts_delete		 = DupLiteSnapJsonU::wp_json_encode_pprint($GLOBALS['DUPLICATOR_OPTS_DELETE']);
		$ac->blogname			 = esc_html(get_option('blogname'));

		$abs_path					 = duplicator_get_abs_path();
		$ac->wproot					 = $abs_path;
		$ac->relative_content_dir	 = str_replace($abs_path, '', WP_CONTENT_DIR);
		$ac->exportOnlyDB			 = $this->Package->Archive->ExportOnlyDB;
		$ac->installSiteOverwriteOn	 = DUPLICATOR_INSTALL_SITE_OVERWRITE_ON;
		$ac->wplogin_url			 = wp_login_url();

		//PRE-FILLED: GENERAL
		$ac->secure_on		 = $this->Package->Installer->OptsSecureOn;
		$ac->secure_pass	 = $pass_hash;
		$ac->skipscan		 = false;
		$ac->dbhost			 = $this->Package->Installer->OptsDBHost;
		$ac->dbname			 = $this->Package->Installer->OptsDBName;
		$ac->dbuser			 = $this->Package->Installer->OptsDBUser;
		$ac->dbpass			 = '';
		$ac->dbcharset		 = $this->Package->Installer->OptsDBCharset;
		$ac->dbcollation     = $this->Package->Installer->OptsDBCollation;

		$ac->wp_tableprefix	 = $wpdb->base_prefix;

		$ac->mu_mode						 = DUP_MU::getMode();
		$ac->is_outer_root_wp_config_file	 = (!file_exists($abs_path.'/wp-config.php')) ? true : false;
		$ac->is_outer_root_wp_content_dir	 = $this->Package->Archive->isOuterWPContentDir();

		$json = DupLiteSnapJsonU::wp_json_encode_pprint($ac);
		DUP_Log::TraceObject('json', $json);

		if (file_put_contents($archive_config_filepath, $json) === false) {
			DUP_Log::error("Error writing archive config", "Couldn't write archive config at $archive_config_filepath", Dup_ErrorBehavior::LogOnly);
			$success = false;
		}

		return $success;
	}

	/**
	 *  Puts an installer zip file in the archive for backup purposes.
	 */
	private function add_extra_files($package)
	{
		$success				 = false;
		$installer_filepath		 = apply_filters('duplicator_installer_file_path', DUP_Settings::getSsdirTmpPath()."/{$this->Package->NameHash}_installer.php");
		$scan_filepath			 = DUP_Settings::getSsdirTmpPath()."/{$this->Package->NameHash}_scan.json";
		$sql_filepath			 = DUP_Settings::getSsdirTmpPath()."/{$this->Package->Database->File}";
		$archive_filepath		 = DUP_Settings::getSsdirTmpPath()."/{$this->Package->Archive->File}";
		$archive_config_filepath = DUP_Settings::getSsdirTmpPath()."/{$this->Package->NameHash}_archive.txt";

		DUP_Log::Info("add_extra_files1");

		if (file_exists($installer_filepath) == false) {
			DUP_Log::error("Installer $installer_filepath not present", '', Dup_ErrorBehavior::LogOnly);
			return false;
		}

		DUP_Log::Info("add_extra_files2");
		if (file_exists($sql_filepath) == false) {
			DUP_Log::error("Database SQL file $sql_filepath not present", '', Dup_ErrorBehavior::LogOnly);
			return false;
		}

		DUP_Log::Info("add_extra_files3");
		if (file_exists($archive_config_filepath) == false) {
			DUP_Log::error("Archive configuration file $archive_config_filepath not present", '', Dup_ErrorBehavior::LogOnly);
			return false;
		}

		DUP_Log::Info("add_extra_files4");
		if ($package->Archive->file_count != 2) {
			DUP_Log::Info("Doing archive file check");
			// Only way it's 2 is if the root was part of the filter in which case the archive won't be there
			DUP_Log::Info("add_extra_files5");
			if (file_exists($archive_filepath) == false) {

				DUP_Log::error("$error_text. **RECOMMENDATION: $fix_text", '', Dup_ErrorBehavior::LogOnly);

				return false;
			}
			DUP_Log::Info("add_extra_files6");
		}

		$wpconfig_filepath = $package->Archive->getWPConfigFilePath();

		if ($package->Archive->Format == 'DAF') {
			DUP_Log::Info("add_extra_files7");
			$success = $this->add_extra_files_using_duparchive($installer_filepath, $scan_filepath, $sql_filepath, $archive_filepath, $archive_config_filepath, $wpconfig_filepath);
		} else {
			DUP_Log::Info("add_extra_files8");
			$success = $this->add_extra_files_using_ziparchive($installer_filepath, $scan_filepath, $sql_filepath, $archive_filepath, $archive_config_filepath, $wpconfig_filepath);
		}

		// No sense keeping the archive config around
		@unlink($archive_config_filepath);
		$package->Archive->Size = @filesize($archive_filepath);

		return $success;
	}

	private function add_extra_files_using_duparchive($installer_filepath, $scan_filepath, $sql_filepath, $archive_filepath, $archive_config_filepath, $wpconfig_filepath)
	{
		$success = false;

		try {
			DUP_Log::Info("add_extra_files_using_da1");
			$htaccess_filepath	 = $this->getHtaccessFilePath();
			$webconf_filepath	 = duplicator_get_abs_path().'/web.config';

			$logger = new DUP_DupArchive_Logger();

			DupArchiveEngine::init($logger, 'DUP_Log::profile');

			$embedded_scan_ark_file_path = $this->getEmbeddedScanFilePath();
			DupArchiveEngine::addRelativeFileToArchiveST($archive_filepath, $scan_filepath, $embedded_scan_ark_file_path);
			$this->numFilesAdded++;

			if (file_exists($htaccess_filepath)) {
				$htaccess_ark_file_path = $this->getHtaccessArkFilePath();
				try {
					DupArchiveEngine::addRelativeFileToArchiveST($archive_filepath, $htaccess_filepath, $htaccess_ark_file_path);
					$this->numFilesAdded++;
				}
				catch (Exception $ex) {
					// Non critical so bury exception
				}
			}

			if (file_exists($webconf_filepath)) {
				try {
					DupArchiveEngine::addRelativeFileToArchiveST($archive_filepath, $webconf_filepath, DUPLICATOR_WEBCONFIG_ORIG_FILENAME);
					$this->numFilesAdded++;
				}
				catch (Exception $ex) {
					// Non critical so bury exception
				}
			}

			if (file_exists($wpconfig_filepath)) {
				$conf_ark_file_path		 = $this->getWPConfArkFilePath();
				$temp_conf_ark_file_path = $this->getTempWPConfArkFilePath();
				if (copy($wpconfig_filepath, $temp_conf_ark_file_path)) {
					$this->cleanTempWPConfArkFilePath($temp_conf_ark_file_path);
					DupArchiveEngine::addRelativeFileToArchiveST($archive_filepath, $temp_conf_ark_file_path, $conf_ark_file_path);
				} else {
					DupArchiveEngine::addRelativeFileToArchiveST($archive_filepath, $wpconfig_filepath, $conf_ark_file_path);
				}
				$this->numFilesAdded++;
			}

			$this->add_installer_files_using_duparchive($archive_filepath, $installer_filepath, $archive_config_filepath);

			$success = true;
		}
		catch (Exception $ex) {
			DUP_Log::error("Error adding installer files to archive. ", $ex->getMessage(), Dup_ErrorBehavior::ThrowException);
		}

		return $success;
	}

	private function add_installer_files_using_duparchive($archive_filepath, $installer_filepath, $archive_config_filepath)
	{
		$installer_backup_filename = $this->Package->NameHash.'_installer-backup.php';
		

		DUP_Log::Info('Adding enhanced installer files to archive using DupArchive');
		DupArchiveEngine::addRelativeFileToArchiveST($archive_filepath, $installer_filepath, $installer_backup_filename);

		$this->numFilesAdded++;

		$base_installer_directory	 = DUPLICATOR_PLUGIN_PATH.'installer';
		$installer_directory		 = "$base_installer_directory/dup-installer";

		$counts				 = DupArchiveEngine::addDirectoryToArchiveST($archive_filepath, $installer_directory, $base_installer_directory, true);
		$this->numFilesAdded += $counts->numFilesAdded;
		$this->numDirsAdded	 += $counts->numDirsAdded;

		$archive_config_relative_path = $this->getArchiveTxtFilePath();

		DupArchiveEngine::addRelativeFileToArchiveST($archive_filepath, $archive_config_filepath, $archive_config_relative_path);
		$this->numFilesAdded++;

		// Include dup archive
		$duparchive_lib_directory	 = DUPLICATOR_PLUGIN_PATH.'lib/dup_archive';
		$duparchive_lib_counts		 = DupArchiveEngine::addDirectoryToArchiveST($archive_filepath, $duparchive_lib_directory, DUPLICATOR_PLUGIN_PATH, true, 'dup-installer/');
		$this->numFilesAdded		 += $duparchive_lib_counts->numFilesAdded;
		$this->numDirsAdded			 += $duparchive_lib_counts->numDirsAdded;

		// Include snaplib
		$snaplib_directory	 = DUPLICATOR_PLUGIN_PATH.'lib/snaplib';
		$snaplib_counts		 = DupArchiveEngine::addDirectoryToArchiveST($archive_filepath, $snaplib_directory, DUPLICATOR_PLUGIN_PATH, true, 'dup-installer/');
		$this->numFilesAdded += $snaplib_counts->numFilesAdded;
		$this->numDirsAdded	 += $snaplib_counts->numDirsAdded;

		// Include fileops
		$fileops_directory	 = DUPLICATOR_PLUGIN_PATH.'lib/fileops';
		$fileops_counts		 = DupArchiveEngine::addDirectoryToArchiveST($archive_filepath, $fileops_directory, DUPLICATOR_PLUGIN_PATH, true, 'dup-installer/');
		$this->numFilesAdded += $fileops_counts->numFilesAdded;
		$this->numDirsAdded	 += $fileops_counts->numDirsAdded;

		// Include config
		$config_directory	 = DUPLICATOR_PLUGIN_PATH.'lib/config';
		$config_counts		 = DupArchiveEngine::addDirectoryToArchiveST($archive_filepath, $config_directory, DUPLICATOR_PLUGIN_PATH, true, 'dup-installer/');
		$this->numFilesAdded += $config_counts->numFilesAdded;
		$this->numDirsAdded	 += $fileops_counts->numDirsAdded;
	}

	private function add_extra_files_using_ziparchive($installer_filepath, $scan_filepath, $sql_filepath, $zip_filepath, $archive_config_filepath, $wpconfig_filepath)
	{
		$htaccess_filepath	 = $this->getHtaccessFilePath();
		$webconfig_filepath	 = duplicator_get_abs_path().'/web.config';

		$success	 = false;
		$zipArchive	 = new ZipArchive();

		if ($zipArchive->open($zip_filepath, ZIPARCHIVE::CREATE) === TRUE) {
			DUP_Log::Info("Successfully opened zip $zip_filepath");

			if (file_exists($htaccess_filepath)) {
				$htaccess_ark_file_path = $this->getHtaccessArkFilePath();
				DUP_Zip_U::addFileToZipArchive($zipArchive, $htaccess_filepath, $htaccess_ark_file_path, true);
			}

			if (file_exists($webconfig_filepath)) {
				DUP_Zip_U::addFileToZipArchive($zipArchive, $webconfig_filepath, DUPLICATOR_WEBCONFIG_ORIG_FILENAME, true);
			}

			if (!empty($wpconfig_filepath)) {
				$conf_ark_file_path		 = $this->getWPConfArkFilePath();
				$temp_conf_ark_file_path = $this->getTempWPConfArkFilePath();
				if (copy($wpconfig_filepath, $temp_conf_ark_file_path)) {
					$this->cleanTempWPConfArkFilePath($temp_conf_ark_file_path);
					DUP_Zip_U::addFileToZipArchive($zipArchive, $temp_conf_ark_file_path, $conf_ark_file_path, true);
				} else {
					DUP_Zip_U::addFileToZipArchive($zipArchive, $wpconfig_filepath, $conf_ark_file_path, true);
				}
			}

			$embedded_scan_file_path = $this->getEmbeddedScanFilePath();
			if (DUP_Zip_U::addFileToZipArchive($zipArchive, $scan_filepath, $embedded_scan_file_path, true)) {
				if ($this->add_installer_files_using_zip_archive($zipArchive, $installer_filepath, $archive_config_filepath, true)) {
					DUP_Log::info("Installer files added to archive");
					DUP_Log::info("Added to archive");

					$success = true;
				} else {
					DUP_Log::error("Unable to add enhanced enhanced installer files to archive.", '', Dup_ErrorBehavior::LogOnly);
				}
			} else {
				DUP_Log::error("Unable to add scan file to archive.", '', Dup_ErrorBehavior::LogOnly);
			}

			if ($zipArchive->close() === false) {
				DUP_Log::error("Couldn't close archive when adding extra files.", '');
				$success = false;
			}

			DUP_Log::Info('After ziparchive close when adding installer');
		}

		return $success;
	}

	// Add installer directory to the archive and the archive.cfg
	private function add_installer_files_using_zip_archive(&$zip_archive, $installer_filepath, $archive_config_filepath, $is_compressed)
	{
		$success = false;
		$installer_backup_filename = $this->Package->NameHash.'_installer-backup.php';

		DUP_Log::Info('Adding enhanced installer files to archive using ZipArchive');

		if (DUP_Zip_U::addFileToZipArchive($zip_archive, $installer_filepath, $installer_backup_filename, true)) {
			DUPLICATOR_PLUGIN_PATH.'installer/';
			$installer_directory = DUPLICATOR_PLUGIN_PATH.'installer/dup-installer';

			if (DUP_Zip_U::addDirWithZipArchive($zip_archive, $installer_directory, true, '', $is_compressed)) {
				$archive_config_local_name = $this->getArchiveTxtFilePath();

				if (DUP_Zip_U::addFileToZipArchive($zip_archive, $archive_config_filepath, $archive_config_local_name, true)) {

					$snaplib_directory	 = DUPLICATOR_PLUGIN_PATH.'lib/snaplib';
					$config_directory	 = DUPLICATOR_PLUGIN_PATH.'lib/config';

					if (DUP_Zip_U::addDirWithZipArchive($zip_archive, $snaplib_directory, true, 'dup-installer/lib/', $is_compressed) &&
						DUP_Zip_U::addDirWithZipArchive($zip_archive, $config_directory, true, 'dup-installer/lib/', $is_compressed)
					) {
						$success = true;
					} else {
						DUP_Log::error("Error adding directory {$snaplib_directory} and {$config_directory} to zipArchive", '', Dup_ErrorBehavior::LogOnly);
					}
				} else {
					DUP_Log::error("Error adding $archive_config_filepath to zipArchive", '', Dup_ErrorBehavior::LogOnly);
				}
			} else {
				DUP_Log::error("Error adding directory $installer_directory to zipArchive", '', Dup_ErrorBehavior::LogOnly);
			}
		} else {
			DUP_Log::error("Error adding backup installer file to zipArchive", '', Dup_ErrorBehavior::LogOnly);
		}

		return $success;
	}

	/**
	 * Get .htaccess file path
	 * 
	 * @return string
	 */
	private function getHtaccessFilePath()
	{
		return duplicator_get_abs_path().'/.htaccess';
	}

	/**
	 * Get .htaccss in archive file
	 * 
	 * @return string
	 */
	private function getHtaccessArkFilePath()
	{
		$packageHash		 = $this->Package->getPackageHash();
		$htaccessArkFilePath = '.htaccess__'.$packageHash;
		return $htaccessArkFilePath;
	}

	/**
	 * Get wp-config.php file path along with name in archive file
	 */
	private function getWPConfArkFilePath()
	{
		if (DUPLICATOR_INSTALL_SITE_OVERWRITE_ON) {
			$package_hash		 = $this->Package->getPackageHash();
			$conf_ark_file_path	 = 'dup-wp-config-arc__'.$package_hash.'.txt';
		} else {
			$conf_ark_file_path = 'wp-config.php';
		}
		return $conf_ark_file_path;
	}

	/**
	 * Get temp wp-config.php file path along with name in temp folder
	 */
	private function getTempWPConfArkFilePath()
	{
		$temp_conf_ark_file_path = DUP_Settings::getSsdirTmpPath().'/'.$this->Package->NameHash.'_wp-config.txt';
		return $temp_conf_ark_file_path;
	}

	/**
	 * Clear out sensitive database connection information
	 *
	 * @param $temp_conf_ark_file_path Temp config file path
	 */
	private static function cleanTempWPConfArkFilePath($temp_conf_ark_file_path)
	{
		if (function_exists('token_get_all')) {
			require_once(DUPLICATOR_PLUGIN_PATH.'lib/config/class.wp.config.tranformer.php');
			$transformer = new DupLiteWPConfigTransformer($temp_conf_ark_file_path);
			$constants	 = array('DB_NAME', 'DB_USER', 'DB_PASSWORD', 'DB_HOST');
			foreach ($constants as $constant) {
				if ($transformer->exists('constant', $constant)) {
					$transformer->update('constant', $constant, '');
				}
			}
		}
	}

	/**
	 * Get scan.json file path along with name in archive file
	 */
	private function getEmbeddedScanFilePath()
	{
		$package_hash				 = $this->Package->getPackageHash();
		$embedded_scan_ark_file_path = 'dup-installer/dup-scan__'.$package_hash.'.json';
		return $embedded_scan_ark_file_path;
	}

	/**
	 * Get archive.txt file path along with name in archive file
	 */
	private function getArchiveTxtFilePath()
	{
		$package_hash			 = $this->Package->getPackageHash();
		$archive_txt_file_path	 = 'dup-installer/dup-archive__'.$package_hash.'.txt';
		return $archive_txt_file_path;
	}
}
PK�{2\i#~~�
�
Apackage/duparchive/class.pack.archive.duparchive.state.create.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

require_once (DUPLICATOR_PLUGIN_PATH.'lib/dup_archive/classes/states/class.duparchive.state.create.php');
require_once (DUPLICATOR_PLUGIN_PATH.'lib/dup_archive/classes/class.duparchive.processing.failure.php');

class DUP_DupArchive_Create_State extends DupArchiveCreateState
{
    /* @var $package DUP_Package */
  //  private $package;

//    public function setPackage(&$package)
     public function setPackage(&$package)
    {
  //      $this->package = &$package;
    }

    // Only one active package so straightforward
   // public static function createFromPackage(&$package)
    public static function get_instance()
    {
        $instance = new DUP_DupArchive_Create_State();

        $data = DUP_Settings::Get('duparchive_create_state');
        
        DUP_Util::objectCopy($data, $instance);
       
        $instance->startTimestamp = time();

        DUP_Log::TraceObject("retrieving create state", $instance);
        
        return $instance;
    }

    public static function createNew($archivePath, $basePath, $timeSliceInSecs, $isCompressed, $setArchiveOffsetToEndOfArchive)
    {
        $instance = new DUP_DupArchive_Create_State();

        if ($setArchiveOffsetToEndOfArchive) {
            $instance->archiveOffset = filesize($archivePath);
        } else {
            $instance->archiveOffset = 0;
        }

        $instance->archivePath           = $archivePath;
        $instance->basePath              = $basePath;
        $instance->currentDirectoryIndex = 0;
        $instance->currentFileOffset     = 0;
        $instance->currentFileIndex      = 0;
        $instance->failures              = array();
        $instance->globSize              = DupArchiveCreateState::DEFAULT_GLOB_SIZE;
        $instance->isCompressed          = $isCompressed;
        $instance->timeSliceInSecs       = $timeSliceInSecs;
        $instance->working               = true;
        $instance->skippedDirectoryCount = 0;
        $instance->skippedFileCount      = 0;

        $instance->startTimestamp = time();

        return $instance;
    }

    public function addFailure($type, $subject, $description, $isCritical = false)
    {
        parent::addFailure($type, $subject, $description, $isCritical);
    }

    public function save()
    {              
        DUP_Log::TraceObject("Saving create state", $this);
        DUP_Settings::Set('duparchive_create_state', $this);
        
        DUP_Settings::Save();
    }
}
PK�{2\ps-{o@o@4package/duparchive/class.pack.archive.duparchive.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

require_once (DUPLICATOR_PLUGIN_PATH.'classes/package/duparchive/class.pack.archive.duparchive.state.expand.php');
require_once (DUPLICATOR_PLUGIN_PATH.'classes/package/duparchive/class.pack.archive.duparchive.state.create.php');
require_once (DUPLICATOR_PLUGIN_PATH.'lib/dup_archive/classes/class.duparchive.loggerbase.php');
require_once (DUPLICATOR_PLUGIN_PATH.'lib/dup_archive/classes/class.duparchive.engine.php');
require_once (DUPLICATOR_PLUGIN_PATH.'lib/dup_archive/classes/states/class.duparchive.state.create.php');
require_once (DUPLICATOR_PLUGIN_PATH.'lib/dup_archive/classes/states/class.duparchive.state.expand.php');
require_once (DUPLICATOR_PLUGIN_PATH.'lib/dup_archive/classes/class.duparchive.processing.failure.php');

class DUP_DupArchive_Logger extends DupArchiveLoggerBase
{

    public function log($s, $flush = false, $callingFunctionOverride = null)
    {
        DUP_Log::Trace($s, true, $callingFunctionOverride);
    }
}

class DUP_DupArchive
{

    // Using a worker time override since evidence shorter time works much
    const WorkerTimeInSec = 10;

    /**
     *  CREATE
     *  Creates the zip file and adds the SQL file to the archive
     */
    public static function create($archive, $buildProgress, $package)
    {
        /* @var $buildProgress DUP_Build_Progress */

        DUP_LOG::trace("start");
        try {
            DUP_Log::Open($package->NameHash);

            if ($buildProgress->retries > DUPLICATOR_MAX_BUILD_RETRIES) {
                $error_msg = __('Package build appears stuck so marking package as failed. Is the Max Worker Time set too high?.', 'duplicator');
                DUP_Log::error(esc_html__('Build Failure', 'duplicator'), esc_html($error_msg), Dup_ErrorBehavior::LogOnly);
                //$buildProgress->failed = true;
                $buildProgress->set_failed($error_msg);
                $package->setStatus(DUP_PackageStatus::ERROR);
                ;
                return true;
            } else {
                // If all goes well retries will be reset to 0 at the end of this function.
                $buildProgress->retries++;
                $package->update();
            }

            $done = false;

            DupArchiveEngine::init(new DUP_DupArchive_Logger(), null, $archive);
            DUP_Package::safeTmpCleanup(true);

            $compressDir  = rtrim(DUP_Util::safePath($archive->PackDir), '/');
            $sqlPath      = DUP_Settings::getSsdirTmpPath()."/{$package->Database->File}";
            $archivePath  = DUP_Settings::getSsdirTmpPath()."/{$archive->File}";
            $scanFilepath = DUP_Settings::getSsdirTmpPath()."/{$package->NameHash}_scan.json";
            $skipArchiveFinalization = false;
            $json                    = '';

            if (file_exists($scanFilepath)) {

                $json = file_get_contents($scanFilepath);

                if (empty($json)) {
                    $errorText = __("Scan file $scanFilepath is empty!", 'duplicator');
                    $fixText   = __("Click on \"Resolve This\" button to fix the JSON settings.", 'duplicator');

                    DUP_Log::Trace($errorText);
                    DUP_Log::error(esc_html($errorText)." **RECOMMENDATION:  ".esc_html($fixText).".", '', Dup_ErrorBehavior::LogOnly);

                    //$buildProgress->failed = true;
                    $buildProgress->set_failed($errorText);
                    $package->setStatus(DUP_PackageStatus::ERROR);
                    return true;
                }
            } else {
                DUP_Log::trace("**** scan file $scanFilepath doesn't exist!!");
                $errorMessage = sprintf(__("ERROR: Can't find Scanfile %s. Please ensure there no non-English characters in the package or schedule name.", 'duplicator'), $scanFilepath);

                DUP_Log::error($errorMessage, '', Dup_ErrorBehavior::LogOnly);

                //$buildProgress->failed = true;
                $buildProgress->set_failed($errorMessage);
                $package->setStatus(DUP_PackageStatus::ERROR);
                return true;
            }

            Dup_Log::TraceObject("buildprogress object", $buildProgress, false);

            $scanReport = json_decode($json);

            if ($buildProgress->archive_started == false) {

                $filterDirs        = empty($archive->FilterDirs) ? 'not set' : $archive->FilterDirs;
                $filterExts        = empty($archive->FilterExts) ? 'not set' : $archive->FilterExts;
                $filterFiles       = empty($archive->FilterFiles) ? 'not set' : $archive->FilterFiles;
                $filterOn          = ($archive->FilterOn) ? 'ON' : 'OFF';
                $filterDirsFormat  = rtrim(str_replace(';', "\n\t", $filterDirs));
                $filterFilesFormat = rtrim(str_replace(';', "\n\t", $filterFiles));

                DUP_Log::info("\n********************************************************************************");
                DUP_Log::info("ARCHIVE Type=DUP Mode=DupArchive");
                DUP_Log::info("********************************************************************************");
                DUP_Log::info("ARCHIVE DIR:  ".$compressDir);
                DUP_Log::info("ARCHIVE FILE: ".basename($archivePath));
                DUP_Log::info("FILTERS: *{$filterOn}*");
                DUP_Log::Info("DIRS:\n\t{$filterDirsFormat}");
                DUP_Log::Info("FILES:\n\t{$filterFilesFormat}");
                DUP_Log::info("EXTS:  {$filterExts}");
                DUP_Log::info("----------------------------------------");
                DUP_Log::info("COMPRESSING");
                DUP_Log::info("SIZE:\t".$scanReport->ARC->Size);
                DUP_Log::info("STATS:\tDirs ".$scanReport->ARC->DirCount." | Files ".$scanReport->ARC->FileCount." | Total ".$scanReport->ARC->FullCount);

                if (($scanReport->ARC->DirCount == '') || ($scanReport->ARC->FileCount == '') || ($scanReport->ARC->FullCount == '')) {
                    $error_message = 'Invalid Scan Report Detected';

                    DUP_Log::error($error_message, 'Invalid Scan Report Detected', Dup_ErrorBehavior::LogOnly);
                    $buildProgress->set_failed($error_message);
                    $package->setStatus(DUP_PackageStatus::ERROR);
                    return true;
                }

                try {
                    DupArchiveEngine::createArchive($archivePath, true);
                    $sql_ark_file_path = $package->getSqlArkFilePath();
                    DupArchiveEngine::addRelativeFileToArchiveST($archivePath, $sqlPath, $sql_ark_file_path);
                }
                catch (Exception $ex) {
                    $error_message = 'Error adding database.sql to archive';

                    DUP_Log::error($error_message, $ex->getMessage(), Dup_ErrorBehavior::LogOnly);
                    $buildProgress->set_failed($error_message);
                    $package->setStatus(DUP_PackageStatus::ERROR);
                    return true;
                }

                $buildProgress->archive_started = true;
                $buildProgress->retries = 0;

                $createState                    = DUP_DupArchive_Create_State::createNew($archivePath, $compressDir, self::WorkerTimeInSec, true, true);
                $createState->throttleDelayInUs = 0;

                $createState->save();
                $package->Update();
            }

            try {
                $createState = DUP_DupArchive_Create_State::get_instance();

                if ($buildProgress->retries > 1) {
                    // Indicates it had problems before so move into robustness mode
                    $createState->isRobust = true;
                    $createState->save();
                }

                if ($createState->working) {
                    DUP_LOG::Trace("Create state is working");
                    //die(0);//rsr
                    // DupArchiveEngine::addItemsToArchive($createState, $scanReport->ARC, $archive);
                    DupArchiveEngine::addItemsToArchive($createState, $scanReport->ARC);

                    $buildProgress->set_build_failures($createState->failures);

                    if ($createState->isCriticalFailurePresent()) {
                        throw new Exception($createState->getFailureSummary());
                    }

                    $totalFileCount = count($scanReport->ARC->Files);
                    $package->Status = DupLiteSnapLibUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, $createState->currentFileIndex);
                    $buildProgress->retries = 0;
                    $createState->save();

                    DUP_LOG::TraceObject("Stored Create State", $createState);
                    DUP_LOG::TraceObject('Stored build_progress', $package->BuildProgress);

                    if ($createState->working == false) {
                        // Want it to do the final cleanup work in an entirely new thread so return immediately
                        $skipArchiveFinalization = true;
                        DUP_LOG::TraceObject("Done build phase. Create State=", $createState);
                    }
                }
            }
            catch (Exception $ex) {
                $message = __('Problem adding items to archive.', 'duplicator').' '.$ex->getMessage();

                DUP_Log::error(__('Problems adding items to archive.', 'duplicator'), $message, Dup_ErrorBehavior::LogOnly);
                DUP_Log::TraceObject($message." EXCEPTION:", $ex);
                //$buildProgress->failed = true;
                $buildProgress->set_failed($message);
                $package->setStatus(DUP_PackageStatus::ERROR);
                return true;
            }

            //-- Final Wrapup of the Archive
            if ((!$skipArchiveFinalization) && ($createState->working == false)) {

                DUP_LOG::Trace("Create state is not working and not skip archive finalization");

                if (!$buildProgress->installer_built) {

                    if ($package->Installer->build($package, false)) {
                        $package->Runtime = -1;
                        $package->ExeSize = DUP_Util::byteSize($package->Installer->Size);
                        $package->ZipSize = DUP_Util::byteSize($package->Archive->Size);
                        $package->update();
                    } else {
                        $package->update();
                        return;
                    }

                    DUP_Log::Trace("Installer has been built so running expand now");

                    $expandState = DUP_DupArchive_Expand_State::getInstance(true);

                    $expandState->archivePath            = $archivePath;
                    $expandState->working                = true;
                    $expandState->timeSliceInSecs        = self::WorkerTimeInSec;
                    $expandState->basePath               = DUP_Settings::getSsdirTmpPath().'/validate';
                    $expandState->throttleDelayInUs      = 0; // RSR TODO
                    $expandState->validateOnly           = true;
                    $expandState->validationType         = DupArchiveValidationTypes::Standard;
                    $expandState->working                = true;
                    $expandState->expectedDirectoryCount = count($scanReport->ARC->Dirs) - $createState->skippedDirectoryCount + $package->Installer->numDirsAdded;
                    $expandState->expectedFileCount      = count($scanReport->ARC->Files) + 1 - $createState->skippedFileCount + $package->Installer->numFilesAdded;    // database.sql will be in there

                    $expandState->save();

                    $sfc = count($scanReport->ARC->Files);
                    $nfa = $package->Installer->numFilesAdded;
                    Dup_Log::trace("####scan files {$sfc} skipped files {$createState->skippedFileCount} num files added {$nfa}");
                    DUP_LOG::traceObject("EXPAND STATE AFTER SAVE", $expandState);
                } else {

                    try {

                        $expandState = DUP_DupArchive_Expand_State::getInstance();

                        if ($buildProgress->retries > 1) {
                            // Indicates it had problems before so move into robustness mode
                            $expandState->isRobust = true;
                            $expandState->save();
                        }

                        DUP_Log::traceObject('Resumed validation expand state', $expandState);
                        DupArchiveEngine::expandArchive($expandState);
                        $buildProgress->set_validation_failures($expandState->failures);
                        $totalFileCount = count($scanReport->ARC->Files);
                        $archiveSize    = @filesize($expandState->archivePath);

                        $package->Status = DupLiteSnapLibUtil::getWorkPercent(DUP_PackageStatus::ARCVALIDATION, DUP_PackageStatus::COMPLETE, $archiveSize,
                                $expandState->archiveOffset);
                        DUP_LOG::TraceObject("package status after expand=", $package->Status);
                        DUP_LOG::Trace("archive size:{$archiveSize} expand offset:{$expandState->archiveOffset}");
                    }
                    catch (Exception $ex) {
                        DUP_Log::Trace('Exception:'.$ex->getMessage().':'.$ex->getTraceAsString());
                        $buildProgress->set_failed('Error validating archive');
                        $package->setStatus(DUP_PackageStatus::ERROR);
                        return true;
                    }

                    if ($expandState->isCriticalFailurePresent()) {
                        // Fail immediately if critical failure present - even if havent completed processing the entire archive.
                        $error_message = __('Critical failure present in validation', 'duplicator');
                        DUP_Log::error($error_message, $expandState->getFailureSummary(), Dup_ErrorBehavior::LogOnly);
                        $buildProgress->set_failed($error_message);
                        return true;
                    } else if (!$expandState->working) {

                        $buildProgress->archive_built = true;
                        $buildProgress->retries       = 0;
                        $package->update();

                        $timerAllEnd = DUP_Util::getMicrotime();
                        $timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $package->TimerStart);

                        DUP_LOG::traceObject("create state", $createState);

                        $archiveFileSize = @filesize($archivePath);
                        DUP_Log::info("COMPRESSED SIZE: ".DUP_Util::byteSize($archiveFileSize));
                        DUP_Log::info("ARCHIVE RUNTIME: {$timerAllSum}");
                        DUP_Log::info("MEMORY STACK: ".DUP_Server::getPHPMemory());
                        DUP_Log::info("CREATE WARNINGS: ".$createState->getFailureSummary(false, true));
                        DUP_Log::info("VALIDATION WARNINGS: ".$expandState->getFailureSummary(false, true));

                        $archive->file_count = $expandState->fileWriteCount + $expandState->directoryWriteCount;
                        $package->update();
                        $done = true;
                    } else {
                        $expandState->save();
                    }
                }
            }
        }
        catch (Exception $ex) {
            // Have to have a catchall since the main system that calls this function is not prepared to handle exceptions
            DUP_Log::trace('Top level create Exception:'.$ex->getMessage().':'.$ex->getTraceAsString());
            //$buildProgress->failed = true;
            $buildProgress->set_failed('Error encoundtered creating archive. See package log');
            return true;
        }

        $buildProgress->retries = 0;

        return $done;
    }
}PK�{2\~Dg�TTApackage/duparchive/class.pack.archive.duparchive.state.expand.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

require_once (DUPLICATOR_PLUGIN_PATH.'lib/dup_archive/classes/states/class.duparchive.state.expand.php');

class DUP_DupArchive_Expand_State extends DupArchiveExpandState
{
    public static function getInstance($reset = false)
    {   
        $instance = new DUP_DupArchive_Expand_State();
        
        if ($reset) {            
            $instance->initMembers();
        } else {
            $instance->loadMembers();            
        }

        return $instance;
    }

    private function loadMembers()
    {
        $data = DUP_Settings::Get('duparchive_expand_state');

        DUP_LOG::traceObject("****RAW EXPAND STATE LOADED****", $data);

        if($data->currentFileHeaderString != null) {
            $this->currentFileHeader      = DUP_JSON::decode($data->currentFileHeaderString);
        } else {
            $this->currentFileHeader      = null;
        }

        if($data->archiveHeaderString != null) {
            $this->archiveHeader      = DUP_JSON::decode($data->archiveHeaderString);
        } else {
            $this->archiveHeader      = null;
        }

        if ($data->failuresString) {
            $this->failures = DUP_JSON::decode($data->failuresString);
        } else {
            $this->failures = array();
        }

        DUP_Util::objectCopy($data, $this, array('archiveHeaderString', 'currentFileHeaderString', 'failuresString'));

//
//        $this->archiveOffset         = $data->archiveOffset;
//        $this->archivePath           = $data->archivePath;
//        $this->basePath              = $data->basePath;
//        $this->currentFileOffset     = $data->currentFileOffset;
//        $this->failures              = $data->failures;
//        $this->isCompressed          = $data->isCompressed;
//        $this->startTimestamp        = $data->startTimestamp;
//        $this->timeSliceInSecs       = $data->timeSliceInSecs;
//        $this->fileWriteCount        = $data->fileWriteCount;
//        $this->directoryWriteCount   = $data->directoryWriteCount;
//        $this->working               = $data->working;
//        $this->directoryModeOverride = $data->directoryModeOverride;
//        $this->fileModeOverride      = $data->fileModeOverride;
//        $this->throttleDelayInUs     = $data->throttleDelayInUs;
//        $this->validateOnly          = $data->validateOnly;
//        $this->validationType        = $data->validationType;
    }

    public function save()
    {
        $data = new stdClass();

        if($this->currentFileHeader != null) {
            $data->currentFileHeaderString      = DupLiteSnapJsonU::wp_json_encode($this->currentFileHeader);
        } else {
            $data->currentFileHeaderString      = null;
        }

        if($this->archiveHeader != null) {
            $data->archiveHeaderString      = DupLiteSnapJsonU::wp_json_encode($this->archiveHeader);
        } else {
            $data->archiveHeaderString      = null;
        }

        $data->failuresString = DupLiteSnapJsonU::wp_json_encode($this->failures);

        // Object members auto skipped
        DUP_Util::objectCopy($this, $data);

//        $data->archiveOffset         = $this->archiveOffset;
//        $data->archivePath           = $this->archivePath;
//        $data->basePath              = $this->basePath;
//        $data->currentFileOffset     = $this->currentFileOffset;
//        $data->failures              = $this->failures;
//        $data->isCompressed          = $this->isCompressed;
//        $data->startTimestamp        = $this->startTimestamp;
//        $data->timeSliceInSecs       = $this->timeSliceInSecs;
//        $data->fileWriteCount        = $this->fileWriteCount;
//        $data->directoryWriteCount   = $this->directoryWriteCount;
//        $data->working               = $this->working;
//        $data->directoryModeOverride = $this->directoryModeOverride;
//        $data->fileModeOverride      = $this->fileModeOverride;
//        $data->throttleDelayInUs     = $this->throttleDelayInUs;
//        $data->validateOnly          = $this->validateOnly;
//        $data->validationType        = $this->validationType;

        DUP_LOG::traceObject("****SAVING EXPAND STATE****", $this);
        DUP_LOG::traceObject("****SERIALIZED STATE****", $data);
        DUP_Settings::Set('duparchive_expand_state', $data);
        DUP_Settings::Save();
    }

    private function initMembers()
    {
        $this->currentFileHeader = null;
        $this->archiveOffset         = 0;
        $this->archiveHeader         = null;
        $this->archivePath           = null;
        $this->basePath              = null;
        $this->currentFileOffset     = 0;
        $this->failures              = array();
        $this->isCompressed          = false;
        $this->startTimestamp        = time();
        $this->timeSliceInSecs       = -1;
        $this->working               = false;
        $this->validateOnly          = false;
        $this->directoryModeOverride = -1;
        $this->fileModeOverride      = -1;
        $this->throttleDelayInUs     = 0;
    }
}
PK�{2\k�fNpackage/duparchive/index.phpnu�[���<?php
//silentPK�{2\�����,�,"package/class.pack.archive.zip.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

require_once (DUPLICATOR_PLUGIN_PATH.'classes/package/class.pack.archive.php');

/**
 *  Creates a zip file using the built in PHP ZipArchive class
 */
class DUP_Zip extends DUP_Archive
{
    //PRIVATE
    private static $compressDir;
    private static $countDirs  = 0;
    private static $countFiles = 0;
    private static $sqlPath;
    private static $zipPath;
    private static $zipFileSize;
    private static $zipArchive;
    private static $limitItems   = 0;
    private static $networkFlush = false;
    private static $scanReport;

    /**
     *  Creates the zip file and adds the SQL file to the archive
     */
    public static function create(DUP_Archive $archive, $buildProgress)
    {
        try {
            $timerAllStart     = DUP_Util::getMicrotime();
            $package_zip_flush = DUP_Settings::Get('package_zip_flush');

            self::$compressDir  = rtrim(wp_normalize_path(DUP_Util::safePath($archive->PackDir)), '/');
            self::$sqlPath      = DUP_Settings::getSsdirTmpPath()."/{$archive->Package->Database->File}";
            self::$zipPath      = DUP_Settings::getSsdirTmpPath()."/{$archive->File}";
            self::$zipArchive   = new ZipArchive();
            self::$networkFlush = empty($package_zip_flush) ? false : $package_zip_flush;

            $filterDirs       = empty($archive->FilterDirs)  ? 'not set' : $archive->FilterDirs;
            $filterExts       = empty($archive->FilterExts)  ? 'not set' : $archive->FilterExts;
			$filterFiles      = empty($archive->FilterFiles) ? 'not set' : $archive->FilterFiles;
            $filterOn         = ($archive->FilterOn) ? 'ON' : 'OFF';
            $filterDirsFormat  = rtrim(str_replace(';', "\n\t", $filterDirs));
			$filterFilesFormat = rtrim(str_replace(';', "\n\t", $filterFiles));
            $lastDirSuccess   = self::$compressDir;

            //LOAD SCAN REPORT
            $json             = file_get_contents(DUP_Settings::getSsdirTmpPath()."/{$archive->Package->NameHash}_scan.json");
            self::$scanReport = json_decode($json);

            DUP_Log::Info("\n********************************************************************************");
            DUP_Log::Info("ARCHIVE (ZIP):");
            DUP_Log::Info("********************************************************************************");
            $isZipOpen = (self::$zipArchive->open(self::$zipPath, ZIPARCHIVE::CREATE) === TRUE);
            if (!$isZipOpen) {
                $error_message = "Cannot open zip file with PHP ZipArchive.";
                $buildProgress->set_failed($error_message);
                DUP_Log::error($error_message, "Path location [".self::$zipPath."]", Dup_ErrorBehavior::LogOnly);
                $archive->Package->setStatus(DUP_PackageStatus::ERROR);
                return;
            }
            DUP_Log::Info("ARCHIVE DIR:  ".self::$compressDir);
            DUP_Log::Info("ARCHIVE FILE: ".basename(self::$zipPath));
            DUP_Log::Info("FILTERS: *{$filterOn}*");
            DUP_Log::Info("DIRS:\n\t{$filterDirsFormat}");
			DUP_Log::Info("FILES:\n\t{$filterFilesFormat}");
            DUP_Log::Info("EXTS:  {$filterExts}");

            DUP_Log::Info("----------------------------------------");
            DUP_Log::Info("COMPRESSING");
            DUP_Log::Info("SIZE:\t".self::$scanReport->ARC->Size);
            DUP_Log::Info("STATS:\tDirs ".self::$scanReport->ARC->DirCount." | Files ".self::$scanReport->ARC->FileCount);

            //ADD SQL
            $sql_ark_file_path = $archive->Package->getSqlArkFilePath();
            $isSQLInZip = self::$zipArchive->addFile(self::$sqlPath, $sql_ark_file_path);

            if ($isSQLInZip) {
                DUP_Log::Info("SQL ADDED: ".basename(self::$sqlPath));
            } else {
                $error_message = "Unable to add database.sql to archive.";
                DUP_Log::error($error_message, "SQL File Path [".self::$sqlPath."]", Dup_ErrorBehavior::LogOnly);
                $buildProgress->set_failed($error_message);
                $archive->Package->setStatus(DUP_PackageStatus::ERROR);
                return;
            }
            self::$zipArchive->close();
            self::$zipArchive->open(self::$zipPath, ZipArchive::CREATE);

            //ZIP DIRECTORIES
            $info = '';
            foreach (self::$scanReport->ARC->Dirs as $dir) {
                $emptyDir = $archive->getLocalDirPath($dir);

                if (is_readable($dir) && self::$zipArchive->addEmptyDir($emptyDir)) {
                    self::$countDirs++;
                    $lastDirSuccess = $dir;
                } else {
                    //Don't warn when dirtory is the root path
                    if (strcmp($dir, rtrim(self::$compressDir, '/')) != 0) {
                        $dir_path = strlen($dir) ? "[{$dir}]" : "[Read Error] - last successful read was: [{$lastDirSuccess}]";
                        $info .= "DIR: {$dir_path}\n";
                    }
                }
            }

            //LOG Unreadable DIR info
            if (strlen($info)) {
                DUP_Log::Info("\nWARNING: Unable to zip directories:");
                DUP_Log::Info($info);
            }

            /**
             * count update for integrity check
             */
            $sumItems   = (self::$countDirs + self::$countFiles);

            /* ZIP FILES: Network Flush
             *  This allows the process to not timeout on fcgi
             *  setups that need a response every X seconds */
            $totalFileCount = count(self::$scanReport->ARC->Files);
            $info = '';
            if (self::$networkFlush) {
                foreach (self::$scanReport->ARC->Files as $file) {
                    $file_size = @filesize($file);
                    $localFileName = $archive->getLocalFilePath($file);

                    if (is_readable($file)) {
                        if (defined('DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR') && DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR && $file_size < DUP_Constants::ZIP_STRING_LIMIT && self::$zipArchive->addFromString($localFileName, file_get_contents($file))) {
                            Dup_Log::Info("Adding {$file} to zip");
                            self::$limitItems++;
                            self::$countFiles++;
                        } elseif (self::$zipArchive->addFile($file, $localFileName)) {
                            Dup_Log::Info("Adding {$file} to zip");
                            self::$limitItems++;
                            self::$countFiles++;
                        } else {
                            $info .= "FILE: [{$file}]\n";
                        }
                    } else {
                        $info .= "FILE: [{$file}]\n";
                    }
                    //Trigger a flush to the web server after so many files have been loaded.
                    if (self::$limitItems > DUPLICATOR_ZIP_FLUSH_TRIGGER) {
                        self::$zipArchive->close();
                        self::$zipArchive->open(self::$zipPath);
                        self::$limitItems = 0;
                        DUP_Util::fcgiFlush();
                        DUP_Log::Info("Items archived [{$sumItems}] flushing response.");
                    }

                    if(self::$countFiles % 500 == 0) {
                        // Every so many files update the status so the UI can display
                        $archive->Package->Status = DupLiteSnapLibUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, self::$countFiles);
                        $archive->Package->update();
                    }
                }
            }
            //Normal
            else {
                foreach (self::$scanReport->ARC->Files as $file) {
                    $file_size = @filesize($file);
                    $localFileName = $archive->getLocalFilePath($file);

                    if (is_readable($file)) {
                        if (defined('DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR') && DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR && $file_size < DUP_Constants::ZIP_STRING_LIMIT && self::$zipArchive->addFromString($localFileName, file_get_contents($file))) {
                            self::$countFiles++;
                        } elseif (self::$zipArchive->addFile($file, $localFileName)) {
                            self::$countFiles++;
                        } else {
                            $info .= "FILE: [{$file}]\n";
                        }
                    } else {
                        $info .= "FILE: [{$file}]\n";
                    }

                    if(self::$countFiles % 500 == 0) {
                        // Every so many files update the status so the UI can display
                        $archive->Package->Status = DupLiteSnapLibUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, self::$countFiles);
                        $archive->Package->update();
                    }
                }
            }

            //LOG Unreadable FILE info
            if (strlen($info)) {
                DUP_Log::Info("\nWARNING: Unable to zip files:");
                DUP_Log::Info($info);
                unset($info);
            }

            DUP_Log::Info(print_r(self::$zipArchive, true));

            /**
             * count update for integrity check
             */
            $archive->file_count = self::$countDirs + self::$countFiles;
            DUP_Log::Info("FILE ADDED TO ZIP: ".$archive->file_count);


            //--------------------------------
            //LOG FINAL RESULTS
            DUP_Util::fcgiFlush();
            $zipCloseResult = self::$zipArchive->close();
            if($zipCloseResult) {
                DUP_Log::Info("COMPRESSION RESULT: '{$zipCloseResult}'");
            } else {
                $error_message = "ZipArchive close failure.";
                DUP_Log::error($error_message,
					"The ZipArchive engine is having issues zipping up the files on this server. For more details visit the FAQ\n"
					. "I'm getting a ZipArchive close failure when building. How can I resolve this?\n"
					. "[https://snapcreek.com/duplicator/docs/faqs-tech/#faq-package-165-q]",
                      Dup_ErrorBehavior::LogOnly);
                $buildProgress->set_failed($error_message);
                $archive->Package->setStatus(DUP_PackageStatus::ERROR);
                return;
            }

            $timerAllEnd = DUP_Util::getMicrotime();
            $timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $timerAllStart);

            self::$zipFileSize = @filesize(self::$zipPath);
            DUP_Log::Info("COMPRESSED SIZE: ".DUP_Util::byteSize(self::$zipFileSize));
            DUP_Log::Info("ARCHIVE RUNTIME: {$timerAllSum}");
            DUP_Log::Info("MEMORY STACK: ".DUP_Server::getPHPMemory());
            

            
        } catch (Exception $e) {
            $error_message = "Runtime error in class.pack.archive.zip.php constructor.";
            DUP_Log::error($error_message, "Exception: {$e}", Dup_ErrorBehavior::LogOnly);
            $buildProgress->set_failed($error_message);
            $archive->Package->setStatus(DUP_PackageStatus::ERROR);
            return;
        }
    }
}PK�{2\�O�1	1	&package/class.pack.archive.filters.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * The base class for all filter types Directories/Files/Extentions
 *
 * @package Duplicator
 * @subpackage classes/package
 *
 */

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_Archive_Filter_Scope_Base
{
    //All internal storage items that duplicator decides to filter
    public $Core     = array();
    //Global filter items added from settings
    public $Global = array();
    //Items when creating a package or template that a user decides to filter
    public $Instance = array();
}

/**
 * The filter types that belong to directories
 *
 * @package Duplicator
 * @subpackage classes/package
 *
 */
class DUP_Archive_Filter_Scope_Directory extends DUP_Archive_Filter_Scope_Base
{
    //Items that are not readable
    public $Warning    = array();
    //Items that are not readable
    public $Unreadable = array();
    public $AddonSites = array();
}

/**
 * The filter types that belong to files
 *
 * @package Duplicator
 * @subpackage classes/package
 *
 */
class DUP_Archive_Filter_Scope_File extends DUP_Archive_Filter_Scope_Directory
{
    //Items that are too large
    public $Size = array();

}

/**
 * The filter information object which store all information about the filtered
 * data that is gathered to the execution of a scan process
 *
 * @package Duplicator
 * @subpackage classes/package
 *
 */
class DUP_Archive_Filter_Info
{
    //Contains all folder filter info
    public $Dirs       = array();
    //Contains all file filter info
    public $Files      = array();
    //Contains all extensions filter info
    public $Exts       = array();
    public $UDirCount  = 0;
    public $UFileCount = 0;
    public $UExtCount  = 0;
	public $TreeSize;
	public $TreeWarning;

    /**
     *  Init this object
     */
    public function __construct()
    {
        $this->reset();
    }

        /**
     * reset and clean all object
     */
    public function reset()
    {
        $this->Dirs  = new DUP_Archive_Filter_Scope_Directory();
        $this->Files = new DUP_Archive_Filter_Scope_File();
        $this->Exts  = new DUP_Archive_Filter_Scope_Base();
		$this->TreeSize = array();
		$this->TreeWarning = array();
    }
}

PK�{2\Q����package/class.pack.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

require_once (DUPLICATOR_PLUGIN_PATH.'classes/utilities/class.u.php');
require_once (DUPLICATOR_PLUGIN_PATH.'classes/package/class.pack.archive.php');
require_once (DUPLICATOR_PLUGIN_PATH.'classes/package/class.pack.installer.php');
require_once (DUPLICATOR_PLUGIN_PATH.'classes/package/class.pack.database.php');

/**
 * Class used to keep track of the build progress
 *
 * @package Duplicator\classes
 */
class DUP_Build_Progress
{
    public $thread_start_time;
    public $initialized = false;
    public $installer_built = false;
    public $archive_started = false;
    public $archive_has_database = false;
    public $archive_built = false;
    public $database_script_built = false;
    public $failed = false;
    public $retries = 0;
    public $build_failures = array();
    public $validation_failures = array();

    /**
     *
     * @var DUP_Package
     */
    private $package;

    /**
     *
     * @param DUP_Package $package
     */
    public function __construct($package)
    {
        $this->package = $package;
    }

    /**
     *
     * @return bool
     */
    public function has_completed()
    {
        return $this->failed || ($this->installer_built && $this->archive_built && $this->database_script_built);
    }

    public function timed_out($max_time)
    {
        if ($max_time > 0) {
            $time_diff = time() - $this->thread_start_time;
            return ($time_diff >= $max_time);
        } else {
            return false;
        }
    }

    public function start_timer()
    {
        $this->thread_start_time = time();
    }

    public function set_validation_failures($failures)
	{
		$this->validation_failures = array();

		foreach ($failures as $failure) {
			$this->validation_failures[] = $failure;
		}
	}

	public function set_build_failures($failures)
	{
		$this->build_failures = array();

		foreach ($failures as $failure) {
			$this->build_failures[] = $failure->description;
		}
	}

	public function set_failed($failure_message = null)
    {
        if($failure_message !== null) {
            $failure = new StdClass();
            $failure->type        = 0;
            $failure->subject     = '';
            $failure->description = $failure_message;
            $failure->isCritical    = true;
            $this->build_failures[] = $failure;
        }

        $this->failed = true;
        $this->package->Status = DUP_PackageStatus::ERROR;
    }
}

/**
 * Class used to emulate and ENUM to give the status of a package from 0 to 100%
 *
 * @package Duplicator\classes
 */
final class DUP_PackageStatus
{
    private function __construct()
    {
    }

	const ERROR = -1;
	const CREATED  = 0;
    const START    = 10;
    const DBSTART  = 20;
    const DBDONE   = 30;
    const ARCSTART = 40;
    const ARCVALIDATION = 60;
    const ARCDONE = 65;
    const COMPLETE = 100;
}

/**
 * Class used to emulate and ENUM to determine how the package was made.
 * For lite only the MANUAL type is used.
 *
 * @package Duplicator\classes
 */
final class DUP_PackageType
{
    const MANUAL    = 0;
    const SCHEDULED = 1;
}

/**
 * Class used to emulate and ENUM to determine the various file types used in a package
 *
 * @package Duplicator\classes
 */
abstract class DUP_PackageFileType
{
    const Installer = 0;
    const Archive = 1;
    const SQL = 2;
    const Log = 3;
    const Scan = 4;
}

/**
 * Class used to store and process all Package logic
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator\classes
 */
class DUP_Package
{
    const OPT_ACTIVE = 'duplicator_package_active';

    //Properties
    public $Created;
    public $Version;
    public $VersionWP;
    public $VersionDB;
    public $VersionPHP;
    public $VersionOS;
    public $ID;
    public $Name;
    public $Hash;
    public $NameHash;
	//Set to DUP_PackageType
    public $Type;
    public $Notes;
    public $ScanFile;
    public $TimerStart = -1;
    public $Runtime;
    public $ExeSize;
    public $ZipSize;
    public $Status;
    public $WPUser;
    //Objects
    public $Archive;
    public $Installer;
    public $Database;

	public $BuildProgress;

    /**
     *  Manages the Package Process
     */
    function __construct()
    {
        $this->ID      = null;
        $this->Version = DUPLICATOR_VERSION;
        $this->Type      = DUP_PackageType::MANUAL;
        $this->Name      = self::getDefaultName();
        $this->Notes     = null;
        $this->Database  = new DUP_Database($this);
        $this->Archive   = new DUP_Archive($this);
        $this->Installer = new DUP_Installer($this);
		$this->BuildProgress = new DUP_Build_Progress($this);
		$this->Status = DUP_PackageStatus::CREATED;
    }

    /**
     * Generates a JSON scan report
     *
     * @return array of scan results
     *
     * @notes: Testing = /wp-admin/admin-ajax.php?action=duplicator_package_scan
     */
    public function runScanner()
    {
        $timerStart     = DUP_Util::getMicrotime();
        $report         = array();
        $this->ScanFile = "{$this->NameHash}_scan.json";

        $report['RPT']['ScanTime'] = "0";
        $report['RPT']['ScanFile'] = $this->ScanFile;

        //SERVER
        $srv           = DUP_Server::getChecks();
        $report['SRV'] = $srv['SRV'];

        //FILES
        $this->Archive->getScannerData();
        $dirCount  = count($this->Archive->Dirs);
        $fileCount = count($this->Archive->Files);
        $fullCount = $dirCount + $fileCount;

        $report['ARC']['Size']      = DUP_Util::byteSize($this->Archive->Size) or "unknown";
        $report['ARC']['DirCount']  = number_format($dirCount);
        $report['ARC']['FileCount'] = number_format($fileCount);
        $report['ARC']['FullCount'] = number_format($fullCount);
        $report['ARC']['WarnFileCount']       = count($this->Archive->FilterInfo->Files->Warning);
        $report['ARC']['WarnDirCount']        = count($this->Archive->FilterInfo->Dirs->Warning);
        $report['ARC']['UnreadableDirCount']  = count($this->Archive->FilterInfo->Dirs->Unreadable);
        $report['ARC']['UnreadableFileCount'] = count($this->Archive->FilterInfo->Files->Unreadable);
		$report['ARC']['FilterDirsAll'] = $this->Archive->FilterDirsAll;
		$report['ARC']['FilterFilesAll'] = $this->Archive->FilterFilesAll;
		$report['ARC']['FilterExtsAll'] = $this->Archive->FilterExtsAll;
        $report['ARC']['FilterInfo'] = $this->Archive->FilterInfo;
        $report['ARC']['RecursiveLinks'] = $this->Archive->RecursiveLinks;
        $report['ARC']['UnreadableItems'] = array_merge($this->Archive->FilterInfo->Files->Unreadable,$this->Archive->FilterInfo->Dirs->Unreadable);
        $report['ARC']['Status']['Size']  = ($this->Archive->Size > DUPLICATOR_SCAN_SIZE_DEFAULT) ? 'Warn' : 'Good';
        $report['ARC']['Status']['Names'] = (count($this->Archive->FilterInfo->Files->Warning) + count($this->Archive->FilterInfo->Dirs->Warning)) ? 'Warn' : 'Good';
        $report['ARC']['Status']['UnreadableItems'] = !empty($this->Archive->RecursiveLinks) || !empty($report['ARC']['UnreadableItems'])? 'Warn' : 'Good';
        /*
        $overwriteInstallerParams = apply_filters('duplicator_overwrite_params_data', array());
        $package_can_be_migrate = !(isset($overwriteInstallerParams['mode_chunking']['value'])
                                    && $overwriteInstallerParams['mode_chunking']['value'] == 3
                                    && isset($overwriteInstallerParams['mode_chunking']['formStatus'])
                                    && $overwriteInstallerParams['mode_chunking']['formStatus'] == 'st_infoonly');
        */
        $package_can_be_migrate = true;
        $report['ARC']['Status']['MigratePackage'] = $package_can_be_migrate ? 'Good' : 'Warn';
        $report['ARC']['Status']['CanbeMigratePackage'] = $package_can_be_migrate;

        $privileges_to_show_create_proc_func = true;
        $procedures = $GLOBALS['wpdb']->get_col("SHOW PROCEDURE STATUS WHERE `Db` = '".$GLOBALS['wpdb']->dbname."'", 1);
        if (count($procedures)) {
            $create = $GLOBALS['wpdb']->get_row("SHOW CREATE PROCEDURE `".$procedures[0]."`", ARRAY_N);
            $privileges_to_show_create_proc_func = isset($create[2]);
        }

        $functions = $GLOBALS['wpdb']->get_col("SHOW FUNCTION STATUS WHERE `Db` = '".$GLOBALS['wpdb']->dbname."'", 1);
        if (count($functions)) {
            $create = $GLOBALS['wpdb']->get_row("SHOW CREATE FUNCTION `".$functions[0]."`", ARRAY_N);
            $privileges_to_show_create_proc_func = $privileges_to_show_create_proc_func && isset($create[2]);
        }

        $privileges_to_show_create_proc_func = apply_filters('duplicator_privileges_to_show_create_proc_func', $privileges_to_show_create_proc_func);
        $report['ARC']['Status']['showCreateProcFuncStatus'] = $privileges_to_show_create_proc_func ? 'Good' : 'Warn';
        $report['ARC']['Status']['showCreateProcFunc'] = $privileges_to_show_create_proc_func;

        //$report['ARC']['Status']['Big']   = count($this->Archive->FilterInfo->Files->Size) ? 'Warn' : 'Good';
        $report['ARC']['Dirs']  = $this->Archive->Dirs;
        $report['ARC']['Files'] = $this->Archive->Files;
		$report['ARC']['Status']['AddonSites'] = count($this->Archive->FilterInfo->Dirs->AddonSites) ? 'Warn' : 'Good';

        //DATABASE
        $db  = $this->Database->getScannerData();
        $report['DB'] = $db;

        //Lite Limits
        $rawTotalSize = $this->Archive->Size + $report['DB']['RawSize'];
        $report['LL']['TotalSize'] = DUP_Util::byteSize($rawTotalSize);
        $report['LL']['Status']['TotalSize'] = ($rawTotalSize > DUPLICATOR_MAX_DUPARCHIVE_SIZE) ? 'Fail' : 'Good';

        $warnings = array(
            $report['SRV']['SYS']['ALL'],
            $report['SRV']['WP']['ALL'],
            $report['ARC']['Status']['Size'],
            $report['ARC']['Status']['Names'],
            $db['Status']['DB_Size'],
            $db['Status']['DB_Rows']
		);

        //array_count_values will throw a warning message if it has null values,
        //so lets replace all nulls with empty string
        foreach ($warnings as $i => $value) {
            if (is_null($value)) {
                $warnings[$i] = '';
            }
        }

        $warn_counts               = is_array($warnings) ? array_count_values($warnings) : 0;
        $report['RPT']['Warnings'] = is_null($warn_counts['Warn']) ? 0 : $warn_counts['Warn'];
        $report['RPT']['Success']  = is_null($warn_counts['Good']) ? 0 : $warn_counts['Good'];
        $report['RPT']['ScanTime'] = DUP_Util::elapsedTime(DUP_Util::getMicrotime(), $timerStart);
        $fp                        = fopen(DUP_Settings::getSsdirTmpPath()."/{$this->ScanFile}", 'w');

        fwrite($fp, DupLiteSnapJsonU::wp_json_encode_pprint($report));
        fclose($fp);

        return $report;
    }

    /**
     * Validates the inputs from the UI for correct data input
	 *
     * @return DUP_Validator
     */
    public function validateInputs()
    {
        $validator = new DUP_Validator();

        $validator->filter_custom($this->Name , DUP_Validator::FILTER_VALIDATE_NOT_EMPTY ,
            array(  'valkey' => 'Name' ,
                    'errmsg' => __('Package name can\'t be empty', 'duplicator'),
                )
            );

        $validator->explode_filter_custom($this->Archive->FilterDirs, ';' , DUP_Validator::FILTER_VALIDATE_FOLDER ,
            array(  'valkey' => 'FilterDirs' ,
                    'errmsg' => __('Directories: <b>%1$s</b> isn\'t a valid path', 'duplicator'),
                )
            );

        $validator->explode_filter_custom($this->Archive->FilterExts, ';' , DUP_Validator::FILTER_VALIDATE_FILE_EXT ,
            array(  'valkey' => 'FilterExts' ,
                    'errmsg' => __('File extension: <b>%1$s</b> isn\'t a valid extension', 'duplicator'),
                )
            );

        $validator->explode_filter_custom($this->Archive->FilterFiles, ';' , DUP_Validator::FILTER_VALIDATE_FILE ,
            array(  'valkey' => 'FilterFiles' ,
                    'errmsg' => __('Files: <b>%1$s</b> isn\'t a valid file name', 'duplicator'),
                )
            );

		//FILTER_VALIDATE_DOMAIN throws notice message on PHP 5.6
		if (defined('FILTER_VALIDATE_DOMAIN')) {
			$validator->filter_var($this->Installer->OptsDBHost, FILTER_VALIDATE_DOMAIN ,  array(
						'valkey' => 'OptsDBHost' ,
						'errmsg' => __('MySQL Server Host: <b>%1$s</b> isn\'t a valid host', 'duplicator'),
						'acc_vals' => array(
							'' ,
							'localhost'
						)
					)
				);
		}

        $validator->filter_var($this->Installer->OptsDBPort, FILTER_VALIDATE_INT , array(
                    'valkey' => 'OptsDBPort' ,
                    'errmsg' => __('MySQL Server Port: <b>%1$s</b> isn\'t a valid port', 'duplicator'),
                    'acc_vals' => array(
                        ''
                    ),
                    'options' => array(
                       'min_range' => 0
                    )
                )
            );

        return $validator;
    }
    
    /**
     * 
     * @return string
     */
    public function getInstDownloadName()
    {
        switch (DUP_Settings::Get('installer_name_mode')) {
            case DUP_Settings::INSTALLER_NAME_MODE_SIMPLE:
                return DUP_Installer::DEFAULT_INSTALLER_FILE_NAME_WITHOUT_HASH;

            case DUP_Settings::INSTALLER_NAME_MODE_WITH_HASH:
            default:
                return basename($this->getLocalPackageFile(DUP_PackageFileType::Installer));
        }
    }

    /**
     *
     * @return bool return true if package is a active_package_id and status is bewteen 0 and 100
     */
    public function isRunning() {
        return DUP_Settings::Get('active_package_id') == $this->ID && $this->Status >= 0 && $this->Status < 100;
    }

    protected function cleanObjectBeforeSave()
    {
        $this->Archive->FilterInfo->reset();
    }

	/**
     * Saves the active package to the package table
     *
     * @return void
     */
	public function save($extension)
	{
        global $wpdb;

		$this->Archive->Format	= strtoupper($extension);
		$this->Archive->File	= "{$this->NameHash}_archive.{$extension}";
		$this->Installer->File	= apply_filters('duplicator_installer_file_path', "{$this->NameHash}_installer.php");
		$this->Database->File	= "{$this->NameHash}_database.sql";
		$this->WPUser          = isset($current_user->user_login) ? $current_user->user_login : 'unknown';

		//START LOGGING
		DUP_Log::Open($this->NameHash);

        do_action('duplicator_lite_build_before_start' , $this);

		$this->writeLogHeader();

		//CREATE DB RECORD
        $this->cleanObjectBeforeSave();
		$packageObj = serialize($this);
		if (!$packageObj) {
			DUP_Log::error("Unable to serialize package object while building record.");
		}

		$this->ID = $this->getHashKey($this->Hash);

		if ($this->ID != 0) {
			DUP_LOG::Trace("ID non zero so setting to start");
			$this->setStatus(DUP_PackageStatus::START);
		} else {
            DUP_LOG::Trace("ID IS zero so creating another package");
            $tablePrefix = DUP_Util::getTablePrefix();
			$results = $wpdb->insert($tablePrefix . "duplicator_packages", array(
				'name' => $this->Name,
				'hash' => $this->Hash,
				'status' => DUP_PackageStatus::START,
				'created' => current_time('mysql', get_option('gmt_offset', 1)),
				'owner' => isset($current_user->user_login) ? $current_user->user_login : 'unknown',
				'package' => $packageObj)
			);
			if ($results === false) {
				$wpdb->print_error();
				DUP_LOG::Trace("Problem inserting package: {$wpdb->last_error}");

				DUP_Log::error("Duplicator is unable to insert a package record into the database table.", "'{$wpdb->last_error}'");
			}
			$this->ID = $wpdb->insert_id;
		}

        do_action('duplicator_lite_build_start' , $this);
	}

	/**
     * Delete all files associated with this package ID
     *
     * @return void
     */
    public function delete()
    {
        global $wpdb;

        $tablePrefix = DUP_Util::getTablePrefix();
        $tblName  = $tablePrefix.'duplicator_packages';
        $getResult = $wpdb->get_results($wpdb->prepare("SELECT name, hash FROM `{$tblName}` WHERE id = %d", $this->ID), ARRAY_A);

        if ($getResult) {
            $row       = $getResult[0];
            $nameHash  = "{$row['name']}_{$row['hash']}";
            $delResult = $wpdb->query($wpdb->prepare("DELETE FROM `{$tblName}` WHERE id = %d", $this->ID));

            if ($delResult != 0) {
                $tmpPath = DUP_Settings::getSsdirTmpPath();
                $ssdPath =  DUP_Settings::getSsdirPath();

                $archiveFile = $this->getArchiveFilename();
                $wpConfigFile = "{$this->NameHash}_wp-config.txt";

                //Perms
                @chmod($tmpPath."/{$archiveFile}", 0644);
                @chmod($tmpPath."/{$nameHash}_database.sql", 0644);
                @chmod($tmpPath."/{$nameHash}_installer.php", 0644);
                @chmod($tmpPath."/{$nameHash}_scan.json", 0644);
                @chmod($tmpPath."/{$wpConfigFile}", 0644);
                @chmod($tmpPath."/{$nameHash}.log", 0644);

                @chmod($ssdPath."/{$archiveFile}", 0644);
                @chmod($ssdPath."/{$nameHash}_database.sql", 0644);
                @chmod($ssdPath."/{$nameHash}_installer.php", 0644);
                @chmod($ssdPath."/{$nameHash}_scan.json", 0644);
                // In older version, The plugin was storing [HASH]_wp-config.txt in main storage area. The below line code is for backward compatibility
                @chmod($ssdPath."/{$wpConfigFile}", 0644);
                @chmod($ssdPath."/{$nameHash}.log", 0644);
                //Remove
                @unlink($tmpPath."/{$archiveFile}");
                @unlink($tmpPath."/{$nameHash}_database.sql");
                @unlink($tmpPath."/{$nameHash}_installer.php");
                @unlink($tmpPath."/{$nameHash}_scan.json");
                @unlink($tmpPath."/{$wpConfigFile}");
                @unlink($tmpPath."/{$nameHash}.log");

                @unlink($ssdPath."/{$archiveFile}");
                @unlink($ssdPath."/{$nameHash}_database.sql");
                @unlink($ssdPath."/{$nameHash}_installer.php");
                @unlink($ssdPath."/{$nameHash}_scan.json");
                // In older version, The plugin was storing [HASH]_wp-config.txt in main storage area. The below line code is for backward compatibility
                @unlink($ssdPath."/{$wpConfigFile}");
                @unlink($ssdPath."/{$nameHash}.log");
            }
        }
    }

    /**
     * Get package archive size.
     * If package isn't complete it get size from sum of temp files.
     *
     * @return int size in byte 
     */
    public function getArchiveSize() {
        $size = 0;

        if ($this->Status >= DUP_PackageStatus::COMPLETE) {
            $size = $this->Archive->Size;
        } else {
            $tmpSearch = glob(DUP_Settings::getSsdirTmpPath() . "/{$this->NameHash}_*");
            if (is_array($tmpSearch)) {
                $result = array_map('filesize', $tmpSearch);
                $size = array_sum($result);
            }
        }

        return $size;
    }

    /**
     * Return true if active package exist and have an active status
     *
     * @return bool
     */
    public static function is_active_package_present()
    {
        $activePakcs = self::get_ids_by_status(array(
                array('op' => '>=', 'status' => DUP_PackageStatus::CREATED),
                array('op' => '<', 'status' => DUP_PackageStatus::COMPLETE)
                ), true);

        return in_array(DUP_Settings::Get('active_package_id'), $activePakcs);
    }

    /**
     *
     * @param array $conditions es. [
     *                                  relation = 'AND',
     *                                  [ 'op' => '>=' ,
     *                                    'status' =>  DUP_PackageStatus::START ]
     *                                  [ 'op' => '<' ,
     *                                    'status' =>  DUP_PackageStatus::COMPLETED ]
     *                              ]
     * @return string
     */
    protected static function statusContitionsToWhere($conditions = array())
    {
        if (empty($conditions)) {
            return '';
        } else {
            $accepted_op = array('<', '>', '=', '<>', '>=', '<=');
            $relation    = (isset($conditions['relation']) && strtoupper($conditions['relation']) == 'OR') ? ' OR ' : ' AND ';
            unset($conditions['relation']);

            $str_conds = array();

            foreach ($conditions as $cond) {
                $op          = (isset($cond['op']) && in_array($cond['op'], $accepted_op)) ? $cond['op'] : '=';
                $status      = isset($cond['status']) ? (int) $cond['status'] : 0;
                $str_conds[] = 'status '.$op.' '.$status;
            }

            return ' WHERE '.implode($relation, $str_conds).' ';
        }
    }

    /**
     * Get packages with status conditions and/or pagination
     *
     * @global wpdb $wpdb
     *
     * @param array                 //  $conditions es. [
     *                                      relation = 'AND',
     *                                      [ 'op' => '>=' ,
     *                                        'status' =>  DUP_PackageStatus::START ]
     *                                      [ 'op' => '<' ,
     *                                        'status' =>  DUP_PackageStatus::COMPLETED ]
     *                                   ]
     *                                  if empty get all pacages
     * @param int $limit            // max row numbers fi false the limit is PHP_INT_MAX
     * @param int $offset           // offset 0 is at begin
     * @param string $orderBy       // default `id` ASC if empty no order
     * @param string $resultType    //  ids => int[]
     *                                  row => row without backage blob
     *                                  fullRow => row with package blob
     *                                  objs => array of DUP_Package objects
     *
     * @return DUP_Package[]|array[]|int[]
     */
    public static function get_packages_by_status($conditions = array(), $limit = false, $offset = 0, $orderBy = '`id` ASC', $resultType = 'obj')
    {
        global $wpdb;
        $table = $wpdb->base_prefix."duplicator_packages";
        $where = self::statusContitionsToWhere($conditions);

        $packages   = array();
        $offsetStr  = ' OFFSET '.(int) $offset;
        $limitStr   = ' LIMIT '.($limit !== false ? max(0, $limit) : PHP_INT_MAX);
        $orderByStr = empty($orderBy) ? '' : ' ORDER BY '.$orderBy.' ';
        switch ($resultType) {
            case 'ids':
                $cols = '`id`';
                break;
            case 'row':
                $cols = '`id`,`name`,`hash`,`status`,`created`,`owner`';
                break;
            case 'fullRow':
                $cols = '*';
                break;
            case 'objs':
            default:
                $cols = '`status`,`package`';
                break;
        }

        $rows = $wpdb->get_results('SELECT '.$cols.' FROM `'.$table.'` '.$where.$orderByStr.$limitStr.$offsetStr);
        if ($rows != null) {
            switch ($resultType) {
                case 'ids':
                    foreach ($rows as $row) {
                        $packages[] = $row->id;
                    }
                    break;
                case 'row':
                case 'fullRow':
                    $packages = $rows;
                    break;
                case 'objs':
                default:
                    foreach ($rows as $row) {
                        $Package = unserialize($row->package);
                        if ($Package) {
                            // We was not storing Status in Lite 1.2.52, so it is for backward compatibility
                            if (!isset($Package->Status)) {
                                $Package->Status = $row->status;
                            }

                            $packages[] = $Package;
                        }
                    }
            }
        }
        return $packages;
    }

    /**
     * Get packages row db with status conditions and/or pagination
     *
     * @param array             //  $conditions es. [
     *                                  relation = 'AND',
     *                                  [ 'op' => '>=' ,
     *                                    'status' =>  DUP_PackageStatus::START ]
     *                                  [ 'op' => '<' ,
     *                                    'status' =>  DUP_PackageStatus::COMPLETED ]
     *                              ]
     *                              if empty get all pacages
     * @param int $limit        // max row numbers
     * @param int $offset       // offset 0 is at begin
     * @param string $orderBy   // default `id` ASC if empty no order
     *
     * @return array[]      // return row database without package blob
     */
    public static function get_row_by_status($conditions = array(), $limit = false, $offset = 0, $orderBy = '`id` ASC')
    {
        return self::get_packages_by_status($conditions, $limit, $offset, $orderBy, 'row');
    }

    /**
     * Get packages ids with status conditions and/or pagination
     *
     * @param array             //  $conditions es. [
     *                                  relation = 'AND',
     *                                  [ 'op' => '>=' ,
     *                                    'status' =>  DUP_PackageStatus::START ]
     *                                  [ 'op' => '<' ,
     *                                    'status' =>  DUP_PackageStatus::COMPLETED ]
     *                              ]
     *                              if empty get all pacages
     * @param int $limit        // max row numbers
     * @param int $offset       // offset 0 is at begin
     * @param string $orderBy   // default `id` ASC if empty no order
     *
     * @return array[]      // return row database without package blob
     */
    public static function get_ids_by_status($conditions = array(), $limit = false, $offset = 0, $orderBy = '`id` ASC')
    {
        return self::get_packages_by_status($conditions, $limit, $offset, $orderBy, 'ids');
    }

    /**
     * count package with status condition
     *
     * @global wpdb $wpdb
     * @param array $conditions es. [
     *                                  relation = 'AND',
     *                                  [ 'op' => '>=' ,
     *                                    'status' =>  DUP_PackageStatus::START ]
     *                                  [ 'op' => '<' ,
     *                                    'status' =>  DUP_PackageStatus::COMPLETED ]
     *                              ]
     * @return int
     */
    public static function count_by_status($conditions = array())
    {
        global $wpdb;

        $table = $wpdb->base_prefix."duplicator_packages";
        $where = self::statusContitionsToWhere($conditions);

        $count = $wpdb->get_var("SELECT count(id) FROM `{$table}` ".$where);
        return $count;
    }

    /**
     * Execute $callback function foreach package result
     * For each iteration the memory is released
     *
     * @param callable $callback    // function callback(DUP_Package $package)
     * @param array             //  $conditions es. [
     *                                  relation = 'AND',
     *                                  [ 'op' => '>=' ,
     *                                    'status' =>  DUP_PackageStatus::START ]
     *                                  [ 'op' => '<' ,
     *                                    'status' =>  DUP_PackageStatus::COMPLETED ]
     *                              ]
     *                              if empty get all pacages
     * @param int $limit        // max row numbers
     * @param int $offset       // offset 0 is at begin
     * @param string $orderBy   // default `id` ASC if empty no order
     *
     * @return void
     */
    public static function by_status_callback($callback, $conditions = array(), $limit = false, $offset = 0, $orderBy = '`id` ASC')
    {
        if (!is_callable($callback)) {
            throw new Exception('No callback function passed');
        }

        $offset      = max(0, $offset);
        $numPackages = self::count_by_status($conditions);
        $maxLimit    = $offset + ($limit !== false ? max(0, $limit) : PHP_INT_MAX - $offset);
        $numPackages = min($maxLimit, $numPackages);
        $orderByStr = empty($orderBy) ? '' : ' ORDER BY '.$orderBy.' ';

        global $wpdb;
        $table = $wpdb->base_prefix."duplicator_packages";
        $where = self::statusContitionsToWhere($conditions);
        $sql   = 'SELECT * FROM `'.$table.'` '.$where.$orderByStr.' LIMIT 1 OFFSET ';

        for (; $offset < $numPackages; $offset ++) {
            $rows = $wpdb->get_results($sql.$offset);
            if ($rows != null) {
                $Package = @unserialize($rows[0]->package);
                if ($Package) {
                    if (empty($Package->ID)) {
                        $Package->ID = $rows[0]->id;
                    }
                    // We was not storing Status in Lite 1.2.52, so it is for backward compatibility
                    if (!isset($Package->Status)) {
                        $Package->Status = $rows[0]->status;
                    }
                    call_user_func($callback, $Package);
                    unset($Package);
                }
                unset($rows);
            }
        }
    }

    public static function purge_incomplete_package()
    {
        $packages = self::get_packages_by_status(array(
                'relation' => 'AND',
                array('op' => '>=', 'status' => DUP_PackageStatus::CREATED),
                array('op' => '<', 'status' => DUP_PackageStatus::COMPLETE)
                ), 1, 0, '`id` ASC');


        if (count($packages) > 0) {
            foreach ($packages as $package) {
                if (!$package->isRunning()) {
                    $package->delete();
                }
            }
        }
    }

    /**
     * Check the DupArchive build to make sure it is good
     *
     * @return void
     */
	public function runDupArchiveBuildIntegrityCheck()
    {
        //INTEGRITY CHECKS
        //We should not rely on data set in the serlized object, we need to manually check each value
        //indepentantly to have a true integrity check.
        DUP_Log::info("\n********************************************************************************");
        DUP_Log::info("INTEGRITY CHECKS:");
        DUP_Log::info("********************************************************************************");

        //------------------------
        //SQL CHECK:  File should be at minimum 5K.  A base WP install with only Create tables is about 9K
        $sql_temp_path = DUP_Settings::getSsdirTmpPath() . '/' . $this->Database->File;
        $sql_temp_size = @filesize($sql_temp_path);
        $sql_easy_size = DUP_Util::byteSize($sql_temp_size);
        $sql_done_txt = DUP_Util::tailFile($sql_temp_path, 3);
        DUP_Log::Trace('[DUP ARCHIVE] '.__FUNCTION__.' '.__LINE__);

        // Note: Had to add extra size check of 800 since observed bad sql when filter was on 
        if (!strstr($sql_done_txt, 'DUPLICATOR_MYSQLDUMP_EOF') || (!$this->Database->FilterOn && $sql_temp_size < 5120) || ($this->Database->FilterOn && $this->Database->info->tablesFinalCount > 0 && $sql_temp_size < 800)) {
            DUP_Log::Trace('[DUP ARCHIVE] '.__FUNCTION__.' '.__LINE__);

            $error_text = "ERROR: SQL file not complete.  The file {$sql_temp_path} looks too small ($sql_temp_size bytes) or the end of file marker was not found.";
            $this->BuildProgress->set_failed($error_text);
            $this->setStatus(DUP_PackageStatus::ERROR);
            DUP_Log::error("$error_text", '', Dup_ErrorBehavior::LogOnly);
            return;
        }

        DUP_Log::Trace('[DUP ARCHIVE] '.__FUNCTION__.' '.__LINE__);
        DUP_Log::Info("SQL FILE: {$sql_easy_size}");

        //------------------------
        //INSTALLER CHECK:
        $exe_temp_path = DUP_Settings::getSsdirTmpPath() . '/' . $this->Installer->File;

        $exe_temp_size = @filesize($exe_temp_path);
        $exe_easy_size = DUP_Util::byteSize($exe_temp_size);
        $exe_done_txt = DUP_Util::tailFile($exe_temp_path, 10);

        if (!strstr($exe_done_txt, 'DUPLICATOR_INSTALLER_EOF') && !$this->BuildProgress->failed) {
            //$this->BuildProgress->failed = true;
            $error_message = 'ERROR: Installer file not complete.  The end of file marker was not found.  Please try to re-create the package.';

            $this->BuildProgress->set_failed($error_message);
            $this->Status = DUP_PackageStatus::ERROR;
            $this->update();
            DUP_Log::error($error_message, '', Dup_ErrorBehavior::LogOnly);
            return;
        }
        DUP_Log::info("INSTALLER FILE: {$exe_easy_size}");

        //------------------------
        //ARCHIVE CHECK:
        DUP_LOG::trace("Archive file count is " . $this->Archive->file_count);

        if ($this->Archive->file_count != -1) {
            $zip_easy_size = DUP_Util::byteSize($this->Archive->Size);
            if (!($this->Archive->Size)) {
                //$this->BuildProgress->failed = true;
                $error_message = "ERROR: The archive file contains no size.";

                $this->BuildProgress->set_failed($error_message);
                $this->setStatus(DUP_PackageStatus::ERROR);
                DUP_Log::error($error_message, "Archive Size: {$zip_easy_size}", Dup_ErrorBehavior::LogOnly);
                return;
            }

            $scan_filepath = DUP_Settings::getSsdirTmpPath() . "/{$this->NameHash}_scan.json";
            $json = '';

            DUP_LOG::Trace("***********Does $scan_filepath exist?");
            if (file_exists($scan_filepath)) {
                $json = file_get_contents($scan_filepath);
            } else {
                $error_message = sprintf(__("Can't find Scanfile %s. Please ensure there no non-English characters in the package or schedule name.", 'duplicator'), $scan_filepath);

                //$this->BuildProgress->failed = true;
                //$this->setStatus(DUP_PackageStatus::ERROR);
                $this->BuildProgress->set_failed($error_message);
                $this->setStatus(DUP_PackageStatus::ERROR);

                DUP_Log::error($error_message, '', Dup_ErrorBehavior::LogOnly);
                return;
            }

            $scanReport = json_decode($json);

			//RSR TODO: rework/simplify the validateion of duparchive
            $dirCount = count($scanReport->ARC->Dirs);
            $numInstallerDirs = $this->Installer->numDirsAdded;
            $fileCount = count($scanReport->ARC->Files);
            $numInstallerFiles = $this->Installer->numFilesAdded;

            $expected_filecount = $dirCount + $numInstallerDirs + $fileCount + $numInstallerFiles + 1 -1;   // Adding database.sql but subtracting the root dir
			//Dup_Log::trace("#### a:{$dirCount} b:{$numInstallerDirs} c:{$fileCount} d:{$numInstallerFiles} = {$expected_filecount}");

            DUP_Log::info("ARCHIVE FILE: {$zip_easy_size} ");
            DUP_Log::info(sprintf(__('EXPECTED FILE/DIRECTORY COUNT: %1$s', 'duplicator'), number_format($expected_filecount)));
            DUP_Log::info(sprintf(__('ACTUAL FILE/DIRECTORY COUNT: %1$s', 'duplicator'), number_format($this->Archive->file_count)));

            $this->ExeSize = $exe_easy_size;
            $this->ZipSize = $zip_easy_size;

            /* ------- ZIP Filecount Check -------- */
            // Any zip of over 500 files should be within 2% - this is probably too loose but it will catch gross errors
            DUP_LOG::trace("Expected filecount = $expected_filecount and archive filecount=" . $this->Archive->file_count);

            if ($expected_filecount > 500) {
                $straight_ratio = (float) $expected_filecount / (float) $this->Archive->file_count;

                $warning_count = $scanReport->ARC->WarnFileCount + $scanReport->ARC->WarnDirCount + $scanReport->ARC->UnreadableFileCount + $scanReport->ARC->UnreadableDirCount;
                DUP_LOG::trace("Warn/unread counts) warnfile:{$scanReport->ARC->WarnFileCount} warndir:{$scanReport->ARC->WarnDirCount} unreadfile:{$scanReport->ARC->UnreadableFileCount} unreaddir:{$scanReport->ARC->UnreadableDirCount}");
                $warning_ratio = ((float) ($expected_filecount + $warning_count)) / (float) $this->Archive->file_count;
                DUP_LOG::trace("Straight ratio is $straight_ratio and warning ratio is $warning_ratio. # Expected=$expected_filecount # Warning=$warning_count and #Archive File {$this->Archive->file_count}");

                // Allow the real file count to exceed the expected by 10% but only allow 1% the other way
                if (($straight_ratio < 0.90) || ($straight_ratio > 1.01)) {
                    // Has to exceed both the straight as well as the warning ratios
                    if (($warning_ratio < 0.90) || ($warning_ratio > 1.01)) {
                        $error_message = sprintf('ERROR: File count in archive vs expected suggests a bad archive (%1$d vs %2$d).', $this->Archive->file_count, $expected_filecount);
                        $this->BuildProgress->set_failed($error_message);
                        $this->Status  = DUP_PackageStatus::ERROR;
                        $this->update();

                        DUP_Log::error($error_message, '');
                        return;
                    }
                }
            }
        }

        /* ------ ZIP CONSISTENCY CHECK ------ */
        if ($this->Archive->getBuildMode() == DUP_Archive_Build_Mode::ZipArchive) {
            DUP_LOG::trace("Running ZipArchive consistency check");
            $zipPath = DUP_Settings::getSsdirTmpPath()."/{$this->Archive->File}";
                        
            $zip = new ZipArchive();

            // ZipArchive::CHECKCONS will enforce additional consistency checks
            $res = $zip->open($zipPath, ZipArchive::CHECKCONS);

            if ($res !== TRUE) {
                $consistency_error = sprintf(__('ERROR: Cannot open created archive. Error code = %1$s', 'duplicator'), $res);

                DUP_LOG::trace($consistency_error);
                switch ($res) {
                    case ZipArchive::ER_NOZIP :
                        $consistency_error = __('ERROR: Archive is not valid zip archive.', 'duplicator');
                        break;

                    case ZipArchive::ER_INCONS :
                        $consistency_error = __("ERROR: Archive doesn't pass consistency check.", 'duplicator');
                        break;


                    case ZipArchive::ER_CRC :
                        $consistency_error = __("ERROR: Archive checksum is bad.", 'duplicator');
                        break;
                }

                $this->BuildProgress->set_failed($consistency_error);
                $this->Status = DUP_PackageStatus::ERROR;
                $this->update();

                DUP_LOG::trace($consistency_error);
                DUP_Log::error($consistency_error, '');
            } else {
                DUP_Log::info(__('ARCHIVE CONSISTENCY TEST: Pass', 'duplicator'));
                DUP_LOG::trace("Zip for package $this->ID passed consistency test");
            }

            $zip->close();
        }
    }

    public function getLocalPackageFile($file_type)
    {
        $file_path = null;

        if ($file_type == DUP_PackageFileType::Installer) {
            DUP_Log::Trace("Installer requested");
            $file_name = apply_filters('duplicator_installer_file_path', $this->getInstallerFilename());
        } else if ($file_type == DUP_PackageFileType::Archive) {
            DUP_Log::Trace("Archive requested");
            $file_name = $this->getArchiveFilename();
        } else if ($file_type == DUP_PackageFileType::SQL) {
            DUP_Log::Trace("SQL requested");
            $file_name = $this->getDatabaseFilename();
        } else {
            DUP_Log::Trace("Log requested");
            $file_name = $this->getLogFilename();
        }

        $file_path = DUP_Settings::getSsdirPath() . "/$file_name";
        DUP_Log::Trace("File path $file_path");

        if (file_exists($file_path)) {
            return $file_path;
        } else {
            return null;
        }
    }

    public function getScanFilename()
    {
        return $this->NameHash . '_scan.json';
    }

    public function getScanUrl()
    {
        return DUP_Settings::getSsdirUrl()."/".$this->getScanFilename();
    }

    public function getLogFilename()
    {
        return $this->NameHash . '.log';
    }

    public function getLogUrl()
    {
        return DUP_Settings::getSsdirUrl()."/".$this->getLogFilename();
    }

    public function getArchiveFilename()
    {
        $extension = strtolower($this->Archive->Format);

        return "{$this->NameHash}_archive.{$extension}";
    }

    public function getInstallerFilename()
    {
        return "{$this->NameHash}_installer.php";
    }

    public function getDatabaseFilename()
    {
        return $this->NameHash . '_database.sql';
    }

    /**
     * @param int $type
     * @return array
     */
    public function getPackageFileDownloadInfo($type)
    {
        $result = array(
            "filename" => "",
            "url"      => ""
        );

        switch ($type){
            case DUP_PackageFileType::Archive;
                $result["filename"] = $this->Archive->File;
                $result["url"]      = $this->Archive->getURL();
                break;
            case DUP_PackageFileType::SQL;
                $result["filename"] = $this->Database->File;
                $result["url"]      = $this->Database->getURL();
                break;
            case DUP_PackageFileType::Log;
                $result["filename"] = $this->getLogFilename();
                $result["url"]      = $this->getLogUrl();
                break;
            case DUP_PackageFileType::Scan;
                $result["filename"] = $this->getScanFilename();
                $result["url"]      = $this->getScanUrl();
                break;
            default:
                break;
        }

        return $result;
    }

    public function getInstallerDownloadInfo()
    {
        return array(
            "id"   => $this->ID,
            "hash" => $this->Hash
        );
    }

    /**
     * Removes all files except those of active packages
     */
    public static function not_active_files_tmp_cleanup()
	{
		//Check for the 'tmp' folder just for safe measures
		if (! is_dir(DUP_Settings::getSsdirTmpPath()) && (strpos(DUP_Settings::getSsdirTmpPath(), 'tmp') !== false) ) {
			return;
		}

        $globs = glob(DUP_Settings::getSsdirTmpPath().'/*.*');
		if (! is_array($globs) || $globs === FALSE) {
			return;
		}
        
        // RUNNING PACKAGES
        $active_pack = self::get_row_by_status(array(
            'relation' => 'AND',
            array('op' => '>=' , 'status' => DUP_PackageStatus::CREATED ),
            array('op' => '<' , 'status' => DUP_PackageStatus::COMPLETE )
        ));
        $active_files = array();
        foreach($active_pack as $row) {
            $active_files[] = $row->name.'_'.$row->hash;
        }

        // ERRORS PACKAGES
        $err_pack = self::get_row_by_status(array(
            array('op' => '<' , 'status' => DUP_PackageStatus::CREATED )
        ));
        $force_del_files = array();
        foreach($err_pack as $row) {
            $force_del_files[] =  $row->name.'_'.$row->hash;
        }

        // Don't remove json file;
        $extension_filter = array('json');

        // Calculate delta time for old files
        $oldTimeToClean = time() - DUPLICATOR_TEMP_CLEANUP_SECONDS;

        foreach ($globs as $glob_full_path) {
            // Don't remove sub dir
            if (is_dir($glob_full_path)) {
                continue;
            }

            $file_name = basename($glob_full_path);
            // skip all active packages
            foreach ($active_files  as $c_nameHash) {
                if (strpos($file_name, $c_nameHash) === 0) {
                    continue 2;
                }
            }

            // Remove all old files
            if (filemtime($glob_full_path) <= $oldTimeToClean) {
                @unlink($glob_full_path);
                continue;
            }

            // remove all error packages files
            foreach ($force_del_files  as $c_nameHash) {
                if (strpos($file_name, $c_nameHash) === 0) {
                    @unlink($glob_full_path);
                    continue 2;
                }
            }

            $file_info = pathinfo($glob_full_path);
            // skip json file for pre build packages
            if (in_array($file_info['extension'], $extension_filter) || in_array($file_name, $active_files)) {
                continue;
            }

            @unlink($glob_full_path);
        }
    }

	/**
     * Cleans up the temp storage folder have a time interval
     *
     * @return void
     */
    public static function safeTmpCleanup($purge_temp_archives = false)
    {
        if ($purge_temp_archives) {
            $dir = DUP_Settings::getSsdirTmpPath() . "/*_archive.zip.*";
            foreach (glob($dir) as $file_path) {
                unlink($file_path);
            }
            $dir = DUP_Settings::getSsdirTmpPath() . "/*_archive.daf.*";
            foreach (glob($dir) as $file_path) {
                unlink($file_path);
            }
        } else {
            //Remove all temp files that are 24 hours old
            $dir = DUP_Settings::getSsdirTmpPath() . "/*";

            $files = glob($dir);

            if ($files !== false) {
                foreach ($files as $file_path) {
                    // Cut back to keeping things around for just an hour 15 min
                    if (filemtime($file_path) <= time() - DUPLICATOR_TEMP_CLEANUP_SECONDS) {
                        unlink($file_path);
                    }
                }
            }
        }
    }

	/**
     * Starts the package DupArchive progressive build process - always assumed to only run off active package, NOT one in the package table
     *
     * @return obj Returns a DUP_Package object
     */
	public function runDupArchiveBuild()
    {
        $this->BuildProgress->start_timer();
        DUP_Log::Trace('Called');

        if ($this->BuildProgress->failed) {

            DUP_LOG::Trace("build progress failed so setting package to failed");
            $this->setStatus(DUP_PackageStatus::ERROR);
            $message = "Package creation failed.";
            DUP_Log::Trace($message);
            return true;
        }

        if ($this->BuildProgress->initialized == false) {
            DUP_Log::Trace('[DUP ARCHIVE] INIZIALIZE');
            $this->BuildProgress->initialized = true;
            $this->TimerStart = Dup_Util::getMicrotime();
            $this->update();
        }

        //START BUILD
        if (!$this->BuildProgress->database_script_built) {
            DUP_Log::Info('[DUP ARCHIVE] BUILDING DATABASE');
            $this->Database->build($this, Dup_ErrorBehavior::ThrowException);
            DUP_Log::Info('[DUP ARCHIVE] VALIDATING DATABASE');
            $this->Database->validateTableWiseRowCounts();
            $this->BuildProgress->database_script_built = true;
            $this->update();
            DUP_Log::Info('[DUP ARCHIVE] DONE DATABASE');
        } else if (!$this->BuildProgress->archive_built) {
            DUP_Log::Info('[DUP ARCHIVE] BUILDING ARCHIVE');
            $this->Archive->build($this);
            $this->update();
            DUP_Log::Info('[DUP ARCHIVE] DONE ARCHIVE');
        } else if (!$this->BuildProgress->installer_built) {
            DUP_Log::Info('[DUP ARCHIVE] BUILDING INSTALLER');
            // Installer being built is stuffed into the archive build phase
        }

        if ($this->BuildProgress->has_completed()) {
            DUP_Log::Info('[DUP ARCHIVE] HAS COMPLETED CLOSING');

            if (!$this->BuildProgress->failed) {
				DUP_LOG::Info("[DUP ARCHIVE] DUP ARCHIVE INTEGRITY CHECK");
                // Only makees sense to perform build integrity check on completed archives
                $this->runDupArchiveBuildIntegrityCheck();
            } else {
				DUP_LOG::trace("top of loop build progress failed");
			}

            $timerEnd = DUP_Util::getMicrotime();
            $timerSum = DUP_Util::elapsedTime($timerEnd, $this->TimerStart);
            $this->Runtime = $timerSum;

            //FINAL REPORT
            $info = "\n********************************************************************************\n";
            $info .= "RECORD ID:[{$this->ID}]\n";
            $info .= "TOTAL PROCESS RUNTIME: {$timerSum}\n";
            $info .= "PEAK PHP MEMORY USED: " . DUP_Server::getPHPMemory(true) . "\n";
            $info .= "DONE PROCESSING => {$this->Name} " . @date("Y-m-d H:i:s") . "\n";

            DUP_Log::info($info);
            DUP_LOG::trace("Done package building");

            if (!$this->BuildProgress->failed) {
                DUP_Log::Trace('[DUP ARCHIVE] HAS COMPLETED DONE');
                $this->setStatus(DUP_PackageStatus::COMPLETE);
				DUP_LOG::Trace("Cleaning up duparchive temp files");
                //File Cleanup
                $this->buildCleanup();
                do_action('duplicator_lite_build_completed' , $this);
            } else {
                DUP_Log::Trace('[DUP ARCHIVE] HAS COMPLETED ERROR');
            }
        }
        DUP_Log::Close();
        return $this->BuildProgress->has_completed();
    }

    /**
     * Starts the package build process
     *
     * @return obj Returns a DUP_Package object
     */
    public function runZipBuild()
    {
        $timerStart = DUP_Util::getMicrotime();

        DUP_Log::Trace('#### start of zip build');
        //START BUILD
        //PHPs serialze method will return the object, but the ID above is not passed
        //for one reason or another so passing the object back in seems to do the trick
        $this->Database->build($this, Dup_ErrorBehavior::ThrowException);
        $this->Database->validateTableWiseRowCounts();
        $this->Archive->build($this);
        $this->Installer->build($this);

        //INTEGRITY CHECKS
        /*DUP_Log::Info("\n********************************************************************************");
        DUP_Log::Info("INTEGRITY CHECKS:");
        DUP_Log::Info("********************************************************************************");*/
        $this->runDupArchiveBuildIntegrityCheck();
        $dbSizeRead  = DUP_Util::byteSize($this->Database->Size);
        $zipSizeRead = DUP_Util::byteSize($this->Archive->Size);
        $exeSizeRead = DUP_Util::byteSize($this->Installer->Size);

        $timerEnd = DUP_Util::getMicrotime();
        $timerSum = DUP_Util::elapsedTime($timerEnd, $timerStart);

        $this->Runtime = $timerSum;
        $this->ExeSize = $exeSizeRead;
        $this->ZipSize = $zipSizeRead;


        $this->buildCleanup();

        //FINAL REPORT
        $info = "\n********************************************************************************\n";
        $info .= "RECORD ID:[{$this->ID}]\n";
        $info .= "TOTAL PROCESS RUNTIME: {$timerSum}\n";
        $info .= "PEAK PHP MEMORY USED: ".DUP_Server::getPHPMemory(true)."\n";
        $info .= "DONE PROCESSING => {$this->Name} ".@date(get_option('date_format')." ".get_option('time_format'))."\n";

        
        DUP_Log::Info($info);
        DUP_Log::Close();

        $this->setStatus(DUP_PackageStatus::COMPLETE);
        return $this;
    }

    /**
     *  Saves the active options associted with the active(latest) package.
     *
     *  @see DUP_Package::getActive
     *
     *  @param $_POST $post The Post server object
     *
     *  @return null
     */
    public function saveActive($post = null)
    {
        global $wp_version;

        if (isset($post)) {
            $post = stripslashes_deep($post);

            $name = isset($post['package-name']) ? trim($post['package-name']) : self::getDefaultName();
            $name = str_replace(array(' ', '-'), '_', $name);
            $name = str_replace(array('.', ';', ':', "'", '"'), '', $name);
            $name = sanitize_file_name($name);
            $name = substr(trim($name), 0, 40);

            if (isset($post['filter-dirs'])) {
                $post_filter_dirs = sanitize_text_field($post['filter-dirs']);
                $filter_dirs  = $this->Archive->parseDirectoryFilter($post_filter_dirs);
            } else {
                $filter_dirs  = '';
            }            

            if (isset($post['filter-files'])) {
                $post_filter_files = sanitize_text_field($post['filter-files']);
                $filter_files = $this->Archive->parseFileFilter($post_filter_files);
            } else {
                $filter_files = '';
            }

            if (isset($post['filter-exts'])) {
                $post_filter_exts = sanitize_text_field($post['filter-exts']);
                $filter_exts  = $this->Archive->parseExtensionFilter($post_filter_exts);
            } else {
                $filter_exts  = '';
            }

			$tablelist = '';
            if (isset($post['dbtables'])) {
                $tablelist   = implode(',', $post['dbtables']);
            }

            if (isset($post['dbcompat'])) {
                $post_dbcompat = sanitize_text_field($post['dbcompat']);
                $compatlist = isset($post['dbcompat'])	 ? implode(',', $post_dbcompat) : '';
            } else {
                $compatlist = '';
            }

            $dbversion    = DUP_DB::getVersion();
            $dbversion    = is_null($dbversion) ? '- unknown -'  : sanitize_text_field($dbversion);
            $dbcomments   = sanitize_text_field(DUP_DB::getVariable('version_comment'));
            $dbcomments   = is_null($dbcomments) ? '- unknown -' : sanitize_text_field($dbcomments);

            //PACKAGE
            $this->Created    = gmdate("Y-m-d H:i:s");
            $this->Version    = DUPLICATOR_VERSION;
            $this->VersionOS  = defined('PHP_OS') ? PHP_OS : 'unknown';
            $this->VersionWP  = $wp_version;
            $this->VersionPHP = phpversion();
            $this->VersionDB  = sanitize_text_field($dbversion);
            $this->Name       = sanitize_text_field($name);
            $this->Hash       = $this->makeHash();
            $this->NameHash   = sanitize_text_field("{$this->Name}_{$this->Hash}");

            $this->Notes                    = sanitize_textarea_field($post['package-notes']);
            //ARCHIVE
            $this->Archive->PackDir         = duplicator_get_abs_path();
            $this->Archive->Format          = 'ZIP';
            $this->Archive->FilterOn        = isset($post['filter-on']) ? 1 : 0;
			$this->Archive->ExportOnlyDB    = isset($post['export-onlydb']) ? 1 : 0;
            $this->Archive->FilterDirs      = sanitize_textarea_field($filter_dirs);
			$this->Archive->FilterFiles    = sanitize_textarea_field($filter_files);
            $this->Archive->FilterExts      = str_replace(array('.', ' '), '', $filter_exts);
            //INSTALLER
            $this->Installer->OptsDBHost		= sanitize_text_field($post['dbhost']);
            $this->Installer->OptsDBPort		= sanitize_text_field($post['dbport']);
            $this->Installer->OptsDBName		= sanitize_text_field($post['dbname']);
            $this->Installer->OptsDBUser		= sanitize_text_field($post['dbuser']);
            $this->Installer->OptsDBCharset		= sanitize_text_field($post['dbcharset']);
            $this->Installer->OptsDBCollation   = sanitize_text_field($post['dbcollation']);
            $this->Installer->OptsSecureOn		= isset($post['secure-on']) ? 1 : 0;
            $post_secure_pass                   = sanitize_text_field($post['secure-pass']);
			$this->Installer->OptsSecurePass	= DUP_Util::installerScramble($post_secure_pass);
            //DATABASE
            $this->Database->FilterOn       = isset($post['dbfilter-on']) ? 1 : 0;
            $this->Database->FilterTables   = sanitize_text_field($tablelist);
            $this->Database->Compatible     = $compatlist;
            $this->Database->Comments       = sanitize_text_field($dbcomments);

            update_option(self::OPT_ACTIVE, $this);
        }
    }

	/**
     * Update the serialized package and status in the database
     *
     * @return void
     */
    public function update()
    {
        global $wpdb;

        $this->Status = number_format($this->Status, 1, '.', '');
        $this->cleanObjectBeforeSave();
        $packageObj = serialize($this);

        if (!$packageObj) {
            DUP_Log::error("Package SetStatus was unable to serialize package object while updating record.");
        }

        $wpdb->flush();
        $tablePrefix = DUP_Util::getTablePrefix();
        $table = $tablePrefix."duplicator_packages";
        $sql  = "UPDATE `{$table}` SET  status = {$this->Status},";
        $sql .= "package = '" . esc_sql($packageObj) . "'";
        $sql .= "WHERE ID = {$this->ID}";

        DUP_Log::Trace("UPDATE PACKAGE ID = {$this->ID} STATUS = {$this->Status}");

        //DUP_Log::Trace('####Executing SQL' . $sql . '-----------');
        $wpdb->query($sql);
    }

    /**
     * Save any property of this class through reflection
     *
     * @param $property     A valid public property in this class
     * @param $value        The value for the new dynamic property
     *
     * @return null
     */
    public function saveActiveItem($property, $value)
    {
        $package = self::getActive();

        $reflectionClass = new ReflectionClass($package);
        $reflectionClass->getProperty($property)->setValue($package, $value);
        update_option(self::OPT_ACTIVE, $package);
    }

    /**
     * Sets the status to log the state of the build
     * The status level for where the package is
     *
     * @param int $status
     *
     * @return void
     */
    public function setStatus($status)
    {
        if (!isset($status)) {
            DUP_Log::error("Package SetStatus did not receive a proper code.");
        }
        $this->Status = $status;
        $this->update();
    }

    /**
     * Does a hash already exists
     * Returns 0 if no hash is found, if found returns the table ID
     *
     * @param string $hash An existing hash value
     *
     * @return int 
     */
    public function getHashKey($hash)
    {
        global $wpdb;

        $tablePrefix = DUP_Util::getTablePrefix();
        $table = $tablePrefix."duplicator_packages";
        $qry   = $wpdb->get_row("SELECT ID, hash FROM `{$table}` WHERE hash = '{$hash}'");
        if (is_null($qry) || strlen($qry->hash) == 0) {
            return 0;
        } else {
            return $qry->ID;
        }
    }

    /**
     * Makes the hashkey for the package files
     *
     * @return string // A unique hashkey
     */
    public function makeHash()
    {
        try {
            if (function_exists('random_bytes') && DUP_Util::PHP53()) {
                return bin2hex(random_bytes(8)) . mt_rand(1000, 9999) . '_' . date("YmdHis");
            } else {
                return strtolower(md5(uniqid(rand(), true))) . '_' . date("YmdHis");
            }
        } catch (Exception $exc) {
            return strtolower(md5(uniqid(rand(), true))) . '_' . date("YmdHis");
        }
    }

    /**
     * Gets the active package which is defined as the package that was lasted saved.
     * Do to cache issues with the built in WP function get_option moved call to a direct DB call.
     *
     * @see DUP_Package::saveActive
     *
     * @return DUP_Package // A copy of the DUP_Package object
     */
    public static function getActive()
    {
        global $wpdb;

        $obj = new DUP_Package();
        $row = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM `{$wpdb->options}` WHERE option_name = %s LIMIT 1", self::OPT_ACTIVE));
        if (is_object($row)) {
            $obj = @unserialize($row->option_value);
        }
        //Incase unserilaize fails
        $obj = (is_object($obj)) ? $obj : new DUP_Package();

        return $obj;
    }

    /**
     * Gets the Package by ID
     *
     * @param int $id A valid package id form the duplicator_packages table
     *
     * @return DUP_Package  // A copy of the DUP_Package object
     */
    public static function getByID($id)
    {
        global $wpdb;
        $obj         = new DUP_Package();
        $tablePrefix = DUP_Util::getTablePrefix();
        $sql         = $wpdb->prepare("SELECT * FROM `{$tablePrefix}duplicator_packages` WHERE ID = %d", $id);
        $row         = $wpdb->get_row($sql);
        if (is_object($row)) {
            $obj = @unserialize($row->package);
            // We was not storing Status in Lite 1.2.52, so it is for backward compatibility
            if (!isset($obj->Status)) {
                $obj->Status = $row->status;
            }
        }
        //Incase unserilaize fails
        $obj = (is_object($obj)) ? $obj : null;
        return $obj;
    }

    /**
     *  Gets a default name for the package
     *
     *  @return string   // A default package name such as 20170218_blogname
     */
    public static function getDefaultName($preDate = true)
    {
        //Remove specail_chars from final result
        $special_chars = array(".", "-");
        $name          = ($preDate)
							? date('Ymd') . '_' . sanitize_title(get_bloginfo('name', 'display'))
							: sanitize_title(get_bloginfo('name', 'display')) . '_' . date('Ymd');
        $name          = substr(sanitize_file_name($name), 0, 40);
        $name          = str_replace($special_chars, '', $name);
        return $name;
    }

    /**
     *  Cleanup all tmp files
     *
     *  @param all empty all contents
     *
     *  @return null
     */
    public static function tempFileCleanup($all = false)
    {
        //Delete all files now
        if ($all) {
            $dir = DUP_Settings::getSsdirTmpPath()."/*";
            foreach (glob($dir) as $file) {
                @unlink($file);
            }
        }
        //Remove scan files that are 24 hours old
        else {
            $dir = DUP_Settings::getSsdirTmpPath()."/*_scan.json";
            foreach (glob($dir) as $file) {
                if (filemtime($file) <= time() - 86400) {
                    @unlink($file);
                }
            }
        }
    }

    /**
     *  Provides various date formats
     *
     *  @param $utcDate created date in the GMT timezone
     *  @param $format  Various date formats to apply
     *
     *  @return string  // a formated date based on the $format
     */
    public static function getCreatedDateFormat($utcDate, $format = 1)
    {
        $date = get_date_from_gmt($utcDate);
        $date = new DateTime($date);
        switch ($format) {
            //YEAR
            case 1: return $date->format('Y-m-d H:i');
                break;
            case 2: return $date->format('Y-m-d H:i:s');
                break;
            case 3: return $date->format('y-m-d H:i');
                break;
            case 4: return $date->format('y-m-d H:i:s');
                break;
            //MONTH
            case 5: return $date->format('m-d-Y H:i');
                break;
            case 6: return $date->format('m-d-Y H:i:s');
                break;
            case 7: return $date->format('m-d-y H:i');
                break;
            case 8: return $date->format('m-d-y H:i:s');
                break;
            //DAY
            case 9: return $date->format('d-m-Y H:i');
                break;
            case 10: return $date->format('d-m-Y H:i:s');
                break;
            case 11: return $date->format('d-m-y H:i');
                break;
            case 12: return $date->format('d-m-y H:i:s');
                break;
            default :
                return $date->format('Y-m-d H:i');
        }
    }

    /**
     *  Cleans up all the tmp files as part of the package build process
     */
    public function buildCleanup()
    {
        $files   = DUP_Util::listFiles(DUP_Settings::getSsdirTmpPath());
        $newPath = DUP_Settings::getSsdirPath();

        $filesToStore = array(
            $this->Installer->File,
            $this->Archive->File,
        );

        foreach ($files as $file) {

            $fileName = basename($file);
            if (!strstr($fileName, $this->NameHash)) {
                continue;
            }

            if (in_array($fileName, $filesToStore)) {
                if (function_exists('rename')) {
                    rename($file, "{$newPath}/{$fileName}");
                } elseif (function_exists('copy')) {
                    copy($file, "{$newPath}/{$fileName}");
                } else {
                    throw new Exception('PHP copy and rename functions not found!  Contact hosting provider!');
                }
            }

            if (file_exists($file)) {
                unlink($file);
            }
        }

    }



    /**
     * Get package hash
     *
     * @return string package hash
     */
    public function getPackageHash() {
        $hashParts = explode('_', $this->Hash);
        $firstPart = substr($hashParts[0], 0, 7);
        $secondPart = substr($hashParts[1], -8);
        $package_hash = $firstPart.'-'.$secondPart;
        return $package_hash;
    }
    
    public function getSecondaryPackageHash() {
        $newHash = $this->makeHash();
        $hashParts = explode('_', $newHash);
        $firstPart = substr($hashParts[0], 0, 7);
        
        $hashParts = explode('_', $this->Hash);
        $secondPart = substr($hashParts[1], -8);
        
        $package_hash = $firstPart.'-'.$secondPart;
        return $package_hash;
    }

    /**
     *  Provides the full sql file path in archive
     *
     *  @return the full sql file path in archive
     */
    public function getSqlArkFilePath()
    {
        $package_hash = $this->getPackageHash();
        $sql_ark_file_Path = 'dup-installer/dup-database__'.$package_hash.'.sql';
        return $sql_ark_file_Path;
    }
	
	private function writeLogHeader()
	{
        $php_max_time   = @ini_get("max_execution_time");
        if (DupLiteSnapLibUtil::wp_is_ini_value_changeable('memory_limit'))
            $php_max_memory = @ini_set('memory_limit', DUPLICATOR_PHP_MAX_MEMORY);
        else
            $php_max_memory = @ini_get('memory_limit');

        $php_max_time   = ($php_max_time == 0) ? "(0) no time limit imposed" : "[{$php_max_time}] not allowed";
        $php_max_memory = ($php_max_memory === false) ? "Unabled to set php memory_limit" : DUPLICATOR_PHP_MAX_MEMORY." ({$php_max_memory} default)";

		$info = "********************************************************************************\n";
        $info .= "DUPLICATOR-LITE PACKAGE-LOG: ".@date(get_option('date_format')." ".get_option('time_format'))."\n";
        $info .= "NOTICE: Do NOT post to public sites or forums \n";
        $info .= "********************************************************************************\n";
        $info .= "VERSION:\t".DUPLICATOR_VERSION."\n";
        $info .= "WORDPRESS:\t{$GLOBALS['wp_version']}\n";
        $info .= "PHP INFO:\t".phpversion().' | '.'SAPI: '.php_sapi_name()."\n";
        $info .= "SERVER:\t\t{$_SERVER['SERVER_SOFTWARE']} \n";
        $info .= "PHP TIME LIMIT: {$php_max_time} \n";
        $info .= "PHP MAX MEMORY: {$php_max_memory} \n";
        $info .= "MEMORY STACK: ".DUP_Server::getPHPMemory();
        DUP_Log::Info($info);

        $info = null;
	}
}PK�{2\v8 
 
class.io.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * @copyright 2018 Snap Creek LLC
 * Class for all IO operations
 */

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_IO
{
    /**
     * Safely deletes a file
     *
     * @param string $file	The full filepath to the file
     *
     * @return TRUE on success or if file does not exist. FALSE on failure
     */
    public static function deleteFile($file)
	{
		if (file_exists($file)) {
			if (@unlink($file) === false) {
				DUP_Log::Info("Could not delete file: {$file}");
				return false;
			}
		}
		return true;
	}

	/**
     * Removes a directory recursively except for the root of a WP Site
     *
     * @param string $directory	The full filepath to the directory to remove
     *
     * @return TRUE on success FALSE on failure
     */
	public static function deleteTree($directory)
	{
		$success = true;

        if(!file_exists("{$directory}/wp-config.php")) {
            $filenames = array_diff(scandir($directory), array('.', '..'));

            foreach ($filenames as $filename) {
                if (is_dir("$directory/$filename")) {
                    $success = self::deleteTree("$directory/$filename");
                } else {
                    $success = @unlink("$directory/$filename");
                }

                if ($success === false) {
					break;
                }
            }
        } else {
            return false;
        }

		return $success && @rmdir($directory);
	}

    /**
     * Safely copies a file to a directory
     *
     * @param string $source_file       The full filepath to the file to copy
     * @param string $dest_dir			The full path to the destination directory were the file will be copied
     * @param string $delete_first		Delete file before copying the new one
     *
     *  @return TRUE on success or if file does not exist. FALSE on failure
     */
    public static function copyFile($source_file, $dest_dir, $delete_first = false)
    {
        //Create directory
        if (file_exists($dest_dir) == false)
        {
            if (wp_mkdir_p($dest_dir) === false) {
                return false;
            }
        }

        //Remove file with same name before copy
        $filename = basename($source_file);
        $dest_filepath = $dest_dir . "/$filename";
        if($delete_first)
        {
            self::deleteFile($dest_filepath);
        }

        return copy($source_file, $dest_filepath);
    }
}
PK�{2\���!!class.archive.config.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * @copyright 2016 Snap Creek LLC
 */

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_Archive_Config
{
    //READ-ONLY: COMPARE VALUES
    public $created;
    public $version_dup;
    public $version_wp;
    public $version_db;
    public $version_php;
    public $version_os;
    public $dbInfo;
    //READ-ONLY: GENERAL
    public $url_old;
    public $opts_delete;
    public $blogname;
    public $wproot;
    public $wplogin_url;
	public $relative_content_dir;
    public $exportOnlyDB;
    public $installSiteOverwriteOn;

    //PRE-FILLED: GENERAL
    public $secure_on;
    public $secure_pass;
    public $skipscan;
    public $dbhost;
    public $dbname;
    public $dbuser;
    public $dbpass;
    public $dbcharset;
    public $dbcollation;

    // MULTISITE
    public $mu_mode;

    public $wp_tableprefix;

    public $is_outer_root_wp_config_file;
    public $is_outer_root_wp_content_dir;
}
PK�{2\__����class.constants.phpnu�[���<?php
defined("ABSPATH") || exit;
class DUP_Constants
{
    const ZIP_STRING_LIMIT = 70000;   // Cutoff for using ZipArchive addtostring vs addfile
}
PK�{2\k�fN	index.phpnu�[���<?php
//silentPK�{2\/��]eehost/class.liquidweb.host.phpnu�[���<?php
/**
 * wpengine custom hosting class
 *
 * Standard: PSR-2
 *
 * @package SC\DUPX\HOST
 * @link http://www.php-fig.org/psr/psr-2/
 *
 */
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

class DUP_Liquidweb_Host implements DUP_Host_interface
{

    public static function getIdentifier()
    {
        return DUP_Custom_Host_Manager::HOST_LIQUIDWEB;
    }

    public function isHosting()
    {
        return apply_filters('duplicator_liquidweb_host_check', file_exists(DupLiteSnapLibIOU::safePathUntrailingslashit(WPMU_PLUGIN_DIR).'/liquid-web.php'));
    }

    public function init()
    {

    }
}PK�{2\iՏ�host/class.wpengine.host.phpnu�[���<?php
defined("ABSPATH") or die("");

// New encryption class

class DUP_WPEngine_Host implements DUP_Host_interface
{
    public function init()
    {
        add_filter('duplicator_installer_file_path', array(__CLASS__, 'installerFilePath'), 10, 1);
        add_filter('duplicator_global_file_filters_on', '__return_true');
        add_filter('duplicator_global_file_filters', array(__CLASS__, 'globalFileFilters'), 10, 1);
        add_filter('duplicator_defaults_settings', array(__CLASS__, 'defaultsSettings'));
    }

    public static function getIdentifier()
    {
        return DUP_Custom_Host_Manager::HOST_WPENGINE;
    }


    public function isHosting()
    {
        return apply_filters('duplicator_wp_engine_host_check', file_exists(DupLiteSnapLibIOU::safePathUntrailingslashit(WPMU_PLUGIN_DIR).'/wpengine-security-auditor.php'));
    }

    public static function installerFilePath($path)
    {
        $path_info = pathinfo($path);
        $newPath   = $path;
        if ('php' == $path_info['extension']) {
            $newPath = substr_replace($path, '.txt', -4);
        }
        return $newPath;
    }

    public static function globalFileFilters($files)
    {
        $files[] = wp_normalize_path(WP_CONTENT_DIR).'/mysql.sql';
        return $files;
    }

    public static function defaultsSettings($defaults)
    {
        $defaults['package_zip_flush'] = '1';
        return $defaults;
    }

}PK�{2\�kX]]host/interface.host.phpnu�[���<?php
/**
 * interface for specific hostings class
 *
 * Standard: PSR-2
 *
 * @package SC\DUPX\HOST
 * @link http://www.php-fig.org/psr/psr-2/
 *
 */
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

interface DUP_Host_interface
{

    /**
     * return the current host itentifier
     *
     * @return string
     */
    public static function getIdentifier();

    /**
     * @return bool true if is current host
     */
    public function isHosting();

    /**
     * the init function.
     * is called only if isHosting is true
     *
     * @return void
     */
    public function init();
}PK�{2\�rr host/class.wordpresscom.host.phpnu�[���<?php
/**
 * godaddy custom hosting class
 *
 * Standard: PSR-2
 *
 * @package SC\DUPX\HOST
 * @link http://www.php-fig.org/psr/psr-2/
 *
 */
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

class DUP_WordpressCom_Host implements DUP_Host_interface
{

    public static function getIdentifier()
    {
        return DUP_Custom_Host_Manager::HOST_WORDPRESSCOM;
    }

    public function isHosting()
    {
        return apply_filters('duplicator_pro_wordpress_host_check', file_exists(DupLiteSnapLibIOU::safePathUntrailingslashit(WPMU_PLUGIN_DIR).'/wpcomsh-loader.php'));
    }

    public function init()
    {

    }
}PK�{2\;��Iffhost/class.flywheel.host.phpnu�[���<?php

/**
 * Flywheel custom hosting class
 *
 * Standard: PSR-2
 *
 * @package SC\DUPX\HOST
 * @link http://www.php-fig.org/psr/psr-2/
 *
 */
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

class DUP_Flywheel_Host implements DUP_Host_interface
{

    public static function getIdentifier()
    {
        return DUP_Custom_Host_Manager::HOST_FLYWHEEL;
    }

    public function isHosting()
    {
        $path = duplicator_get_home_path().'/.fw-config.php';
        return apply_filters('duplicator_host_check', file_exists($path), self::getIdentifier());
    }

    public function init()
    {

    }
}
PK�{2\��!�"host/class.custom.host.manager.phpnu�[���<?php

/**
 * custom hosting manager
 * singleton class
 *
 * Standard: PSR-2
 *
 * @package SC\DUPX\HOST
 * @link http://www.php-fig.org/psr/psr-2/
 *
 */
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

require_once (DUPLICATOR_PLUGIN_PATH . '/classes/host/interface.host.php');
require_once (DUPLICATOR_PLUGIN_PATH . '/classes/host/class.godaddy.host.php');
require_once (DUPLICATOR_PLUGIN_PATH . '/classes/host/class.wpengine.host.php');
require_once (DUPLICATOR_PLUGIN_PATH . '/classes/host/class.wordpresscom.host.php');
require_once (DUPLICATOR_PLUGIN_PATH . '/classes/host/class.liquidweb.host.php');
require_once (DUPLICATOR_PLUGIN_PATH . '/classes/host/class.pantheon.host.php');
require_once (DUPLICATOR_PLUGIN_PATH . '/classes/host/class.flywheel.host.php');

class DUP_Custom_Host_Manager
{
    const HOST_GODADDY      = 'godaddy';
    const HOST_WPENGINE     = 'wpengine';
    const HOST_WORDPRESSCOM = 'wordpresscom';
    const HOST_LIQUIDWEB    = 'liquidweb';
    const HOST_PANTHEON     = 'pantheon';
    const HOST_FLYWHEEL     = 'flywheel';

    /**
     *
     * @var DUP_Custom_Host_Manager
     */
    protected static $instance = null;

    /**
     *
     * @var bool
     */
    private $initialized = false;

    /**
     *
     * @var DUP_Host_interface[]
     */
    private $customHostings = array();

    /**
     *
     * @var string[]
     */
    private $activeHostings = array();

    /**
     *
     * @return self
     */
    public static function getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    private function __construct()
    {
        $this->customHostings[DUP_WPEngine_Host::getIdentifier()]     = new DUP_WPEngine_Host();
        $this->customHostings[DUP_GoDaddy_Host::getIdentifier()]      = new DUP_GoDaddy_Host();
        $this->customHostings[DUP_WordpressCom_Host::getIdentifier()] = new DUP_WordpressCom_Host();
        $this->customHostings[DUP_Liquidweb_Host::getIdentifier()]    = new DUP_Liquidweb_Host();
        $this->customHostings[DUP_Pantheon_Host::getIdentifier()]     = new DUP_Pantheon_Host();
        $this->customHostings[DUP_Flywheel_Host::getIdentifier()]     = new DUP_Flywheel_Host();
    }

    public function init()
    {
        if ($this->initialized) {
            return true;
        }
        foreach ($this->customHostings as $cHost) {
            if (!($cHost instanceof DUP_Host_interface)) {
                throw new Exception('Host must implement DUP_Host_interface');
            }
            if ($cHost->isHosting()) {
                $this->activeHostings[] = $cHost->getIdentifier();
                $cHost->init();
            }
        }
        $this->initialized = true;
        return true;
    }

    public function getActiveHostings()
    {
        return $this->activeHostings;
    }

    public function isHosting($identifier)
    {
        return in_array($identifier, $this->activeHostings);
    }

    public function isManaged()
    {
        if ($this->isHosting(self::HOST_WORDPRESSCOM)) {
            return true;
        }

        if ($this->isHosting(self::HOST_GODADDY)) {
            return true;
        }

        if ($this->isHosting(self::HOST_WPENGINE)) {
            return true;
        }

        if ($this->isHosting(self::HOST_LIQUIDWEB)) {
            return true;
        }

        if ($this->isHosting(self::HOST_PANTHEON)) {
            return true;
        }

        if ($this->isHosting(self::HOST_FLYWHEEL)) {
            return true;
        }

        if ($this->WPConfigIsNotWriteable()) {
            return true;
        }

        if ($this->notAccessibleCoreDirPresent()) {
            return true;
        }

        return false;
    }

    public function WPConfigIsNotWriteable()
    {
        $wpConfigPath = duplicator_get_abs_path()."/wp-config.php";

        return file_exists($wpConfigPath) && !is_writeable($wpConfigPath);
    }

    public function notAccessibleCoreDirPresent()
    {
        $absPath = duplicator_get_abs_path();
        $coreDirs = array(
            $absPath.'/wp-admin',
            $absPath.'/wp-includes',
            WP_CONTENT_DIR
        );

        foreach ($coreDirs as $coreDir) {
            if (file_exists($coreDir) && !is_writeable($coreDir)) {
                return true;
            }
        }

        return false;
    }

    public function getHosting($identifier)
    {
        if ($this->isHosting($identifier)) {
            return $this->activeHostings[$identifier];
        } else {
            return false;
        }
    }
}
PK�{2\�v�__host/class.pantheon.host.phpnu�[���<?php
/**
 * godaddy custom hosting class
 *
 * Standard: PSR-2
 *
 * @package SC\DUPX\HOST
 * @link http://www.php-fig.org/psr/psr-2/
 *
 */
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

class DUP_Pantheon_Host implements DUP_Host_interface
{

    public static function getIdentifier()
    {
        return DUP_Custom_Host_Manager::HOST_PANTHEON;
    }

    public function isHosting()
    {
        return apply_filters('duplicator_pantheon_host_check', file_exists(DupLiteSnapLibIOU::safePathUntrailingslashit(WPMU_PLUGIN_DIR).'/pantheon.php'));
    }

    public function init()
    {

    }
}PK�{2\+93���host/class.godaddy.host.phpnu�[���<?php
defined("ABSPATH") or die("");

class DUP_GoDaddy_Host implements DUP_Host_interface
{

    public static function getIdentifier()
    {
        return DUP_Custom_Host_Manager::HOST_GODADDY;
    }

    public function isHosting()
    {
        return apply_filters('duplicator_godaddy_host_check', file_exists(DupLiteSnapLibIOU::safePathUntrailingslashit(WPMU_PLUGIN_DIR).'/gd-system-plugin.php'));
    }

    public function init()
    {
        add_filter('duplicator_defaults_settings', array(__CLASS__, 'defaultsSettings'));
    }

    public static function defaultsSettings($defaults)
    {
        $defaults['archive_build_mode'] = DUP_Archive_Build_Mode::DupArchive;
        return $defaults;
    }
}PK�{2\����!)!)class.settings.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
// Exit if accessed directly
if (!defined('DUPLICATOR_VERSION'))
    exit;

abstract class DUP_Archive_Build_Mode
{

    const Unconfigured = -1;
    const Auto         = 0; // should no longer be used
    //  const Shell_Exec   = 1;
    const ZipArchive   = 2;
    const DupArchive   = 3;

}

class DUP_Settings
{

    const OPT_SETTINGS                  = 'duplicator_settings';
    const INSTALLER_NAME_MODE_WITH_HASH = 'withhash';
    const INSTALLER_NAME_MODE_SIMPLE    = 'simple';
    const STORAGE_POSITION_LECAGY       = 'legacy';
    const STORAGE_POSITION_WP_CONTENT   = 'wpcont';
    const SSDIR_NAME_LEGACY             = 'wp-snapshots';
    const SSDIR_NAME_NEW                = 'backups-dup-lite';

    protected static $Data;
    protected static $ssDirPath = null;
    protected static $ssDirUrl  = null;

    /**
     *  Class used to manage all the settings for the plugin
     */
    public static function init()
    {
        self::$Data = get_option(self::OPT_SETTINGS);
        //when the plugin updated, this will be true
        if (empty(self::$Data) || empty(self::$Data['version']) || version_compare(DUPLICATOR_VERSION, self::$Data['version'], '>')) {
            self::SetDefaults();
        }
    }

    /**
     *  Find the setting value
     *  @param string $key	The name of the key to find
     *  @return The value stored in the key returns null if key does not exist
     */
    public static function Get($key = '')
    {
        $result = null;
        if (isset(self::$Data[$key])) {
            $result = self::$Data[$key];
        } else {
            $defaults = self::GetAllDefaults();
            if (isset($defaults[$key])) {
                $result = $defaults[$key];
            }
        }
        return $result;
    }

    /**
     *  Set the settings value in memory only
     *  @param string $key		The name of the key to find
     *  @param string $value		The value to set
     *  remarks:	 The Save() method must be called to write the Settings object to the DB
     */
    public static function Set($key, $value)
    {
        if (isset(self::$Data[$key])) {
            self::$Data[$key] = ($value == null) ? '' : $value;
        } elseif (!empty($key)) {
            self::$Data[$key] = ($value == null) ? '' : $value;
        }
    }

    public static function setStoragePosition($newPosition)
    {
        $legacyPath = self::getSsdirPathLegacy();
        $wpContPath = self::getSsdirPathWpCont();

        $oldStoragePost = self::Get('storage_position');

        self::resetPositionVars();

        switch ($newPosition) {
            case self::STORAGE_POSITION_LECAGY:
                self::$Data['storage_position'] = self::STORAGE_POSITION_LECAGY;
                if (!DUP_Util::initSnapshotDirectory()) {
                    self::resetPositionVars();
                    self::$Data['storage_position'] = $oldStoragePost;
                    return false;
                }
                if (is_dir($wpContPath)) {
                    if (DupLiteSnapLibIOU::moveContentDirToTarget($wpContPath, $legacyPath, true)) {
                        DupLiteSnapLibIOU::rrmdir($wpContPath);
                    }
                }
                break;
            case self::STORAGE_POSITION_WP_CONTENT:
                self::$Data['storage_position'] = self::STORAGE_POSITION_WP_CONTENT;
                if (!DUP_Util::initSnapshotDirectory()) {
                    self::resetPositionVars();
                    self::$Data['storage_position'] = $oldStoragePost;
                    return false;
                }
                if (is_dir($legacyPath)) {
                    if (DupLiteSnapLibIOU::moveContentDirToTarget($legacyPath, $wpContPath, true)) {
                        DupLiteSnapLibIOU::rrmdir($legacyPath);
                    }
                }
                break;
            default:
                throw new Exception('Unknown storage position');
        }

        return true;
    }

    protected static function resetPositionVars()
    {
        self::$ssDirPath = null;
        self::$ssDirUrl  = null;
    }

    /**
     *  Saves all the setting values to the database
     *  @return True if option value has changed, false if not or if update failed.
     */
    public static function Save()
    {
        return update_option(self::OPT_SETTINGS, self::$Data);
    }

    /**
     *  Deletes all the setting values to the database
     *  @return True if option value has changed, false if not or if update failed.
     */
    public static function Delete()
    {
        return delete_option(self::OPT_SETTINGS);
    }

    /**
     *  Sets the defaults if they have not been set
     *  @return True if option value has changed, false if not or if update failed.
     */
    public static function SetDefaults()
    {
        $defaults   = self::GetAllDefaults();
        self::$Data = apply_filters('duplicator_defaults_settings', $defaults);
        return self::Save();
    }

    /**
     *  DeleteWPOption: Cleans up legacy data
     */
    public static function DeleteWPOption($optionName)
    {
        if (in_array($optionName, $GLOBALS['DUPLICATOR_OPTS_DELETE'])) {
            return delete_option($optionName);
        }
        return false;
    }

    public static function GetAllDefaults()
    {
        $default            = array();
        $default['version'] = DUPLICATOR_VERSION;

        //Flag used to remove the wp_options value duplicator_settings which are all the settings in this class
        $default['uninstall_settings'] = isset(self::$Data['uninstall_settings']) ? self::$Data['uninstall_settings'] : true;
        //Flag used to remove entire wp-snapshot directory
        $default['uninstall_files']    = isset(self::$Data['uninstall_files']) ? self::$Data['uninstall_files'] : true;
        //Flag used to remove all tables
        $default['uninstall_tables']   = isset(self::$Data['uninstall_tables']) ? self::$Data['uninstall_tables'] : true;

        //Flag used to show debug info
        $default['package_debug']            = isset(self::$Data['package_debug']) ? self::$Data['package_debug'] : false;
        //Flag used to enable mysqldump
        $default['package_mysqldump']        = isset(self::$Data['package_mysqldump']) ? self::$Data['package_mysqldump'] : true;
        //Optional mysqldump search path
        $default['package_mysqldump_path']   = isset(self::$Data['package_mysqldump_path']) ? self::$Data['package_mysqldump_path'] : '';
        //Optional mysql limit size
        $default['package_phpdump_qrylimit'] = isset(self::$Data['package_phpdump_qrylimit']) ? self::$Data['package_phpdump_qrylimit'] : "100";
        //Optional mysqldump search path
        $default['package_zip_flush']        = isset(self::$Data['package_zip_flush']) ? self::$Data['package_zip_flush'] : false;
        //Optional mysqldump search path
        $default['installer_name_mode']      = isset(self::$Data['installer_name_mode']) ? self::$Data['installer_name_mode'] : self::INSTALLER_NAME_MODE_SIMPLE;
        // storage position
        $default['storage_position']         = isset(self::$Data['storage_position']) ? self::$Data['storage_position'] : self::STORAGE_POSITION_WP_CONTENT;

        //Flag for .htaccess file
        $default['storage_htaccess_off'] = isset(self::$Data['storage_htaccess_off']) ? self::$Data['storage_htaccess_off'] : false;
        // Initial archive build mode
        if (isset(self::$Data['archive_build_mode'])) {
            $default['archive_build_mode'] = self::$Data['archive_build_mode'];
        } else {
            $is_ziparchive_available       = apply_filters('duplicator_is_ziparchive_available', class_exists('ZipArchive'));
            $default['archive_build_mode'] = $is_ziparchive_available ? DUP_Archive_Build_Mode::ZipArchive : DUP_Archive_Build_Mode::DupArchive;
        }

        // $default['package_zip_flush'] = apply_filters('duplicator_package_zip_flush_default_setting', '0');
        //Skip scan archive
        $default['skip_archive_scan']      = isset(self::$Data['skip_archive_scan']) ? self::$Data['skip_archive_scan'] : false;
        $default['unhook_third_party_js']  = isset(self::$Data['unhook_third_party_js']) ? self::$Data['unhook_third_party_js'] : false;
        $default['unhook_third_party_css'] = isset(self::$Data['unhook_third_party_css']) ? self::$Data['unhook_third_party_css'] : false;

        $default['active_package_id'] = -1;

        return $default;
    }

    public static function get_create_date_format()
    {
        static $ui_create_frmt = null;
        if (is_null($ui_create_frmt)) {
            $ui_create_frmt = is_numeric(self::Get('package_ui_created')) ? self::Get('package_ui_created') : 1;
        }
        return $ui_create_frmt;
    }

    public static function getSsdirPathLegacy()
    {
        return DupLiteSnapLibIOU::safePathTrailingslashit(duplicator_get_abs_path()).self::SSDIR_NAME_LEGACY;
    }

    public static function getSsdirPathWpCont()
    {
        return DupLiteSnapLibIOU::safePathTrailingslashit(WP_CONTENT_DIR).self::SSDIR_NAME_NEW;
    }

    public static function getSsdirPath()
    {
        if (is_null(self::$ssDirPath)) {
            if (self::Get('storage_position') === self::STORAGE_POSITION_LECAGY) {
                self::$ssDirPath = self::getSsdirPathLegacy();
            } else {
                self::$ssDirPath = self::getSsdirPathWpCont();
            }
        }
        return self::$ssDirPath;
    }

    public static function getSsdirUrl()
    {
        if (is_null(self::$ssDirUrl)) {
            if (self::Get('storage_position') === self::STORAGE_POSITION_LECAGY) {
                self::$ssDirUrl = DupLiteSnapLibIOU::trailingslashit(DUPLICATOR_SITE_URL).self::SSDIR_NAME_LEGACY;
            } else {
                self::$ssDirUrl = DupLiteSnapLibIOU::trailingslashit(content_url()).self::SSDIR_NAME_NEW;
            }
        }
        return self::$ssDirUrl;
    }

    public static function getSsdirTmpPath()
    {
        return self::getSsdirPath().'/tmp';
    }

    public static function getSsdirInstallerPath()
    {
        return self::getSsdirPath().'/installer';
    }
}PK�{2\o@i�	�	class.plugin.upgrade.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

/**

 * Upgrade logic of plugin resides here

 */
class DUP_LITE_Plugin_Upgrade
{

    const DUP_VERSION_OPT_KEY = 'duplicator_version_plugin';

    public static function onActivationAction()
    {
        if (($oldDupVersion = get_option(self::DUP_VERSION_OPT_KEY, false)) === false) {
            self::newInstallation();
        } else {
            self::updateInstallation($oldDupVersion);
        }

        //Setup All Directories
        DUP_Util::initSnapshotDirectory();
    }

    protected static function newInstallation()
    {
        self::updateDatabase();

        //WordPress Options Hooks
        update_option(self::DUP_VERSION_OPT_KEY, DUPLICATOR_VERSION);
    }

    protected static function updateInstallation($oldVersion)
    {
        self::updateDatabase();

        //PRE 1.3.35
        //Do not update to new wp-content storage till after
        if (version_compare($oldVersion, '1.3.35', '<')) {
            DUP_Settings::Set('storage_position', DUP_Settings::STORAGE_POSITION_LECAGY);
            DUP_Settings::Save();
        }

        //PRE 1.4.7
        //Remove the core dup-install file that might exist in local storage directory
        if (version_compare($oldVersion, '1.4.7', '<')) {
            $ssdir           = DUP_Settings::getSsdirPath();
            $dupInstallFile  = "{$ssdir}/dup-installer/main.installer.php";
            if (file_exists($dupInstallFile) ) {
                @unlink("{$dupInstallFile}");
            }
        }
        
        //WordPress Options Hooks
        update_option(self::DUP_VERSION_OPT_KEY, DUPLICATOR_VERSION);
    }

    protected static function updateDatabase()
    {
        global $wpdb;

        $table_name = $wpdb->prefix."duplicator_packages";

        //PRIMARY KEY must have 2 spaces before for dbDelta to work
        //see: https://codex.wordpress.org/Creating_Tables_with_Plugins
        $sql = "CREATE TABLE `{$table_name}` (
			   id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
			   name VARCHAR(250) NOT NULL,
			   hash VARCHAR(50) NOT NULL,
			   status INT(11) NOT NULL,
			   created DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
			   owner VARCHAR(60) NOT NULL,
			   package LONGTEXT NOT NULL,
			   PRIMARY KEY  (id),
			   KEY hash (hash))";

        $abs_path = duplicator_get_abs_path();
        require_once($abs_path.'/wp-admin/includes/upgrade.php');
        @dbDelta($sql);
    }
}PK�{2\0N��A
A
ui/class.ui.messages.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

/**
 * Used to generate a thick box inline dialog such as an alert or confirm pop-up
 *
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/ui
 * @copyright (c) 2017, Snapcreek LLC
 */

class DUP_UI_Messages
{
    const UNIQUE_ID_PREFIX = 'dup_ui_msg_';
    const NOTICE           = 'updated';
    const WARNING          = 'update-nag';
    const ERROR            = 'error';

    private static $unique_id = 0;
    private $id;
    public $type              = self::NOTICE;
    public $content           = '';
    public $wrap_cont_tag     = 'p';
    public $hide_on_init      = true;
    public $is_dismissible    = false;
    /**
     *
     * @var int delay in milliseconds
     */
    public $auto_hide_delay   = 0;
    public $callback_on_show  = null;
    public $callback_on_hide  = null;

    public function __construct($content = '', $type = self::NOTICE)
    {
        self::$unique_id ++;
        $this->id = self::UNIQUE_ID_PREFIX.self::$unique_id;

        $this->content = (string) $content;
        $this->type    = $type;
    }

    protected function get_notice_classes($classes = array())
    {
        if (is_string($classes)) {
            $classes = explode(' ', $classes);
        } else if (is_array($classes)) {

        } else {
            $classes = array();
        }

        if ($this->is_dismissible) {
            $classes[] = 'is-dismissible';
        }

        $result = array_merge(array('notice', $this->type), $classes);
        return trim(implode(' ', $result));
    }

    public function initMessage()
    {
        $classes = array();
        if ($this->hide_on_init) {
            $classes[] = 'no_display';
        }

        $this->wrap_tag = empty($this->wrap_tag) ? 'p' : $this->wrap_tag;
        echo '<div id="'.$this->id.'" class="'.$this->get_notice_classes($classes).'">'.
        '<'.$this->wrap_cont_tag.' class="msg-content">'.
        $this->content.
        '</'.$this->wrap_cont_tag.'>'.
        '</div>';
    }

    public function updateMessage($jsVarName, $echo = true)
    {
        $result = 'jQuery("#'.$this->id.' > .msg-content").html('.$jsVarName.');';
        
        if ($echo) {
            echo $result;
        } else {
            return $result;
        }
    }

    public function showMessage($echo = true)
    {
        $callStr = !empty($this->callback_on_show) ? $this->callback_on_show.';' : '';
        $result  = 'jQuery("#'.$this->id.'").fadeIn( "slow", function() { $(this).removeClass("no_display");'.$callStr.' });';
        if ($this->auto_hide_delay > 0) {
            $result .= 'setTimeout(function () { '.$this->hideMessage(false).' }, '.$this->auto_hide_delay.');';
        }

        if ($echo) {
            echo $result;
        } else {
            return $result;
        }
    }

    public function hideMessage($echo = true)
    {
        $callStr = !empty($this->callback_on_hide) ? $this->callback_on_hide.';' : '';
        $result  = 'jQuery("#'.$this->id.'").fadeOut( "slow", function() { $(this).addClass("no_display");'.$callStr.' });';

        if ($echo) {
            echo $result;
        } else {
            return $result;
        }
    }
}PK�{2\k�fNui/index.phpnu�[���<?php
//silentPK�{2\�#
��ui/class.ui.dialog.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Used to generate a thick-box inline dialog such as an alert or confirm pop-up
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/ui
 * @copyright (c) 2017, Snapcreek LLC
 *
 */

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_UI_Dialog
{
    /**
     * The title that shows up in the dialog
     */
    public $title;

    /**
     * The message displayed in the body of the dialog
     */
    public $message;

    /**
     * The width of the dialog the default is used if not set
     * Alert = 475px (default) |  Confirm = 500px (default)
     */
    public $width;

    /**
     * The height of the dialog the default is used if not set
     * Alert = 125px (default) |  Confirm = 150px (default)
     */
    public $height;

    /**
     * When the progress meter is running show this text
     * Available only on confirm dialogs
     */
    public $progressText;

    /**
     * When true a progress meter will run until page is reloaded
     * Available only on confirm dialogs
     */
    public $progressOn = true;

    /**
     * The javascript call back method to call when the 'Yes' button is clicked
     * Available only on confirm dialogs
     */
    public $jscallback;

    /**
     *
     * @var string
     */
    public $okText;

    /**
     *
     * @var string
     */
    public $cancelText;

    /**
     * If true close dialog on confirm
     * @var bool
     */
    public $closeOnConfirm = false;


    /**
     * The id given to the full dialog
     */
    private $id;

    /**
     * A unique id that is added to all id elements
     */
    private $uniqid;

    /**
     *  Init this object when created
     */
    public function __construct()
    {
        add_thickbox();
        $this->progressText = __('Processing please wait...', 'duplicator');
        $this->uniqid		= substr(uniqid('',true),0,14) . rand();
        $this->id           = 'dup-dlg-'.$this->uniqid;
        $this->okText       = __('OK', 'duplicator');
        $this->cancelText   = __('Cancel', 'duplicator');
    }

    /**
     * Gets the unique id that is assigned to each instance of a dialog
     *
     * @return int      The unique ID of this dialog
     */
    public function getID()
    {
        return $this->id;
    }

    /**
     * Gets the unique id that is assigned to each instance of a dialogs message text
     *
     * @return int      The unique ID of the message
     */
    public function getMessageID()
    {
        return "{$this->id}_message";
    }

    /**
     * Initialize the alert base html code used to display when needed
     *
     * @return string	The html content used for the alert dialog
     */
    public function initAlert()
    {
        $onClickClose = '';
        if (!is_null($this->jscallback)) {
            $onClickClose .= $this->jscallback.';';
        }
        $onClickClose .= 'tb_remove();';

        if (strlen($this->okText) == 0) {
            $hideButton = "style='display:none'";
        }

        $html = '
		<div id="'.esc_attr($this->id).'" style="display:none">
			<div class="dup-dlg-alert-txt">
				'.$this->message.'
				<br/><br/>
			</div>
			<div class="dup-dlg-alert-btns">
				<input type="button" class="button button-large" value="'.esc_attr($this->okText).'" onclick="'.$onClickClose.'" '. $hideButton . '/>
			</div>
		</div>';

        echo $html;
    }

    /**
     * Shows the alert base JS code used to display when needed
     *
     * @return string	The javascript content used for the alert dialog
     */
    public function showAlert()
    {
        $this->width  = is_numeric($this->width) ? $this->width : 500;
        $this->height = is_numeric($this->height) ? $this->height : 175;

        $html = "tb_show('".esc_js($this->title)."', '#TB_inline?width=".esc_js($this->width)."&height=".esc_js($this->height)."&inlineId=".esc_js($this->id)."');\n" .
				 "var styleData = jQuery('#TB_window').attr('style') + 'height: ".esc_js($this->height)."px !important';\n" .
			 	 "jQuery('#TB_window').attr('style', styleData);";

		echo $html;
    }

    /**
     * Shows the confirm base JS code used to display when needed
     *
     * @return string	The javascript content used for the confirm dialog
     */
    public function initConfirm()
    {
        $progress_data  = '';
        $progress_func2 = '';

        $onClickConfirm = '';
        if (!is_null($this->jscallback)) {
            $onClickConfirm .= $this->jscallback.';';
        }

        //Enable the progress spinner
        if ($this->progressOn) {
            $progress_func1 = "__DUP_UI_Dialog_".$this->uniqid;
            $progress_func2 = ";{$progress_func1}(this)";
            $progress_data  = "<div class='dup-dlg-confirm-progress'><i class='fas fa-circle-notch fa-spin fa-lg fa-fw'></i> ".esc_js($this->progressText)."</div>
				<script>
					function {$progress_func1}(obj)
					{
						jQuery(obj).parent().parent().find('.dup-dlg-confirm-progress').show();
						jQuery(obj).closest('.dup-dlg-confirm-btns').find('input').attr('disabled', 'true');
					}
				</script>";
            $onClickConfirm .= $progress_func2.';';
        }

        if ($this->closeOnConfirm) {
             $onClickConfirm .= 'tb_remove();';
        }

        $html =
            '<div id="'.esc_attr($this->id).'" style="display:none">
				<div class="dup-dlg-confirm-txt">
					<span id="'.esc_attr($this->id).'_message">'.esc_html($this->message).'</span>
					<br/><br/>
					'.$progress_data.'
				</div>
				<div class="dup-dlg-confirm-btns">
					<input type="button" class="button button-large" value="'.esc_attr($this->okText).'" onclick="'.$onClickConfirm.'" />
					<input type="button" class="button button-large" value="'.esc_attr($this->cancelText).'" onclick="tb_remove()" />
				</div>
			</div>';

        echo $html;
    }

    /**
     * Shows the confirm base JS code used to display when needed
     *
     * @return string	The javascript content used for the confirm dialog
     */
    public function showConfirm()
    {
        $this->width  = is_numeric($this->width) ? $this->width : 500;
        $this->height = is_numeric($this->height) ? $this->height : 225;
        $html = "tb_show('".esc_js($this->title)."', '#TB_inline?width=".esc_js($this->width)."&height=".esc_js($this->height)."&inlineId=".esc_js($this->id)."');\n" .
				 "var styleData = jQuery('#TB_window').attr('style') + 'height: ".esc_js($this->height)."px !important';\n" .
			 	 "jQuery('#TB_window').attr('style', styleData);";

		echo $html;
    }
}PK�{2\D���xxui/class.ui.screen.base.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * The base class for all screen.php files.  This class is used to control items that are common
 * among all screens, namely the Help tab and Screen Options drop down items.  When creating a
 * screen object please extent this class.
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/ui
 * @copyright (c) 2017, Snapcreek LLC
 *
 */
// Exit if accessed directly
if (!defined('DUPLICATOR_VERSION'))
    exit;

class DUP_UI_Screen
{

    /**
     * Used as a placeholder for the current screen object
     */
    public $screen;

    /**
     *  Init this object when created
     */
    public function __construct()
    {
        
    }

    public static function getCustomCss()
    {
        $screen = get_current_screen();
        if (!in_array($screen->id, array(
            'toplevel_page_duplicator',
            'duplicator_page_duplicator-tools',
            'duplicator_page_duplicator-settings',
            'duplicator_page_duplicator-gopro'))) {
            return;
        }

        $colorScheme = self::getCurrentColorScheme();
        if ($colorScheme !== false) {
            ?>
            <style>
                .link-style {
                    color: <?php echo $colorScheme->colors[2]; ?>;
                }

                .link-style:hover {
                    color: <?php echo $colorScheme->colors[3]; ?>;
                }
            </style>
            <?php
        }
    }

    public static function getCurrentColorScheme()
    {
        global $_wp_admin_css_colors;
        if(!isset($_wp_admin_css_colors) || !is_array($_wp_admin_css_colors)){
            return false;
        }

        $colorScheme = get_user_option('admin_color');

        if (isset($_wp_admin_css_colors[$colorScheme])) {
            return $_wp_admin_css_colors[$colorScheme];
        } else {
             return $_wp_admin_css_colors[DupLiteSnapLibUtil::arrayKeyFirst($_wp_admin_css_colors)];
        }
    }

    /**
     * Get the help support tab view content shown in the help system
     *
     * @param string $guide		The target URL to navigate to on the online user guide
     * @param string $faq		The target URL to navigate to on the online user tech FAQ
     *
     * @return null
     */
    public function getSupportTab($guide, $faq)
    {
        $content = __("<b>Need Help?</b>  Please check out these resources first:"
            ."<ul>"
            ."<li><a href='https://snapcreek.com/duplicator/docs/guide{$guide}' target='_sc-faq'>Full Online User Guide</a></li>"
            ."<li><a href='https://snapcreek.com/duplicator/docs/faqs-tech{$faq}' target='_sc-faq'>Frequently Asked Questions</a></li>"
            ."</ul>", 'duplicator');

        $this->screen->add_help_tab(array(
            'id'      => 'dup_help_tab_callback',
            'title'   => esc_html__('Support', 'duplicator'),
            'content' => "<p>{$content}</p>"
            )
        );
    }

    /**
     * Get the help support side bar found in the right most part of the help system
     *
     * @return null
     */
    public function getHelpSidbar()
    {
        $txt_title = __("Resources", 'duplicator');
        $txt_home  = __("Knowledge Base", 'duplicator');
        $txt_guide = __("Full User Guide", 'duplicator');
        $txt_faq   = __("Technical FAQs", 'duplicator');
        $txt_sets  = __("Package Settings", 'duplicator');
        $this->screen->set_help_sidebar(
            "<div class='dup-screen-hlp-info'><b>".esc_html($txt_title).":</b> <br/>"
            ."<i class='fa fa-home'></i> <a href='https://snapcreek.com/duplicator/docs/' target='_sc-home'>".esc_html($txt_home)."</a> <br/>"
            ."<i class='fa fa-book'></i> <a href='https://snapcreek.com/duplicator/docs/guide/' target='_sc-guide'>".esc_html($txt_guide)."</a> <br/>"
            ."<i class='far fa-file-code'></i> <a href='https://snapcreek.com/duplicator/docs/faqs-tech/' target='_sc-faq'>".esc_html($txt_faq)."</a> <br/>"
            ."<i class='fa fa-cog'></i> <a href='admin.php?page=duplicator-settings&tab=package'>".esc_html($txt_sets)."</a></div>"
        );
    }
}PK�{2\��v<v<ui/class.ui.notice.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Used to display notices in the WordPress Admin area
 * This class takes advantage of the admin_notice action.
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/ui
 * @copyright (c) 2017, Snapcreek LLC
 *
 */

class DUP_UI_Notice
{

    const OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL = 'duplicator_reactivate_plugins_after_installation';

    //TEMPLATE VALUE: This is a just a simple example for setting up quick notices
    const OPTION_KEY_NEW_NOTICE_TEMPLATE            = 'duplicator_new_template_notice';
    const OPTION_KEY_IS_PRO_ENABLE_NOTICE_DISMISSED = 'duplicator_is_pro_enable_notice_dismissed';
    const OPTION_KEY_IS_MU_NOTICE_DISMISSED         = 'duplicator_is_mu_notice_dismissed';

    const GEN_INFO_NOTICE    = 0;
    const GEN_SUCCESS_NOTICE = 1;
    const GEN_WARNING_NOTICE = 2;
    const GEN_ERROR_NOTICE   = 3;

    /**
     * init notice actions
     */
    public static function init()
    {
        $methods = array(
            'showReservedFilesNotice',
            'installAutoDeactivatePlugins',
            'showFeedBackNotice',
            'showNoExportCapabilityNotice',
            //FUTURE NOTICE TEMPLATE
            //'newNotice_TEMPLATE',
        );
        foreach ($methods as $method) {
            add_action('admin_notices', array(__CLASS__, $method));
        }
    }

     /**
      * NOTICE SAMPLE:  This method serves as a quick example for how to quickly setup a new notice
      * Please do not edit this method, but simply use to copy a new setup.
     */
    public static function newNotice_TEMPLATE()
    {
        if (get_option(self::OPTION_KEY_NEW_NOTICE_TEMPLATE) != true) {
            return;
        }

        $screen = get_current_screen();
        if (!in_array($screen->parent_base, array('plugins', 'duplicator'))) {
            return;
        }

        ?>
        <div class="notice notice-success duplicator-admin-notice is-dismissible" data-to-dismiss="<?php echo esc_attr(self::OPTION_KEY_NEW_NOTICE_TEMPLATE); ?>" >
            <p>
                <?php esc_html_e('NOTICE: This is a sample message notice demo.', 'duplicator'); ?><br>
                <?php
                echo sprintf(__('Example for passing dynamic data to notice message <i>%s</i> to <i>%s</i>', 'duplicator'),
                    esc_html("test 1"),
                    esc_html(time()));
                ?>
            </p>
            <p>
                <?php echo sprintf(__('More info here: Goto <a href="%s">General Settings</a>', 'duplicator'), 'admin.php?page=duplicator-settings'); ?>
            </p>
        </div>
        <?php
    }

    /**
     * Shows a display message in the wp-admin if any reserved files are found
     *
     * @return string   Html formatted text notice warnings
     */
    public static function showReservedFilesNotice()
    {
        //Show only on Duplicator pages and Dashboard when plugin is active
        $dup_active = is_plugin_active('duplicator/duplicator.php');
        $dup_perm   = current_user_can('manage_options');
        if (!$dup_active || !$dup_perm)
            return;

        $screen = get_current_screen();
        if (!isset($screen))
            return;

        $is_installer_cleanup_req = ($screen->id == 'duplicator_page_duplicator-tools' && isset($_GET['action']) && $_GET['action'] == 'installer');
        if (DUP_Server::hasInstallerFiles() && !$is_installer_cleanup_req) {
            DUP_Migration::renameInstallersPhpFiles();

            $on_active_tab = isset($_GET['section']) ? $_GET['section'] : '';
            echo '<div class="dup-updated notice notice-success dup-global-error-reserved-files" id="message"><p>';

            //Safe Mode Notice
            $safe_html = '';
            if (get_option("duplicator_exe_safe_mode", 0) > 0) {
                $safe_msg1 = __('Safe Mode:', 'duplicator');
                $safe_msg2 = __('During the install safe mode was enabled deactivating all plugins.<br/> Please be sure to ', 'duplicator');
                $safe_msg3 = __('re-activate the plugins', 'duplicator');
                $safe_html = "<div class='notice-safemode'><b>{$safe_msg1}</b><br/>{$safe_msg2} <a href='plugins.php'>{$safe_msg3}</a>!</div><br/>";
            }

            //On Tools > Cleanup Page
            if ($screen->id == 'duplicator_page_duplicator-tools' && ($on_active_tab == "info" || $on_active_tab == '')) {

                $title = __('This site has been successfully migrated!', 'duplicator');
                $msg1  = __('Final step(s):', 'duplicator');
                $msg2  = __('This message will be removed after all installer files are removed.  Installer files must be removed to maintain a secure site.  '
                    .'Click the link above or button below to remove all installer files and complete the migration.', 'duplicator');

                echo "<b class='pass-msg'><i class='fa fa-check-circle'></i> ".esc_html($title)."</b> <br/> {$safe_html} <b>".esc_html($msg1)."</b> <br/>";
                printf("1. <a href='javascript:void(0)' onclick='jQuery(\"#dup-remove-installer-files-btn\").click()'>%s</a><br/>", esc_html__('Remove Installation Files Now!', 'duplicator'));
                printf("2. <a href='https://wordpress.org/support/plugin/duplicator/reviews/?filter=5' target='wporg'>%s</a> <br/> ", esc_html__('Optionally, Review Duplicator at WordPress.org...', 'duplicator'));
                echo "<div class='pass-msg'>".esc_html($msg2)."</div>";

                //All other Pages
            } else {

                $title = __('Migration Almost Complete!', 'duplicator');
                $msg   = __('Reserved Duplicator installation files have been detected in the root directory.  Please delete these installation files to '
                    .'avoid security issues. <br/> Go to:Duplicator > Tools > Information >Stored Data and click the "Remove Installation Files" button', 'duplicator');

                $nonce = wp_create_nonce('duplicator_cleanup_page');
                $url   = self_admin_url('admin.php?page=duplicator-tools&tab=diagnostics&section=info&_wpnonce='.$nonce);
                echo "<b>{$title}</b><br/> {$safe_html} {$msg}";
                @printf("<br/><a href='{$url}'>%s</a>", __('Take me there now!', 'duplicator'));
            }
            echo "</p></div>";
        }
    }

    /**
     * Shows a message for redirecting a page
     *
     * @return string   The location to redirect to
     */
    public static function redirect($location)
    {
        echo '<div class="dup-redirect"><i class="fas fa-circle-notch fa-spin fa-fw"></i>';
        esc_html__('Redirecting Please Wait...', 'duplicator');
        echo '</div>';
        echo "<script>window.location = '{$location}';</script>";
        die(esc_html__('Invalid token permissions to perform this request.', 'duplicator'));
    }

    /**
     * Shows install deactivated function
     */
    public static function installAutoDeactivatePlugins()
    {
        $reactivatePluginsAfterInstallation = get_option(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL, false);
        if (is_array($reactivatePluginsAfterInstallation)) {
            $installedPlugins  = array_keys(get_plugins());
            $shouldBeActivated = array();
            foreach ($reactivatePluginsAfterInstallation as $pluginSlug => $pluginTitle) {
                if (in_array($pluginSlug, $installedPlugins) && !is_plugin_active($pluginSlug)) {
                    $shouldBeActivated[$pluginSlug] = $pluginTitle;
                }
            }

            if (empty($shouldBeActivated)) {
                delete_option(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL);
            } else {
                $activatePluginsAnchors = array();
                foreach ($shouldBeActivated as $slug => $title) {
                    $activateURL              = wp_nonce_url(admin_url('plugins.php?action=activate&plugin='.$slug), 'activate-plugin_'.$slug);
                    $anchorTitle              = sprintf(esc_html__('Activate %s', 'duplicator'), $title);
                    $activatePluginsAnchors[] = '<a href="'.$activateURL.'" 
                                                    title="'.esc_attr($anchorTitle).'">'.
                        $title.'</a>';
                }
                ?>
                <div class="update-nag duplicator-plugin-activation-admin-notice notice notice-warning duplicator-admin-notice is-dismissible"
                     data-to-dismiss="<?php echo esc_attr(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL); ?>" >
                    <p>
                        <?php
                        echo "<b>".esc_html__("Warning!", "duplicator")."</b> ".esc_html__("Migration Almost Complete!", "duplicator")." <br/>";
                        echo esc_html__("Plugin(s) listed here have been deactivated during installation to help prevent issues. Please activate them to finish this migration: ", "duplicator")."<br/>";
                        echo implode(' ,', $activatePluginsAnchors);
                        ?>
                    </p>
                </div>
                <?php
            }
        }
    }

    /**
     * Shows feedback notices after certain no. of packages successfully created
     */
    public static function showFeedBackNotice()
    {
        $notice_id = 'rate_us_feedback';

        if (!current_user_can('manage_options')) {
            return;
        }

        $notices = get_user_meta(get_current_user_id(), DUPLICATOR_ADMIN_NOTICES_USER_META_KEY, true);
        if (empty($notices)) {
            $notices = array();
        }

        $duplicator_pages = array(
            'toplevel_page_duplicator',
            'duplicator_page_duplicator-tools',
            'duplicator_page_duplicator-settings',
            'duplicator_page_duplicator-gopro',
        );

        if (!in_array(get_current_screen()->id, $duplicator_pages) || (isset($notices[$notice_id]) && 'true' === $notices[$notice_id])) {
            return;
        }

        // not using DUP_Util::getTablePrefix() in place of $tablePrefix because DUP_UI_Notice included initially (Duplicator\Lite\Requirement is depended on the DUP_UI_Notice)
        $tablePrefix = (is_multisite() && is_plugin_active_for_network('duplicator/duplicator.php')) ? $GLOBALS['wpdb']->base_prefix : $GLOBALS['wpdb']->prefix;
        $packagesCount = $GLOBALS['wpdb']->get_var('SELECT count(id) FROM '.$tablePrefix.'duplicator_packages WHERE status=100');

        if ($packagesCount < DUPLICATOR_FEEDBACK_NOTICE_SHOW_AFTER_NO_PACKAGE) {
            return;
        }

        $notices[$notice_id] = 'false';
        update_user_meta(get_current_user_id(), DUPLICATOR_ADMIN_NOTICES_USER_META_KEY, $notices);
        $dismiss_url = wp_nonce_url(
            add_query_arg(array(
            'action'    => 'duplicator_set_admin_notice_viewed',
            'notice_id' => esc_attr($notice_id),
                ), admin_url('admin-post.php')),
            'duplicator_set_admin_notice_viewed',
            'nonce'
        );
        ?>
        <div class="notice updated duplicator-message duplicator-message-dismissed" data-notice_id="<?php echo esc_attr($notice_id); ?>">
            <div class="duplicator-message-inner">
                <div class="duplicator-message-icon">
                    <img src="<?php echo esc_url(DUPLICATOR_PLUGIN_URL."assets/img/logo.png"); ?>" style="text-align:top; margin:0; height:60px; width:60px;" alt="Duplicator">
                </div>
                <div class="duplicator-message-content">
                    <p><strong><?php echo __('Congrats!', 'duplicator'); ?></strong> <?php printf(esc_html__('You created over %d packages with Duplicator. Great job! If you can spare a minute, please help us by leaving a five star review on WordPress.org.', 'duplicator'), DUPLICATOR_FEEDBACK_NOTICE_SHOW_AFTER_NO_PACKAGE); ?></p>
                    <p class="duplicator-message-actions">
                        <a href="https://wordpress.org/support/plugin/duplicator/reviews/?filter=5/#new-post" target="_blank" class="button button-primary duplicator-notice-rate-now"><?php esc_html_e("Sure! I'd love to help", 'duplicator'); ?></a>
                        <a href="<?php echo esc_url_raw($dismiss_url); ?>" class="button duplicator-notice-dismiss"><?php esc_html_e('Hide Notification', 'duplicator'); ?></a>
                    </p>
                </div>
            </div>
        </div>
        <?php
    }

    /**
     * Shows a display message in the wp-admin if the logged in user role has not export capability
     *
     * @return void
     */
    public static function showNoExportCapabilityNotice()
    {
        if (is_admin() && in_array('administrator', $GLOBALS['current_user']->roles) && !current_user_can('export')) {
            $errorMessage = __('<strong>Duplicator</strong><hr> Your logged-in user role does not have export capability so you don\'t have access to Duplicator functionality.', 'duplicator').
                "<br>".
                sprintf(__('<strong>RECOMMENDATION:</strong> Add export capability to your role. See FAQ: <a target="_blank" href="%s">%s</a>', 'duplicator'), 'https://snapcreek.com/duplicator/docs/faqs-tech/#faq-licensing-040-q', __('Why is the Duplicator/Packages menu missing from my admin menu?', 'duplicator'));
            DUP_UI_Notice::displayGeneralAdminNotice($errorMessage, self::GEN_ERROR_NOTICE, true);
        }
    }

    /**
     * display genral admin notice by printing it
     *
     * @param string $htmlMsg html code to be printed
     * @param integer $noticeType constant value of SELF::GEN_
     * @param boolean $isDismissible whether the notice is dismissable or not. Default is true 
     * @param array|string $extraClasses add more classes to the notice div
     * @return void
     */
    public static function displayGeneralAdminNotice($htmlMsg, $noticeType, $isDismissible = true, $extraClasses = array())
    {
        if (empty($extraClasses)) {
            $classes = array();
        } elseif (is_array($extraClasses)) {
            $classes = $extraClasses;
        } else {
            $classes = array($extraClasses);
        }

        $classes[] = 'notice';

        switch ($noticeType) {
            case self::GEN_INFO_NOTICE:
                $classes[] = 'notice-info';
                break;
            case self::GEN_SUCCESS_NOTICE:
                $classes[] = 'notice-success';
                break;
            case self::GEN_WARNING_NOTICE:
                $classes[] = 'notice-warning';
                break;
            case self::GEN_ERROR_NOTICE:
                $classes[] = 'notice-error';
                break;
            default:
                throw new Exception('Invalid Admin notice type!');
        }

        if ($isDismissible) {
            $classes[] = 'is-dismissible';
        }

        $classesStr = implode(' ', $classes);
        ?>
        <div class="<?php echo esc_attr($classesStr); ?>">
            <p>
                <?php
                if (self::GEN_ERROR_NOTICE == $noticeType) {
                    ?>
                    <i class='fa fa-exclamation-triangle'></i>
                    <?php
                }
                ?>
                <?php
                echo $htmlMsg;
                ?>
            </p>
        </div>
        <?php
    }
}PK�{2\xXtp/	/	ui/class.ui.viewstate.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Gets the view state of UI elements to remember its viewable state
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/ui
 * @copyright (c) 2017, Snapcreek LLC
 *
 */

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_UI_ViewState
{
    /**
     * The key used in the wp_options table
     *
     * @var string
     */
    private static $optionsViewStateKey = 'duplicator_ui_view_state';

    /**
     * Save the view state of UI elements
     *
     * @param string $key A unique key to define the UI element
     * @param string $value A generic value to use for the view state
     *
     * @return bool Returns true if the value was successfully saved
     */
    public static function save($key, $value)
    {
        $view_state       = array();
        $view_state       = get_option(self::$optionsViewStateKey);
        $view_state[$key] = $value;
        $success          = update_option(self::$optionsViewStateKey, $view_state);
        return $success;
    }

    /**
     * 	Gets all the values from the settings array
     *
     *  @return array Returns and array of all the values stored in the settings array
     */
    public static function getArray()
    {
        return get_option(self::$optionsViewStateKey);
    }

    /**
     * Sets all the values from the settings array
     * @param array $view_state states
     * 
     * @return boolean Returns whether updated or not
     */
    public static function setArray($view_state)
    {
        return update_option(self::$optionsViewStateKey, $view_state);
    }

    /**
     * Return the value of the of view state item
     *
     * @param type $searchKey The key to search on
     * @return string Returns the value of the key searched or null if key is not found
     */
    public static function getValue($searchKey)
    {
        $view_state = get_option(self::$optionsViewStateKey);
        if (is_array($view_state)) {
            foreach ($view_state as $key => $value) {
                if ($key == $searchKey) {
                    return $value;
                }
            }
        }
        return null;
    }
}PK�{2\�'d�* * class.db.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Lightweight abstraction layer for common simple database routines
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 *
 */
// Exit if accessed directly
if (!defined('DUPLICATOR_VERSION')) exit;

class DUP_DB extends wpdb
{
    const MAX_TABLE_COUNT_IN_PACKET    = 100;
    
	public static $remove_placeholder_escape_exists = null;

	public static function init()
	{
		global $wpdb;
		self::$remove_placeholder_escape_exists = method_exists($wpdb, 'remove_placeholder_escape');
	}

	/**
	 * Get the requested MySQL system variable
	 *
	 * @param string $name The database variable name to lookup
	 *
	 * @return string the server variable to query for
	 */
	public static function getVariable($name)
	{
		global $wpdb;
		if (strlen($name)) {
			$row = $wpdb->get_row("SHOW VARIABLES LIKE '{$name}'", ARRAY_N);
			return isset($row[1]) ? $row[1] : null;
		} else {
			return null;
		}
	}

	/**
	 * Gets the MySQL database version number
	 *
	 * @param bool $full    True:  Gets the full version
	 *                      False: Gets only the numeric portion i.e. 5.5.6 or 10.1.2 (for MariaDB)
	 *
	 * @return false|string 0 on failure, version number on success
	 */
	public static function getVersion($full = false)
	{
		global $wpdb;

		if ($full) {
			$version = self::getVariable('version');
		} else {
			$version = preg_replace('/[^0-9.].*/', '', self::getVariable('version'));
		}

		//Fall-back for servers that have restricted SQL for SHOW statement
		if (empty($version)) {
			$version = $wpdb->db_version();
		}

		return empty($version) ? 0 : $version;
	}

	/**
	 * Try to return the mysqldump path on Windows servers
	 *
	 * @return boolean|string
	 */
	public static function getWindowsMySqlDumpRealPath()
	{
		if (function_exists('php_ini_loaded_file')) {
			$get_php_ini_path = php_ini_loaded_file();
			if (file_exists($get_php_ini_path)) {
				$search = array(
					dirname(dirname($get_php_ini_path)).'/mysql/bin/mysqldump.exe',
					dirname(dirname(dirname($get_php_ini_path))).'/mysql/bin/mysqldump.exe',
					dirname(dirname($get_php_ini_path)).'/mysql/bin/mysqldump',
					dirname(dirname(dirname($get_php_ini_path))).'/mysql/bin/mysqldump',
				);

				foreach ($search as $mysqldump) {
					if (file_exists($mysqldump)) {
						return str_replace("\\", "/", $mysqldump);
					}
				}
			}
		}

		unset($search);
		unset($get_php_ini_path);

		return false;
	}

    /**
     * Returns the correct database build mode PHP, MYSQLDUMP, PHPCHUNKING
     *
     * @return string	Returns a string with one of theses three values PHP, MYSQLDUMP, PHPCHUNKING
     */
    public static function getBuildMode()
    {
        $package_mysqldump = DUP_Settings::Get('package_mysqldump');
        $mysqlDumpPath     = DUP_DB::getMySqlDumpPath();

        return ($mysqlDumpPath && $package_mysqldump) ? 'MYSQLDUMP' : 'PHP';
    }

    /**
     * Returns the mysqldump path if the server is enabled to execute it otherwise false
     *
     * @return boolean|string
     */
    public static function getMySqlDumpPath()
    {
        //Is shell_exec possible
        if (!DUP_Util::hasShellExec()) {
            return false;
        }

        $custom_mysqldump_path = DUP_Settings::Get('package_mysqldump_path');
        $custom_mysqldump_path = (strlen($custom_mysqldump_path)) ? $custom_mysqldump_path : '';

        //Common Windows Paths
        if (DUP_Util::isWindows()) {
            $paths = array(
                $custom_mysqldump_path,
                'mysqldump.exe',
                self::getWindowsMySqlDumpRealPath(),
                'C:/xampp/mysql/bin/mysqldump.exe',
                'C:/Program Files/xampp/mysql/bin/mysqldump',
                'C:/Program Files/MySQL/MySQL Server 6.0/bin/mysqldump',
                'C:/Program Files/MySQL/MySQL Server 5.5/bin/mysqldump',
                'C:/Program Files/MySQL/MySQL Server 5.4/bin/mysqldump'
            );

            //Common Linux Paths
        } else {
            $paths = array(
                $custom_mysqldump_path,
                'mysqldump',
                '/usr/local/bin/mysqldump',
                '/usr/local/mysql/bin/mysqldump',
                '/usr/mysql/bin/mysqldump',
                '/usr/bin/mysqldump',
                '/opt/local/lib/mysql6/bin/mysqldump',
				'/opt/local/lib/mysql5/bin/mysqldump',
				'/usr/bin/mysqldump',
            );
        }

		$exec_available = function_exists('exec');
        foreach ($paths as $path) {
			if (@file_exists($path)) {
				if (DUP_Util::isExecutable($path)) {
					return $path;
				}
			} elseif ($exec_available) {
				$out = array();
				$rc  = -1;
				$cmd = $path . ' --help';
				@exec($cmd, $out, $rc);
				if ($rc === 0) {
					return $path;
				}
			}
        }

        return false;
    }

    /**
     * Returns all collation types that are assigned to the tables and columns table in
     * the current database.  Each element in the array is unique
     *
     * @param array &$tablesToInclude A list of tables to include in the search.
     *        Parameter does not change in the function, is passed by reference only to avoid copying.
     *
     * @return array    Returns an array with all the collation types being used
     */
    public static function getTableCollationList(&$tablesToInclude)
    {
        global $wpdb;
        static $collations = null;
        if (is_null($collations)) {
            $collations = array();
            //use half the number of tables since we are using them twice
            foreach (array_chunk($tablesToInclude, self::MAX_TABLE_COUNT_IN_PACKET) as $tablesChunk) {
                $sqlTables = implode(",", array_map(array(__CLASS__, 'escValueToQueryString'), $tablesChunk));

                //UNION is by default DISTINCT
                $query = "SELECT `COLLATION_NAME` FROM `information_schema`.`columns` WHERE `COLLATION_NAME` IS NOT NULL AND `table_schema` = '{$wpdb->dbname}' "
                    . "AND `table_name` in (" . $sqlTables . ")"
                    . "UNION SELECT `TABLE_COLLATION` FROM `information_schema`.`tables` WHERE `TABLE_COLLATION` IS NOT NULL AND `table_schema` = '{$wpdb->dbname}' "
                    . "AND `table_name` in (" . $sqlTables . ")";

                if (!$wpdb->query($query)) {
                    DUP_Log::Info("GET TABLE COLLATION ERROR: " . $wpdb->last_error);
                    continue;
                }

                $collations = array_unique(array_merge($collations, $wpdb->get_col()));
            }
            sort($collations);
        }
        return $collations;
    }

	/**
	 * Returns an escaped SQL string
	 *
	 * @param string	$sql						The SQL to escape
	 * @param bool		$removePlaceholderEscape	Patch for how the default WP function works.
	 *
	 * @return boolean|string
	 * @also see: https://make.wordpress.org/core/2017/10/31/changed-behaviour-of-esc_sql-in-wordpress-4-8-3/
	 */
	public static function escSQL($sql, $removePlaceholderEscape = false)
	{
		global $wpdb;

		$removePlaceholderEscape = $removePlaceholderEscape && self::$remove_placeholder_escape_exists;

		if ($removePlaceholderEscape) {
			return $wpdb->remove_placeholder_escape(@esc_sql($sql));
		} else {
			return @esc_sql($sql);
		}
	}
    
     /**
     * this function escape sql string without add and remove remove_placeholder_escape
     * doesn't work on array
     *
     * @global type $wpdb
     * @param mixed $sql
     * @return string
     */
    public static function escValueToQueryString($value)
    {
        global $wpdb;

        if (is_null($value)) {
            return 'NULL';
        }

        if ($wpdb->use_mysqli) {
            return '"'.mysqli_real_escape_string($wpdb->dbh, $value).'"';
        } else {
            return '"'.mysql_real_escape_string($value, $wpdb->dbh).'"';
        }
    }
}PK�{2\r9����class.password.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
#
# Portable PHP password hashing framework.
#
# Version 0.5 / genuine.
#
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
# the public domain.  Revised in subsequent years, still public domain.
#
# There's absolutely no warranty.
#
# The homepage URL for this framework is:
#
#	http://www.openwall.com/phpass/
#
# Please be sure to update the Version line if you edit this file in any way.
# It is suggested that you leave the main version number intact, but indicate
# your project name (after the slash) and add your own revision information.
#
# Please do not change the "private" password hashing method implemented in
# here, thereby making your hashes incompatible.  However, if you must, please
# change the hash type identifier (the "$P$") to something different.
#
# Obviously, since this code is in the public domain, the above are not
# requirements (there can be none), but merely suggestions.
#

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_PasswordHash
{

	var $itoa64;
	var $iteration_count_log2;
	var $portable_hashes;
	var $random_state;

	function __construct($iteration_count_log2, $portable_hashes)
	{
		$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

		if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
			$iteration_count_log2 = 8;
		$this->iteration_count_log2 = $iteration_count_log2;

		$this->portable_hashes = $portable_hashes;

		$this->random_state = microtime();
		if (function_exists('getmypid'))
			$this->random_state .= getmypid();
	}

	function PasswordHash($iteration_count_log2, $portable_hashes)
	{
		self::__construct($iteration_count_log2, $portable_hashes);
	}

	function get_random_bytes($count)
	{
		$output = '';
		if (@is_readable('/dev/urandom') &&
		    ($fh = @fopen('/dev/urandom', 'rb'))) {
			$output = fread($fh, $count);
			fclose($fh);
		}

		if (strlen($output) < $count) {
			$output = '';
			for ($i = 0; $i < $count; $i += 16) {
				$this->random_state =
				    md5(microtime() . $this->random_state);
				$output .= md5($this->random_state, TRUE);
			}
			$output = substr($output, 0, $count);
		}

		return $output;
	}

	function encode64($input, $count)
	{
		$output = '';
		$i = 0;
		do {
			$value = ord($input[$i++]);
			$output .= $this->itoa64[$value & 0x3f];
			if ($i < $count)
				$value |= ord($input[$i]) << 8;
			$output .= $this->itoa64[($value >> 6) & 0x3f];
			if ($i++ >= $count)
				break;
			if ($i < $count)
				$value |= ord($input[$i]) << 16;
			$output .= $this->itoa64[($value >> 12) & 0x3f];
			if ($i++ >= $count)
				break;
			$output .= $this->itoa64[($value >> 18) & 0x3f];
		} while ($i < $count);

		return $output;
	}

	function gensalt_private($input)
	{
		$output = '$P$';
		$output .= $this->itoa64[min($this->iteration_count_log2 +
			((PHP_VERSION >= '5') ? 5 : 3), 30)];
		$output .= $this->encode64($input, 6);

		return $output;
	}

	function crypt_private($password, $setting)
	{
		$output = '*0';
		if (substr($setting, 0, 2) === $output)
			$output = '*1';

		$id = substr($setting, 0, 3);
		# We use "$P$", phpBB3 uses "$H$" for the same thing
		if ($id !== '$P$' && $id !== '$H$')
			return $output;

		$count_log2 = strpos($this->itoa64, $setting[3]);
		if ($count_log2 < 7 || $count_log2 > 30)
			return $output;

		$count = 1 << $count_log2;

		$salt = substr($setting, 4, 8);
		if (strlen($salt) !== 8)
			return $output;

		# We were kind of forced to use MD5 here since it's the only
		# cryptographic primitive that was available in all versions
		# of PHP in use.  To implement our own low-level crypto in PHP
		# would have resulted in much worse performance and
		# consequently in lower iteration counts and hashes that are
		# quicker to crack (by non-PHP code).
		$hash = md5($salt . $password, TRUE);
		do {
			$hash = md5($hash . $password, TRUE);
		} while (--$count);

		$output = substr($setting, 0, 12);
		$output .= $this->encode64($hash, 16);

		return $output;
	}

	function gensalt_blowfish($input)
	{
		# This one needs to use a different order of characters and a
		# different encoding scheme from the one in encode64() above.
		# We care because the last character in our encoded string will
		# only represent 2 bits.  While two known implementations of
		# bcrypt will happily accept and correct a salt string which
		# has the 4 unused bits set to non-zero, we do not want to take
		# chances and we also do not want to waste an additional byte
		# of entropy.
		$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

		$output = '$2a$';
		$output .= chr(ord('0') + (int)($this->iteration_count_log2 / 10));
		$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
		$output .= '$';

		$i = 0;
		do {
			$c1 = ord($input[$i++]);
			$output .= $itoa64[$c1 >> 2];
			$c1 = ($c1 & 0x03) << 4;
			if ($i >= 16) {
				$output .= $itoa64[$c1];
				break;
			}

			$c2 = ord($input[$i++]);
			$c1 |= $c2 >> 4;
			$output .= $itoa64[$c1];
			$c1 = ($c2 & 0x0f) << 2;

			$c2 = ord($input[$i++]);
			$c1 |= $c2 >> 6;
			$output .= $itoa64[$c1];
			$output .= $itoa64[$c2 & 0x3f];
		} while (1);

		return $output;
	}

	function HashPassword($password)
	{
		$random = '';

		if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) {
			$random = $this->get_random_bytes(16);
			$hash =
			    crypt($password, $this->gensalt_blowfish($random));
			if (strlen($hash) === 60)
				return $hash;
		}

		if (strlen($random) < 6)
			$random = $this->get_random_bytes(6);
		$hash =
		    $this->crypt_private($password,
		    $this->gensalt_private($random));
		if (strlen($hash) === 34)
			return $hash;

		# Returning '*' on error is safe here, but would _not_ be safe
		# in a crypt(3)-like function used _both_ for generating new
		# hashes and for validating passwords against existing hashes.
		return '*';
	}

	function CheckPassword($password, $stored_hash)
	{
		$hash = $this->crypt_private($password, $stored_hash);
		if ($hash[0] === '*')
			$hash = crypt($password, $stored_hash);

		return $hash === $stored_hash;
	}
}

PK�{2\���M;M;class.logging.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
// Exit if accessed directly
if (!defined('DUPLICATOR_VERSION')) exit;

/**
 * Helper Class for logging
 * @package Duplicator\classes
 */
abstract class Dup_ErrorBehavior
{
	const LogOnly			 = 0;
	const ThrowException	 = 1;
	const Quit			 = 2;
}

class DUP_Log
{
    /**
     * The file handle used to write to the package log file
     */
    private static $logFileHandle = null;

    /**
     * Get the setting which indicates if tracing is enabled
     */
    private static $traceEnabled = false;

    public static $profileLogs = null;
    
	/**
	 * Init this static object
	 */
	public static function Init()
	{
		self::$traceEnabled = (DUP_Settings::Get('trace_log_enabled') == 1);
	}

    /**
     * Open a log file connection for writing to the package log file
     *
     * @param string $nameHas The Name of the log file to create
     *
     * @return nul
     */
    public static function Open($nameHash)
    {
        if (!isset($nameHash)) {
            throw new Exception("A name value is required to open a file log.");
        }
        self::Close();
        if ((self::$logFileHandle = @fopen(DUP_Settings::getSsdirPath()."/{$nameHash}.log", "a+")) === false) {
            self::$logFileHandle = null;
            return false;
        } else {
            /**
             * By initializing the error_handler on opening the log, I am sure that when a package is processed, the handler is active.
             */
            DUP_Handler::init_error_handler();
            return true;
        }
    }

    /**
     * Close the package log file connection if is opened
     *
     * @return bool Returns TRUE on success or FALSE on failure.
     */
    public static function Close()
    {
        $result = true;
        
        if (!is_null(self::$logFileHandle)) {
            $result              = @fclose(self::$logFileHandle);
            self::$logFileHandle = null;
        } else {
            $result = true;
        }
        return $result;
    }

    /**
     *  General information send to the package log if opened
     *  @param string $msg	The message to log
     *
     *  REPLACE TO DEBUG: Memory consumption as script runs
     * 	$results = DUP_Util::byteSize(memory_get_peak_usage(true)) . "\t" . $msg;
     * 	@fwrite(self::$logFileHandle, "{$results} \n");
     *
     *  @param string $msg	The message to log
     *
     *  @return null
     */
    public static function Info($msg)
    {
        if (self::$traceEnabled) {
            self::Trace($msg);
        }
        if (!is_null(self::$logFileHandle)) {
            @fwrite(self::$logFileHandle, $msg."\n");
        }
    }

    public static function print_r_info($val, $name = '')
    {
        $msg = empty($name) ? '' : 'VALUE '.$name.': ';
        $msg .= print_r($val, true);
        self::info($msg);
    }

	/**
	 * Does the trace file exists
	 *
	 * @return bool Returns true if an active trace file exists
	 */
	public static function TraceFileExists()
	{
		$file_path = self::getTraceFilepath();
		return file_exists($file_path);
	}

	/**
	 * Gets the current file size of the active trace file
	 *
	 * @return string   Returns a human readable file size of the active trace file
	 */
	public static function getTraceStatus()
	{
		$file_path	 = DUP_Log::getTraceFilepath();
		$backup_path = DUP_Log::getBackupTraceFilepath();

		if (file_exists($file_path)) {

            $filesize = is_file($file_path) ? @filesize($file_path) : 0;

            //Its possible mulitple trace log files exist due to size
			if (is_file($backup_path)) {
				$filesize += @filesize($backup_path);
			}

			$message = sprintf('%1$s', DUP_Util::byteSize($filesize));
		} else {
			$message = esc_html__('No Log', 'duplicator');
		}

		return $message;
	}

	// RSR TODO: Swap trace logic out for real trace later
	public static function Trace($message, $calling_function_override = null)
	{

		if (self::$traceEnabled) {
			$unique_id = sprintf("%08x", abs(crc32($_SERVER['REMOTE_ADDR'].$_SERVER['REQUEST_TIME'].$_SERVER['REMOTE_PORT'])));

			if ($calling_function_override == null) {
				$calling_function = DupLiteSnapLibUtil::getCallingFunctionName();
			} else {
				$calling_function = $calling_function_override;
			}

			if (is_object($message)) {
				$ov		 = get_object_vars($message);
				$message = print_r($ov, true);
			} else if (is_array($message)) {
				$message = print_r($message, true);
			}

			$logging_message			 = "{$unique_id}|{$calling_function} | {$message}";
			$ticks						 = time() + ((int) get_option('gmt_offset') * 3600);
			$formatted_time				 = date('d-m-H:i:s', $ticks);
			$formatted_logging_message	 = "{$formatted_time}|DUP|{$logging_message} \r\n";

			// Always write to error log - if they don't want the info they can turn off WordPress error logging or tracing
			self::ErrLog($logging_message);

			// Everything goes to the plugin log, whether it's part of package generation or not.
			self::WriteToTrace($formatted_logging_message);
		}
	}

    public static function print_r_trace($val, $name = '', $calling_function_override = null)
    {
        $msg = empty($name) ? '' : 'VALUE '.$name.': ';
        $msg .= print_r($val, true);
        self::trace($msg, $calling_function_override);
    }

	public static function errLog($message)
	{
		$message = 'DUP:'.$message;
		error_log($message);
	}

	public static function TraceObject($msg, $o, $log_private_members = true)
	{
		if (self::$traceEnabled) {
			if (!$log_private_members) {
				$o = get_object_vars($o);
			}
			self::Trace($msg.':'.print_r($o, true));
		}
	}

	public static function GetDefaultKey()
	{
		$auth_key	 = defined('AUTH_KEY') ? AUTH_KEY : 'atk';
		$auth_key	 .= defined('DB_HOST') ? DB_HOST : 'dbh';
		$auth_key	 .= defined('DB_NAME') ? DB_NAME : 'dbn';
		$auth_key	 .= defined('DB_USER') ? DB_USER : 'dbu';
		return hash('md5', $auth_key);
	}

    /**
	 * Gets the current file size of the old trace file "1"
	 *
	 * @return string   Returns a human readable file size of the active trace file
	 */
	public static function GetBackupTraceFilepath()
	{
		$default_key		 = self::getDefaultKey();
		$backup_log_filename = "dup_$default_key.log1";
		$backup_path		 = DUP_Settings::getSsdirPath()."/".$backup_log_filename;
		return $backup_path;
	}

	/**
	 * Gets the active trace file path
	 *
	 * @return string   Returns the full path to the active trace file (i.e. dup-pro_hash.log)
	 */
	public static function GetTraceFilepath()
	{
		$default_key	 = self::getDefaultKey();
		$log_filename	 = "dup_$default_key.log";
		$file_path		 = DUP_Settings::getSsdirPath()."/".$log_filename;
		return $file_path;
	}

	/**
	 * Deletes the trace log and backup trace log files
	 *
	 * @return null
	 */
	public static function DeleteTraceLog()
	{
		$file_path	 = self::GetTraceFilepath();
		$backup_path = self::GetBackupTraceFilepath();
		self::trace("deleting $file_path");
		@unlink($file_path);
		self::trace("deleting $backup_path");
		@unlink($backup_path);
	}

    /**
     *  Called when an error is detected and no further processing should occur
     * @param string $msg The message to log
     * @param string $detail Additional details to help resolve the issue if possible
     * @param int $behavior
     * @throws Exception
     */
	public static function error($msg, $detail = '', $behavior = Dup_ErrorBehavior::Quit)
	{

		error_log($msg.' DETAIL:'.$detail);
		$source = self::getStack(debug_backtrace());

		$err_msg = "\n==================================================================================\n";
		$err_msg .= "DUPLICATOR ERROR\n";
		$err_msg .= "Please try again! If the error persists see the Duplicator 'Help' menu.\n";
		$err_msg .= "---------------------------------------------------------------------------------\n";
		$err_msg .= "MESSAGE:\n\t{$msg}\n";
		if (strlen($detail)) {
			$err_msg .= "DETAILS:\n\t{$detail}\n";
		}
		$err_msg .= "TRACE:\n{$source}";
		$err_msg .= "==================================================================================\n\n";
		@fwrite(self::$logFileHandle, "{$err_msg}");

		switch ($behavior) {
            
			case Dup_ErrorBehavior::ThrowException:
				DUP_LOG::trace("throwing exception");
                throw new Exception($msg);
				break;

			case Dup_ErrorBehavior::Quit:
				DUP_LOG::trace("quitting");
				die("DUPLICATOR ERROR: Please see the 'Package Log' file link below.");
				break;

			default:
			// Nothing
		}
	}

	/**
	 * The current stack trace of a PHP call
	 * @param $stacktrace The current debug stack
	 * @return string
	 */
	public static function getStack($stacktrace)
	{
		$output	 = "";
		$i		 = 1;
		foreach ($stacktrace as $node) {
			$output .= "\t $i. ".basename($node['file'])." : ".$node['function']." (".$node['line'].")\n";
			$i++;
		}
		return $output;
	}

	/**
	 * Manages writing the active or backup log based on the size setting
	 *
	 * @return null
	 */
	private static function WriteToTrace($formatted_logging_message)
	{
		$log_filepath = self::GetTraceFilepath();

		if (@filesize($log_filepath) > DUPLICATOR_MAX_LOG_SIZE) {
			$backup_log_filepath = self::GetBackupTraceFilepath();

			if (file_exists($backup_log_filepath)) {
				if (@unlink($backup_log_filepath) === false) {
					self::errLog("Couldn't delete backup log $backup_log_filepath");
				}
			}

			if (@rename($log_filepath, $backup_log_filepath) === false) {
				self::errLog("Couldn't rename log $log_filepath to $backup_log_filepath");
			}
		}

		if (@file_put_contents($log_filepath, $formatted_logging_message, FILE_APPEND) === false) {
			// Not en error worth reporting
		}
	}
}

class DUP_Handler
{
    const MODE_OFF         = 0; // don't write in log
    const MODE_LOG         = 1; // write errors in log file
    const MODE_VAR         = 2; // put php errors in $varModeLog static var
    const SHUTDOWN_TIMEOUT = 'tm';

    /**
     *
     * @var bool
     */
    private static $initialized = false;

    /**
     *
     * @var array
     */
    private static $shutdownReturns = array(
        'tm' => 'timeout'
    );

    /**
     *
     * @var int
     */
    private static $handlerMode = self::MODE_LOG;

    /**
     *
     * @var bool // print code reference and errno at end of php error line  [CODE:10|FILE:test.php|LINE:100]
     */
    private static $codeReference = true;

    /**
     *
     * @var bool // print prefix in php error line [PHP ERR][WARN] MSG: .....
     */
    private static $errPrefix = true;

    /**
     *
     * @var string // php errors in MODE_VAR
     */
    private static $varModeLog = '';

    /**
     * This function only initializes the error handler the first time it is called
     */
    public static function init_error_handler()
    {
        if (!self::$initialized) {
            @set_error_handler(array(__CLASS__, 'error'));
            @register_shutdown_function(array(__CLASS__, 'shutdown'));
            self::$initialized = true;
        }
    }

    /**
     * Error handler
     *
     * @param  integer $errno   Error level
     * @param  string  $errstr  Error message
     * @param  string  $errfile Error file
     * @param  integer $errline Error line
     * @return void
     */
    public static function error($errno, $errstr, $errfile, $errline)
    {
        switch (self::$handlerMode) {
            case self::MODE_OFF:
                if ($errno == E_ERROR) {
                    $log_message = self::getMessage($errno, $errstr, $errfile, $errline);
                    DUP_Log::error($log_message);
                }
                break;
            case self::MODE_VAR:
                self::$varModeLog .= self::getMessage($errno, $errstr, $errfile, $errline)."\n";
                break;
            case self::MODE_LOG:
            default:
                switch ($errno) {
                    case E_ERROR :
                        $log_message = self::getMessage($errno, $errstr, $errfile, $errline);
                        DUP_Log::error($log_message);
                        break;
                    case E_NOTICE :
                    case E_WARNING :
                    default :
                        $log_message = self::getMessage($errno, $errstr, $errfile, $errline);
                        DUP_Log::Info($log_message);
                        break;
                }
        }
    }

    private static function getMessage($errno, $errstr, $errfile, $errline)
    {
        $result = '';

        if (self::$errPrefix) {
            $result = '[PHP ERR]';
            switch ($errno) {
                case E_ERROR :
                    $result .= '[FATAL]';
                    break;
                case E_WARNING :
                    $result .= '[WARN]';
                    break;
                case E_NOTICE :
                    $result .= '[NOTICE]';
                    break;
                default :
                    $result .= '[ISSUE]';
                    break;
            }
            $result .= ' MSG:';
        }

        $result .= $errstr;

        if (self::$codeReference) {
            $result .= ' [CODE:'.$errno.'|FILE:'.$errfile.'|LINE:'.$errline.']';
            $result .= "\n".wp_debug_backtrace_summary();
        }

        return $result;
    }

    /**
     * if setMode is called without params set as default
     *
     * @param int $mode
     * @param bool $errPrefix // print prefix in php error line [PHP ERR][WARN] MSG: .....
     * @param bool $codeReference // print code reference and errno at end of php error line  [CODE:10|FILE:test.php|LINE:100]
     */
    public static function setMode($mode = self::MODE_LOG, $errPrefix = true, $codeReference = true)
    {
        switch ($mode) {
            case self::MODE_OFF:
            case self::MODE_VAR:
                self::$handlerMode = $mode;
                break;
            case self::MODE_LOG:
            default:
                self::$handlerMode = self::MODE_LOG;
        }

        self::$varModeLog    = '';
        self::$errPrefix     = $errPrefix;
        self::$codeReference = $codeReference;
    }

    /**
     *
     * @return string // return var log string in MODE_VAR
     */
    public static function getVarLog()
    {
        return self::$varModeLog;
    }

    /**
     *
     * @return string // return var log string in MODE_VAR and clean var
     */
    public static function getVarLogClean()
    {
        $result           = self::$varModeLog;
        self::$varModeLog = '';
        return $result;
    }

    /**
     *
     * @param string $status // timeout
     * @param string
     */
    public static function setShutdownReturn($status, $str)
    {
        self::$shutdownReturns[$status] = $str;
    }

    /**
     * Shutdown handler
     *
     * @return void
     */
    public static function shutdown()
    {
        if (($error = error_get_last())) {
            if (preg_match('/^Maximum execution time (?:.+) exceeded$/i', $error['message'])) {
                echo self::$shutdownReturns[self::SHUTDOWN_TIMEOUT];
            }
            self::error($error['type'], $error['message'], $error['file'], $error['line']);
        }
    }
}
PK�{2\n7��**utilities/class.u.shell.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_Shell_U
{
    /**
     * Escape a string to be used as a shell argument with bypass support for Windows
     *
     * 	NOTES:
     * 		Provides a way to support shell args on Windows OS and allows %,! on Windows command line
     * 		Safe if input is know such as a defined constant and not from user input escape shellarg
     * 		on Windows with turn %,! into spaces
     *
     * @return string
     */
    public static function escapeshellargWindowsSupport($string)
    {
        if (strncasecmp(PHP_OS, 'WIN', 3) == 0) {
            if (strstr($string, '%') || strstr($string, '!')) {
                $result = '"'.str_replace('"', '', $string).'"';
                return $result;
            }
        }
        return escapeshellarg($string);
    }

    /**
     *
     * @return boolean
     *
     */
    public static function isPopenEnabled() {

        if (!DUP_Util::isIniFunctionEnalbe('popen') || !DUP_Util::isIniFunctionEnalbe('proc_open')) {
            $ret = false;
        } else {
            $ret = true;
        }

        $ret = apply_filters('duplicator_is_popen_enabled', $ret);
        return $ret;
    }
}PK�{2\Q����utilities/class.u.zip.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Utility class for zipping up content
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 * @license	https://opensource.org/licenses/GPL-3.0 GNU Public License
 */

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_Zip_U
{
    /**
     * Add a directory to an existing ZipArchive object
     *
     * @param ZipArchive $zipArchive        An existing ZipArchive object
     * @param string     $directoryPath     The full directory path to add to the ZipArchive
     * @param bool       $retainDirectory   Should the full directory path be retained in the archive
     *
     * @return bool Returns true if the directory was added to the object
     */
    public static function addDirWithZipArchive(&$zipArchive, $directoryPath, $retainDirectory, $localPrefix, $isCompressed)
    {
        $success = TRUE;
        $directoryPath = rtrim($directoryPath, '/\\').'/';
		if (!$fp = @opendir($directoryPath)) {
			return FALSE;
		}
		while (FALSE !== ($file = readdir($fp))) {
			if ($file === '.' || $file === '..')    continue;
            $objectPath = $directoryPath . $file;
            // Not used DUP_U::safePath(), because I would like to decrease max_nest_level
            // Otherwise we will get the error:
            // PHP Fatal error:  Uncaught Error: Maximum function nesting level of '512' reached, aborting! in ...
            // $objectPath = DUP_U::safePath($objectPath);
            $objectPath = str_replace("\\", '/', $objectPath);
            $localName = ltrim(str_replace($directoryPath, '', $objectPath), '/');
            if ($retainDirectory) {
                $localName = basename($directoryPath)."/$localName";
            }
            $localName = $localPrefix . $localName;

			if (is_dir($objectPath)) {
                $localPrefixArg = substr($localName, 0, strrpos($localName, '/')).'/';
                $added = self::addDirWithZipArchive($zipArchive, $objectPath, $retainDirectory, $localPrefixArg, $isCompressed);
			} else if (is_readable($objectPath)) {
                $added = DUP_Zip_U::addFileToZipArchive($zipArchive, $objectPath, $localName, $isCompressed);                
            } else {
                $added = FALSE;
            }

            if (!$added) {
                DUP_Log::error("Couldn't add file $objectPath to archive", '', false);
                $success = FALSE;
                break;
            }
		}
		@closedir($fp);
		return $success;
    }


    public static function extractFiles($archiveFilepath, $relativeFilesToExtract, $destinationDirectory, $useShellUnZip)
    {
        // TODO: Unzip using either shell unzip or ziparchive
        if($useShellUnZip) {
            $shellExecPath = DUPX_Server::get_unzip_filepath();
            $filenameString = implode(' ', $relativeFilesToExtract);
            $command = "{$shellExecPath} -o -qq \"{$archiveFilepath}\" {$filenameString} -d {$destinationDirectory} 2>&1";
            $stderr = shell_exec($command);

            if ($stderr != '') {
                $errorMessage = DUP_U::__("Error extracting {$archiveFilepath}): {$stderr}");

                throw new Exception($errorMessage);
            }
        } else {
            $zipArchive = new ZipArchive();
            $result = $zipArchive->open($archiveFilepath);

            if($result !== true) {
                throw new Exception("Error opening {$archiveFilepath} when extracting. Error code: {$retVal}");
            }

            $result = $zipArchive->extractTo($destinationDirectory, $relativeFilesToExtract);

            if($result === false) {
                throw new Exception("Error extracting {$archiveFilepath}.");
            }
        }
    }

    /**
     * Add a directory to an existing ZipArchive object
     *
     * @param string    $sourceFilePath     The file to add to the zip file
     * @param string    $zipFilePath        The zip file to be added to
     * @param bool      $deleteOld          Delete the zip file before adding a file
     * @param string    $newName            Rename the $sourceFile if needed
     *
     * @return bool Returns true if the file was added to the zip file
     */
	public static function zipFile($sourceFilePath, $zipFilePath, $deleteOld, $newName, $isCompressed)
    {
        if ($deleteOld && file_exists($zipFilePath)) {
            DUP_IO::deleteFile($zipFilePath);
        }

        if (file_exists($sourceFilePath)) {
            $zip_archive = new ZipArchive();

            $is_zip_open = ($zip_archive->open($zipFilePath, ZIPARCHIVE::CREATE) === TRUE);

            if ($is_zip_open === false) {
                DUP_Log::error("Cannot create zip archive {$zipFilePath}");
            } else {
                //ADD SQL
                if ($newName == null) {
                    $source_filename = basename($sourceFilePath);
                    DUP_Log::Info("adding {$source_filename}");
                } else {
                    $source_filename = $newName;
                    DUP_Log::Info("new name added {$newName}");
                }

                $in_zip = DUP_Zip_U::addFileToZipArchive($zip_archive, $sourceFilePath, $source_filename, $isCompressed);

                if ($in_zip === false) {
                    DUP_Log::error("Unable to add {$sourceFilePath} to $zipFilePath");
                }

                $zip_archive->close();

                return true;
            }
        } else {
            DUP_Log::error("Trying to add {$sourceFilePath} to a zip but it doesn't exist!");
        }

        return false;
    }

    public static function addFileToZipArchive(&$zipArchive, $filepath, $localName, $isCompressed)
    {
		$added = $zipArchive->addFile($filepath, $localName);
        return $added;
    }
}PK�{2\di�		utilities/class.u.string.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Utility class working with strings
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package DUP
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 * @license	https://opensource.org/licenses/GPL-3.0 GNU Public License
 *
 */
class DUP_STR
{

    /**
     * Append the value to the string if it doesn't already exist
     *
     * @param string $string The string to append to
     * @param string $value The string to append to the $string
     *
     * @return string Returns the string with the $value appended once
     */
    public static function appendOnce($string, $value)
    {
        return $string.(substr($string, -1) == $value ? '' : $value);
    }

    /**
     * Returns true if the string contains UTF8 characters
     * @see http://php.net/manual/en/function.mb-detect-encoding.php
     *
     * @param string  $string     The class name where the $destArray exists
     *
     * @return null
     */
    public static function hasUTF8($string)
    {
        return preg_match('%(?:
            [\xC2-\xDF][\x80-\xBF]        # non-overlong 2-byte
            |\xE0[\xA0-\xBF][\x80-\xBF]               # excluding overlongs
            |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}      # straight 3-byte
            |\xED[\x80-\x9F][\x80-\xBF]               # excluding surrogates
            |\xF0[\x90-\xBF][\x80-\xBF]{2}    # planes 1-3
            |[\xF1-\xF3][\x80-\xBF]{3}                  # planes 4-15
            |\xF4[\x80-\x8F][\x80-\xBF]{2}    # plane 16
            )+%xs', $string);
    }

    /**
     * Returns true if the $needle is found in the $haystack
     *
     * @param string  $haystack     The full string to search in
     * @param string  $needle       The string to for
     *
     * @return bool
     */
    public static function contains($haystack, $needle)
    {
        $pos = strpos($haystack, $needle);
        return ($pos !== false);
    }

    /**
     * Returns true if the $haystack string starts with the $needle
     *
     * @param string  $haystack     The full string to search in
     * @param string  $needle       The string to for
     *
     * @return bool Returns true if the $haystack string starts with the $needle
     */
    public static function startsWith($haystack, $needle)
    {
        $length = strlen($needle);
        return (substr($haystack, 0, $length) === $needle);
    }

    /**
     * Returns true if the $haystack string ends with the $needle
     *
     * @param string  $haystack     The full string to search in
     * @param string  $needle       The string to for
     *
     * @return bool Returns true if the $haystack string ends with the $needle
     */
    public static function endsWith($haystack, $needle)
    {
        $length = strlen($needle);
        if ($length == 0) {
            return true;
        }
        return (substr($haystack, -$length) === $needle);
    }


}

PK�{2\�t����utilities/class.u.multisite.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Methods used to work with WordPress MU sites
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 *
 * @todo Refactor out IO methods into class.io.php file
 */

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_MU
{
    public static function isMultisite()
    {
        return self::getMode() > 0;
    }

    // 0 = single site; 1 = multisite subdomain; 2 = multisite subdirectory
    public static function getMode()
    {
		if(is_multisite()) {
            if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) {
                return 1;
            } else {
                return 2;
            }
        } else {
            return 0;
        }
    }
}PK�{2\��g�utilities/class.u.validator.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Validate variables
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 *
 */
// Exit if accessed directly
if (!defined('DUPLICATOR_VERSION')) {
    exit;
}

class DUP_Validator
{
    /**
     * @var array $patterns
     */
    private static $patterns = array(
        'fdir' => '/^([a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]+$/',
        'ffile' => '/^([a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]+$/',
        'fext' => '/^\.?[^\\\\\/*:<>\0?"|\s\.]+$/',
        'email' => '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_\`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/',
        'empty' => '/^$/',
        'nempty' => '/^.+$/',
    );

    const FILTER_VALIDATE_IS_EMPTY  = 'empty';
    const FILTER_VALIDATE_NOT_EMPTY = 'nempty';
    const FILTER_VALIDATE_FILE      = 'ffile';
    const FILTER_VALIDATE_FOLDER    = 'fdir';
    const FILTER_VALIDATE_FILE_EXT  = 'fext';
    const FILTER_VALIDATE_EMAIL     = 'email';

    /**
     * @var array $errors [ ['key' => string field key,
     *                      'msg' => error message ] , [] ]
     */
    private $errors = array();

    /**
     *
     */
    public function __construct()
    {
        $this->errors = array();
    }

    /**
     *
     */
    public function reset()
    {
        $this->errors = array();
    }

    /**
     *
     * @return bool
     */
    public function isSuccess()
    {
        return empty($this->errors);
    }

    /**
     *
     * @return array
     */
    public function getErrors()
    {
        return $this->errors;
    }

    /**
     *
     * @return array return errors messages
     */
    public function getErrorsMsg()
    {
        $result = array();
        foreach ($this->errors as $err) {
            $result[] = $err['msg'];
        }
        return $result;
    }

    /**
     *
     * @param string $format printf format message where %s is the variable content default "%s\n"
     * @param bool $echo if false return string
     * @return void|string
     */
    public function getErrorsFormat($format = "%s\n", $echo = true)
    {
        $msgs = $this->getErrorsMsg();

        ob_start();
        foreach ($msgs as $msg) {
            printf($format, $msg);
        }

        if ($echo) {
            ob_end_flush();
        } else {
            return ob_get_clean();
        }
    }

    /**
     *
     * @param string $key
     * @param string $msg
     */
    protected function addError($key, $msg)
    {
        $this->errors[] = array(
            'key' => $key,
            'msg' => $msg
        );
    }

    /**
     * filter_var function wrapper see http://php.net/manual/en/function.filter-var.php
     *
     * additional options
     * valkey => key of field
     * errmsg => error message; % s will be replaced with the contents of the variable  es. "<b>%s</b> isn't a valid field"
     * acc_vals => array of accepted values that skip validation
     *
     * @param mixed $variable
     * @param int $filter
     * @param array $options
     * @return mixed
     */
    public function filter_var($variable, $filter = FILTER_DEFAULT, $options = array())
    {
        $success = true;
        $result  = null;

        if (isset($options['acc_vals']) && in_array($variable, $options['acc_vals'])) {
            return $variable;
        }

        if ($filter === FILTER_VALIDATE_BOOLEAN) {
            $options['flags'] = FILTER_NULL_ON_FAILURE;

            $result = filter_var($variable, $filter, $options);

            if (is_null($result)) {
                $success = false;
            }
        } else {
            $result = filter_var($variable, $filter, $options);

            if ($result === false) {
                $success = false;
            }
        }

        if (!$success) {
            $key = isset($options['valkey']) ? $options['valkey'] : '';

            if (isset($options['errmsg'])) {
                $msg = sprintf($options['errmsg'], $variable);
            } else {
                $msg = sprintf('%1$s isn\'t a valid value', $variable);
            }

            $this->addError($key, $msg);
        }

        return $result;
    }

    /**
     * validation of predefined regular expressions
     *
     * @param mixed $variable
     * @param string $filter
     * @param array $options
     * @return type
     * @throws Exception
     */
    public function filter_custom($variable, $filter, $options = array())
    {

        if (!isset(self::$patterns[$filter])) {
            throw new Exception('Filter not valid');
        }

        $options = array_merge($options, array(
            'options' => array(
                'regexp' => self::$patterns[$filter])
            )
        );

        //$options['regexp'] = self::$patterns[$filter];

        return $this->filter_var($variable, FILTER_VALIDATE_REGEXP, $options);
    }

    /**
     * it explodes a string with a delimiter and validates every element of the array
     *
     * @param string $variable
     * @param string $delimiter
     * @param string $filter
     * @param array $options
     */
    public function explode_filter_custom($variable, $delimiter, $filter, $options = array())
    {
        if (empty($variable)) {
            return array();
        }

        $vals = explode($delimiter, trim($variable, $delimiter));
        $res  = array();

        foreach ($vals as $val) {
            $res[] = $this->filter_custom($val, $filter, $options);
        }

        return $res;
    }
}PK�{2\�U���utilities/class.u.json.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Utility class for working with JSON data
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 * @license	https://opensource.org/licenses/GPL-3.0 GNU Public License
 */

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_JSON
{
	protected static $_messages = array(
		JSON_ERROR_NONE => 'No error has occurred',
		JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded',
		JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
		JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
		JSON_ERROR_SYNTAX => 'Syntax error',
		JSON_ERROR_UTF8 => 'Malformed UTF-8 characters. To resolve see https://snapcreek.com/duplicator/docs/faqs-tech/?utm_source=duplicator_free&utm_medium=wordpress_plugin&utm_campaign=problem_resolution&utm_content=malformed_utf8#faq-package-170-q'
	);

	/**
	 * Used on PHP 5.3+ to better handle calling the json_encode method
	 *
	 * Returns a string containing the JSON representation of the supplied value
	 *
	 * @return string 
	 */
	public static function customEncode($value, $iteration = 1)
	{
		if (DUP_Util::$on_php_53_plus) {
            $encoded = DupLiteSnapJsonU::wp_json_encode_pprint($value);

			switch (json_last_error()) {
				case JSON_ERROR_NONE:
					return $encoded;
				case JSON_ERROR_DEPTH:
					throw new RuntimeException('Maximum stack depth exceeded');
				case JSON_ERROR_STATE_MISMATCH:
					throw new RuntimeException('Underflow or the modes mismatch');
				case JSON_ERROR_CTRL_CHAR:
					throw new RuntimeException('Unexpected control character found');
				case JSON_ERROR_SYNTAX:
					throw new RuntimeException('Syntax error, malformed JSON');
				case JSON_ERROR_UTF8:
					if ($iteration == 1) {
						$clean = self::makeUTF8($value);
						return self::customEncode($clean, $iteration + 1);
					} else {
						throw new RuntimeException('UTF-8 error loop');
					}
				default:
					throw new RuntimeException('Unknown error');
			}
		} else {
			return self::oldCustomEncode($value);
		}
	}

    public static function safeEncode($data, $options = 0, $depth = 512)
    {
        try {
            $jsonString = DupLiteSnapJsonU::wp_json_encode($data, $options, $depth);
        } catch (Exception $e) {
            $jsonString = false;
        }

        if (($jsonString === false) || trim($jsonString) == '') {
            $jsonString = self::customEncode($value);

            if (($jsonString === false) || trim($jsonString) == '') {
                throw new Exception('Unable to generate JSON from object');
            }
        }
        return $jsonString;
    }

	/**
	 * Attempts to only call the json_decode method directly
	 *
	 * Returns the value encoded in json in appropriate PHP type. Values true, false and null are returned as TRUE, FALSE and NULL respectively.
	 * NULL is returned if the json cannot be decoded or if the encoded data is deeper than the recursion limit.
	 *
	 * @return object
	 */
	public static function decode($json, $assoc = false)
	{
		$result = json_decode($json, $assoc);

		if ($result !== null) {
			return $result;
		}

		if (function_exists('json_last_error')) {
			throw new RuntimeException(self::$_messages[json_last_error()]);
		} else {
			throw new RuntimeException("DUP_JSON decode error");
		}

	}

	private static function makeUTF8($mixed)
	{
		if (is_array($mixed)) {
			foreach ($mixed as $key => $value) {
				$mixed[$key] = self::makeUTF8($value);
			}
		} else if (is_string($mixed)) {
			return utf8_encode($mixed);
		}
		return $mixed;
	}

	private static function escapeString($str)
	{
		return addcslashes($str, "\v\t\n\r\f\"\\/");
	}

	private static function oldCustomEncode($in)
	{
		$out = "";

		if (is_object($in)) {
			$arr[$key]	 = "\"".self::escapeString($key)."\":\"{$val}\"";
			$in			 = get_object_vars($in);
		}

		if (is_array($in)) {
			$obj = false;
			$arr = array();

			foreach ($in AS $key => $val) {
				if (!is_numeric($key)) {
					$obj = true;
				}
				$arr[$key] = self::oldCustomEncode($val);
			}

			if ($obj) {
				foreach ($arr AS $key => $val) {
					$arr[$key] = "\"".self::escapeString($key)."\":{$val}";
				}
				$val = implode(',', $arr);
				$out .= "{{$val}}";
			} else {
				$val = implode(',', $arr);
				$out .= "[{$val}]";
			}
		} elseif (is_bool($in)) {
			$out .= $in ? 'true' : 'false';
		} elseif (is_null($in)) {
			$out .= 'null';
		} elseif (is_string($in)) {
			$out .= "\"".self::escapeString($in)."\"";
		} else {
			$out .= $in;
		}

		return "{$out}";
	}
}PK�{2\r�
ZZutilities/class.u.migration.phpnu�[���<?php

/**
 * Utility class managing th emigration data
 *
 * Standard: PSR_2
 * @link http://www.php_fig.org/psr/psr_2
 * @copyright (c) 2017, Snapcreek LLC
 * @license https://opensource.org/licenses/GPL_3.0 GNU Public License
 *
 */

defined('ABSPATH') || defined('DUPXABSPATH') || exit;

class DUP_Migration
{
    const CLEAN_INSTALL_REPORT_OPTION    = 'duplicator_clean_install_report';
    const ARCHIVE_REGEX_PATTERN          = '/^(.+_[a-z0-9]{7,}_[0-9]{14})_archive\.(?:zip|daf)$/';

    /**
     * messages to be displayed in the successful migration box
     *
     * @var array
     */
    protected static $migrationCleanupReport = array(
        'removed' => array(),
        'stored'  => array(),
        'instFile' => array()
    );

    /**
     * Check the root path and in case there are installer files without hashes rename them.
     *
     * @return void
     */
    public static function renameInstallersPhpFiles()
    {
        $pathsTocheck = array(
            DupLiteSnapLibIOU::safePathTrailingslashit(ABSPATH),
            DupLiteSnapLibIOU::safePathTrailingslashit(DupLiteSnapLibUtilWp::getHomePath()),
            DupLiteSnapLibIOU::safePathTrailingslashit(WP_CONTENT_DIR)
        );

        $pathsTocheck = array_unique($pathsTocheck);

        $filesToCheck = array();
        foreach ($pathsTocheck as $cFolder) {
            if (
                !is_dir($cFolder) ||
                !is_writable($cFolder) // rename permissions
            ) {
                continue;
            }
            $cFile = $cFolder . 'installer.php';
            if (
                !is_file($cFile) ||
                !DupLiteSnapLibIOU::chmod($cFile, 'u+rw') ||
                !is_readable($cFile)
            ) {
                continue;
            }
            $filesToCheck[] = $cFile;
        }

        $installerTplCheck = '/class DUPX_Bootstrap.+const\s+ARCHIVE_FILENAME\s*=\s*[\'"](.+?)[\'"]\s*;.*const\s+PACKAGE_HASH\s*=\s*[\'"](.+?)[\'"];/s';

        foreach ($filesToCheck as $file) {
            $fileName = basename($file);
            if (($content = @file_get_contents($file, false, null, 0, 5000)) === false) {
                continue;
            }
            $matches = null;
            if (preg_match($installerTplCheck, $content, $matches) !== 1) {
                continue;
            }

            $archiveName = $matches[1];
            $hash = $matches[2];
            $matches = null;

            if (preg_match(self::ARCHIVE_REGEX_PATTERN, $archiveName, $matches) !== 1) {
                if (DupLiteSnapLibIOU::unlink($file)) {
                    self::$migrationCleanupReport['instFile'][] = "<div class='failure'>"
                        . "<i class='fa fa-check green'></i> "
                        . sprintf(__('Installer file <b>%s</b> removed for secority reasons', 'duplicator'), esc_html($fileName))
                        . "</div>";
                } else {
                    self::$migrationCleanupReport['instFile'][] = "<div class='success'>"
                        . '<i class="fa fa-exclamation-triangle red"></i> '
                        . sprintf(__('Can\'t remove installer file <b>%s</b>, please remove it for security reasons', 'duplicator'), esc_html($fileName))
                        . '</div>';
                }
                continue;
            }

            $archiveHash =  $matches[1];
            if (strpos($file, $archiveHash) === false) {
                if (DupLiteSnapLibIOU::rename($file, dirname($file) . '/' . $archiveHash . '_installer.php', true)) {
                    self::$migrationCleanupReport['instFile'][] = "<div class='failure'>"
                        . "<i class='fa fa-check green'></i> "
                        . sprintf(__('Installer file <b>%s</b> renamed with HASH', 'duplicator'), esc_html($fileName))
                        . "</div>";
                } else {
                    self::$migrationCleanupReport['instFile'][] = "<div class='success'>"
                        . '<i class="fa fa-exclamation-triangle red"></i> '
                        . sprintf(__('Can\'t rename installer file <b>%s</b> with HASH, please remove it for security reasons', 'duplicator'), esc_html($fileName))
                        . '</div>';
                }
            }
        }
    }

    /**
     * return cleanup report
     *
     * @return array
     */
    public static function getCleanupReport()
    {
        $option = get_option(self::CLEAN_INSTALL_REPORT_OPTION);
        if (is_array($option)) {
            self::$migrationCleanupReport = array_merge(self::$migrationCleanupReport, $option);
        }

        return self::$migrationCleanupReport;
    }

    /**
     * save clean up report in wordpress options
     *
     * @return boolean
     */
    public static function saveCleanupReport()
    {
        return add_option(self::CLEAN_INSTALL_REPORT_OPTION, self::$migrationCleanupReport, '', 'no');
    }
}
PK�{2\���.g.gutilities/class.u.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;

/**
 * Recursivly scans a directory and finds all sym-links and unreadable files
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 *
 * @todo Refactor out IO methods into class.io.php file
 */
class DUP_Util
{

    /**
     * Is PHP 5.2.9 or better running
     */
    public static $on_php_529_plus;

    /**
     * Is PHP 5.3 or better running
     */
    public static $on_php_53_plus;

    /**
     * Is PHP 5.4 or better running
     */
    public static $on_php_54_plus;

    /**
     * Is PHP 7 or better running
     */
    public static $PHP7_plus;

    /**
     * array of ini disable functions
     *
     * @var array
     */
    private static $iniDisableFuncs = null;

    /**
     *  Initialized on load (see end of file)
     */
    public static function init()
    {
        self::$on_php_529_plus = version_compare(PHP_VERSION, '5.2.9') >= 0;
        self::$on_php_53_plus  = version_compare(PHP_VERSION, '5.3.0') >= 0;
        self::$on_php_54_plus  = version_compare(PHP_VERSION, '5.4.0') >= 0;
        self::$PHP7_plus       = version_compare(PHP_VERSION, '7.0.0', '>=');
    }

    public static function getArchitectureString()
    {
        $php_int_size = PHP_INT_SIZE;

        switch ($php_int_size) {
            case 4:
                return esc_html__('32-bit', 'duplicator');
                break;
            case 8:
                return esc_html__('64-bit', 'duplicator');
                break;
            default:
                return esc_html__('Unknown', 'duplicator');
        }
    }

    public static function objectCopy($srcObject, $destObject, $skipMemberArray = null)
    {
        foreach ($srcObject as $member_name => $member_value) {
            if (!is_object($member_value) && (($skipMemberArray == null) || !in_array($member_name, $skipMemberArray))) {
                // Skipping all object members
                $destObject->$member_name = $member_value;
            }
        }
    }

    public static function getWPCoreDirs()
    {
        $wp_core_dirs = array(get_home_path().'wp-admin', get_home_path().'wp-includes');

        //if wp_content is overrided
        $wp_path = get_home_path()."wp-content";
        if (get_home_path().'wp-content' != WP_CONTENT_DIR) {
            $wp_path = WP_CONTENT_DIR;
        }
        $wp_path = str_replace("\\", "/", $wp_path);

        $wp_core_dirs[] = $wp_path;
        $wp_core_dirs[] = $wp_path.'/plugins';
        $wp_core_dirs[] = $wp_path.'/themes';

        return $wp_core_dirs;
    }

    /**
     * return absolute path for the files that are core directories
     * @return string array
     */
    public static function getWPCoreFiles()
    {
        $wp_cored_dirs = array(get_home_path().'wp-config.php');
        return $wp_cored_dirs;
    }

    /**
     * Groups an array into arrays by a given key, or set of keys, shared between all array members.
     *
     * Based on {@author Jake Zatecky}'s {@link https://github.com/jakezatecky/array_group_by array_group_by()} function.
     * This variant allows $key to be closures.
     *
     * @param array $array   The array to have grouping performed on.
     * @param mixed $key,... The key to group or split by. Can be a _string_, an _integer_, a _float_, or a _callable_.
     *                       - If the key is a callback, it must return a valid key from the array.
     *                       - If the key is _NULL_, the iterated element is skipped.
     *                       - string|oink callback ( mixed $item )
     *
     * @return array|null Returns a multidimensional array or `null` if `$key` is invalid.
     */
    public static function array_group_by(array $array, $key)
    {
        if (!is_string($key) && !is_int($key) && !is_float($key) && !is_callable($key)) {
            trigger_error('array_group_by(): The key should be a string, an integer, or a callback', E_USER_ERROR);
            return null;
        }
        $func    = (!is_string($key) && is_callable($key) ? $key : null);
        $_key    = $key;
        // Load the new array, splitting by the target key
        $grouped = array();
        foreach ($array as $value) {
            $key = null;
            if (is_callable($func)) {
                $key = call_user_func($func, $value);
            } elseif (is_object($value) && isset($value->{$_key})) {
                $key = $value->{$_key};
            } elseif (isset($value[$_key])) {
                $key = $value[$_key];
            }
            if ($key === null) {
                continue;
            }
            $grouped[$key][] = $value;
        }
        // Recursively build a nested grouping if more parameters are supplied
        // Each grouped array value is grouped according to the next sequential key
        if (func_num_args() > 2) {
            $args = func_get_args();
            foreach ($grouped as $key => $value) {
                $params        = array_merge(array($value), array_slice($args, 2, func_num_args()));
                $grouped[$key] = call_user_func_array('DUP_Util::array_group_by', $params);
            }
        }
        return $grouped;
    }

    /**
     * PHP_SAPI for FCGI requires a data flush of at least 256
     * bytes every 40 seconds or else it forces a script halt
     *
     * @return string A series of 256 space characters
     */
    public static function fcgiFlush()
    {
        echo(str_repeat(' ', 300));
        @flush();
        @ob_flush();
    }

    public static function isWpDebug()
    {
        return defined('WP_DEBUG') && WP_DEBUG;
    }

    /**
     * Returns the last N lines of a file. Equivalent to tail command
     *
     * @param string $filepath The full path to the file to be tailed
     * @param int $lines The number of lines to return with each tail call
     *
     * @return string The last N parts of the file
     */
    public static function tailFile($filepath, $lines = 2)
    {
        // Open file
        $f = @fopen($filepath, "rb");
        if ($f === false)
            return false;

        // Sets buffer size
        $buffer = 256;

        // Jump to last character
        fseek($f, -1, SEEK_END);

        // Read it and adjust line number if necessary
        // (Otherwise the result would be wrong if file doesn't end with a blank line)
        if (fread($f, 1) != "\n")
            $lines -= 1;

        // Start reading
        $output = '';
        $chunk  = '';

        // While we would like more
        while (ftell($f) > 0 && $lines >= 0) {
            // Figure out how far back we should jump
            $seek   = min(ftell($f), $buffer);
            // Do the jump (backwards, relative to where we are)
            fseek($f, -$seek, SEEK_CUR);
            // Read a chunk and prepend it to our output
            $output = ($chunk  = fread($f, $seek)).$output;
            // Jump back to where we started reading
            fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
            // Decrease our line counter
            $lines  -= substr_count($chunk, "\n");
        }

        // While we have too many lines
        // (Because of buffer size we might have read too many)
        while ($lines++ < 0) {
            // Find first newline and remove all text before that
            $output = substr($output, strpos($output, "\n") + 1);
        }
        fclose($f);
        return trim($output);
    }

    /**
     * Display human readable byte sizes
     *
     * @param int $size    The size in bytes
     *
     * @return string The size of bytes readable such as 100KB, 20MB, 1GB etc.
     */
    public static function byteSize($size, $roundBy = 2)
    {
        try {
            $units = array('B', 'KB', 'MB', 'GB', 'TB');
            for ($i = 0; $size >= 1024 && $i < 4; $i++) {
                $size /= 1024;
            }
            return round($size, $roundBy).$units[$i];
        }
        catch (Exception $e) {
            return "n/a";
        }
    }

    /**
     * Makes path safe for any OS
     *      Paths should ALWAYS READ be "/"
     *          uni: /home/path/file.txt
     *          win:  D:/home/path/file.txt
     *
     * @param string $path		The path to make safe
     *
     * @return string A path with all slashes facing "/"
     */
    public static function safePath($path)
    {
        return str_replace("\\", "/", $path);
    }

    /**
     * Get current microtime as a float.  Method is used for simple profiling
     *
     * @see elapsedTime
     *
     * @return  string   A float in the form "msec sec", where sec is the number of seconds since the Unix epoch
     */
    public static function getMicrotime()
    {
        return microtime(true);
    }

    /**
     * Append the value to the string if it doesn't already exist
     *
     * @param string $string The string to append to
     * @param string $value The string to append to the $string
     *
     * @return string Returns the string with the $value appended once
     */
    public static function appendOnce($string, $value)
    {
        return $string.(substr($string, -1) == $value ? '' : $value);
    }

    /**
     * Return a string with the elapsed time
     *
     * @see getMicrotime()
     *
     * @param mixed number $end     The final time in the sequence to measure
     * @param mixed number $start   The start time in the sequence to measure
     *
     * @return  string   The time elapsed from $start to $end
     */
    public static function elapsedTime($end, $start)
    {
        return sprintf("%.2f sec.", abs($end - $start));
    }

    /**
     * List all of the files of a path
     *
     * @param string $path The full path to a system directory
     *
     * @return array of all files in that path
     *
     * Notes:
     * 	- Avoid using glob() as GLOB_BRACE is not an option on some operating systems
     * 	- Pre PHP 5.3 DirectoryIterator will crash on unreadable files
     *  - Scandir will not crash on unreadable items, but will not return results
     */
    public static function listFiles($path = '.')
    {
        try {
            $files = array();
            if ($dh    = opendir($path)) {
                while (($file = readdir($dh)) !== false) {
                    if ($file == '.' || $file == '..')
                        continue;
                    $full_file_path = trailingslashit($path).$file;
                    $files[]        = str_replace("\\", '/', $full_file_path);
                }
                @closedir($dh);
            }
            return $files;
        }
        catch (Exception $exc) {
            $result = array();
            $files  = @scandir($path);
            if (is_array($files)) {
                foreach ($files as $file) {
                    $result[] = str_replace("\\", '/', $path).$file;
                }
            }
            return $result;
        }
    }

    /**
     * List all of the directories of a path
     *
     * @param string $path The full path to a system directory
     *
     * @return array of all dirs in the $path
     */
    public static function listDirs($path = '.')
    {
        $dirs = array();

        foreach (new DirectoryIterator($path) as $file) {
            if ($file->isDir() && !$file->isDot()) {
                $dirs[] = DUP_Util::safePath($file->getPathname());
            }
        }
        return $dirs;
    }

    /**
     * Does the directory have content
     *
     * @param string $path The full path to a system directory
     *
     * @return bool Returns true if directory is empty
     */
    public static function isDirectoryEmpty($path)
    {
        if (!is_readable($path))
            return NULL;
        return (count(scandir($path)) == 2);
    }

    /**
     * Size of the directory recursively in bytes
     *
     * @param string $path The full path to a system directory
     *
     * @return int Returns the size of the directory in bytes
     *
     */
    public static function getDirectorySize($path)
    {
        if (!file_exists($path))
            return 0;
        if (is_file($path))
            return filesize($path);

        $size = 0;
        $list = glob($path."/*");
        if (!empty($list)) {
            foreach ($list as $file)
                $size += self::getDirectorySize($file);
        }
        return $size;
    }

    /**
     * Can shell_exec be called on this server
     *
     * @return bool Returns true if shell_exec can be called on server
     *
     */
    public static function hasShellExec()
    {
        $cmds = array('shell_exec', 'escapeshellarg', 'escapeshellcmd', 'extension_loaded');

        //Function disabled at server level
        if (array_intersect($cmds, array_map('trim', explode(',', @ini_get('disable_functions')))))
            return apply_filters('duplicator_is_shellzip_available', false);

        //Suhosin: http://www.hardened-php.net/suhosin/
        //Will cause PHP to silently fail
        if (extension_loaded('suhosin')) {
            $suhosin_ini = @ini_get("suhosin.executor.func.blacklist");
            if (array_intersect($cmds, array_map('trim', explode(',', $suhosin_ini))))
                return apply_filters('duplicator_is_shellzip_available', false);
        }

        if (! function_exists('shell_exec')) {
			return apply_filters('duplicator_is_shellzip_available', false);
	    }

        // Can we issue a simple echo command?
        if (!@shell_exec('echo duplicator'))
            return apply_filters('duplicator_is_shellzip_available', false);

        return apply_filters('duplicator_is_shellzip_available', true);
    }

    /**
     * Is the server running Windows operating system
     *
     * @return bool Returns true if operating system is Windows
     *
     */
    public static function isWindows()
    {
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            return true;
        }
        return false;
    }

    /**
     * Wrap to prevent malware scanners from reporting false/positive
     * Switched from our old method to avoid WordFence reporting a false positive
     *
     * @param string $string The string to decrypt i.e. base64_decode
     *
     * @return string Returns the string base64 decoded
     */
    public static function installerUnscramble($string)
    {
        return base64_decode($string);
    }

    /**
     * Wrap to prevent malware scanners from reporting false/positive
     * Switched from our old method to avoid WordFence reporting a false positive
     *
     * @param string $string The string to decrypt i.e. base64_encode
     *
     * @return string Returns the string base64 encode
     */
    public static function installerScramble($string)
    {
        return base64_encode($string);
    }
    const SECURE_ISSUE_DIE    = 'die';
    const SECURE_ISSUE_THROW  = 'throw';
    const SECURE_ISSUE_RETURN = 'return';

    /**
     * Does the current user have the capability
     *
     * @param type $permission
     * @param type $exit    //  SECURE_ISSUE_DIE die script with die function
     *                          SECURE_ISSUE_THROW throw an exception if fail
     *                          SECURE_ISSUE_RETURN return false if fail
     *
     * @return boolean      // return false is fail and $exit is SECURE_ISSUE_THROW
     *                      // true if success
     *
     * @throws Exception    // thow exception if $exit is SECURE_ISSUE_THROW
     */
    public static function hasCapability($permission = 'read', $exit = self::SECURE_ISSUE_DIE)
    {
        $capability = apply_filters('wpfront_user_role_editor_duplicator_translate_capability', $permission);

        if (!current_user_can($capability)) {
            $exitMsg = __('You do not have sufficient permissions to access this page.', 'duplicator');
            DUP_LOG::Trace('You do not have sufficient permissions to access this page. PERMISSION: '.$permission);

            switch ($exit) {
                case self::SECURE_ISSUE_THROW:
                    throw new Exception($exitMsg);
                case self::SECURE_ISSUE_RETURN:
                    return false;
                case self::SECURE_ISSUE_DIE:
                default:
                    wp_die($exitMsg);
            }
        }
        return true;
    }

    /**
     *  Gets the name of the owner of the current PHP script
     *
     * @return string The name of the owner of the current PHP script
     */
    public static function getCurrentUser()
    {
        $unreadable = 'Undetectable';
        if (function_exists('get_current_user') && is_callable('get_current_user')) {
            $user = get_current_user();
            return strlen($user) ? $user : $unreadable;
        }
        return $unreadable;
    }

    /**
     * Gets the owner of the PHP process
     *
     * @return string Gets the owner of the PHP process
     */
    public static function getProcessOwner()
    {
        $unreadable = 'Undetectable';
        $user       = '';
        try {
            if (function_exists('exec')) {
                $user = @exec('whoami');
            }

            if (!strlen($user) && function_exists('posix_getpwuid') && function_exists('posix_geteuid')) {
                $user = posix_getpwuid(posix_geteuid());
                $user = $user['name'];
            }

            return strlen($user) ? $user : $unreadable;
        }
        catch (Exception $ex) {
            return $unreadable;
        }
    }

    /**
     * Creates the snapshot directory if it doesn't already exist
     *
     * @return bool
     */
    public static function initSnapshotDirectory()
    {
        $error = false;

        $path_wproot = duplicator_get_abs_path();
        $path_ssdir  = DUP_Settings::getSsdirPath();
        $path_plugin = DUP_Util::safePath(DUPLICATOR_PLUGIN_PATH);

        if (!file_exists($path_ssdir)) {
            $old_root_perm = @fileperms($path_wproot);

            //--------------------------------
            //CHMOD DIRECTORY ACCESS
            //wordpress root directory
            DupLiteSnapLibIOU::chmod($path_wproot, 'u+rwx');

            //snapshot directory
            if (DupLiteSnapLibIOU::dirWriteCheckOrMkdir($path_ssdir, 'u+rwx,go+rx') == false) {
                $error = true;
            }

            // restore original root perms
            DupLiteSnapLibIOU::chmod($path_wproot, $old_root_perm);

            if ($error) {
                return false;
            }
        }

        DupLiteSnapLibIOU::chmod($path_ssdir, 'u+rwx,go+rx');

        DupLiteSnapLibIOU::dirWriteCheckOrMkdir(DUP_Settings::getSsdirTmpPath(), 'u+rwx');

        //plugins dir/files
        DupLiteSnapLibIOU::dirWriteCheckOrMkdir($path_plugin.'files', 'u+rwx');

        //--------------------------------
        //FILE CREATION
        //SSDIR: Create Index File
        $fileName = $path_ssdir.'/index.php';
        if (!file_exists($fileName)) {
            $ssfile = @fopen($fileName, 'w');
            @fwrite($ssfile,
                    '<?php error_reporting(0);  if (stristr(php_sapi_name(), "fcgi")) { $url  =  "http://" . $_SERVER["HTTP_HOST"]; header("Location: {$url}/404.html");} else { header("HTTP/1.1 404 Not Found", true, 404);} exit(); ?>');
            @fclose($ssfile);
        }

        //SSDIR: Create .htaccess
        $storage_htaccess_off = DUP_Settings::Get('storage_htaccess_off');
        $fileName             = $path_ssdir.'/.htaccess';
        if ($storage_htaccess_off) {
            @unlink($fileName);
        } else if (!file_exists($fileName)) {
            $htfile   = @fopen($fileName, 'w');
            $htoutput = "Options -Indexes";
            @fwrite($htfile, $htoutput);
            @fclose($htfile);
        }

        //SSDIR: Robots.txt file
        $fileName = $path_ssdir.'/robots.txt';
        if (!file_exists($fileName)) {
            $robotfile = @fopen($fileName, 'w');
            @fwrite($robotfile,
                    "User-agent: * \n"
                    ."Disallow: /".DUP_Settings::SSDIR_NAME_LEGACY."/\n"
                    ."Disallow: /".DUP_Settings::SSDIR_NAME_NEW."/");
            @fclose($robotfile);
        }

        return true;
    }

    /**
     * Attempts to get the file zip path on a users system
     *
     * @return null
     */
    public static function getZipPath()
    {
        $filepath = null;

        if (self::hasShellExec()) {
            if (shell_exec('hash zip 2>&1') == NULL) {
                $filepath = 'zip';
            } else {
                $possible_paths = array(
                    '/usr/bin/zip',
                    '/opt/local/bin/zip'
                    //'C:/Program\ Files\ (x86)/GnuWin32/bin/zip.exe');
                );

                foreach ($possible_paths as $path) {
                    if (@file_exists($path)) {
                        $filepath = $path;
                        break;
                    }
                }
            }
        }

        return $filepath;
    }

    /**
     * Is the server PHP 5.3 or better
     *
     * @return  bool    Returns true if the server PHP 5.3 or better
     */
    public static function PHP53()
    {
        return version_compare(PHP_VERSION, '5.3.2', '>=');
    }

    /**
     * Returns an array of the WordPress core tables.
     *
     * @return array  Returns all WP core tables
     */
    public static function getWPCoreTables()
    {
        global $wpdb;
        $result = array();
        foreach (self::getWPCoreTablesEnd() as $tend) {
            $result[] = $wpdb->prefix.$tend;
        }
        return $result;
    }

    public static function getWPCoreTablesEnd()
    {
        return array(
            'commentmeta',
            'comments',
            'links',
            'options',
            'postmeta',
            'posts',
            'term_relationships',
            'term_taxonomy',
            'termmeta',
            'terms',
            'usermeta',
            'blogs',
            'blog_versions',
            'blogmeta',
            'users',
            'site',
            'sitemeta',
            'signups',
            'registration_log',
            'blog_versions');
    }

    public static function isWPCoreTable($table)
    {
        global $wpdb;

        if (strpos($table, $wpdb->prefix) !== 0) {
            return false;
        }

        $subTName = substr($table, strlen($wpdb->prefix));
        $coreEnds = self::getWPCoreTablesEnd();

        if (in_array($subTName, $coreEnds)) {
            return true;
        } else if (is_multisite()) {
            $exTable = explode('_', $subTName);
            if (count($exTable) >= 2 && is_numeric($exTable[0])) {
                $tChekc = implode('_', array_slice($exTable, 1));
                if (get_blog_details((int) $exTable[0], false) !== false && in_array($tChekc, $coreEnds)) {
                    return true;
                }
            }
        }

        return false;
    }

    public static function getWPBlogIdTable($table)
    {
        global $wpdb;

        if (!is_multisite() || strpos($table, $wpdb->prefix) !== 0) {
            return 0;
        }

        $subTName = substr($table, strlen($wpdb->prefix));
        $exTable  = explode('_', $subTName);
        if (count($exTable) >= 2 && is_numeric($exTable[0]) && get_blog_details((int) $exTable[0], false) !== false) {
            return (int) $exTable[0];
        } else {
            return 0;
        }
    }

    /**
     * Check given table is exist in real
     * 
     * @param $table string Table name
     * @return booleam
     */
    public static function isTableExists($table)
    {
        // It will clear the $GLOBALS['wpdb']->last_error var
        $GLOBALS['wpdb']->flush();
        $sql = "SELECT 1 FROM `".esc_sql($table)."` LIMIT 1;";
        $ret = $GLOBALS['wpdb']->get_var($sql);
        if (empty($GLOBALS['wpdb']->last_error))
            return true;
        return false;
    }

    /**
     * Finds if its a valid executable or not
     *
     * @param type $exe A non zero length executable path to find if that is executable or not.
     * @param type $expectedValue expected value for the result
     * @return boolean
     */
    public static function isExecutable($cmd)
    {
        if (strlen($cmd) < 1)
            return false;

        if (@is_executable($cmd)) {
            return true;
        }

        $output = shell_exec($cmd);
        if (!is_null($output)) {
            return true;
        }

        $output = shell_exec($cmd.' -?');
        if (!is_null($output)) {
            return true;
        }

        return false;
    }

    /**
     * Display human readable byte sizes
     *
     * @param string $size	The size in bytes
     *
     * @return string Human readable bytes such as 50MB, 1GB
     */
    public static function readableByteSize($size)
    {
        try {
            $units = array('B', 'KB', 'MB', 'GB', 'TB');
            for ($i = 0; $size >= 1024 && $i < 4; $i++)
                $size  /= 1024;
            return round($size, 2).$units[$i];
        }
        catch (Exception $e) {
            return "n/a";
        }
    }

    public static function getTablePrefix()
    {
        global $wpdb;
        $tablePrefix = (is_multisite() && is_plugin_active_for_network('duplicator/duplicator.php')) ? $wpdb->base_prefix : $wpdb->prefix;
        return $tablePrefix;
    }

    /**
     * return ini disable functions array
     *
     * @return array
     */
    public static function getIniDisableFuncs()
    {
        if (is_null(self::$iniDisableFuncs)) {
            $tmpFuncs              = ini_get('disable_functions');
            $tmpFuncs              = explode(',', $tmpFuncs);
            self::$iniDisableFuncs = array();
            foreach ($tmpFuncs as $cFunc) {
                self::$iniDisableFuncs[] = trim($cFunc);
            }
        }

        return self::$iniDisableFuncs;
    }

    /**
     * Check if function exists and isn't in ini disable_functions
     *
     * @param string $function_name
     * @return bool
     */
    public static function isIniFunctionEnalbe($function_name)
    {
        return function_exists($function_name) && !in_array($function_name, self::getIniDisableFuncs());
    }
}PK�{2\���;;utilities/class.u.scancheck.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
/**
 * Recursivly scans a directory and finds all sym-links and unreadable files
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 *
 */

// Exit if accessed directly
if (! defined('DUPLICATOR_VERSION')) exit;

class DUP_ScanCheck
{
    /**
     * The number of files scanned
     */
    public $fileCount = 0;

    /**
     * The number of directories scanned
     */
    public $dirCount  = 0;

    /**
     * The maximum count of files before the recursive function stops
     */
    public $maxFiles = 1000000;

    /**
     * The maximum count of directories before the recursive function stops
     */
    public $maxDirs = 75000;

    /**
     * Recursivly scan the root directory provided
     */
    public $recursion = true;

    /**
     * Stores a list of symbolic link files
     */
    public $symLinks = array();

    /**
     *  Stores a list of files unreadable by PHP
     */
    public $unreadable = array();

	 /**
     *  Stores a list of dirs with utf8 settings
     */
    public $nameTestDirs = array();

	 /**
     *  Stores a list of files with utf8 settings
     */
    public $nameTestFiles = array();

    /**
     *  If the maxFiles or maxDirs limit is reached then true
     */
    protected $limitReached = false;

    /**
     *  Is the server running on Windows
     */
    private $isWindows = false;

    /**
     *  Init this instance of the object
     */
    function __construct()
    {
       $this->isWindows = defined('PHP_WINDOWS_VERSION_BUILD');
    }

    /**
     * Start the scan process
     *
     * @param string $dir A valid directory path where the scan will run
     * @param array $results Used for recursion, do not pass in value with calling
     *
     * @return obj  The scan check object with the results of the scan
     */
    public function run($dir, &$results = array())
    {
        //Stop Recursion if Max search is reached
        if ($this->fileCount > $this->maxFiles || $this->dirCount > $this->maxDirs) {
            $this->limitReached = true;
            return $results;
        }

        $files = @scandir($dir);
        if (is_array($files)) {
            foreach ($files as $key => $value) {
                $path = realpath($dir.DIRECTORY_SEPARATOR.$value);
                if ($path) {
                    //Files
                    if (!is_dir($path)) {
                        if (!is_readable($path)) {
                            $results[]          = $path;
                            $this->unreadable[] = $path;
                        } else if ($this->isLink($path)) {
                            $results[]        = $path;
                            $this->symLinks[] = $path;
                        } else {
							$name = basename($path);
							$invalid_test =  preg_match('/(\/|\*|\?|\>|\<|\:|\\|\|)/', $name)
								|| trim($name) == ''
								|| (strrpos($name, '.') == strlen($name) - 1 && substr($name, -1) == '.')
								|| preg_match('/[^\x20-\x7f]/', $name);

							if ($invalid_test) {
								if (! DUP_Util::$PHP7_plus && DUP_Util::isWindows()) {
									$this->nameTestFiles[] = utf8_decode($path);
								} else {
									$this->nameTestFiles[] = $path;
								}
							}
						}
                        $this->fileCount++;
                    }
                    //Dirs
                    else if ($value != "." && $value != "..") {
                        if (!$this->isLink($path) && $this->recursion) {
                            $this->Run($path, $results);
                        }

                        if (!is_readable($path)) {
                            $results[]          = $path;
                            $this->unreadable[] = $path;
                        } else if ($this->isLink($path)) {
                            $results[]        = $path;
                            $this->symLinks[] = $path;
                        } else {

							$invalid_test = strlen($path) > 244
								|| trim($path) == ''
								|| preg_match('/[^\x20-\x7f]/', $path);

							if ($invalid_test) {
								if (! DUP_Util::$PHP7_plus && DUP_Util::isWindows()) {
									$this->nameTestDirs[] = utf8_decode($path);
								} else {
									$this->nameTestDirs[] = $path;
								}
							}
						}

                        $this->dirCount++;
                    }
                }
            }
        }
        return $this;
    }

    /**
     * Separation logic for supporting how different operating systems work
     *
     * @param string $target A valid file path
     *
     * @return bool  Is the target a sym link
     */
    private function isLink($target)
    {
		//Currently Windows does not support sym-link detection
        if ($this->isWindows) {
           return false;
        } elseif (is_link($target)) {
            return true;
        }
        return false;
    }
}PK�{2\0�k�77class.server.phpnu�[���<?php
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
require_once (DUPLICATOR_PLUGIN_PATH.'classes/utilities/class.u.php');

/**
 * Used to get various pieces of information about the server environment
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package Duplicator
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 *
 */

class DUP_Server
{

	const LockFileName = 'lockfile.txt';

	// Possibly use in the future if we want to prevent double building
	public static function isEngineLocked()
	{
		if (self::setEngineLock(true)) {
			self::setEngineLock(false);
			$locked = false;
		} else {
			$locked = true;
		}
	}

	// Possibly use in the future if we want to prevent double building
	public static function setEngineLock($shouldLock)
	{
		$success		 = false;
		$locking_file	 = @fopen(self::LockFileName, 'c+');
		if ($locking_file != false) {
			if ($shouldLock) {
				$success = @flock($locking_file, LOCK_EX | LOCK_NB);
			} else {
				$success = @flock($locking_file, LOCK_UN);
			}

			@fclose($locking_file);
		}
		return $success;
	}

    public static function mysqlEscapeIsOk()
    {
        $escape_test_string            = chr(0).chr(26)."\r\n'\"\\";
        $escape_expected_result        = "\"\\0\Z\\r\\n\\'\\\"\\\\\"";
        $escape_actual_result          = DUP_DB::escValueToQueryString($escape_test_string);
        $result                        = $escape_expected_result === $escape_actual_result;

        if (!$result) {
            $msg = "mysqli_real_escape_string test results\n".
                "Expected escape result: ".$escape_expected_result."\n".
                "Actual escape result: ".$escape_actual_result;
            DUP_Log::trace($msg);

        }

        return $result;
    }

	/**
	 * Gets the system requirements which must pass to build a package
	 *
	 * @return array   An array of requirements
	 */
	public static function getRequirements()
	{
		$dup_tests = array();

		//PHP SUPPORT
		$safe_ini						 = strtolower(ini_get('safe_mode'));
		$dup_tests['PHP']['SAFE_MODE']	 = $safe_ini != 'on' || $safe_ini != 'yes' || $safe_ini != 'true' || ini_get("safe_mode") != 1 ? 'Pass' : 'Fail';
		self::logRequirementFail($dup_tests['PHP']['SAFE_MODE'], 'SAFE_MODE is on.');

		$dup_tests['PHP']['VERSION'] = DUP_Util::$on_php_529_plus ? 'Pass' : 'Fail';
		$phpversion					 = phpversion();
		self::logRequirementFail($dup_tests['PHP']['VERSION'], 'PHP version('.$phpversion.') is lower than 5.2.9');

		if (DUP_Settings::Get('archive_build_mode') == DUP_Archive_Build_Mode::ZipArchive) {
			$dup_tests['PHP']['ZIP'] = class_exists('ZipArchive') ? 'Pass' : 'Fail';
			self::logRequirementFail($dup_tests['PHP']['ZIP'], 'ZipArchive class doesn\'t exist.');
		}

		$dup_tests['PHP']['FUNC_1'] = function_exists("file_get_contents") ? 'Pass' : 'Fail';
		self::logRequirementFail($dup_tests['PHP']['FUNC_1'], 'file_get_contents function doesn\'t exist.');

		$dup_tests['PHP']['FUNC_2'] = function_exists("file_put_contents") ? 'Pass' : 'Fail';
		self::logRequirementFail($dup_tests['PHP']['FUNC_2'], 'file_put_contents function doesn\'t exist.');

		$dup_tests['PHP']['FUNC_3'] = function_exists("mb_strlen") ? 'Pass' : 'Fail';
		self::logRequirementFail($dup_tests['PHP']['FUNC_3'], 'mb_strlen function doesn\'t exist.');

		$dup_tests['PHP']['ALL'] = !in_array('Fail', $dup_tests['PHP']) ? 'Pass' : 'Fail';

		//REQUIRED PATHS
		$abs_path					 = duplicator_get_abs_path();
		$handle_test				 = @opendir($abs_path);
		$dup_tests['IO']['WPROOT']	 = is_writeable($abs_path) && $handle_test ? 'Pass' : 'Warn';
		@closedir($handle_test);
		self::logRequirementFail($dup_tests['IO']['WPROOT'], $abs_path.' (abs path) can\'t be opened.');

		$dup_tests['IO']['SSDIR'] = is_writeable(DUP_Settings::getSsdirPath()) ? 'Pass' : 'Fail';
		self::logRequirementFail($dup_tests['IO']['SSDIR'], DUP_Settings::getSsdirPath().' (DUPLICATOR_SSDIR_PATH) can\'t be writeable.');

		$dup_tests['IO']['SSTMP'] = is_writeable(DUP_Settings::getSsdirTmpPath()) ? 'Pass' : 'Fail';
		self::logRequirementFail($dup_tests['IO']['SSTMP'], DUP_Settings::getSsdirTmpPath().' (DUPLICATOR_SSDIR_PATH_TMP) can\'t be writeable.');

		$dup_tests['IO']['ALL'] = !in_array('Fail', $dup_tests['IO']) ? 'Pass' : 'Fail';

		//SERVER SUPPORT
		$dup_tests['SRV']['MYSQLi'] = function_exists('mysqli_connect') ? 'Pass' : 'Fail';
		self::logRequirementFail($dup_tests['SRV']['MYSQLi'], 'mysqli_connect function doesn\'t exist.');

        //mysqli_real_escape_string test
        $dup_tests['SRV']['MYSQL_ESC'] = self::mysqlEscapeIsOk() ? 'Pass' : 'Fail';
        self::logRequirementFail($dup_tests['SRV']['MYSQL_ESC'], "The function mysqli_real_escape_string is not escaping strings as expected.");

		$db_version						 = DUP_DB::getVersion();
		$dup_tests['SRV']['MYSQL_VER']	 = version_compare($db_version, '5.0', '>=') ? 'Pass' : 'Fail';
		self::logRequirementFail($dup_tests['SRV']['MYSQL_VER'], 'MySQL version '.$db_version.' is lower than 5.0.');

		$dup_tests['SRV']['ALL'] = !in_array('Fail', $dup_tests['SRV']) ? 'Pass' : 'Fail';

		//RESERVED FILES
		$dup_tests['RES']['INSTALL'] = !(self::hasInstallerFiles()) ? 'Pass' : 'Fail';
		self::logRequirementFail($dup_tests['RES']['INSTALL'], 'Installer file(s) are exist on the server.');
		$dup_tests['Success']		 = $dup_tests['PHP']['ALL'] == 'Pass' && $dup_tests['IO']['ALL'] == 'Pass' && $dup_tests['SRV']['ALL'] == 'Pass' && $dup_tests['RES']['INSTALL'] == 'Pass';

		$dup_tests['Warning'] = $dup_tests['IO']['WPROOT'] == 'Warn';

		return $dup_tests;
	}

	/**
	 * Logs requirement fail status informative message
	 *
	 * @param string $testStatus Either it is Pass or Fail
	 * @param string $errorMessage Error message which should be logged
	 * @return void
	 */
	private static function logRequirementFail($testStatus, $errorMessage)
	{
		if (empty($testStatus)) {
			throw new Exception('Exception: Empty $testStatus [File: '.__FILE__.', Ln: '.__LINE__);
		}

		if (empty($errorMessage)) {
			throw new Exception('Exception: Empty $errorMessage [File: '.__FILE__.', Ln: '.__LINE__);
		}

		$validTestStatuses = array('Pass', 'Fail', 'Warn');

		if (!in_array($testStatus, $validTestStatuses)) {
			throw new Exception('Exception: Invalid $testStatus value: '.$testStatus.' [File: '.__FILE__.', Ln: '.__LINE__);
		}

		if ('Fail' == $testStatus) {
			DUP_LOG::trace($errorMessage);
		}
	}

	/**
	 * Gets the system checks which are not required
	 *
	 * @return array   An array of system checks
	 */
	public static function getChecks()
	{
		$checks = array();

		//PHP/SYSTEM SETTINGS
		//Web Server
		$php_test0 = false;
		foreach ($GLOBALS['DUPLICATOR_SERVER_LIST'] as $value) {
			if (stristr($_SERVER['SERVER_SOFTWARE'], $value)) {
				$php_test0 = true;
				break;
			}
		}
		self::logCheckFalse($php_test0, 'Any out of server software ('.implode(', ', $GLOBALS['DUPLICATOR_SERVER_LIST']).') doesn\'t exist.');

		$php_test1	 = ini_get("open_basedir");
		$php_test1	 = empty($php_test1) ? true : false;
		self::logCheckFalse($php_test1, 'open_basedir is enabled.');

		$max_execution_time	 = ini_get("max_execution_time");
		$php_test2			 = ($max_execution_time > DUPLICATOR_SCAN_TIMEOUT) || (strcmp($max_execution_time, 'Off') == 0 || $max_execution_time == 0) ? true : false;
		if (strcmp($max_execution_time, 'Off') == 0) {
			$max_execution_time_error_message = '$max_execution_time should not be'.$max_execution_time;
		} else {
			$max_execution_time_error_message = '$max_execution_time ('.$max_execution_time.') should not  be lower than the DUPLICATOR_SCAN_TIMEOUT'.DUPLICATOR_SCAN_TIMEOUT;
		}
		self::logCheckFalse($php_test2, $max_execution_time_error_message);

		$php_test3 = function_exists('mysqli_connect');
		self::logCheckFalse($php_test3, 'mysqli_connect function doesn\'t exist.');

		$php_test4 = DUP_Util::$on_php_53_plus ? true : false;
		self::logCheckFalse($php_test4, 'PHP Version is lower than 5.3.');

		$checks['SRV']['PHP']['websrv']		 = $php_test0;
		$checks['SRV']['PHP']['openbase']	 = $php_test1;
		$checks['SRV']['PHP']['maxtime']	 = $php_test2;
		$checks['SRV']['PHP']['mysqli']		 = $php_test3;
		$checks['SRV']['PHP']['version']	 = $php_test4;
        //MANAGED HOST
        $checks['SRV']['SYS']['managedHost'] = !DUP_Custom_Host_Manager::getInstance()->isManaged();
		$checks['SRV']['SYS']['ALL']		 = ($php_test0 && $php_test1 && $php_test2 && $php_test3 && $php_test4 && $checks['SRV']['SYS']['managedHost']) ? 'Good' : 'Warn';

		//WORDPRESS SETTINGS
		global $wp_version;
		$wp_test1 = version_compare($wp_version, DUPLICATOR_SCAN_MIN_WP) >= 0 ? true : false;
		self::logCheckFalse($wp_test1, 'WP version ('.$wp_version.') is lower than the DUPLICATOR_SCAN_MIN_WP ('.DUPLICATOR_SCAN_MIN_WP.').');

		//Core Files
		$files						 = array();
		$proper_wp_config_file_path	 = duplicator_get_abs_path().'/wp-config.php';
		$files['wp-config.php']		 = file_exists($proper_wp_config_file_path);
		self::logCheckFalse($files['wp-config.php'], 'The wp-config.php file doesn\'t exist on the '.$proper_wp_config_file_path);

		/** searching wp-config in working word press is not worthy
		 * if this script is executing that means wp-config.php exists :)
		 * we need to know the core folders and files added by the user at this point
		 * retaining old logic as else for the case if its used some where else
		 */
		//Core dir and files logic
		if (isset($_POST['file_notice']) && isset($_POST['dir_notice'])) {
			//means if there are core directories excluded or core files excluded return false
			if ((bool) $_POST['file_notice'] || (bool) $_POST['dir_notice'])
				$wp_test2	 = false;
			else
				$wp_test2	 = true;
		} else {
			$wp_test2 = $files['wp-config.php'];
		}

		//Cache
		/*
		  $Package = DUP_Package::getActive();
		  $cache_path = DUP_Util::safePath(WP_CONTENT_DIR) . '/cache';
		  $dirEmpty = DUP_Util::isDirectoryEmpty($cache_path);
		  $dirSize = DUP_Util::getDirectorySize($cache_path);
		  $cach_filtered = in_array($cache_path, explode(';', $Package->Archive->FilterDirs));
		  $wp_test3 = ($cach_filtered || $dirEmpty || $dirSize < DUPLICATOR_SCAN_CACHESIZE ) ? true : false;
		 */
		$wp_test3 = is_multisite();
		self::logCheckFalse($wp_test3, 'WP is multi-site setup.');

		$checks['SRV']['WP']['version']	 = $wp_test1;
		$checks['SRV']['WP']['core']	 = $wp_test2;
		$checks['SRV']['WP']['ismu']	 = $wp_test3;
		$checks['SRV']['WP']['ALL']		 = $wp_test1 && $wp_test2 && !$wp_test3 ? 'Good' : 'Warn';

		return $checks;
	}

	/**
	 * Logs checks false informative message
	 *
	 * @param boolean $check Either it is true or false
	 * @param string $errorMessage Error message which should be logged when check is false
	 * @return void
	 */
	private static function logCheckFalse($check, $errorMessage)
	{
		if (empty($errorMessage)) {
			throw new Exception('Exception: Empty $errorMessage variable [File: '.__FILE__.', Ln: '.__LINE__);
		}

		if (filter_var($check, FILTER_VALIDATE_BOOLEAN) === false) {
			DUP_LOG::trace($errorMessage);
		}
	}

	/**
	 * Check to see if duplicator installer files are present
	 *
	 * @return bool   True if any reserved files are found
	 */
	public static function hasInstallerFiles()
	{
		$files = self::getInstallerFiles();
		foreach ($files as $file => $path) {
			if (false !== strpos($path, '*')) {
				$glob_files = glob($path);
				if (!empty($glob_files)) {
					return true;
				}
			} elseif (file_exists($path))
				return true;
		}
		return false;
	}

	/**
	 * Gets a list of all the installer files by name and full path
	 *
	 * @remarks
	 *  FILES:		installer.php, installer-backup.php, dup-installer-bootlog__[HASH].txt
	 * 	DIRS:		dup-installer
	 * 	DEV FILES:	wp-config.orig
	 * 	Last set is for lazy developer cleanup files that a developer may have
	 *  accidently left around lets be proactive for the user just in case.
	 *
	 * @return array [file_name, file_path]
	 */
	public static function getInstallerFiles()
	{
		// alphanumeric 7 time, then -(dash), then 8 digits
		$abs_path				 = duplicator_get_abs_path();
		$four_digit_glob_pattern = '[0-9][0-9][0-9][0-9]';
		$retArr					 = array(
			basename(DUPLICATOR_INSTALLER_DIRECTORY).' '.esc_html__('(directory)', 'duplicator') => DUPLICATOR_INSTALLER_DIRECTORY,
			DUPLICATOR_INSTALL_PHP																 => $abs_path.'/'.DUPLICATOR_INSTALL_PHP,
			'[HASH]'.'_'.DUPLICATOR_INSTALL_PHP													 => $abs_path.'/*_*'.$four_digit_glob_pattern.'_'.DUPLICATOR_INSTALL_PHP,
			DUPLICATOR_INSTALL_BAK																 => $abs_path.'/'.DUPLICATOR_INSTALL_BAK,
			'[HASH]'.'_'.DUPLICATOR_INSTALL_BAK													 => $abs_path.'/*_*'.$four_digit_glob_pattern.'_'.DUPLICATOR_INSTALL_BAK,
			'[HASH]_archive.zip|daf'															 => $abs_path.'/*_*'.$four_digit_glob_pattern.'_archive.[zd][ia][pf]',
			'dup-installer-bootlog__[HASH].txt'													 => $abs_path.'/dup-installer-bootlog__'.DUPLICATOR_INSTALLER_HASH_PATTERN.'.txt',
		);
		if (DUPLICATOR_INSTALL_SITE_OVERWRITE_ON) {
			$retArr['dup-wp-config-arc__[HASH].txt'] = $abs_path.'/dup-wp-config-arc__'.DUPLICATOR_INSTALLER_HASH_PATTERN.'.txt';
		}
		return $retArr;
	}

	/**
	 * Get the IP of a client machine
	 *
	 * @return string   IP of the client machine
	 */
	public static function getClientIP()
	{
		if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
			return $_SERVER["HTTP_X_FORWARDED_FOR"];
		} else if (array_key_exists('REMOTE_ADDR', $_SERVER)) {
			return $_SERVER["REMOTE_ADDR"];
		} else if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) {
			return $_SERVER["HTTP_CLIENT_IP"];
		}
		return '';
	}

	/**
	 * Get PHP memory usage
	 *
	 * @return string   Returns human readable memory usage.
	 */
	public static function getPHPMemory($peak = false)
	{
		if ($peak) {
			$result = 'Unable to read PHP peak memory usage';
			if (function_exists('memory_get_peak_usage')) {
				$result = DUP_Util::byteSize(memory_get_peak_usage(true));
			}
		} else {
			$result = 'Unable to read PHP memory usage';
			if (function_exists('memory_get_usage')) {
				$result = DUP_Util::byteSize(memory_get_usage(true));
			}
		}
		return $result;
	}
}PK{�1\&O����'class-twentynineteen-walker-comment.phpnu&1i�PK{�1\������"Kclass-twentynineteen-svg-icons.phpnu&1i�PK.�1\���b�i�i&�autoptimizeCriticalCSSSettingsAjax.phpnu�[���PK.�1\������jautoptimizePartners.phpnu�[���PK.�1\�y|B�1�1�~autoptimizeImages.phpnu�[���PK.�1\T�H��
�
ΰstatic/toolbar.jsnu�[���PK.�1\�אh���static/toolbar.min.cssnu�[���PK.�1\Oy�i_
_
��static/toolbar.min.jsnu�[���PK.�1\4�''"��static/exit-survey/exit-survey.cssnu�[���PK.�1\��FF!�static/exit-survey/exit-survey.jsnu�[���PK.�1\*�pƵ���static/toolbar.cssnu�[���PK.�1\Z�j
  �static/loading.gifnu�[���PK.�1\?�������$autoptimizeConfig.phpnu�[���PK.�1\.���X�X,�autoptimizeUtils.phpnu�[���PK.�1\�E��NWautoptimizeToolbar.phpnu�[���PK.�1\��mJ�J�/sautoptimizeMain.phpnu�[���PK.�1\Q��"���
autoptimizeCriticalCSSCron.phpnu�[���PK.�1\�M��
��index.htmlnu�[���PK.�1\���nxx��autoptimizeCriticalCSSCore.phpnu�[���PK.�1\ccCۋ>�>!6=autoptimizeCriticalCSSEnqueue.phpnu�[���PK.�1\DE/����|autoptimizeStyles.phpnu�[���PK.�1\�ь��[�[VPautoptimizeBase.phpnu�[���PK.�1\� �P��'�autoptimizeCacheChecker.phpnu�[���PK.�1\:t��88�autoptimizeSpeedupper.phpnu�[���PK.�1\}S�##��autoptimizeOptionWrapper.phpnu�[���PK.�1\D�����autoptimizeCompatibility.phpnu�[���PK.�1\������autoptimizeCLI.phpnu�[���PK.�1\S�5��
autoptimizeHTML.phpnu�[���PK.�1\�u�:�:�autoptimizeCriticalCSSBase.phpnu�[���PK.�1\7̀��~�~)XautoptimizeScripts.phpnu�[���PK.�1\�̡OO)�autoptimizeCSSmin.phpnu�[���PK.�1\_Ss�^0^0$��autoptimizeVersionUpdatesHandler.phpnu�[���PK.�1\�pYHHo	external/js/jquery.cookie.jsnu�[���PK.�1\B.xG��	external/js/index.htmlnu�[���PK.�1\�Uۅ(( 	external/js/jquery.cookie.min.jsnu�[���PK.�1\�G��TT!	external/js/unslider.min.jsnu�[���PK.�1\�l�

9	external/js/unslider-dots.cssnu�[���PK.�1\�m~XU&U&u<	external/js/lazysizes.min.jsnu�[���PK.�1\N/>>c	external/js/unslider.cssnu�[���PK.�1\���"q%q%#�e	external/do_not_donate_smallest.pngnu�[���PK.�1\B.xG��`�	external/index.htmlnu�[���PK.�1\B.xG��.a�	external/php/yui-php-cssmin-bundled/index.htmlnu�[���PK.�1\����-}�	external/php/yui-php-cssmin-bundled/Utils.phpnu�[���PK.�1\�ڸH
�
�0Ν	external/php/yui-php-cssmin-bundled/Minifier.phpnu�[���PK.�1\P���}}.8 
external/php/yui-php-cssmin-bundled/Colors.phpnu�[���PK.�1\z7�U�"�"6
external/php/ao-minify-html.phpnu�[���PK.�1\B.xG���X
external/php/index.htmlnu�[���PK.�1\oPU��@�@�Y
external/php/jsmin.phpnu�[���PK.�1\
R!��P�
external/php/persist-admin-notices-dismissal/persist-admin-notices-dismissal.phpnu�[���PK.�1\ZW��6O�
external/php/persist-admin-notices-dismissal/README.mdnu�[���PK.�1\}�dd>��
external/php/persist-admin-notices-dismissal/dismiss-notice.jsnu�[���PK.�1\��I��?�?$|�
critcss-inc/admin_settings_rules.phpnu�[���PK.�1\5�h�m
m
$�critcss-inc/admin_settings_debug.phpnu�[���PK.�1\�R�"^critcss-inc/admin_settings_key.phpnu�[���PK.�1\eQ0h��&�critcss-inc/admin_settings_explain.phpnu�[���PK.�1\�Y_�/�/'(critcss-inc/admin_settings_queue.js.phpnu�[���PK.�1\�%�F__$Xcritcss-inc/admin_settings_queue.phpnu�[���PK.�1\SQ�W0-0-"�ucritcss-inc/admin_settings_adv.phpnu�[���PK.�1\|#.+L+L'7�critcss-inc/admin_settings_rules.js.phpnu�[���PK.�1\Di�X����critcss-inc/js/md5.min.jsnu�[���PK.�1\ ��� ��critcss-inc/js/admin_settings.jsnu�[���PK.�1\m��B�@�@(�critcss-inc/js/jquery.tablesorter.min.jsnu�[���PK.�1\����II(�Acritcss-inc/admin_settings_impexp.js.phpnu�[���PK.�1\�{Z��� wJcritcss-inc/css/admin_styles.cssnu�[���PK.�1\^8��@@%GZcritcss-inc/css/ao-tablesorter/bg.gifnu�[���PK.�1\�ލ�66&�Zcritcss-inc/css/ao-tablesorter/asc.gifnu�[���PK.�1\W8766'h[critcss-inc/css/ao-tablesorter/desc.gifnu�[���PK.�1\��2k(�[critcss-inc/css/ao-tablesorter/style.cssnu�[���PK.�1\���;;X`autoptimizeMetabox.phpnu�[���PK.�1\X��oo��autoptimizeProTab.phpnu�[���PK.�1\rJ#�\�\"n�autoptimizeCriticalCSSSettings.phpnu�[���PK.�1\Ki����t
autoptimizeExtra.phpnu�[���PK.�1\�v�vU�
autoptimizeCache.phpnu�[���PK.�1\@9�����
autoptimizeExitSurvey.phpnu�[���PKg2\�E_^^ �*class-twentytwenty-svg-icons.phpnu&1i�PKg2\�@�Ĝ�"x6class-twentytwenty-walker-page.phpnu&1i�PKg2\�+ɿ�%fKclass-twentytwenty-walker-comment.phpnu&1i�PKg2\r�y99 z^class-twentytwenty-customize.phpnu&1i�PKg2\��!!$˗class-twentytwenty-script-loader.phpnu&1i�PKg2\J���$$(@�class-twentytwenty-separator-control.phpnu&1i�PKg2\��Bii*��class-twentytwenty-non-latin-languages.phpnu&1i�PK�{2\k�fN�package/index.phpnu�[���PK�{2\�S���p�pλpackage/class.pack.archive.phpnu�[���PK�{2\�}���y�y�,package/class.pack.database.phpnu�[���PK�{2\t]��R�R ��package/class.pack.installer.phpnu�[���PK�{2\i#~~�
�
A�package/duparchive/class.pack.archive.duparchive.state.create.phpnu�[���PK�{2\ps-{o@o@4)package/duparchive/class.pack.archive.duparchive.phpnu�[���PK�{2\~Dg�TTA�Epackage/duparchive/class.pack.archive.duparchive.state.expand.phpnu�[���PK�{2\k�fN�[package/duparchive/index.phpnu�[���PK�{2\�����,�,"\package/class.pack.archive.zip.phpnu�[���PK�{2\�O�1	1	&�package/class.pack.archive.filters.phpnu�[���PK�{2\Q������package/class.pack.phpnu�[���PK�{2\v8 
 
��class.io.phpnu�[���PK�{2\���!!�class.archive.config.phpnu�[���PK�{2\__����x�class.constants.phpnu�[���PK�{2\k�fN	Q�index.phpnu�[���PK�{2\/��]ee��host/class.liquidweb.host.phpnu�[���PK�{2\iՏ�J�host/class.wpengine.host.phpnu�[���PK�{2\�kX]]%�host/interface.host.phpnu�[���PK�{2\�rr ɿhost/class.wordpresscom.host.phpnu�[���PK�{2\;��Iff��host/class.flywheel.host.phpnu�[���PK�{2\��!�"=�host/class.custom.host.manager.phpnu�[���PK�{2\�v�__��host/class.pantheon.host.phpnu�[���PK�{2\+93���T�host/class.godaddy.host.phpnu�[���PK�{2\����!)!)i�class.settings.phpnu�[���PK�{2\o@i�	�	�class.plugin.upgrade.phpnu�[���PK�{2\0N��A
A
�ui/class.ui.messages.phpnu�[���PK�{2\k�fN?ui/index.phpnu�[���PK�{2\�#
���ui/class.ui.dialog.phpnu�[���PK�{2\D���xx�9ui/class.ui.screen.base.phpnu�[���PK�{2\��v<v<�Jui/class.ui.notice.phpnu�[���PK�{2\xXtp/	/	G�ui/class.ui.viewstate.phpnu�[���PK�{2\�'d�* * ��class.db.phpnu�[���PK�{2\r9����%�class.password.phpnu�[���PK�{2\���M;M;W�class.logging.phpnu�[���PK�{2\n7��**�utilities/class.u.shell.phpnu�[���PK�{2\Q����Zutilities/class.u.zip.phpnu�[���PK�{2\di�		I#utilities/class.u.string.phpnu�[���PK�{2\�t�����/utilities/class.u.multisite.phpnu�[���PK�{2\��g�x3utilities/class.u.validator.phpnu�[���PK�{2\�U����Jutilities/class.u.json.phpnu�[���PK�{2\r�
ZZ^utilities/class.u.migration.phpnu�[���PK�{2\���.g.g�qutilities/class.u.phpnu�[���PK�{2\���;;�utilities/class.u.scancheck.phpnu�[���PK�{2\0�k�77��class.server.phpnu�[���PK}}�-�$

Youez - 2016 - github.com/yon3zu
LinuXploit