﻿PK      \ -r&  &    core/limited_offers.phpnu W+A        <?php
/**
 * Limited offers utility class.
 *
 * Author:          Soare Robert Danial <robert.soare@themeisle.com>
 * Created on:      17/10/2023
 *
 * @package Neve\Core
 */

namespace Neve\Core;

use DateTime;
use DateTimeZone;
use Exception;

/**
 * Class LimitedOffers
 */
class Limited_Offers {

	/**
	 * Active deal.
	 *
	 * @var string
	 */
	private $active = '';

	/**
	 * The key for WP Options to disable the dashboard notification.
	 *
	 * @var string
	 */
	public $wp_option_dismiss_notification_key_base = 'dismiss_themeisle_notice_event_';

	/**
	 * Offer Links
	 *
	 * @var array<string>
	 */
	public $offer_metadata = array();

	/**
	 * Timeline for the offers.
	 *
	 * @var array[]
	 */
	public $timelines = array(
		'bf' => array(
			'start' => '2023-11-20 00:00:00',
			'end'   => '2023-11-27 23:59:00',
		),
	);

	/**
	 * LimitedOffers constructor.
	 */
	public function __construct() {
		try {
			if ( $this->is_deal_active( 'bf' ) ) {
				$this->activate_bff();
			}
		} catch ( Exception $e ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( $e->getMessage() ); // phpcs:ignore
			}
		}
	}

	/**
	 * Load hooks for the dashboard.
	 *
	 * @return void
	 */
	public function load_dashboard_hooks() {
		add_filter( 'themeisle_products_deal_priority', array( $this, 'add_priority' ) );
		add_action( 'admin_notices', array( $this, 'render_notice' ) );
		add_action( 'wp_ajax_dismiss_themeisle_event_notice_neve', array( $this, 'disable_notification_ajax' ) );
	}

	/**
	 * Check if we have an active deal.
	 *
	 * @return bool True if the deal is active.
	 */
	public function is_active() {
		return ! empty( $this->active );
	}

	/**
	 * Activate the Black Friday deal.
	 *
	 * @return void
	 */
	public function activate_bff() {
		$this->active = 'bf';

		$this->offer_metadata = array(
			'bannerUrl'           => get_template_directory_uri() . '/assets/img/dashboard/black-friday-banner.png',
			'bannerAlt'           => 'Neve Black Friday Sale',
			'customizerBannerUrl' => get_template_directory_uri() . '/assets/img/dashboard/black-friday-customizer-banner.png',
			'customizerBannerAlt' => 'Neve Black Friday Sale',
			'linkDashboard'       => tsdk_utmify( 'https://themeisle.com/themes/neve/blackfriday/', 'blackfridayltd23', 'dashboard' ),
			'linkGlobal'          => tsdk_utmify( 'https://themeisle.com/themes/neve/blackfriday/', 'blackfridayltd23', 'globalnotice' ),
			'linkCustomizer'      => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade', 'blackfriday23', 'customizer' ),
		);
	}

	/**
	 * Get the slug of the active deal.
	 *
	 * @return string Active deal.
	 */
	public function get_active_deal() {
		return $this->active;
	}

	/**
	 * Check if the deal is active with the given slug.
	 *
	 * @param string $slug Slug of the deal.
	 *
	 * @throws Exception When date is invalid.
	 */
	public function is_deal_active( $slug ) {

		if ( empty( $slug ) || ! array_key_exists( $slug, $this->timelines ) ) {
			return false;
		}

		return $this->check_date_range( $this->timelines[ $slug ]['start'], $this->timelines[ $slug ]['end'] );
	}

	/**
	 * Get the remaining time for the deal in a human readable format.
	 *
	 * @param string $slug Slug of the deal.
	 * @return string Remaining time for the deal.
	 */
	public function get_remaining_time_for_deal( $slug ) {
		if ( empty( $slug ) || ! array_key_exists( $slug, $this->timelines ) ) {
			return '';
		}

		try {
			$end_date     = new DateTime( $this->timelines[ $slug ]['end'], new DateTimeZone( 'GMT' ) );
			$current_date = new DateTime( 'now', new DateTimeZone( 'GMT' ) );
			$diff         = $end_date->diff( $current_date );

			if ( $diff->days > 0 ) {
				return $diff->days === 1 ? $diff->format( '%a day' ) : $diff->format( '%a days' );
			}

			if ( $diff->h > 0 ) {
				return $diff->h === 1 ? $diff->format( '%h hour' ) : $diff->format( '%h hours' );
			}

			if ( $diff->i > 0 ) {
				return $diff->i === 1 ? $diff->format( '%i minute' ) : $diff->format( '%i minutes' );
			}

			return $diff->s === 1 ? $diff->format( '%s second' ) : $diff->format( '%s seconds' );
		} catch ( Exception $e ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( $e->getMessage() ); // phpcs:ignore
			}
		}

		return '';
	}

	/**
	 * Check if the current date is in the range of the offer.
	 *
	 * @param string $start Start date.
	 * @param string $end   End date.
	 *
	 * @throws Exception When date is invalid.
	 */
	public function check_date_range( $start, $end ) {

		$start_date   = new DateTime( $start, new DateTimeZone( 'GMT' ) );
		$end_date     = new DateTime( $end, new DateTimeZone( 'GMT' ) );
		$current_date = new DateTime( 'now', new DateTimeZone( 'GMT' ) );

		return $start_date <= $current_date && $current_date <= $end_date;
	}

	/**
	 * Get the localized data for the plugin.
	 *
	 * @return array Localized data.
	 */
	public function get_localized_data() {
		return array_merge(
			array(
				'active'        => $this->is_active(),
				'dealSlug'      => $this->get_active_deal(),
				'remainingTime' => $this->get_remaining_time_for_deal( $this->get_active_deal() ),
				'urgencyText'   => 'Hurry Up! Only ' . $this->get_remaining_time_for_deal( $this->get_active_deal() ) . ' left',
			),
			$this->offer_metadata
		);
	}

	/**
	 * Disable the notification via ajax.
	 *
	 * @return void
	 */
	public function disable_notification_ajax() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['nonce'] ), 'dismiss_themeisle_event_notice_neve' ) ) {
			wp_die( 'Invalid nonce! Refresh the page and try again.' );
		}

		// We record the time and the plugin of the dismissed notification.
		update_option( $this->wp_option_dismiss_notification_key_base . $this->active, 'neve_' . $this->active . '_' . current_time( 'Y_m_d' ) );
		wp_die( 'success' );
	}

	/**
	 * Render the dashboard banner.
	 *
	 * @return void
	 */
	public function render_notice() {

		if ( ! $this->has_priority() ) {
			return;
		}

		$message = 'Neve <strong>Black Friday Sale</strong> - Save big with a <strong>Lifetime License</strong> of Neve Agency Plan. <strong>Only 100 licenses</strong>, for a limited time!';

		?>
		<style>
			.themeisle-sale {
				padding: 10px 15px;

				display: flex;
				align-items: center;

				opacity: 1;
				transition: opacity 0.8s ease-out;
			}
			.themeisle-sale svg {
				margin-right: 15px;
				min-width: 24px;
			}
			.themeisle-sale a {
			}
			.themeisle-sale-error {
				color: red;
			}
			.themeisle-sale.hidden {
				opacity: 0;
			}
			.themeisle-sdk-notice:is([id*="review"]) { /* Do not show the review notice when the sale is active. */
				display: none;
			}
		</style>
		<div class="themeisle-sale notice notice-info is-dismissible">
			<div class="notice-dismiss"></div>
			<svg width="24" height="24" viewBox="0 0 61 60" fill="none" xmlns="http://www.w3.org/2000/svg">
				<path fill-rule="evenodd" clip-rule="evenodd" d="M0.5 0.198486H61V59.9128H0.5V0.198486ZM25.0305 30.0698V44.2415H17.7858V15.4382C17.7858 15.2989 17.8281 15.2013 17.9129 15.1456C17.9976 15.0899 18.1529 15.1595 18.3789 15.3546L36.4696 30.0698V15.8145H43.7143V44.6596C43.7143 44.8268 43.672 44.9313 43.5872 44.9731C43.5025 45.0149 43.3472 44.9383 43.1212 44.7432L25.0305 30.0698ZM43.7143 48.9127H17.7858V51.2699H43.7143V48.9127Z" fill="#0073AA"/>
			</svg>

			<span>
				<?php echo wp_kses_post( $message ); ?>
				<a href="<?php echo esc_url( ! empty( $this->offer_metadata['linkGlobal'] ) ? $this->offer_metadata['linkGlobal'] : '' ); ?>" target="_blank" rel="external noreferrer noopener">
					Learn more
				</a>
			</span>
			<span class="themeisle-sale-error"></span>
		</div>
		<script type="text/javascript">
			window.document.addEventListener( 'DOMContentLoaded', () => {
				const button = document.querySelector( '.themeisle-sale.notice .notice-dismiss' );
				button?.addEventListener( 'click', e => {
					e.preventDefault();
					fetch('<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>', {
						method: 'POST',
						headers: {
							'Content-Type': 'application/x-www-form-urlencoded'
						},
						body: new URLSearchParams({
							action: 'dismiss_themeisle_event_notice_neve',
							nonce: '<?php echo esc_attr( wp_create_nonce( 'dismiss_themeisle_event_notice_neve' ) ); ?>'
						})
					})
						.then(response => response.text())
						.then(response => {
							if ( ! response?.includes( 'success' ) ) {
								document.querySelector( '.themeisle-sale-error' ).innerHTML = response;
								return;
							}

							document.querySelectorAll( '.themeisle-sale.notice' ).forEach( el => {
								el.classList.add( 'hidden' );
								setTimeout( () => {
									el.remove();
								}, 800 );
							});
						})
						.catch(error => {
							console.error( 'Error:', error );
							document.querySelector( '.themeisle-sale-error' ).innerHTML = error;
						});
				});
			});
		</script>
		<?php
	}

	/**
	 * Check if we can show the dashboard banner. Since it is shared between themes, the user need only to dismiss it once.
	 *
	 * @return bool
	 */
	public function can_show_dashboard_banner() {
		return ! get_option( $this->wp_option_dismiss_notification_key_base . $this->active, false );
	}

	/**
	 * Add product priority to the filter.
	 *
	 * @param array $products Registered products.
	 * @return array Array enhanced with Neve priority.
	 */
	public function add_priority( $products ) {
		$products['neve'] = 0;
		return $products;
	}

	/**
	 * Check if the current product has priority.
	 * Use this for conditional rendering if you want to show the banner only for one product.
	 *
	 * @return bool True if the current product has priority.
	 */
	public function has_priority() {
		$products = apply_filters( 'themeisle_products_deal_priority', [] );

		if ( empty( $products ) ) {
			return true;
		}

		$highest_priority = array_search( min( $products ), $products );
		return 'neve' === $highest_priority;
	}
}
PK      \!>AW<L  <L    core/front_end.phpnu W+A        <?php
/**
 * Front end functionality
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      17/08/2018
 *
 * @package Neve\Core
 */

namespace Neve\Core;

use Neve\Compatibility\Elementor;
use Neve\Compatibility\Starter_Content;
use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Core\Dynamic_Css;
use Neve\Core\Traits\Theme_Mods;

/**
 * Front end handler class.
 *
 * @package Neve\Core
 */
class Front_End {
	use Theme_Mods;

	/**
	 * Theme setup.
	 */
	public function setup_theme() {

		// Maximum allowed width for any content in the theme, like oEmbeds and images added to posts.  https://codex.wordpress.org/Content_Width
		global $content_width;
		if ( ! isset( $content_width ) ) {
			$content_width = apply_filters( 'neve_content_width', 1200 );
		}

		load_theme_textdomain( 'neve', get_template_directory() . '/languages' );

		$logo_settings = array(
			'flex-width'  => true,
			'flex-height' => true,
			'height'      => 50,
			'width'       => 200,
		);

		add_theme_support( 'align-wide' );
		add_theme_support( 'automatic-feed-links' );
		add_theme_support( 'border' );
		add_theme_support( 'custom-background', [] );
		add_theme_support( 'custom-logo', $logo_settings );
		add_theme_support( 'custom-spacing' );
		add_theme_support( 'custom-units' );
		add_theme_support( 'customize-selective-refresh-widgets' );
		add_theme_support( 'editor-color-palette', $this->get_gutenberg_color_palette() );
		add_theme_support( 'fl-theme-builder-footers' );
		add_theme_support( 'fl-theme-builder-headers' );
		add_theme_support( 'fl-theme-builder-parts' );
		add_theme_support( 'header-footer-elementor' );
		add_theme_support( 'html5', array( 'search-form' ) );
		add_theme_support( 'lifterlms-sidebars' );
		add_theme_support( 'lifterlms' );
		add_theme_support( 'link-color' );
		add_theme_support( 'post-thumbnails' );
		add_theme_support( 'service_worker', true );
		add_theme_support( 'starter-content', ( new Starter_Content() )->get() );
		add_theme_support( 'title-tag' );
		add_filter( 'script_loader_tag', array( $this, 'filter_script_loader_tag' ), 10, 2 );
		add_filter( 'embed_oembed_html', array( $this, 'wrap_oembeds' ), 10, 3 );
		add_filter( 'video_embed_html', array( $this, 'wrap_jetpack_oembeds' ), 10, 1 );

		$this->add_amp_support();
		$nav_menus_to_register = apply_filters(
			'neve_register_nav_menus',
			array(
				'primary' => esc_html__( 'Primary Menu', 'neve' ),
				'footer'  => esc_html__( 'Footer Menu', 'neve' ),
				'top-bar' => esc_html__( 'Secondary Menu', 'neve' ),
			)
		);
		register_nav_menus( $nav_menus_to_register );

		add_image_size( 'neve-blog', 930, 620, true );
		add_filter( 'wp_nav_menu_args', array( $this, 'nav_walker' ), 1001 );
		add_filter( 'theme_mod_background_color', '__return_empty_string' );
		$this->add_woo_support();
		add_filter( 'neve_dynamic_style_output', array( $this, 'css_global_custom_colors' ), PHP_INT_MAX, 2 );
	}

	/**
	 * Gutenberg Block Color Palettes.
	 *
	 * Get the color palette in Gutenberg from Customizer colors.
	 */
	private function get_gutenberg_color_palette() {
		$prefix                  = ( apply_filters( 'ti_wl_theme_is_localized', false ) ? __( 'Theme', 'neve' ) : 'Neve' ) . ' - ';
		$gutenberg_color_palette = array();
		$from_global_colors      = [
			'neve-link-color'       => array(
				'val'   => 'var(--nv-primary-accent)',
				'label' => $prefix . __( 'Primary Accent', 'neve' ),
			),
			'neve-link-hover-color' => array(
				'val'   => 'var(--nv-secondary-accent)',
				'label' => $prefix . __( 'Secondary Accent', 'neve' ),
			),
			'nv-site-bg'            => array(
				'val'   => 'var(--nv-site-bg)',
				'label' => $prefix . __( 'Site Background', 'neve' ),
			),
			'nv-light-bg'           => array(
				'val'   => 'var(--nv-light-bg)',
				'label' => $prefix . __( 'Light Background', 'neve' ),
			),
			'nv-dark-bg'            => array(
				'val'   => 'var(--nv-dark-bg)',
				'label' => $prefix . __( 'Dark Background', 'neve' ),
			),
			'neve-text-color'       => array(
				'val'   => 'var(--nv-text-color)',
				'label' => $prefix . __( 'Text Color', 'neve' ),
			),
			'nv-text-dark-bg'       => array(
				'val'   => 'var(--nv-text-dark-bg)',
				'label' => $prefix . __( 'Text Dark Background', 'neve' ),
			),
			'nv-c-1'                => array(
				'val'   => 'var(--nv-c-1)',
				'label' => $prefix . __( 'Extra Color 1', 'neve' ),
			),
			'nv-c-2'                => array(
				'val'   => 'var(--nv-c-2)',
				'label' => $prefix . __( 'Extra Color 2', 'neve' ),
			),
		];

		// Add custom global colors
		$from_global_colors = array_merge( $from_global_colors, $this->get_global_custom_color_vars() );

		foreach ( $from_global_colors as $slug => $args ) {
			array_push(
				$gutenberg_color_palette,
				array(
					'name'  => esc_html( $args['label'] ),
					'slug'  => esc_html( $slug ),
					'color' => neve_sanitize_colors( $args['val'] ),
				)
			);
		}

		return array_values( $gutenberg_color_palette );
	}

	/**
	 * Returns global custom colors with css vars
	 *
	 * @return array[]
	 */
	private function get_global_custom_color_vars() {
		$css_vars = [];
		foreach ( Mods::get( Config::MODS_GLOBAL_CUSTOM_COLORS, [] ) as $slug => $args ) {
			$css_vars[ $slug ] = [
				'label' => $args['label'],
				'val'   => sprintf( 'var(--%s)', $slug ),
			];
		}

		return $css_vars;
	}

	/**
	 * Add AMP support
	 */
	private function add_amp_support() {
		if ( ! defined( 'AMP__VERSION' ) ) {
			return;
		}
		if ( version_compare( AMP__VERSION, '1.0.0', '<' ) ) {
			return;
		}
		add_theme_support(
			'amp',
			apply_filters(
				'neve_filter_amp_support',
				array(
					'paired' => true,
				)
			)
		);
	}

	/**
	 * Add WooCommerce support
	 */
	private function add_woo_support() {
		if ( ! class_exists( 'WooCommerce', false ) ) {
			return;
		}

		$woocommerce_settings = apply_filters(
			'neves_woocommerce_args',
			array(
				'product_grid' => array(
					'default_columns' => 3,
					'default_rows'    => 4,
					'min_columns'     => 1,
					'max_columns'     => 6,
					'min_rows'        => 1,
				),
			)
		);

		add_theme_support( 'woocommerce', $woocommerce_settings );
		add_theme_support( 'wc-product-gallery-zoom' );
		add_theme_support( 'wc-product-gallery-lightbox' );
		add_theme_support( 'wc-product-gallery-slider' );

	}

	/**
	 * 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.
	 *
	 * @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;
	}

	/**
	 * Wrap embeds.
	 *
	 * @param string $markup embed markup.
	 * @param string $url embed url.
	 * @param array  $attr embed attributes [width/height].
	 *
	 * @return string
	 */
	public function wrap_oembeds( $markup, $url, $attr ) {
		$sources = [
			'youtube.com',
			'youtu.be',
			'cloudup.com',
			'dailymotion.com',
			'ted.com',
			'vimeo.com',
			'speakerdeck.com',
		];
		foreach ( $sources as $source ) {
			if ( strpos( $url, $source ) !== false ) {
				return '<div class="nv-iframe-embed">' . $markup . '</div>';
			}
		}

		return $markup;
	}

	/**
	 * Wrap Jetpack embeds.
	 * Fixes the compose module aspect ratio issue.
	 *
	 * @param string $markup embed markup.
	 *
	 * @return string
	 */
	public function wrap_jetpack_oembeds( $markup ) {
		return '<div class="nv-iframe-embed">' . $markup . '</div>';
	}

	/**
	 * Tweak menu walker to support selective refresh.
	 *
	 * @param array $args List of arguments for navigation.
	 *
	 * @return mixed
	 */
	public function nav_walker( $args ) {
		if ( isset( $args['walker'] ) && is_string( $args['walker'] ) && class_exists( $args['walker'] ) ) {
			$args['walker'] = new $args['walker']();
		}

		return $args;
	}

	/**
	 * Enqueue scripts and styles.
	 */
	public function enqueue_scripts() {
		$this->add_styles();
		$this->add_inline_styles();
		$this->add_scripts();
	}

	/**
	 * Enqueue inline styles for core components.
	 */
	private function add_inline_styles() {

		// Add Inline styles if buttons shadows are being used.
		$primary_values   = get_theme_mod( Config::MODS_BUTTON_PRIMARY_STYLE, neve_get_button_appearance_default() );
		$secondary_values = get_theme_mod( Config::MODS_BUTTON_SECONDARY_STYLE, neve_get_button_appearance_default( 'secondary' ) );

		$style = '';

		if (
			( isset( $primary_values['useShadow'] ) && ! empty( $primary_values['useShadow'] ) ) ||
			( isset( $primary_values['useShadowHover'] ) && ! empty( $primary_values['useShadowHover'] ) ) ||
			( isset( $secondary_values['useShadow'] ) && ! empty( $secondary_values['useShadow'] ) ) ||
			( isset( $secondary_values['useShadowHover'] ) && ! empty( $secondary_values['useShadowHover'] ) )
		) {
			$style .= '.button.button-primary, .is-style-primary .wp-block-button__link {box-shadow: var(--primarybtnshadow, none);} .button.button-primary:hover, .is-style-primary .wp-block-button__link:hover {box-shadow: var(--primarybtnhovershadow, none);} .button.button-secondary, .is-style-secondary .wp-block-button__link {box-shadow: var(--secondarybtnshadow, none);} .button.button-secondary:hover, .is-style-secondary .wp-block-button__link:hover {box-shadow: var(--secondarybtnhovershadow, none);}';
		}

		foreach ( neve_get_headings_selectors() as $heading_id => $heading_selector ) {
			$font_family = get_theme_mod( $this->get_mod_key_heading_fontfamily( $heading_id ), '' ); // default value is empty string to be consistent with default customizer control value.

			$css_var = sprintf( '--%1$sfontfamily', $heading_id );

			if ( is_customize_preview() ) {
				$style .= sprintf( '%s {font-family: var(%s, var(--headingsfontfamily)), var(--nv-fallback-ff);} ', $heading_id, $css_var ); // fallback values for the first page load on the customizer
				continue;
			}

			// If font family is inherit, do not add a style for this heading.
			if ( $font_family === '' ) {
				continue;
			}

			$style .= sprintf( '%s {font-family: var(%s);}', $heading_id, $css_var );
		}

		if ( empty( $style ) ) {
			return;
		}

		wp_add_inline_style( 'neve-style', Dynamic_Css::minify_css( $style ) );
	}

	/**
	 * Enqueue styles.
	 */
	private function add_styles() {
		if ( class_exists( 'WooCommerce', false ) ) {
			$style_path = 'css/woocommerce';

			wp_register_style( 'neve-woocommerce', NEVE_ASSETS_URL . $style_path . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.css', array(), apply_filters( 'neve_version_filter', NEVE_VERSION ) );
			wp_style_add_data( 'neve-woocommerce', 'rtl', 'replace' );
			wp_style_add_data( 'neve-woocommerce', 'suffix', '.min' );
			if ( ! Elementor::is_elementor_checkout() ) {
				wp_enqueue_style( 'neve-woocommerce' );
			}
		}

		if ( class_exists( 'Easy_Digital_Downloads' ) ) {

			$style_path = 'css/easy-digital-downloads';

			wp_register_style( 'neve-easy-digital-downloads', NEVE_ASSETS_URL . $style_path . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.css', array(), apply_filters( 'neve_version_filter', NEVE_VERSION ) );
			wp_style_add_data( 'neve-easy-digital-downloads', 'rtl', 'replace' );
			wp_style_add_data( 'neve-easy-digital-downloads', 'suffix', '.min' );
			wp_enqueue_style( 'neve-easy-digital-downloads' );

		}

		$style_path = '/style-main-new';

		wp_register_style( 'neve-style', get_template_directory_uri() . $style_path . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.css', array(), apply_filters( 'neve_version_filter', NEVE_VERSION ) );
		wp_style_add_data( 'neve-style', 'rtl', 'replace' );
		wp_style_add_data( 'neve-style', 'suffix', '.min' );
		wp_enqueue_style( 'neve-style' );

		$mm_path = 'mega-menu';

		wp_register_style( 'neve-mega-menu', get_template_directory_uri() . '/assets/css/' . $mm_path . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.css', array(), apply_filters( 'neve_version_filter', NEVE_VERSION ) );
		wp_style_add_data( 'neve-mega-menu', 'rtl', 'replace' );
		wp_style_add_data( 'neve-mega-menu', 'suffix', '.min' );
	}

	/**
	 * Enqueue scripts.
	 */
	private function add_scripts() {
		if ( neve_is_amp() ) {
			return;
		}

		wp_register_script( 'neve-script', NEVE_ASSETS_URL . 'js/build/modern/frontend.js', apply_filters( 'neve_filter_main_script_dependencies', array() ), NEVE_VERSION, true );

		wp_localize_script(
			'neve-script',
			'NeveProperties',
			apply_filters(
				'neve_filter_main_script_localization',
				array(
					'ajaxurl'     => esc_url( admin_url( 'admin-ajax.php' ) ),
					'nonce'       => wp_create_nonce( 'wp_rest' ),
					'isRTL'       => is_rtl(),
					'isCustomize' => is_customize_preview(),
				)
			)
		);
		wp_enqueue_script( 'neve-script' );
		wp_script_add_data( 'neve-script', 'async', true );
		$inline_scripts = apply_filters( 'hfg_component_scripts', '' );
		if ( ! empty( $inline_scripts ) ) {
			wp_add_inline_script( 'neve-script', $inline_scripts );
		}

		if ( class_exists( 'WooCommerce', false ) && is_woocommerce() ) {
			wp_register_script( 'neve-shop-script', NEVE_ASSETS_URL . 'js/build/modern/shop.js', array(), NEVE_VERSION, true );
			wp_enqueue_script( 'neve-shop-script' );
			wp_script_add_data( 'neve-shop-script', 'async', true );
		}

		if ( $this->should_load_comments_reply() ) {
			wp_enqueue_script( 'comment-reply' );
		}
	}

	/**
	 * Dequeue comments-reply script if comments are closed.
	 *
	 * @return bool
	 */
	public function should_load_comments_reply() {

		if ( ! is_singular() ) {
			return false;
		}

		if ( ! comments_open() ) {
			return false;
		}

		if ( ! (bool) get_option( 'thread_comments' ) ) {
			return false;
		}

		if ( post_password_required() ) {
			return false;
		}

		$post_type = get_post_type();
		if ( ! post_type_supports( $post_type, 'comments' ) ) {
			return false;
		}

		if ( ! apply_filters( 'neve_post_has_comments', false ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Register widgets for the theme.
	 *
	 * @since    1.0.0
	 */
	public function register_sidebars() {
		$sidebars = array(
			'blog-sidebar' => esc_html__( 'Sidebar', 'neve' ),
			'shop-sidebar' => esc_html__( 'Shop Sidebar', 'neve' ),
		);

		$footer_sidebars = apply_filters(
			'neve_footer_widget_areas_array',
			array(
				'footer-one-widgets'   => esc_html__( 'Footer One', 'neve' ),
				'footer-two-widgets'   => esc_html__( 'Footer Two', 'neve' ),
				'footer-three-widgets' => esc_html__( 'Footer Three', 'neve' ),
				'footer-four-widgets'  => esc_html__( 'Footer Four', 'neve' ),
			)
		);

		$sidebars = array_merge( $sidebars, $footer_sidebars );

		foreach ( $sidebars as $sidebar_id => $sidebar_name ) {
			$sidebar_settings = array(
				'name'          => $sidebar_name,
				'id'            => $sidebar_id,
				'before_widget' => '<div id="%1$s" class="widget %2$s">',
				'after_widget'  => '</div>',
				'before_title'  => '<p class="widget-title">',
				'after_title'   => '</p>',
			);
			register_sidebar( $sidebar_settings );
		}
	}

	/**
	 * Get strings.
	 *
	 * @return array
	 */
	public function get_strings() {
		return [
			'add_item'                 => __( 'Add item', 'neve' ),
			'add_items'                => __( 'Add items by clicking the ones below.', 'neve' ),
			'all_selected'             => __( 'All items are already selected.', 'neve' ),
			'page_layout'              => __( 'Page Layout', 'neve' ),
			'page_title'               => __( 'Page Title', 'neve' ),
			'upsell_components'        => __( 'Upgrade to Neve Pro and unlock all components, including Wish List, Breadcrumbs, Custom Layouts and many more.', 'neve' ),
			'header_booster'           => esc_html__( 'Header Booster', 'neve' ),
			'blog_booster'             => esc_html__( 'Blog Booster', 'neve' ),
			'woo_booster'              => esc_html__( 'WooCommerce Booster', 'neve' ),
			'custom_layouts'           => esc_html__( 'Custom Layouts', 'neve' ),
			'white_label'              => esc_html__( 'White Label module', 'neve' ),
			'scroll_to_top'            => esc_html__( 'Scroll to Top module', 'neve' ),
			'elementor_booster'        => esc_html__( 'Elementor Booster', 'neve' ),
			'ext_h_description'        => esc_html__( 'Extend your header with more components and settings, build sticky/transparent headers or display them conditionally.', 'neve' ),
			'ctm_h_description'        => esc_html__( 'Easily create custom headers and footers as well as adding your own custom code or content in any of the hooks locations.', 'neve' ),
			'elem_description'         => esc_html__( 'Leverage the true flexibility of Elementor with powerful addons and templates that you can import with just one click.', 'neve' ),
			'get_pro_cta'              => esc_html__( 'Get the PRO version!', 'neve' ),
			'opens_new_tab_des'        => esc_html__( '(opens in a new tab)', 'neve' ),
			'filter'                   => __( 'Filter', 'neve' ),
			/* translators: %s - Theme name */
			'neve_options'             => __( '%s Options', 'neve' ),
			'migrate_builder_d'        => __( 'Migrating builder data', 'neve' ),
			'rollback_builder'         => __( 'Rolling back builder', 'neve' ),
			'remove_old_data'          => __( 'Removing old data', 'neve' ),
			'customizer_values_notice' => __( 'You must save the current customizer values before running the migration.', 'neve' ),
			'wrong_reload_notice'      => __( 'Something went wrong. Please reload the page and try again.', 'neve' ),
			'rollback_to_old'          => __( 'Want to roll back to the old builder?', 'neve' ),
			'new_hfg_experience'       => __( "We've created a new Header/Footer Builder experience! You can always roll back to the old builder from right here.", 'neve' ),
			'manual_adjust'            => __( 'Some manual adjustments may be required.', 'neve' ),
			'reload'                   => __( 'Reload', 'neve' ),
			'migrate'                  => __( 'Migrate Builders Data', 'neve' ),
			'legacy_skin'              => __( 'Legacy Skin', 'neve' ),
			'neve_30'                  => __( 'Neve 3.0', 'neve' ),
			'switching_skin'           => __( 'Switching skin', 'neve' ),
			'switch_skin'              => __( 'Switch Skin', 'neve' ),
			'dismiss'                  => __( 'Dismiss', 'neve' ),
			'rollback'                 => __( 'Roll Back', 'neve' ),
		];
	}

	/**
	 * Adds CSS rules to resolve .has-dynamicslug-color .has-dynamicslug-background-color classes.
	 *
	 * @param  string $current_styles Current dynamic style.
	 * @param  string $context gutenberg|frontend Represents the type of the context.
	 * @return string dynamic styles has resolving global custom colors
	 */
	public function css_global_custom_colors( $current_styles, $context ) {
		if ( $context !== 'frontend' ) {
			return $current_styles;
		}

		foreach ( Mods::get( Config::MODS_GLOBAL_CUSTOM_COLORS, [] ) as $slug => $args ) {
			$css_var         = sprintf( 'var(--%s) !important', $slug );
			$current_styles .= Dynamic_CSS::minify_css( sprintf( '.has-%s-color {color:%s} .has-%s-background-color {background-color:%s}', $slug, $css_var, $slug, $css_var ) );
		}

		return $current_styles;
	}
}
PK      \)|	  |	    core/styles/gutenberg.phpnu W+A        <?php
/**
 * Style generator based on settings.
 *
 * @package Neve\Core\Styles
 */

namespace Neve\Core\Styles;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;

/**
 * Class Generator for Gutenberg editor.
 *
 * @package Neve\Core\Styles
 */
class Gutenberg extends Generator {
	use Css_Vars;

	/**
	 * Generator constructor.
	 */
	public function __construct() {
		$this->context = Dynamic_Selector::CONTEXT_GUTENBERG;
		$this->setup_buttons();
		$this->setup_typography();
		$this->add_editor_color_palette_styles();
	}

	/**
	 * Setup typography subscribers.
	 */
	public function setup_typography() {

		$rules                = $this->get_typography_rules();
		$this->_subscribers[] = [
			Dynamic_Selector::KEY_SELECTOR => ':root',
			Dynamic_Selector::KEY_RULES    => $rules,
			Dynamic_Selector::KEY_CONTEXT  => [
				Dynamic_Selector::CONTEXT_GUTENBERG => true,
			],
		];
	}

	/**
	 * Setup button subscribers.
	 */
	public function setup_buttons() {

		$rules                = $this->get_button_rules();
		$this->_subscribers[] = [
			Dynamic_Selector::KEY_SELECTOR => ':root',
			Dynamic_Selector::KEY_RULES    => $rules,
			Dynamic_Selector::KEY_CONTEXT  => [
				Dynamic_Selector::CONTEXT_GUTENBERG => true,
			],
		];
	}

	/**
	 * Adds colors from the editor-color-palette theme support.
	 */
	private function add_editor_color_palette_styles() {
		$is_new_user           = get_option( 'neve_new_user' );
		$imported_starter_site = get_option( 'neve_imported_demo' );
		if ( $is_new_user === 'yes' && $imported_starter_site !== 'yes' ) {
			return;
		}

		$this->_subscribers['.has-neve-button-color-color']            = [
			Config::CSS_PROP_COLOR => [
				Dynamic_Selector::META_KEY       => Config::MODS_BUTTON_PRIMARY_STYLE . '.background',
				Dynamic_Selector::META_IMPORTANT => true,
				Dynamic_Selector::META_DEFAULT   => 'var(--nv-primary-accent)',
				Dynamic_Selector::KEY_CONTEXT    => [
					Dynamic_Selector::CONTEXT_GUTENBERG => true,
				],
			],
		];
		$this->_subscribers['.has-neve-button-color-background-color'] = [
			Config::CSS_PROP_BACKGROUND_COLOR => [
				Dynamic_Selector::META_KEY       => Config::MODS_BUTTON_PRIMARY_STYLE . '.background',
				Dynamic_Selector::META_IMPORTANT => true,
				Dynamic_Selector::META_DEFAULT   => 'var(--nv-primary-accent)',
				Dynamic_Selector::KEY_CONTEXT    => [
					Dynamic_Selector::CONTEXT_GUTENBERG => true,
				],
			],
		];
	}
}
PK      \$B    3  core/styles/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \Sb6  b6    core/styles/css_prop.phpnu W+A        <?php


namespace Neve\Core\Styles;


use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Views\Font_Manager;

class Css_Prop {
	/**
	 * Helper method to build the value based on 100 diff.
	 */
	public static function minus_100( $css_prop, $value, $meta, $device ) {
		return sprintf( "%s: %s%s;",
			( $css_prop ),
			( 100 - $value ),
			isset( $meta[ Dynamic_Selector::META_SUFFIX ] ) ? $meta[ Dynamic_Selector::META_SUFFIX ] : 'px'
		);
	}

	/**
	 * Get suffix from controls that store data in the following format:
	 * { desktop: value, tablet: value, mobile: value, deskotp-unit: px, tablet-unit: px, mobile-unit: px }
	 *
	 * @param array $meta Subscribers meta data.
	 */
	public static function get_unit_responsive( $meta, $device ) {
		$all_value = Mods::get( $meta['key'], isset( $meta[ Dynamic_Selector::META_DEFAULT ] ) ? $meta[ Dynamic_Selector::META_DEFAULT ] : null );
		$suffix    = 'px';
		if ( isset( $all_value[ $device . '-unit' ] ) ) {
			$suffix = $all_value[ $device . '-unit' ];
		} elseif ( isset( $all_value['unit'] ) ) {
			$suffix = $all_value['unit'];
		}

		return $suffix;
	}

	/**
	 * Get suffix from controls that store data in the following format:
	 * { desktop: value, tablet: value, mobile: value, suffix : { deskop: px, tablet: px, mobile: px} }
	 *
	 * @param array $meta Subscribers meta data.
	 */
	public static function get_suffix_responsive( $meta, $device ) {
		$default_value = isset( $meta[ Dynamic_Selector::META_DEFAULT ] ) ? $meta[ Dynamic_Selector::META_DEFAULT ] : null;
		$all_value     = isset( $meta[ Dynamic_Selector::META_AS_JSON ] ) ? Mods::to_json( $meta['key'], $default_value ) : Mods::get( $meta['key'], $default_value );

		// The neve_responsive_range_control component double JSON stringified, therefore try to parse it again.
		if( ! is_array( $all_value ) ) {
			$maybe_parse_json = json_decode( $all_value , true);

			if( is_array( $maybe_parse_json ) ) {
				$all_value = $maybe_parse_json;
			}
		}

		return isset( $all_value['suffix'][ $device ] ) ? $all_value['suffix'][ $device ] : ( isset( $all_value['suffix'] ) && is_string( $all_value['suffix'] ) ? $all_value['suffix'] : 'px' );;
	}

	/**
	 * Transform rule meta into CSS rule string.
	 *
	 * @param string $css_prop CSS Prop.
	 * @param string|array $value Dynamic value.
	 * @param array $meta Rule meta.
	 * @param string $device Current device.
	 *
	 * @return string
	 */
	public static function transform( $css_prop, $value, $meta, $device ) {
		//If we have a custom filter, let's call it.
		if ( isset( $meta['filter'] ) ) {
			if ( is_callable( $meta['filter'] ) ) {
				return call_user_func_array( $meta['filter'], [ $css_prop, $value, $meta, $device ] );
			}
			if ( method_exists( __CLASS__, $meta['filter'] ) ) {
				return call_user_func_array( [ __CLASS__, $meta['filter'] ], [ $css_prop, $value, $meta, $device ] );
			}

			return '';
		}

		if ( isset( $meta['override'] ) ) {
			return sprintf( '%s:%s;', $css_prop, $meta['override'] );
		}

		switch ( $css_prop ) {
			case Config::CSS_PROP_BACKGROUND_COLOR:
			case Config::CSS_PROP_COLOR:
			case Config::CSS_PROP_FILL_COLOR:
			case Config::CSS_PROP_BORDER_COLOR:
				$mode   = ( false === strpos( $value, 'rgba' ) ) ? 'hex' : 'rgba';
				$is_var = ( strpos( $value, 'var' ) !== false );

				if ( $mode === 'hex' && ! $is_var ) {
					$value = strpos( $value, "#" ) === 0 ? $value : '#' . $value;
				}

				return sprintf( "%s: %s%s;", ( $css_prop ), neve_sanitize_colors( $value ), isset( $meta['important'] ) && $meta['important'] ? '!important' : '' );
			case Config::CSS_PROP_MAX_WIDTH:
			case Config::CSS_PROP_WIDTH:
			case Config::CSS_PROP_FLEX_BASIS:
			case Config::CSS_PROP_MARGIN_LEFT:
			case Config::CSS_PROP_MARGIN_RIGHT:
			case Config::CSS_PROP_MARGIN_TOP:
			case Config::CSS_PROP_MARGIN_BOTTOM:
			case Config::CSS_PROP_PADDING_LEFT:
			case Config::CSS_PROP_PADDING_RIGHT:
			case Config::CSS_PROP_HEIGHT:
			case Config::CSS_PROP_MIN_HEIGHT:
			case Config::CSS_PROP_LEFT:
			case Config::CSS_PROP_RIGHT:
				$suffix = isset( $meta[ Dynamic_Selector::META_SUFFIX ] ) ? $meta[ Dynamic_Selector::META_SUFFIX ] : 'px';

				if ( $suffix === 'responsive_suffix' ) {
					$suffix = self::get_suffix_responsive( $meta, $device );
				}

				return sprintf( "%s: %s%s;",
					( $css_prop ),
					( $value ),
					$suffix
				);
			case Config::CSS_PROP_BORDER_RADIUS:
			case Config::CSS_PROP_BORDER_WIDTH:
			case Config::CSS_PROP_PADDING:
			case Config::CSS_PROP_MARGIN:
				$suffix = isset( $meta[ Dynamic_Selector::META_SUFFIX ] ) ? $meta[ Dynamic_Selector::META_SUFFIX ] : 'px';
				if ( ! is_array( $value ) ) {
					return sprintf( "%s:%s%s;",
						$css_prop,
						absint( $value ), $suffix );
				}

				if ( ! isset( $meta['is_responsive'] ) || $meta['is_responsive'] === false ) {
					$suffix = isset( $value['unit'] ) ? $value['unit'] : 'px';
				}

				if ( $suffix === 'responsive_unit' ) {
					$suffix = self::get_unit_responsive( $meta, $device );
				}

				$non_empty_values = array_filter( $value, 'strlen' ); // @phpstan-ignore-line
				if ( count( $non_empty_values ) === 4 ) {
					return sprintf( "%s:%s%s %s%s %s%s %s%s;",
						$css_prop,
						(int) $value['top'],
						$suffix,
						(int) $value['right'],
						$suffix,
						(int) $value['bottom'],
						$suffix,
						(int) $value['left'],
						$suffix
					);
				}
				$rule     = '';
				$patterns = [
					Config::CSS_PROP_MARGIN        => 'margin-%s',
					Config::CSS_PROP_PADDING       => 'padding-%s',
					Config::CSS_PROP_BORDER_WIDTH  => 'border-%s-width',
					Config::CSS_PROP_BORDER_RADIUS => [
						'top'    => 'border-top-left-radius',
						'right'  => 'border-top-right-radius',
						'bottom' => 'border-bottom-right-radius',
						'left'   => 'border-bottom-left-radius',
					],
				];

				if ( isset( $non_empty_values['unit'] ) ) {
					unset ( $non_empty_values['unit'] );
				}

				foreach ( $non_empty_values as $position => $position_value ) {
					$rule .= sprintf( "%s:%s%s;",
						sprintf( ( is_array( $patterns[ $css_prop ] ) ? $patterns[ $css_prop ][ $position ] : $patterns[ $css_prop ] ), $position ),
						(int) $position_value,
						$suffix
					);
				}

				return $rule;
			//Line height uses an awkward format saved, and we can't define it as responsive because we would need to use the suffix part.
			case Config::CSS_PROP_LINE_HEIGHT:
			case Config::CSS_PROP_FONT_SIZE:

				$suffix = isset( $meta[ Dynamic_Selector::META_SUFFIX ] ) ? $meta[ Dynamic_Selector::META_SUFFIX ] : 'em';
				// We consider the provided suffix as default, in case that we have a responsive setting with responsive suffix.
				if ( isset( $meta[ Dynamic_Selector::META_IS_RESPONSIVE ] ) && $meta[ Dynamic_Selector::META_IS_RESPONSIVE ] ) {
					$all_value = Mods::get( $meta['key'] );
					$suffix    = isset( $all_value['suffix'][ $device ] ) ? $all_value['suffix'][ $device ] : ( isset( $all_value['suffix'] ) ? $all_value['suffix'] : $suffix );
				}

				return sprintf( ' %s: %s%s;', $css_prop, $value, $suffix );
			//Letter spacing has a legacy value of non-responsive which we need to take into consideration.
			case Config::CSS_PROP_LETTER_SPACING:
				return sprintf( ' %s: %spx;', $css_prop, $value );
			case Config::CSS_PROP_CUSTOM_BTN_TYPE:
				if ( $value !== 'outline' ) {
					return 'border:none;';
				}

				return "border:1px solid;";
			case Config::CSS_PROP_FONT_WEIGHT:
				if ( isset( $meta['font'] ) ) {
					$font = strpos( $meta['font'], 'mods_' ) === 0 ? Mods::get( str_replace( 'mods_', '', $meta['font'] ) ) : $meta['font'];
					Font_Manager::add_google_font( $font, strval( $value ) );
				}

				return sprintf( ' %s: %s;', $css_prop, intval( $value ) );
			case Config::CSS_PROP_FONT_FAMILY:
				if ( $value === 'default' ) {
					return '';
				}
				Font_Manager::add_google_font( $value );

				return sprintf( ' %s: %s, var(--nv-fallback-ff);', $css_prop, $value );
			case Config::CSS_PROP_TEXT_TRANSFORM:
			case Config::CSS_PROP_BOX_SHADOW:
			case Config::CSS_PROP_MIX_BLEND_MODE:
			case Config::CSS_PROP_OPACITY:
			case Config::CSS_PROP_GRID_TEMPLATE_COLS:
				return sprintf( ' %s: %s;', $css_prop, $value );
			default:
				$is_font_family_var = strpos( strtolower( $css_prop ), 'fontfamily' ) > - 1;

				if ( $is_font_family_var ) {
					Font_Manager::add_google_font( $value );

					$value = self::format_font_family_value( $value );
				}

				if ( isset( $meta['directional-prop'] ) ) {
					return self::transform_directional_prop( $meta, $device, $value, $css_prop, $meta['directional-prop'] );
				}

				$suffix = self::get_suffix( $meta, $device, $value, $css_prop );

				return sprintf( ' %s: %s%s;', $css_prop, $value, $suffix );
		}
	}

	/**
	 * Get suffix for generic settings.
	 *
	 * @param array $meta Meta array.
	 * @param string $device Current device.
	 * @param mixed $value Value.
	 *
	 * @return string
	 *
	 * @since 3.0.0
	 */
	public static function get_suffix( $meta, $device, $value, $css_prop ) {
		$suffix = isset( $meta[ Dynamic_Selector::META_SUFFIX ] ) ? $meta[ Dynamic_Selector::META_SUFFIX ] : '';

		// If not responsive, most controls use 'unit' key inside value.
		if ( ! isset( $meta['is_responsive'] ) || $meta['is_responsive'] === false ) {
			$suffix = isset( $value['unit'] ) ? $value['unit'] : $suffix;
		}

		// If responsive, try to find the suffix.
		if ( isset( $meta[ Dynamic_Selector::META_IS_RESPONSIVE ] ) && $meta[ Dynamic_Selector::META_IS_RESPONSIVE ] ) {
			$all_value = Mods::get( $meta['key'] );
			$suffix    = isset( $all_value['suffix'][ $device ] ) ? $all_value['suffix'][ $device ] : ( isset( $all_value['suffix'] ) ? $all_value['suffix'] : $suffix );
		}

		if ( $suffix === 'responsive_unit' ) {
			$suffix = self::get_unit_responsive( $meta, $device );
		}

		if ( $suffix === 'responsive_suffix' ) {
			$suffix = self::get_suffix_responsive( $meta, $device );
		}

		// Enqueue any google fonts we might be missing.
		if ( isset ( $meta['font'] ) ) {
			$font = strpos( $meta['font'], 'mods_' ) === 0 ? Mods::get( str_replace( 'mods_', '', $meta['font'] ) ) : $meta['font'];
			Font_Manager::add_google_font( $font, strval( $value ) );
		}

		return $suffix === '—' ? '' : $suffix;
	}

	/**
	 * Transforms the directional properties.
	 *
	 * @param array $meta Meta array.
	 * @param string $device Current device.
	 * @param array|int $value Value.
	 * @param string $css_prop Css Property.
	 * @param string $type Type of directional property.
	 *
	 * @return string
	 */
	public static function transform_directional_prop( $meta, $device, $value, $css_prop, $type ) {
		$suffix   = self::get_suffix( $meta, $device, $value, $css_prop );
		$suffix   = $suffix ? $suffix : 'px';
		$template = '';

		// Make sure that this is directional, even if an int value is provided.
		if ( is_int( $value ) ) {
			$directions = Config::$directional_keys;
			$value      = array_fill_keys( $directions, $value );
		}

		// If we still don't have an array. Make sure to drop this setting.
		if ( ! is_array( $value ) ) {
			return '';
		}

		// Directional array without any other keys than the actual directions.
		$filtered = array_filter( $value, function ( $key ) {
			return in_array( $key, Config::$directional_keys, true );
		}, ARRAY_FILTER_USE_KEY );

		$number_of_directions = $type === Config::CSS_PROP_DIRECTIONAL_ONE_AXIS ? 4 : count( array_unique( $filtered ) );

		if ( $number_of_directions === 1 ) {

			if ( neve_value_is_zero( $value['top'] ) ) {
				$suffix = '';
			}

			if ( empty( $value['top'] ) && ! neve_value_is_zero( $value['top'] ) ) {
				return '';
			}

			$template .= $value['top'] . $suffix;

			return $css_prop . ':' . $template . ';';
		}

		if ( $number_of_directions === 2 && $value['top'] === $value['bottom'] && $value['right'] === $value['left'] ) {

			if ( isset( $value['is_outline_button_padding'] ) ) {
				return $css_prop . ':' . $value['top'] . ' ' . $value['right'] . ';';
			}

			if ( neve_value_is_zero( $value['top'] ) && neve_value_is_zero( $value['right'] ) ) {
				return '';
			}

			$top_suffix   = neve_value_is_zero( $value['top'] ) ? '' : $suffix;
			$right_suffix = neve_value_is_zero( $value['right'] ) ? '' : $suffix;

			$template .= $value['top'] . $top_suffix . ' ' . $value['right'] . $right_suffix;

			return $css_prop . ':' . $template . ';';
		}

		foreach ( Config::$directional_keys as $direction ) {
			if ( isset( $value['is_outline_button_padding'] ) ) {
				$template .= $value[$direction] . ' ';
				continue;
			}

			if ( ! isset( $value[ $direction ] ) || neve_value_is_zero( $value[ $direction ] ) ) {
				$template .= '0 ';
				continue;
			}

			$template .= $value[ $direction ] . $suffix . ' ';
		}

		if ( empty( $template ) ) {
			return '';
		}

		$template = trim( $template ) . ';';

		return $css_prop . ':' . $template . ';';
	}

	/**
	 * Format the font family value.
	 *
	 * @param string $value the font family value.
	 */
	public static function format_font_family_value( $value ) {
		// At some point we were setting the DB values with quotes and removed that.
		// Make sure we drop the slashes and quotes.
		$value = str_replace( [ '"', '\\' ], '', $value );

		if ( strpos( $value, ',' ) !== false ) {
			$value = explode( ',', $value );

			$value = array_map( 'Neve\Core\Styles\CSS_Prop::quote_font_family', $value );

			return join( ',', $value );
		}

		return self::quote_font_family( $value );
	}

	/**
	 * Strip side spaces wrap font family in quotes.
	 *
	 * @param string $family the font family.
	 *
	 * @return string
	 */
	private static function quote_font_family( $family ) {
		// Make sure we don't have whitespace.
		$family = trim( $family );
		// Remove quotes. Because of previously faulty fix.
		$family = trim( $family, '"' );
		if ( strpos( $family, ' ' ) === false ) {
			return $family;
		}

		return '"' . $family . '"';
	}
}
PK      \S~C3  C3    core/styles/css_vars.phpnu W+A        <?php
/**
 * CSS Variables trait
 */

namespace Neve\Core\Styles;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Core\Traits\Theme_Mods;

/**
 * Trait Css_Vars
 *
 * @since 3.0.0
 */
trait Css_Vars {
	use Theme_Mods;

	/**
	 * Get container rules.
	 *
	 * @return array[]
	 */
	public function get_container_rules() {
		return [
			'--container' => [
				Dynamic_Selector::META_KEY           => Config::MODS_CONTAINER_WIDTH,
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_SUFFIX        => 'px',
				Dynamic_Selector::META_DEFAULT       => '{ "mobile": 748, "tablet": 992, "desktop": 1170 }',
			],
		];
	}

	/**
	 * Get button rules.
	 *
	 * @return array
	 */
	public function get_button_rules() {
		$mod_key_primary   = Config::MODS_BUTTON_PRIMARY_STYLE;
		$default_primary   = neve_get_button_appearance_default();
		$mod_key_secondary = Config::MODS_BUTTON_SECONDARY_STYLE;
		$default_secondary = neve_get_button_appearance_default( 'secondary' );

		$rules = [
			'--primarybtnbg'             => [
				Dynamic_Selector::META_KEY => $mod_key_primary . '.background',
			],
			'--secondarybtnbg'           => [
				Dynamic_Selector::META_KEY => $mod_key_secondary . '.background',
			],
			'--primarybtnhoverbg'        => [
				Dynamic_Selector::META_KEY => $mod_key_primary . '.backgroundHover',
			],
			'--secondarybtnhoverbg'      => [
				Dynamic_Selector::META_KEY => $mod_key_secondary . '.backgroundHover',
			],
			'--primarybtncolor'          => [
				Dynamic_Selector::META_KEY => $mod_key_primary . '.text',
			],
			'--secondarybtncolor'        => [
				Dynamic_Selector::META_KEY => $mod_key_secondary . '.text',
			],
			'--primarybtnhovercolor'     => [
				Dynamic_Selector::META_KEY => $mod_key_primary . '.textHover',
			],
			'--secondarybtnhovercolor'   => [
				Dynamic_Selector::META_KEY => $mod_key_secondary . '.textHover',
			],
			'--primarybtnborderradius'   => [
				Dynamic_Selector::META_KEY    => $mod_key_primary . '.borderRadius',
				Dynamic_Selector::META_SUFFIX => 'px',
				'directional-prop'            => Config::CSS_PROP_BORDER_RADIUS,
			],
			'--secondarybtnborderradius' => [
				Dynamic_Selector::META_KEY    => $mod_key_secondary . '.borderRadius',
				Dynamic_Selector::META_SUFFIX => 'px',
				'directional-prop'            => Config::CSS_PROP_BORDER_RADIUS,
			],
		];


		$primary_values   = get_theme_mod( $mod_key_primary, $default_primary );
		$secondary_values = get_theme_mod( $mod_key_secondary, $default_secondary );

		// Button Shadow Primary
		if ( isset( $primary_values['useShadow'] ) && ! empty( $primary_values['useShadow'] ) ) {
			$rules['--primarybtnshadow'] = [
				Dynamic_Selector::META_KEY    => $mod_key_primary . '.shadowColor',
				Dynamic_Selector::META_DEFAULT       => 'none',
				Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) use ($primary_values) {
					$blur   = intval($primary_values['shadowProperties']['blur']);
					$width  = intval($primary_values['shadowProperties']['width']);
					$height = intval($primary_values['shadowProperties']['height']);

					return sprintf( '%s:%s;', $css_prop, sprintf('%spx %spx %spx %s;', $width, $height, $blur, $value ) );
				}
			];
		}

		// Button Shadow Primary Hover
		if ( isset( $primary_values['useShadowHover'] ) && ! empty( $primary_values['useShadowHover'] ) ) {
			$rules['--primarybtnhovershadow'] = [
				Dynamic_Selector::META_KEY    => $mod_key_primary . '.shadowColorHover',
				Dynamic_Selector::META_DEFAULT       => 'none',
				Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) use ($primary_values) {
					$blur   = intval($primary_values['shadowPropertiesHover']['blur']);
					$width  = intval($primary_values['shadowPropertiesHover']['width']);
					$height = intval($primary_values['shadowPropertiesHover']['height']);

					return sprintf( '%s:%s;', $css_prop, sprintf('%spx %spx %spx %s;', $width, $height, $blur, $value ) );
				}
			];
		}

		// Button Shadow Secondary
		if ( isset( $secondary_values['useShadow'] ) && ! empty( $secondary_values['useShadow'] ) ) {
			$rules['--secondarybtnshadow'] = [
				Dynamic_Selector::META_KEY    => $mod_key_secondary . '.shadowColor',
				Dynamic_Selector::META_DEFAULT       => 'none',
				Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) use ($secondary_values) {
					$blur   = intval($secondary_values['shadowProperties']['blur']);
					$width  = intval($secondary_values['shadowProperties']['width']);
					$height = intval($secondary_values['shadowProperties']['height']);

					return sprintf( '%s:%s;', $css_prop, sprintf('%spx %spx %spx %s;', $width, $height, $blur, $value ) );
				}
			];
		}

		// Button Shadow Secondary Hover
		if ( isset( $secondary_values['useShadowHover'] ) && ! empty( $secondary_values['useShadowHover'] ) ) {
			$rules['--secondarybtnhovershadow'] = [
				Dynamic_Selector::META_KEY    => $mod_key_secondary . '.shadowColorHover',
				Dynamic_Selector::META_DEFAULT       => 'none',
				Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) use ($secondary_values) {
					$blur   = intval($secondary_values['shadowPropertiesHover']['blur']);
					$width  = intval($secondary_values['shadowPropertiesHover']['width']);
					$height = intval($secondary_values['shadowPropertiesHover']['height']);

					return sprintf( '%s:%s;', $css_prop, sprintf('%spx %spx %spx %s;', $width, $height, $blur, $value ) );
				}
			];
		}

		// Border Width
		if ( isset( $primary_values['type'] ) && $primary_values['type'] === 'outline' ) {
			$rules['--primarybtnborderwidth'] = [
				Dynamic_Selector::META_KEY    => $mod_key_primary . '.borderWidth',
				Dynamic_Selector::META_SUFFIX => 'px',
				'directional-prop'            => Config::CSS_PROP_BORDER_WIDTH,
			];
		}
		if ( isset( $secondary_values['type'] ) && $secondary_values['type'] === 'outline' ) {
			$rules['--secondarybtnborderwidth'] = [
				Dynamic_Selector::META_KEY    => $mod_key_secondary . '.borderWidth',
				Dynamic_Selector::META_SUFFIX => 'px',
				'directional-prop'            => Config::CSS_PROP_BORDER_WIDTH,
			];
		}

		$mod_key_primary       = Config::MODS_BUTTON_PRIMARY_PADDING;
		$default_primary       = Mods::get_alternative_mod_default( Config::MODS_BUTTON_PRIMARY_PADDING );

		$rules['--btnpadding'] = [
			Dynamic_Selector::META_KEY           => $mod_key_primary,
			Dynamic_Selector::META_DEFAULT       => $default_primary,
			Dynamic_Selector::META_IS_RESPONSIVE => true,
			Dynamic_Selector::META_SUFFIX => 'responsive_unit',
			Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) {
				$mod_key_primary = Config::MODS_BUTTON_PRIMARY_STYLE;
				$default_primary = neve_get_button_appearance_default();

				$mod_key_secondary = Config::MODS_BUTTON_SECONDARY_STYLE;
				$default_secondary = neve_get_button_appearance_default( 'secondary' );

				$values   = [
					'primary'   => get_theme_mod( $mod_key_primary, $default_primary ),
					'secondary' => get_theme_mod( $mod_key_secondary, $default_secondary ),
				];
				$paddings = [
					'primary'   => $value,
					'secondary' => $value,
				];

				foreach ( $values as $btn_type => $appearance_values ) {
					if ( ! isset( $appearance_values['type'] ) || $appearance_values['type'] !== 'outline' ) {
						continue;
					}
					$border_width = $appearance_values['borderWidth'];

					foreach ( $paddings[ $btn_type ] as $direction => $padding_value ) {
						if ( ! isset( $border_width[ $direction ] ) || absint( $border_width[ $direction ] ) === 0 ) {
							continue;
						}
						if(  ! is_numeric( $padding_value ) ){
							continue;
						}

						$suffix_css_prop = $btn_type === 'primary' ? '--primarybtnpadding' : '--secondarybtnpadding';
						$suffix =  Css_Prop::get_suffix( $meta, $device, $value, $suffix_css_prop );
						$paddings[ $btn_type ][ 'is_outline_button_padding' ] = true;
						$paddings[ $btn_type ][ $direction ]                  = 'calc(' . $padding_value . $suffix . ' - ' . $border_width[ $direction ] . 'px)';
					}
				}
				$final_value_default   = Css_Prop::transform_directional_prop( $meta, $device, $value, '--btnpadding', Config::CSS_PROP_PADDING );
				$final_value_primary   = Css_Prop::transform_directional_prop( $meta, $device, $paddings['primary'], '--primarybtnpadding', Config::CSS_PROP_PADDING );
				$final_value_secondary = Css_Prop::transform_directional_prop( $meta, $device, $paddings['secondary'], '--secondarybtnpadding', Config::CSS_PROP_PADDING );

				return $final_value_default . $final_value_primary . $final_value_secondary;
			},
			'directional-prop'                   => Config::CSS_PROP_PADDING,
		];

		$mod_key_primary             = Config::MODS_BUTTON_TYPEFACE;
		$rules['--btnfs']            = [
			Dynamic_Selector::META_KEY           => $mod_key_primary . '.fontSize',
			Dynamic_Selector::META_IS_RESPONSIVE => true,
		];
		$rules['--btnlineheight']    = [
			Dynamic_Selector::META_KEY           => $mod_key_primary . '.lineHeight',
			Dynamic_Selector::META_IS_RESPONSIVE => true,
		];
		$rules['--btnletterspacing'] = [
			Dynamic_Selector::META_KEY           => $mod_key_primary . '.letterSpacing',
			Dynamic_Selector::META_IS_RESPONSIVE => true,
			Dynamic_Selector::META_SUFFIX        => 'px',
		];
		$rules['--btntexttransform'] = [
			Dynamic_Selector::META_KEY           => $mod_key_primary . '.textTransform',
			Dynamic_Selector::META_IS_RESPONSIVE => false,
		];
		$rules['--btnfontweight']    = [
			Dynamic_Selector::META_KEY => $mod_key_primary . '.fontWeight',
		];

		return $rules;
	}

	/**
	 * Get the common typography rules
	 *
	 * @retun array
	 */
	public function get_typography_rules() {
		$default = Mods::get_alternative_mod_default( Config::MODS_TYPEFACE_GENERAL );
		$mod_key = Config::MODS_TYPEFACE_GENERAL;

		$rules = [
			'--bodyfontfamily'     => [
				Dynamic_Selector::META_KEY     => Config::MODS_FONT_GENERAL,
				Dynamic_Selector::META_DEFAULT => Mods::get_alternative_mod_default( Config::MODS_FONT_GENERAL ),
			],
			'--bodyfontsize'       => [
				Dynamic_Selector::META_KEY           => $mod_key . '.fontSize',
				Dynamic_Selector::META_DEFAULT       => $default['fontSize'],
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_SUFFIX        => 'px',
			],
			'--bodylineheight'     => [
				Dynamic_Selector::META_KEY           => $mod_key . '.lineHeight',
				Dynamic_Selector::META_DEFAULT       => $default['lineHeight'],
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_SUFFIX        => '',
			],
			'--bodyletterspacing'  => [
				Dynamic_Selector::META_KEY           => $mod_key . '.letterSpacing',
				Dynamic_Selector::META_DEFAULT       => $default['letterSpacing'],
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_SUFFIX        => 'px',
			],
			'--bodyfontweight'     => [
				Dynamic_Selector::META_KEY     => $mod_key . '.fontWeight',
				Dynamic_Selector::META_DEFAULT => $default['fontWeight'],
				'font'                         => 'mods_' . Config::MODS_FONT_HEADINGS,
			],
			'--bodytexttransform'  => [
				Dynamic_Selector::META_KEY => $mod_key . '.textTransform',
			],
			'--headingsfontfamily' => [
				Dynamic_Selector::META_KEY => Config::MODS_FONT_HEADINGS,
			],
		];
		foreach ( neve_get_headings_selectors() as $id => $heading_selector ) {

			$composed_key = sprintf( 'neve_%s_typeface_general', $id );
			$mod_key      = $composed_key;
			$default      = Mods::get_alternative_mod_default( $composed_key );

			$rules[ '--' . $id . 'fontfamily' ] = [
				Dynamic_Selector::META_KEY           => $this->get_mod_key_heading_fontfamily( $id )
			];

			$rules[ '--' . $id . 'fontsize' ] = [
				Dynamic_Selector::META_KEY           => $mod_key . '.fontSize',
				Dynamic_Selector::META_DEFAULT       => $default['fontSize'],
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_SUFFIX        => 'px',
			];

			$rules[ '--' . $id . 'fontweight' ] = [
				Dynamic_Selector::META_KEY     => $mod_key . '.fontWeight',
				Dynamic_Selector::META_DEFAULT => $default['fontWeight'],
				'font'                         => 'mods_' . Config::MODS_FONT_HEADINGS,
			];

			$rules[ '--' . $id . 'lineheight' ] = [
				Dynamic_Selector::META_KEY           => $mod_key . '.lineHeight',
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_DEFAULT       => $default['lineHeight'],
				Dynamic_Selector::META_SUFFIX        => '',
			];

			$rules[ '--' . $id . 'letterspacing' ] = [
				Dynamic_Selector::META_KEY           => $mod_key . '.letterSpacing',
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_DEFAULT       => $default['letterSpacing'],
				Dynamic_Selector::META_SUFFIX        => 'px',
			];

			$rules[ '--' . $id . 'texttransform' ] = [
				Dynamic_Selector::META_KEY     => $mod_key . '.textTransform',
				Dynamic_Selector::META_DEFAULT => $default['textTransform'],
			];
		}

		return $rules;
	}

}
PK      \ȑй      core/styles/generator.phpnu W+A        <?php
/**
 * Style generator based on settings.
 *
 * @package Neve\Core\Styles
 */

namespace Neve\Core\Styles;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;

/**
 * Class Generator
 *
 * @package Neve\Core\Styles
 */
class Generator {
	/**
	 * Subscriber list used for CSS generation.
	 *
	 * @var array Subscriber list.
	 */
	protected $_subscribers   = [];
	const SUBSCRIBER_TYPE     = 'type';
	const SUBSCRIBER_MAP      = 'map';
	const SUBSCRIBER_KEY      = 'key';
	const SUBSCRIBER_DEFAULTS = 'defaults';
	/**
	 * Current context.
	 *
	 * @var string|null
	 */
	protected $context = null;

	/**
	 * Generate the dynamic CSS.
	 *
	 * @param bool $echo Should we write it or return it.
	 *
	 * @return string|void Css output.
	 */
	public function generate( $echo = false ) {
		$desktop_css = '';
		$tablet_css  = '';
		$all_css     = '';

		if ( $this->context === null ) {
			$this->context = Dynamic_Selector::CONTEXT_FRONTEND;
		}

		/**
		 * Neve try to build the CSS as mobile first.
		 * Based on this fact, the general CSS is considered the mobile one.
		 */
		$dynamic_selectors = new Dynamic_Selector( $this->_subscribers, $this->context );

		$all_css     .= $dynamic_selectors->for_mobile();
		$tablet_css  .= $dynamic_selectors->for_tablet();
		$desktop_css .= $dynamic_selectors->for_desktop();
		if ( ! empty( $tablet_css ) ) {
			$all_css .= sprintf( '@media(min-width: 576px){ %s }', $tablet_css );
		}
		if ( ! empty( $desktop_css ) ) {
			$all_css .= sprintf( '@media(min-width: 960px){ %s }', $desktop_css );
		}

		if ( ! $echo ) {
			return $all_css;
		}


		echo $all_css; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Set new subscribers.
	 *
	 * @param array $subscribers New generator list.
	 */
	public function set( $subscribers ) {
		$this->_subscribers = $subscribers;
	}

	/**
	 * Return current subscribers.
	 *
	 * @return array
	 */
	public function get() {
		return $this->_subscribers;
	}
}
PK      \{      core/styles/frontend.phpnu W+A        <?php
/**
 * Style generator based on settings.
 *
 * @package Neve\Core\Styles
 */

namespace Neve\Core\Styles;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Customizer\Defaults\Layout;
use Neve\Customizer\Defaults\Single_Post;

/**
 * Class Generator for Frontend.
 *
 * @package Neve\Core\Styles
 */
class Frontend extends Generator {
	use Css_Vars;
	use Single_Post;
	use Layout;

	/**
	 * Box shadow map values
	 *
	 * @var string[]
	 */
	private $box_shadow_map = [
		1 => '0 1px 3px -2px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.1)',
		2 => '0 3px 6px -5px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.1)',
		3 => '0 10px 20px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.1)',
		4 => '0 14px 28px rgba(0, 0, 0, 0.12), 0 10px 10px rgba(0, 0, 0, 0.12)',
		5 => '0 16px 38px -12px rgba(0,0,0,0.56), 0 4px 25px 0 rgba(0,0,0,0.12), 0 8px 10px -5px rgba(0,0,0,0.2)',
	];

	/**
	 * Generator constructor.
	 */
	public function __construct() {
		$this->_subscribers = [];
		$this->setup_container();
		$this->setup_blog_layout();
		$this->setup_legacy_gutenberg_palette();
		$this->setup_layout_subscribers();
		$this->setup_buttons();
		$this->setup_typography();
		$this->setup_blog_meta();
		$this->setup_blog_typography();
		$this->setup_blog_colors();
		$this->setup_form_fields_style();
		$this->setup_header_style();
		$this->setup_single_post_style();
		$this->setup_content_vspacing();
	}

	/**
	 * Setup the container styles.
	 *
	 * @return void
	 */
	private function setup_container() {
		$this->_subscribers[] = [
			Dynamic_Selector::KEY_SELECTOR => ':root',
			Dynamic_Selector::KEY_RULES    => $this->get_container_rules(),
		];
	}

	/**
	 * Setup legacy gutenberg palette for old users.
	 */
	private function setup_legacy_gutenberg_palette() {
		$is_new_user           = get_option( 'neve_new_user' );
		$imported_starter_site = get_option( 'neve_imported_demo' );

		if ( $is_new_user === 'yes' && $imported_starter_site !== 'yes' ) {
			return;
		}

		$this->_subscribers['.has-neve-button-color-color']            = [
			Config::CSS_PROP_COLOR => [
				Dynamic_Selector::META_KEY       => Config::MODS_BUTTON_PRIMARY_STYLE . '.background',
				Dynamic_Selector::META_IMPORTANT => true,
				Dynamic_Selector::META_DEFAULT   => '#0366d6',
			],
		];
		$this->_subscribers['.has-neve-button-color-background-color'] = [
			Config::CSS_PROP_BACKGROUND_COLOR => [
				Dynamic_Selector::META_KEY       => Config::MODS_BUTTON_PRIMARY_STYLE . '.background',
				Dynamic_Selector::META_IMPORTANT => true,
				Dynamic_Selector::META_DEFAULT   => '#0366d6',
			],
		];
	}

	/**
	 * Add css for blog colors.
	 */
	public function setup_blog_colors() {

		$layout = get_theme_mod( 'neve_blog_archive_layout', 'grid' );
		if ( $layout === 'covers' ) {
			$this->_subscribers['.neve-main'] = [
				'--color' => 'neve_blog_covers_text_color',
			];
		}

		$thumbnail_box_shadow_meta_name                  = apply_filters( 'neve_thumbnail_box_shadow_meta_filter', 'neve_post_thumbnail_box_shadow' );
		$this->_subscribers['.neve-main']['--boxshadow'] = [
			Dynamic_Selector::META_KEY    => $thumbnail_box_shadow_meta_name,
			Dynamic_Selector::META_FILTER => function ( $css_prop, $value, $meta, $device ) {
				if ( absint( $value ) === 0 ) {
					return '';
				}

				if ( ! array_key_exists( absint( $value ), $this->box_shadow_map ) ) {
					return '';
				}

				return sprintf( '%s:%s;', $css_prop, $this->box_shadow_map[ $value ] );
			},
		];
	}

	/**
	 * Add css for blog typography.
	 */
	public function setup_blog_typography() {

		$archive_typography = [
			Config::CSS_SELECTOR_ARCHIVE_POST_TITLE        => [
				'mod'  => Config::MODS_TYPEFACE_ARCHIVE_POST_TITLE,
				'font' => Config::MODS_FONT_HEADINGS,
			],
			Config::CSS_SELECTOR_ARCHIVE_POST_EXCERPT      => [
				'mod'  => Config::MODS_TYPEFACE_ARCHIVE_POST_EXCERPT,
				'font' => Config::MODS_FONT_GENERAL,
			],
			Config::CSS_SELECTOR_ARCHIVE_POST_META         => [
				'mod'  => Config::MODS_TYPEFACE_ARCHIVE_POST_META,
				'font' => Config::MODS_FONT_GENERAL,
			],
			Config::CSS_SELECTOR_SINGLE_POST_TITLE         => [
				'mod'  => Config::MODS_TYPEFACE_SINGLE_POST_TITLE,
				'font' => Config::MODS_FONT_HEADINGS,
			],
			Config::CSS_SELECTOR_SINGLE_POST_META          => [
				'mod'  => Config::MODS_TYPEFACE_SINGLE_POST_META,
				'font' => Config::MODS_FONT_GENERAL,
			],
			Config::CSS_SELECTOR_SINGLE_POST_COMMENT_TITLE => [
				'mod'  => Config::MODS_TYPEFACE_SINGLE_POST_COMMENT_TITLE,
				'font' => Config::MODS_FONT_HEADINGS,
			],
		];
		foreach ( $archive_typography as $selector => $args ) {
			$this->_subscribers[ $selector ] = [
				'--fontsize'      => [
					Dynamic_Selector::META_KEY           => $args['mod'] . '.fontSize',
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_SUFFIX        => 'px',
				],
				'--lineheight'    => [
					Dynamic_Selector::META_KEY           => $args['mod'] . '.lineHeight',
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_SUFFIX        => '',
				],
				'--letterspacing' => [
					Dynamic_Selector::META_KEY           => $args['mod'] . '.letterSpacing',
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_SUFFIX        => 'px',
				],
				'--fontweight'    => [
					Dynamic_Selector::META_KEY => $args['mod'] . '.fontWeight',
					'font'                     => 'mods_' . $args['font'],
				],
				'--texttransform' => $args['mod'] . '.textTransform',
			];
		}
	}

	/**
	 * Add css for blog layout.
	 *
	 * Removed grid in new skin CSS so this should handle the grid.
	 *
	 * @return bool|void
	 * @since 3.0.0
	 */
	public function setup_blog_layout() {

		$this->_subscribers[':root'] = [
			'--postwidth' => [
				Dynamic_Selector::META_KEY           => 'neve_grid_layout',
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_DEFAULT       => $this->grid_columns_default(),
				Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) {
					$blog_layout = get_theme_mod( 'neve_blog_archive_layout', 'grid' );
					if ( ! in_array( $blog_layout, [ 'grid', 'covers' ], true ) ) {
						return sprintf( '%s:%s;', $css_prop, '100%' );
					}

					if ( $value < 1 ) {
						$value = 1;
					}

					return sprintf( '%s:%s;', $css_prop, 100 / $value . '%' );
				},
			],
		];
	}

	/**
	 * Setup typography subscribers.
	 */
	public function setup_typography() {
		$rules                = $this->get_typography_rules();
		$this->_subscribers[] = [
			Dynamic_Selector::KEY_SELECTOR => ':root',
			Dynamic_Selector::KEY_RULES    => $rules,
		];
	}

	/**
	 * Setup button subscribers.
	 */
	public function setup_buttons() {

		$rules                = $this->get_button_rules();
		$this->_subscribers[] = [
			Dynamic_Selector::KEY_SELECTOR => ':root',
			Dynamic_Selector::KEY_RULES    => $rules,
		];
	}

	/**
	 * Setup settings subscribers for layout.
	 *
	 * TODO: Exclude sidebar CSS when there is not sidebar option selected.
	 * TODO: Better exclude classes when Woo is not present, i.e shop-sidebar class is added even when Woo is not used.
	 */
	public function setup_layout_subscribers() {
		$is_advanced_on = Mods::get( Config::MODS_ADVANCED_LAYOUT_OPTIONS, true );
		if ( ! $is_advanced_on ) {

			$this->_subscribers['#content .container .col, #content .container-fluid .col']                             = [
				Config::CSS_PROP_MAX_WIDTH => [
					Dynamic_Selector::META_KEY         => Config::MODS_SITEWIDE_CONTENT_WIDTH,
					Dynamic_Selector::META_SUFFIX      => '%',
					Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				],
			];
			$this->_subscribers['.alignfull > [class*="__inner-container"], .alignwide > [class*="__inner-container"]'] = [
				Config::CSS_PROP_MAX_WIDTH => [
					Dynamic_Selector::META_KEY           => Config::MODS_SITEWIDE_CONTENT_WIDTH,
					Dynamic_Selector::META_DEFAULT       => 70,
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) {
						$width = Mods::to_json( Config::MODS_CONTAINER_WIDTH );
						if ( $device === Dynamic_Selector::DESKTOP ) {
							return sprintf( 'max-width:%spx', round( ( $value / 100 ) * $width[ $device ] - Config::CONTENT_DEFAULT_PADDING ) );
						}
						if ( $device === Dynamic_Selector::MOBILE ) {
							return sprintf( 'max-width:%spx;margin:auto', ( $width[ $device ] - Config::CONTENT_DEFAULT_PADDING ) );
						}

						return '';
					},
				],
			];
			$this->_subscribers['.container-fluid .alignfull > [class*="__inner-container"], .container-fluid .alignwide > [class*="__inner-container"]'] = [
				Config::CSS_PROP_MAX_WIDTH => [
					Dynamic_Selector::META_KEY         => Config::MODS_SITEWIDE_CONTENT_WIDTH,
					Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
					Dynamic_Selector::META_FILTER      => function ( $css_prop, $value, $meta, $device ) {
						return sprintf( 'max-width:calc(%s%% + %spx)', $value, Config::CONTENT_DEFAULT_PADDING / 2 );
					},
				],
			];
			$this->_subscribers['.nv-sidebar-wrap, .nv-sidebar-wrap.shop-sidebar'] = [
				Config::CSS_PROP_MAX_WIDTH => [
					Dynamic_Selector::META_KEY         => Config::MODS_SITEWIDE_CONTENT_WIDTH,
					Dynamic_Selector::META_FILTER      => 'minus_100',
					Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
					Dynamic_Selector::META_SUFFIX      => '%',
				],
			];

			return;
		}
		// Others content width.
		$this->_subscribers['body:not(.single):not(.archive):not(.blog):not(.search):not(.error404) .neve-main > .container .col, body.post-type-archive-course .neve-main > .container .col, body.post-type-archive-llms_membership .neve-main > .container .col'] = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => Config::MODS_OTHERS_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT     => $this->sidebar_layout_width_default( Config::MODS_OTHERS_CONTENT_WIDTH ),
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];
		$this->_subscribers['body:not(.single):not(.archive):not(.blog):not(.search):not(.error404) .nv-sidebar-wrap, body.post-type-archive-course .nv-sidebar-wrap, body.post-type-archive-llms_membership .nv-sidebar-wrap']                                     = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => Config::MODS_OTHERS_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT     => $this->sidebar_layout_width_default( Config::MODS_OTHERS_CONTENT_WIDTH ),
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_FILTER      => 'minus_100',
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];
		// Archive content width.
		$this->_subscribers['.neve-main > .archive-container .nv-index-posts.col'] = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => Config::MODS_ARCHIVE_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT     => $this->sidebar_layout_width_default( Config::MODS_ARCHIVE_CONTENT_WIDTH ),
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];
		$this->_subscribers['.neve-main > .archive-container .nv-sidebar-wrap']    = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => Config::MODS_ARCHIVE_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT     => $this->sidebar_layout_width_default( Config::MODS_ARCHIVE_CONTENT_WIDTH ),
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_FILTER      => 'minus_100',
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];
		// Single content width.
		list( $context, $allowed_context ) = $this->get_cpt_context( [ 'post' ] );
		$sidebar_content_width_meta        = $this->get_sidebar_content_width_meta( $context, $allowed_context );
		$sidebar_layout_width_default      = $this->sidebar_layout_width_default( $sidebar_content_width_meta );
		$this->_subscribers['.neve-main > .single-post-container .nv-single-post-wrap.col'] = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => $sidebar_content_width_meta,
				Dynamic_Selector::META_DEFAULT     => $sidebar_layout_width_default,
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];

		$this->_subscribers['.single-post-container .alignfull > [class*="__inner-container"], .single-post-container .alignwide > [class*="__inner-container"]']                                 = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY           => $sidebar_content_width_meta,
				Dynamic_Selector::META_DEFAULT       => $sidebar_layout_width_default,
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) {
					$width = Mods::to_json( Config::MODS_CONTAINER_WIDTH );
					$value = $device !== Dynamic_Selector::DESKTOP ? ( $width[ $device ] - Config::CONTENT_DEFAULT_PADDING ) : round( ( $value / 100 ) * $width[ $device ] - Config::CONTENT_DEFAULT_PADDING );

					return sprintf( 'max-width:%spx', $value );
				},
			],
		];
		$this->_subscribers['.container-fluid.single-post-container .alignfull > [class*="__inner-container"], .container-fluid.single-post-container .alignwide > [class*="__inner-container"]'] = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => $sidebar_content_width_meta,
				Dynamic_Selector::META_DEFAULT     => $sidebar_layout_width_default,
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_FILTER      => function ( $css_prop, $value, $meta, $device ) {
					return sprintf( 'max-width:calc(%s%% + %spx)', $value, Config::CONTENT_DEFAULT_PADDING / 2 );
				},
			],
		];

		$this->_subscribers['.neve-main > .single-post-container .nv-sidebar-wrap'] = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => $sidebar_content_width_meta,
				Dynamic_Selector::META_DEFAULT     => $sidebar_layout_width_default,
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_FILTER      => 'minus_100',
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];

		// TODO provide context handler for better checks.
		if ( ! class_exists( 'WooCommerce', false ) ) {
			return;
		}

		$this->_subscribers['.archive.woocommerce .neve-main > .shop-container .nv-shop.col']     = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => Config::MODS_SHOP_ARCHIVE_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT     => $this->sidebar_layout_width_default( Config::MODS_SHOP_ARCHIVE_CONTENT_WIDTH ),
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];
		$this->_subscribers['.archive.woocommerce .neve-main > .shop-container .nv-sidebar-wrap'] = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => Config::MODS_SHOP_ARCHIVE_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT     => $this->sidebar_layout_width_default( Config::MODS_SHOP_ARCHIVE_CONTENT_WIDTH ),
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_FILTER      => 'minus_100',
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];


		$this->_subscribers['.single-product .neve-main > .shop-container .nv-shop.col'] = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => Config::MODS_SHOP_SINGLE_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT     => $this->sidebar_layout_width_default( Config::MODS_SHOP_SINGLE_CONTENT_WIDTH ),
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];

		$this->_subscribers['.single-product .alignfull > [class*="__inner-container"], .single-product .alignwide > [class*="__inner-container"]']                  = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY           => Config::MODS_SHOP_SINGLE_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT       => $this->sidebar_layout_width_default( Config::MODS_SHOP_SINGLE_CONTENT_WIDTH ),
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) {
					$width = Mods::to_json( Config::MODS_CONTAINER_WIDTH );
					$value = $device !== Dynamic_Selector::DESKTOP ? ( $width[ $device ] - Config::CONTENT_DEFAULT_PADDING ) : round( ( $value / 100 ) * $width[ $device ] - Config::CONTENT_DEFAULT_PADDING );

					return sprintf( 'max-width:%spx', $value );
				},
			],
		];
		$this->_subscribers['.single-product .container-fluid .alignfull > [class*="__inner-container"], .single-product .alignwide > [class*="__inner-container"]'] = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => Config::MODS_SHOP_SINGLE_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT     => $this->sidebar_layout_width_default( Config::MODS_SHOP_SINGLE_CONTENT_WIDTH ),
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_FILTER      => function ( $css_prop, $value, $meta, $device ) {
					return sprintf( 'max-width:calc(%s%% + %spx)', $value, Config::CONTENT_DEFAULT_PADDING / 2 );
				},
			],
		];
		$this->_subscribers['.single-product .neve-main > .shop-container .nv-sidebar-wrap'] = [
			Config::CSS_PROP_MAX_WIDTH => [
				Dynamic_Selector::META_KEY         => Config::MODS_SHOP_SINGLE_CONTENT_WIDTH,
				Dynamic_Selector::META_DEFAULT     => $this->sidebar_layout_width_default( Config::MODS_SHOP_SINGLE_CONTENT_WIDTH ),
				Dynamic_Selector::META_DEVICE_ONLY => Dynamic_Selector::DESKTOP,
				Dynamic_Selector::META_FILTER      => 'minus_100',
				Dynamic_Selector::META_SUFFIX      => '%',
			],
		];
	}

	/**
	 * Adds form field styles
	 */
	private function setup_form_fields_style() {

		$border_width_default  = array_fill_keys( Config::$directional_keys, '2' );
		$border_radius_default = array_fill_keys( Config::$directional_keys, '3' );

		$this->_subscribers[] = [
			Dynamic_Selector::KEY_SELECTOR => ':root',
			Dynamic_Selector::KEY_RULES    => [
				'--formfieldborderwidth'   => [
					Dynamic_Selector::META_KEY     => Config::MODS_FORM_FIELDS_BORDER_WIDTH,
					Dynamic_Selector::META_SUFFIX  => 'px',
					Dynamic_Selector::META_DEFAULT => $border_width_default,
					'directional-prop'             => Config::CSS_PROP_BORDER_WIDTH,
				],
				'--formfieldborderradius'  => [
					Dynamic_Selector::META_KEY     => Config::MODS_FORM_FIELDS_BORDER_RADIUS,
					Dynamic_Selector::META_SUFFIX  => 'px',
					Dynamic_Selector::META_DEFAULT => $border_radius_default,
					'directional-prop'             => Config::CSS_PROP_BORDER_RADIUS,
				],
				'--formfieldbgcolor'       => [
					Dynamic_Selector::META_KEY     => Config::MODS_FORM_FIELDS_BACKGROUND_COLOR,
					Dynamic_Selector::META_DEFAULT => 'var(--nv-site-bg)',
				],
				'--formfieldbordercolor'   => [
					Dynamic_Selector::META_KEY     => Config::MODS_FORM_FIELDS_BORDER_COLOR,
					Dynamic_Selector::META_DEFAULT => '#dddddd',
				],
				'--formfieldcolor'         => [
					Dynamic_Selector::META_KEY     => Config::MODS_FORM_FIELDS_COLOR,
					Dynamic_Selector::META_DEFAULT => 'var(--nv-text-color)',
				],
				'--formfieldpadding'       => [
					Dynamic_Selector::META_KEY           => Config::MODS_FORM_FIELDS_PADDING,
					Dynamic_Selector::META_DEFAULT       => Mods::get_alternative_mod_default( Config::MODS_FORM_FIELDS_PADDING ),
					Dynamic_Selector::META_SUFFIX        => 'px',
					Dynamic_Selector::META_IS_RESPONSIVE => false,
					'directional-prop'                   => Config::CSS_PROP_PADDING,
				],
				'--formfieldtexttransform' => [
					Dynamic_Selector::META_KEY           => Config::MODS_FORM_FIELDS_TYPEFACE . '.textTransform',
					Dynamic_Selector::META_IS_RESPONSIVE => false,
				],
				'--formfieldfontsize'      => [
					Dynamic_Selector::META_KEY           => Config::MODS_FORM_FIELDS_TYPEFACE . '.fontSize',
					Dynamic_Selector::META_SUFFIX        => 'px',
					Dynamic_Selector::META_IS_RESPONSIVE => true,
				],
				'--formfieldlineheight'    => [
					Dynamic_Selector::META_KEY           => Config::MODS_FORM_FIELDS_TYPEFACE . '.lineHeight',
					Dynamic_Selector::META_SUFFIX        => '',
					Dynamic_Selector::META_IS_RESPONSIVE => true,
				],
				'--formfieldletterspacing' => [
					Dynamic_Selector::META_KEY           => Config::MODS_FORM_FIELDS_TYPEFACE . '.letterSpacing',
					Dynamic_Selector::META_SUFFIX        => 'px',
					Dynamic_Selector::META_IS_RESPONSIVE => true,
				],
				'--formfieldfontweight'    => [
					Dynamic_Selector::META_KEY => Config::MODS_FORM_FIELDS_TYPEFACE . '.fontWeight',
				],
				// Form Labels
				'--formlabelfontsize'      => [
					Dynamic_Selector::META_KEY           => Config::MODS_FORM_FIELDS_LABELS_TYPEFACE . '.fontSize',
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_SUFFIX        => 'px',
				],
				'--formlabellineheight'    => [
					Dynamic_Selector::META_KEY           => Config::MODS_FORM_FIELDS_LABELS_TYPEFACE . '.lineHeight',
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_SUFFIX        => '',
				],
				'--formlabelletterspacing' => [
					Dynamic_Selector::META_KEY           => Config::MODS_FORM_FIELDS_LABELS_TYPEFACE . '.letterSpacing',
					Dynamic_Selector::META_IS_RESPONSIVE => true,
				],
				'--formlabelfontweight'    => [
					Dynamic_Selector::META_KEY => Config::MODS_FORM_FIELDS_LABELS_TYPEFACE . '.fontWeight',
				],
				'--formlabeltexttransform' => [
					Dynamic_Selector::META_KEY => Config::MODS_FORM_FIELDS_LABELS_TYPEFACE . '.textTransform',
				],
			],
		];

		// Form button style. Override if needed.
		$form_buttons_type = get_theme_mod( 'neve_form_button_type', 'primary' );

		if ( $form_buttons_type === 'primary' ) {
			return;
		}

		$this->_subscribers[ Config::CSS_SELECTOR_FORM_BUTTON ]['background-color']       = [
			'key'      => 'neve_form_button_type',
			'override' => 'var(--secondarybtnbg, transparent)',
		];
		$this->_subscribers[ Config::CSS_SELECTOR_FORM_BUTTON ]['color']                  = [
			'key'      => 'neve_form_button_type',
			'override' => 'var(--secondarybtncolor)',
		];
		$this->_subscribers[ Config::CSS_SELECTOR_FORM_BUTTON ]['padding']                = [
			'key'      => 'neve_form_button_type',
			'override' => 'var(--secondarybtnpadding, 7px 12px)',
		];
		$this->_subscribers[ Config::CSS_SELECTOR_FORM_BUTTON ]['border-radius']          = [
			'key'      => 'neve_form_button_type',
			'override' => 'var(--secondarybtnborderradius, 3px)',
		];
		$this->_subscribers[ Config::CSS_SELECTOR_FORM_BUTTON_HOVER ]['background-color'] = [
			'key'      => 'neve_form_button_type',
			'override' => 'var(--secondarybtnhoverbg, transparent)',
		];
		$this->_subscribers[ Config::CSS_SELECTOR_FORM_BUTTON_HOVER ]['color']            = [
			'key'      => 'neve_form_button_type',
			'override' => 'var(--secondarybtnhovercolor)',
		];

		$mod_key_secondary = Config::MODS_BUTTON_SECONDARY_STYLE;
		$default_secondary = Mods::get_alternative_mod_default( Config::MODS_BUTTON_SECONDARY_STYLE );
		$secondary_values  = get_theme_mod( $mod_key_secondary, $default_secondary );

		if ( ! isset( $secondary_values['type'] ) || $secondary_values['type'] !== 'outline' ) {
			return;
		}

		$this->_subscribers[ Config::CSS_SELECTOR_FORM_BUTTON ]['border-width']       = [
			'key'      => 'neve_form_button_type',
			'override' => 'var(--secondarybtnborderwidth, 3px)',
		];
		$this->_subscribers[ Config::CSS_SELECTOR_FORM_BUTTON ]['border-color']       = [
			'key'      => 'neve_form_button_type',
			'override' => 'var(--secondarybtnhovercolor)',
		];
		$this->_subscribers[ Config::CSS_SELECTOR_FORM_BUTTON_HOVER ]['border-color'] = [
			'key'      => 'neve_form_button_type',
			'override' => 'var(--secondarybtnhovercolor)',
		];
	}

	/**
	 * Add form buttons selectors to the Buttons selector.
	 *
	 * @param string $selector the CSS selector received from the filter.
	 *
	 * @return string
	 */
	public function add_form_buttons( $selector ) {
		return ( $selector . ', form input[type="submit"], form button[type="submit"]' );
	}

	/**
	 * Add form buttons hover selectors to the Buttons selector.
	 *
	 * @param string $selector the CSS selector received from the filter.
	 *
	 * @return string
	 */
	public function add_form_buttons_hover( $selector ) {
		return ( $selector . ', form input[type="submit"]:hover, form button[type="submit"]:hover' );
	}

	/**
	 * Add css for blog meta.
	 */
	public function setup_blog_meta() {

		list( $context, $allowed_context ) = $this->get_cpt_context();
		$archive_avatar_size_meta_key      = Config::MODS_ARCHIVE_POST_META_AUTHOR_AVATAR_SIZE;
		$single_avatar_size_meta_key       = Config::MODS_SINGLE_POST_META_AUTHOR_AVATAR_SIZE;
		if ( in_array( $context, $allowed_context, true ) && is_singular( $context ) || is_post_type_archive( $context ) ) {
			$archive_avatar_size_meta_key = 'neve_' . $context . '_archive_author_avatar_size';
			$single_avatar_size_meta_key  = 'neve_single_' . $context . '_avatar_size';
		}

		$rules = [
			'--avatarsize' => [
				Dynamic_Selector::META_KEY           => $archive_avatar_size_meta_key,
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_SUFFIX        => 'responsive_suffix',
				Dynamic_Selector::META_DEFAULT       => '{ "mobile": 20, "tablet": 20, "desktop": 20 }',
			],
		];

		$rules_single = [
			'--avatarsize' => [
				Dynamic_Selector::META_KEY           => $single_avatar_size_meta_key,
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_SUFFIX        => 'responsive_suffix',
				Dynamic_Selector::META_DEFAULT       => Mods::get( 'neve_author_avatar_size', '{ "mobile": 20, "tablet": 20, "desktop": 20 }' ),
			],
		];

		$this->_subscribers[] = [
			'selectors' => '.nv-meta-list',
			'rules'     => $rules,
		];

		$this->_subscribers[] = [
			'selectors' => '.single .nv-meta-list',
			'rules'     => $rules_single,
		];
	}

	/**
	 * Add css for single post.
	 */
	private function setup_single_post_style() {

		$boxed_comments_rules = [
			'--padding' => [
				Dynamic_Selector::META_KEY           => Config::MODS_POST_COMMENTS_PADDING,
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_DEFAULT       => $this->padding_default(),
				'directional-prop'                   => Config::CSS_PROP_PADDING,
			],
			'--bgcolor' => [
				Dynamic_Selector::META_KEY => Config::MODS_POST_COMMENTS_BACKGROUND_COLOR,
			],
			'--color'   => [
				Dynamic_Selector::META_KEY => Config::MODS_POST_COMMENTS_TEXT_COLOR,
			],
		];

		$this->_subscribers[] = [
			'selectors' => '.nv-is-boxed.nv-comments-wrap',
			'rules'     => $boxed_comments_rules,
		];

		$boxed_comment_form_rules = [
			'--padding' => [
				Dynamic_Selector::META_KEY           => Config::MODS_POST_COMMENTS_FORM_PADDING,
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_SUFFIX        => 'responsive_unit',
				Dynamic_Selector::META_DEFAULT       => $this->padding_default(),
				'directional-prop'                   => Config::CSS_PROP_PADDING,
			],
			'--bgcolor' => [
				Dynamic_Selector::META_KEY => Config::MODS_POST_COMMENTS_FORM_BACKGROUND_COLOR,
			],
			'--color'   => [
				Dynamic_Selector::META_KEY => Config::MODS_POST_COMMENTS_FORM_TEXT_COLOR,
			],
		];

		$this->_subscribers[] = [
			'selectors' => '.nv-is-boxed.comment-respond',
			'rules'     => $boxed_comment_form_rules,
		];

		$spacing_rules = [
			'--spacing' => [
				Dynamic_Selector::META_KEY           => Config::MODS_SINGLE_POST_ELEMENTS_SPACING,
				Dynamic_Selector::META_IS_RESPONSIVE => true,
				Dynamic_Selector::META_SUFFIX        => 'responsive_suffix',
			],
		];

		$this->_subscribers[] = [
			'selectors' => '.nv-single-post-wrap',
			'rules'     => $spacing_rules,
		];
	}

	/**
	 * Check that all mods passed can be used for the provided context.
	 * We use this to check if we can register subscribers for the provided mods.
	 *
	 * @since 3.1.0
	 *
	 * @param string[] $mods               A list of mods.
	 * @param string   $context            A context for the mods.
	 * @param array    $allowed_context    A list of allowed contexts to be passed on.
	 *
	 * @return int
	 */
	private function can_use_mods( $mods, $context, $allowed_context ) {
		return array_reduce(
			$mods,
			function ( $carry, $item ) use ( $context, $allowed_context ) {
				if ( empty( $this->get_cover_meta( $context, $item, $allowed_context ) ) ) {
					return 0;
				}
				return $carry;
			},
			1
		);
	}

	/**
	 * Add css for post/page header.
	 */
	private function setup_header_style() {

		list( $context, $allowed_context ) = $this->get_cpt_context();

		$justify_map = [
			'left'   => 'flex-start',
			'center' => 'center',
			'right'  => 'flex-end',
		];

		$can_use_cover_rules = $this->can_use_mods(
			[
				Config::MODS_COVER_HEIGHT,
				Config::MODS_COVER_PADDING,
				Config::MODS_COVER_TITLE_ALIGNMENT,
				Config::MODS_COVER_TITLE_POSITION,
			],
			$context,
			$allowed_context
		);
		if ( $can_use_cover_rules ) {
			$cover_rules          = [
				'--height'    => [
					Dynamic_Selector::META_KEY           => $this->get_cover_meta( $context, Config::MODS_COVER_HEIGHT, $allowed_context ),
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_AS_JSON       => true,
					Dynamic_Selector::META_SUFFIX        => 'responsive_suffix',
					Dynamic_Selector::META_DEFAULT       => '{ "mobile": "250", "tablet": "320", "desktop": "400" }',
				],
				'--padding'   => [
					Dynamic_Selector::META_KEY           => $this->get_cover_meta( $context, Config::MODS_COVER_PADDING, $allowed_context ),
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_DEFAULT       => $this->padding_default( 'cover' ),
					Dynamic_Selector::META_SUFFIX        => 'responsive_unit',
					'directional-prop'                   => Config::CSS_PROP_PADDING,
				],
				'--justify'   => [
					Dynamic_Selector::META_KEY           => $this->get_cover_meta( $context, Config::MODS_COVER_TITLE_ALIGNMENT, $allowed_context ),
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_DEFAULT       => self::post_title_alignment(),
					Dynamic_Selector::META_FILTER        => function ( $css_prop, $value, $meta, $device ) use ( $justify_map ) {
						return sprintf( '%s: %s;', $css_prop, $justify_map[ $value ] );
					},
				],
				'--textalign' => [
					Dynamic_Selector::META_KEY           => $this->get_cover_meta( $context, Config::MODS_COVER_TITLE_ALIGNMENT, $allowed_context ),
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_DEFAULT       => self::post_title_alignment(),
				],
				'--valign'    => [
					Dynamic_Selector::META_KEY           => $this->get_cover_meta( $context, Config::MODS_COVER_TITLE_POSITION, $allowed_context ),
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_DEFAULT       => [
						'mobile'  => 'center',
						'tablet'  => 'center',
						'desktop' => 'center',
					],
				],
			];
			$this->_subscribers[] = [
				'selectors' => '.nv-post-cover',
				'rules'     => $cover_rules,
			];
		}

		$can_use_title_rules = $this->can_use_mods(
			[ Config::MODS_COVER_TEXT_COLOR, Config::MODS_COVER_TITLE_ALIGNMENT ],
			$context,
			$allowed_context
		);
		if ( $can_use_title_rules ) {
			$title_rules          = [
				'--color'     => [
					Dynamic_Selector::META_KEY => $this->get_cover_meta( $context, Config::MODS_COVER_TEXT_COLOR, $allowed_context ),
				],
				'--textalign' => [
					Dynamic_Selector::META_KEY           => $this->get_cover_meta( $context, Config::MODS_COVER_TITLE_ALIGNMENT, $allowed_context ),
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_DEFAULT       => self::post_title_alignment(),
				],
			];
			$this->_subscribers[] = [
				'selectors' => '.nv-post-cover .nv-title-meta-wrap, .nv-page-title-wrap, .entry-header',
				'rules'     => $title_rules,
			];
		}

		$can_use_boxed_title_rules = $this->can_use_mods(
			[ Config::MODS_COVER_BOXED_TITLE_PADDING, Config::MODS_COVER_BOXED_TITLE_BACKGROUND ],
			$context,
			$allowed_context
		);
		if ( $can_use_boxed_title_rules ) {
			$boxed_title_rules    = [
				'--padding' => [
					Dynamic_Selector::META_KEY           => $this->get_cover_meta( $context, Config::MODS_COVER_BOXED_TITLE_PADDING, $allowed_context ),
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_DEFAULT       => $this->padding_default( 'cover' ),
					Dynamic_Selector::META_SUFFIX        => 'responsive_unit',
					'directional-prop'                   => Config::CSS_PROP_PADDING,
				],
				'--bgcolor' => [
					Dynamic_Selector::META_KEY     => $this->get_cover_meta( $context, Config::MODS_COVER_BOXED_TITLE_BACKGROUND, $allowed_context ),
					Dynamic_Selector::META_DEFAULT => 'var(--nv-dark-bg)',
				],
			];
			$this->_subscribers[] = [
				'selectors' => '.nv-is-boxed.nv-title-meta-wrap',
				'rules'     => $boxed_title_rules,
			];
		}

		$can_use_overlay_rules = $this->can_use_mods(
			[ Config::MODS_COVER_BACKGROUND_COLOR, Config::MODS_COVER_OVERLAY_OPACITY, Config::MODS_COVER_BLEND_MODE ],
			$context,
			$allowed_context
		);
		if ( $can_use_overlay_rules ) {
			$overlay_rules        = [
				'--bgcolor'   => [
					Dynamic_Selector::META_KEY => $this->get_cover_meta( $context, Config::MODS_COVER_BACKGROUND_COLOR, $allowed_context ),
				],
				'--opacity'   => [
					Dynamic_Selector::META_KEY     => $this->get_cover_meta( $context, Config::MODS_COVER_OVERLAY_OPACITY, $allowed_context ),
					Dynamic_Selector::META_DEFAULT => 50,
				],
				'--blendmode' => [
					Dynamic_Selector::META_KEY     => $this->get_cover_meta( $context, Config::MODS_COVER_BLEND_MODE, $allowed_context ),
					Dynamic_Selector::META_DEFAULT => 'normal',
				],
			];
			$this->_subscribers[] = [
				'selectors' => '.nv-overlay',
				'rules'     => $overlay_rules,
			];
		}

	}

	/**
	 * Add content vertical spacing rules.
	 */
	private function setup_content_vspacing() {
		$rules = [
			'--c-vspace' => [
				Dynamic_Selector::META_KEY              => Config::MODS_CONTENT_VSPACING,
				Dynamic_Selector::META_IS_RESPONSIVE    => true,
				Dynamic_Selector::META_SUFFIX           => 'responsive_unit',
				Dynamic_Selector::META_DEFAULT          => $this->content_vspacing_default(),
				Dynamic_Selector::META_DIRECTIONAL_PROP => Config::CSS_PROP_DIRECTIONAL_ONE_AXIS,
			],
		];

		$this->_subscribers[] = [
			'selectors' => '.single:not(.single-product), .page',
			'rules'     => $rules,
		];

		$post_inherits_vspace = Mods::get( Config::MODS_SINGLE_POST_VSPACING_INHERIT, 'inherit' ) === 'inherit';
		if ( ! $post_inherits_vspace ) {
			$default    = Mods::get( Config::MODS_CONTENT_VSPACING, $this->content_vspacing_default() );
			$post_rules = [
				'--c-vspace' => [
					Dynamic_Selector::META_KEY           => Config::MODS_SINGLE_POST_CONTENT_VSPACING,
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_SUFFIX        => 'responsive_unit',
					Dynamic_Selector::META_DEFAULT       => $default,
					Dynamic_Selector::META_DIRECTIONAL_PROP => Config::CSS_PROP_DIRECTIONAL_ONE_AXIS,
				],
			];

			$this->_subscribers[] = [
				'selectors' => '.single:not(.single-product) .neve-main',
				'rules'     => $post_rules,
			];
		}

		list( $context )      = $this->get_cpt_context();
		$post_inherits_vspace = Mods::get( 'neve_' . $context . '_' . Config::MODS_POST_TYPE_VSPACING_INHERIT, 'inherit' ) === 'inherit';
		if ( ! $post_inherits_vspace ) {
			$rules = [
				'--c-vspace' => [
					Dynamic_Selector::META_KEY           => 'neve_' . $context . '_' . Config::MODS_POST_TYPE_VSPACING,
					Dynamic_Selector::META_IS_RESPONSIVE => true,
					Dynamic_Selector::META_SUFFIX        => 'responsive_unit',
					Dynamic_Selector::META_DEFAULT       => $this->content_vspacing_default(),
					'directional-prop'                   => Config::CSS_PROP_DIRECTIONAL_ONE_AXIS,
				],
			];

			$selectors = $context === 'page' ? '.' . $context : '.single-' . $context;

			$this->_subscribers[] = [
				'selectors' => $selectors . ' .neve-main',
				'rules'     => $rules,
			];
		}
	}
}
PK      \,L  L     core/styles/dynamic_selector.phpnu W+A        <?php
/**
 * Dynamic selector.
 *
 * @package Neve\Core\Styles
 */

namespace Neve\Core\Styles;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;

/**
 * Class Dynamic_Selector
 *
 * @package Neve\Core\Styles
 */
class Dynamic_Selector {
	const MOBILE  = 'mobile';
	const TABLET  = 'tablet';
	const DESKTOP = 'desktop';

	const META_IS_RESPONSIVE    = 'is_responsive';
	const META_SUFFIX           = 'suffix';
	const META_KEY              = 'key';
	const META_IMPORTANT        = 'important';
	const META_DEFAULT          = 'default';
	const META_DIRECTIONAL_PROP = 'directional-prop';
	const META_DEVICE_ONLY      = 'device_only';
	const META_FILTER           = 'filter';
	const META_AS_JSON          = 'as_json';

	const KEY_SELECTOR = 'selectors';
	const KEY_RULES    = 'rules';
	const KEY_CONTEXT  = 'context';

	const CONTEXT_FRONTEND  = 'frontend';
	const CONTEXT_GUTENBERG = 'gutenberg';
	/**
	 * Holds CSS selector mapping.
	 *
	 * @var array Selector mapping.
	 */
	private $mapping = [];
	/**
	 * Current device.
	 *
	 * @var string Current device.
	 */
	protected $device = null;
	/**
	 * Used to flag the shorthand tag to css selector transformations.
	 *
	 * @var bool Flag shorthand transformation.
	 */
	private $is_transformed = false;
	/**
	 * Holds current context.
	 *
	 * @var string Current context.
	 */
	private $current_context = null;

	/**
	 * Dynamic_Selector constructor.
	 *
	 * @param array $mapping CSS selector mapping.
	 */
	public function __construct( $mapping, $context = null ) {
		$this->current_context = $context;
		$this->mapping         = $mapping;
	}

	/**
	 * Mark the subscriber for Mobile.
	 *
	 * @return $this
	 */
	public function for_mobile() {
		$this->device = self::MOBILE;

		return $this;
	}

	/**
	 * Mark the subscriber for tablet.
	 *
	 * @return $this
	 */
	public function for_tablet() {
		$this->device = self::TABLET;

		return $this;
	}

	/**
	 * Mark the subscriber for desktop.
	 *
	 * @return $this
	 */
	public function for_desktop() {
		$this->device = self::DESKTOP;

		return $this;
	}

	/**
	 * Get current context.
	 *
	 * @return string Context.
	 */
	public function get_context() {
		return ! empty( $this->current_context ) ? $this->current_context : self::CONTEXT_FRONTEND;
	}

	/**
	 * Transform selectors tags into CSS selectors.
	 *
	 * @return array Transformed mapping.
	 */
	public function transform_selectors() {
		if ( $this->is_transformed ) {
			return $this->mapping;
		}
		$map = [];
		foreach ( $this->mapping as $key_map => $value_map ) {
			$selector           = ! isset( $value_map[ self::KEY_SELECTOR ] ) ? $key_map : $value_map[ self::KEY_SELECTOR ];
			$props              = ! isset( $value_map[ self::KEY_RULES ] ) ? $value_map : $value_map[ self::KEY_RULES ];
			$context            = isset( $value_map[ self::KEY_CONTEXT ] ) ? $value_map[ self::KEY_CONTEXT ] : [ self::CONTEXT_FRONTEND => true ];
			$expanded_selectors = $selector;
			if ( ! isset( $context[ $this->get_context() ] ) ) {
				continue;
			}
			if ( isset( Config::$css_selectors_map[ $expanded_selectors ] ) ) {
				$expanded_selectors = Config::$css_selectors_map[ $expanded_selectors ];
			}
			$expanded_selectors = apply_filters( 'neve_selectors_' . $selector, $expanded_selectors, $context, $this );
			if ( empty( $expanded_selectors ) ) {
				continue;
			}
			if ( $this->get_context() === self::CONTEXT_GUTENBERG ) {
				$expanded_selectors = explode( ',', $expanded_selectors );

				$expanded_selectors = array_map(
					function ( $value ) {
						return $value === ':root' ? $value : '.editor-styles-wrapper ' . $value;
					},
					$expanded_selectors
				);
				$expanded_selectors = implode( ',', $expanded_selectors );
			}

			$map[ $expanded_selectors ] = isset( $map[ $expanded_selectors ] ) ? array_merge( $map[ $expanded_selectors ], $props ) : $props;
		}
		$this->is_transformed = true;
		$this->mapping        = $map;

		return $this->mapping;
	}

	/**
	 * Get dynamic value.
	 *
	 * @param array|string $meta Prop meta.
	 *
	 * @return bool|mixed
	 */
	public function get_value( $meta ) {
		// By default the non-responsive settings are shown only on mobile media query. By using the META_DEVICE_ONLY prop we can move this to different devices.
		if ( ( ! isset( $meta[ self::META_IS_RESPONSIVE ] ) || ! $meta[ self::META_IS_RESPONSIVE ] ) && $this->get_device() !== ( isset( $meta[ self::META_DEVICE_ONLY ] ) ? $meta[ self::META_DEVICE_ONLY ] : self::MOBILE ) ) {
			return false;
		}
		$key     = is_array( $meta ) ? $meta[ self::META_KEY ] : $meta;
		$default = isset( $meta[ self::META_DEFAULT ] ) ? $meta[ self::META_DEFAULT ] : false;
		$value   = Mods::get( $key, $default );


		if ( $value === false || $value === null || $value === '' ) {
			return $default;
		}
		if ( isset( $meta[ self::META_IS_RESPONSIVE ] ) && $meta[ self::META_IS_RESPONSIVE ] ) {
			// If the value is defined as responsive and we don't have a responsive mapped value,
			// we use that value for all devices.
			if ( is_string( $value ) ) {
				$decoded_value = json_decode( $value, true );
				$value         = $decoded_value !== null ? $decoded_value : $value;
			}

			return isset( $value[ $this->get_device() ] ) ? $value[ $this->get_device() ] : ( ( is_string( $value ) || is_numeric( $value ) ) ? $value : false );
		}

		return $value;
	}

	/**
	 * Get current device.
	 *
	 * @return string Current device.
	 */
	public function get_device() {
		return ! empty( $this->device ) ? $this->device : self::MOBILE;
	}

	/**
	 * Return CSS rules.
	 *
	 * @return string CSS style.
	 */
	public function __toString() {
		$rules = '';

		$selectors = $this->transform_selectors();

		foreach ( $selectors as $selector => $props ) {
			$rules_selector = '';
			foreach ( $props as $css_prop => $meta ) {
				$value = $this->get_value( $meta );
				if ( $value === false || $value === null || $value === '' ) {
					continue;
				}
				$rules_selector .= Css_Prop::transform( $css_prop, $value, $meta, $this->get_device() );
			}
			$rules_selector = trim( $rules_selector );
			if ( empty( $rules_selector ) ) {
				continue;
			}
			$rules .= sprintf( "\n%s{ \n%s \n}\n", $selector, $rules_selector );
		}

		return $rules;
	}

}
PK      \$B    ,  core/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \oc<%  %    core/settings/mods.phpnu W+A        <?php
/**
 * Theme mods wrapper
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      17/08/2018
 *
 * @package Neve\Core
 */

namespace Neve\Core\Settings;

/**
 * Class Admin
 *
 * @package Neve\Core\Settings
 */
class Mods {

	/**
	 * Cached values.
	 *
	 * @var array Values cached.
	 */
	private static $_cached = [];
	/**
	 * No cache mode.
	 *
	 * @var bool Should we avoid cache.
	 */
	public static $no_cache = false;

	/**
	 * Get theme mod.
	 *
	 * @param string $key Key value.
	 * @param mixed  $default Default value.
	 *
	 * @return mixed Mod value.
	 */
	public static function get( $key, $default = false ) {
		$master_default = $default;
		$subkey         = null;
		if ( strpos( $key, '.' ) !== false ) {
			$key_parts      = explode( '.', $key );
			$key            = $key_parts[0];
			$subkey         = $key_parts[1];
			$master_default = false;
		}

		if ( ! isset( self::$_cached[ $key ] ) || self::$no_cache ) {
			$master_default        = $master_default === false ? self::defaults( $key ) : $master_default;
			self::$_cached[ $key ] =
				( $master_default === false ) ?
					get_theme_mod( $key ) :
					get_theme_mod( $key, $master_default );
		}

		if ( $subkey === null ) {
			return self::$_cached[ $key ];
		}
		$value = is_string( self::$_cached[ $key ] ) ? json_decode( self::$_cached[ $key ], true ) : self::$_cached[ $key ];

		return isset( $value[ $subkey ] ) ? $value[ $subkey ] : $default;
	}

	/**
	 * Forced defaults.
	 *
	 * @param string $key Key name.
	 *
	 * @return array|bool|string
	 */
	private static function defaults( $key ) {
		switch ( $key ) {
			case Config::MODS_CONTAINER_WIDTH:
				return '{ "mobile": 748, "tablet": 992, "desktop": 1170 }';
			case Config::MODS_BUTTON_PRIMARY_STYLE:
				return neve_get_button_appearance_default();
			case Config::MODS_BUTTON_SECONDARY_STYLE:
				return neve_get_button_appearance_default( 'secondary' );
			case Config::MODS_TYPEFACE_GENERAL:
				$defaults  = self::get_typography_defaults(
					[
						'line_height'    => 'neve_body_line_height',
						'letter_spacing' => 'neve_body_letter_spacing',
						'font_weight'    => 'neve_body_font_weight',
						'text_transform' => 'neve_body_text_transform',
					]
				);
				$font_size = self::to_json( 'neve_body_font_size' );
				if ( ! empty( $font_size ) ) {
					$defaults['fontSize'] = $font_size;
				}

				return $defaults;
			case Config::MODS_TYPEFACE_H1:
			case Config::MODS_TYPEFACE_H2:
			case Config::MODS_TYPEFACE_H3:
			case Config::MODS_TYPEFACE_H4:
			case Config::MODS_TYPEFACE_H5:
			case Config::MODS_TYPEFACE_H6:
				$defaults   = self::get_typography_defaults(
					[
						'line_height'    => 'neve_headings_line_height',
						'letter_spacing' => 'neve_headings_letter_spacing',
						'font_weight'    => 'neve_headings_font_weight',
						'text_transform' => 'neve_headings_text_transform',
					]
				);
				$legacy_map = [
					Config::MODS_TYPEFACE_H6 => [
						'font_size'   => 'neve_h6_font_size',
						'line_height' => 'neve_h6_line_height',
					],
					Config::MODS_TYPEFACE_H5 => [
						'font_size'   => 'neve_h5_font_size',
						'line_height' => 'neve_h5_line_height',
					],
					Config::MODS_TYPEFACE_H4 => [
						'font_size'   => 'neve_h4_font_size',
						'line_height' => 'neve_h4_line_height',
					],
					Config::MODS_TYPEFACE_H3 => [
						'font_size'   => 'neve_h3_font_size',
						'line_height' => 'neve_h3_line_height',
					],
					Config::MODS_TYPEFACE_H2 => [
						'font_size'   => 'neve_h2_font_size',
						'line_height' => 'neve_h2_line_height',
					],
					Config::MODS_TYPEFACE_H1 => [
						'font_size'   => 'neve_h1_font_size',
						'line_height' => 'neve_h1_line_height',
					],
				];

				$font_size = self::to_json( $legacy_map[ $key ]['font_size'] );
				if ( ! empty( $font_size ) ) {
					$defaults['fontSize'] = $font_size;
				}
				$line_height = self::to_json( $legacy_map[ $key ]['line_height'] );
				if ( ! empty( $line_height ) ) {
					$defaults['lineHeight'] = $line_height;
				}

				return $defaults;
		}

		return false;
	}

	/**
	 * Helper method to get defaults for typography.
	 *
	 * @param array $args Legacy mods.
	 *
	 * @return array
	 */
	private static function get_typography_defaults( $args ) {

		$line_height    = self::to_json( $args['line_height'] );
		$letter_spacing = self::get( $args['letter_spacing'] );
		$font_weight    = self::get( $args['font_weight'] );
		$text_transform = self::get( $args['text_transform'] );
		$defaults       = [];
		if ( ! empty( $line_height ) ) {
			$defaults['lineHeight'] = $line_height;
		}
		if ( ! empty( $letter_spacing ) ) {
			$defaults['letterSpacing'] = $letter_spacing;
		}
		if ( ! empty( $font_weight ) ) {
			$defaults['fontWeight'] = $font_weight;
		}
		if ( ! empty( $text_transform ) ) {
			$defaults['textTransform'] = $text_transform;
		}

		return $defaults;
	}

	/**
	 * Setter for the manager.
	 *
	 * @param string $key Key.
	 * @param mixed  $value Value.
	 */
	public static function set( $key, $value ) {
		self::$_cached[ $key ] = $value;
	}

	/**
	 * Get and transform setting to json.
	 *
	 * @param string $key Key name.
	 * @param mixed  $default Default value.
	 * @param bool   $as_array As array or Object.
	 *
	 * @return mixed
	 */
	public static function to_json( $key, $default = false, $as_array = true ) {
		return json_decode( self::get( $key, $default ), $as_array );
	}

	/**
	 * Get alternative mod default.
	 *
	 * @param string $key theme mod key.
	 *
	 * @return string|array|int|false
	 */
	public static function get_alternative_mod_default( $key ) {
		$headings_generic_setup = [
			'fontWeight'    => '700',
			'textTransform' => 'none',
			'letterSpacing' => [
				'mobile'  => 0,
				'tablet'  => 0,
				'desktop' => 0,
			],
		];
		$headings_sufix         = [
			'mobile'  => 'px',
			'tablet'  => 'px',
			'desktop' => 'px',
		];
		switch ( $key ) {
			case Config::MODS_FONT_GENERAL:
				return 'Arial, Helvetica, sans-serif';
			case Config::MODS_TYPEFACE_GENERAL:
				return [
					'fontSize'      => [
						'suffix'  => [
							'mobile'  => 'px',
							'tablet'  => 'px',
							'desktop' => 'px',
						],
						'mobile'  => 15,
						'tablet'  => 16,
						'desktop' => 16,
					],
					'lineHeight'    => [
						'mobile'  => 1.6,
						'tablet'  => 1.6,
						'desktop' => 1.7,
					],
					'letterSpacing' => [
						'mobile'  => 0,
						'tablet'  => 0,
						'desktop' => 0,
					],
					'fontWeight'    => '400',
					'textTransform' => 'none',
				];
			case Config::MODS_TYPEFACE_H1:
				return array_merge(
					$headings_generic_setup,
					array(
						'fontSize'   => [
							'mobile'  => '36',
							'tablet'  => '38',
							'desktop' => '40',
							'suffix'  => $headings_sufix,
						],
						'lineHeight' => [
							'mobile'  => 1.2,
							'tablet'  => 1.2,
							'desktop' => 1.1,
						],
					)
				);
			case Config::MODS_TYPEFACE_H2:
				return array_merge(
					$headings_generic_setup,
					array(
						'fontSize'   => [
							'mobile'  => '28',
							'tablet'  => '30',
							'desktop' => '32',
							'suffix'  => $headings_sufix,
						],
						'lineHeight' => [
							'mobile'  => 1.3,
							'tablet'  => 1.2,
							'desktop' => 1.2,
						],
					)
				);
			case Config::MODS_TYPEFACE_H3:
				return array_merge(
					$headings_generic_setup,
					array(
						'fontSize'   => [
							'mobile'  => '24',
							'tablet'  => '26',
							'desktop' => '28',
							'suffix'  => $headings_sufix,
						],
						'lineHeight' => [
							'mobile'  => 1.4,
							'tablet'  => 1.4,
							'desktop' => 1.4,
						],
					)
				);
			case Config::MODS_TYPEFACE_H4:
				return array_merge(
					$headings_generic_setup,
					array(
						'fontSize'   => [
							'mobile'  => '20',
							'tablet'  => '22',
							'desktop' => '24',
							'suffix'  => $headings_sufix,
						],
						'lineHeight' => [
							'mobile'  => 1.6,
							'tablet'  => 1.5,
							'desktop' => 1.5,
						],
					)
				);
			case Config::MODS_TYPEFACE_H5:
				return array_merge(
					$headings_generic_setup,
					array(
						'fontSize'   => [
							'mobile'  => '16',
							'tablet'  => '18',
							'desktop' => '20',
							'suffix'  => $headings_sufix,
						],
						'lineHeight' => [
							'mobile'  => 1.6,
							'tablet'  => 1.6,
							'desktop' => 1.6,
						],
					)
				);
			case Config::MODS_TYPEFACE_H6:
				return array_merge(
					$headings_generic_setup,
					array(
						'fontSize'   => [
							'mobile'  => '14',
							'tablet'  => '14',
							'desktop' => '16',
							'suffix'  => $headings_sufix,
						],
						'lineHeight' => [
							'mobile'  => 1.6,
							'tablet'  => 1.6,
							'desktop' => 1.6,
						],
					)
				);
			case Config::MODS_BUTTON_PRIMARY_PADDING:
				$device = [
					'top'    => 13,
					'right'  => 15,
					'bottom' => 13,
					'left'   => 15,
				];

				return [
					'desktop'      => $device,
					'tablet'       => $device,
					'mobile'       => $device,
					'desktop-unit' => 'px',
					'tablet-unit'  => 'px',
					'mobile-unit'  => 'px',
				];
			case Config::MODS_FORM_FIELDS_PADDING:
				return [
					'top'    => 10,
					'bottom' => 10,
					'left'   => 12,
					'right'  => 12,
					'unit'   => 'px',
				];
			case Config::MODS_FORM_FIELDS_BORDER_WIDTH:
				return [
					'top'    => 2,
					'right'  => 2,
					'left'   => 2,
					'bottom' => 2,
					'unit'   => 'px',
				];
			default:
				return false;
		}
	}
}
PK      \s=A  A    core/settings/config.phpnu W+A        <?php
/**
 * Config related constants.
 *
 * @package Neve\Core\Settings
 */

namespace Neve\Core\Settings;

/**
 * Class Admin
 *
 * @package Neve\Core\Settings
 */
class Config {
	/**
	 * Link color - deprecated.
	 *
	 * @deprecated
	 */
	const MODS_LINK_COLOR = 'neve_link_color';
	/**
	 * Link hover color - deprecated.
	 *
	 * @deprecated
	 */
	const MODS_LINK_HOVER_COLOR           = 'neve_link_hover_color';
	const MODS_GLOBAL_COLORS              = 'neve_global_colors';
	const MODS_GLOBAL_CUSTOM_COLORS       = 'neve_global_custom_colors';
	const MODS_TEXT_COLOR                 = 'neve_text_color';
	const MODS_CONTAINER_WIDTH            = 'neve_container_width';
	const MODS_SITEWIDE_CONTENT_WIDTH     = 'neve_sitewide_content_width';
	const MODS_OTHERS_CONTENT_WIDTH       = 'neve_other_pages_content_width';
	const MODS_ARCHIVE_CONTENT_WIDTH      = 'neve_blog_archive_content_width';
	const MODS_SINGLE_CONTENT_WIDTH       = 'neve_single_post_content_width';
	const MODS_SHOP_ARCHIVE_CONTENT_WIDTH = 'neve_shop_archive_content_width';
	const MODS_SHOP_SINGLE_CONTENT_WIDTH  = 'neve_single_product_content_width';
	const MODS_ADVANCED_LAYOUT_OPTIONS    = 'neve_advanced_layout_options';
	const MODS_BUTTON_PRIMARY_STYLE       = 'neve_button_appearance';
	const MODS_BUTTON_SECONDARY_STYLE     = 'neve_secondary_button_appearance';
	const MODS_BUTTON_PRIMARY_PADDING     = 'neve_button_padding';
	/**
	 * Background color - deprecated.
	 *
	 * @deprecated
	 */
	const MODS_BACKGROUND_COLOR            = 'background_color';
	const MODS_BUTTON_SECONDARY_PADDING    = 'neve_secondary_button_padding';
	const MODS_TYPEFACE_GENERAL            = 'neve_typeface_general';
	const MODS_TYPEFACE_H1                 = 'neve_h1_typeface_general';
	const MODS_TYPEFACE_H2                 = 'neve_h2_typeface_general';
	const MODS_TYPEFACE_H3                 = 'neve_h3_typeface_general';
	const MODS_TYPEFACE_H4                 = 'neve_h4_typeface_general';
	const MODS_TYPEFACE_H5                 = 'neve_h5_typeface_general';
	const MODS_TYPEFACE_H6                 = 'neve_h6_typeface_general';
	const MODS_FONT_GENERAL                = 'neve_body_font_family';
	const MODS_FONT_GENERAL_VARIANTS       = 'neve_body_font_family_variants';
	const MODS_FONT_HEADINGS               = 'neve_headings_font_family';
	const MODS_DEFAULT_CONTAINER_STYLE     = 'neve_default_container_style';
	const MODS_SINGLE_POST_CONTAINER_STYLE = 'neve_single_post_container_style';

	const MODS_BUTTON_TYPEFACE           = 'neve_button_typeface';
	const MODS_SECONDARY_BUTTON_TYPEFACE = 'neve_secondary_button_typeface';

	const MODS_TYPEFACE_ARCHIVE_POST_TITLE   = 'neve_archive_typography_post_title';
	const MODS_TYPEFACE_ARCHIVE_POST_EXCERPT = 'neve_archive_typography_post_excerpt';
	const MODS_TYPEFACE_ARCHIVE_POST_META    = 'neve_archive_typography_post_meta';

	const MODS_TYPEFACE_SINGLE_POST_TITLE         = 'neve_single_post_typography_post_title';
	const MODS_TYPEFACE_SINGLE_POST_META          = 'neve_single_post_typography_post_meta';
	const MODS_TYPEFACE_SINGLE_POST_COMMENT_TITLE = 'neve_single_post_typography_comments_title';

	const MODS_FORM_FIELDS_PADDING          = 'neve_form_fields_padding';
	const MODS_FORM_FIELDS_SPACING          = 'neve_form_fields_spacing';
	const MODS_FORM_FIELDS_BACKGROUND_COLOR = 'neve_form_fields_background_color';
	const MODS_FORM_FIELDS_BORDER_WIDTH     = 'neve_form_fields_border_width';
	const MODS_FORM_FIELDS_BORDER_RADIUS    = 'neve_form_fields_border_radius';
	const MODS_FORM_FIELDS_BORDER_COLOR     = 'neve_form_fields_border_color';
	const MODS_FORM_FIELDS_LABELS_SPACING   = 'neve_label_spacing';
	const MODS_FORM_FIELDS_TYPEFACE         = 'neve_input_typeface';
	const MODS_FORM_FIELDS_COLOR            = 'neve_input_text_color';
	const MODS_FORM_FIELDS_LABELS_TYPEFACE  = 'neve_label_typeface';

	const MODS_ARCHIVE_POST_META_AUTHOR_AVATAR_SIZE = 'neve_author_avatar_size';
	const MODS_SINGLE_POST_META_AUTHOR_AVATAR_SIZE  = 'neve_single_post_avatar_size';
	const MODS_SINGLE_POST_ELEMENTS_SPACING         = 'neve_single_post_elements_spacing';

	const MODS_CONTENT_VSPACING             = 'neve_content_vspacing';
	const MODS_SINGLE_POST_VSPACING_INHERIT = 'neve_post_inherit_vspacing';
	const MODS_SINGLE_POST_CONTENT_VSPACING = 'neve_post_content_vspacing';

	const MODS_POST_TYPE_VSPACING_INHERIT = 'inherit_vspacing';
	const MODS_POST_TYPE_VSPACING         = 'content_vspacing';

	const OPTION_LOCAL_GOOGLE_FONTS_HOSTING = 'nv_pro_enable_local_fonts';

	const MODS_TPOGRAPHY_FONT_PAIRS = 'neve_font_pairs';

	/**
	 * This is only used in a dynamic context for all allowed post types
	 */
	const MODS_CONTENT_WIDTH                = 'content_width';
	const MODS_COVER_HEIGHT                 = 'cover_height';
	const MODS_COVER_PADDING                = 'cover_padding';
	const MODS_COVER_BACKGROUND_COLOR       = 'cover_background_color';
	const MODS_COVER_OVERLAY_OPACITY        = 'cover_overlay_opacity';
	const MODS_COVER_TEXT_COLOR             = 'cover_text_color';
	const MODS_COVER_BLEND_MODE             = 'cover_blend_mode';
	const MODS_COVER_TITLE_ALIGNMENT        = 'title_alignment';
	const MODS_COVER_TITLE_POSITION         = 'title_position';
	const MODS_COVER_BOXED_TITLE_PADDING    = 'cover_title_boxed_padding';
	const MODS_COVER_BOXED_TITLE_BACKGROUND = 'cover_title_boxed_background_color';

	const MODS_POST_COMMENTS_PADDING               = 'neve_comments_boxed_padding';
	const MODS_POST_COMMENTS_BACKGROUND_COLOR      = 'neve_comments_boxed_background_color';
	const MODS_POST_COMMENTS_TEXT_COLOR            = 'neve_comments_boxed_text_color';
	const MODS_POST_COMMENTS_FORM_PADDING          = 'neve_comments_form_boxed_padding';
	const MODS_POST_COMMENTS_FORM_BACKGROUND_COLOR = 'neve_comments_form_boxed_background_color';
	const MODS_POST_COMMENTS_FORM_TEXT_COLOR       = 'neve_comments_form_boxed_text_color';

	const CSS_PROP_BORDER_COLOR               = 'border-color';
	const CSS_PROP_BACKGROUND_COLOR           = 'background-color';
	const CSS_PROP_BACKGROUND                 = 'background';
	const CSS_PROP_COLOR                      = 'color';
	const CSS_PROP_MAX_WIDTH                  = 'max-width';
	const CSS_PROP_BORDER_RADIUS_TOP_LEFT     = 'border-top-left-radius';
	const CSS_PROP_BORDER_RADIUS_TOP_RIGHT    = 'border-top-right-radius';
	const CSS_PROP_BORDER_RADIUS_BOTTOM_RIGHT = 'border-bottom-right-radius';
	const CSS_PROP_BORDER_RADIUS_BOTTOM_LEFT  = 'border-bottom-left-radius';
	const CSS_PROP_BORDER_RADIUS              = 'border-radius';
	const CSS_PROP_BORDER_WIDTH               = 'border-width';
	const CSS_PROP_BORDER                     = 'border';
	const CSS_PROP_FLEX_BASIS                 = 'flex-basis';
	const CSS_PROP_PADDING                    = 'padding';
	const CSS_PROP_PADDING_RIGHT              = 'padding-right';
	const CSS_PROP_PADDING_LEFT               = 'padding-left';
	const CSS_PROP_MARGIN                     = 'margin';
	const CSS_PROP_MARGIN_LEFT                = 'margin-left';
	const CSS_PROP_MARGIN_RIGHT               = 'margin-right';
	const CSS_PROP_MARGIN_TOP                 = 'margin-top';
	const CSS_PROP_MARGIN_BOTTOM              = 'margin-bottom';
	const CSS_PROP_RIGHT                      = 'right';
	const CSS_PROP_LEFT                       = 'left';
	const CSS_PROP_WIDTH                      = 'width';
	const CSS_PROP_HEIGHT                     = 'height';
	const CSS_PROP_MIN_HEIGHT                 = 'min-height';
	const CSS_PROP_FONT_SIZE                  = 'font-size';
	const CSS_PROP_FILL_COLOR                 = 'fill';
	const CSS_PROP_LETTER_SPACING             = 'letter-spacing';
	const CSS_PROP_LINE_HEIGHT                = 'line-height';
	const CSS_PROP_FONT_WEIGHT                = 'font-weight';
	const CSS_PROP_TEXT_TRANSFORM             = 'text-transform';
	const CSS_PROP_FONT_FAMILY                = 'font-family';
	const CSS_PROP_BOX_SHADOW                 = 'box-shadow';
	const CSS_PROP_MIX_BLEND_MODE             = 'mix-blend-mode';
	const CSS_PROP_OPACITY                    = 'opacity';
	const CSS_PROP_GRID_TEMPLATE_COLS         = 'grid-template-columns';
	const CSS_PROP_DIRECTIONAL_ONE_AXIS       = 'directional-one-axis';
	const CSS_PROP_CUSTOM_BTN_TYPE            = 'btn-type';
	const CSS_PROP_CUSTOM_FONT_WEIGHT_FAMILY  = 'btn-type';

	const CSS_SELECTOR_BTN_PRIMARY_NORMAL          = 'buttons_primary_normal';
	const CSS_SELECTOR_BTN_PRIMARY_HOVER           = 'buttons_primary_hover';
	const CSS_SELECTOR_BTN_SECONDARY_NORMAL        = 'buttons_secondary_normal';
	const CSS_SELECTOR_BTN_SECONDARY_HOVER         = 'buttons_secondary_hover';
	const CSS_SELECTOR_BTN_SECONDARY_DEFAULT       = 'buttons_secondary_default';
	const CSS_SELECTOR_BTN_SECONDARY_DEFAULT_HOVER = 'buttons_secondary_default_hover';
	const CSS_SELECTOR_BTN_PRIMARY_PADDING         = 'buttons_primary_padding';
	const CSS_SELECTOR_BTN_SECONDARY_PADDING       = 'buttons_secondary_padding';
	const CSS_SELECTOR_TYPEFACE_GENERAL            = 'typeface_general';
	const CSS_SELECTOR_TYPEFACE_H1                 = 'typeface_h1';
	const CSS_SELECTOR_TYPEFACE_H2                 = 'typeface_h2';
	const CSS_SELECTOR_TYPEFACE_H3                 = 'typeface_h3';
	const CSS_SELECTOR_TYPEFACE_H4                 = 'typeface_h4';
	const CSS_SELECTOR_TYPEFACE_H5                 = 'typeface_h5';
	const CSS_SELECTOR_TYPEFACE_H6                 = 'typeface_h6';

	const CSS_SELECTOR_ARCHIVE_POST_TITLE   = 'archive_entry_title';
	const CSS_SELECTOR_ARCHIVE_POST_EXCERPT = 'archive_entry_summary';
	const CSS_SELECTOR_ARCHIVE_POST_META    = 'archive_entry_meta_list';

	const CSS_SELECTOR_SINGLE_POST_TITLE         = 'single_post_entry_title';
	const CSS_SELECTOR_SINGLE_POST_META          = 'single_post_entry_meta_list';
	const CSS_SELECTOR_SINGLE_POST_COMMENT_TITLE = 'single_post_comment_title';

	const CSS_SELECTOR_FORM_INPUTS_WITH_SPACING = 'form_inputs_no_search';
	const CSS_SELECTOR_FORM_INPUTS              = 'form_inputs';
	const CSS_SELECTOR_FORM_INPUTS_LABELS       = 'form_labels';
	const CSS_SELECTOR_FORM_BUTTON              = 'form_buttons';
	const CSS_SELECTOR_FORM_BUTTON_HOVER        = 'form_buttons_hover';
	const CSS_SELECTOR_FORM_SEARCH_INPUTS       = 'search_form_inputs';

	const CONTENT_DEFAULT_PADDING = 30;

	/**
	 * Keys for directional values.
	 *
	 * @var string[]
	 */
	public static $directional_keys = [ 'top', 'right', 'bottom', 'left' ];

	/**
	 * Holds tag->css selector mapper.
	 *
	 * @var array Mapper.
	 */
	public static $css_selectors_map = [
		self::CSS_SELECTOR_TYPEFACE_H1                 => 'h1, .single h1.entry-title',
		self::CSS_SELECTOR_TYPEFACE_H2                 => 'h2',
		self::CSS_SELECTOR_TYPEFACE_H3                 => 'h3, .woocommerce-checkout h3',
		self::CSS_SELECTOR_TYPEFACE_H4                 => 'h4',
		self::CSS_SELECTOR_TYPEFACE_H5                 => 'h5',
		self::CSS_SELECTOR_TYPEFACE_H6                 => 'h6',
		self::CSS_SELECTOR_TYPEFACE_GENERAL            => 'body, .site-title',
		self::CSS_SELECTOR_BTN_PRIMARY_PADDING         => '.button.button-primary, .wp-block-button.is-style-primary .wp-block-button__link,  .wc-block-grid .wp-block-button .wp-block-button__link',
		self::CSS_SELECTOR_BTN_SECONDARY_PADDING       => '.button.button-secondary:not(.secondary-default), .wp-block-button.is-style-secondary .wp-block-button__link',
		self::CSS_SELECTOR_BTN_PRIMARY_NORMAL          => '.button.button-primary,
				button, input[type=button],
				.btn, input[type="submit"],
				/* Buttons in navigation */
				ul[id^="nv-primary-navigation"] li.button.button-primary > a,
				.menu li.button.button-primary > a,  .wp-block-button.is-style-primary .wp-block-button__link,  .wc-block-grid .wp-block-button .wp-block-button__link',
		self::CSS_SELECTOR_BTN_PRIMARY_HOVER           => '.button.button-primary:hover,
				ul[id^="nv-primary-navigation"] li.button.button-primary > a:hover,
				.menu li.button.button-primary > a:hover, .wp-block-button.is-style-primary .wp-block-button__link:hover,  .wc-block-grid .wp-block-button .wp-block-button__link:hover',
		self::CSS_SELECTOR_BTN_SECONDARY_NORMAL        => '.button.button-secondary:not(.secondary-default),  .wp-block-button.is-style-secondary .wp-block-button__link',
		self::CSS_SELECTOR_BTN_SECONDARY_HOVER         => '.button.button-secondary:not(.secondary-default):hover,  .wp-block-button.is-style-secondary .wp-block-button__link:hover',
		self::CSS_SELECTOR_BTN_SECONDARY_DEFAULT       => '.button.button-secondary.secondary-default',
		self::CSS_SELECTOR_BTN_SECONDARY_DEFAULT_HOVER => '.button.button-secondary.secondary-default:hover',
		self::CSS_SELECTOR_ARCHIVE_POST_TITLE          => '.blog .blog-entry-title, .archive .blog-entry-title',
		self::CSS_SELECTOR_ARCHIVE_POST_EXCERPT        => '.blog .entry-summary, .archive .entry-summary, .blog .post-pages-links',
		self::CSS_SELECTOR_ARCHIVE_POST_META           => '.blog .nv-meta-list li, .archive .nv-meta-list li',
		self::CSS_SELECTOR_SINGLE_POST_TITLE           => '.single h1.entry-title',
		self::CSS_SELECTOR_SINGLE_POST_META            => '.single .nv-meta-list li',
		self::CSS_SELECTOR_SINGLE_POST_COMMENT_TITLE   => '.single .comment-reply-title',
		self::CSS_SELECTOR_FORM_INPUTS_WITH_SPACING    => 'form:not([role="search"]):not(.woocommerce-cart-form):not(.woocommerce-ordering):not(.cart) input:read-write:not(#coupon_code), form textarea, form select, .widget select',
		self::CSS_SELECTOR_FORM_INPUTS                 => 'form input:read-write, form textarea, form select, form select option, form.wp-block-search input.wp-block-search__input, .widget select',
		self::CSS_SELECTOR_FORM_INPUTS_LABELS          => 'form label, .wpforms-container .wpforms-field-label',
		self::CSS_SELECTOR_FORM_BUTTON                 => 'form input[type="submit"], form button:not(.search-submit)[type="submit"], form *[value*="ubmit"], #comments input[type="submit"]',
		self::CSS_SELECTOR_FORM_BUTTON_HOVER           => 'form input[type="submit"]:hover, form button:not(.search-submit)[type="submit"]:hover, form *[value*="ubmit"]:hover, #comments input[type="submit"]:hover',
		self::CSS_SELECTOR_FORM_SEARCH_INPUTS          => 'form.search-form input:read-write',
	];

	/**
	 * The default Font pairings available for all instances.
	 *
	 * Default preview size for fonts is 24px for heading and 16px for body.
	 *
	 * @var array[]
	 */
	public static $typography_default_pairs = [
		[
			'headingFont' => [
				'font'        => 'Inter',
				'fontSource'  => 'Google',
				'previewSize' => '25px',
			],
			'bodyFont'    => [
				'font'       => 'Inter',
				'fontSource' => 'Google',
			],
		],
		[
			'headingFont' => [
				'font'        => 'Playfair Display',
				'fontSource'  => 'Google',
				'previewSize' => '27px',
			],
			'bodyFont'    => [
				'font'        => 'Source Sans Pro',
				'fontSource'  => 'Google',
				'previewSize' => '18px',
			],
		],
		[
			'headingFont' => [
				'font'       => 'Montserrat',
				'fontSource' => 'Google',
			],
			'bodyFont'    => [
				'font'       => 'Open Sans',
				'fontSource' => 'Google',
			],
		],
		[
			'headingFont' => [
				'font'       => 'Nunito',
				'fontSource' => 'Google',
			],
			'bodyFont'    => [
				'font'       => 'Lora',
				'fontSource' => 'Google',
			],
		],
		[
			'headingFont' => [
				'font'       => 'Lato',
				'fontSource' => 'Google',
			],
			'bodyFont'    => [
				'font'       => 'Karla',
				'fontSource' => 'Google',
			],
		],
		[
			'headingFont' => [
				'font'        => 'Outfit',
				'fontSource'  => 'Google',
				'previewSize' => '25px',
			],
			'bodyFont'    => [
				'font'       => 'Spline Sans',
				'fontSource' => 'Google',
			],
		],
		[
			'headingFont' => [
				'font'        => 'Lora',
				'fontSource'  => 'Google',
				'previewSize' => '25px',
			],
			'bodyFont'    => [
				'font'       => 'Ubuntu',
				'fontSource' => 'Google',
			],
		],
		[
			'headingFont' => [
				'font'        => 'Prata',
				'fontSource'  => 'Google',
				'previewSize' => '25px',
			],
			'bodyFont'    => [
				'font'        => 'Hanken Grotesk',
				'fontSource'  => 'Google',
				'previewSize' => '17px',
			],
		],
		[
			'headingFont' => [
				'font'        => 'Albert Sans',
				'fontSource'  => 'Google',
				'previewSize' => '25px',
			],
			'bodyFont'    => [
				'font'        => 'Albert Sans',
				'fontSource'  => 'Google',
				'previewSize' => '17px',
			],
		],
		[
			'headingFont' => [
				'font'        => 'Fraunces',
				'fontSource'  => 'Google',
				'previewSize' => '25px',
			],
			'bodyFont'    => [
				'font'        => 'Hanken Grotesk',
				'fontSource'  => 'Google',
				'previewSize' => '17px',
			],
		],
	];
}
PK      \$B    5  core/settings/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \^b?  ?    core/settings/mods_migrator.phpnu W+A        <?php
/**
 * Handles mod migration upon import.
 *
 * This class will receive an array of theme mods (key-value) and migrate legacy things.
 *
 * @since 3.0.0
 *
 * @package Neve\Core
 */

namespace Neve\Core\Settings;

use Neve\Core\Builder_Migrator;

/**
 * Class Mods_Migrator
 */
class Mods_Migrator {

	const LEGACY_HEADINGS = [
		Config::MODS_TYPEFACE_H6 => [
			'neve_h6_font_size'   => 'fontSize',
			'neve_h6_line_height' => 'lineHeight',
		],
		Config::MODS_TYPEFACE_H5 => [
			'neve_h5_font_size'   => 'fontSize',
			'neve_h5_line_height' => 'lineHeight',
		],
		Config::MODS_TYPEFACE_H4 => [
			'neve_h4_font_size'   => 'fontSize',
			'neve_h4_line_height' => 'lineHeight',
		],
		Config::MODS_TYPEFACE_H3 => [
			'neve_h3_font_size'   => 'fontSize',
			'neve_h3_line_height' => 'lineHeight',
		],
		Config::MODS_TYPEFACE_H2 => [
			'neve_h2_font_size'   => 'fontSize',
			'neve_h2_line_height' => 'lineHeight',
		],
		Config::MODS_TYPEFACE_H1 => [
			'neve_h1_font_size'   => 'fontSize',
			'neve_h1_line_height' => 'lineHeight',
		],
	];

	/**
	 * Builders
	 *
	 * @var string[]
	 */
	private $builder_map = [ 'hfg_header_layout', 'hfg_footer_layout', 'hfg_page_header_layout' ];

	/**
	 * Mods array.
	 *
	 * @var array
	 */
	private $mods = [];

	/**
	 * Mods to migrate.
	 *
	 * @var array
	 */
	private $mods_to_migrate_to = [
		Config::MODS_TYPEFACE_GENERAL,
		Config::MODS_TYPEFACE_H1,
		Config::MODS_TYPEFACE_H2,
		Config::MODS_TYPEFACE_H3,
		Config::MODS_TYPEFACE_H4,
		Config::MODS_TYPEFACE_H5,
		Config::MODS_TYPEFACE_H6,
	];

	/**
	 * Mods_Migrator constructor.
	 *
	 * @param array $incoming_mods the incoming mods from import.
	 */
	public function __construct( $incoming_mods ) {
		$this->mods = $incoming_mods;
	}

	/**
	 * Get migrated mods.
	 *
	 * @return array
	 */
	public function get_migrated_mods() {
		$this->migrate_mods();
		$this->attempt_builders_migration();
		$this->unset_unused();

		return $this->mods;
	}

	/**
	 * Migrate mods.
	 *
	 * @return void
	 */
	private function migrate_mods() {
		foreach ( $this->mods_to_migrate_to as $new_mod_key ) {
			// If the new mod is already in use, we don't need to migrate anything.
			if ( isset( $this->mods[ $new_mod_key ] ) ) {
				continue;
			}

			$next_value = $this->transform_to_new_value( $new_mod_key );

			if ( empty( $next_value ) ) {
				continue;
			}

			$this->mods[ $new_mod_key ] = $next_value;
		}
	}

	/**
	 * Attempt to migrate builders.
	 *
	 * @return void
	 */
	private function attempt_builders_migration() {
		$hfg_migrator = new Builder_Migrator();

		foreach ( $this->builder_map as $builder ) {
			$new_builder_mod = $builder . '_v2';
			if ( isset( $this->mods[ $new_builder_mod ] ) ) {
				continue;
			}

			if ( ! isset( $this->mods[ $builder ] ) ) {
				continue;
			}

			$new_value = $hfg_migrator->get_new_builder_value_from_old( json_decode( $this->mods[ $builder ], true ) );

			if ( $new_value === false ) {
				continue;
			}

			$this->mods[ $new_builder_mod ] = wp_json_encode( $new_value );
			unset( $this->mods[ $builder ] );
		}
	}

	/**
	 * Get the array of old values that will match the new values.
	 *
	 * @param string $new_mod_key the new mod key.
	 *
	 * @return array
	 */
	private function transform_to_new_value( $new_mod_key ) {
		$defaults = Mods::get_alternative_mod_default( $new_mod_key );

		switch ( $new_mod_key ) {
			case Config::MODS_TYPEFACE_GENERAL:
				$old_value = $this->get_composed_value(
					[
						'neve_body_line_height'    => 'lineHeight',
						'neve_body_letter_spacing' => 'letterSpacing',
						'neve_body_font_weight'    => 'fontWeight',
						'neve_body_text_transform' => 'textTransform',
						'neve_body_font_size'      => 'fontSize',
					]
				);

				return array_merge( $defaults, $old_value );

			case Config::MODS_TYPEFACE_H1:
			case Config::MODS_TYPEFACE_H2:
			case Config::MODS_TYPEFACE_H3:
			case Config::MODS_TYPEFACE_H4:
			case Config::MODS_TYPEFACE_H5:
			case Config::MODS_TYPEFACE_H6:
				$partial = [
					'neve_headings_line_height'    => 'lineHeight',
					'neve_headings_letter_spacing' => 'letterSpacing',
					'neve_headings_font_weight'    => 'fontWeight',
					'neve_headings_text_transform' => 'textTransform',
				];

				$keys      = array_merge( $partial, self::LEGACY_HEADINGS[ $new_mod_key ] );
				$old_value = $this->get_composed_value( $keys );

				return array_merge( $defaults, $old_value );
		}

		return array();
	}

	/**
	 * Get the old values for the new mod.
	 *
	 * @param array $args args array [$old_mod => $key_on_new_mod].
	 *
	 * @return array
	 */
	private function get_composed_value( $args ) {
		$new_values = [];
		foreach ( $args as $old_mod => $new_key ) {
			if ( ! isset( $this->mods[ $old_mod ] ) ) {
				continue;
			}

			$final_value = $this->mods[ $old_mod ];
			// If the value is either font-size or line-height we should migrate it from previous json format.
			if ( in_array( $new_key, [ 'fontSize', 'lineHeight' ] ) ) {
				$final_value = json_decode( $final_value, true );
			}

			$new_values[ $new_key ] = $final_value;

			unset( $this->mods[ $old_mod ] );
		}

		return $new_values;
	}

	/**
	 * Unset unused theme mods.
	 *
	 * @return void
	 */
	private function unset_unused() {
		$to_remove = array_merge( $this->builder_map, [ 'background_color' ] );

		foreach ( $to_remove as $slug ) {
			if ( isset( $this->mods[ $slug ] ) ) {
				unset( $this->mods[ $slug ] );
			}
		}
	}
}
PK      \®6-  -    core/builder_migrator.phpnu W+A        <?php
/**
 * Builder Migrator
 *
 * @since 3.0.0
 *
 * @package Neve\Core
 */

namespace Neve\Core;

/**
 * Class Admin
 *
 * @package Neve\Core
 */
class Builder_Migrator {

	/**
	 * Known builders.
	 *
	 * @var array
	 */
	private $builders = [
		'header'      => [ 'top', 'main', 'bottom', 'sidebar' ],
		'footer'      => [ 'top', 'main', 'bottom' ],
		'page_header' => [ 'top', 'bottom' ],
	];

	/**
	 * Current device migrating.
	 *
	 * @var string|null
	 */
	private static $current_device = null;

	/**
	 * Current builder migrating.
	 *
	 * @var string|null
	 */
	private static $current_builder = null;

	/**
	 * Current Row Migrating
	 *
	 * @var string|null
	 */
	private static $current_row = null;

	/**
	 * Row slots.
	 *
	 * @var string[]
	 */
	private $row_slots = [ 'left', 'c-left', 'center', 'c-right', 'right' ];

	/**
	 * Migrate row for columned builder.
	 *
	 * @param array $old_row old row values.
	 * @param array $next_row empty row array.
	 *
	 * @return array
	 */
	private function migrate_columns_row( $old_row, $next_row ) {
		$items_no        = count( $old_row );
		$columns_setting = 'hfg_' . self::$current_builder . '_layout_' . self::$current_row . '_columns_number';

		if ( $items_no > 5 ) {
			$items_no = 5;
		}

		set_theme_mod( $columns_setting, $items_no );

		foreach ( $old_row as $index => $item ) {
			$slot = $this->row_slots[ $index ];

			$next_row[ $slot ][] = [ 'id' => $item['id'] ];
		}

		return $next_row;
	}

	/**
	 * Get the component horizontal alignment on currently migrating device.
	 *
	 * @param string $component_id the component id.
	 *
	 * @return string
	 */
	private function get_component_alignment( $component_id ) {
		$default = [
			'desktop' => 'left',
			'mobile'  => 'left',
			'tablet'  => 'left',
		];

		if ( strpos( $component_id, 'primary-menu' ) !== false ) {
			$default['desktop'] = 'right';
		}

		$alignment = get_theme_mod( $component_id . '_component_align', $default );


		if ( ! isset( $alignment[ self::$current_device ] ) ) {
			return 'left';
		}

		$allowed = [ 'left', 'right', 'center' ];

		if ( ! in_array( $alignment[ self::$current_device ], $allowed ) ) {
			return 'left';
		}

		return $alignment[ self::$current_device ];
	}

	/**
	 * Migrate row for fluid builder.
	 *
	 * @param array $next_row empty row array.
	 * @param array $old_items old row values.
	 *
	 * @return array
	 */
	private function migrate_fluid_row( $next_row, $old_items ) {
		$items_no = count( $old_items );

		// We have only one item.
		if ( $items_no === 1 ) {
			$alignment        = $this->get_component_alignment( $old_items[0]['id'] );
			$width            = $old_items[0]['width'];
			$start_position   = $old_items[0]['x'];
			$next_row_content = [ 'id' => $old_items[0]['id'] ];

			// Item is at start of row. Slot it according to the alignment.
			// In the previous version, if the item was alone and started at the beginning of the row, it spanned the whole width.
			if ( $start_position === 0 ) {
				$next_row[ $alignment ][] = $next_row_content;

				return $next_row;
			}

			// Item is not at start or end. It spans until the end of the row.
			if ( $start_position > 0 ) {
				if ( $alignment === 'right' ) {
					$next_row['right'][] = $next_row_content;

					return $next_row;
				}

				$next_row['center'][] = $next_row_content;

				return $next_row;
			}

			// Item is at end of row. Slot it to the right.
			if ( $width + $start_position === 12 ) {
				$next_row['right'][] = $next_row_content;

				return $next_row;
			}

			// Item is not at start or end. Slot it at center.
			$next_row['center'][] = $next_row_content;

			return $next_row;
		}

		// Check if items fill the whole row so we can know if it has gaps.
		$filled_columns = array_reduce(
			$old_items,
			function ( $columns, $item ) {
				$columns += $item['width'];

				return $columns;
			}
		);
		$no_gaps        = $filled_columns === 12;

		// There are no gaps so we will only slot left and right;
		if ( $no_gaps ) {
			foreach ( $old_items as $item ) {
				$width             = $item['width'];
				$start_position    = $item['x'];
				$next_item_content = [ 'id' => $item['id'] ];
				$alignment         = $this->get_component_alignment( $item['id'] );

				// Item touches right. Slot it to the right.
				if ( $start_position + $width === 12 ) {
					$next_row['right'][] = $next_item_content;
					continue;
				}

				// Item is before center. Slot it to the left only if it isn't aligned to the right.
				if ( $start_position < 5 ) {
					if ( $alignment === 'right' ) {
						$next_row['right'][] = $next_item_content;
						continue;
					}

					$next_row['left'][] = $next_item_content;
					continue;
				}

				// Item is after center. Slot it to the right.
				if ( $start_position >= 5 ) {
					$next_row['right'][] = $next_item_content;
					continue;
				}
			}

			return $next_row;
		}

		$previous_item = null;
		$previous_slot = null;

		foreach ( $old_items as $index => $item ) {
			$width          = $item['width'];
			$start_position = $item['x'];
			$item_value     = [ 'id' => $item['id'] ];

			// Item starts at the most left point. Slot it to the left.
			if ( $start_position === 0 ) {
				$next_row['left'][] = $item_value;
				$previous_item      = $item;
				$previous_slot      = 'left';
				continue;
			}

			// If we already had an item, check if it touches the new one.
			if ( $previous_item && $previous_slot ) {
				// Slot it inside the same slot if it does and there is no gap.
				if ( $previous_item['x'] + $previous_item['width'] === $start_position ) {
					$next_row[ $previous_slot ][] = $item_value;
					$previous_item                = $item;
					continue;
				}

				// If item is at the end slot it right.
				// Accounts for previous but where items were extending the whole row when last.
				if ( $item['x'] + $item['width'] === 12 || $index === count( $old_items ) - 1 ) {
					$next_row['right'][] = $item_value;
					$previous_item       = $item;
					$previous_slot       = 'right';
					continue;
				}

				// Move to center slot if there is a gap and previous slotted was left.
				if ( $previous_slot === 'left' ) {
					$next_row['center'][] = $item_value;
					$previous_item        = $item;
					$previous_slot        = 'center';
					continue;
				}

				// All other cases fall inside the right slot.
				$next_row['right'][] = $item_value;
				$previous_item       = $item;
				$previous_slot       = 'right';
				continue;
			}

			// Item touches end. Slot it to the right.
			if ( $start_position + $width === 12 ) {
				$next_row['right'][] = $item_value;
				$previous_item       = $item;
				$previous_slot       = 'right';
				continue;
			}

			// Item is first but doesn't start at the left most point. Slot it to the center.
			if ( $index === 0 ) {
				$next_row['center'][] = $item_value;
				$previous_item        = $item;
				$previous_slot        = 'center';
				continue;
			}

			// Does not touch sides. Is not nearby previous.
			$next_row['center'][] = $item_value;
			$previous_slot        = 'center';
			$previous_item        = $item;
		}

		return $next_row;
	}

	/**
	 * Migrate single row of the builder.
	 *
	 * @param array $old_items old items inside the row.
	 *
	 * @return array
	 */
	private function migrate_single_row( $old_items ) {
		$next_row_value = array_fill_keys( $this->row_slots, [] );

		if ( count( $old_items ) === 0 ) {
			return $next_row_value;
		}

		if ( self::$current_builder === 'footer' ) {
			return $this->migrate_columns_row( $old_items, $next_row_value );
		}

		return $this->migrate_fluid_row( $next_row_value, $old_items );
	}

	/**
	 * Migrate single builder value.
	 *
	 * @return bool
	 */
	private function migrate_single_builder() {
		$old_value = $this->get_builder_value( self::$current_builder );

		if ( empty( $old_value ) ) {
			return true;
		}

		$old_value = json_decode( $old_value, true );

		$new_value = $this->get_new_builder_value_from_old( $old_value );

		set_theme_mod( $this->get_new_builder_mod_slug( self::$current_builder ), wp_json_encode( $new_value ) );

		return true;
	}

	/**
	 * Migrate old builder value to new format.
	 *
	 * @param array $old_value old builder value.
	 *
	 * @return array|boolean
	 */
	public function get_new_builder_value_from_old( $old_value ) {
		if ( ! is_array( $old_value ) ) {
			return false;
		}

		$empty_row = array_fill_keys( $this->row_slots, [] );

		$new_value = [];

		foreach ( $old_value as $device => $rows ) {

			self::$current_device = $device;

			// Setup the builders for each device.
			$new_value[ $device ] = array_fill_keys( $this->builders[ self::$current_builder ], $empty_row );

			// Sidebar is available only on mobile. We should remove it on other devices.
			if ( $device !== 'mobile' && isset( $new_value[ $device ]['sidebar'] ) ) {
				unset( $new_value[ $device ]['sidebar'] );
			}

			foreach ( $rows as $row_slug => $items ) {
				self::$current_row = $row_slug;
				// Sidebar row is treated differently.
				if ( $row_slug === 'sidebar' ) {
					// Make sure we have an empty array for the sidebar.
					$new_value[ $device ]['sidebar'] = [];
					// Push items inside the sidebar.
					foreach ( $items as $item ) {
						$new_value[ $device ]['sidebar'][] = [ 'id' => $item['id'] ];
					}
					continue;
				}

				// Proceed with normal migration for each row.
				$new_value[ $device ][ $row_slug ] = $this->migrate_single_row( $items );
			}
			self::$current_row = null;
		}

		self::$current_device = null;

		return $new_value;
	}

	/**
	 * Get the new theme mod slug for the specified builder.
	 *
	 * @param string $builder builder slug.
	 *
	 * @return string
	 */
	private function get_new_builder_mod_slug( $builder ) {
		return 'hfg_' . $builder . '_layout_v2';
	}

	/**
	 * Get individual builder value.
	 *
	 * @param string $builder builder slug.
	 *
	 * @return string
	 */
	private function get_builder_value( $builder ) {
		return get_theme_mod( 'hfg_' . $builder . '_layout' );
	}

	/**
	 * Main run function of the migrator.
	 *
	 * @return bool
	 */
	public function run() {
		$expected_builders = array_keys( $this->builders );

		foreach ( $expected_builders as $builder ) {
			// Attempt migration for every builder.
			self::$current_builder = $builder;
			$success               = $this->migrate_single_builder();

			// If it fails for one, it failed.
			if ( ! $success ) {
				return false;
			}
		}
		self::$current_builder = null;

		$success = $this->migrate_conditional_headers();

		if ( ! $success ) {
			return false;
		}

		// Migration success.
		return true;
	}

	/**
	 * Migrate conditional headers
	 *
	 * @return boolean
	 */
	private function migrate_conditional_headers() {
		if ( ! class_exists( '\Neve_Pro\Admin\Custom_Layouts_Cpt' ) ) {
			return true;
		}

		if ( ! method_exists( '\Neve_Pro\Admin\Custom_Layouts_Cpt', 'get_conditional_headers' ) ) {
			return true;
		}

		$headers = \Neve_Pro\Admin\Custom_Layouts_Cpt::get_conditional_headers();

		self::$current_builder = 'header';

		foreach ( $headers as $cpt_id => $header ) {
			$decoded = json_decode( $header, true );

			if ( ! is_array( $decoded ) || empty( $decoded ) ) {
				continue;
			}
			if ( ! isset( $decoded['hfg_header_layout'] ) ) {
				continue;
			}


			$migrated_value          = $this->get_new_builder_value_from_old( $decoded['hfg_header_layout'] );
			$new_mod_key             = $this->get_new_builder_mod_slug( 'header' );
			$decoded[ $new_mod_key ] = $migrated_value;

			update_post_meta( $cpt_id, 'theme-mods', wp_json_encode( $decoded ) );
			delete_transient( 'custom_layouts_post_map_v2' );
		}

		self::$current_builder = null;

		return true;
	}
}
PK      \L%      core/traits/theme_mods.phpnu W+A        <?php
/**
 * Theme Mods trait.
 *
 * @package Neve\Core
 */

namespace Neve\Core\Traits;

/**
 * Theme_Mods trait
 */
trait Theme_Mods {
	/**
	 * Returns the mod key for the granular font family control.
	 *
	 * @param  string $heading_id like h1 or h2 or etc.
	 * @return string   theme mod key which stores font family of the heading.
	 */
	private function get_mod_key_heading_fontfamily( $heading_id ) {
		return 'neve_headings_' . $heading_id . '_font_family';
	}
}
PK      \$B    3  core/traits/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \      core/dynamic_css.phpnu W+A        <?php


namespace Neve\Core;


use Neve\Core\Styles\Frontend;
use Neve\Core\Styles\Generator;
use Neve\Core\Styles\Gutenberg;
use Neve\Core\Settings\Mods;
use Neve\Core\Settings\Config;

class Dynamic_Css {
	const FRONTEND_HANDLE = 'neve-style';
	const GUTENBERG_HANDLE = 'neve-gutenberg-style';
	/**
	 * Generator object.
	 *
	 * @var Generator CSS generator object.
	 */
	private $generator = null;
	const EDITOR_ACTION = 'enqueue_block_editor_assets';
	const FRONTEND_ACTION = 'wp_enqueue_scripts';

	/**
	 * Register actions.
	 */
	public function init() {
		add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue' ), 100 );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue' ), 100 );
		add_action( 'customize_controls_enqueue_scripts', [ $this, 'add_customize_vars_tag' ] );

		if ( is_customize_preview() ) {
			add_action( 'wp_head', [ $this, 'add_customize_vars_tag' ] );
		}
	}

	public function legacy_style() {
		$classes = apply_filters( 'neve_filter_inline_style_classes', [], 'neve-generated-style' );
		$mobile_css = '';
		$desktop_css = '';
		$tablet_css = '';
		foreach ( $classes as $class ) {
			$object = new $class();
			$object->init();
			$mobile_css .= $object->get_style( 'mobile' );
			$desktop_css .= $object->get_style( 'desktop' );
			$tablet_css .= $object->get_style( 'tablet' );
		}
		$all_css = $mobile_css;
		if ( ! empty( $tablet_css ) ) {
			$all_css .= sprintf( '@media(min-width: 576px){ %s }', $tablet_css );
		}
		if ( ! empty( $desktop_css ) ) {
			$all_css .= sprintf( '@media(min-width: 960px){ %s }', $desktop_css );
		}
		add_filter( 'neve_dynamic_style_output', function ( $css ) use ( $all_css ) {
			return $all_css . $css;
		} );
	}

	/**
	 * Load frontend style.
	 */
	public function enqueue() {
		$is_for_gutenberg = (current_action() === self::EDITOR_ACTION);
		if ( ! class_exists( ' Neve_Pro\Core\Generic_Style', true ) ) {
			$this->legacy_style();
		}

		$this->generator = $is_for_gutenberg ? new Gutenberg() : new Frontend();
		$_subscribers = $this->generator->get();

		$_subscribers = array_merge( $_subscribers, apply_filters( 'neve_style_subscribers', [] ) );

		$this->generator->set( $_subscribers );

		$style = apply_filters( 'neve_dynamic_style_output', $this->generator->generate(), $is_for_gutenberg ? 'gutenberg' : 'frontend' );

		$style .= self::get_root_css();

		$style = self::minify_css( $style );

		wp_add_inline_style( $is_for_gutenberg ? self::GUTENBERG_HANDLE : self::FRONTEND_HANDLE, $style );
	}

	/**
	 * Basic CSS minification.
	 *
	 * @param string $css
	 *
	 * @return string
	 */
	public static function minify_css( $css ) {
		return preg_replace( '/\s+/', ' ', $css );
	}

	/**
	 * Adds customizer CSS tag for CSS vars.
	 */
	public function add_customize_vars_tag() {
		wp_register_style( 'nv-css-vars', false );
		wp_enqueue_style( 'nv-css-vars' );

		$css = ':root{' . $this->get_css_vars() . '}';
		$css .= apply_filters( 'neve_after_css_root', $css );
		wp_add_inline_style( 'nv-css-vars', self::minify_css($css ) );

		/**
		 * Custom colors needs a separate style to prevent overwriting the main color for the pallets.
		 */
		wp_register_style( 'nv-custom-color-vars', false );
		wp_enqueue_style( 'nv-custom-color-vars' );
		$custom_colors = self::get_global_custom_colors();
		$colors_css = '';
		foreach ( $custom_colors as $slug => $color ) {
			$colors_css .= '--' . $slug . ':' . $color . ';';
		}
		$css = ':root{' . esc_html( $colors_css ) . '}';
		wp_add_inline_style( 'nv-custom-color-vars', self::minify_css($css ) );
	}

	/**
	 * Get root style (css variables)
	 *
	 * @return string
	 */
	public static function get_root_css() {
		$css = ':root{';

		$css .= self::get_css_vars();
		$css .= self::get_fallback_font();

		/**
		 * We already have the style in customizer from add_customize_vars_tag method, no need to add it again.
		 * We do need it on frontend.
		 */
		if ( ! is_customize_preview() ) {
			$custom_colors = self::get_global_custom_colors();
			foreach ($custom_colors as $slug => $color) {
				$css .= '--' . $slug . ':' . $color . ';';
			}
		}

		$css .= '}';

		$css .= apply_filters( 'neve_after_css_root', $css );

		return self::minify_css( $css );
	}

	/**
	 * Get the fallback font.
	 *
	 * @return string
	 */
	public static function get_fallback_font() {
		$fallback = get_theme_mod( 'neve_fallback_font_family', 'Arial, Helvetica, sans-serif' );

		return '--nv-fallback-ff:' . $fallback . ';';
	}

	/**
	 * Get color values of the custom global colors
	 *
	 * @return string[] colors values HEX, RGB etc.
	 */
	private static function get_global_custom_colors() {
		$colors = [];

		foreach( Mods::get( Config::MODS_GLOBAL_CUSTOM_COLORS, [] ) as $slug=>$args ) {
			$colors[$slug] = $args['val'];
		}

		return $colors;
	}

	/**
	 * Returns CSS vars style.
	 *
	 * @return string
	 */
	private static function get_css_vars() {
		$global_colors = get_theme_mod( 'neve_global_colors', neve_get_global_colors_default( true ) );

		if ( empty( $global_colors ) ) {
			return '';
		}

		if ( ! isset( $global_colors[ 'activePalette' ] ) ) {
			return '';
		}

		$active = $global_colors[ 'activePalette' ];

		if ( ! isset( $global_colors[ 'palettes' ][ $active ] ) ) {
			return '';
		}

		$palette = $global_colors[ 'palettes' ][ $active ];

		if ( ! isset( $palette[ 'colors' ] ) ) {
			return '';
		}

		$css = '';

		foreach ( $palette[ 'colors' ] as $slug => $color ) {
			$css .= '--' . $slug . ':' . $color . ';';
		}

		return $css;
	}
}
PK      \İ<Z  <Z    core/admin.phpnu W+A        <?php
/**
 * Admin functionality
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      17/08/2018
 *
 * @package Neve\Core
 */

namespace Neve\Core;

use Neve\Admin\Dashboard\Plugin_Helper;
use Neve\Core\Settings\Mods_Migrator;
use Neve\Core\Theme_Info;

/**
 * Class Admin
 *
 * @package Neve\Core
 */
class Admin {
	use Theme_Info;

	/**
	 * Dismiss notice key.
	 *
	 * @var string
	 */
	private $dismiss_notice_key = 'neve_notice_dismissed';

	/**
	 * Theme Details
	 *
	 * @var \WP_Theme
	 */
	private $theme_args;

	/**
	 * Admin constructor.
	 */
	public function __construct() {
		$this->set_props();
		add_action(
			'admin_init',
			function () {
				if ( get_option( 'themeisle_ob_plugins_installed' ) !== 'yes' ) {
					return;
				}
				update_option( 'themeisle_blocks_settings_redirect', false );
				delete_transient( 'wpforms_activation_redirect' );
				update_option( 'themeisle_ob_plugins_installed', 'no' );
			},
			0
		);
		add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_gutenberg_scripts' ] );
		add_filter( 'themeisle_sdk_hide_dashboard_widget', '__return_true' );

		if ( get_option( $this->dismiss_notice_key ) !== 'yes' ) {
			add_action( 'admin_notices', [ $this, 'admin_notice' ], 0 );
			add_action( 'wp_ajax_neve_dismiss_welcome_notice', [ $this, 'remove_notice' ] );
		}

		// load upsell dismiss only if neve pro is not active or license is invalid.
		if ( ! $this->has_valid_addons() ) {
			add_action(
				'wp_ajax_neve_dismiss_customizer_upsell_notice',
				[
					'Neve\Customizer\Options\Upsells',
					'remove_customizer_upsell_notice',
				]
			);
		}

		add_action( 'admin_menu', [ $this, 'remove_background_submenu' ], 110 );
		add_action( 'after_switch_theme', [ $this, 'get_previous_theme' ] );

		add_filter( 'all_plugins', array( $this, 'change_plugin_names' ) );

		$this->auto_update_skin_and_builder();

		add_action( 'after_switch_theme', array( $this, 'migrate_options' ) );

		add_filter( 'ti_tpc_theme_mods_pre_import', [ $this, 'migrate_theme_mods_for_new_skin' ] );

		add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
		add_filter( 'neve_pro_react_controls_localization', [ $this, 'adapt_conditional_headers' ] );


		if ( ! defined( 'NEVE_PRO_VERSION' ) ) {
			$offer = new Limited_Offers();
			if ( $offer->can_show_dashboard_banner() && $offer->is_active() ) {
				$offer->load_dashboard_hooks();
			}
		}
	}

	/**
	 * Automatic upgrade from legacy builder and skin on init.
	 *
	 * @return void
	 */
	private function auto_update_skin_and_builder() {
		// If already on new skin bail.
		if ( get_theme_mod( 'neve_new_skin' ) === 'new' || neve_was_auto_migrated_to_new() ) {
			return;
		}
		set_theme_mod( 'neve_auto_migrated_to_new_skin', true );

		$this->run_skin_and_builder_switches();

		$migrator = new Builder_Migrator();
		$response = $migrator->run();

		if ( $response === true ) {
			set_theme_mod( 'neve_migrated_builders', true );
			set_theme_mod( 'neve_new_skin', 'new' );
		}
	}

	/**
	 * Get data specific to TPC plugin.
	 *
	 * @return array
	 */
	private function get_tpc_plugin_data() {
		$plugin_helper = new Plugin_Helper();
		$slug          = 'templates-patterns-collection';
		$tpc_version   = $plugin_helper->get_plugin_version( $slug, false );

		$tpc_plugin_data['nonce']      = wp_create_nonce( 'wp_rest' );
		$tpc_plugin_data['slug']       = $slug;
		$tpc_plugin_data['cta']        = $plugin_helper->get_plugin_state( $slug );
		$tpc_plugin_data['path']       = $plugin_helper->get_plugin_path( $slug );
		$tpc_plugin_data['activate']   = $plugin_helper->get_plugin_action_link( $slug );
		$tpc_plugin_data['deactivate'] = $plugin_helper->get_plugin_action_link( $slug, 'deactivate' );
		$tpc_plugin_data['version']    = $tpc_version !== false ? $tpc_version : '';
		$tpc_plugin_data['adminURL']   = admin_url( 'admin.php?page=tiob-starter-sites' );
		$tpc_plugin_data['pluginsURL'] = esc_url( admin_url( 'plugins.php' ) );
		$tpc_plugin_data['ajaxURL']    = esc_url( admin_url( 'admin-ajax.php' ) );
		$tpc_plugin_data['ajaxNonce']  = esc_attr( wp_create_nonce( 'remove_notice_confirmation' ) );
		$tpc_plugin_data['canInstall'] = current_user_can( 'install_plugins' );

		return $tpc_plugin_data;
	}

	/**
	 * Maybe register the script required for the welcome notice.
	 * The script has a component that replaces the "Try one of our ready to use Starter Sites" button.
	 * The button installs/activates and/or dismisses the notice as required.
	 */
	private function maybe_register_notice_script_starter_sites() {
		if ( get_option( $this->dismiss_notice_key, 'no' ) === 'yes' ) {
			return;
		}
		$screen = get_current_screen();
		if ( empty( $screen ) ) {
			return;
		}
		if ( ! in_array( $screen->id, [ 'dashboard', 'themes' ], true ) ) {
			return;
		}

		$bundle_path  = get_template_directory_uri() . '/assets/apps/starter-sites/build/';
		$dependencies = ( include get_template_directory() . '/assets/apps/starter-sites/build/notice.asset.php' );
		wp_register_script( 'neve-ss-notice', $bundle_path . 'notice.js', $dependencies['dependencies'], $dependencies['version'], true );

		wp_localize_script( 'neve-ss-notice', 'tpcPluginData', $this->get_tpc_plugin_data() );
		wp_enqueue_script( 'neve-ss-notice' );
		wp_set_script_translations( 'neve-ss-notice', 'neve' );
	}

	/**
	 * Register the script for react components.
	 */
	public function register_react_components() {
		$this->maybe_register_notice_script_starter_sites();

		$deps = include trailingslashit( NEVE_MAIN_DIR ) . 'assets/apps/components/build/components.asset.php';

		wp_register_script( 'neve-components', trailingslashit( NEVE_ASSETS_URL ) . 'apps/components/build/components.js', $deps['dependencies'], $deps['version'], false );
		wp_localize_script(
			'neve-components',
			'nvComponents',
			[
				'shouldUseColorPickerFix' => (int) ( ! neve_is_using_wp_version( '5.8' ) ),
				'customizerURL'           => esc_url( admin_url( 'customize.php' ) ),
			]
		);
		wp_set_script_translations( 'neve-components', 'neve' );
		wp_register_style( 'neve-components', trailingslashit( NEVE_ASSETS_URL ) . 'apps/components/build/style-components.css', [ 'wp-components' ], $deps['version'] );
		wp_add_inline_style( 'neve-components', Dynamic_Css::get_root_css() );
	}

	/**
	 * Switch to the new 3.0 features.
	 *
	 * @return void
	 *
	 * @since 3.0.0
	 */
	public function run_skin_and_builder_switches() {
		$flag = 'neve_ran_migrations';

		if ( get_theme_mod( $flag ) === true ) {
			return;
		}

		set_theme_mod( $flag, true );

		if ( neve_had_old_hfb() ) {
			set_theme_mod( 'neve_migrated_builders', false );
		}

		$all_mods = get_theme_mods();

		$mods = [
			'hfg_header_layout',
			'hfg_footer_layout',
			'neve_blog_archive_layout',
			'neve_headings_font_family',
			'neve_body_font_family',
			'neve_global_colors',
			'neve_button_appearance',
			'neve_secondary_button_appearance',
			'neve_typeface_general',
			'neve_form_fields_padding',
			'neve_default_sidebar_layout',
			'neve_advanced_layout_options',
		];

		$should_switch = false;
		foreach ( $mods as $mod_to_check ) {
			if ( isset( $all_mods[ $mod_to_check ] ) ) {
				$should_switch = true;
				break;
			}
		}

		if ( ! $should_switch ) {
			return;
		}

		set_theme_mod( 'neve_new_skin', 'old' );
		set_theme_mod( 'neve_had_old_skin', true );
	}

	/**
	 * Filter out old HFG values if the new builder is active.
	 *
	 * @param array $theme_mods the theme mods array.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function migrate_theme_mods_for_new_skin( $theme_mods ) {
		if ( ! neve_is_new_skin() ) {
			return $theme_mods;
		}
		$migrator = new Mods_Migrator( $theme_mods );

		return $migrator->get_migrated_mods();
	}

	/**
	 * Filter localization data to adapt to the new builder.
	 *
	 * @param array $array localization array.
	 *
	 * @return array
	 */
	public function adapt_conditional_headers( $array ) {
		if ( ! neve_is_new_builder() ) {
			return $array;
		}

		if ( isset( $array['headerControls'] ) ) {
			$array['headerControls'][] = 'hfg_header_layout_v2';
		}

		$array['currentValues'] = [ 'hfg_header_layout_v2' => json_decode( get_theme_mod( 'hfg_header_layout_v2', wp_json_encode( neve_hfg_header_settings() ) ), true ) ];

		return $array;
	}

	/**
	 * Register Rest Routes.
	 */
	public function register_rest_routes() {
		register_rest_route(
			'nv/v1/dashboard',
			'/plugin-state/(?P<slug>[a-z0-9-]+)',
			[
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_plugin_state' ],
				'permission_callback' => function() {
					return ( current_user_can( 'install_plugins' ) && current_user_can( 'activate_plugins' ) );
				},
				'args'                => [
					'slug' => [
						'sanitize_callback' => 'sanitize_key',
					],
				],
			]
		);
	}

	/**
	 * Get any plugin's state.
	 *
	 * @param  \WP_REST_Request $request Request details.
	 * @return \WP_REST_Request|\WP_Error
	 */
	public function get_plugin_state( \WP_REST_Request $request ) {
		$slug = $request->get_param( 'slug' );

		$state = ( new Plugin_Helper() )->get_plugin_state( $slug );

		return rest_ensure_response(
			[
				'slug'  => $slug,
				'state' => $state,
			]
		);
	}

	/**
	 * Drop `Background` submenu item.
	 */
	public function remove_background_submenu() {
		global $submenu;

		if ( ! isset( $submenu['themes.php'] ) ) {
			return false;
		}

		foreach ( $submenu['themes.php'] as $index => $submenu_args ) {
			foreach ( $submenu_args as $arg_index => $arg ) {
				if ( preg_match( '/customize\.php.+autofocus%5Bcontrol%5D=background_image/', $arg ) === 1 ) {
					unset( $submenu['themes.php'][ $index ] );
				}
			}
		}
	}

	/**
	 * Setup Class Properties
	 */
	public function set_props() {
		$this->theme_args = wp_get_theme();
	}

	/**
	 * Get notice screenshot based on previous theme.
	 *
	 * @return string Image url.
	 */
	private function get_notice_picture() {
		return get_template_directory_uri() . '/assets/img/sites-list.jpg';
	}

	/**
	 * Add notice.
	 */
	public function admin_notice() {
		if ( apply_filters( 'neve_disable_starter_sites_admin_notice', false ) === true ) {
			return;
		}
		if ( defined( 'TI_ONBOARDING_DISABLED' ) && TI_ONBOARDING_DISABLED === true ) {
			return;
		}

		$current_screen = get_current_screen();
		if ( $current_screen->id !== 'dashboard' && $current_screen->id !== 'themes' ) {
			return;
		}

		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
			return;
		}

		if ( is_network_admin() ) {
			return;
		}

		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		// to check under the gutenberg v5.5.0
		if ( function_exists( 'is_gutenberg_page' ) && is_gutenberg_page() ) {
			return;
		}

		// to check above the gutenberg v5.5.0 (is_gutenberg_page is deprecated with )
		if ( method_exists( $current_screen, 'is_block_editor' ) ) {
			if ( $current_screen->is_block_editor() ) {
				return;
			}
		}

		/**
		 * Backwards compatibility.
		 */
		global $current_user;
		$user_id          = $current_user->ID;
		$dismissed_notice = get_user_meta( $user_id, $this->dismiss_notice_key, true );

		if ( $dismissed_notice === 'dismissed' ) {
			update_option( $this->dismiss_notice_key, 'yes' );
		}

		if ( get_option( $this->dismiss_notice_key, 'no' ) === 'yes' ) {
			return;
		}

		// Let's dismiss the notice if the user sees it for more than 1 week.
		$activated_time = get_option( 'neve_install' );

		if ( ! empty( $activated_time ) ) {
			if ( time() - intval( $activated_time ) > WEEK_IN_SECONDS ) {
				update_option( $this->dismiss_notice_key, 'yes' );

				return;
			}
		}

		$style = '
			.ti-about-notice{
				position: relative;
			}

			.ti-about-notice .notice-dismiss{
				position: absolute;
				z-index: 10;
			    top: 10px;
			    right: 10px;
			    padding: 10px 15px 10px 21px;
			    font-size: 13px;
			    line-height: 1.23076923;
			    text-decoration: none;
			}

			.ti-about-notice .notice-dismiss:before{
			    position: absolute;
			    top: 8px;
			    left: 0;
			    transition: all .1s ease-in-out;
			    background: none;
			}

			.ti-about-notice .notice-dismiss:hover{
				color: #00a0d2;
			}
		';

		echo '<style>' . wp_kses_post( $style ) . '</style>';
		$this->dismiss_script();
		echo '<div class="nv-welcome-notice updated notice ti-about-notice">';
		echo '<div class="notice-dismiss"></div>';
		$this->welcome_notice_content();
		echo '</div>';
	}

	/**
	 * Render welcome notice content
	 */
	public function welcome_notice_content() {
		$name       = apply_filters( 'ti_wl_theme_name', $this->theme_args->__get( 'Name' ) );
		$template   = $this->theme_args->get( 'Template' );
		$slug       = $this->theme_args->__get( 'stylesheet' );
		$theme_page = ! empty( $template ) ? $template . '-welcome' : $slug . '-welcome';

		$notice_template = '
			<div class="nv-notice-wrapper">
			%1$s
			<hr/>
				<div class="nv-notice-column-container">
					<div class="nv-notice-column nv-notice-image">%2$s</div>
					<div class="nv-notice-column nv-notice-starter-sites">%3$s</div>
					<div class="nv-notice-column nv-notice-documentation">%4$s</div>
				</div>
			</div>
			<style>%5$s</style>';

		/* translators: 1 - notice title, 2 - notice message */
		$notice_header = sprintf(
			'<h2>%1$s</h2><p class="about-description">%2$s</p></hr>',
			esc_html__( 'Congratulations!', 'neve' ),
			sprintf(
				/* translators: %s - theme name */
				esc_html__( '%s is now installed and ready to use. We\'ve assembled some links to get you started.', 'neve' ),
				$name
			)
		);
		$ob_btn_link = admin_url( 'admin.php?page=' . $theme_page . '&onboarding=yes#starter-sites' );
		if ( defined( 'TIOB_PATH' ) ) {
			$url_path = 'admin.php?page=tiob-starter-sites';
			if ( current_user_can( 'install_plugins' ) ) {
				$url_path .= '&onboarding=yes';
			}
			$ob_btn_link = admin_url( $url_path );
		}
		$ob_btn = sprintf(
		/* translators: 1 - onboarding url, 2 - button text */
			'<a href="%1$s" class="button button-primary button-hero install-now" >%2$s</a>',
			esc_url( $ob_btn_link ),
			sprintf( apply_filters( 'ti_onboarding_neve_start_site_cta', esc_html__( 'Try one of our ready to use Starter Sites', 'neve' ) ) )
		);
		$ob_return_dashboard = sprintf(
		/* translators: 1 - button text */
			'<a href="' . esc_url( admin_url() ) . '" class=" ti-return-dashboard  button button-secondary button-hero install-now" ><span>%1$s</span></a>',
			__( 'Return to your dashboard', 'neve' )
		);
		$options_page_btn = sprintf(
		/* translators: 1 - options page url, 2 - button text */
			'<a href="%1$s" class="options-page-btn">%2$s</a>',
			esc_url( admin_url( 'admin.php?page=' . $theme_page ) ),
			esc_html__( 'or go to the theme settings', 'neve' )
		);
		$notice_picture    = sprintf(
			'<picture>
					<source srcset="about:blank" media="(max-width: 1024px)">
					<img src="%1$s"/>
				</picture>',
			esc_url( $this->get_notice_picture() )
		);
		$notice_sites_list = sprintf(
			'<div><h3><span class="dashicons dashicons-images-alt2"></span> %1$s</h3><p>%2$s</p><p>%3$s</p></div><div> <p id="neve-ss-install">%4$s</p><p>%5$s</p> </div>',
			__( 'Sites Library', 'neve' ),
			// translators: %s - Theme name
				sprintf( esc_html__( '%s now comes with a sites library with various designs to pick from. Visit our collection of demos that are constantly being added.', 'neve' ), $name ),
			esc_html( __( 'Install the template patterns plugin to get started.', 'neve' ) ),
			$ob_btn,
			$options_page_btn
		);
		$notice_documentation = sprintf(
			'<div><h3><span class="dashicons dashicons-format-aside"></span> %1$s</h3><p>%2$s</p><a target="_blank" rel="external noopener noreferrer" href="%3$s"><span class="screen-reader-text">%4$s</span><svg xmlns="http://www.w3.org/2000/svg" focusable="false" role="img" viewBox="0 0 512 512" width="12" height="12" style="margin-right: 5px;"><path fill="currentColor" d="M432 320H400a16 16 0 0 0-16 16V448H64V128H208a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16H48A48 48 0 0 0 0 112V464a48 48 0 0 0 48 48H400a48 48 0 0 0 48-48V336A16 16 0 0 0 432 320ZM488 0h-128c-21.4 0-32 25.9-17 41l35.7 35.7L135 320.4a24 24 0 0 0 0 34L157.7 377a24 24 0 0 0 34 0L435.3 133.3 471 169c15 15 41 4.5 41-17V24A24 24 0 0 0 488 0Z"/></svg>%5$s</a></div><div> <p>%6$s</p></div>',
			__( 'Documentation', 'neve' ),
			// translators: %s - Theme name
				sprintf( esc_html__( 'Need more details? Please check our full documentation for detailed information on how to use %s.', 'neve' ), $name ),
			'https://docs.themeisle.com/article/946-neve-doc',
			esc_html__( '(opens in a new tab)', 'neve' ),
			esc_html__( 'Read full documentation', 'neve' ),
			$ob_return_dashboard
		);
		$style = '
		.nv-notice-wrapper h2{
			margin: 0;
			font-size: 21px;
			font-weight: 400;
			line-height: 1.2;
		}
		.nv-notice-wrapper p.about-description{
			color: #72777c;
			font-size: 16px;
			margin: 0;
			padding:0px;
		}
		.nv-notice-wrapper{
			padding: 23px 10px 0;
			max-width: 1500px;
		}
		.nv-notice-wrapper hr {
			margin: 20px -23px 0;
			border-top: 1px solid #f3f4f5;
			border-bottom: none;
		}
		.nv-notice-column-container h3{
			margin: 17px 0 0;
			font-size: 16px;
			line-height: 1.4;
		}
		.nv-notice-column-container p {
			color: #72777c;
		}
		.nv-notice-text p.ti-return-dashboard {
			margin-top: 30px;
	}
		.nv-notice-column-container .nv-notice-column{
			 padding-right: 40px;
		}
		.nv-notice-column-container img{
			margin-top: 23px;
			width: calc(100% - 40px);
			border: 1px solid #f3f4f5;
		}
		.nv-notice-column-container {
			display: -ms-grid;
			display: grid;
			-ms-grid-columns: 24% 32% 32%;
			grid-template-columns: 24% 32% 32%;
			margin-bottom: 13px;
		}
		.nv-notice-column-container a.button.button-hero.button-secondary,
		.nv-notice-column-container a.button.button-hero.button-primary{
			margin:0px;
		}
		.nv-notice-column-container .nv-notice-column:not(.nv-notice-image) {
			display: -ms-grid;
			display: grid;
			-ms-grid-rows: auto 100px;
			grid-template-rows: auto 100px;
		}
		@media screen and (max-width: 1280px) {
			.nv-notice-wrapper .nv-notice-column-container {
				-ms-grid-columns: 50% 50%;
				grid-template-columns: 50% 50%;
			}
			.nv-notice-column-container a.button.button-hero.button-secondary,
			.nv-notice-column-container a.button.button-hero.button-primary{
				padding:6px 18px;
			}
			.nv-notice-wrapper .nv-notice-image {
				display: none;
			}
		}
		@media screen and (max-width: 870px) {

			.nv-notice-wrapper .nv-notice-column-container {
				-ms-grid-columns: 100%;
				grid-template-columns: 100%;
			}
			.nv-notice-column-container a.button.button-hero.button-primary{
				padding:12px 36px;
			}
		}
		@-webkit-keyframes spin {
			from {
				transform: rotate(0deg);
			}
			to {
				transform: rotate(360deg);
			}
		}
		#neve-ss-install button.is-loading {
			color: #828282 !important;
		}
		#neve-ss-install button.is-loading .dashicon {
			color: #646D82;
			animation-name: spin;
			animation-duration: 2000ms;
			animation-iteration-count: infinite;
			animation-timing-function: linear;
		}
		';

		echo sprintf(
			$notice_template, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			$notice_header, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			$notice_picture, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			$notice_sites_list, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			$notice_documentation, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			$style // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		);
	}

	/**
	 * Load site import module.
	 */
	public function load_site_import() {
		if ( class_exists( '\TIOB\Main' ) ) {
			\TIOB\Main::instance();
		}
	}

	/**
	 * Enqueue gutenberg scripts.
	 */
	public function enqueue_gutenberg_scripts() {
		$screen = get_current_screen();
		// if is_block_editor is `true` we should allow the Gutenberg styles to load eg. the new widgets page.
		if ( ! post_type_supports( $screen->post_type, 'editor' ) && $screen->is_block_editor !== true ) {
			return;
		}
		wp_enqueue_script(
			'neve-gutenberg-script',
			NEVE_ASSETS_URL . 'js/build/all/gutenberg.js',
			array( 'wp-blocks', 'wp-dom' ),
			NEVE_VERSION,
			true
		);

		$path = 'gutenberg-editor-style';

		wp_enqueue_style( 'neve-gutenberg-style', NEVE_ASSETS_URL . 'css/' . $path . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.css', array(), NEVE_VERSION );
	}

	/**
	 * Dismiss notice JS
	 */
	private function dismiss_script() {
		?>
		<script type="text/javascript">
			function handleNoticeActions($) {
				var actions = $('.nv-welcome-notice').find('.notice-dismiss, .ti-return-dashboard, .options-page-btn')
				$.each(actions, function (index, actionButton) {
					$(actionButton).on('click', function (e) {
						e.preventDefault()
						var redirect = $(this).attr('href')
						$.post(
							'<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>',
							{
								nonce: '<?php echo esc_attr( wp_create_nonce( 'remove_notice_confirmation' ) ); ?>',
								action: 'neve_dismiss_welcome_notice',
								success: function () {
									if (typeof redirect !== 'undefined' && window.location.href !== redirect) {
										window.location = redirect
										return false
									}
									$('.nv-welcome-notice').fadeOut()
								}
							}
						)
					})
				})
			}

			jQuery(document).ready(function () {
				handleNoticeActions(jQuery)
			})
		</script>
		<?php
	}

	/**
	 * Memorize the previous theme to later display the import template for it.
	 */
	public function get_previous_theme() {
		$previous_theme = strtolower( get_option( 'theme_switched' ) );
		set_theme_mod( 'ti_prev_theme', $previous_theme );
	}

	/**
	 * Remove notice;
	 */
	public function remove_notice() {
		if ( ! isset( $_POST['nonce'] ) ) {
			return;
		}
		if ( ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'remove_notice_confirmation' ) ) {
			return;
		}
		update_option( $this->dismiss_notice_key, 'yes' );
		wp_die();
	}

	/**
	 * Change Orbit Fox and Otter plugin names to make clear where they are from.
	 */
	public function change_plugin_names( $plugins ) {
		if ( array_key_exists( 'themeisle-companion/themeisle-companion.php', $plugins ) ) {
			$plugins['themeisle-companion/themeisle-companion.php']['Name'] = 'Orbit Fox Companion by Neve theme';
		}
		if ( array_key_exists( 'otter-pro/otter-pro.php', $plugins ) ) {
			$plugins['otter-pro/otter-pro.php']['Description'] = $plugins['otter-pro/otter-pro.php']['Description'] . ' It is part of Block Editor Booster from Neve.';
		}

		return $plugins;
	}

	/**
	 * Import neve options when switching to a child theme.
	 */
	public function migrate_options() {
		$old_theme = strtolower( get_option( 'theme_switched' ) );
		if ( 'neve' !== $old_theme ) {
			return;
		}

		/* import Neve options */
		$neve_mods = get_option( 'theme_mods_neve' );

		if ( ! empty( $neve_mods ) ) {

			foreach ( $neve_mods as $neve_mod_k => $neve_mod_v ) {
				set_theme_mod( $neve_mod_k, $neve_mod_v );
			}
		}
	}
}
PK      \ެ      core/theme_info.phpnu W+A        <?php
/**
 * Theme Info trait.
 *
 * @package Neve\Core
 */

namespace Neve\Core;

/**
 * Theme_Info trait
 */
trait Theme_Info {
	/**
	 * Check validity of addons plugin.
	 *
	 * @return bool
	 */
	private function has_valid_addons() {
		if ( ! defined( 'NEVE_PRO_BASEFILE' ) ) {
			return false;
		}

		$option_name = basename( dirname( NEVE_PRO_BASEFILE ) );
		$option_name = str_replace( '-', '_', strtolower( trim( $option_name ) ) );
		$status      = get_option( $option_name . '_license_data' );

		if ( ! $status ) {
			return false;
		}

		if ( ! isset( $status->license ) ) {
			return false;
		}

		if ( $status->license === 'not_active' || $status->license === 'invalid' ) {
			return false;
		}

		return true;
	}
}
PK      \2"o      core/supported_post_types.phpnu W+A        <?php
/**
 * The class are used to get supported post types for the specific context.
 * 
 * @package Neve\Core
 */
namespace Neve\Core;

/**
 * Supported_Post_Types.
 */
class Supported_Post_Types {
	const SUPPORTED_CONTEXTS = array( 'block_editor' );

	/**
	 * 
	 * Stores post types.
	 * 
	 * @var array
	 */
	public static $maps = [];
	
	/**
	 * Get supported post types
	 *
	 * @param  string $context can be block_editor for now.
	 * @return array|false
	 */
	public static function get( $context ) {
		if ( ! in_array( $context, self::SUPPORTED_CONTEXTS, true ) ) {
			return false;
		}

		if ( ! array_key_exists( $context, self::$maps ) ) {
			$get_func = 'get_' . $context;

			self::$maps[ $context ] = apply_filters( 'neve_post_type_supported_list', self::$get_func(), $context );
		}

		return self::$maps[ $context ];
	}

	/**
	 * Get supported post types for the 'block_editor' context.
	 *
	 * @return array
	 */
	public static function get_block_editor() {
		return array( 'post', 'page' );
	}
}
PK      \7>      core/core_loader.phpnu W+A        <?php
/**
 * Neve Features Factory
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      17/08/2018
 *
 * @package Neve\Core
 */

namespace Neve\Core;

use Neve\Core\Settings\Mods;

/**
 * The core entry class.
 *
 * @package Neve\Core
 */
class Core_Loader {
	/**
	 * Features that will be loaded.
	 *
	 * @access   protected
	 * @var array $features Features that will be loaded.
	 */
	protected $features = array();

	/**
	 * Define the core functionality of the theme.
	 *
	 * @access public
	 */
	public function __construct() {
		add_action( 'after_switch_theme', [ $this, 'check_new_user' ] );
		add_action( 'themeisle_ob_after_xml_import', [ $this, 'update_content_import_flag' ] );

		$this->define_hooks();
		$this->define_modules();
		$this->load_modules();
	}

	/**
	 * Update content import flag.
	 */
	public function update_content_import_flag() {
		update_option( 'neve_imported_demo', 'yes' );
	}

	/**
	 * Checks that the user is new.
	 */
	public function check_new_user() {
		$new = get_option( 'neve_new_user' );
		if ( $new === 'yes' ) {
			return;
		}

		$install_time = get_option( 'neve_install' );
		$now          = get_option( 'neve_user_check_time' );

		if ( empty( $now ) ) {
			$now = time();
			update_option( 'neve_user_check_time', $now );
		}

		if ( empty( $install_time ) ) {
			return;
		}

		if ( ( $now - $install_time ) <= 60 ) {
			update_option( 'neve_new_user', 'yes' );

			return;
		}

		update_option( 'neve_new_user', 'no' );
	}

	/**
	 * Define the features that will be loaded.
	 */
	private function define_modules() {

		$features = array(
			'Customizer\Loader',
			'Views\Tweaks',
			'Views\Font_Manager',
			'Views\Top_Bar',
			'Views\Header',
			'Views\Template_Parts',
			'Views\Page_Header',
			'Views\Post_Layout',
			'Views\Page_Layout',
			'Views\Cover_Header',
			'Views\Product_Layout',
			'Views\Content_None',
			'Views\Content_404',
			'Views\Breadcrumbs',

			'Views\Layouts\Layout_Container',
			'Views\Layouts\Layout_Sidebar',

			'Views\Partials\Post_Meta',
			'Views\Partials\Excerpt',
			'Views\Partials\Comments',

			'Views\Pluggable\Pagination',
			'Views\Pluggable\Masonry',
			'Views\Pluggable\Metabox_Settings',

			'Core\Dynamic_Css',

			'Compatibility\Generic',
			'Compatibility\WooCommerce',
			'Compatibility\Elementor',
			'Compatibility\Header_Footer_Elementor',
			'Compatibility\Amp',
			'Compatibility\Header_Footer_Beaver',
			'Compatibility\Beaver',
			'Compatibility\Lifter',
			'Compatibility\Patterns',
			'Compatibility\PWA',
			'Compatibility\PPOM',
			'Compatibility\Web_Stories',
			'Compatibility\Easy_Digital_Downloads',
			'Compatibility\WPML',

			'Admin\Metabox\Manager',
			'Admin\Troubleshoot\Main',
			'Admin\Dashboard\Main',
			'Admin\Hooks_Upsells',
		);

		if ( $this->is_fse_child_theme() ) {
			$features[] = 'Compatibility\Fse';
		}

		$this->features = apply_filters( 'neve_filter_main_modules', $features );
	}

	/**
	 * Check Features and register them.
	 *
	 * @access  private
	 */
	private function load_modules() {
		$factory = new Factory( $this->features );
		$factory->load_modules();
	}

	/**
	 * Register all of the hooks related to the functionality
	 * of the theme setup.
	 *
	 * @access   private
	 */
	private function define_hooks() {
		// Avoid mods cache on customizer preview.
		if ( is_customize_preview() ) {
			Mods::$no_cache = true;
		}
		$admin = new Admin();
		add_action( 'init', array( $admin, 'load_site_import' ), 20 );
		add_action( 'admin_enqueue_scripts', array( $admin, 'register_react_components' ), 0 );
		add_action( 'ti-about-after-sidebar-content', array( $admin, 'render_logger_toggle' ) );

		$key_lite = str_replace( '-', '_', basename( get_template_directory() ) );
		add_filter(
			$key_lite . '_logger_data',
			function () {
				return [ 'mods' => array_filter( get_theme_mods() ) ];
			}
		);
		$front_end = new Front_End();
		add_action( 'wp_enqueue_scripts', array( $front_end, 'enqueue_scripts' ) );
		add_action( 'after_setup_theme', array( $front_end, 'setup_theme' ) );
		add_action( 'widgets_init', array( $front_end, 'register_sidebars' ) );
	}

	/**
	 * Check if we're on a child theme, and it enables FSE.
	 *
	 * @return bool
	 */
	private function is_fse_child_theme() {
		if ( ! is_child_theme() ) {
			return false;
		}

		$theme_json = get_stylesheet_directory() . '/theme.json';

		if ( ! file_exists( $theme_json ) ) {
			return false;
		}

		if ( ! defined( 'NEVE_FSE_MODE' ) ) {
			return false;
		}

		if ( NEVE_FSE_MODE !== true ) {
			return false;
		}

		return true;
	}
}
PK      \t  t    core/factory.phpnu W+A        <?php
/**
 * Features Factory
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      17/08/2018
 *
 * @package Neve\Core
 */

namespace Neve\Core;

/**
 * The class responsible for instantiating new Neve features.
 *
 * @package    Neve\Core
 * @author     Themeisle <friends@themeisle.com>
 */
class Factory {
	/**
	 * Modules
	 *
	 * @var array
	 */
	private $modules;

	/**
	 * The modules namespace.
	 *
	 * @var string
	 */
	private $namespace;

	/**
	 * Factory constructor.
	 *
	 * @param array  $modules   the modules that will be loaded.
	 * @param string $namespace the modules namespace.
	 */
	public function __construct( $modules, $namespace = '\\Neve\\' ) {
		if ( ! is_array( $modules ) || empty( $modules ) ) {
			return;
		}
		$this->namespace = $namespace;
		$this->modules   = $modules;
	}

	/**
	 * Actually load the modules.
	 */
	public function load_modules() {
		foreach ( $this->modules as $module_name ) {
			$module = $this->build( $module_name );
			if ( $module !== null ) {
				$module->init();
			}
		}
	}

	/**
	 * The build method for creating a new Neve module class.
	 *
	 * @since   1.0.0
	 * @access  public
	 *
	 * @param   string $class The name of the feature to instantiate.
	 *
	 * @return  object|null
	 */
	public function build( $class ) {
		$full_class_name = $this->namespace . $class;
		return new $full_class_name();
	}
}
PK      \$B    '  elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \\i      views/inline/base_inline.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      29/08/2018
 *
 * @package Neve\Views\Inline
 */

namespace Neve\Views\Inline;

/**
 * Class Base_Inline
 *
 * @deprecated Functionality replaced with the subscriber mechanism, we keep it only for compatibility with the pro version.
 * @package Neve\Views\Inline
 */
abstract class Base_Inline {
	/**
	 * Mobile style (default).
	 *
	 * @var array
	 */
	private $style = array(
		'mobile'  => '',
		'tablet'  => '',
		'desktop' => '',
	);

	/**
	 * Base_Inline constructor.
	 */
	final public function __construct() {

		$this->init();
	}

	/**
	 * Do all actions necessary.
	 *
	 * @return mixed
	 */
	abstract public function init();

	/**
	 * Add single style.
	 *
	 * @param array  $styles      styles.
	 * @param string $selectors   css selectors.
	 * @param string $media_query media query.
	 */
	final protected function add_style( $styles, $selectors, $media_query = 'mobile' ) {
		if ( ! in_array( $media_query, array( 'mobile', 'tablet', 'desktop' ), true ) ) {
			return;
		}

		if ( empty( $styles ) || empty( $selectors ) ) {
			return;
		}

		$use_style = false;

		foreach ( $styles as $style ) {
			/* Allow 0 values through, but don't allow empty style or undefined. */
			if ( ! isset( $style['value'] ) || ( empty( $style['value'] ) && ( $style['value'] !== 0 && $style['value'] !== '0' ) ) ) {
				continue;
			}
			$use_style = true;
		}

		if ( $use_style === false ) {
			return;
		}

		$css = $selectors . '{';
		foreach ( $styles as $style ) {
			if ( isset( $style['suffix'] ) && is_array( $style['suffix'] ) ) {
				$style['suffix'] = $style['suffix'][ $media_query ];
			}
			$css .= $this->add_styles( $style );
		}
		$css                         .= '}';
		$this->style[ $media_query ] .= $css;
	}

	/**
	 * Add responsive style.
	 *
	 * @param array  $styles    styles.
	 * @param string $selectors css selectors.
	 */
	final protected function add_responsive_style( $styles, $selectors ) {
		$media_queries = array( 'mobile', 'tablet', 'desktop' );
		foreach ( $media_queries as $media_query ) {
			$settings = $styles;
			foreach ( $settings as $index => $setting ) {
				if ( ! isset( $setting['value'] ) || ! isset( $setting['value'][ $media_query ] ) ) {
					continue;
				}
				$settings[ $index ]['value'] = $setting['value'][ $media_query ];
			}
			$this->add_style( $settings, $selectors, $media_query );
		}
	}

	/**
	 * Add color.
	 *
	 * Example parameters:
	 *    array(
	 *      'border-top-color-desktop' => array(
	 *          'css_prop'    => 'border-top-color',
	 *          'selectors'   => '#nv-primary-navigation .sub-menu',
	 *          'media-query' => 'desktop',
	 *      )
	 *  ), $color );
	 *
	 * @param array  $args  parameters.
	 * @param string $value value.
	 */
	final protected function add_color( $args, $value ) {
		$default = array(
			'selectors'   => '',
			'css_prop'    => '',
			'media_query' => 'mobile',
			'value'       => $value ? $value : null,
			'suffix'      => '',
			'prefix'      => '',
		);
		foreach ( $args as $style ) {
			$style = wp_parse_args( $style, $default );
			$setup = array(
				array(
					'css_prop' => esc_attr( $style['css_prop'] ),
					'value'    => esc_attr( $style['prefix'] . $value ),
					'suffix'   => esc_attr( $style['suffix'] ),
				),
			);
			$this->add_style( $setup, $style['selectors'], $style['media_query'] );
		}
	}

	/**
	 * Add styles.
	 *
	 * @param array $style [css_prop, value].
	 *
	 * @return string
	 */
	private function add_styles( $style ) {
		if ( ! isset( $style['css_prop'] ) || ! isset( $style['value'] ) || ( empty( $style['value'] ) && $style['value'] !== 0 && $style['value'] !== '0' ) ) {
			return '';
		}
		$suffix = isset( $style['suffix'] ) ? $style['suffix'] : '';


		if (
			in_array(
				$style['css_prop'],
				array(
					'font-family',
					'content',
				),
				true
			) &&
			! in_array( $style['value'], neve_get_standard_fonts(), true ) ) {
			return esc_attr( $style['css_prop'] ) . ':"' . esc_attr( $style['value'] ) . '"' . esc_attr( $suffix ) . ';';
		}

		return esc_attr( $style['css_prop'] ) . ':' . esc_attr( $style['value'] ) . esc_attr( $suffix ) . ';';
	}

	/**
	 * Get the style.
	 *
	 * @param string $context ['mobile','desktop','tablet'].
	 *
	 * @return string
	 */
	final public function get_style( $context ) {
		$allowed_contexts = array( 'mobile', 'desktop', 'tablet' );
		if ( ! in_array( $context, $allowed_contexts, true ) ) {
			return '';
		}
		if ( ! array_key_exists( $context, $this->style ) ) {
			return '';
		}

		return $this->style[ $context ];
	}
}
PK      \      views/cover_header.phpnu W+A        <?php
/**
 * Cover header view.
 *
 * @package Neve\Views
 */

namespace Neve\Views;

use Neve\Customizer\Defaults\Single_Post;

/**
 * Class Cover_Header
 *
 * @package Neve\Views
 */
class Cover_Header extends Base_View {
	use Single_Post;

	/**
	 * Init function
	 */
	public function init() {
		add_action( 'neve_after_header_wrapper_hook', [ $this, 'render_cover_header' ] );
	}

	/**
	 * Render the cover layout on single post.
	 */
	public function render_cover_header() {
		list( $context, $allowed_context ) = $this->get_cpt_context();
		if ( ! in_array( $context, $allowed_context, true ) || ! $this->is_valid_context( $context ) ) {
			return;
		}

		$header_layout = get_theme_mod( 'neve_' . $context . '_header_layout', 'normal' );
		if ( $header_layout !== 'cover' ) {
			return;
		}

		$hide_thumbnail = get_theme_mod( 'neve_' . $context . '_cover_hide_thumbnail', false );
		$post_thumbnail = get_the_post_thumbnail_url();
		$cover_style    = '';
		if ( $hide_thumbnail === false && ! empty( $post_thumbnail ) ) {
			$cover_style = 'background-image:url(' . esc_url( $post_thumbnail ) . ');';
		}

		$container_mode          = get_theme_mod( 'neve_' . $context . '_cover_container', 'contained' );
		$title_mode              = get_theme_mod( 'neve_' . $context . '_cover_title_boxed_layout', false );
		$title_meta_wrap_classes = [
			'nv-title-meta-wrap',
			$title_mode ? 'nv-is-boxed' : '',
		];

		$meta_before = '';
		if ( $context === 'post' ) {
			$meta_before = get_theme_mod( 'neve_post_cover_meta_before_title', false );
		}

		/**
		 * Filters the post title styles to override specific styles.
		 *
		 * @param string $style The styles for the title.
		 * @param string $context The context of the layout (e.g. 'cover', 'normal'). Default is 'normal'.
		 *
		 * @since 3.1.0
		 */
		$cover_style = apply_filters( 'neve_title_alignment_style', $cover_style, 'cover' );

		if ( ! empty( $cover_style ) ) {
			$cover_style = 'style="' . $cover_style . '"';
		}

		if ( $context === 'page' ) {
			$hide_title          = get_theme_mod( 'neve_page_hide_title', false );
			$specific_hide_title = get_post_meta( get_the_ID(), 'neve_meta_disable_title', true );
			$hide_title          = ! empty( $specific_hide_title ) ? $specific_hide_title === 'on' : $hide_title;
			if ( $hide_title ) {
				return;
			}
		}

		echo '<div class="nv-post-cover" ' . wp_kses_post( $cover_style ) . '>';
		echo '<div class="nv-overlay"></div>';
		echo $container_mode === 'contained' ? '<div class="container">' : '';
		echo '<div class="' . esc_attr( implode( ' ', $title_meta_wrap_classes ) ) . '">';

		if ( $meta_before === true ) {
			Post_Layout::render_post_meta();
		}

		do_action( 'neve_before_post_title' );

		echo '<h1 class="title entry-title">' . wp_kses_post( get_the_title() ) . '</h1>';
		if ( $meta_before === false ) {
			Post_Layout::render_post_meta();
		}

		echo '</div>';
		echo $container_mode === 'contained' ? '</div>' : '';
		echo '</div>';
	}
}
PK      \      views/post_layout.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      28/08/2018
 *
 * @package Neve\Views
 */


namespace Neve\Views;

use Neve\Customizer\Defaults\Layout;
use Neve\Customizer\Defaults\Single_Post;
use Neve\Customizer\Options\Layout_Single_Post;

/**
 * Class Post_Layout
 *
 * @package Neve\Views
 */
class Post_Layout extends Base_View {
	use Single_Post;
	use Layout;

	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'neve_do_single_post', [ $this, 'render_post' ] );
		add_filter( 'neve_post_has_comments', [ $this, 'post_has_comments' ] );
	}

	/**
	 * Detect if comments post element is enabled.
	 *
	 * @return bool
	 */
	public function post_has_comments() {
		$post_type              = get_post_type();
		$supported_post_types   = apply_filters( 'neve_post_type_supported_list', [], 'block_editor' );
		$supported_post_types[] = 'post';
		if ( ! in_array( $post_type, $supported_post_types, true ) ) {
			return false;
		}

		if ( ! is_singular( $post_type ) ) {
			return false;
		}

		$content_order = $this->get_content_order();

		if ( empty( $content_order ) ) {
			return false;
		}

		if ( ! in_array( 'comments', $content_order ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Render the post header.
	 *
	 * @param string $context the context provided in do_action.
	 */
	public function render_post( $context ) {
		if ( $context !== 'single-post' ) {
			return;
		}

		$content_order = $this->get_content_order();
		if ( empty( $content_order ) ) {
			return;
		}

		/** This filter is documented in header-footer-grid/templates/components/component-logo.php */
		$should_add_skip_lazy = apply_filters( 'neve_skip_lazy', true );
		$skip_lazy_class      = '';
		if ( $should_add_skip_lazy ) {
			$thumbnail_index = array_search( 'thumbnail', $content_order );
			$content_index   = array_search( 'content', $content_order );
			if ( $thumbnail_index < $content_index ) {
				$skip_lazy_class = 'skip-lazy';
			}
		}

		$content_order_length = count( $content_order );
		foreach ( $content_order as $index => $item ) {
			switch ( $item ) {
				case 'title-meta':
					if ( Layout_Single_Post::is_cover_layout() ) {
						break;
					}
					$this->render_entry_header();
					break;
				case 'thumbnail':
					if ( Layout_Single_Post::is_cover_layout() ) {
						break;
					}
					echo '<div class="nv-thumb-wrap">';
					echo get_the_post_thumbnail(
						null,
						'neve-blog',
						array( 'class' => $skip_lazy_class )
					);
					echo '</div>';
					break;
				case 'content':
					do_action( 'neve_before_content', 'single-post' );
					echo '<div class="nv-content-wrap entry-content">';
					the_content();
					echo '</div>';
					do_action( 'neve_do_pagination', 'single' );
					do_action( 'neve_after_content', 'single-post' );
					break;
				case 'post-navigation':
					do_action( 'neve_post_navigation' );
					break;
				case 'tags':
					do_action( 'neve_do_tags' );
					break;
				case 'title':
					if ( Layout_Single_Post::is_cover_layout() ) {
						break;
					}
					if ( $index !== $content_order_length - 1 && $content_order[ $index + 1 ] === 'meta' ) {
						$this->render_entry_header();
						break;
					}
					$this->render_entry_header( false );
					break;
				case 'meta':
					if ( Layout_Single_Post::is_cover_layout() ) {
						break;
					}
					if ( $index !== 0 && $content_order[ $index - 1 ] === 'title' ) {
						break;
					}
					self::render_post_meta();
					break;
				case 'author-biography':
					do_action( 'neve_layout_single_post_author_biography' );
					break;
				case 'related-posts':
					do_action( 'neve_do_related_posts' );
					break;
				case 'sharing-icons':
					do_action( 'neve_do_sharing' );
					break;
				case 'comments':
					comments_template();
					break;
				default:
					break;
			}
		}
	}

	/**
	 * Render the post meta.
	 *
	 * @param bool $is_list Flag to render meta as a list or as a text.
	 *
	 * @return bool
	 */
	public static function render_post_meta( $is_list = true ) {
		if ( ! get_post() ) {
			return false;
		}

		$meta_order = get_theme_mod( 'neve_single_post_meta_fields', self::get_default_single_post_meta_fields() );
		$meta_order = is_string( $meta_order ) ? json_decode( $meta_order ) : $meta_order;

		// We take the result and apply the neve_post_meta_ordering_filter that allows us to add values in neve pro
		$meta_order = apply_filters( 'neve_post_meta_ordering_filter', $meta_order );

		do_action( 'neve_post_meta_single', $meta_order, $is_list );
		return true;
	}

	/**
	 * Render post header
	 *
	 * @param bool $render_meta Render meta flag.
	 * @return void
	 */
	private function render_entry_header( $render_meta = true ) {
		$normal_style = apply_filters( 'neve_title_alignment_style', '', 'normal' );
		if ( ! empty( $normal_style ) ) {
			$normal_style = 'style="' . $normal_style . '"';
		}

		echo '<div class="entry-header" ' . wp_kses_post( $normal_style ) . '>';
		echo '<div class="nv-title-meta-wrap">';
		do_action( 'neve_before_post_title' );
		echo '<h1 class="title entry-title">' . wp_kses_post( get_the_title() ) . '</h1>';
		if ( $render_meta ) {
			self::render_post_meta();
		}
		echo '</div>';
		echo '</div>';
	}

	/**
	 * Get elements order.
	 *
	 * @return array
	 */
	private function get_content_order() {
		$default_order = $this->post_ordering();

		$content_order = get_theme_mod( 'neve_layout_single_post_elements_order', wp_json_encode( $default_order ) );
		if ( ! is_string( $content_order ) ) {
			$content_order = wp_json_encode( $default_order );
		}
		$content_order = json_decode( $content_order, true );
		if ( apply_filters( 'neve_filter_toggle_content_parts', true, 'title' ) !== true ) {
			$title_key = array_search( 'title-meta', $content_order, true );
			if ( $title_key !== false ) {
				unset( $content_order[ $title_key ] );
			}
		}

		if ( apply_filters( 'neve_filter_toggle_content_parts', true, 'featured-image' ) !== true || ! has_post_thumbnail() ) {
			$thumb_index = array_search( 'thumbnail', $content_order, true );
			if ( $thumb_index !== false ) {
				unset( $content_order[ $thumb_index ] );
			}
		}

		return apply_filters( 'neve_layout_single_post_elements_order', $content_order );
	}
}
PK      \K/  /     views/layouts/layout_sidebar.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      27/08/2018
 *
 * @package Neve\Views\Layouts
 */

namespace Neve\Views\Layouts;

use Neve\Customizer\Defaults\Layout;
use Neve\Views\Base_View;

/**
 * Class Layout_Container
 *
 * @package Neve\Views\Layouts
 */
class Layout_Sidebar extends Base_View {
	use Layout;

	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'neve_do_sidebar', array( $this, 'sidebar' ), 10, 2 );
		add_filter( 'body_class', array( $this, 'add_body_class' ) );
	}

	/**
	 * Render the sidebar.
	 *
	 * @param string $context  context passed into do_action.
	 * @param string $position position passed into do_action.
	 */
	public function sidebar( $context, $position ) {
		$sidebar_setup = $this->get_sidebar_setup( $context );
		$theme_mod     = $sidebar_setup['theme_mod'];
		$theme_mod     = apply_filters( 'neve_sidebar_position', get_theme_mod( $theme_mod, $this->sidebar_layout_alignment_default( $theme_mod ) ) );

		$content_width = get_theme_mod( $sidebar_setup['content_width'], $this->sidebar_layout_width_default( $sidebar_setup['content_width'] ) );

		$meta_width = apply_filters( 'neve_meta_content_width', false );

		if ( $meta_width !== false && ! empty( $meta_width ) ) {
			$content_width = $meta_width;
		}

		$class_hide_sidebar_conditionally = '';

		if ( $content_width >= 95 && ! $this->shop_sidebar_is_off_canvas() ) {
			$class_hide_sidebar_conditionally = 'hide';
			if ( $context !== 'shop' && ! is_customize_preview() ) {
				// do not load sidebar as SSR
				return;
			}
		}

		if ( $theme_mod !== $position ) {
			return;
		}

		if ( ! is_active_sidebar( $sidebar_setup['sidebar_slug'] ) ) {
			return;
		}

		$args = array(
			'wrap_classes' => 'nv-' . $position . ' ' . $sidebar_setup['sidebar_slug'] . ' ' . $class_hide_sidebar_conditionally,
			'data_attrs'   => apply_filters( 'neve_sidebar_data_attrs', '', $sidebar_setup['sidebar_slug'] ),
			'close_button' => $this->get_sidebar_close( $sidebar_setup['sidebar_slug'] ),
			'slug'         => $sidebar_setup['sidebar_slug'],
			'context'      => $context,
			'position'     => $position,
		);

		$this->get_view( 'sidebar', $args );
	}

	/**
	 * Add classes to the main tag.
	 *
	 * @param array $classes the body classes.
	 *
	 * @return array
	 */
	public function add_body_class( $classes ) {
		$context = $this->get_context();

		$sidebar_setup = $this->get_sidebar_setup( $context );
		$theme_mod     = $sidebar_setup['theme_mod'];
		$theme_mod     = apply_filters( 'neve_sidebar_position', get_theme_mod( $theme_mod, $this->sidebar_layout_alignment_default( $theme_mod ) ) );

		$layout       = get_theme_mod( 'neve_blog_archive_layout', 'grid' );
		$posts_layout = ' nv-blog-' . $layout;

		$classes[] = $posts_layout;
		$classes[] = 'nv-sidebar-' . $theme_mod;

		return $classes;
	}

	/**
	 * Get the sidebar setup. Returns array (`theme_mod`, `sidebar_slug`) based on context.
	 *
	 * @param string $context the provided context.
	 *
	 * @return array
	 */
	public function get_sidebar_setup( $context ) {
		$advanced_options = get_theme_mod( 'neve_advanced_layout_options', true );
		$sidebar_setup    = [
			'theme_mod'     => '',
			'content_width' => '',
			'sidebar_slug'  => 'blog-sidebar',
		];

		if ( class_exists( 'WooCommerce', false ) && ( is_woocommerce() || is_product() || is_cart() || is_checkout() || is_account_page() ) ) {
			$sidebar_setup['sidebar_slug'] = 'shop-sidebar';
		}

		if ( $advanced_options === false ) {
			$sidebar_setup['theme_mod']     = 'neve_default_sidebar_layout';
			$sidebar_setup['content_width'] = 'neve_sitewide_content_width';
			$sidebar_setup['has_widgets']   = is_active_sidebar( $sidebar_setup['sidebar_slug'] );

			return apply_filters( 'neve_before_returning_sidebar_setup', $sidebar_setup );
		}

		switch ( $context ) {
			case 'blog-archive':
				$sidebar_setup['theme_mod']     = 'neve_blog_archive_sidebar_layout';
				$sidebar_setup['content_width'] = 'neve_blog_archive_content_width';
				break;
			case 'single-post':
				$sidebar_setup['theme_mod'] = 'neve_single_post_sidebar_layout';
				if ( class_exists( 'WooCommerce', false ) && is_product() ) {
					$sidebar_setup['theme_mod']     = 'neve_single_product_sidebar_layout';
					$sidebar_setup['content_width'] = 'neve_single_product_content_width';
				}
				break;
			case 'single-page':
				$sidebar_setup['theme_mod']     = 'neve_other_pages_sidebar_layout';
				$sidebar_setup['content_width'] = 'neve_other_pages_content_width';
				break;
			case 'shop':
				if ( class_exists( 'WooCommerce', false ) ) {
					$sidebar_setup['sidebar_slug'] = 'shop-sidebar';
					if ( is_woocommerce() ) {
						$sidebar_setup['theme_mod']     = 'neve_shop_archive_sidebar_layout';
						$sidebar_setup['content_width'] = 'neve_shop_archive_content_width';
					}
					if ( is_product() ) {
						$sidebar_setup['theme_mod']     = 'neve_single_product_sidebar_layout';
						$sidebar_setup['content_width'] = 'neve_single_product_content_width';
					}
				}
				break;
			default:
				$sidebar_setup['theme_mod']     = 'neve_other_pages_sidebar_layout';
				$sidebar_setup['content_width'] = 'neve_other_pages_content_width';
		}

		$sidebar_setup['has_widgets'] = is_active_sidebar( $sidebar_setup['sidebar_slug'] );

		$sidebar_setup = apply_filters( 'neve_before_returning_sidebar_setup', apply_filters( 'neve_sidebar_setup_filter', $sidebar_setup ) );

		add_filter(
			'neve_' . $context . '_sidebar_setup',
			function () use ( $sidebar_setup ) {
				return $sidebar_setup;
			}
		);
		return $sidebar_setup;
	}

	/**
	 * Render sidebar toggle.
	 *
	 * @param string $slug sidebar slug.
	 *
	 * @return string
	 */
	private function get_sidebar_close( $slug ) {
		if ( $slug !== 'shop-sidebar' ) {
			return '';
		}
		$label        = apply_filters( 'neve_filter_sidebar_close_button_text', __( 'Close', 'neve' ), $slug );
		$button_attrs = apply_filters( 'neve_filter_sidebar_close_button_data_attrs', '', $slug );

		return '<div class="sidebar-header"><a href="#" class="nv-sidebar-toggle in-sidebar button-secondary secondary-default" ' . $button_attrs . '>' . esc_html( $label ) . '</a></div>';
	}

	/**
	 * Get current context.
	 *
	 * @return string|false
	 */
	private function get_context() {
		if ( class_exists( 'WooCommerce', false ) && ( is_woocommerce() || is_product() || is_cart() || is_checkout() || is_account_page() ) ) {
			return 'shop';
		}

		if ( is_page() ) {
			return 'single-page';
		}

		if ( is_single() ) {
			return 'single-post';
		}

		if ( is_archive() || is_home() ) {
			return 'blog-archive';
		}

		return false;
	}
}
PK      \=LC  C  "  views/layouts/layout_container.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      27/08/2018
 *
 * @package Neve\Views\Layouts
 */

namespace Neve\Views\Layouts;

use Neve\Views\Base_View;

/**
 * Class Layout_Container
 *
 * @package Neve\Views\Layouts
 */
class Layout_Container extends Base_View {

	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_filter( 'neve_container_class_filter', array( $this, 'container_layout' ), 10, 2 );
	}

	/**
	 * Get the container style.
	 *
	 * @param string $value   the value passed in the filter.
	 * @param string $context the context passed in the filter.
	 *
	 * @return string
	 */
	public function container_layout( $value, $context = 'single-page' ) {
		if ( $context === 'blog-archive' ) {
			return ( $this->get_container_class( 'neve_blog_archive_container_style' ) );
		}

		if ( $context === 'single-post' ) {
			return apply_filters( 'neve_single_container_style_filter', $this->get_container_class( 'neve_single_post_container_style' ) );
		}

		if ( $context === 'single-page' && class_exists( 'WooCommerce', false ) ) {
			if ( is_product() ) {
				return ( $this->get_container_class( 'neve_single_product_container_style' ) );
			}

			if ( is_shop() || is_product_category() ) {
				return ( $this->get_container_class( 'neve_shop_archive_container_style' ) );
			}
		}

		return $this->get_container_class( 'neve_default_container_style' );
	}

	/**
	 * Returns container class based on the theme mod.
	 *
	 * @param string $theme_mod the theme mod from which to get the container class.
	 *
	 * @return string
	 */
	private function get_container_class( $theme_mod ) {
		$container_type = get_theme_mod( $theme_mod, 'contained' );
		if ( $container_type === 'contained' ) {
			return 'container';
		}

		return 'container-fluid';
	}
}
PK      \;e      views/top_bar.phpnu W+A        <?php
/**
 * Top Bar
 *
 * @package Neve\Views
 */

namespace Neve\Views;

/**
 * Class Top_Bar
 */
class Top_Bar extends Base_View {

	/**
	 * Add hooks for the front end.
	 */
	public function init() {
		$this->filter_content();
	}

	/**
	 * Apply filters on the top bar content.
	 */
	private function filter_content() {
		add_filter( 'neve_top_bar_content', 'wptexturize' );
		add_filter( 'neve_top_bar_content', 'convert_smilies' );
		add_filter( 'neve_top_bar_content', 'convert_chars' );
		add_filter( 'neve_top_bar_content', 'wpautop' );
		add_filter( 'neve_top_bar_content', 'shortcode_unautop' );
		add_filter( 'neve_top_bar_content', 'do_shortcode' );
	}
}
PK      \  y6  y6    views/template_parts.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      27/08/2018
 *
 * @package Neve\Views
 */

namespace Neve\Views;

use Neve\Compatibility\WPML;
use Neve\Core\Dynamic_Css;
use Neve\Core\Settings\Config;
use Neve\Customizer\Defaults\Layout;
use Neve_Pro\Modules\Blog_Pro\Dynamic_Style;

/**
 * Class Template_Parts
 *
 * @package Neve\Views
 */
class Template_Parts extends Base_View {
	use Layout;

	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_enqueue_scripts', array( $this, 'add_featured_post_style' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'add_vertical_spacing_style' ) );
		add_action( 'neve_do_featured_post', array( $this, 'render_featured_post' ) );
		add_action( 'neve_blog_post_template_part_content', array( $this, 'render_post' ) );
		add_filter( 'excerpt_more', array( $this, 'link_excerpt_more' ) );
		add_filter( 'the_content_more_link', array( $this, 'link_excerpt_more' ) );
	}

	/**
	 * Add vertical spacing inline style if the control has values.
	 */
	public function add_vertical_spacing_style() {
		if ( ! get_theme_mod( Config::MODS_CONTENT_VSPACING ) ) {
			return;
		}
		$inline_style = '.page .neve-main, .single:not(.single-product) .neve-main{ margin:var(--c-vspace) }';
		wp_add_inline_style( 'neve-style', Dynamic_Css::minify_css( $inline_style ) );
	}

	/**
	 * Add inline style for featured post.
	 */
	public function add_featured_post_style() {
		if ( ! get_theme_mod( 'neve_enable_featured_post', false ) ) {
			return;
		}

		wp_add_inline_style(
			'neve-style',
			'
			.nv-ft-post {
				margin-top:60px
			}
			.nv-ft-post .nv-ft-wrap:not(.layout-covers){
				background:var(--nv-light-bg);
			}
			.nv-ft-post h2{
				font-size:calc( var(--fontsize, var(--h2fontsize)) * 1.3)
			}
			.nv-ft-post .nv-meta-list{
				display:block
			}
			.nv-ft-post .non-grid-content{
				padding:32px
			}
			.nv-ft-post .wp-post-image{
				position:absolute;
				object-fit:cover;
				width:100%;
				height:100%
			}
			.nv-ft-post:not(.layout-covers) .nv-post-thumbnail-wrap{
				margin:0;
				position:relative;
				min-height:320px
			}
			'
		);
	}

	/**
	 * Render featured posts section.
	 */
	public function render_featured_post() {
		if ( ! get_theme_mod( 'neve_enable_featured_post', false ) ) {
			return;
		}

		$post_type = get_post_type();
		if ( $post_type !== 'post' || ! is_home() ) {
			return;
		}

		// If the query is for a paged result and not for the first page don't display featured post.
		if ( is_paged() ) {
			return;
		}

		/**
		 * Filters the content parts.
		 *
		 * @since 3.2
		 *
		 * @param int $value Number of featured posts
		 */
		$post_number = apply_filters( 'nv_featured_posts_number', 1 );

		$target = get_theme_mod( 'neve_featured_post_target', 'latest' );
		if ( $target === 'latest' ) {
			$posts = wp_get_recent_posts(
				array(
					'numberposts' => $post_number,
					'post_status' => 'publish',
				)
			);

			/**
			 * Filters the featured posts.
			 *
			 * @since 3.5.6
			 *
			 * @param array $posts Array of posts. The return value can be an array of posts or an array of post IDs.
			 */
			$posts = apply_filters( 'neve_filter_featured_posts', $posts );
		}

		if ( $target === 'sticky' ) {
			$posts = get_option( 'sticky_posts' );
		}

		if ( empty( $posts ) ) {
			return;
		}

		$wrapper_classes  = apply_filters( 'neve_posts_wrapper_class', [] );
		$posts_to_exclude = [];

		echo '<div class="' . esc_attr( join( ' ', $wrapper_classes ) ) . '">';
		foreach ( $posts as $post ) {
			$post_id            = is_array( $post ) && array_key_exists( 'ID', $post ) ? $post['ID'] : $post;
			$posts_to_exclude[] = $post_id;
			// Fixes the excerpt link for the featured post
			add_filter(
				'excerpt_more',
				function() use ( $post_id ) {
					return $this->link_excerpt_more( ' [&hellip;]', $post_id );
				}
			);

			$has_thumbnail_class = apply_filters( 'neve_featured_has_post_thumbnail', '', $post_id );
			$data                = [
				'post_id'    => 'post-' . $post_id,
				'post_class' => $this->post_class( $post_id, 'nv-ft-post ' . $has_thumbnail_class ),
				'content'    => $this->get_article_inner_content( $post_id ),
			];
			$this->get_view( 'archive-post', $data );

			remove_all_filters( 'excerpt_more' );
		}
		echo '</div>';
		add_filter(
			'nv_exclude_posts',
			function () use ( $posts_to_exclude ) {
				return $posts_to_exclude;
			}
		);
		add_filter( 'excerpt_more', array( $this, 'link_excerpt_more' ) );
	}

	/**
	 * Render the post.
	 */
	public function render_post() {

		$args = array(
			'post_id'    => 'post-' . get_the_ID(),
			'post_class' => $this->post_class(),
			'content'    => $this->get_article_inner_content(),
		);

		$this->get_view( 'archive-post', $args );
	}

	/**
	 * Echo the post class.
	 *
	 * @param int | null $post_id Post id.
	 * @param string     $additional Additional classes.
	 */
	protected function post_class( $post_id = null, $additional = '' ) {
		$class     = join( ' ', get_post_class( '', $post_id ) );
		$post_type = get_post_type( $post_id );
		if ( $post_type === 'neve_custom_layouts' ) {
			return $class;
		}
		$layout = $this->get_layout();
		$class .= ' layout-' . $layout;
		if ( ! in_array( $layout, [ 'grid', 'covers' ], true ) ) {
			$class .= ' col-12 ';
			if ( $post_id === null ) {
				$class .= ' nv-non-grid-article';
			}
		}
		
		// Filter the Core classes for missing components.
		$is_thumbnail_inactive = ! in_array( 'thumbnail', $this->get_ordered_components(), true );
		if ( $is_thumbnail_inactive ) {
			$class = str_replace( 'has-post-thumbnail', '', $class );
		}

		$class .= ' ' . $additional;
		return $class;
	}

	/**
	 * Render inner content for <article>
	 *
	 * @param int | null $post_id Post id.
	 *
	 * @return string
	 */
	private function get_article_inner_content( $post_id = null ) {
		$markup            = '';
		$layout            = $this->get_layout();
		$is_featured_post  = $post_id !== null;
		$featured_template = in_array( $layout, [ 'alternative', 'default', 'grid' ], true ) ? 'tp1' : 'tp2';

		$is_thumbnail_active = in_array( 'thumbnail', $this->get_ordered_components(), true );

		if ( in_array( $layout, [ 'alternative', 'default' ], true ) || ( $is_featured_post && $featured_template === 'tp1' ) ) {
			$markup .= '<div class="' . esc_attr( $layout ) . '-post nv-ft-wrap">';
			if ( $is_thumbnail_active || ( $is_featured_post && $featured_template === 'tp1' ) ) {
				$markup .= $this->get_post_thumbnail( $post_id );
			}
			$markup .= '<div class="non-grid-content ' . esc_attr( $layout ) . '-layout-content">';
			$markup .= $this->get_ordered_content_parts( true, $post_id );
			$markup .= '</div>';
			$markup .= '</div>';

			return $markup;
		}

		if ( $layout === 'covers' ) {
			$markup .= '<div class="cover-post nv-ft-wrap">';
			$markup .= '<div class="cover-overlay"></div>';
			if ( $is_thumbnail_active ) {
				$markup .= $this->get_post_thumbnail( $post_id, true );
			}
			$markup .= '<div class="inner">';
			$markup .= $this->get_ordered_content_parts( true, $post_id );
			$markup .= '</div>';
			$markup .= '</div>';

			return $markup;
		}

		return $this->get_ordered_content_parts( false, $post_id );
	}

	/**
	 * Render the post thumbnail.
	 *
	 * @param int | null $post_id Post id.
	 * @param bool       $skip_link Flag to skip wrapping post image in a tag.
	 *
	 * @return string
	 */
	private function get_post_thumbnail( $post_id = null, $skip_link = false ) {
		if ( ! has_post_thumbnail( $post_id ) ) {
			return '';
		}

		global $neve_thumbnail_skip_lazy_added;

		/** This filter is documented in header-footer-grid/templates/components/component-logo.php */
		$should_add_skip_lazy = apply_filters( 'neve_skip_lazy', true );
		$image_class          = '';
		if ( $should_add_skip_lazy && ! isset( $neve_thumbnail_skip_lazy_added ) ) {
			$image_class                    = 'skip-lazy';
			$neve_thumbnail_skip_lazy_added = true;
		}

		$image_wrap_classes = $this->get_image_wrap_classes();
		$markup             = '<div class="' . esc_attr( $image_wrap_classes ) . '">';

		if ( ! $skip_link ) {
			$markup .= '<a href="' . esc_url( get_the_permalink( $post_id ) ) . '" rel="bookmark" title="' . the_title_attribute(
				array(
					'echo' => false,
				)
			) . '">';
		}

		$pid     = $post_id ? $post_id : get_the_ID();
		$markup .= get_the_post_thumbnail(
			$pid,
			'neve-blog',
			array( 'class' => $image_class )
		);
		if ( ! $skip_link ) {
			$markup .= '</a>';
		}
		$markup .= '</div>';

		return apply_filters( 'neve_blog_post_thumbnail_markup', $markup );
	}

	/**
	 * Get css classes for post image wrap.
	 *
	 * @return string
	 */
	private function get_image_wrap_classes() {
		$post_classes = [ 'nv-post-thumbnail-wrap', 'img-wrap' ];

		if ( defined( 'NEVE_PRO_VERSION' ) ) {
			$blog_image_hover = get_theme_mod( 'neve_blog_image_hover', 'none' );
			if ( $blog_image_hover !== 'none' ) {
				$post_classes[] = $blog_image_hover;
			}
		}
		return esc_attr( implode( ' ', $post_classes ) );
	}

	/**
	 * Get the posts layout.
	 *
	 * @return string
	 */
	private function get_layout() {
		$layout = get_theme_mod( 'neve_blog_archive_layout', 'grid' );

		if ( $layout !== 'default' ) {
			return $layout;
		}

		if ( get_theme_mod( 'neve_blog_list_alternative_layout', false ) === true ) {
			$layout = 'alternative';
		}

		return $layout;
	}

	/**
	 * Render title.
	 *
	 * @param int | null $post_id Post id.
	 *
	 * @return string
	 */
	private function get_title( $post_id = null ) {
		$markup = '<h2 class="blog-entry-title entry-title">';

		$markup .= '<a href="' . esc_url( get_the_permalink( $post_id ) ) . '" rel="bookmark">';
		$markup .= get_the_title( $post_id );
		$markup .= '</a>';
		$markup .= '</h2>';

		return $markup;
	}

	/**
	 * Render meta.
	 *
	 * @param int | null $post_id Post id.
	 *
	 * @return string | bool
	 */
	private function get_meta( $post_id = null ) {
		$default       = wp_json_encode( [ 'author', 'date', 'comments' ] );
		$default_value = neve_get_default_meta_value( 'neve_post_meta_ordering', $default );
		$meta_order    = get_theme_mod( 'neve_blog_post_meta_fields', wp_json_encode( $default_value ) );

		if ( ! is_array( $meta_order ) ) {
			$meta_order = json_decode( $meta_order );
			if ( empty( $meta_order ) ) {
				return false;
			}
		}

		ob_start();
		do_action( 'neve_post_meta_archive', $meta_order, true, $post_id );
		$meta = ob_get_clean();

		return $meta;
	}

	/**
	 * Render excerpt.
	 *
	 * @param int | null $post_id Post id.
	 * @return string
	 */
	private function get_excerpt( $post_id = null ) {
		ob_start();
		do_action( 'neve_excerpt_archive', 'index', $post_id );
		$excerpt = ob_get_clean();

		return $excerpt;
	}

	/**
	 * Change link excerpt more.
	 *
	 * @param string     $moretag read more tag.
	 * @param int | null $post_id Post id.
	 * @return string
	 */
	public function link_excerpt_more( $moretag, $post_id = null ) {
		$new_moretag = '&hellip;&nbsp;';

		if ( $moretag !== ' [&hellip;]' ) {
			$new_moretag = '';
		}

		$read_more_args = apply_filters(
			'neve_read_more_args',
			array(
				'text'    => esc_html__( 'Read More', 'neve' ) . ' &raquo;',
				'classes' => '',
			)
		);

		$markup  = '<a href="' . esc_url( get_the_permalink( $post_id ) ) . '"';
		$markup .= ' class="' . esc_attr( $read_more_args['classes'] ) . '"';
		$markup .= ' rel="bookmark">';
		$markup .= esc_html( $read_more_args['text'] );
		$markup .= '<span class="screen-reader-text">' . get_the_title( $post_id ) . '</span>';
		$markup .= '</a>';

		if ( ! empty( $read_more_args['classes'] ) ) {
			$markup = '<div class="read-more-wrapper">' . $markup . '</div>';
		}

		$new_moretag .= $markup;

		return $new_moretag;
	}

	/**
	 * Get ordered content parts.
	 *
	 * @param bool       $exclude_thumbnail exclude thumbnail from order.
	 * @param int | null $post_id Post id.
	 * @return string
	 */
	private function get_ordered_content_parts( $exclude_thumbnail = false, $post_id = null ) {
		$markup             = '';
		$ordered_components = $this->get_ordered_components( true );

		if ( $post_id !== null && in_array( 'thumbnail', $ordered_components, true ) ) {
			$key = array_search( 'thumbnail', $ordered_components );
			if ( $key !== false ) {
				unset( $ordered_components[ $key ] );
			}
			array_unshift( $ordered_components, 'thumbnail' );
		}

		foreach ( $ordered_components as $content_bit ) {
			switch ( $content_bit ) {
				case 'thumbnail':
					if ( $exclude_thumbnail ) {
						break;
					}
					$markup .= $this->get_post_thumbnail( $post_id );
					break;
				case 'title':
					$markup .= $this->get_title( $post_id );
					break;
				case 'meta':
					$markup .= $this->get_meta( $post_id );
					break;
				case 'title-meta':
					$markup .= $this->get_title( $post_id );
					$markup .= $this->get_meta( $post_id );
					break;
				case 'excerpt':
					$markup .= $this->get_excerpt( $post_id );
					$markup .= wp_link_pages(
						array(
							'before'      => '<div class="post-pages-links"><span>' . apply_filters( 'neve_page_link_before', esc_html__( 'Pages:', 'neve' ) ) . '</span>',
							'after'       => '</div>',
							'link_before' => '<span class="page-link">',
							'link_after'  => '</span>',
							'echo'        => false,
						)
					);
					break;
				default:
					break;
			}
		}

		return $markup;
	}

	/**
	 * Get components (thumbnail, title, meta, excerpt) in the order they are set in the Customizer.
	 *
	 * @param bool $associative Whether to return an associative array or not.
	 *
	 * @return mixed
	 */
	public function get_ordered_components( $associative = false ) {
		$default_ordered_components = array(
			'thumbnail',
			'title-meta',
			'excerpt',
		);

		return json_decode( get_theme_mod( 'neve_post_content_ordering', wp_json_encode( $default_ordered_components ) ), $associative );
	}
}
PK      \VyA*:  *:    views/partials/post_meta.phpnu W+A        <?php
/**
 * Should handle post meta display.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      28/08/2018
 *
 * @package Neve\Views
 */

namespace Neve\Views\Partials;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Views\Base_View;

/**
 * Class Post_Meta
 *
 * @package Neve\Views
 */
class Post_Meta extends Base_View {
	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_filter( 'neve_display_author_avatar', array( $this, 'should_display_author_avatar' ), 15 );
		add_action( 'neve_post_meta_archive', array( $this, 'render_meta_list' ), 10, 3 );
		add_action( 'neve_post_meta_single', array( $this, 'render_meta_list' ), 10, 3 );
		add_action( 'neve_do_tags', array( $this, 'render_tags_list' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'meta_custom_separator' ) );
		add_filter( 'neve_gravatar_args', [ $this, 'add_dynamic_gravatar' ] );
	}

	/**
	 * Show/hide author avatar based on customizer option
	 *
	 * @param bool $value option value.
	 *
	 * @return bool
	 */
	public function should_display_author_avatar( $value ) {

		$show_avatar = get_theme_mod( 'neve_author_avatar', false );
		if ( is_singular() ) {
			$show_avatar = get_theme_mod( 'neve_single_post_author_avatar', $show_avatar );
		}

		return $show_avatar;
	}

	/**
	 * Add dynamic gravatar values.
	 *
	 * @param array $args_array Avatar args.
	 *
	 * @return mixed
	 */
	public function add_dynamic_gravatar( $args_array ) {

		$meta_key    = Config::MODS_ARCHIVE_POST_META_AUTHOR_AVATAR_SIZE;
		$default     = '{ "mobile": 20, "tablet": 20, "desktop": 20 }';
		$avatar_size = Mods::to_json( $meta_key, $default );
		if ( is_singular( 'post' ) ) {
			$single_avatar_size = Mods::to_json( Config::MODS_SINGLE_POST_META_AUTHOR_AVATAR_SIZE );
			$avatar_size        = ! empty( $single_avatar_size ) ? $single_avatar_size : $avatar_size;
		}
		$avatar_size = apply_filters( 'neve_author_avatar_size_filter', $avatar_size );

		if ( ! isset( $args_array['size'] ) ) {
			return $args_array;
		}

		if ( ! is_array( $avatar_size ) ) {
			return $args_array;
		}

		$args_array['size'] = max( $avatar_size );

		return $args_array;
	}

	/**
	 * Get the index of the last item on mobile to be able to hide the separator
	 *
	 * @param array $order Meta elements order.
	 *
	 * @return int
	 */
	private function get_mobile_last_item( $order ) {
		$order_length    = count( $order );
		$last_item_index = -1;
		for ( $i = 0; $i < $order_length; $i++ ) {
			for ( $j = $i + 1; $j < $order_length; $j++ ) {
				if ( ! isset( $order[ $j ]->hide_on_mobile ) || (bool) $order[ $j ]->hide_on_mobile === false ) {
					$last_item_index = $j;
					break;
				}
			}
		}
		return $last_item_index;
	}

	/**
	 * Render meta list.
	 *
	 * @param array      $order   the order array. Passed through the action parameter.
	 * @param bool       $as_list Flag to display meta as list or as text.
	 * @param int | null $post_id Post id.
	 */
	public function render_meta_list( $order, $as_list = true, $post_id = null ) {
		if ( ! is_array( $order ) || empty( $order ) ) {
			return;
		}

		// Filter the array to contain only visible elements
		$order = array_filter(
			$order,
			function ( $el ) {
				return isset( $el->visibility ) && $el->visibility === 'yes';
			}
		);

		$order = $this->sanitize_order_array( $order );
		$order = array_values( $order );

		$pid                 = $post_id ? $post_id : get_the_ID();
		$post_type           = get_post_type( $pid );
		$markup              = $as_list === true ? '<ul class="nv-meta-list">' : '<span class="nv-meta-list nv-dynamic-meta">';
		$tag                 = $as_list === true ? 'li' : 'span';
		$last_item_on_mobile = $this->get_mobile_last_item( $order );
		$order_length        = count( $order );

		for ( $i = 0; $i < $order_length; $i++ ) {
			$meta           = $order[ $i ];
			$element_class  = $last_item_on_mobile === $i ? 'last' : '';
			$slug           = $meta->slug ?? 'custom';
			$format         = ! empty( $meta->format ) ? $meta->format : '{meta}';
			$element_class .= isset( $meta->hide_on_mobile ) && (bool) $meta->hide_on_mobile === true ? ' no-mobile' : '';
			switch ( $slug ) {
				case 'author':
					$show_before  = $format === '{meta}';
					$meta_content = str_replace( '{meta}', self::neve_get_author_meta( $pid, $show_before ), $format );
					$markup      .= '<' . $tag . '  class="meta author vcard ' . esc_attr( $element_class ) . '">';
					$markup      .= wp_kses_post( $meta_content );
					$markup      .= '</' . $tag . '>';
					break;
				case 'date':
					$date_meta_classes = array(
						'meta',
						'date',
						'posted-on',
					);

					$created           = get_the_time( 'U' );
					$modified          = get_the_modified_time( 'U' );
					$has_updated_time  = $created !== $modified;
					$show_updated_time = get_theme_mod( 'neve_show_last_updated_date', false );
					if ( is_singular( 'post' ) ) {
						$show_updated_time = get_theme_mod( 'neve_single_post_show_last_updated_date', $show_updated_time );
					}
					if ( $show_updated_time && $has_updated_time ) {
						$date_meta_classes[] = 'nv-show-updated';
					}
					$meta_content = str_replace( '{meta}', self::get_time_tags( $pid ), $format );
					$markup      .= '<' . $tag . ' class="' . esc_attr( implode( ' ', $date_meta_classes ) ) . ' ' . esc_attr( $element_class ) . '">';
					$markup      .= $meta_content;
					$markup      .= '</' . $tag . '>';
					break;
				case 'category':
					if ( ! in_array( 'category', get_object_taxonomies( $post_type ) ) ) {
						break;
					}
					$meta_content = str_replace( '{meta}', get_the_category_list( ', ', '', $pid ), $format );
					$markup      .= '<' . $tag . ' class="meta category ' . esc_attr( $element_class ) . '">';
					$markup      .= wp_kses_post( $meta_content );
					$markup      .= '</' . $tag . '>';
					break;
				case 'comments':
					$comments = self::get_comments( $pid );
					if ( empty( $comments ) ) {
						break;
					}
					$meta_content = str_replace( '{meta}', $comments, $format );
					$markup      .= '<' . $tag . ' class="meta comments ' . esc_attr( $element_class ) . '">';
					$markup      .= wp_kses_post( $meta_content );
					$markup      .= '</' . $tag . '>';
					break;
				case 'reading':
					$allowed_context = apply_filters( 'neve_post_type_supported_list', [ 'post' ], 'block_editor' );
					if ( ! in_array( $post_type, $allowed_context ) ) {
						break;
					}
					$reading_time = apply_filters( 'neve_do_read_time', $pid );
					if ( empty( $reading_time ) ) {
						break;
					}
					$meta_content = str_replace( '{meta}', $reading_time, $format );
					$markup      .= '<' . $tag . ' class="meta reading-time ' . esc_attr( $element_class ) . '">';
					$markup      .= wp_kses_post( $meta_content );
					$markup      .= '</' . $tag . '>';
					break;
				case 'custom':
					$custom_meta = apply_filters( 'neve_do_custom_meta', '', $meta, $tag, $pid );
					if ( empty( $custom_meta ) ) {
						break;
					}
					$markup .= '<' . $tag . ' class="meta custom ' . esc_attr( $element_class ) . '">';
					$markup .= wp_kses_post( $custom_meta );
					$markup .= '</' . $tag . '>';
					break;
				case 'default':
				default:
					break;
			}
		}
		$markup .= $as_list === true ? '</ul>' : '</span>';
		echo ( $markup ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Makes sure there's a valid meta order array.
	 *
	 * @param array $order meta order array.
	 *
	 * @return mixed
	 */
	private function sanitize_order_array( $order ) {
		$allowed_order_values = apply_filters(
			'neve_meta_filter',
			array(
				'author'   => __( 'Author', 'neve' ),
				'category' => __( 'Category', 'neve' ),
				'date'     => __( 'Date', 'neve' ),
				'comments' => __( 'Comments', 'neve' ),
			)
		);
		foreach ( $order as $index => $meta_item ) {
			if ( isset( $meta_item->blocked ) && isset( $meta_item->slug ) && ! array_key_exists( $meta_item->slug, $allowed_order_values ) ) {
				unset( $order[ $index ] );
			}
		}

		return $order;
	}

	/**
	 * Get the author meta.
	 *
	 * @param int | null $post_id Post id.
	 * @param bool       $show_before Show the 'by' string before the author name.
	 *
	 * @return string | false
	 */
	public static function neve_get_author_meta( $post_id = null, $show_before = true ) {

		global $post;

		$current_post = $post_id !== null ? get_post( $post_id ) : $post;

		if ( ! isset( $current_post ) ) {
			return false;
		}
		// we need to set the global post to the current post in order to allow other filters that hook into get_the_author_meta hooks access to the current post.
		// we reset this at the end of the function.
		$original_global_post = $post;
		$post                 = $current_post; //phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
		setup_postdata( $current_post );

		$author_id      = $current_post->post_author;
		$user_nicename  = get_the_author_meta( 'user_nicename', $author_id );
		$display_name   = get_the_author_meta( 'display_name', $author_id );
		$author_email   = get_the_author_meta( 'user_email', $author_id );
		$gravatar_args  = apply_filters(
			'neve_gravatar_args',
			array(
				'size' => 20,
			)
		);
		$display_avatar = apply_filters( 'neve_display_author_avatar', false );
		$avatar_url     = get_avatar_url( $author_email, $gravatar_args );
		$avatar_markup  = '<img class="photo" alt="' . esc_attr( $display_name ) . '" src="' . esc_url( $avatar_url ) . '" />&nbsp;';

		$markup = '';
		if ( $display_avatar ) {
			$markup .= $avatar_markup;
		}
		$markup .= '<span class="author-name fn">';
		if ( ! $display_avatar && $show_before ) {
			$markup .= __( 'by', 'neve' ) . ' ';
		}

		$link = sprintf(
			'<a href="%1$s" title="%2$s" rel="author">%3$s</a>',
			esc_url( get_author_posts_url( $author_id, $user_nicename ) ),
			/* translators: %s: Author's display name. */
			esc_attr( sprintf( __( 'Posts by %s', 'neve' ), $display_name ) ),
			$display_name
		);

		$link = apply_filters( 'the_author_posts_link', $link );

		$markup .= wp_kses_post( $link ) . '</span>';

		/**
		 * Filters the author post meta markup.
		 *
		 * @since 3.5.2
		 *
		 * @param string $markup The HTML markup used to display the post meta author.
		 * @param int | null $post_id Post id.
		 * @param bool $show_before Show the 'by' sting before the author name.
		 *
		 * @return string
		 */
		$markup = apply_filters( 'neve_filter_author_meta_markup', $markup, $post_id, $show_before );

		$post = $original_global_post; //phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
		return wp_kses_post( $markup );
	}

	/**
	 * Get <time> tags.
	 *
	 * @param int | null $post_id Post id.
	 * @return string
	 */
	public static function get_time_tags( $post_id = null ) {
		$created           = get_the_time( 'U', $post_id );
		$modified          = get_the_modified_time( 'U', $post_id );
		$has_updated_time  = $created !== $modified;
		$show_updated_time = get_theme_mod( 'neve_show_last_updated_date', false );
		if ( is_singular( 'post' ) ) {
			$show_updated_time = get_theme_mod( 'neve_single_post_show_last_updated_date', $show_updated_time );
		}
		$format = get_option( 'date_format' );

		$prefixes = array(
			'published' => '',
			'updated'   => '',
		);

		/**
		 * Filters the prefix of the published date and the updated date of a post.
		 *
		 * @param array $prefixes {
		 *     Prefix location and value.
		 *
		 *     @type string $published The prefix for the published date.
		 *     @type string $updated The prefix for the updated date.
		 * }
		 *
		 * @since 2.11
		 */
		$prefixes = apply_filters( 'neve_meta_date_prefix', $prefixes );

		$updated_time_markup = '<time class="updated" datetime="' . esc_attr( date_i18n( 'c', $modified ) ) . '">';
		if ( array_key_exists( 'updated', $prefixes ) && ! empty( $prefixes['updated'] ) ) {
			$updated_time_markup .= wp_kses_post( $prefixes['updated'] );
		}
		$updated_time_markup .= esc_html( date_i18n( $format, $modified ) ) . '</time>';

		if ( $show_updated_time && $has_updated_time ) {
			return $updated_time_markup;
		}

		$time = '<time class="entry-date published" datetime="' . esc_attr( date_i18n( 'c', $created ) ) . '" content="' . esc_attr( date_i18n( 'Y-m-d', $created ) ) . '">';
		if ( array_key_exists( 'published', $prefixes ) && ! empty( $prefixes['published'] ) ) {
			$time .= wp_kses_post( $prefixes['published'] );
		}
		$time .= esc_html( date_i18n( $format, $created ) ) . '</time>';

		if ( ! $has_updated_time ) {
			return $time;
		}
		$time .= $updated_time_markup;

		return $time;
	}

	/**
	 * Get the comments with a link.
	 *
	 * @param int | null $post_id Post id.
	 *
	 * @return string|false
	 */
	public static function get_comments( $post_id = null ) {
		if ( ! get_post( $post_id ) ) {
			return false;
		}
		if ( ! comments_open( $post_id ) ) {
			return false;
		}
		$comments_number = get_comments_number( $post_id );
		if ( $comments_number < 1 ) {
			return false;
		}
		/* translators: %s: number of comments */
		$comments = sprintf( _n( '%s Comment', '%s Comments', $comments_number, 'neve' ), $comments_number );

		return '<a href="' . esc_url( get_comments_link( $post_id ) ) . '">' . esc_html( $comments ) . '</a>';
	}

	/**
	 * Render the tags list.
	 */
	public function render_tags_list() {
		$tags = get_the_tags();
		if ( ! is_array( $tags ) ) {
			return;
		}
		$html  = '<div class="nv-tags-list">';
		$html .= '<span>' . __( 'Tags', 'neve' ) . ':</span>';
		foreach ( $tags as $tag ) {
			$tag_link = get_tag_link( $tag->term_id );
			$html    .= '<a href=' . esc_url( $tag_link ) . ' title="' . esc_attr( $tag->name ) . '" class=' . esc_attr( $tag->slug ) . ' rel="tag">';
			$html    .= esc_html( $tag->name ) . '</a>';
		}
		$html .= ' </div> ';
		echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Change metadata separator according to the customizer setting
	 */
	public function meta_custom_separator() {

		$separator = get_theme_mod( 'neve_metadata_separator', esc_html( '/' ) );
		if ( is_singular() ) {
			$separator = get_theme_mod( 'neve_single_post_metadata_separator', $separator );
		}
		$separator = apply_filters( 'neve_metadata_separator_filter', $separator );

		$custom_css  = '';
		$custom_css .= '.nv-meta-list li.meta:not(:last-child):after { content:"' . esc_html( $separator ) . '" }';

		$custom_css .= '.nv-meta-list .no-mobile{
			display:none;
		}';
		$custom_css .= '.nv-meta-list li.last::after{
			content: ""!important;
		}';
		$custom_css .= '@media (min-width: 769px) {
			.nv-meta-list .no-mobile {
				display: inline-block;
			}
			.nv-meta-list li.last:not(:last-child)::after {
		 		content: "' . esc_html( $separator ) . '" !important;
			}
		}';
		wp_add_inline_style( 'neve-style', $custom_css );
	}

}
PK      \/	  	    views/partials/excerpt.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      28/08/2018
 *
 * @package Neve\Views\Partials
 */

namespace Neve\Views\Partials;

use Neve\Views\Base_View;

/**
 * Class Excerpt
 *
 * @package Neve\Views\Partials
 */
class Excerpt extends Base_View {
	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'neve_excerpt_archive', array( $this, 'render_post_excerpt' ), 10, 2 );
	}

	/**
	 * Echo the post excerpt.
	 *
	 * @param string     $context the provided context in do_action.
	 * @param int | null $post_id Post id.
	 */
	public function render_post_excerpt( $context, $post_id = null ) {
		echo $this->get_post_excerpt( $context, $post_id ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Get the post excerpt.
	 *
	 * @param string     $context NOT YET USED. Might come in handily at some later point.
	 * @param int | null $post_id Post id.
	 * @return string
	 */
	private function get_post_excerpt( $context, $post_id = null ) {
		$length = $this->get_excerpt_length();

		$output  = '';
		$output .= '<div class="excerpt-wrap entry-summary">';
		$output .= $this->get_excerpt( $length, $post_id );
		$output .= '</div>';

		return $output;
	}

	/**
	 * Get the excerpt length.
	 *
	 * @param int $length Post excerpt length.
	 * @param int $post_id Post id.
	 *
	 * @return string
	 */
	private function get_excerpt( $length = 25, $post_id = null ) {

		global $post;

		if ( $length === 300 ) {
			return apply_filters( 'the_content', get_the_content( null, false, $post_id ) );
		}

		if ( strpos( $post->post_content, '<!--more-->' ) ) {
			return apply_filters( 'the_content', get_the_content( null, false, $post_id ) );
		}

		if ( has_excerpt() ) {
			return apply_filters( 'the_excerpt', get_the_excerpt( $post_id ) );
		}

		add_filter( 'excerpt_length', array( $this, 'change_excerpt_length' ), 10 );
		$content = get_the_excerpt( $post_id );
		remove_filter( 'excerpt_length', array( $this, 'change_excerpt_length' ), 10 );

		return apply_filters( 'the_excerpt', $content );
	}

	/**
	 * Get the excerpt length option casted as `int`.
	 *
	 * @return int
	 */
	private function get_excerpt_length() {
		return absint( round( get_theme_mod( 'neve_post_excerpt_length', '25' ) ) );
	}

	/**
	 * Change excerpt length.
	 *
	 * @return int
	 */
	public function change_excerpt_length() {
		return $this->get_excerpt_length();
	}
}
PK      \5#  #    views/partials/comments.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      02/11/2018
 *
 * @package neve
 */

namespace Neve\Views\Partials;

use Neve\Views\Base_View;

/**
 * Class Comments
 *
 * @package Neve\Views\Partials
 */
class Comments extends Base_View {

	/**
	 * Holds comment IDs that have an opened children tag.
	 *
	 * @var array
	 */
	private $comments_with_children = [];

	/**
	 * Add in functionality.
	 */
	public function init() {
		add_action( 'neve_do_comment_area', array( $this, 'render_comment_form' ) );
		add_filter( 'comment_form_defaults', array( $this, 'leave_reply_title_tag' ) );
		add_filter( 'comment_form_fields', array( $this, 'move_textarea' ) );
	}

	/**
	 * Render the comments form.
	 */
	public function render_comment_form() {
		$display_form_first    = apply_filters( 'neve_show_comment_form_first', false );
		$comment_form_settings = $this->get_sumbit_form_settings();

		if ( $display_form_first ) {
			comment_form( $comment_form_settings );
		}

		if ( have_comments() ) {
			$comments_wrap_classes = [ 'nv-comments-wrap' ];
			$is_boxed              = get_theme_mod( 'neve_comments_boxed_layout', false );
			if ( $is_boxed ) {
				$comments_wrap_classes[] = 'nv-is-boxed';
			}

			?>
			<div class="<?php echo esc_attr( implode( ' ', $comments_wrap_classes ) ); ?>">

				<div class="nv-comments-title-wrap">
					<?php
					echo '<h2 class="comments-title">';
					echo wp_kses_post( $this->get_comments_title() );
					echo '</h2>'
					?>
				</div>

				<ol class="nv-comments-list">
					<?php
					wp_list_comments(
						array(
							'callback'     => array( $this, 'comment_list_callback' ),
							'end-callback' => array( $this, 'end_comment_list_callback' ),
							'style'        => 'div',
						)
					);
					?>
				</ol>

			</div>

			<?php
			$this->maybe_render_comments_navigation();
		}

		if ( ! comments_open() &&
			get_comments_number() &&
			post_type_supports( get_post_type(), 'comments' ) ) {
			?>
			<p class="no-comments"><?php esc_html_e( 'Comments are closed.', 'neve' ); ?></p>
			<?php
		}
		if ( ! $display_form_first ) {
			comment_form( $comment_form_settings );
		}
	}

	/**
	 * Get forms settings.
	 *
	 * @return array
	 */
	private function get_sumbit_form_settings() {
		$form_settings = [];

		$form_title = get_theme_mod( 'neve_post_comment_form_title' );
		if ( ! empty( $form_title ) ) {
			$form_settings['title_reply'] = $form_title;
		}

		$submit_button_style           = get_theme_mod( 'neve_post_comment_form_button_style', 'primary' );
		$form_settings['class_submit'] = 'button button-' . esc_attr( $submit_button_style );

		$button_text = get_theme_mod( 'neve_post_comment_form_button_text' );
		if ( ! empty( $button_text ) ) {
			$form_settings['label_submit'] = $button_text;
		}

		$boxed_layout = get_theme_mod( 'neve_comments_form_boxed_layout', true );
		if ( $boxed_layout ) {
			$form_settings['class_container'] = 'comment-respond nv-is-boxed';
		}

		return $form_settings;
	}

	/**
	 * Render comment navigation if needed
	 *
	 * @return void
	 */
	private function maybe_render_comments_navigation() {
		if ( get_comment_pages_count() <= 1 || ! get_option( 'page_comments' ) ) {
			return;
		}

		$aria_label = __( 'Comments Navigation', 'neve' );
		?>
		<nav class="nv-comment-navigation" role="navigation" aria-label="<?php echo esc_attr( $aria_label ); ?>">
			<div class="nav-links">
				<div class="nav-previous">
					<?php previous_comments_link( __( 'Previous', 'neve' ) ); ?>
				</div>
				<div class="nav-next">
					<?php next_comments_link( __( 'Next', 'neve' ) ); ?>
				</div>
			</div>
		</nav>
		<?php
	}

	/**
	 * Comment list end callback.
	 *
	 * @param \WP_Comment $comment comment.
	 * @param array       $args    arguments.
	 * @param int         $depth   the comments depth.
	 */
	public function end_comment_list_callback( $comment, $args, $depth ) {
		if ( in_array( $comment->comment_ID, $this->comments_with_children ) ) {
			unset( $this->comments_with_children[ $comment->comment_ID ] );
			echo '</ol></li><!-- close children li -->';
		}
	}

	/**
	 * Comment list callback.
	 *
	 * @param \WP_Comment $comment comment.
	 * @param array       $args    arguments.
	 * @param int         $depth   the comments depth.
	 */
	public function comment_list_callback( $comment, $args, $depth ) {
		switch ( $comment->comment_type ) {

			case 'pingback':
			case 'trackback':
				?>
				<li <?php comment_class(); ?> id="comment-<?php comment_ID(); ?>">
					<p>
						<?php
						echo esc_html__( 'Pingback:', 'neve' ) . '&nbsp;';
						comment_author_link();
						edit_comment_link( '(' . esc_html__( 'Edit', 'neve' ) . ')', '&nbsp;<span class="edit-link">', '</span>' );
						?>
					</p>
				</li>
				<?php
				break;
			default:
				?>
				<li <?php comment_class(); ?> id="comment-item-<?php comment_ID(); ?>">
					<article id="comment-<?php comment_ID(); ?>" class="nv-comment-article">
						<?php
						echo '<div class="nv-comment-avatar">';
						echo get_avatar( $comment, 50 );
						echo '</div>';
						echo '<div class="comment-content">';
						?>
						<div class="nv-comment-header">
							<div class="comment-author vcard">
								<span class="fn author"><?php echo get_comment_author_link(); ?></span>
								<a href="<?php echo esc_url( get_comment_link() ); ?>">
									<time class="entry-date published"
											datetime="<?php echo esc_attr( get_comment_time( 'c' ) ); ?>"
											content="<?php echo esc_attr( get_comment_time( 'Y-m-d' ) ); ?>">
										<?php
										/* translators: 1: date, 2: time */
										echo sprintf( esc_html__( '%1$s at %2$s', 'neve' ), esc_html( get_comment_date() ), esc_html( get_comment_time() ) );
										?>
									</time>
								</a>
							</div>
							<?php
							$this->render_edit_reply_link( $args, $depth );
							?>
						</div>
						<div class="nv-comment-content comment nv-content-wrap">
							<?php comment_text(); ?>
							<?php if ( '0' === $comment->comment_approved ) { ?>
								<p class="comment-awaiting-moderation">
									<?php echo esc_html__( 'Comment awaiting moderation.', 'neve' ); ?>
								</p>
							<?php } ?>
						</div>
						<?php
						echo '</div>';
						?>
					</article>
				</li>
				<?php
				break;
		}
		if ( $args['has_children'] === true ) {
			array_push( $this->comments_with_children, $comment->comment_ID );
			echo '<li class="children" role="listitem"><ol>';
		}
	}

	/**
	 *  Render edit/reply link.
	 *
	 * @param array $args comment args.
	 * @param int   $depth the depth of comment.
	 */
	private function render_edit_reply_link( $args, $depth ) {
		?>
		<div class="edit-reply">
			<?php edit_comment_link( '(' . esc_html__( 'Edit', 'neve' ) . ')' ); ?>
			<?php
			comment_reply_link(
				array_merge(
					$args,
					array(
						'reply_text' => esc_html__( 'Reply', 'neve' ),
						'add_below'  => 'comment',
						'depth'      => $depth,
						'max_depth'  => $args['max_depth'],
						'before'     => '<span class="nv-reply-link">',
						'after'      => '</span>',
					)
				)
			);
			?>
		</div>
		<?php
	}

	/**
	 * Get the comments title.
	 *
	 * @return string
	 */
	private function get_comments_title() {

		$comments_number = number_format_i18n( get_comments_number() );
		$title           = get_the_title();

		$empty_comments_title =
			sprintf(
				esc_html(
					/* translators: number of comments */
					_nx(
						'%1$s thought on &ldquo;%2$s&rdquo;',
						'%1$s thoughts on &ldquo;%2$s&rdquo;',
						get_comments_number(),
						'comments title',
						'neve'
					)
				),
				$comments_number,
				$title
			);

		$comments_title = get_theme_mod( 'neve_post_comment_section_title' );
		if ( empty( $comments_title ) ) {
			return apply_filters( 'neve_filter_comments_title', $empty_comments_title );
		}

		$comments_title = str_replace( '{comments_number}', $comments_number, $comments_title );
		$comments_title = str_replace( '{title}', $title, $comments_title );

		return apply_filters( 'neve_filter_comments_title', $comments_title );
	}

	/**
	 * Change reply title tag to h2.
	 *
	 * @param array $args comment form args.
	 *
	 * @return array
	 */
	public function leave_reply_title_tag( $args ) {
		$tag = 'h2';

		$args['title_reply_before'] = '<' . $tag . ' id="reply-title" class="comment-reply-title">';
		$args['title_reply_after']  = '</' . $tag . '>';

		return $args;
	}


	/**
	 * Move textarea field in comment form after the website field.
	 *
	 * @param array $fields array of fields.
	 *
	 * @return array
	 */
	public function move_textarea( $fields ) {
		if ( ! isset( $fields['url'] ) ) {
			return $fields;
		}
		$keys = array_keys( $fields );

		$textarea_index = array_search( 'comment', $keys );
		// Remove textarea
		unset( $keys[ $textarea_index ] );
		// Reset indexes.
		$keys = array_values( $keys );

		// Get website url field index.
		$index = array_search( 'url', $keys );
		// Insert textarea after url field.
		array_splice( $keys, $index + 1, 0, 'comment' );

		$new_fields = [];
		foreach ( $keys as $key ) {
			$new_fields[ $key ] = $fields[ $key ];
		}

		return $new_fields;
	}
}
PK      \$B    -  views/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \p      views/base_view.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      17/08/2018
 *
 * @package Neve\Views
 */

namespace Neve\Views;

/**
 * The base view class.
 *
 * @package Neve\Views
 */
abstract class Base_View {
	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	abstract public function init();

	/**
	 * Get view path to include.
	 *
	 * @param string $view_slug the view to be loaded from `views` folder, without extension.
	 * @param array  $vars variables used in template.
	 *
	 * @return void
	 */
	public function get_view( $view_slug, $vars ) {
		if ( empty( $view_slug ) ) {
			return;
		}

		$args = apply_filters( 'neve_filter_view_data_' . $view_slug, $vars );

		$rest_of_path = 'views/' . $view_slug . '.php';

		$path = trailingslashit( get_stylesheet_directory() ) . $rest_of_path;

		if ( is_file( $path ) ) {
			include $path;

			return;
		}

		$path = trailingslashit( get_template_directory() ) . $rest_of_path;

		if ( is_file( $path ) ) {
			include $path;

			return;
		}
	}
}
PK      \u;  ;    views/nav_walker.phpnu W+A        <?php
/**
 * Custom navwalker.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      24/08/2018
 *
 * @package Neve\Views
 */

namespace Neve\Views;

use HFG\Core\Components\Nav;
use Neve\Core\Dynamic_Css;

/**
 * Class Nav_Walker
 *
 * @package Neve\Views
 */
class Nav_Walker extends \Walker_Nav_Menu {
	/**
	 * Flag to check if the mega menu CSS was already enqueued.
	 *
	 * @var bool
	 */
	public static $mega_menu_enqueued = false;

	/**
	 * Flag used to add inline sidebar accessibility styles.
	 *
	 * @var bool
	 */
	public static $add_sidebar_accessibility_style = false;

	/**
	 * Flag used to add inline mobile submenu button styles.
	 *
	 * @var bool
	 */
	public static $add_mobile_caret_button_style = false;

	/**
	 * Flag used to add inline mobile dropdown js.
	 *
	 * @var bool
	 */
	public static $dropdowns_inline_js_enqueued = false;

	/**
	 * Flag to check if the accessibility JS was already enqueued.
	 *
	 * @var bool
	 */
	public static $accessibility_menu_enqueued = false;

	/**
	 * Nav_Walker constructor.
	 */
	public function __construct() {
		add_filter( 'nav_menu_item_args', array( $this, 'tweak_mm_heading' ), 10, 3 );
		add_filter( 'nav_menu_item_title', array( $this, 'add_caret' ), 10, 4 );

		add_action( 'neve_after_header_wrapper_hook', [ $this, 'inline_style_for_sidebar' ], 9 );
	}

	/**
	 * Print inline styles if sidebar is used.
	 */
	public function inline_style_for_sidebar() {
		if ( self::$add_sidebar_accessibility_style ) {
			return;
		}
		echo '<style>' . $this->get_sidebar_and_accessibility_style() . '</style>'; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		self::$add_sidebar_accessibility_style = true;
	}

	/**
	 * Print inline styles if mobile submenu is used.
	 */
	public function inline_style_for_caret() {
		if ( self::$add_mobile_caret_button_style ) {
			return;
		}
		echo '<style>' . $this->get_mobile_caret_style() . '</style>'; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		self::$add_mobile_caret_button_style = true;
	}

	/**
	 * Add the caret inside the menu item link.
	 *
	 * @param string    $title menu item title.
	 * @param \WP_Post  $item menu item object.
	 * @param \stdClass $args menu args.
	 * @param int       $depth the menu item depth.
	 *
	 * @return string
	 */
	public function add_caret( $title, $item, $args, $depth ) {
		if ( neve_is_amp() ) {
			return $title;
		}

		if ( strpos( $title, 'class="caret"' ) ) {
			return $title;
		}

		if ( strpos( $args->menu_id, 'nv-primary-navigation' ) === false ) {
			return $title;
		}

		if ( ! isset( $item->classes ) || ! is_array( $item->classes ) ) {
			return $title;
		}

		$args->before = '';
		$args->after  = '';

		$default_caret_settings = [
			'side'      => is_rtl() ? 'left' : 'right',
			'icon_type' => 'icon',
			'icon'      => '<svg fill="currentColor" aria-label="' . esc_attr__( 'Dropdown', 'neve' ) . '" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"/></svg>',
		];

		$component_id    = $args->component_id ?? '';
		$caret_settings  = apply_filters( 'neve_submenu_icon_settings', $default_caret_settings, $component_id );
		$caret_pictogram = $this->get_caret_pictogram( $caret_settings );


		$is_sidebar_item = strpos( $args->menu_id, 'sidebar' ) !== false;
		// We add tabindex 0 in order for the caret to  be focusable.
		$expanded = 'tabindex="0"';

		// Register sidebar inline styles
		if ( $item->url === '#' && ! self::$dropdowns_inline_js_enqueued ) { // @phpstan-ignore-line url is defined on WP_Post object that is used as Menu Item.
			$this->enqueue_hash_url_dropdowns_inline_js();
		}

		$args->before = '<div class="wrap">';
		$args->after  = '</div>';

		if ( in_array( 'menu-item-has-children', $item->classes, true ) ) {
			add_action( 'neve_after_header_wrapper_hook', [ $this, 'inline_style_for_caret' ], 9 );
			if ( strpos( $title, 'menu-item-title-wrap' ) === false ) {
				$title = '<span class="menu-item-title-wrap dd-title">' . $title . '</span>';
			}

			$caret_wrap_css = $caret_settings['side'] === 'right' ? 'margin-left:5px;' : 'margin-right:5px;';

			if ( $is_sidebar_item ) {
				$expand_dropdowns = apply_filters( 'neve_first_level_expanded', false );
				$additional_class = $expand_dropdowns && $depth === 0 ? 'dropdown-open' : '';

				$caret  = '<button ' . $expanded . ' type="button" class="caret-wrap navbar-toggle ' . esc_attr( (string) $item->menu_order ) . ' ' . esc_attr( $additional_class ) . '" style="' . esc_attr( $caret_wrap_css ) . '"  aria-label="' . __( 'Toggle', 'neve' ) . ' ' . wp_filter_nohtml_kses( $title ) . '">';
				$caret .= $caret_pictogram;
				$caret .= '</button>';

				if ( $caret_settings['side'] === 'left' ) {
					$args->before = $args->before . $caret;
				} else {
					$args->after = $caret . $args->after;
				}
			} else {

				$caret  = '<div role="button" aria-pressed="false" aria-label="' . __( 'Open Submenu', 'neve' ) . '" ' . $expanded . ' class="caret-wrap caret ' . $item->menu_order . '" style="' . esc_attr( $caret_wrap_css ) . '">';
				$caret .= $caret_pictogram;
				$caret .= '</div>';

				if ( $caret_settings['side'] === 'left' ) {
					$args->before = $args->before . $caret;
				} else {
					$args->after = $caret . $args->after;
				}
			}
		}



		return $title;
	}

	/**
	 * Get submenu icon.
	 *
	 * @param array $settings Submenu icon settings.
	 *
	 * @return string
	 */
	private function get_caret_pictogram( $settings ) {
		$pictogram = $settings['icon'];

		if ( ! isset( $settings['icon_type'] ) ) {
			return '<span class="caret">' . $pictogram . '</span>';
		}

		if ( $settings['icon_type'] === 'image' && array_key_exists( 'image', $settings ) && ! empty( $settings['image'] ) ) {
			$pictogram = wp_get_attachment_image( $settings['image'], 'thumbnail', true );
		}

		return '<span class="caret">' . $pictogram . '</span>';
	}

	/**
	 * Get mobile caret inline styles
	 */
	public function get_mobile_caret_style() {
		/* Mobile button caret css. */
		$mobile_button_caret_css  = '.header-menu-sidebar .nav-ul li .wrap { padding: 0 4px; }';
		$mobile_button_caret_css .= '.header-menu-sidebar .nav-ul li .wrap a { flex-grow: 1; display: flex; }';
		$mobile_button_caret_css .= '.header-menu-sidebar .nav-ul li .wrap a .dd-title { width: var(--wrapdropdownwidth); }';
		$mobile_button_caret_css .= '.header-menu-sidebar .nav-ul li .wrap button { border: 0; z-index: 1; background: 0; }';
		$mobile_button_caret_css .= '.header-menu-sidebar .nav-ul li.menu-item-has-children:not([class*=block])  > .wrap > a { margin-right: calc(-1em - (18px*2));}';

		return Dynamic_Css::minify_css( $mobile_button_caret_css );
	}

	/**
	 * Get sidebar inline styles and accessibility
	 */
	public function get_sidebar_and_accessibility_style() {
		/* Showing Menu Sidebar animation css. */
		$sidebar_animation_css  = '.is-menu-sidebar .header-menu-sidebar { visibility: visible; }';
		$sidebar_animation_css .= '.is-menu-sidebar.menu_sidebar_slide_left .header-menu-sidebar { transform: translate3d(0, 0, 0); left: 0; }';
		$sidebar_animation_css .= '.is-menu-sidebar.menu_sidebar_slide_right .header-menu-sidebar { transform: translate3d(0, 0, 0); right: 0; }';
		$sidebar_animation_css .= '.is-menu-sidebar.menu_sidebar_pull_right .header-menu-sidebar, .is-menu-sidebar.menu_sidebar_pull_left .header-menu-sidebar { transform: translateX(0); }';
		$sidebar_animation_css .= '.is-menu-sidebar.menu_sidebar_dropdown .header-menu-sidebar { height: auto; }';
		$sidebar_animation_css .= '.is-menu-sidebar.menu_sidebar_dropdown .header-menu-sidebar-inner { max-height: 400px; padding: 20px 0; }';
		$sidebar_animation_css .= '.is-menu-sidebar.menu_sidebar_full_canvas .header-menu-sidebar { opacity: 1; }';
		$sidebar_animation_css .= '.header-menu-sidebar .menu-item-nav-search { pointer-events: none; }';
		$sidebar_animation_css .= '.header-menu-sidebar .menu-item-nav-search .is-menu-sidebar & { pointer-events: unset; }';
		/* Accessibility css. */
		$accessibility_caret_css  = '.nav-ul li:focus-within .wrap.active + .sub-menu { opacity: 1; visibility: visible; }';
		$accessibility_caret_css .= '.nav-ul li.neve-mega-menu:focus-within .wrap.active + .sub-menu { display: grid; }';
		$accessibility_caret_css .= '.nav-ul li > .wrap { display: flex; align-items: center; position: relative; padding: 0 4px; }';
		$accessibility_caret_css .= '.nav-ul:not(.menu-mobile):not(.neve-mega-menu) > li > .wrap > a { padding-top: 1px }';

		return Dynamic_Css::minify_css( $sidebar_animation_css . $accessibility_caret_css );
	}

	/**
	 * Start_el
	 *
	 * @param string    $output Output.
	 * @param \WP_Post  $item Item.
	 * @param int       $depth Depth.
	 * @param \stdClass $args Args.
	 * @param int       $id id.
	 * @since 3.0.0
	 *
	 * @see   Walker::start_el()
	 */
	public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {
		if ( ! is_object( $args ) ) {
			return;
		}

		// Only enqueue accessibility js listeners if menu uses sub-menus.
		if ( ! self::$accessibility_menu_enqueued && $args->walker->has_children ) {
			$this->enqueue_accessibility_menu_js();
		}

		if ( ! self::$mega_menu_enqueued && isset( $item->classes ) && in_array( 'neve-mega-menu', $item->classes ) ) {
			$this->enqueue_mega_menu_style();
		}

		if ( isset( $item->title ) && $item->title === 'divider' ) {
			$classes = [ 'neve-mm-divider' ];

			if ( isset( $item->classes ) ) {
				$classes = array_merge( $classes, $item->classes );
			}

			$output .= '<li role="presentation" class="' . esc_attr( join( ' ', $classes ) ) . '">';

			return;
		}

		if ( isset( $item->classes ) && in_array( 'neve-mm-col', $item->classes, true ) ) {
			$output .= '<li class="' . esc_attr( join( ' ', $item->classes ) ) . '">';

			return;
		}

		parent::start_el( $output, $item, $depth, $args, $id );
	}

	/**
	 * Ends the element output, if needed.
	 *
	 * @param string    $output the end el string.
	 * @param \WP_Post  $item item.
	 * @param int       $depth item depth.
	 * @param \stdClass $args item args.
	 */
	public function end_el( &$output, $item, $depth = 0, $args = null ) {
		if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
			$t = '';
			$n = '';
		} else {
			$t = "\t";
			$n = "\n";
		}
		if ( $depth >= 1 ) {
			if ( isset( $item->description ) && ! empty( $item->description ) ) {
				if ( ! self::$mega_menu_enqueued ) {
					$this->enqueue_mega_menu_style();
				}
				$output .= '<div class="neve-mm-description">' . esc_html( $item->description ) . '</div>';
			}
		}
		$output .= "</li>{$n}";
	}


	/**
	 * Tweak the mega menu heading markup.
	 *
	 * @param array    $args the menu item args.
	 * @param \WP_Post $item the menu item.
	 * @param int      $depth the depth of the menu item.
	 *
	 * @return mixed
	 */
	public function tweak_mm_heading( $args, $item, $depth ) {
		if ( ! isset( $item->classes ) ) {
			return $args;
		}

		if ( in_array( 'neve-mm-heading', $item->classes, true ) ) {
			if ( $item->url === '#' ) { // @phpstan-ignore-line url is defined on WP_Post object that is used as Menu Item.
				add_filter(
					'walker_nav_menu_start_el',
					function ( $item_output, $nav_item, $depth, $args ) use ( $item ) {
						if ( $nav_item !== $item ) {
							return $item_output;
						}

						$item_output = '';

						$item_output .= $args->before;
						$item_output .= '<span>';
						$item_output .= $args->link_before . $nav_item->title . $args->link_after; // @phpstan-ignore-line title is defined on WP_Post object that is used as Menu Item.
						$item_output .= '</span>';
						$item_output .= $args->after;

						return $item_output;
					},
					10,
					4
				);
			}
		}

		return $args;
	}

	/**
	 * Display all pages when there is no menu assigned to the primary location
	 */
	public static function fallback() {
		$fallback_args = array(
			'depth'      => - 1,
			'menu_id'    => 'nv-primary-navigation-' . \HFG\current_row( \HFG\Core\Builder\Header::BUILDER_NAME ),
			'menu_class' => 'primary-menu-ul nav-ul',
			'container'  => 'ul',
			'before'     => '',
			'echo'       => false,
			'after'      => '',
		);

		return wp_page_menu( $fallback_args );
	}

	/**
	 * Enqueue menu accessibility script
	 */
	public function enqueue_accessibility_menu_js() {
		if ( self::$accessibility_menu_enqueued ) {
			return;
		}

		$script = <<<'JS'
var menuCarets = document.querySelectorAll(
		'.nav-ul li > .wrap > .caret'
	);
menuCarets.forEach( function (caretElem) {
	caretElem.addEventListener( "keydown", (event) => {
		if ( event.keyCode === 13 ) {
			event.target.parentElement.classList.toggle('active');
            if ( event.target.getAttribute('aria-pressed') ) {
				event.target.setAttribute('aria-pressed', 'true' === event.target.getAttribute('aria-pressed') ? 'false' : 'true');
			}
		}
	});
	caretElem.parentElement.parentElement.addEventListener( "focusout", (event) => {
		// If focus is still in the element, do nothing
		if ( caretElem.parentElement.parentElement.contains(event.relatedTarget) ) {
			return;
		};
		caretElem.parentElement.classList.remove('active');
        caretElem.setAttribute('aria-pressed', 'false');
	});
} );
JS;

		$script_min_js = <<<'JSMIN'
var menuCarets=document.querySelectorAll(".nav-ul li > .wrap > .caret");menuCarets.forEach(function(e){e.addEventListener("keydown",e=>{13===e.keyCode&&(e.target.parentElement.classList.toggle("active"),e.target.getAttribute("aria-pressed")&&e.target.setAttribute("aria-pressed","true"===e.target.getAttribute("aria-pressed")?"false":"true"))}),e.parentElement.parentElement.addEventListener("focusout",t=>{!e.parentElement.parentElement.contains(t.relatedTarget)&&(e.parentElement.classList.remove("active"),e.setAttribute("aria-pressed","false"))})});
JSMIN;


		wp_add_inline_script( 'neve-script', ( NEVE_DEBUG ) ? $script : $script_min_js );
		self::$accessibility_menu_enqueued = true;
	}
	/**
	 * Enqueue mega menu style
	 */
	public function enqueue_mega_menu_style() {
		if ( self::$mega_menu_enqueued ) {
			return;
		}

		wp_enqueue_style( 'neve-mega-menu' );
		self::$mega_menu_enqueued = true;
	}

	/**
	 * Enqueue JS for dropdowns with hash link.
	 */
	public function enqueue_hash_url_dropdowns_inline_js() {
		if ( self::$dropdowns_inline_js_enqueued ) {
			return;
		}

		// Fix for MegaMenu alignment
		$script = <<<'JS'
function initNoLinkDD() {
    var noLinkDDs = document.querySelectorAll(
		'.header-menu-sidebar-inner .menu-item-has-children a[href="#"]'
	);

    if( noLinkDDs.length < 1 ) {
        return;
	}

    noLinkDDs.forEach( function (noLinkDD) {
        var dropdownButton = noLinkDD.parentElement.querySelector('button');
		noLinkDD.addEventListener('click', function (e) {
			e.preventDefault();
            dropdownButton.click();
		});
	});
}
window.addEventListener('DOMContentLoaded', initNoLinkDD);
JS;
		wp_add_inline_script( 'neve-script', $script );
		self::$dropdowns_inline_js_enqueued = true;
	}
}
PK      \DuA  A    views/header.phpnu W+A        <?php
/**
 * Header View Manager
 *
 * @package Neve\Views
 */

namespace Neve\Views;

use HFG\Core\Components\Nav;

/**
 * Class Header
 *
 * @package Neve\Views
 */
class Header extends Base_View {
	/**
	 * Nav instance number
	 *
	 * @var int
	 */
	public static $primary_nav_instance_no = 1;
	/**
	 * Add hooks for the front end.
	 */
	public function init() {
		add_filter( 'wp_nav_menu_items', array( $this, 'add_last_menu_item' ), 10, 2 );
		add_filter( 'wp_page_menu', array( $this, 'add_fallback_last_menu_items' ), 10, 2 );
		add_action( 'wp_enqueue_scripts', array( $this, 'hide_last_menu_item_search_in_sidebar' ) );
	}

	/**
	 * Hide last menu item search in mobile sidebar.
	 */
	public function hide_last_menu_item_search_in_sidebar() {
		if ( neve_is_new_builder() ) {
			return;
		}

		wp_add_inline_style(
			'neve-style',
			'.header-menu-sidebar-inner li.menu-item-nav-search { display: none; }
		[data-row-id] .row { display: flex !important; align-items: center; flex-wrap: unset;}
		@media (max-width: 960px) { .footer--row .row { flex-direction: column; } }'
		);
	}

	/**
	 * Add the last menu item.
	 *
	 * @param string $items the nav menu markup.
	 * @param object $args  menu properties.
	 *
	 * @return string
	 */
	public function add_last_menu_item( $items, $args ) {
		if ( $args->theme_location !== 'primary' ) {
			return $items;
		}

		if ( strpos( $args->menu_class, 'max-mega-menu' ) ) {
			return $items;
		}

		$items = $this->get_last_menu_items_markup( $items );

		return $items;
	}

	/**
	 * Get the last menu items.
	 *
	 * @param string $items the menu markup.
	 *
	 * @return string
	 */
	private function get_last_menu_items_markup( $items = '' ) {
		$additional_items = $this->get_last_menu_item_setting();
		$additional_items = json_decode( $additional_items, true );
		if ( empty( $additional_items ) ) {
			return $items;
		}
		foreach ( $additional_items as $item ) {
			if ( $item === 'search' ) {
				$items .= $this->get_nav_menu_search();
			} elseif ( $item === 'cart' ) {
				$items .= $this->get_nav_menu_cart();
			} else {
				$items .= apply_filters( 'neve_last_menu_item_' . $item, '' );
			}
		}

		return apply_filters( 'neve_last_menu_item', $items );
	}

	/**
	 * Get the last menu item theme mod value.
	 *
	 * @return string
	 */
	private function get_last_menu_item_setting() {
		$default           = array();
		$current_component = 'default';
		if ( isset( Nav::$current_component ) ) {
			$current_component = Nav::$current_component;
		}
		$last_menu_setting_slug = apply_filters( 'neve_last_menu_setting_slug_' . $current_component, 'neve_last_menu_item' );

		return get_theme_mod( $last_menu_setting_slug, wp_json_encode( $default ) );
	}

	/**
	 * Get the markup for the nav menu search.
	 *
	 * @param bool $responsive should get the responsive version.
	 *
	 * @return string
	 */
	private function get_nav_menu_search( $responsive = false ) {
		// TODO when HFG is live we should drop this at all as we have a section for icon, or offer a way of disabling it.
		$tag   = 'li';
		$class = 'menu-item-nav-search minimal';
		if ( $responsive === true ) {
			$tag = 'span';

			$class .= ' responsive-nav-search ';
		}
		$search = '';

		$id = 'nv-menu-item-search-' . self::$primary_nav_instance_no;

		$search     .= '<' . esc_attr( $tag ) . ' class="' . esc_attr( $class ) . '" id="' . esc_attr( $id ) . '"  aria-label="search">';
		$extra_attrs = apply_filters( 'neve_search_menu_item_filter', '', self::$primary_nav_instance_no );
		$search     .= '<a href="#" class="nv-nav-search-icon" ' . $extra_attrs . '>' . neve_search_icon() . '</a>';
		$search     .= '<div class="nv-nav-search">';
		if ( $responsive === true ) {
			$search .= '<div class="container close-container">';
			$search .= '<a class="button button-secondary close-responsive-search">' . __( 'Close', 'neve' ) . '</a>';
			$search .= '</div>';
		}
		if ( version_compare( get_bloginfo( 'version' ), '5.2.0', '>=' ) ) {
			$search .= get_search_form( array( 'echo' => false ) );
		} else {
			$search .= get_search_form( false ); // @phpstan-ignore-line
		}
		$search .= '</div>';
		$search .= '</' . esc_attr( $tag ) . '>';

		self::$primary_nav_instance_no ++;

		return $search;
	}

	/**
	 * Get the markup for the nav menu cart.
	 *
	 * @param bool $responsive should get the responsive version.
	 *
	 * @return string
	 */
	private function get_nav_menu_cart( $responsive = false ) {
		if ( ! class_exists( 'WooCommerce', false ) ) {
			return '';
		}

		$tag   = 'li';
		$class = 'menu-item-nav-cart';
		if ( $responsive === true ) {
			$tag = 'span';

			$class .= ' responsive-nav-cart ';
		}
		$cart = '';

		$cart .= '<' . esc_attr( $tag ) . ' class="' . esc_attr( $class ) . '"><a href="' . esc_url( wc_get_cart_url() ) . '" class="cart-icon-wrapper">';
		$cart .= neve_cart_icon();
		$cart .= '<span class="screen-reader-text">' . __( 'Cart', 'neve' ) . '</span>';
		$cart .= '<span class="cart-count">' . WC()->cart->get_cart_contents_count() . '</span>';
		$cart .= '</a>';

		if ( is_cart() || is_checkout() ) {
			$cart .= '</' . esc_attr( $tag ) . '>';

			return $cart;
		}

		if ( $responsive === false ) {
			ob_start();
			the_widget(
				'WC_Widget_Cart',
				array(
					'title'         => ' ',
					'hide_if_empty' => true,
				),
				array(
					'before_widget' => $this->before_cart_popup(),
					'after_widget'  => $this->after_cart_popup(),
					'before_title'  => '',
					'after_title'   => '',
				)
			);
			$cart_widget = ob_get_contents();
			ob_end_clean();
			$cart .= $cart_widget;
		}

		$cart .= '</' . esc_attr( $tag ) . '>';

		return $cart;
	}

	/**
	 * Markup before cart popup widget in header.
	 *
	 * @return string
	 */
	private function before_cart_popup() {
		ob_start();
		echo '<div class="nv-nav-cart widget">';
		echo '<div class="widget woocommerce widget_shopping_cart">';
		do_action( 'neve_before_cart_popup' );
		$markup = ob_get_contents();
		ob_end_clean();
		return $markup;
	}

	/**
	 * Markup after cart popup widget in header.
	 *
	 * @return string
	 */
	private function after_cart_popup() {
		ob_start();
		do_action( 'neve_after_cart_popup' );
		echo '</div>';
		echo '</div>';
		$markup = ob_get_contents();
		ob_end_clean();
		return $markup;
	}


	/**
	 * Add last menu items to fallback menu.
	 *
	 * @param string $menu the menu markup.
	 * @param array  $args the menu args.
	 *
	 * @return string;
	 */
	public function add_fallback_last_menu_items( $menu, $args ) {
		if ( strpos( $args['menu_id'], 'nv-primary-navigation' ) === false ) {
			return $menu;
		}

		$items = $this->get_last_menu_items_markup( '' );

		$menu = str_replace( '</ul>', $items . '</ul>', $menu );

		return $menu;
	}
}
PK      \FA[7  7    views/tweaks.phpnu W+A        <?php
/**
 * Generic Tweaks
 *
 * @package Neve\Views
 */

namespace Neve\Views;

/**
 * Class Tweaks
 */
class Tweaks extends Base_View {
	/**
	 * Add hooks for the front end.
	 */
	public function init() {
		// Remove gallery default style.
		add_filter( 'use_default_gallery_style', '__return_false' );
	}
}
PK      \@Z!  !    views/font_manager.phpnu W+A        <?php
/**
 * Handles Google Fonts enqueueing.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      22/08/2018
 *
 * @package Neve\Views
 */

namespace Neve\Views;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;

/**
 * Class Typography
 *
 * @package Neve\Views
 */
class Font_Manager extends Base_View {
	/**
	 * The font families buffer.
	 *
	 * @var array
	 */
	public static $font_families = array();

	/**
	 * Body font family variants.
	 *
	 * @var array
	 */
	public static $body_variants = array();

	/**
	 * Add a google font.
	 *
	 * @param string $font_family font family.
	 * @param string $font_weight font weight.
	 */
	final public static function add_google_font( $font_family, $font_weight = '400' ) {
		if ( empty( $font_family ) ) {
			$body_font = self::get_body_font_family();
			if ( empty( $body_font ) ) {
				return;
			}
			$font_family = $body_font;
		}

		$allowed_variants = [
			'100',
			'200',
			'300',
			'400',
			'500',
			'600',
			'700',
			'800',
			'900',
			'100italic',
			'200italic',
			'300italic',
			'400italic',
			'500italic',
			'600italic',
			'700italic',
			'800italic',
			'900italic',
		];

		if ( ! in_array( $font_weight, $allowed_variants, true ) ) {
			$font_weight = '400';
		}

		if ( array_key_exists( $font_family, self::$font_families ) ) {
			self::$font_families[ $font_family ][] = $font_weight;
			self::$font_families[ $font_family ]   = array_unique( self::$font_families[ $font_family ] );

			return;
		}
		self::$font_families[ $font_family ] = [ $font_weight ];
	}

	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		$this->add_body_variants();
		add_action( 'wp_enqueue_scripts', array( $this, 'register_google_fonts' ), 200 );
		add_action( 'enqueue_block_editor_assets', array( $this, 'register_google_fonts' ), 200 );
		add_action( 'enqueue_block_editor_assets', array( $this, 'do_editor_styles_google_fonts' ), 210 );
		add_action( 'wp_print_styles', array( $this, 'load_external_fonts_locally' ), PHP_INT_MAX );
	}

	/**
	 * Add body font family variants.
	 */
	private function add_body_variants() {
		$body_font   = self::get_body_font_family();
		$gfonts_data = neve_get_google_fonts( true );

		// Variations are only available for Google fonts.
		if ( ! in_array( $body_font, array_keys( $gfonts_data ) ) ) {
			return;
		}

		// If there are no variations statically defined then don't do anything.
		if ( empty( $gfonts_data[ $body_font ] ) ) {
			return;
		}

		$variations = $this->get_body_variations();

		sort( $variations );

		foreach ( $variations as $variation ) {
			self::add_google_font( $body_font, $variation );
		}
	}

	/**
	 * Get body font family.
	 *
	 * @return string
	 */
	public static function get_body_font_family() {
		$body_mod     = Config::MODS_FONT_GENERAL;
		$body_default = Mods::get_alternative_mod_default( Config::MODS_FONT_GENERAL );

		return Mods::get( $body_mod, $body_default );
	}

	/**
	 * Get body variations.
	 *
	 * @return string[]
	 */
	private function get_body_variations() {
		return get_theme_mod( Config::MODS_FONT_GENERAL_VARIANTS, [] );
	}

	/**
	 * Register the fonts that are selected in customizer.
	 */
	public function register_google_fonts() {
		foreach ( self::$font_families as $font_family => $font_weights ) {
			$this->enqueue_google_font( $font_family, $font_weights );
		}
	}

	/**
	 * Will register fonts for the editor preview (mobile|tablet)
	 */
	public function do_editor_styles_google_fonts() {
		if ( empty( self::$font_families ) ) {
			return;
		}
		add_theme_support( 'editor-styles' );
		$style_array = array(
			'editor-styles.css',
		);
		foreach ( self::$font_families as $font_family => $font_weights ) {
			$url = $this->enqueue_google_font( $font_family, $font_weights, true );
			if ( ! empty( $url ) ) {
				array_push( $style_array, 'https:' . $url );
			}
		}
		add_editor_style(
			$style_array
		);
	}

	/**
	 * Enqueues a Google Font
	 *
	 * @param string  $font font string.
	 * @param array   $weights font weights.
	 * @param boolean $skip_enqueue flag to skip enqueue and return url.
	 *
	 * @since 1.1.38
	 */
	private function enqueue_google_font( $font, $weights = [], $skip_enqueue = false ) {
		// Get list of all Google Fonts.
		$google_fonts = neve_get_google_fonts( true );

		$font = str_replace( '"', '', $font );

		// Make sure font is in our list of fonts.
		if ( ! in_array( $font, array_keys( $google_fonts ), true ) ) {
			return;
		}

		// Make sure 400 font weight is always included.
		$weights = array_unique( array_merge( $weights, [ '400' ] ) );

		// In customizer, all font weights should be active for the preview.
		if ( is_customize_preview() ) {
			$weights = isset( $google_fonts[ $font ] ) ? $google_fonts[ $font ] : [
				'100',
				'200',
				'300',
				'400',
				'500',
				'600',
				'700',
				'800',
				'900',
			];
		}

		// Sanitize font name.
		$url_string = trim( $font );

		$base_url = '//fonts.googleapis.com/css';

		// Add weights to URL.
		if ( ! empty( $weights ) ) {
			$url_string .= ':' . implode( ',', $weights );
		}

		$query_args = array(
			'family'  => urlencode( $url_string ),
			'display' => 'swap',
		);

		$subsets = [
			'ru_RU' => 'cyrillic',
			'bg_BG' => 'cyrillic',
			'he_IL' => 'hebrew',
			'el'    => 'greek',
			'vi'    => 'vietnamese',
			'uk'    => 'cyrillic',
			'cs_CZ' => 'latin-ext',
			'ro_RO' => 'latin-ext',
			'pl_PL' => 'latin-ext',
		];
		$locale  = get_locale();

		if ( isset( $subsets[ $locale ] ) ) {
			$query_args['subset'] = urlencode( $subsets[ $locale ] );
		}
		$url = add_query_arg( $query_args, $base_url );

		if ( $skip_enqueue ) {
			return $url;
		}

		wp_enqueue_style( 'neve-google-font-' . str_replace( ' ', '-', strtolower( $font ) ), $url, array(), NEVE_VERSION );
	}

	/**
	 * Load Google Fonts locally.
	 *
	 * @return void
	 */
	public function load_external_fonts_locally() {

		$toggle = get_option( Config::OPTION_LOCAL_GOOGLE_FONTS_HOSTING, false );

		/**
		 * Filters whether the remote fonts should be hosted locally.
		 *
		 * This filter applies for both Google Fonts and Typekit Fonts if the Typekit module is used.
		 *
		 * @param bool $should_enqueue_locally Whether the Google Fonts should be hosted locally. Default value is false.
		 *
		 * @since 2.11
		 */
		$should_enqueue_locally = apply_filters( 'neve_load_remote_fonts_locally', $toggle );

		if ( ! (bool) $should_enqueue_locally ) {
			return;
		}

		$is_admin_context = is_admin() || is_customize_preview();
		$wptt_vendor_file = trailingslashit( get_template_directory() ) . 'vendor/wptt/webfont-loader/wptt-webfont-loader.php';

		if ( $is_admin_context || ! is_readable( $wptt_vendor_file ) ) {
			return;
		}

		/**
		 * Bail when in Elementor edit mode.
		 *
		 * We do this so that initial load of Elementor editor will not be slowed down by Neve Pro downloading Roboto fonts.
		 */
		if ( class_exists( '\Elementor\Plugin' ) ) {
			global $post;
			if ( \Elementor\Plugin::$instance->editor->is_edit_mode() ||
				\Elementor\Plugin::$instance->preview->is_preview_mode()
				) {
				return;
			}
		}

		/**
		 * Allow filtering for additional external font providers.
		 *
		 * The domains provided to this filter should be from services that link directly to a CSS file or else WPTT will not be able to download the fonts.
		 */
		$external_font_domains = apply_filters(
			'neve_font_providers',
			array(
				'fonts.googleapis.com/css',
				'use.typekit.net',
			)
		);

		global $wp_styles;
		$enqueued_styles = $wp_styles->queue ?? '';

		if ( ! is_array( $enqueued_styles ) ) {
			return;
		}

		$external_fonts = array();

		foreach ( $enqueued_styles as $style ) {

			$source = $wp_styles->registered[ $style ]->src;

			foreach ( $external_font_domains as $domain ) {

				if ( strpos( $source, $domain ) !== false ) {
					$parts = wp_parse_url( $source );
					/**
					 * Some plugins might set font url without protocol example //fonts.googleapis.com...
					 * Lets build the url ourselves to be sure the its whats expected.
					*/
					$normalized_source = 'https://' . $parts['host'] . $parts['path'] . ( isset( $parts['query'] ) ? '?' . $parts['query'] : '' );
					$external_fonts[]  = $normalized_source;
					// Dequeue this handle since we are going to load the font locally
					wp_dequeue_style( $style );
				}
			}
		}

		$external_fonts = array_unique( $external_fonts );

		require_once $wptt_vendor_file;

		foreach ( $external_fonts as $font_link ) {
			wp_add_inline_style( 'neve-style', wptt_get_webfont_styles( $font_link ) );
		}

	}

}
PK      \:|Ͼ      views/content_none.phpnu W+A        <?php
/**
 * Content none class.
 *
 * @package Neve\Views
 */

namespace Neve\Views;

/**
 * Class Content_None
 *
 * @package Neve\Views
 */
class Content_None extends Base_View {

	/**
	 * Init function.
	 */
	public function init() {
		add_filter( 'get_search_form', [ $this, 'add_instance_id' ] );
		add_action( 'neve_do_content_none', array( $this, 'render_content_none' ) );
	}

	/**
	 * Add input inside the HTML of search form to differentiate the instances.
	 *
	 * @param string $form Form HTML.
	 *
	 * @since   2.4.0
	 * @access  public
	 * @return string
	 */
	public function add_instance_id( $form ) {
		$form = str_replace( 'search-submit', 'search-submit nv-submit', $form );

		if ( ! isset( $_GET['form-instance'] ) ) {
			return $form;
		}

		$component_id = sanitize_text_field( $_GET['form-instance'] );
		$form         = str_replace( '</label>', '</label><input type="hidden" name="form-instance" value="' . esc_attr( $component_id ) . '">', $form );
		return $form;
	}

	/**
	 * Render content none;
	 */
	public function render_content_none() {
		echo '<div class="col-12 nv-content-none-wrap">';
		if ( is_home() && current_user_can( 'publish_posts' ) ) {
			echo '<p>';

			printf(
				/* translators: %s is Link to new post */
				esc_html__( 'Ready to publish your first post? %s.', 'neve' ),
				sprintf(
					/* translators: %1$s is Link to new post, %2$s is Get started here */
					'<a href="%1$s">%2$s</a>',
					esc_url( admin_url( 'post-new.php' ) ),
					esc_html__( 'Get started here', 'neve' )
				)
			);

			echo '</p>';
		} elseif ( is_search() ) {
			$this->render_search_none();
		}

		echo '</div>';
		comments_template();
	}

	/**
	 * Render Search 404.
	 */
	private function render_search_none() {
		echo '<p>';
		esc_html_e( 'Sorry, but nothing matched your search terms. Please try again with some different keywords.', 'neve' );
		echo '</p>';
		echo '<div class="nv-seach-form-wrap">';
		get_search_form();
		echo '</div>';
	}
}
PK      \FL  L    views/product_layout.phpnu W+A        <?php
/**
 * Product layout class.
 *
 * @package Neve\Views
 */

namespace Neve\Views;

/**
 * Class Product_Layout
 *
 * @package Neve\Views
 */
class Product_Layout extends Base_View {

	/**
	 * Init function.
	 */
	public function init() {
		if ( ! $this->should_load() ) {
			return;
		}
		add_action( 'woocommerce_after_single_product_summary', array( $this, 'render_exclusive_products_section' ), 20 );
		add_filter( 'body_class', array( $this, 'body_classes' ) );
		add_action( 'woocommerce_before_shop_loop_item', array( $this, 'card_content_wrapper' ), 1 );
		add_action( 'woocommerce_after_shop_loop_item', array( $this, 'wrapper_close_div' ), 100 );

		// Wrap product image in a div and add another div for buttons on image option
		add_action( 'woocommerce_before_shop_loop_item_title', array( $this, 'product_image_wrap' ), 8 );
		add_action( 'woocommerce_before_shop_loop_item_title', array( $this, 'out_of_stock_badge' ), 9 );

		// We are using this twice since product_image_wrap is opening two divs which needs to be closed.
		add_action( 'woocommerce_before_shop_loop_item_title', array( $this, 'wrapper_close_div' ), 11 );
		add_action( 'woocommerce_before_shop_loop_item_title', array( $this, 'wrapper_close_div' ), 14 );

	}

	/**
	 * Product image wrapper.
	 */
	public function product_image_wrap() {
		$product_classes = apply_filters( 'neve_wrapper_class', '' );
		echo '<div class="sp-product-image ' . esc_attr( $product_classes ) . '">';
		/**
		 * Fires before the product warpper is rendered.
		 *
		 * @since 2.11
		 */
		do_action( 'neve_product_image_wrap_before' );
		echo '<div class="img-wrap">';
	}

	/**
	 * Closing tag
	 */
	public function wrapper_close_div() {
		echo '</div>';
	}
	/**
	 * Add out of stock label.
	 */
	public function out_of_stock_badge() {
		global $product;
		if ( $product->is_in_stock() ) {
			return;
		}
		$out_of_stock_label = apply_filters( 'nv_out_of_stock_text', __( 'Out of stock', 'neve' ) );

		echo '<div class="out-of-stock-badge">';
		echo wp_kses_post( $out_of_stock_label );
		echo '</div>';
	}

	/**
	 * Wrapper for card content.
	 */
	public function card_content_wrapper() {
		$card_attributes = apply_filters(
			'nv_product_card_wrapper_attributes',
			[
				'class' => 'nv-card-content-wrapper',
			]
		);

		$attributes = '';
		foreach ( $card_attributes as $attr => $val ) {
			$attributes .= ' ' . $attr . '="' . $val . '"';
		}

		echo wp_kses_post( '<div ' . $attributes . '>' );
	}

	/**
	 * Check if the class should load.
	 *
	 * @return bool
	 */
	private function should_load() {
		return class_exists( 'WooCommerce', false );
	}

	/**
	 * Render exclusive products section
	 */
	public function render_exclusive_products_section() {
		$products_category = get_theme_mod( 'neve_exclusive_products_category', '-' );
		if ( $products_category === '-' || neve_is_amp() ) {
			return;
		}

		$title = get_theme_mod( 'neve_exclusive_products_title' );

		$query_args = array(
			'post_type'           => 'product',
			'post_status'         => 'publish',
			'ignore_sticky_posts' => 1,
			'posts_per_page'      => 10,
		);

		if ( $products_category !== 'all' ) {
			$query_args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
				array(
					'taxonomy' => 'product_cat',
					'field'    => 'term_id', // This is optional, as it defaults to 'term_id'
					'terms'    => $products_category,
					'operator' => 'IN', // Possible values are 'IN', 'NOT IN', 'AND'.
				),
				array(
					'taxonomy' => 'product_visibility',
					'field'    => 'slug',
					'terms'    => 'exclude-from-catalog', // Possibly 'exclude-from-search' too
					'operator' => 'NOT IN',
				),
			);
		}

		$loop = new \WP_Query( $query_args );
		if ( ! $loop->have_posts() ) {
			return;
		}
		$dots = 0;
		echo '<section class="' . esc_attr( apply_filters( 'neve_exclusive_products_class', 'exclusive products' ) ) . '">';
		if ( ! empty( $title ) ) {
			echo '<h2>' . wp_kses_post( $title ) . '</h2>';
		}

		echo '<ul class="products exclusive-products">';
		add_filter( 'woocommerce_post_class', array( $this, 'prefix_post_class' ), 21 );
		while ( $loop->have_posts() ) { // @phpstan-ignore-line impure WP function
			$loop->the_post();
			wc_get_template_part( 'content', 'product' );
			$dots++;
		} // @phpstan-ignore-next-line code is reachable
		remove_filter( 'woocommerce_post_class', array( $this, 'prefix_post_class' ) );
		wp_reset_postdata();
		echo '</ul>';

		if ( $loop->post_count > 4 ) {
			echo '<div class="dots-nav">';
			for ( $i = 0; $i < $dots; $i++ ) {
				echo '<a class="dot"></a>';
			}
			echo '</div>';
		}

		echo '</section>';
	}

	/**
	 * Function that remove woocommerce first / last classes on products.
	 * This function is applied only on Exclusive products.
	 *
	 * @param array $classes WooCommerce classes on products.
	 *
	 * @return array|mixed
	 */
	public function prefix_post_class( $classes ) {
		if ( 'product' === get_post_type() ) {
			$classes = array_diff( $classes, array( 'first', 'last' ) );
		}
		return $classes;
	}

	/**
	 * Add body classes contextually.
	 *
	 * @param array $classes the body classes.
	 *
	 * @return array
	 */
	public function body_classes( $classes ) {
		$products_category = get_theme_mod( 'neve_exclusive_products_category', '-' );
		if ( $products_category === '-' || ! is_product() ) {
			return $classes;
		}
		$classes[] = 'nv-exclusive';

		return $classes;
	}
}
PK      \yY2B  2B  $  views/pluggable/metabox_settings.phpnu W+A        <?php
/**
 * Handles post meta for metabox.
 *
 * @package Neve\Views\Pluggable
 */

namespace Neve\Views\Pluggable;

use Neve\Core\Dynamic_Css;
use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Core\Styles\Dynamic_Selector;

/**
 * Class Metabox_Settings
 *
 * @package Neve\Views\Pluggable
 */
class Metabox_Settings {
	const CONTENT_WIDTH          = 'neve_meta_content_width';
	const ENABLE_CONTENT_WIDTH   = 'neve_meta_enable_content_width';
	const CONTAINER              = 'neve_meta_container';
	const SIDEBAR                = 'neve_meta_sidebar';
	const TITLE_ALIGNMENT        = 'neve_meta_title_alignment';
	const DISABLE_HEADER         = 'neve_meta_disable_header';
	const DISABLE_TITLE          = 'neve_meta_disable_title';
	const DISABLE_FEATURED_IMAGE = 'neve_meta_disable_featured_image';
	const DISABLE_FOOTER         = 'neve_meta_disable_footer';
	const ELEMENTS_ORDER         = 'neve_post_elements_order';
	const SHOW_AVATAR            = 'neve_meta_author_avatar';
	/**
	 * Context mapping for the post meta.
	 *
	 * @var array
	 */
	private $context_mapping = array(
		'header'         => self::DISABLE_HEADER,
		'title'          => self::DISABLE_TITLE,
		'featured-image' => self::DISABLE_FEATURED_IMAGE,
		'footer'         => self::DISABLE_FOOTER,
	);

	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_filter( 'neve_sidebar_position', array( $this, 'filter_sidebar_position' ) );
		add_filter( 'neve_container_class_filter', array( $this, 'filter_container_class' ), 100 );
		add_filter( 'body_class', array( $this, 'body_classes' ) );
		add_filter( 'neve_filter_toggle_content_parts', array( $this, 'filter_components_toggle' ), 100, 2 );
		add_action( 'enqueue_block_editor_assets', array( $this, 'editor_content_width' ), 100 );
		add_action( 'wp_enqueue_scripts', array( $this, 'content_width' ), 999 );

		add_filter(
			'ti_tpc_template_meta',
			function () {
				return [
					self::SHOW_AVATAR,
					self::ELEMENTS_ORDER,
					self::DISABLE_FOOTER,
					self::DISABLE_FEATURED_IMAGE,
					self::DISABLE_TITLE,
					self::DISABLE_HEADER,
					self::TITLE_ALIGNMENT,
					self::SIDEBAR,
					self::CONTAINER,
					self::ENABLE_CONTENT_WIDTH,
					self::CONTENT_WIDTH,
				];
			}
		);
		add_filter( 'neve_layout_single_post_elements_order', array( $this, 'filter_post_elements' ) );
		add_filter( 'neve_title_alignment_style', array( $this, 'filter_title_alignment_style' ), 10, 2 );
		add_filter( 'neve_display_author_avatar', array( $this, 'filter_author_avatar_display' ), 15 );
		add_filter( 'neve_meta_content_width', array( $this, 'get_content_width' ) );
	}

	/**
	 * Add body classes contextually.
	 *
	 * @param array $classes the body classes.
	 *
	 * @return array
	 */
	public function body_classes( $classes ) {

		if ( ! $this->has_settings() ) {
			return $classes;
		}

		$post_id = $this->get_post_id();

		foreach ( $this->context_mapping as $context => $meta_key ) {
			$meta_value = get_post_meta( $post_id, $meta_key, true );

			if ( empty( $meta_value ) ) {
				continue;
			}

			if ( $meta_value === 'on' ) {
				$classes[] = 'nv-without-' . $context;
			}
		}

		return $classes;
	}

	/**
	 * Check if we should account for the meta settings.
	 *
	 * @return bool
	 */
	private function has_settings() {
		if (
			! is_single() &&
			! is_page() &&
			! $this->is_blog_static() &&
			$this->is_not_woo_shop()
		) {
			return false;
		}

		$post_id = $this->get_post_id();

		if ( $post_id === false ) {
			return false;
		}

		return true;
	}

	/**
	 * Check if the blog is set to a static page.
	 *
	 * @return bool
	 */
	private function is_blog_static() {
		return ( get_option( 'show_on_front' ) === 'page' && is_home() );
	}

	/**
	 * Get the post id.
	 *
	 * @return int|false
	 */
	private function get_post_id() {
		if ( $this->is_blog_static() ) {
			return (int) get_option( 'page_for_posts' );
		}

		// On shop page the returning id is the id of the first product. We need the id of the page.
		// is_archive is true for shop page, so we need to check shop before is_archive
		if ( class_exists( 'WooCommerce' ) && is_shop() ) {
			return wc_get_page_id( 'shop' );
		}

		if ( is_archive() ) {
			return false;
		}

		if ( is_search() ) {
			return false;
		}

		if ( is_home() ) {
			return false;
		}


		global $post;
		if ( empty( $post ) ) {
			return false;
		}

		$post_id = apply_filters( 'neve_post_meta_filters_post_id', $post->ID );

		if ( ! isset( $post_id ) ) {
			return false;
		}

		return $post_id;
	}

	/**
	 * Set editor width.
	 */
	public function editor_content_width() {
		global $post_type;
		$meta_value = $this->get_content_width();
		$container  = $this->get_current_layout();


		// If contained, we set the block max-width based on the desktop container width.
		if ( $container === 'contained' ) {
			$editor_width = Mods::get( Config::MODS_CONTAINER_WIDTH );
			$editor_width = isset( $editor_width['desktop'] ) ? (int) $editor_width['desktop'] : 1170;
			if ( empty( $meta_value ) ) {
				$meta_key   = $post_type === 'post' ? Config::MODS_SINGLE_CONTENT_WIDTH : Config::MODS_OTHERS_CONTENT_WIDTH;
				$meta_value = Mods::get( $meta_key, $this->get_content_width_default() );
			}
			$editor_width_normal = round( ( $meta_value / 100 ) * $editor_width ) . 'px';
		} else {
			// For full-width container, we use the content percent value.
			$editor_width_normal = ( empty( $meta_value ) ? 100 : $meta_value ) . '%';
		}

		$editor_title_opacity = 1;
		$tile_disabled        = $this->get_title_disabled();
		if ( $tile_disabled === 'on' ) {
			$editor_title_opacity = 0.5;
		}

		$style = sprintf(
			'
			/* Main column width */
			.wp-block,
			.block-editor-block-list__layout > .wp-block-separator,
			.block-editor-block-list__layout > .wp-block-separator:not(.is-style-wide):not(.is-style-dots) {
			    max-width: %s;
			}

			.wp-block[data-align="wide"] {
			    max-width: 70vw;
			}

			.wp-block[data-align="full"] {
			    max-width: none;
			}
			h1.editor-post-title { opacity: %s; }
			',
			$editor_width_normal,
			$editor_title_opacity
		);

		$style = $this->add_button_shadow_styles( $style );

		wp_add_inline_style( 'neve-gutenberg-style', $style );


	}

	/**
	 * Get the disabled title status.
	 *
	 * @return false|mixed Status of the title.
	 */
	public function get_title_disabled() {
		$post_id = $this->get_post_id();
		if ( $post_id === false ) {
			return false;
		}
		return get_post_meta( $post_id, self::DISABLE_TITLE, true );
	}

	/**
	 * Get content width, if any.
	 *
	 * @return int|bool Content width.
	 */
	public function get_content_width() {
		$post_id = $this->get_post_id();

		if ( $post_id === false ) {
			return false;
		}

		$content_width_status = get_post_meta( $post_id, self::ENABLE_CONTENT_WIDTH, true );
		$content_width_status = empty( $content_width_status ) ? $this->get_content_width_status_default() : $content_width_status;
		if ( $content_width_status !== 'on' ) {
			return false;
		}

		return get_post_meta( $post_id, self::CONTENT_WIDTH, true );

	}

	/**
	 * Get content width status default.
	 *
	 * @return string
	 */
	private function get_content_width_status_default() {
		if ( (int) $this->get_post_id() === (int) get_option( 'woocommerce_checkout_page_id' ) ) {
			return 'on';
		}

		return '';
	}

	/**
	 * Return container type for the selected post.
	 *
	 * @return string
	 */
	public function get_current_layout() {
		$container = $this->get_container_type();

		// Check customizer container type based on the context.
		if ( empty( $container ) ) {
			global $post_type;
			$container = $post_type === 'post' ? Mods::get( Config::MODS_SINGLE_POST_CONTAINER_STYLE, 'contained' ) : Mods::get( Config::MODS_DEFAULT_CONTAINER_STYLE, 'contained' );
		}

		return $container;
	}

	/**
	 * Get continer type for current post.
	 *
	 * @return mixed|string
	 */
	public function get_container_type() {

		$post_id = $this->get_post_id();

		$meta_value = get_post_meta( $post_id, self::CONTAINER, true );

		if ( empty( $meta_value ) || $meta_value === 'default' ) {
			return '';
		}
		if ( $post_id === false ) {
			return '';
		}

		return $meta_value;
	}

	/**
	 * Get content width status.
	 *
	 * @return int
	 */
	private function get_content_width_default() {
		if ( (int) $this->get_post_id() === (int) get_option( 'woocommerce_checkout_page_id' ) ) {
			return 100;
		}

		return 70;
	}

	/**
	 * Add content width.
	 */
	public function content_width() {
		$meta_value = (int) $this->get_content_width();

		if ( empty( $meta_value ) ) {
			return;
		}

		$sidebar_width   = 100 - absint( $meta_value );
		$container       = $this->get_current_layout();
		$container_class = $container === 'contained' ? ' .container ' : ' .container-fluid ';
		// Add the `!important` if in customizer, so that the live refresh doesn't affect this.
		$important    = '';
		$hide_sidebar = '';
		if ( is_customize_preview() ) {
			$important = '!important';

			if ( $sidebar_width === 0 ) {
				$hide_sidebar = 'display: none;';
			}
		}
		$max_width = Mods::to_json( Config::MODS_CONTAINER_WIDTH );
		$extra_css = '';
		if ( $container === 'contained' ) {
			$extra_css = sprintf(
				'
			#content.neve-main .container .alignfull > [class*="__inner-container"],#content.neve-main .alignwide > [class*="__inner-container"]{
				max-width: %s;
			}
			@media(min-width: 576px){
				#content.neve-main .container .alignfull > [class*="__inner-container"],#content.neve-main .alignwide > [class*="__inner-container"]{
					max-width: %s;
				}
			}
			',
				( $max_width[ Dynamic_Selector::MOBILE ] - Config::CONTENT_DEFAULT_PADDING ) . 'px',
				( $max_width[ Dynamic_Selector::TABLET ] - Config::CONTENT_DEFAULT_PADDING ) . 'px'
			);
		}

		$desktop_value = $container === 'contained'
			? round( ( $meta_value / 100 ) * $max_width[ Dynamic_Selector::DESKTOP ] - Config::CONTENT_DEFAULT_PADDING ) . 'px'
			: 'calc(' . $meta_value . '% + ' . ( Config::CONTENT_DEFAULT_PADDING / 2 ) . 'px)';


		$style = $extra_css . '
		@media(min-width: 960px) {
			#content.neve-main ' . esc_attr( $container_class ) . '.alignfull > [class*="__inner-container"],#content.neve-main ' . esc_attr( $container_class ) . ' .alignwide > [class*="__inner-container"]{
				max-width: ' . $desktop_value . ';
			}
			#content.neve-main > ' . esc_attr( $container_class ) . ' > .row > .col{ max-width: ' . absint( $meta_value ) . '%' . esc_attr( $important ) . '; }
			body:not(.neve-off-canvas) #content.neve-main > ' . esc_attr( $container_class ) . ' > .row > .nv-sidebar-wrap,
			body:not(.neve-off-canvas) #content.neve-main > ' . esc_attr( $container_class ) . ' > .row > .nv-sidebar-wrap.shop-sidebar { max-width: ' . absint( $sidebar_width ) . '%' . esc_attr( $important ) . '; ' . esc_attr( $hide_sidebar ) . ' }
		}
		';

		wp_add_inline_style( 'neve-style', Dynamic_Css::minify_css( $style ) );
	}

	/**
	 * Add button shadow styles if used.
	 *
	 * @param string $style Inline styles for the Gutenberg editor.
	 */
	private function add_button_shadow_styles( $style ) {
		$primary_values   = Mods::get( Config::MODS_BUTTON_PRIMARY_STYLE, neve_get_button_appearance_default() );
		$secondary_values = Mods::get( Config::MODS_BUTTON_SECONDARY_STYLE, neve_get_button_appearance_default( 'secondary' ) );
		if (
			( isset( $primary_values['useShadow'] ) && ! empty( $primary_values['useShadow'] ) ) ||
			( isset( $primary_values['useShadowHover'] ) && ! empty( $primary_values['useShadowHover'] ) ) ||
			( isset( $secondary_values['useShadow'] ) && ! empty( $secondary_values['useShadow'] ) ) ||
			( isset( $secondary_values['useShadowHover'] ) && ! empty( $secondary_values['useShadowHover'] ) )
		) {
			$style = '.editor-styles-wrapper .wp-block-button.is-style-primary .wp-block-button__link {box-shadow: var(--primarybtnshadow, none);} .editor-styles-wrapper .wp-block-button.is-style-primary .wp-block-button__link:hover {box-shadow: var(--primarybtnhovershadow, none);} .editor-styles-wrapper .wp-block-button.is-style-secondary .wp-block-button__link {box-shadow: var(--secondarybtnshadow, none);} .editor-styles-wrapper .wp-block-button.is-style-secondary .wp-block-button__link:hover {box-shadow: var(--secondarybtnhovershadow, none);}';
		}

		return $style;
	}

	/**
	 * Filter components that will be shown.
	 *
	 * @param bool   $status the status of the component.
	 * @param string $context context of the filter.
	 *
	 * @return bool
	 */
	public function filter_components_toggle( $status, $context ) {

		if ( ! $this->has_settings() ) {
			return $status;
		}

		$post_id = $this->get_post_id();

		/* If context isn't valid, bail. */
		if ( ! array_key_exists( $context, $this->context_mapping ) ) {
			return $status;
		}

		$meta_value = get_post_meta( $post_id, $this->context_mapping[ $context ], true );

		if ( empty( $meta_value ) ) {
			return $status;
		}

		if ( $meta_value === 'on' ) {
			return false;
		}

		return true;
	}

	/**
	 * If WooCommerce does not exist or if ir exists and page is not shop
	 * This also touches the following issues:
	 * Codeinwp/neve-pro-addon/issues/999
	 * Codeinwp/neve/issues/2790
	 *
	 * @return bool
	 */
	private function is_not_woo_shop() {
		return ( ! class_exists( 'WooCommerce', false ) || ( class_exists( 'WooCommerce', false ) && ! is_shop() ) );
	}

	/**
	 * Change sidebar position based on meta.
	 *
	 * @param string $position sidebar position coming from filter.
	 *
	 * @return mixed
	 */
	public function filter_sidebar_position( $position ) {
		if (
			! is_single() &&
			! is_page() &&
			! $this->is_blog_static() &&
			$this->is_not_woo_shop()
		) {
			return $position;
		}

		$post_id = $this->get_post_id();

		if ( $post_id === false ) {
			return $position;
		}

		$meta_value       = get_post_meta( $post_id, self::SIDEBAR, true );
		$sidebar_position = empty( $meta_value ) || $meta_value === 'default' ? $position : $meta_value;

		return $sidebar_position;
	}

	/**
	 * Filter the container class based on meta.
	 *
	 * @param string $class container class.
	 *
	 * @return string
	 */
	public function filter_container_class( $class ) {

		// Don't filter on blog.
		if (
			! is_single() &&
			! is_page() &&
			! $this->is_blog_static() &&
			$this->is_not_woo_shop()
		) {
			return $class;
		}

		$meta_value = $this->get_container_type();

		if ( empty( $meta_value ) ) {
			return $class;
		}

		// Add `set-in-metabox` so that we don't affect this in customizer with live refresh.
		$customizer_context = '';
		if ( is_customize_preview() ) {
			$customizer_context = ' set-in-metabox ';
		}

		if ( $meta_value === 'contained' ) {
			return $customizer_context . ' container';
		} elseif ( $meta_value === 'full-width' ) {
			return $customizer_context . ' container-fluid';
		}

		return $class;
	}

	/**
	 * Post elements order for title components.
	 *
	 * @param array $elements_order Elements order before this filter.
	 *
	 * @return array
	 */
	public function filter_post_elements( $elements_order ) {
		$post_id = $this->get_post_id();

		if ( $post_id === false ) {
			return $elements_order;
		}

		$meta_elements_order = get_post_meta( $post_id, self::ELEMENTS_ORDER, true );
		if ( empty( $meta_elements_order ) ) {
			return $elements_order;
		}

		return json_decode( $meta_elements_order, true );
	}

	/**
	 * Filters the styles provided and adds specific vars for post title if meta exists.
	 *
	 * @since 3.1.0
	 *
	 * @param string $style The inline title styles.
	 * @param string $context The context. ('cover', 'normal'). Defaults: 'normal'.
	 *
	 * @return string
	 */
	public function filter_title_alignment_style( $style, $context = 'normal' ) {
		// Don't override with specific post title styles in Customizer context.
		if ( is_customize_preview() ) {
			return $style;
		}

		$post_id = $this->get_post_id();

		if ( $post_id === false ) {
			return $style;
		}

		$title_meta_alignment = get_post_meta( $post_id, self::TITLE_ALIGNMENT, true );
		if ( empty( $title_meta_alignment ) ) {
			return $style;
		}

		$style .= '--textalign:' . esc_attr( $title_meta_alignment ) . ';';
		if ( $context === 'cover' ) {
			$justify_map = [
				'left'   => 'flex-start',
				'center' => 'center',
				'right'  => 'flex-end',
			];
			if ( isset( $justify_map[ $title_meta_alignment ] ) ) {
				$style .= '--justify:' . esc_attr( $justify_map[ $title_meta_alignment ] ) . ';';
			}
		}
		return $style;
	}

	/**
	 * Filter the display of author avatar
	 *
	 * @param bool $show_avatar Display avatar flag.
	 *
	 * @return bool
	 */
	public function filter_author_avatar_display( $show_avatar ) {

		$post_id = $this->get_post_id();

		if ( $post_id === false ) {
			return $show_avatar;
		}
		$show_author_avatar = get_post_meta( $post_id, self::SHOW_AVATAR, true );

		if ( ! empty( $show_author_avatar ) ) {
			return $show_author_avatar === 'on';
		}

		return $show_avatar;
	}
}
PK      \D=  =    views/pluggable/masonry.phpnu W+A        <?php
/**
 * Handles blog masonry.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      31/08/2018
 *
 * @package Neve\Views\Pluggable
 */

namespace Neve\Views\Pluggable;

use Neve\Customizer\Defaults\Layout;
use Neve\Views\Base_View;
use Neve\Views\Template_Parts;

/**
 * Class Masonry
 *
 * @package Neve\Views\Pluggable
 */
class Masonry extends Template_Parts {
	use Layout;
	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_filter( 'neve_filter_main_script_localization', array( $this, 'filter_localization' ) );
		add_filter( 'neve_filter_main_script_dependencies', array( $this, 'filter_dependencies' ) );
		add_filter( 'neve_custom_layout_magic_tags', array( $this, 'maybe_wrap_custom_layout' ), PHP_INT_MAX, 2 );
	}

	/**
	 * Filter to maybe wrap custom layout in loop when using masonry.
	 *
	 * @param string  $content The custom layout content.
	 * @param integer $post_id The custom post ID.
	 *
	 * @return string
	 */
	public function maybe_wrap_custom_layout( $content, $post_id ) {
		if ( ! $this->is_masonry_enabled() ) {
			return $content;
		}

		if ( ! $this->is_blog() ) {
			return $content;
		}

		return '<article class="' . $this->post_class( $post_id ) . '">' . $content . '</article>';
	}

	/**
	 * Check that the page is in blog context.
	 *
	 * @return bool
	 */
	private function is_blog() {
		return ( is_archive() || is_author() || is_category() || is_home() || is_single() || is_tag() );
	}

	/**
	 * Filter localization to add masonry to the main script.
	 *
	 * @param array $data localization array.
	 *
	 * @return array
	 */
	public function filter_localization( $data ) {
		if ( ! $this->is_masonry_enabled() ) {
			return $data;
		}
		$layout  = get_theme_mod( 'neve_blog_archive_layout', 'grid' );
		$columns = $this->get_max_columns();

		$data['masonryStatus']  = 'enabled';
		$data['masonryColumns'] = absint( $columns );
		$data['blogLayout']     = esc_html( $layout );
		return $data;
	}

	/**
	 * Filter dependencies to add masonry to the main script.
	 *
	 * @param array $data dependencies array.
	 *
	 * @return array
	 */
	public function filter_dependencies( $data ) {
		if ( ! $this->is_masonry_enabled() ) {
			return $data;
		}
		array_push( $data, 'masonry' );

		return $data;
	}

	/**
	 * Check if masonry is enabled.
	 *
	 * @return bool
	 */
	public function is_masonry_enabled() {
		$blog_layout = get_theme_mod( 'neve_blog_archive_layout', 'grid' );
		$columns     = $this->get_max_columns();

		if ( ! in_array( $blog_layout, [ 'grid', 'covers' ], true ) || $columns === 1 ) {
			return false;
		}

		return (bool) get_theme_mod( 'neve_enable_masonry', false );
	}

	/**
	 * Get the maximum number of columns.
	 *
	 * @return int
	 */
	private function get_max_columns() {
		$columns = get_theme_mod( 'neve_grid_layout', $this->grid_columns_default() );
		if ( is_int( $columns ) ) {
			return $columns;
		}
		if ( empty( $columns ) ) {
			return 1;
		}

		$columns = json_decode( $columns, true );
		if ( ! is_array( $columns ) ) {
			return 1;
		}
		return max( $columns );
	}
}
PK      \z!  !    views/pluggable/pagination.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      31/08/2018
 *
 * @package Neve\Views\Pluggable
 */

namespace Neve\Views\Pluggable;

use Neve\Views\Base_View;

/**
 * Class Pagination
 *
 * @package Neve\Views\Pluggable
 */
class Pagination extends Base_View {
	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'rest_api_init', array( $this, 'register_endpoints' ) );
		add_filter( 'neve_filter_main_script_localization', array( $this, 'filter_localization' ) );
		add_action( 'neve_do_pagination', array( $this, 'render_pagination' ) );
		add_action( 'neve_post_navigation', array( $this, 'render_post_navigation' ) );
	}

	/**
	 * Register Rest API endpoint for posts retrieval.
	 */
	public function register_endpoints() {
		register_rest_route(
			'nv/v1/posts',
			'/page/(?P<page_number>\d+)(?:/(?P<lang>[a-zA-Z0-9-_]+))?',
			array(
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'get_posts' ),
				'permission_callback' => '__return_true',
			)
		);
	}

	/**
	 * Get paginated posts.
	 *
	 * @param \WP_REST_Request $request the rest request.
	 *
	 * @return \WP_REST_Response
	 */
	public function get_posts( \WP_REST_Request $request ) {
		if ( empty( $request['page_number'] ) ) {
			return new \WP_REST_Response( '' );
		}

		$query_args = $request->get_body();
		$args       = json_decode( $query_args, true );

		$per_page = get_option( 'posts_per_page' );
		if ( $per_page > 100 ) {
			$per_page = 100;
		}

		/**
		 * If homepage is set to 'A static page', there will be a parameter inside the query named 'pagename'.
		 * That parameter is the title of the blog page and because of it the WP_Query will return 0 posts.
		 * Here, we unset it. We can't reset the query because we can lose search parameters for example.
		 */
		if ( array_key_exists( 'pagename', $args ) ) {
			unset( $args['pagename'] );
		}

		$args['posts_per_page'] = $per_page;

		if ( empty( $args['post_type'] ) ) {
			$args['post_type'] = 'post';
		}

		$args['paged']               = $request['page_number'];
		$args['ignore_sticky_posts'] = 1;
		$args['post_status']         = 'publish';

		if ( ! empty( $request['lang'] ) ) {
			if ( defined( 'POLYLANG_VERSION' ) ) {
				$args['lang'] = $request['lang'];
			}

			if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
				global $sitepress;
				if ( gettype( $sitepress ) === 'object' && method_exists( $sitepress, 'switch_lang' ) ) {
					$sitepress->switch_lang( $request['lang'] );
				}
			}
		}

		$output = '';

		$query = new \WP_Query( $args );
		if ( $query->have_posts() ) {
			ob_start();
			while ( $query->have_posts() ) { // @phpstan-ignore-line impure WP function
				$query->the_post();

				/**
				 * Executes actions before rendering the post content.
				 *
				 * @since 2.11 in the /index.php
				 */
				do_action( 'neve_loop_entry_before' );

				get_template_part( 'template-parts/content' );

				/**
				 * Executes actions after rendering the post content.
				 *
				 * @since 2.11 in the /index.php
				 */
				do_action( 'neve_loop_entry_after' );
			} // @phpstan-ignore-next-line code is reachable
			wp_reset_postdata();
			$output = ob_get_contents();
			ob_end_clean();
		}

		return new \WP_REST_Response( $output );
	}

	/**
	 * Filter localization to add infinite scroll to the main script.
	 *
	 * @param array $data localization array.
	 *
	 * @return array
	 */
	public function filter_localization( $data ) {
		if ( ! $this->has_infinite_scroll() ) {
			return $data;
		}
		global $wp_query;
		$max_pages = $wp_query->max_num_pages;

		$data['infScroll'] = 'enabled';
		$data['maxPages']  = $max_pages;
		$data['endpoint']  = rest_url( 'nv/v1/posts/page/' );
		$data['query']     = wp_json_encode( $wp_query->query );
		$data['lang']      = get_locale();

		// WPML language parameter
		$current_lang = apply_filters( 'wpml_current_language', null );
		if ( ! empty( $current_lang ) ) {
			$data['lang'] = $current_lang;
		}

		return $data;
	}

	/**
	 * Render the pagination.
	 *
	 * @param string $context Pagination location context.
	 */
	public function render_pagination( $context ) {
		if ( $context === 'single' ) {
			$this->render_single_pagination();

			return;
		}

		$paginate_args = array( 'type' => 'list' );
		if ( $this->has_jump_to() ) {
			$paginate_args['format'] = '?paged=%#%';
		}
		$links = paginate_links( $paginate_args );

		if ( empty( $links ) ) {
			return;
		}

		if ( ! $this->has_infinite_scroll() ) {
			/**
			 * Executes actions before pagination.
			 *
			 * @since 2.3.8
			 */
			do_action( 'neve_before_pagination' );
		}

		$links = str_replace(
			array( '<a class="prev', '<a class="next' ),
			array(
				'<a rel="prev" class="prev',
				'<a rel="next" class="next',
			),
			$links
		);

		$allowed_tags = 'post';
		if ( $this->has_jump_to() ) {

			parse_str( wp_parse_url( get_pagenum_link(), PHP_URL_QUERY ), $url_query_args );

			$current_page      = ( ! empty( get_query_var( 'paged' ) ) ) ? get_query_var( 'paged' ) : 1;
			$additional_fields = '';
			foreach ( $url_query_args as $key => $value ) {
				$additional_fields .= '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $value ) . '" />';
			}

			$allowed_tags = array(
				'ul'    => array(
					'class' => array(),
				),
				'li'    => array(
					'class' => array(),
				),
				'a'     => array(
					'class' => array(),
					'href'  => array(),
				),
				'span'  => array(
					'class' => array(),
				),
				'form'  => array(
					'class'        => array(),
					'action'       => array( esc_url( get_pagenum_link() ) ),
					'method'       => array( 'get' ),
					'autocomplete' => array( 'off' ),
				),
				'input' => array(
					'class'       => array(),
					'type'        => array( 'number', 'hidden' ),
					'name'        => array( 'paged', 'page_id', 's' ),
					'value'       => array(),
					'min'         => array(),
					'step'        => array(),
					'placeholder' => array(),
					'size'        => array(),
				),
			);

			$jump_to_form = '<form class="nv-page-nav-form" action="' . esc_url( get_pagenum_link() ) . '" method="get" autocomplete="off">
				<input class="page-input" type="number" min="1" step="1" value="' . absint( $current_page ) . '" placeholder="1" size="3" name="paged" />
				' . wp_kses(
					$additional_fields,
					$allowed_tags
				) . '
				<input value="»" type="submit" >
			</form>';

			$links = str_replace(
				'</ul>',
				'<li>' . $jump_to_form . '</li></ul>',
				$links
			);
		}

		echo $this->has_infinite_scroll() ? '<div style="display: none;">' : '';
		echo wp_kses( $links, $allowed_tags );
		echo $this->has_infinite_scroll() ? '</div>' : '';

		if ( $this->has_infinite_scroll() ) {
			echo wp_kses_post( '<div class="load-more-posts"><span class="nv-loader" style="display: none;"></span><span class="infinite-scroll-trigger"></span></div>' );
		}
	}

	/**
	 * Render single post / page pagination.
	 */
	private function render_single_pagination() {
		wp_link_pages(
			array(
				'before'      => '<div class="post-pages-links"><span>' . apply_filters( 'neve_page_link_before', esc_html__( 'Pages:', 'neve' ) ) . '</span>',
				'after'       => '</div>',
				'link_before' => '<span>',
				'link_after'  => '</span>',
			)
		);
	}

	/**
	 * Render single post navigation links
	 */
	public function render_post_navigation() {
		$prev_format = '<div class="previous">%link</div>';
		$next_format = '<div class="next">%link</div>';

		$prev_link = sprintf(
			'<span class="nav-direction">%1$s</span><span>%2$s</span>',
			esc_html__( 'previous', 'neve' ),
			'%title'
		);
		$next_link = sprintf(
			'<span class="nav-direction">%1$s</span><span>%2$s</span>',
			esc_html__( 'next', 'neve' ),
			'%title'
		);

		echo '<div class="' . esc_attr( apply_filters( 'neve_post_navigation_class', 'nv-post-navigation' ) ) . '">';
		previous_post_link( $prev_format, $prev_link );
		next_post_link( $next_format, $next_link );
		echo '</div>';
	}

	/**
	 * Go to page option is enabled
	 *
	 * @return bool
	 */
	private function has_jump_to() {
		return get_theme_mod( 'neve_pagination_type', 'number' ) === 'jump-to';
	}

	/**
	 * Has infinite scroll.
	 *
	 * @return bool
	 */
	private function has_infinite_scroll() {
		if ( neve_is_amp() ) {
			return false;
		}
		if ( is_search() ) {
			return false;
		}

		$pagination_type = get_theme_mod( 'neve_pagination_type', 'number' );
		if ( $pagination_type === 'infinite' ) {
			return true;
		}

		return false;
	}
}
PK      \$B    7  views/pluggable/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \WKl  l    views/content_404.phpnu W+A        <?php
/**
 * Content 404 class.
 *
 * @package Neve\Views
 */

namespace Neve\Views;

/**
 * Class Content_404
 *
 * @package Neve\Views
 */
class Content_404 extends Base_View {

	/**
	 * Init function.
	 */
	public function init() {
		add_action( 'neve_do_404', array( $this, 'render_404_page' ) );
	}

	/**
	 * Render 404 page.
	 */
	public function render_404_page() {
		$container_class = apply_filters( 'neve_container_class_filter', 'container', 'blog-archive' );

		echo '<div class="' . esc_attr( $container_class ) . ' archive-container">';
		echo '<div class="row">';
		do_action( 'neve_do_sidebar', 'blog-archive', 'left' );
		echo '<div class="nv-index-posts blog col">';

		echo '<div class="col-12 nv-content-none-wrap">';
		echo '<p>';
		esc_html_e( 'It seems we can&rsquo;t find what you&rsquo;re looking for. Perhaps searching can help.', 'neve' );
		echo '</p>';
		echo '<div class="nv-seach-form-wrap">';
		get_search_form();
		echo '</div>';
		echo '</div>';

		echo '<div class="w-100"></div>';
		echo '</div>';
		do_action( 'neve_do_sidebar', 'blog-archive', 'right' );
		echo '</div>';
		echo '</div>';
	}
}
PK      \~'uD  D    views/breadcrumbs.phpnu W+A        <?php
/**
 * Breadcrumbs integration file.
 *
 * @package Neve\Views
 */

namespace Neve\Views;

use RankMath\Helpers\Api;
use WPSEO_Options;


/**
 * Class Yoast
 *
 * @package Neve\Compatibility
 */
class Breadcrumbs extends Base_View {

	/**
	 * Module init.
	 */
	public function init() {
		add_action( 'neve_pro_hfg_breadcrumb', array( $this, 'render_breadcrumbs' ), 10, 2 );
		$this->load_theme_breadcrumbs();
	}

	/**
	 * Load hooks and filters.
	 */
	private function load_theme_breadcrumbs() {

		$breadcrumbs_hooks = apply_filters(
			'neve_breadcrumbs_locations',
			array(
				'neve_before_page_title',
				'neve_before_post_title',
			)
		);

		foreach ( $breadcrumbs_hooks as $hook ) {
			add_action( $hook, array( $this, 'render_theme_breadcrumbs' ) );
		}
	}

	/**
	 * Render breadcrumbs in Neve theme.
	 *
	 * @return bool | void
	 */
	public function render_theme_breadcrumbs() {
		if ( ! $this->is_breadcrumb_enabled() ) {
			return false;
		}
		$this->render_breadcrumbs( 'small' );
	}

	/**
	 * Check if Yoast breadcrumbs are enabled.
	 *
	 * @return bool
	 */
	public function is_breadcrumb_enabled() {

		if ( ! apply_filters( 'neve_show_breadcrumbs', true ) ) {
			return false;
		}

		// Yoast breadcrumbs
		if ( function_exists( 'yoast_breadcrumb' ) ) {
			return WPSEO_Options::get( 'breadcrumbs-enable', false ) === true;
		}

		// SEOPress breadcrumbs
		if ( function_exists( 'seopress_display_breadcrumbs' ) ) {
			return true;
		}

		// Rank Math breadcrumbs
		if ( function_exists( 'rank_math_the_breadcrumbs' ) ) {
			return true;
		}

		if ( function_exists( 'bcn_display' ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Render Breadcrumbs.
	 *
	 * @param string $html_tag Wrapper HTML tag.
	 */
	public function render_breadcrumbs( $html_tag, $context = '' ) {
		if ( $context !== 'hfg' && is_front_page() ) {
			return;
		}
		if ( empty( $html_tag ) ) {
			$html_tag = 'small';
		}

		self::maybe_render_seo_breadcrumbs( $html_tag );
	}


	/**
	 * Render 3rd parties breadcrumbs
	 *
	 * @param string $html_tag Wrapper HTML tag.
	 * @param bool   $check Flag that controls if is needed to check if breadcrumbs are enabled in 3rd parties.
	 *
	 * @return bool
	 */
	public static function maybe_render_seo_breadcrumbs( $html_tag, $check = false ) {

		// Yoast breadcrumbs
		$yoast_breadcrumbs_enabled = true;
		if ( function_exists( 'yoast_breadcrumb' ) ) {
			if ( $check && class_exists( 'WPSEO_Options' ) && method_exists( 'WPSEO_Options', 'get' ) ) {
				$yoast_breadcrumbs_enabled = WPSEO_Options::get( 'breadcrumbs-enable', false );
			}
			if ( $yoast_breadcrumbs_enabled ) {
				yoast_breadcrumb( '<' . esc_html( $html_tag ) . ' class="nv--yoast-breadcrumb neve-breadcrumbs-wrapper">', '</' . esc_html( $html_tag ) . '>' );
				return true;
			}
		}

		// SEOPress breadcrumbs
		$seopress_breadcrumbs_enabled = true;
		if ( function_exists( 'seopress_display_breadcrumbs' ) ) {
			if ( $check ) {
				$seopress_breadcrumbs_enabled = get_option( 'seopress_toggle' );
			}
			if ( $seopress_breadcrumbs_enabled ) {
				echo '<' . esc_html( $html_tag ) . ' class="neve-breadcrumbs-wrapper">';
				seopress_display_breadcrumbs();
				echo '</' . esc_html( $html_tag ) . '>';
				return true;
			}
		}

		// Rank Math breadcrumbs
		$rankmath_breadcrumbs_enabled = true;
		if ( function_exists( 'rank_math_the_breadcrumbs' ) ) {
			if ( $check && class_exists( '\RankMath\Helpers\Api' ) ) {
				$rankmath_breadcrumbs_enabled = Api::get_settings( 'general.breadcrumbs' );
			}

			if ( $rankmath_breadcrumbs_enabled ) {
				echo '<' . esc_html( $html_tag ) . ' class="neve-breadcrumbs-wrapper">';
				rank_math_the_breadcrumbs(
					[
						'wrap_before' => '<nav aria-label="breadcrumbs" class="rank-math-breadcrumb">',
						'wrap_after'  => '</nav>',
					]
				);
				echo '</' . esc_html( $html_tag ) . '>';
				return true;
			}
		}

		return false;
	}


}
PK      \H",'z  z    views/page_header.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      28/08/2018
 *
 * @package Neve\Views
 */

namespace Neve\Views;

/**
 * Class Page_Header
 *
 * @package Neve\Views
 */
class Page_Header extends Base_View {
	/**
	 * Title arguments.
	 *
	 * @var array
	 */
	private $title_args = array(
		'string'     => '',
		'class'      => '',
		'wrap-class' => '',
	);

	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp', [ $this, 'run' ] );
	}

	/**
	 * Run the actions for the page header
	 *
	 * @return void
	 */
	public function run() {
		$header_hook = get_theme_mod( 'neve_enable_featured_post', false ) && is_home() ? 'neve_do_featured_post' : 'neve_page_header';
		add_action( $header_hook, array( $this, 'render_page_header' ), 9 );
		add_filter( 'get_the_archive_title', array( $this, 'filter_archive_title' ) );
	}

	/**
	 * Render the page header.
	 *
	 * @param string $context the context provided in do_action.
	 */
	public function render_page_header( $context ) {
		$title_args = $this->get_the_page_title( $context );

		if ( empty( $title_args['string'] ) ) {
			return;
		}
		$header_layout = get_theme_mod( 'neve_page_header_layout', 'normal' );
		if ( $header_layout !== 'normal' && in_array( $context, [ 'single-page', 'woo-page' ], true ) ) {
			return;
		}

		$title_args['category_description'] = $this->get_archive_description();

		$this->get_view( 'page-header', $title_args );
	}

	/**
	 * The page title
	 *
	 * @param string $context context of current page passed in do_action.
	 *
	 * @return array
	 */
	private function get_the_page_title( $context ) {
		$title_args = array();
		if ( $context === 'index' || $context === 'single-page' ) {
			$title_args               = $this->get_blog_archive_title();
			$title_args['wrap-class'] = 'nv-big-title';
		}

		if ( $context === 'search' ) {
			/* translators: search result */
			$title_args['string']     = sprintf( esc_html__( 'Search Results for: %s', 'neve' ), get_search_query() );
			$title_args['wrap-class'] = 'nv-big-title';
		}

		// If no title is set until now simply get the title.
		if ( empty( $title_args['string'] ) && ! is_home() ) {
			$title_args['string'] = get_the_title();
		}

		$title_args = wp_parse_args( $title_args, $this->title_args );

		return $title_args;
	}

	/**
	 * Get the blog/archive title.
	 *
	 * @return array
	 */
	private function get_blog_archive_title() {
		if ( is_home() && get_option( 'show_on_front' ) === 'posts' ) {
			return array(
				'string' => '',
				'class'  => 'nv-blog-description',
			);
		}

		if ( get_option( 'show_on_front' ) === 'page' && is_home() ) {
			$blog_page_id = get_option( 'page_for_posts' );

			return array(
				'string' => get_the_title( $blog_page_id ),
				'class'  => '',
			);
		}

		if ( is_archive() ) {
			return array(
				'string' => get_the_archive_title(),
			);
		}

		if ( class_exists( 'WooCommerce' ) && function_exists( 'is_wc_endpoint_url' ) && is_wc_endpoint_url() ) {
			$endpoint       = WC()->query->get_current_endpoint();
			$endpoint_title = WC()->query->get_endpoint_title( $endpoint );
			return array(
				'string' => $endpoint_title,
			);
		}

		return array();
	}

	/**
	 * Remove "Category:", "Tag:", "Author:" from the archive title.
	 *
	 * @param string $title Archive title.
	 *
	 * @return string
	 */
	public function filter_archive_title( $title ) {
		if ( is_category() ) {
			$title = single_cat_title( '', false );
		} elseif ( is_tag() ) {
			$title = single_tag_title( '', false );
		} elseif ( is_author() ) {
			$title = '<span class="vcard">' . get_the_author() . '</span>';
		} elseif ( is_year() ) {
			$title = get_the_date( 'Y' );
		} elseif ( is_month() ) {
			$title = get_the_date( 'F Y' );
		} elseif ( is_day() ) {
			$title = get_the_date( 'F j, Y' );
		} elseif ( is_post_type_archive() ) {
			$title = post_type_archive_title( '', false );
		} elseif ( is_tax() ) {
			$title = single_term_title( '', false );
		}

		return esc_html( $title );
	}

	/**
	 * Add description after the title on archive page.
	 */
	public function get_archive_description() {
		if ( is_author() ) {
			$author_meta = get_the_author_meta( 'description' );
			if ( empty( $author_meta ) ) {
				return '';
			}

			return '<p>' . wp_kses_post( $author_meta ) . '</p>';
		}
		if ( is_category() || is_tag() ) {
			return get_the_archive_description();
		}

		return '';
	}
}
PK      \#)}  }    views/page_layout.phpnu W+A        <?php
/**
 * Single page layout.
 *
 * @package Neve\Views
 */

namespace Neve\Views;

/**
 * Class Page_Layout
 *
 * @package Neve\Views
 */
class Page_Layout extends Base_View {

	/**
	 * Init function
	 */
	public function init() {
		add_action( 'neve_do_single_page', [ $this, 'render_page' ] );
	}

	/**
	 * Render single page.
	 */
	public function render_page() {
		echo '<div class="nv-content-wrap entry-content">';
		the_content();

		do_action( 'neve_before_page_comments' );
		if ( comments_open() || get_comments_number() ) {
			comments_template();
		}

		echo '</div>';
		do_action( 'neve_do_pagination', 'single' );
	}
}
PK      \V9"    (  compatibility/easy_digital_downloads.phpnu W+A        <?php
/**
 * Author:          Uriahs Victor
 * Created on:      14/07/2021 (d/m/y)
 *
 * @package Neve
 */
namespace Neve\Compatibility;

/**
 * Class Easy_Digital_Downloads
 *
 * @package Neve\Compatibility
 */
class Easy_Digital_Downloads {

	/**
	 * Function that is run after instantiation.
	 *
	 * @return void
	 */
	public function init() {
		if ( ! class_exists( 'Easy_Digital_Downloads', false ) ) {
			return;
		}
		add_action( 'wp_enqueue_scripts', array( $this, 'dequeue_edd_styles' ) );
		$edd_settings_filter = 'edd_settings_misc';
		// For EDD 2.x, use the `edd_settings_styles` filter.
		if ( defined( 'EDD_VERSION' ) && version_compare( '2.10.999', EDD_VERSION, '>' ) ) {
			$edd_settings_filter = 'edd_settings_styles';
		}
		add_filter( $edd_settings_filter, array( $this, 'edd_settings_styles' ) );
		add_filter( 'body_class', array( $this, 'add_body_class' ) );
	}

	/**
	 * Add neve easy digital downloads body class.
	 *
	 * @param array $classes Current classes on body.
	 */
	public function add_body_class( $classes ) {

		if ( edd_is_checkout() ||
		edd_is_success_page() ||
		edd_is_failed_transaction_page() ||
		edd_is_purchase_history_page() ||
		is_post_type_archive( 'download' ) ||
		get_post_type() == 'download' ||
		is_tax( 'download_category' ) ||
		is_tax( 'download_tag' )
		) {
			$classes[] = 'nv-edd';
		}

		return $classes;

	}

	/**
	 * Dequeue the EDD default styles as we have our own.
	 *
	 * @return void
	 */
	public function dequeue_edd_styles() {
		wp_dequeue_style( 'edd-styles' );
	}

	/**
	 * Filter the settings from EDD's "Styles" tab
	 *
	 * @param mixed $settings EDD style settings.
	 * @return array
	 */
	public function edd_settings_styles( $settings ) {
		/*
		 * Settings with type 'descriptive_text' are automatically stripped by EDD
		 * So this field is not saved to the DB on save changes.
		 *
		 * see edd_settings_sanitize()
		 */
		$settings['main'] = array(
			'neve_controlled' => array(
				'id'   => 'neve_controlled',
				'name' => esc_html__( 'Controlled by Neve', 'neve' ),
				'desc' => esc_html__( 'Neve Theme controls base style settings of Easy Digital Downloads. Additional settings from other extensions might appear below.', 'neve' ),
				'type' => 'descriptive_text',
			),
		);

		return $settings;
	}

}
PK      \J"  "    compatibility/lifter.phpnu W+A        <?php
/**
 * Compatibility with Lifter LMS Plugin.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

use Neve\Core\Settings\Config;

/**
 * Class Lifter
 *
 * @package Neve\Compatibility
 */
class Lifter {

	/**
	 * Primary button selectors.
	 *
	 * @var array
	 */
	private $primary_buttons_selectors = array(
		'default' => '
			,a.llms-button-primary,
			button.llms-button-primary,
			a.llms-button-action,
			button.llms-button-action',
		'hover'   => '
			,a.llms-button-primary:hover,
			button.llms-button-primary:hover,
			a.llms-button-primary:active,
			button.llms-button-primary:active,
			a.llms-button-primary:focus,
			button.llms-button-primary:focus,
			a.llms-button-action:hover,
			a.llms-button-action:active,
			a.llms-button-action:focus,
			button.llms-button-action:hover,
			button.llms-button-action:active,
			button.llms-button-action:focus',
	);

	/**
	 * Secondary buttons selectors.
	 *
	 * @var array
	 */
	private $secondary_buttons_selectors = array(
		'default' => '
			,a.llms-button-secondary',
		'hover'   => '
			,a.llms-button-secondary:hover',
	);

	/**
	 * Init function.
	 */
	public function init() {
		if ( ! $this->should_load() ) {
			return;
		}
		$this->load_hooks();
		$this->add_inline_selectors();
	}

	/**
	 * Check if LifterLMS plugin is activated.
	 */
	private function should_load() {
		return class_exists( 'LifterLMS', false );
	}

	/**
	 * Load hooks and filters.
	 */
	private function load_hooks() {
		add_action( 'wp_enqueue_scripts', array( $this, 'load_styles' ) );

		remove_action( 'lifterlms_before_main_content', 'lifterlms_output_content_wrapper', 10 );
		remove_action( 'lifterlms_after_main_content', 'lifterlms_output_content_wrapper_end', 10 );
		remove_all_actions( 'lifterlms_sidebar' );

		add_action( 'lifterlms_before_main_content', array( $this, 'content_wrapper_open' ), 0 );
		add_action( 'lifterlms_loop', array( $this, 'content_wrapper_close' ), 100 );
		add_filter( 'lifterlms_show_page_title', '__return_false' );

		add_action( 'neve_llms_content', array( $this, 'content_open' ), 10 );
		add_action(
			'lifterlms_loop',
			function() {
				echo '</div>';
				echo '</div>';
			},
			20
		);
		add_action(
			'lifterlms_after_main_content',
			function() {
				echo '</div>';
			},
			20
		);

		add_action( 'widgets_init', array( $this, 'register_catalog_sidebar' ) );
		add_filter( 'llms_get_theme_default_sidebar', array( $this, 'lms_sidebar' ) );
		add_action( 'wp', array( $this, 'load_catalog_sidebar' ) );

		add_filter( 'llms_checkout_error_output', array( $this, 'checkout_error_entry_content_close' ) );
		add_filter(
			'neve_lifter_sidebar_setup',
			function() {
				return [
					'theme_mod' => 'neve_default_sidebar_layout',
					'side'      => $this->get_sidebar_position(),
				];
			}
		);
	}

	/**
	 * Add inline selectors for LifterLMS.
	 */
	private function add_inline_selectors() {
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_PRIMARY_NORMAL,
			array(
				$this,
				'add_primary_btns_normal',
			),
			10,
			1
		);
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_PRIMARY_HOVER,
			array(
				$this,
				'add_primary_btns_hover',
			),
			10,
			1
		);
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_PRIMARY_PADDING,
			array(
				$this,
				'add_primary_btns_padding',
			),
			10,
			1
		);


		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_SECONDARY_NORMAL,
			array(
				$this,
				'add_secondary_btns_normal',
			),
			10,
			1
		);
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_SECONDARY_HOVER,
			array(
				$this,
				'add_secondary_btns_hover',
			),
			10,
			1
		);
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_SECONDARY_PADDING,
			array(
				$this,
				'add_secondary_btns_padding',
			),
			10,
			1
		);

	}

	/**
	 * Add primary btn selectors for padding.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_primary_btns_padding( $selectors ) {
		return ( $selectors . $this->primary_buttons_selectors['default'] );

	}

	/**
	 * Add primary btn selectors for padding.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_secondary_btns_padding( $selectors ) {
		return ( $selectors . $this->secondary_buttons_selectors['default'] );

	}

	/**
	 * Add primary btn selectors.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_primary_btns_normal( $selectors ) {
		return ( $selectors . $this->primary_buttons_selectors['default'] );
	}

	/**
	 * Add primary btn selectors.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_primary_btns_hover( $selectors ) {
		return ( $selectors . $this->primary_buttons_selectors['hover'] );
	}

	/**
	 * Add secondary btn selectors.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_secondary_btns_normal( $selectors ) {
		return ( $selectors . $this->secondary_buttons_selectors['default'] );

	}

	/**
	 * Add secondary btn selectors.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_secondary_btns_hover( $selectors ) {
		return ( $selectors . $this->secondary_buttons_selectors['hover'] );
	}

	/**
	 * Enqueue styles.
	 */
	public function load_styles() {
		$path = 'lifter';

		wp_enqueue_style( 'neve-lifter', NEVE_ASSETS_URL . 'css/' . $path . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.css', array(), apply_filters( 'neve_version_filter', NEVE_VERSION ) );
	}

	/**
	 * Get sidebar position for catalog page.
	 *
	 * @return string
	 */
	private function get_sidebar_position() {
		$advanced_sidebar = get_theme_mod( 'neve_advanced_layout_options', false );
		$sidebar_position = get_theme_mod( 'neve_default_sidebar_layout', 'right' );
		if ( $advanced_sidebar === true ) {
			$sidebar_position = get_theme_mod( 'neve_other_pages_sidebar_layout', 'right' );
		}
		return $sidebar_position;
	}

	/**
	 * Load Sidebar
	 */
	public function load_catalog_sidebar() {
		$sidebar_position = $this->get_sidebar_position();
		if ( $sidebar_position === 'right' ) {
			add_action( 'neve_llms_content_after', array( $this, 'render_catalog_sidebar' ), 11 );
		}
		if ( $sidebar_position === 'left' ) {
			add_action( 'neve_llms_content', array( $this, 'render_catalog_sidebar' ), 1 );
		}
	}

	/**
	 * Display LifterLMS Course and Lesson sidebars
	 * on courses and lessons in place of the sidebar returned by
	 * this function
	 *
	 * @return string
	 */
	public function lms_sidebar() {
		return 'blog-sidebar';
	}

	/**
	 * Add markup before main content.
	 */
	public function content_wrapper_open() {
		$container_class = apply_filters( 'neve_container_class_filter', 'container' );
		echo '<div class="' . esc_attr( $container_class ) . ' lms-container">';
		echo '<div class="row">';
		do_action( 'neve_llms_content' );
	}

	/**
	 * Close Content Wrapper
	 */
	public function content_wrapper_close() {
		do_action( 'neve_llms_content_after' );
		echo '</div>';
	}

	/**
	 * Add markup for LifterLms title and open content wrap div
	 */
	public function content_open() {
		echo '<div class="nv-single-page-wrap col">';
		echo '<div class="nv-page-title-wrap nv-big-title">';
		echo '<div class="nv-page-title ">';
		echo '<h1 class="page-title">';
		lifterlms_page_title();
		echo '</h1>';
		echo '</div>';
		echo '</div>';
		$class = apply_filters( 'neve_lifter_wrap_classes', 'nv-content-wrap entry-content' );
		echo '<div class="' . esc_attr( $class ) . '">';
	}

	/**
	 * Register LifterLMS Catalog Sidebar.
	 */
	public function register_catalog_sidebar() {
		register_sidebar(
			array(
				'name'          => __( 'Catalog Sidebar', 'neve' ),
				'id'            => 'llms_shop',
				'before_widget' => '<div id="%1$s" class="widget %2$s">',
				'after_widget'  => '</div>',
				'before_title'  => '<p class="widget-title">',
				'after_title'   => '</p>',
			)
		);
	}

	/**
	 * Sidebar Markup Wrapper open.
	 */
	public function render_catalog_sidebar() {
		if ( ! is_active_sidebar( 'llms_shop' ) ) {
			return;
		}

		$sidebar_position = $this->get_sidebar_position();
		echo '<div class="nv-sidebar-wrap col-sm-12 nv-' . esc_attr( $sidebar_position ) . ' catalog-sidebar">';

		do_action( 'neve_before_sidebar_content', 'lifter', $sidebar_position );

		$has_custom_sidebar = apply_filters( 'neve_has_custom_sidebar', false, 'lifter' );

		if ( ! $has_custom_sidebar ) {
			dynamic_sidebar( 'llms_shop' );
		}

		do_action( 'neve_after_sidebar_content', 'lifter', $sidebar_position );

		echo '</div>';
	}

	/**
	 * Close entry content div on error.
	 *
	 * @param string $error Error text.
	 *
	 * @return string
	 */
	public function checkout_error_entry_content_close( $error ) {
		return $error . '</div>';
	}

}
PK      \ItV  V    compatibility/patterns.phpnu W+A        <?php
/**
 * Patterns Compatibility.
 *
 * @package Patterns.php
 */

namespace Neve\Compatibility;

/**
 * Class Patterns
 *
 * @package Neve\Compatibility
 */
class Patterns {
	/**
	 * Define list of the patterns to load.
	 *
	 * @var string[] Patterns list.
	 */
	private $patterns = [
		'dark-header-centered-content',
		'two-columns-image-text',
		'three-columns-images-text',
		'three-columns-images-text',
		'three-columns-images-texts-content',
		'four-columns-team-members',
		'two-columns-centered-content',
		'two-columns-with-text',
		'testimonials-columns',
		'gallery-grid-buttons',
		'gallery-title-buttons',
		'light-header-left-aligned-content',
	];

	/**
	 * Register patterns bootstrap hook.
	 */
	public function init() {
		add_action( 'init', [ $this, 'define_patterns' ] );
	}

	/**
	 * Load patterns.
	 */
	public function define_patterns() {
		if ( ! function_exists( 'register_block_pattern' ) ) {
			return;
		}
		foreach ( $this->patterns as $pattern ) {
			register_block_pattern(
				'neve/' . $pattern,
				require __DIR__ . '/block-patterns/' . $pattern . '.php'
			);
		}
	}

}
PK      \c    #  compatibility/page_builder_base.phpnu W+A        <?php
/**
 * Page Builder Compatibility extendable class.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

/**
 * Class Page_Builder_Base
 *
 * @package Neve\Compatibility
 */
abstract class Page_Builder_Base {

	/**
	 * Init function.
	 */
	abstract public function init();

	/**
	 * Decide if a page is edited with a page builder or not.
	 *
	 * @param int $pid Post id.
	 *
	 * @return bool
	 */
	abstract protected function is_edited_with_builder( $pid );

	/**
	 * Decide if we should set page template in builder or not.
	 */
	public function maybe_set_page_template() {
		if ( get_post_type() !== 'page' ) {
			return;
		}

		global $post;

		if ( ! isset( $post ) ) {
			return;
		}

		$post_id = get_the_ID();
		if ( ! $post_id ) {
			return;
		}

		/**
		 * Don't change if user already set a page template.
		 */
		$post_meta_template = get_post_meta( $post_id, '_wp_page_template', true );
		if ( $post_meta_template !== 'default' && ! empty( $post_meta_template ) ) {
			return;
		}

		/**
		 * Bail if page is not edited with builder.
		 */
		if ( $this->is_edited_with_builder( $post_id ) === false ) {
			return;
		}

		$this->set_page_template( $post_id );
	}

	/**
	 * Set page layout.
	 *
	 * @param int $post_id the post id.
	 */
	private function set_page_template( $post_id ) {
		global $post;

		if ( isset( $post ) && ( is_admin() || is_singular() ) && empty( $post->post_content ) ) {
			update_post_meta( $post_id, '_wp_page_template', 'page-templates/template-pagebuilder-full-width.php' );
		}
	}
}
PK      \$B    5  compatibility/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \B    )  compatibility/header_footer_elementor.phpnu W+A        <?php
/**
 * Compatibility with Elementor Header Footer plugin.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

/**
 * Class Header_Footer_Elementor
 *
 * @package Neve\Compatibility
 */
class Header_Footer_Elementor {

	/**
	 * Check if plugin is installed.
	 */
	private function should_load() {
		if ( ! defined( 'ELEMENTOR_VERSION' ) ) {
			return false;
		}
		if ( ! class_exists( 'Header_Footer_Elementor', false ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Init function.
	 */
	public function init() {
		if ( ! $this->should_load() ) {
			return;
		}

		$this->add_theme_builder_hooks();
	}

	/**
	 * Replace theme hooks with the one from the plugin.
	 */
	private function add_theme_builder_hooks() {
		add_action( 'neve_do_header', array( $this, 'do_header' ), 0 );
		add_action( 'neve_do_footer', array( $this, 'do_footer' ), 0 );
	}

	/**
	 * Replace Header hooks.
	 */
	public function do_header() {
		if ( ! hfe_header_enabled() ) {
			return;
		}
		hfe_render_header();
		remove_all_actions( 'neve_do_top_bar' );
		remove_all_actions( 'neve_do_header' );
	}

	/**
	 * Replace Footer hooks.
	 */
	public function do_footer() {
		if ( ! hfe_footer_enabled() ) {
			return;
		}
		hfe_render_footer();
		remove_all_actions( 'neve_do_footer' );
	}
}
PK      \`      compatibility/web_stories.phpnu W+A        <?php
/**
 * Web_Stories compatibility.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

/**
 * Class PWA
 */
class Web_Stories {

	/**
	 * Init function.
	 *
	 * @return bool
	 */
	public function init() {
		if ( ! $this->should_load() ) {
			return false;
		}
		$this->load_hooks();

		return true;
	}

	/**
	 * Is plugin available?.
	 */
	private function should_load() {
		return defined( 'WEBSTORIES_VERSION' );
	}

	/**
	 * Load hooks.
	 */
	private function load_hooks() {
		add_action( 'after_setup_theme', array( $this, 'setup' ) );
		add_action( 'wp_body_open', array( $this, 'embed' ) );
	}

	/**
	 * Setup theme support for web stories.
	 */
	public function setup() {
		add_theme_support(
			'web-stories',
			[
			// 'view-type'         => [ 'circles', 'carousel', 'grid', 'list' ]
			]
		);
	}

	/**
	 * Render the theme stories.
	 */
	public function embed() {
		if ( function_exists( '\Google\Web_Stories\render_theme_stories' ) ) {
			\Google\Web_Stories\render_theme_stories();
		}
	}
}
PK      \      compatibility/ppom.phpnu W+A        <?php
/**
 * PPOM compatibility.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

/**
 * Class PPOM
 */
class PPOM {

	/**
	 * Init function.
	 *
	 * @return bool
	 */
	public function init() {
		if ( ! $this->should_load() ) {
			return false;
		}

		$this->load_hooks();

		return true;
	}

	/**
	 * Is plugin available?.
	 */
	private function should_load() {
		return defined( 'PPOM_VERSION' );
	}

	/**
	 * Load hooks.
	 */
	private function load_hooks() {
		/**
		 * Neve has display:flex on the single product page, but PPOM has display:block on the same page.
		 * Prevent PPOM's override of Neve's CSS.
		 *
		 * Issue: https://github.com/Codeinwp/woocommerce-product-addon/issues/106
		*/
		add_filter( 'ppom_sp_ac_force_css_display_block', '__return_false' );
	}
}
PK      \      compatibility/pwa.phpnu W+A        <?php
/**
 * PWA Plugin compatibility.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

/**
 * Class PWA
 */
class PWA {

	/**
	 * Init function.
	 *
	 * @return bool
	 */
	public function init() {
		if ( ! $this->should_load() ) {
			return false;
		}
		$this->load_hooks();

		return true;
	}

	/**
	 * Decide if class should run.
	 */
	private function should_load() {
		return defined( 'PWA_VERSION' ) && function_exists( 'wp_service_worker_error_details_template' ) && function_exists( 'pwa_get_header' ) && function_exists( 'wp_service_worker_error_message_placeholder' ) && function_exists( 'pwa_get_footer' );
	}

	/**
	 * Load hooks.
	 */
	private function load_hooks() {
		add_action( 'neve_do_offline', array( $this, 'offline_default_template' ) );
		add_action( 'neve_do_server_error', array( $this, 'server_error_default_template' ) );
	}

	/**
	 * Load offline default template.
	 */
	public function offline_default_template() {
		?>
		<main>
			<h1><?php esc_html_e( 'Oops! It looks like you&#8217;re offline.', 'neve' ); ?></h1>
			<?php wp_service_worker_error_message_placeholder(); ?>
		</main>
		<?php
	}

	/**
	 * Load server error template.
	 */
	public function server_error_default_template() {

		?>
		<main>
			<h1><?php esc_html_e( 'Oops! Something went wrong.', 'neve' ); ?></h1>
			<?php wp_service_worker_error_message_placeholder(); ?>
			<?php wp_service_worker_error_details_template(); ?>
		</main>
		<?php
	}
}
PK      \l  l    compatibility/wpml.phpnu W+A        <?php
/**
 * Holds the compatibility logic for WPML plugin.
 *
 * Author:          Bogdan Preda <friends@themeisle.com>
 * Created on:      17/03/2023
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

/**
 * Class WPML
 *
 * @package Neve\Compatibility
 */
class WPML {

	/**
	 * Init function.
	 *
	 * @return bool
	 */
	public function init() {
		if ( ! $this->should_load() ) {
			return false;
		}
		$this->load_hooks();

		return true;
	}

	/**
	 * Load hooks.
	 */
	public function load_hooks() {
		add_filter( 'neve_filter_featured_posts', array( $this, 'filter_posts_and_return_original' ), 10, 1 );
	}

	/**
	 * Decide if this class should load.
	 *
	 * @return bool
	 */
	private function should_load() {
		return defined( 'WPML_PLUGIN_PATH' );
	}

	/**
	 * This will return the original language posts.
	 * This hooks into `neve_filter_featured_posts` filter.
	 * We ensure that returned posts are in the original language.
	 *
	 * @param array $posts The posts to filter.
	 *
	 * @return array This returns an array of post id's. The filter also accepts an array of posts.
	 */
	final public function filter_posts_and_return_original( $posts ) {
		$original_post_ids = array();
		$current_language  = apply_filters( 'wpml_current_language', null );
		foreach ( $posts as $post ) {
			$original_post_id = apply_filters( 'wpml_object_id', $post['ID'], $post['post_type'], true, $current_language );
			if ( $original_post_id !== $post['ID'] ) {
				$original_post_ids[ $original_post_id ] = true;
				continue;
			}
			$original_post_ids[ $post['ID'] ] = true;
		}

		return array_keys( $original_post_ids );
	}
}
PK      \if  f  &  compatibility/header_footer_beaver.phpnu W+A        <?php
/**
 * Compatibility with Header Footer for Beaver Builder plugin.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

/**
 * Class Header_Footer_Beaver
 *
 * @package Neve\Compatibility
 */
class Header_Footer_Beaver {


	/**
	 * Init function.
	 */
	public function init() {
		if ( ! $this->should_load() ) {
			return false;
		}
		$this->add_theme_builder_hooks();
	}

	/**
	 * Check if plugin is installed.
	 */
	private function should_load() {
		if ( ! defined( 'FL_BUILDER_VERSION' ) ) {
			return false;
		}
		if ( ! class_exists( '\BB_Header_Footer', false ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Replace theme hooks with the one from the plugin.
	 */
	private function add_theme_builder_hooks() {
		add_action( 'neve_do_header', array( $this, 'do_header' ), 0 );
		add_action( 'neve_do_footer', array( $this, 'do_footer' ), 0 );
	}

	/**
	 * Replace Header hooks.
	 */
	public function do_header() {
		$header_id = \BB_Header_Footer::get_settings( 'bb_header_id', '' );
		if ( empty( $header_id ) ) {
			return false;
		}
		remove_all_actions( 'neve_do_top_bar' );
		remove_all_actions( 'neve_do_header' );

		echo '<header id="nv-beaver-header">';
		\BB_Header_Footer::get_header_content();
		echo '</header>';

		return true;
	}

	/**
	 * Replace Footer hooks.
	 */
	public function do_footer() {
		$footer_id = \BB_Header_Footer::get_settings( 'bb_footer_id', '' );
		if ( empty( $footer_id ) ) {
			return false;
		}
		remove_all_actions( 'neve_do_footer' );
		echo '<footer id="nv-beaver-footer">';
		\BB_Header_Footer::get_footer_content();
		echo '</footer>';

		return true;
	}
}
PK      \\(m=    !  compatibility/starter_content.phpnu W+A        <?php
/**
 * Starter Content Compatibility.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

/**
 * Class Starter_Content
 *
 * @package Neve\Compatibility
 */
class Starter_Content {
	const HOME_SLUG       = 'home';
	const BLOG_SLUG       = 'blog';
	const ABOUT_SLUG      = 'about';
	const CONTACT         = 'contact';
	const PORTOFOLIO_SLUG = 'portofolio';
	const PROJECT_DETAILS = 'project-details';


	/**
	 * Run the hooks and filters.
	 */
	public function __construct() {
		$is_fresh_site = get_option( 'fresh_site' );
		if ( ! $is_fresh_site ) {
			return;
		}

		add_action(
			'wp_insert_post',
			[
				$this,
				'register_listener',
			],
			99,
			3
		); // starter content does not provide means of adding post meta so we need to tweak it.

		if ( ! is_customize_preview() ) {
			return;
		}
		add_filter(
			'default_post_metadata',
			[ $this, 'starter_meta' ],
			99,
			3
		);

	}

	/**
	 * Load default starter meta.
	 *
	 * @param mixed  $value Value.
	 * @param int    $post_id Post id.
	 * @param string $meta_key Meta key.
	 *
	 * @return string Meta value.
	 */
	public function starter_meta( $value, $post_id, $meta_key ) {
		if ( get_post_type( $post_id ) !== 'page' ) {
			return $value;
		}
		if ( $meta_key === 'neve_meta_disable_title' ) {
			return 'on';
		}
		if ( $meta_key === 'neve_meta_enable_content_width' ) {
			return 'on';
		}
		if ( $meta_key === 'neve_meta_content_width' ) {
			return '100';
		}

		return $value;
	}


	/**
	 * Register listener to insert post.
	 *
	 * @param int      $post_ID Post Id.
	 * @param \WP_Post $post Post object.
	 * @param bool     $update Is update.
	 */
	public function register_listener( $post_ID, $post, $update ) {
		if ( $update ) {
			return;
		}
		$is_from_starter_content = ! empty( get_post_meta( $post_ID, '_customize_draft_post_name', true ) );
		if ( ! $is_from_starter_content ) {
			return;
		}
		if ( $post->post_type === 'page' ) {
			update_post_meta( $post_ID, 'neve_meta_disable_title', 'on' );
			update_post_meta( $post_ID, 'neve_meta_enable_content_width', 'on' );
			update_post_meta( $post_ID, 'neve_meta_content_width', '100' );
		}
	}

	/**
	 * Return starter content definition.
	 *
	 * @return mixed|void
	 */
	public function get() {

		$nav_items = [
			'home'                 => [
				'type'      => 'post_type',
				'object'    => 'page',
				'object_id' => '{{' . self::HOME_SLUG . '}}',
			],
			'page_about'           => [
				'type'      => 'post_type',
				'object'    => 'page',
				'object_id' => '{{' . self::ABOUT_SLUG . '}}',
			],
			'page_portofolio'      => [
				'type'      => 'post_type',
				'object'    => 'page',
				'object_id' => '{{' . self::PORTOFOLIO_SLUG . '}}',
			],
			'page_project_details' => [
				'type'      => 'post_type',
				'object'    => 'page',
				'object_id' => '{{' . self::PROJECT_DETAILS . '}}',
			],
			'page_blog'            => [
				'type'      => 'post_type',
				'object'    => 'page',
				'object_id' => '{{' . self::BLOG_SLUG . '}}',
			],
		];

		$content = [
			'nav_menus'   =>
				[
					'primary' => [
						'items' => $nav_items,
					],
				],
			'options'     => [
				'page_on_front'  => '{{' . self::HOME_SLUG . '}}',
				'page_for_posts' => '{{' . self::BLOG_SLUG . '}}',
				'show_on_front'  => 'page',
				'blogname'       => 'Web Agency Demo 1',
			],
			'theme_mods'  => require __DIR__ . '/starter-content/theme-mods.php',
			'attachments' => array(
				'featured-image-logo' => array(
					'post_title'   => 'Featured Logo',
					'post_content' => 'Attachment Description',
					'post_excerpt' => 'Attachment Caption',
					'file'         => 'assets/img/starter-content/logo-agency.png',
				),
			),
			'posts'       => [
				self::HOME_SLUG       => require __DIR__ . '/starter-content/home.php',
				self::ABOUT_SLUG      => require __DIR__ . '/starter-content/about.php',
				self::CONTACT         => require __DIR__ . '/starter-content/contact.php',
				self::PORTOFOLIO_SLUG => require __DIR__ . '/starter-content/portofolio.php',
				self::PROJECT_DETAILS => require __DIR__ . '/starter-content/project-details.php',
				self::BLOG_SLUG       => [
					'post_name'  => self::BLOG_SLUG,
					'post_type'  => 'page',
					'post_title' => _x( 'Blog', 'Theme starter content', 'neve' ),
				],
			],
		];


		return apply_filters( 'neve_starter_content', $content );
	}
}
PK      \7e  e    compatibility/generic.phpnu W+A        <?php
/**
 * Generic compatibility class.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      2019-06-05
 *
 * @package generic.php
 */

namespace Neve\Compatibility;

/**
 * Class Generic
 *
 * @package Neve\Compatibility
 */
class Generic {
	/**
	 * Init function.
	 */
	public function init() {
		if ( class_exists( 'Mega_Menu', false ) ) {
			add_filter( 'megamenu_themes', array( $this, 'max_megamenu_theme' ) );
		}
	}

	/**
	 * Max Mega Menu Hestia Compatibility
	 *
	 * @param array $themes the themes.
	 *
	 * @return array
	 **/
	public function max_megamenu_theme( $themes ) {
		$themes['neve_max_mega_menu'] = array(
			'title'                           => 'Neve',
			'menu_item_link_height'           => '50px',
			'menu_item_align'                 => 'right',
			'container_background_from'       => 'rgba(255, 255, 255, 0)',
			'container_background_to'         => 'rgba(255, 255, 255, 0)',
			'menu_item_background_hover_from' => 'rgba(255, 255, 255, 0.1)',
			'menu_item_background_hover_to'   => 'rgba(255, 255, 255, 0.1)',
			'menu_item_link_color'            => '#555',
			'menu_item_link_color_hover'      => '#0366d6',
			'menu_item_highlight_current'     => 'off',
			'panel_background_from'           => 'rgb(255, 255, 255)',
			'panel_background_to'             => 'rgb(255, 255, 255)',
			'responsive_breakpoint'           => '959px',
			'resets'                          => 'on',
			'toggle_background_from'          => 'rgba(255, 255, 255, 0.1)',
			'toggle_background_to'            => 'rgba(255, 255, 255, 0.1)',
			'toggle_font_color'               => 'rgb(102, 102, 102)',
			'mobile_background_from'          => 'rgb(255, 255, 255)',
			'mobile_background_to'            => 'rgb(255, 255, 255)',
			'mobile_menu_item_link_color'     => 'rgb(102, 102, 102)',
			'responsive_text'                 => '',
		);

		return $themes;
	}

}
PK      \2 ,   ,    compatibility/amp.phpnu W+A        <?php
/**
 * AMP Compatibility.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      17/10/2018
 *
 * @package Amp.php
 */

namespace Neve\Compatibility;

/**
 * Class Amp
 *
 * @package Neve\Compatibility
 */
class Amp {

	/**
	 * Run the hooks and filters.
	 */
	public function register_hooks() {
		if ( ! neve_is_amp() ) {
			return;
		}
		add_filter( 'neve_caret_wrap_filter', array( $this, 'amp_dropdowns' ), 10, 2 );
		add_filter(
			'neve_woocommerce_sidebar_filter_btn_data_attrs',
			array(
				$this,
				'add_woo_sidebar_filter_btn_attrs',
			)
		);
		add_filter(
			'neve_filter_sidebar_close_button_data_attrs',
			array(
				$this,
				'sidebar_close_button_attrs',
			),
			10,
			2
		);
		add_filter( 'walker_nav_menu_start_el', array( $this, 'wrap_content' ), 10, 4 );
		add_filter( 'neve_sidebar_data_attrs', array( $this, 'add_woo_sidebar_attrs' ), 10, 2 );
		add_filter( 'neve_search_menu_item_filter', array( $this, 'add_search_menu_item_attrs' ), 10, 2 );
		add_action( 'neve_after_header_hook', array( $this, 'render_amp_states' ) );
		add_filter( 'neve_nav_toggle_data_attrs', array( $this, 'add_nav_toggle_attrs' ) );
		add_action( 'wp_head', array( $this, 'inline_styles' ) );


		/**
		 * Add infinite scroll for amp.
		 */
		$this->maybe_add_amp_infinite_scroll();
	}

	/**
	 * Add inline styles for AMP.
	 *
	 * @return void
	 */
	public function inline_styles() {
		echo '
			<style>
			.header-menu-sidebar .has-caret.amp {
				padding: 15px 0 !important;
			}
			.header-menu-sidebar .amp.dropdown-open + .sub-menu {
				display: block !important;
			}
			.site-logo amp-img img {
				max-height: 60px;
			}
			.sub-menu .has-caret.amp {
				padding: 10px 20px;
			}
			.amp-desktop-caret-wrap {
				display: none;
			}
			.amp-caret-wrap svg {
				fill: currentColor;
				width: 1em;
			}
			.has-caret.amp {
				height: 100%;
				display: flex;
				align-items: center;
			}
			.has-caret.amp a {
				flex-grow: 1;
			}
			.has-caret.amp .caret-wrap {
				margin-left: auto;
			}
			.nv-post-thumbnail-wrap amp-img {
				box-shadow: var(--boxshadow, none);
			}
			.cover-post amp-img {
				--boxshadow: none;
			}
			@media (min-width: 960px) {
				.amp-desktop-caret-wrap {
					display: none;
				}
				.amp-caret-wrap {
					display: block;
				}
			}
			</style>';
	}

	/**
	 * Register amp bootstrap hook.
	 */
	public function init() {
		add_action( 'wp', array( $this, 'register_hooks' ) );
	}

	/**
	 * Wrap the content of the menu items in case of AMP.
	 *
	 * @param string $item_output item markup.
	 * @param object $item item information.
	 * @param int    $depth item depth.
	 * @param object $args menu args.
	 *
	 * @return string
	 */
	public function wrap_content( $item_output, $item, $depth, $args ) {
		if ( ! neve_is_amp() ) {
			return $item_output;
		}

		if ( strpos( $args->menu_id, 'nv-primary-navigation' ) === false ) {
			return $item_output;
		}

		if ( ! in_array( 'menu-item-has-children', $item->classes, true ) ) {
			return $item_output;
		}

		if ( strpos( $item_output, 'has-caret' ) > - 1 ) {
			return $item_output;
		}

		$caret  = '<div  class="caret-wrap ' . $item->menu_order . '">';
		$caret .= '<span class="caret"><svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"/></svg></span>';
		$caret .= '</div>';

		$item_output = '<div class="has-caret amp">' . $item_output . $caret . '</div>';
		// Filter that is used for AMP proper event integration.
		$item_output = apply_filters( 'neve_caret_wrap_filter', $item_output, $item->menu_order );

		return $item_output;
	}

	/**
	 * Add amp parameters for menu child search icon.
	 *
	 * @param string $input input string.
	 * @param int    $instance instance number of nav menu.
	 *
	 * @return string
	 */
	public function add_search_menu_item_attrs( $input, $instance ) {
		return $input . ' on="tap:nv-menu-item-search-' . $instance . '.toggleClass(class=\'active\')" ';
	}

	/**
	 * Add amp states to the dom.
	 */
	public function render_amp_states() {
		echo '<amp-state id="nvAmpMenuExpanded">';
		echo '<script type="application/json">false</script>';
		echo '</amp-state>';

		echo '<amp-state id="nvAmpWooSidebarExpanded">';
		echo '<script type="application/json">false</script>';
		echo '</amp-state>';
	}

	/**
	 * Add the nav toggle data attributes.
	 *
	 * @return string
	 */
	public function add_nav_toggle_attrs( $input = '' ) {
		$input  = ' on="tap:neve_body.toggleClass(class=\'is-menu-sidebar\'),AMP.setState( { nvAmpMenuExpanded: ! nvAmpMenuExpanded } )" ';
		$input .= ' role="button" ';
		$input .= ' aria-expanded="false" ';
		$input .= ' [aria-expanded]="nvAmpMenuExpanded ? \'true\' : \'false\'" ';

		return $input;
	}

	/**
	 * Add filter button amp attrs.
	 *
	 * @param string $input input.
	 *
	 * @return string
	 */
	public function add_woo_sidebar_filter_btn_attrs( $input ) {

		$input .= ' on="tap:AMP.setState( { nvAmpWooSidebarExpanded: true } )" ';
		$input .= ' role="button" ';
		$input .= ' ';

		return $input;
	}

	/**
	 * Add woo sidebar amp attrs.
	 *
	 * @param string $input input.
	 * @param string $slug sidebar slug.
	 *
	 * @return string
	 */
	public function add_woo_sidebar_attrs( $input, $slug ) {
		if ( $slug !== 'shop-sidebar' ) {
			return $input;
		}

		$input .= ' [class]="\'nv-sidebar-wrap col-sm-12 left shop-sidebar \' + ( nvAmpWooSidebarExpanded ? \'sidebar-open\' : \'\' )" ';
		$input .= ' aria-expanded="false" [aria-expanded]="nvAmpWooSidebarExpanded ? \'true\' : \'false\'" ';

		return $input;
	}

	/**
	 * Add amp attributes to sidebar close button.
	 *
	 * @param string $input empty string.
	 * @param string $slug sidebar slug.
	 *
	 * @return string
	 */
	public function sidebar_close_button_attrs( $input, $slug ) {
		if ( $slug !== 'shop-sidebar' ) {
			return $input;
		}
		$input .= ' on="tap:AMP.setState( { nvAmpWooSidebarExpanded: false } )" ';
		$input .= ' role="button" ';
		$input .= '  ';

		return $input;
	}

	/**
	 * Implement AMP integration on drop-downs.
	 *
	 * @param string $output the output.
	 * @param string $id menu item order.
	 *
	 * @return mixed
	 */
	public function amp_dropdowns( $output, $id ) {

		// Generate a unique id for drop-down items.
		$state = 'neveMenuItemExpanded' . $id;

		$attrs     = '';
		$amp_caret = '';
		$caret     = '<span class="caret"><svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"/></svg></span>';

		$attrs .= '<div class="has-caret amp" [class]="\'has-caret amp\' + ( ' . $state . ' ? \' dropdown-open\' : \'\')">';
		$attrs .= '<amp-state id="' . $state . '"><script type="application/json">false</script></amp-state>';

		$amp_caret .= '<div class="caret-wrap amp-desktop-caret-wrap">' . $caret . '</div>';
		$amp_caret .= '<div class="caret-wrap amp-caret-wrap"';
		$amp_caret .= ' on="tap:AMP.setState( { ' . $state . ': ! ' . $state . ' } )"';
		$amp_caret .= ' role="button" ';
		$amp_caret .= ' aria-expanded="false" ';
		$amp_caret .= ' [aria-expanded]="' . $state . ' ? \'true\' : \'false\'">' . $caret . '</div>';

		$output = str_replace( '<div class="has-caret amp">', $attrs, $output );
		$output = str_replace( $caret, $amp_caret, $output );

		return $output;
	}

	/**
	 * Try to add amp infinite scroll.
	 *
	 * @return bool
	 */
	private function maybe_add_amp_infinite_scroll() {

		if ( ! $this->should_display_infinite_scroll() ) {
			return false;
		}

		add_action( 'wp_head', [ $this, 'add_amp_experiments' ], 1 );

		remove_all_actions( 'neve_do_pagination' );
		add_action( 'neve_before_footer_hook', [ $this, 'wrap_footer_before' ] );
		add_action( 'neve_after_footer_hook', [ $this, 'wrap_footer_after' ] );

		return true;
	}

	/**
	 * Check if it's blog post index.
	 *
	 * @return bool
	 */
	private function is_blog_page() {
		global $post;

		$post_type = get_post_type( $post );

		return ( $post_type === 'post' ) && ( is_home() || is_archive() );
	}

	/**
	 * Decide if amp infinite scroll should work.
	 *
	 * @return bool
	 */
	public function should_display_infinite_scroll() {

		if ( ! $this->is_blog_page() ) {
			return false;
		}
		if ( $this->blog_has_sidebar() ) {
			return false;
		}

		$pagination_type = get_theme_mod( 'neve_pagination_type', 'number' );
		if ( $pagination_type !== 'infinite' ) {
			return false;
		}

		$has_pagination = ! empty( get_the_posts_pagination() );
		if ( ! $has_pagination ) {
			return false;
		}

		return true;
	}

	/**
	 * Amp experiments for infinite scroll feature.
	 */
	public function add_amp_experiments() {
		echo '<meta name="amp-experiments-opt-in" content="amp-next-page">';
	}

	/**
	 * Check if blog has sidebar.
	 *
	 * @return bool
	 */
	public function blog_has_sidebar() {
		$option           = 'neve_default_sidebar_layout';
		$advanced_options = get_theme_mod( 'neve_advanced_layout_options', false );
		if ( $advanced_options !== false ) {
			$option = 'neve_blog_archive_sidebar_layout';
		}

		return apply_filters( 'neve_sidebar_position', get_theme_mod( $option, 'right' ) ) !== 'full-width';
	}

	/**
	 * Before footer pagination tags.
	 */
	public function wrap_footer_before() {
		$amp_pagination_data = $this->get_amp_pagination_data();

		if ( ! is_paged() ) {
			echo '<amp-next-page>';
			echo '<script type="application/json">';
			echo wp_json_encode( $amp_pagination_data );
			echo '</script>';
			echo '<div footer>';
		} else {
			$links = paginate_links( array( 'type' => 'list' ) );
			$links = str_replace(
				array( '<a class="prev', '<a class="next' ),
				array(
					'<a rel="prev" class="prev',
					'<a rel="next" class="next',
				),
				$links
			);
			echo '<div class="nv-index-posts" next-page-hide>';
			echo wp_kses_post( $links );
			echo '</div>';
		}
	}

	/**
	 * After footer pagination tags.
	 */
	public function wrap_footer_after() {
		if ( ! is_paged() ) {
			echo '</div>';
			echo '</amp-next-page>';
		}
	}

	/**
	 * Get pagination data for amp-next-page
	 *
	 * @return array
	 */
	private function get_amp_pagination_data() {
		$amp_pagination = [];
		$pagination     = paginate_links(
			[
				'show_all'  => true,
				'prev_next' => false,
				'type'      => 'array',
			]
		);

		foreach ( $pagination as $page ) {

			preg_match( '#<a.+?href="(.+?)">(.+?)</a>#s', $page, $matches );

			if ( empty( $matches ) ) {
				continue;
			}

			$url   = html_entity_decode( $matches[1] );
			$page  = html_entity_decode( $matches[2] );
			$image = get_site_icon_url();

			$amp_pagination[] = [
				'url'   => $url,
				'title' => get_bloginfo( 'name' ) . ' - ' . __( 'Page', 'neve' ) . ' ' . $page . ' - ' . get_bloginfo( 'description' ),
				'image' => $image,
			];

		}

		return $amp_pagination;
	}
}
PK      \?Ozw  zw    compatibility/woocommerce.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      04/09/2018
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

use HFG\Core\Components\CartIcon;
use HFG\Core\Magic_Tags;
use Neve\Core\Dynamic_Css;
use Neve\Core\Settings\Config;
use Neve\Customizer\Defaults\Layout;
use Neve\Views\Breadcrumbs;
use Neve\Views\Layouts\Layout_Sidebar;
use RankMath\Helper;
use WC_Payment_Gateways;

/**
 * Class Woocommerce
 *
 * @package Neve\Compatibility
 */
class Woocommerce {

	use Layout;

	/**
	 * Primary button selectors.
	 *
	 * @var array
	 */
	private $primary_buttons_selectors = array(
		'default' => '
			,.woocommerce *:not(.woocommerce-mini-cart__buttons) > a.button,
			.woocommerce *:not(.woocommerce-mini-cart__buttons) > .button:not(.nv-sidebar-toggle):not(.nv-close-cart-sidebar):not([name="apply_coupon"]):not(.more-details):not(.checkout-button),
			.woocommerce a.button.alt,
			.woocommerce a.button.button-primary,
			.woocommerce button.button:disabled,
			.woocommerce button.button:disabled[disabled],
			.woocommerce a.button.add_to_cart,
			.woocommerce a.product_type_grouped,
			.woocommerce a.product_type_external,
			.woocommerce a.product_type_variable,
			.woocommerce button.button.alt,
			.woocommerce button.button.alt.single_add_to_cart_button.disabled,
			.woocommerce button.button.alt.single_add_to_cart_button,
			.woocommerce .actions > button[type=submit],
			.woocommerce button#place_order,
			.woocommerce .return-to-shop > .button,
			.button.woocommerce-form-login__submit',
		'hover'   => '
			,.woocommerce *:not(.woocommerce-mini-cart__buttons) > a.button:hover,
			.woocommerce *:not(.woocommerce-mini-cart__buttons) > .button:not(.nv-sidebar-toggle):not(.nv-close-cart-sidebar):not([name="apply_coupon"]):not(.more-details):not(.checkout-button):hover,
			.woocommerce a.button.alt:hover,
			.woocommerce a.button.button-primary:hover,
			.woocommerce button.button:disabled:hover,
			.woocommerce button.button:disabled[disabled]:hover,
			.woocommerce a.button.add_to_cart:hover,
			.woocommerce a.product_type_grouped:hover,
			.woocommerce a.product_type_external:hover,
			.woocommerce a.product_type_variable:hover,
			.woocommerce button.button.alt.single_add_to_cart_button.disabled:hover,
			.woocommerce button.button.alt.single_add_to_cart_button:hover,
			.woocommerce .actions > button[type=submit]:hover,
			.woocommerce .return-to-shop > .button:hover,
			.button.woocommerce-form-login__submit:hover',
	);

	/**
	 * Secondary buttons selectors.
	 *
	 * @var array
	 */
	private $secondary_buttons_selectors = array(
		'default' => '
			,.woocommerce-cart table.cart td.actions .coupon > .input-text + .button,
			.woocommerce-checkout #neve-checkout-coupon .woocommerce-form-coupon .form-row-last button,
			.woocommerce button.button:not(.single_add_to_cart_button),
			.woocommerce a.added_to_cart,
			.woocommerce .checkout_coupon button.button,
			.woocommerce .price_slider_amount button.button,
			.woocommerce .button.button-secondary.more-details,
			.woocommerce-checkout #neve-checkout-coupon .woocommerce-form-coupon .form-row-last button.button',
		'hover'   => '
			,.woocommerce-cart table.cart td.actions .coupon > .input-text + .button:hover,
			.woocommerce-checkout #neve-checkout-coupon .woocommerce-form-coupon .form-row-last button:hover,
			.woocommerce button.button:not(.single_add_to_cart_button):hover,
			.woocommerce a.added_to_cart:hover,
			.woocommerce .checkout_coupon button.button:hover,
			.woocommerce .price_slider_amount button.button:hover,
			.woocommerce .button.button-secondary.more-details:hover,
			.woocommerce-checkout #neve-checkout-coupon .woocommerce-form-coupon .form-row-last button.button:hover',
	);
	/**
	 * Sidebar manager.
	 *
	 * @var \Neve\Views\Layouts\Layout_Sidebar
	 */
	private $sidebar_manager;

	/**
	 * Initialize the module.
	 */
	public function init() {
		add_filter( 'body_class', array( $this, 'add_payment_method_class' ) );
		add_action( 'wp', array( $this, 'register_hooks' ), 11 );
		add_filter( 'neve_react_controls_localization', array( $this, 'add_customizer_options' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'payment_style' ), 100 );
	}

	/**
	 * Add payment method class on body.
	 *
	 * @param array $classes Body classes.
	 *
	 * @return array
	 */
	public function add_payment_method_class( $classes ) {
		if ( ! class_exists( 'WooCommerce', false ) ) {
			return $classes;
		}

		if ( ! is_checkout() ) {
			return $classes;
		}

		$payment_method = $this->get_payment_method();
		if ( ! $payment_method ) {
			return $classes;
		}

		$classes[] = 'nv-pay-' . esc_html( $payment_method );
		return $classes;
	}

	/**
	 * Add params to specify if the site has elementor templates.
	 *
	 * @param  array $options current options.
	 * @return array
	 */
	public function add_customizer_options( $options ) {
		$options['elementor']['hasElementorShopTemplate']    = Elementor::has_template( 'product_archive' );
		$options['elementor']['hasElementorProductTemplate'] = Elementor::has_template( 'single_product' );
		return $options;
	}

	/**
	 * Add inline style for payment methods.
	 */
	public function payment_style() {
		if ( ! class_exists( 'WooCommerce', false ) ) {
			return;
		}

		$payment_gateways = WC()->payment_gateways()->get_available_payment_gateways();
		if ( empty( $payment_gateways ) ) {
			return;
		}

		if ( ! isset( $payment_gateways['stripe'] ) ) {
			return;
		}

		if ( ! isset( $payment_gateways['stripe']->settings ) ) {
			return;
		}

		if ( ! isset( $payment_gateways['stripe']->settings['payment_request_button_locations'] ) ) {
			return;
		}

		$css = '';

		// Inline style for stripe.
		$button_locations = $payment_gateways['stripe']->settings['payment_request_button_locations'];
		if ( in_array( 'product', $button_locations ) ) {
			$css .= '
			.woocommerce.single .entry-summary > form.cart { display:block; }
			.woocommerce div.product form.cart .button { float: none; }
			.sp-wl-wrap.sp-wl-product-wrap { margin-left: 0; margin-top: 5px;}';
		}
		if ( in_array( 'cart', $button_locations ) ) {
			$css .= '.woocommerce .cart_totals .wc-proceed-to-checkout { display:block; }';
		}


		if ( empty( $css ) ) {
			return;
		}

		$css = Dynamic_Css::minify_css( $css );
		wp_add_inline_style( 'neve-woocommerce', $css );
	}

	/**
	 * Get the selected payment method.
	 *
	 * @return string | null
	 */
	private function get_payment_method() {
		if ( ! function_exists( 'WC' ) ) {
			return null;
		}

		if ( ! class_exists( 'WC_Payment_Gateways' ) ) {
			return null;
		}

		$payment_method = WC()->session->get( 'chosen_payment_method' );
		if ( ! $payment_method ) {
			// If payment method is null, see if there is only one option;
			$payment_gateways          = new WC_Payment_Gateways();
			$available_payment_methods = $payment_gateways->get_available_payment_gateways();
			if ( is_array( $available_payment_methods ) && count( $available_payment_methods ) === 1 ) {
				return array_keys( $available_payment_methods )[0];
			}
		}
		return $payment_method;
	}

	/**
	 * Should module load?
	 *
	 * @return bool
	 */
	public function should_load() {
		if ( ! class_exists( 'WooCommerce', false ) ) {
			return false;
		}

		// Prevent doing any modifications on the checkout page if the payment method is Klarna.
		if ( is_checkout() ) {
			$payment_method = $this->get_payment_method();
			if ( $payment_method === 'kco' ) {
				return false;
			}
		}

		return ! $this->is_current_page_elementor_template();
	}

	/**
	 * Is the current page Elementor template?
	 *
	 * @return bool
	 */
	private function is_current_page_elementor_template() {
		/**
		 * Detect if there is a template in Elementor for the shop page.
		 */
		$is_shop_template = Elementor::is_elementor_template( 'product_archive' );

		/**
		 * Detect if there is a template in Elementor for the single product page.
		 */
		$is_product_template = Elementor::is_elementor_template( 'single_product' );

		/**
		 * Detect if a page is using the checkout widget.
		 */
		$is_elementor_checkout = Elementor::is_elementor_checkout();

		// Prevent any modifications if any condition above are true.
		return ( $is_shop_template || $is_product_template || $is_elementor_checkout );
	}

	/**
	 * Register hooks
	 *
	 * @return void
	 */
	public function register_hooks() {
		if ( ! $this->should_load() ) {
			return;
		}

		$this->sidebar_manager = new Layout_Sidebar();

		add_action( 'admin_init', array( $this, 'set_update_woo_width_flag' ), 9 );
		add_action( 'admin_footer', array( $this, 'update_woo_width' ) );

		// Wrap content.
		add_action( 'neve_after_primary_start', array( $this, 'wrap_pages_start' ) );
		add_action( 'neve_before_primary_end', array( $this, 'wrap_pages_end' ) );

		add_action( 'woocommerce_before_main_content', array( $this, 'wrap_main_content_start' ), 15 );
		add_action( 'woocommerce_after_main_content', array( $this, 'close_div' ), 15 );

		// Handle shop sidebar.
		remove_action( 'woocommerce_sidebar', 'woocommerce_get_sidebar', 10 );
		add_action( 'woocommerce_before_main_content', array( $this, 'shop_sidebar_left' ) );
		add_action( 'woocommerce_sidebar', array( $this, 'shop_sidebar_right' ) );

		/**
		 * Change product page sidebar default position
		 * Priority 9 to allow meta control to override this value
		 */
		// add_filter( 'neve_sidebar_position', array( $this, 'product_page_sidebar_default_position' ), 9 );

		// Remove WooCommerce wrap.
		remove_action( 'woocommerce_after_main_content', 'woocommerce_output_content_wrapper_end', 10 );
		remove_action( 'woocommerce_before_main_content', 'woocommerce_output_content_wrapper', 10 );

		add_action( 'woocommerce_before_main_content', 'woocommerce_breadcrumb', 20, 0 );

		add_filter( 'neve_post_meta_filters_post_id', array( $this, 'adapt_meta_for_shop_page' ) );

		/**
		 * Ensure cart contents update when products are added to the cart via AJAX
		 */
		add_filter( 'woocommerce_add_to_cart_fragments', array( $this, 'cart_link_fragment' ) );

		remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart', 10 );

		add_filter( 'woocommerce_product_description_heading', '__return_false' );
		add_filter( 'woocommerce_product_additional_information_heading', '__return_false' );

		add_filter( 'woocommerce_cart_item_price', array( $this, 'show_crossed_regular_price_on_cart' ), 10, 3 );

		// Add breadcrumbs and results count
		add_action( 'neve_bc_count', [ $this, 'render_breadcrumbs' ] );
		add_action( 'neve_bc_count', 'woocommerce_result_count' );

		$this->edit_woocommerce_header();
		$this->move_checkout_coupon();
		$this->add_inline_selectors();
		add_action( 'wp', [ $this, 'setup_form_buttons' ] );

		add_action(
			'woocommerce_checkout_before_customer_details',
			function () {
				echo '<div class="nv-customer-details">';
			},
			0
		);
		add_action( 'woocommerce_checkout_after_customer_details', [ $this, 'close_div' ], PHP_INT_MAX );
		add_action(
			'woocommerce_checkout_before_order_review_heading',
			function () {
				echo '<div class="nv-order-review">';
			}
		);
		add_action( 'woocommerce_checkout_after_order_review', [ $this, 'close_div' ] );

		add_action(
			'woocommerce_before_single_product_summary',
			function () {
				echo '<div class="nv-single-product-top">';
			},
			11
		);
		// here the priority should always be to close earlier than the Neve PRO performance module opening div
		add_action( 'woocommerce_after_single_product_summary', [ $this, 'close_div' ], -100 );
		// Change default for shop columns WooCommerce option.
		add_filter( 'default_option_woocommerce_catalog_columns', [ $this, 'change_default_shop_cols' ] );
	}

	/**
	 * Normally, WooCommerce doesn't show the regular price on the cart, just shows the sale price.
	 * With that, the regular price is shown also as crossed on the cart.
	 *
	 * @param  string $price that current price column value on the cart page.
	 * @param  array  $cart_item current cart item.
	 * @param  string $cart_item_key cart item key.
	 * @return string
	 */
	public function show_crossed_regular_price_on_cart( $price, $cart_item, $cart_item_key ) {
		$product = $cart_item['data'];

		return $product->get_price_html();
	}

	/**
	 * Change default for catalog columns.
	 *
	 * @param int $default default value -> 4.
	 *
	 * @return int
	 */
	public function change_default_shop_cols( $default ) {
		return 3;
	}

	/**
	 * Set a flag that tells the plugin that woocommerce pages were created from their tool.
	 */
	public function set_update_woo_width_flag() {

		if ( ! isset( $_GET['page'] ) ) {
			return;
		}

		$current_page = sanitize_text_field( wp_unslash( $_GET['page'] ) );
		if ( 'wc-status' !== $current_page && 'wc-setup' !== $current_page ) {
			return;
		}

		if ( $current_page === 'wc-status' && ( ! isset( $_GET['action'] ) || ! isset( $_GET['_wpnonce'] ) || 'install_pages' !== sanitize_text_field( wp_unslash( $_GET['action'] ) ) ) ) {
			return;
		}

		if ( $current_page === 'wc-setup' && ( ! isset( $_GET['step'] ) || 'payment' !== sanitize_text_field( wp_unslash( $_GET['step'] ) ) ) ) {
			return;
		}

		update_option( 'neve_update_woo_width', 'yes' );
		set_transient( 'woocommerce_shop_page_id', 'executed', 10 * MINUTE_IN_SECONDS );
	}

	/**
	 * Update WooCommerce pages after the pages were created from their tool,
	 */
	public function update_woo_width() {
		if ( get_option( 'neve_update_woo_width' ) !== 'yes' ) {
			return;
		}

		$cart_id     = get_option( 'woocommerce_cart_page_id' );
		$checkout_id = get_option( 'woocommerce_checkout_page_id' );
		$my_account  = get_option( 'woocommerce_myaccount_page_id' );
		$pages       = array( $cart_id, $checkout_id, $my_account );
		foreach ( $pages as $page_id ) {
			if ( empty( $page_id ) ) {
				continue;
			}
			update_post_meta( $page_id, 'neve_meta_sidebar', 'full-width' );
			update_post_meta( $page_id, 'neve_meta_enable_content_width', 'on' );
			update_post_meta( $page_id, 'neve_meta_content_width', 100 );
		}
		update_option( 'neve_update_woo_width', false );
	}

	/**
	 * Change breadcrumb delimiter.
	 *
	 * @param array $default breadcrumbs defaults.
	 *
	 * @return mixed
	 */
	public function change_breadcrumbs_delimiter( $default ) {
		$default['delimiter'] = '<span class="nv-breadcrumb-delimiter">\</span>';

		return $default;
	}

	/**
	 * Change the default sidebar position for the product page.
	 *
	 * @param string $input - the value of the customizer control.
	 *
	 * @return string
	 */
	public function product_page_sidebar_default_position( $input ) {

		if ( is_product() ) {
			return get_theme_mod( 'neve_single_product_sidebar_layout', 'full-width' );
		}

		return $input;
	}

	/**
	 * Change functions hooked into woocommerce header.
	 */
	private function edit_woocommerce_header() {
		remove_action( 'woocommerce_before_main_content', 'woocommerce_breadcrumb', 20 );
		remove_action( 'woocommerce_before_shop_loop', 'woocommerce_result_count', 20 );
		remove_action( 'woocommerce_before_shop_loop', 'woocommerce_catalog_ordering', 30 );
		add_action( 'nv_woo_header_bits', 'woocommerce_catalog_ordering', 30 );
		add_filter( 'woocommerce_show_page_title', '__return_false' );
		add_action( 'neve_before_shop_loop_content', array( $this, 'add_header_bits' ), 0 );

		// Change breadcrumbs.
		add_filter( 'woocommerce_breadcrumb_defaults', array( $this, 'change_breadcrumbs_delimiter' ) );

	}

	/**
	 * Add header for woocommerce pages.
	 */
	public function add_header_bits() {
		if ( ! is_shop() && ! is_product() && ! is_product_category() && ! is_product_taxonomy() && ! is_product_tag() ) {
			return;
		}

		echo '<div class="nv-bc-count-wrap">';
		do_action( 'neve_bc_count' );
		echo '</div>';

		if ( is_product() ) {
			return;
		}

		echo '<div class="nv-woo-filters">';
		$this->sidebar_toggle();
		do_action( 'nv_woo_header_bits' );
		echo '</div>';
	}

	/**
	 * Handle left shop sidebar.
	 */
	public function shop_sidebar_left() {
		$this->sidebar_manager->sidebar( 'shop', 'left' );
	}

	/**
	 * Handle right shop sidebar.
	 */
	public function shop_sidebar_right() {
		$this->sidebar_manager->sidebar( 'shop', 'right' );
	}

	/**
	 * Close div.
	 */
	public function close_div() {
		echo '</div>';
	}

	/**
	 * Wrap main content start.
	 */
	public function wrap_main_content_start() {
		$before_shop_classes = apply_filters( 'neve_before_shop_classes', 'nv-index-posts nv-shop col' );
		echo '<div class="' . esc_attr( $before_shop_classes ) . '">';
		do_action( 'neve_before_shop_loop_content' );
	}

	/**
	 * Wrap start of woocommerce pages.
	 */
	public function wrap_pages_start() {
		if ( ! is_woocommerce() ) {
			return;
		}
		echo '<div class="' . esc_attr( apply_filters( 'neve_container_class_filter', 'container' ) ) . ' shop-container">';
		echo '<div class="row">';
	}

	/**
	 * Wrap end of page.
	 */
	public function wrap_pages_end() {
		if ( ! is_woocommerce() ) {
			return;
		}
		$this->close_div();
		$this->close_div();
	}

	/**
	 * Render sidebar toggle for responsive view.
	 */
	public function sidebar_toggle() {

		if ( ! $this->should_render_sidebar_toggle() ) {
			return;
		}

		$button_attrs = apply_filters( 'neve_woocommerce_sidebar_filter_btn_data_attrs', '' );

		echo '<a href="#" class="nv-sidebar-toggle" ' . wp_kses_post( $button_attrs ) . '>';
		echo '<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M25 21.6667V1.66667C25 0.75 24.25 0 23.3333 0H1.66667C0.75 0 0 0.75 0 1.66667V21.6667C0 22.5833 0.75 23.3333 1.66667 23.3333H23.3333C24.25 23.3333 25 22.5833 25 21.6667ZM8.33333 13.3333H10C10.9167 13.3333 11.6667 14.0833 11.6667 15C11.6667 15.9167 10.9167 16.6667 10 16.6667H8.33333V19.1667C8.33333 19.6333 7.96667 20 7.5 20C7.03333 20 6.66667 19.6333 6.66667 19.1667V16.6667H5C4.08333 16.6667 3.33333 15.9167 3.33333 15C3.33333 14.0833 4.08333 13.3333 5 13.3333H6.66667V4.16667C6.66667 3.7 7.03333 3.33333 7.5 3.33333C7.96667 3.33333 8.33333 3.7 8.33333 4.16667V13.3333ZM15 10H16.6667V19.1667C16.6667 19.6333 17.0333 20 17.5 20C17.9667 20 18.3333 19.6333 18.3333 19.1667V10H20C20.9167 10 21.6667 9.25 21.6667 8.33333C21.6667 7.41667 20.9167 6.66667 20 6.66667H18.3333V4.16667C18.3333 3.7 17.9667 3.33333 17.5 3.33333C17.0333 3.33333 16.6667 3.7 16.6667 4.16667V6.66667H15C14.0833 6.66667 13.3333 7.41667 13.3333 8.33333C13.3333 9.25 14.0833 10 15 10Z" fill="currentColor"/></svg>';
		echo '</a>';
	}

	/**
	 * Add inline selectors for woocommerce.
	 */
	private function add_inline_selectors() {

		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_PRIMARY_NORMAL,
			array(
				$this,
				'add_primary_btns_normal',
			),
			10,
			1
		);
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_PRIMARY_HOVER,
			array(
				$this,
				'add_primary_btns_hover',
			),
			10,
			1
		);
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_PRIMARY_PADDING,
			array(
				$this,
				'add_primary_btns_padding',
			),
			10,
			1
		);


		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_SECONDARY_NORMAL,
			array(
				$this,
				'add_secondary_btns_normal',
			),
			10,
			1
		);
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_SECONDARY_HOVER,
			array(
				$this,
				'add_secondary_btns_hover',
			),
			10,
			1
		);
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_BTN_SECONDARY_PADDING,
			array(
				$this,
				'add_secondary_btns_padding',
			),
			10,
			1
		);

		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_FORM_INPUTS,
			array(
				$this,
				'add_inputs_selectors',
			),
			10,
			1
		);

		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_FORM_INPUTS_WITH_SPACING,
			array(
				$this,
				'add_inputs_spacing_selectors',
			),
			10,
			1
		);


		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_FORM_INPUTS_LABELS,
			array(
				$this,
				'add_labels_selectors',
			),
			10,
			1
		);

		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_FORM_SEARCH_INPUTS,
			array(
				$this,
				'add_search_inputs_selector',
			),
			10,
			1
		);

		add_filter( 'neve_body_font_family_selectors', array( $this, 'add_font_families' ) );
		add_filter( 'neve_headings_typeface_selectors', array( $this, 'add_typeface_selectors' ) );
	}

	/**
	 * Add checkout labels to style.
	 *
	 * @param string $selectors css selectors for labels.
	 *
	 * @return string
	 */
	public function add_inputs_selectors( $selectors ) {
		return $selectors . ',
		.woocommerce-cart table.cart td.actions .coupon .input-text,
		.woocommerce-page .select2-container--default .select2-selection--single,
		.woocommerce-page .woocommerce form .form-row input.input-text,
		.woocommerce-page .woocommerce form .form-row textarea,
		input.wc-block-product-search__field';
	}

	/**
	 * Add additional inputs spacing.
	 *
	 * @param string $selectors css selectors for inputs that need spacing.
	 *
	 * @return string
	 */
	public function add_inputs_spacing_selectors( $selectors ) {
		return $selectors . ', .woocommerce-page .select2';
	}

	/**
	 * Add checkout labels to style.
	 *
	 * @param string $selectors css selectors for labels.
	 *
	 * @return string
	 */
	public function add_labels_selectors( $selectors ) {
		return ( $selectors . ', .woocommerce form .form-row label' );
	}

	/**
	 * Add product search input selector.
	 *
	 * @param string $selectors css selectors for labels.
	 *
	 * @return string
	 */
	public function add_search_inputs_selector( $selectors ) {
		return ( $selectors . ', form.woocommerce-product-search input[type="search"]' );
	}

	/**
	 * Add primary btn selectors.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_primary_btns_normal( $selectors ) {
		return ( $selectors . $this->primary_buttons_selectors['default'] );
	}

	/**
	 * Add primary btn selectors.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_primary_btns_hover( $selectors ) {
		return ( $selectors . $this->primary_buttons_selectors['hover'] );
	}

	/**
	 * Add primary btn selectors for padding.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_primary_btns_padding( $selectors ) {
		return ( $selectors . ',.woocommerce a.button,
			.woocommerce .button,
			.woocommerce a.button.loading,
			.woocommerce a.button.alt,
			.woocommerce a.button.button-primary,
			.woocommerce button.button:disabled,
			.woocommerce button.button:disabled[disabled],
			.woocommerce a.button.add_to_cart,
			.woocommerce a.product_type_grouped,
			.woocommerce a.product_type_external,
			.woocommerce a.product_type_variable,
			.woocommerce button.button.alt.single_add_to_cart_button.disabled,
			.woocommerce button.button.alt.single_add_to_cart_button,
			.woocommerce .actions > button[type=submit],
			.woocommerce button#place_order,
			.woocommerce .return-to-shop > .button,
			.woocommerce .button.woocommerce-form-login__submit' );
	}

	/**
	 * Add secondary btn selectors.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_secondary_btns_normal( $selectors ) {

		return ( $selectors . $this->secondary_buttons_selectors['default'] );

	}

	/**
	 * Add secondary btn selectors.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_secondary_btns_hover( $selectors ) {

		return ( $selectors . $this->secondary_buttons_selectors['hover'] );

	}

	/**
	 * Add secondary btn selectors for padding.
	 *
	 * @param string $selectors Current CSS selectors.
	 *
	 * @return string
	 */
	public function add_secondary_btns_padding( $selectors ) {
		return ( $selectors . $this->secondary_buttons_selectors['default'] . ',.woocommerce div.sp-quick-view-product.top' );
	}

	/**
	 * Add selectors for the headings typeface styles.
	 *
	 * @param array $array the headings selectors.
	 *
	 * @return array
	 */
	public function add_typeface_selectors( $array ) {
		$array['h1'] = $array['h1'] . ', .woocommerce.single .product_title';
		$array['h3'] = $array['h3'] . ', .woocommerce-billing-fields > h3, .woocommerce-shipping-fields > h3';

		return $array;
	}

	/**
	 * Add font family selectors.
	 *
	 * @param string $selectors css selectors to apply body font family to.
	 *
	 * @return string
	 */
	public function add_font_families( $selectors ) {
		$selectors .= ',.cart_totals > h2, .cross-sells > h2, #order_review_heading';

		return $selectors;
	}


	/**
	 * Adapt the meta-box so it works on the shop page.
	 *
	 * @param string $post_id post id.
	 *
	 * @return string
	 */
	public function adapt_meta_for_shop_page( $post_id ) {
		if ( ! is_shop() ) {
			return $post_id;
		}

		return get_option( 'woocommerce_shop_page_id' );
	}

	/**
	 * Check if we should render the mobile sidebar toggle.
	 *
	 * @return bool
	 */
	private function should_render_sidebar_toggle() {
		if ( ! is_active_sidebar( 'shop-sidebar' ) ) {
			return false;
		}

		$advanced_options = get_theme_mod( 'neve_advanced_layout_options', true );

		$mod = 'neve_default_sidebar_layout';
		if ( $advanced_options === true ) {
			$mod = 'neve_shop_archive_sidebar_layout';
			if ( is_product() ) {
				$mod = 'neve_single_product_sidebar_layout';
			}
		}

		$default   = $this->sidebar_layout_alignment_default( $mod );
		$theme_mod = apply_filters( 'neve_sidebar_position', get_theme_mod( $mod, $default ) );
		if ( $theme_mod !== 'right' && $theme_mod !== 'left' ) {
			return false;
		}

		return true;
	}

	/**
	 * Does what it says.
	 */
	private function move_checkout_coupon() {
		/**
		 * Checkout page
		 *
		 * @see move_coupon()
		 * @see clear_coupon()
		 */
		add_action( 'woocommerce_before_checkout_form', array( $this, 'move_coupon' ) );
		add_action( 'woocommerce_before_checkout_billing_form', array( $this, 'clear_coupon' ) );
	}

	/**
	 * Checkout page
	 * Move the coupon field and message info after the order table.
	 */
	public function move_coupon() {
		wc_enqueue_js( '$( $( ".woocommerce-checkout div.woocommerce-info, .checkout_coupon, .woocommerce-form-login" ).detach() ).appendTo( "#neve-checkout-coupon" );' );
	}

	/**
	 * Checkout page
	 * Add the id neve-checkout-coupon to be able to Move the coupon field and message info after the order table.
	 */
	public function clear_coupon() {
		echo '<div id="neve-checkout-coupon"></div><div style="clear:both"></div>';
	}

	/**
	 * Update the counter of products in cart.
	 *
	 * @param array $fragments WooFragments.
	 *
	 * @return mixed
	 */
	public function cart_link_fragment( $fragments ) {
		$fragments['.cart-count'] = '<span class="cart-count">' . WC()->cart->get_cart_contents_count() . '</span>';

		$cart_label = get_theme_mod( 'header_cart_icon_' . CartIcon::CART_LABEL );
		if ( ! empty( $cart_label ) ) {
			$cart_label                    = Magic_Tags::get_instance()->do_magic_tags( $cart_label );
			$fragments['.cart-icon-label'] = '<span class="cart-icon-label inherit-ff">' . $cart_label . '</span>';
		}

		return $fragments;
	}

	/**
	 * Render woocommerce breadcrumbs.
	 */
	public function render_breadcrumbs() {

		/**
		 * Filters the visibility of breadcrumbs.
		 *
		 * @param bool $status Whether the breadcrumbs should be displayed or not.
		 * @since 3.1
		 */
		$should_display_breadcrumbs = apply_filters( 'neve_breadcrumbs_toggle', true );
		if ( ! $should_display_breadcrumbs ) {
			return;
		}

		/**
		 * Filters the condition that decides if breadcrumbs should be loaded from WooCommerce or 3rd parties.
		 *
		 * @param bool $status Whether the breadcrumbs should be loaded from 3rd parties or not.
		 * @since 3.1
		 */
		$enable_3rd_party_breadcrumbs = apply_filters( 'neve_woo_3rd_party_breadcrumbs', false );
		if ( ! $enable_3rd_party_breadcrumbs ) {
			woocommerce_breadcrumb();
			return;
		}

		$is_seo_breadcrumb = Breadcrumbs::maybe_render_seo_breadcrumbs( 'small', true );
		if ( ! $is_seo_breadcrumb ) {
			woocommerce_breadcrumb();
		}
	}

	/**
	 * Add form buttons selectors.
	 *
	 * @param string $selector css selector.
	 *
	 * @return string
	 */
	public function add_buttons_selectors( $selector ) {
		return $selector . ',.woocommerce #review_form #respond input#submit,
		.woocommerce-cart .woocommerce .wc-proceed-to-checkout > a.button.checkout-button,
		.woocommerce-checkout #payment .place-order button#place_order,
		.woocommerce-account.woocommerce-edit-account .woocommerce .woocommerce-MyAccount-content p > button[type="submit"][name="save_account_details"].woocommerce-Button.button,
		.wc-block-product-search .wc-block-product-search__button:not(:disabled):not([aria-disabled=true])';
	}

	/**
	 * Add form buttons padding selectors.
	 *
	 * @param string $selector css selector.
	 *
	 * @return string
	 */
	public function add_buttons_padding_selectors( $selector ) {
		return $selector . ',.woocommerce #review_form #respond input#submit';
	}

	/**
	 * Add form buttons hover selectors.
	 *
	 * @param string $selector css selector.
	 *
	 * @return string
	 */
	public function add_buttons_hover_selectors( $selector ) {
		return $selector . ',.woocommerce #review_form #respond input#submit:hover,
		 .woocommerce a.button.checkout-button:hover,
		 .woocommerce button#place_order:hover,
		 .woocommerce-account.woocommerce-edit-account .woocommerce .woocommerce-MyAccount-content p > button[type="submit"][name="save_account_details"].woocommerce-Button.button:hover,
		 .wc-block-product-search .wc-block-product-search__button:not(:disabled):not([aria-disabled=true]):hover';
	}

	/**
	 * Setup Form Buttons Type
	 */
	public function setup_form_buttons() {
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_FORM_BUTTON,
			function ( $selectors ) {
				return $selectors . '
				,#review_form #respond input#submit,
				.woocommerce-cart .wc-proceed-to-checkout a.checkout-button,
				.woocommerce-checkout #payment .place-order button#place_order,
				.woocommerce-account .woocommerce [type="submit"]';
			},
			10,
			1
		);
		add_filter(
			'neve_selectors_' . Config::CSS_SELECTOR_FORM_BUTTON_HOVER,
			function ( $selectors ) {
				return $selectors . '
				,#review_form #respond input#submit:hover,
				.woocommerce-cart .wc-proceed-to-checkout a.checkout-button:hover,
				.woocommerce-checkout #payment .place-order button#place_order:hover,
				.woocommerce-account .woocommerce [type="submit"]:hover';
			},
			10,
			1
		);
	}
}
PK      \      compatibility/beaver.phpnu W+A        <?php
/**
 * Add compatibility with Beaver Themer header and footer.
 * Check https://kb.wpbeaverbuilder.com/article/389-add-header-footer-and-parts-support-to-your-theme for the
 * compatibility guid.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;

/**
 * Class Bever
 *
 * @package Neve\Compatibility
 */
class Beaver extends Page_Builder_Base {

	/**
	 * Init function.
	 */
	public function init() {
		if ( defined( 'FL_BUILDER_VERSION' ) ) {
			add_filter( 'fl_builder_color_presets', array( $this, 'global_color_presets' ) );
		}

		if ( defined( 'FL_THEME_BUILDER_VERSION' ) ) {
			add_action( 'wp', array( $this, 'add_theme_builder_hooks' ) );
			add_filter( 'fl_theme_builder_part_hooks', array( $this, 'register_part_hooks' ) );
		}
	}

	/**
	 * Check if it page was edited with page builder.
	 *
	 * @param int $pid post id.
	 *
	 * @return bool
	 */
	protected function is_edited_with_builder( $pid ) {
		if ( class_exists( '\FLBuilderModel', false ) ) {
			return \FLBuilderModel::is_builder_enabled( $pid );
		}

		return false;
	}

	/**
	 * Load Beaver compatibility style.
	 */
	public function load_style() {
		wp_add_inline_style( 'neve-style', '.fl-builder.bbhf-transparent-header:not(.bhf-sticky-header) #nv-beaver-header .fl-row-content-wrap{background-color:transparent;border:none;transition:background-color .3s ease-in-out}.fl-builder.bbhf-transparent-header .bhf-fixed-header:not(.bhf-fixed) .fl-row-content-wrap{background-color:transparent;border:none;transition:background-color .3s ease-in-out}.fl-builder.bbhf-transparent-header #nv-beaver-header{position:absolute;z-index:10;width:100%}' );
	}

	/**
	 * Add support for elementor theme locations.
	 */
	public function add_theme_builder_hooks() {

		if ( ! class_exists( '\FLThemeBuilderLayoutData', false ) ) {
			return;
		}
		add_action( 'wp_enqueue_scripts', [ $this, 'load_style' ] );
		// Get the header ID.
		$header_ids = \FLThemeBuilderLayoutData::get_current_page_header_ids();

		// If we have a header, remove the theme header and hook in Theme Builder's.
		if ( ! empty( $header_ids ) ) {
			remove_all_actions( 'neve_do_top_bar' );
			remove_all_actions( 'neve_do_header' );
			add_action( 'neve_do_header', 'FLThemeBuilderLayoutRenderer::render_header' );
		}

		// Get the footer ID.
		$footer_ids = \FLThemeBuilderLayoutData::get_current_page_footer_ids();

		// If we have a footer, remove the theme footer and hook in Theme Builder's.
		if ( ! empty( $footer_ids ) ) {
			remove_all_actions( 'neve_do_footer' );
			add_action( 'neve_do_footer', 'FLThemeBuilderLayoutRenderer::render_footer' );
		}

	}

	/**
	 * Beautify hook names.
	 *
	 * @param string $hook Hook name.
	 *
	 * @return string
	 */
	private function beautify_hook( $hook ) {
		$hook_label = str_replace( '_', ' ', $hook );
		$hook_label = str_replace( 'neve', ' ', $hook_label );
		$hook_label = str_replace( 'woocommerce', ' ', $hook_label );
		$hook_label = ucwords( $hook_label );
		return $hook_label;
	}

	/**
	 * Mapping function to move from neve_hooks format to the format required by Beaver Builder.
	 *
	 * @param string $location Current location, the key of neve_hooks array.
	 * @param array  $hooks Hooks from that location.
	 *
	 * @return array
	 */
	private function hook_to_part( $location, $hooks ) {
		$part = array(
			'label' => ucfirst( $location ),
		);
		foreach ( $hooks as $hook ) {
			$part['hooks'][ $hook ] = $this->beautify_hook( $hook );
		}
		return $part;
	}

	/**
	 * Register part hooks for Beaver Themer.
	 *
	 * @return array
	 */
	public function register_part_hooks() {
		$hooks = neve_hooks();
		return array_map( array( $this, 'hook_to_part' ), array_keys( $hooks ), $hooks );
	}

	/**
	 * Adds global colors from neve to Beaver Builder color presets.
	 *
	 * @param array $colors Color presets.
	 *
	 * @return array
	 */
	public function global_color_presets( $colors ) {

		$global_colors = get_theme_mod( 'neve_global_colors', neve_get_global_colors_default( true ) );

		if ( empty( $global_colors ) ) {
			return $colors;
		}

		if ( ! isset( $global_colors['activePalette'] ) ) {
			return $colors;
		}

		$active = $global_colors['activePalette'];

		if ( ! isset( $global_colors['palettes'][ $active ] ) ) {
			return $colors;
		}

		$palette = $global_colors['palettes'][ $active ];

		if ( ! isset( $palette['colors'] ) ) {
			return $colors;
		}

		$palette_colors = array_values( $palette['colors'] );

		$global_custom_colors = Mods::get( Config::MODS_GLOBAL_CUSTOM_COLORS, [] );

		foreach ( $global_custom_colors as $args ) {
			$palette_colors[] = $args['val'];
		}

		foreach ( $palette_colors as $color ) {
			if ( ! array_search( $color, $colors, true ) ) {
				$colors[] = str_replace( '#', '', $color );
			}
		}

		return array_values( array_unique( $colors ) );
	}
}
PK      \K    5  compatibility/block-patterns/gallery-grid-buttons.phpnu W+A        <?php
/**
 * Gallery grid with buttons pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Gallery grid with buttons', 'neve' ),
	'content'    => '<!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:gallery {"ids":[],"columns":4} -->
<figure class="wp-block-gallery columns-4 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-11.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-11.jpg" data-link="#" /></figure></li><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-10.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-10.jpg" data-link="#" /></figure></li><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-6.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-6.jpg" data-link="#" /></figure></li><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-22.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-22.jpg" data-link="#" /></figure></li><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-21.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-21.jpg" data-link="#" /></figure></li><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-19.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-19.jpg" data-link="#" /></figure></li><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-20.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-20.jpg" data-link="#" ></figure></li></ul></figure>
<!-- /wp:gallery -->

<!-- wp:buttons {"align":"center", "layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons aligncenter"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link">This is Primary Button</a></div>
<!-- /wp:button -->

<!-- wp:button {"className":"is-style-secondary"} -->
<div class="wp-block-button is-style-secondary"><a class="wp-block-button__link">This is Secondary</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons --></div></div>
<!-- /wp:group -->',
	'categories' => array( 'gallery' ),
);
PK      \E=5	  5	  6  compatibility/block-patterns/gallery-title-buttons.phpnu W+A        <?php
/**
 * Gallery with title and button.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Gallery with title and button', 'neve' ),
	'content'    => '<!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column {"width":66.66} -->
<div class="wp-block-column" style="flex-basis:66.66%"><!-- wp:heading -->
<h2>Gallery with title and button</h2>
<!-- /wp:heading -->
</div>
<!-- /wp:column -->

<!-- wp:column {"width":33.33} -->
<div class="wp-block-column" style="flex-basis:33.33%"><!-- wp:buttons {"align":"right"} -->
<div class="wp-block-buttons alignright"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link">This is primary button</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->
</div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:gallery {"ids":[],"columns":4} -->
<figure class="wp-block-gallery columns-4 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-22.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-22.jpg" data-link="#"/></figure></li><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-20.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-20.jpg" data-link="#"/></figure></li><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-21.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-21.jpg" data-link="#"/></figure></li><li class="blocks-gallery-item"><figure><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-19.jpg" alt="" data-full-url="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-19.jpg" data-link="#"/></figure></li></ul></figure>
<!-- /wp:gallery --></div></div>
<!-- /wp:group -->',
	'categories' => array( 'gallery' ),
);
PK      \$B    D  compatibility/block-patterns/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \_Z    C  compatibility/block-patterns/three-columns-images-texts-content.phpnu W+A        <?php
/**
 * Three columns with images, content and buttons pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Three columns with images, content and buttons', 'neve' ),
	'content'    => '<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-10.jpg" alt="" /></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>Heading three</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. </p>
<!-- /wp:paragraph -->

<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link">LEARN MORE</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-6.jpg" alt=""/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>Heading three</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The passage is attributed to an unknown typesetter in the 15th century who is thought to have scrambled parts.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link">LEARN MORE</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-11.jpg" alt="" /></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>Heading three</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link">LEARN MORE</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->',
	'categories' => array( 'columns' ),
);
PK      \}.    6  compatibility/block-patterns/two-columns-with-text.phpnu W+A        <?php
/**
 * Two columns with text pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Two columns with text', 'neve' ),
	'content'    => '<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column"><!-- wp:heading -->
<h2>This is a heading on the left, more text on the right!</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong><a href="#">Learn More</a></strong></p>
<!-- /wp:paragraph --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:heading {"level":3} -->
<h3>This is Heading 3</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>This is Heading 3</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->',
	'categories' => array( 'text' ),
);
PK      \S5m    =  compatibility/block-patterns/dark-header-centered-content.phpnu W+A        <?php
/**
 * Dark header with centered content pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Dark header with centered content', 'neve' ),
	'content'    => '<!-- wp:cover {"url":"' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-8.jpg","dimRatio":26,"focalPoint":{"x":"0.50","y":"0.50"},"minHeight":700,"align":"full"} -->
<div class="wp-block-cover alignfull has-background-dim-30 has-background-dim" style="background-image:url(' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-8.jpg);background-position:50% 50%;min-height:700px"><div class="wp-block-cover__inner-container"><!-- wp:heading {"align":"center","level":1,"textColor":"white"} -->
<h1 class="has-text-align-center has-white-color has-text-color">Welcome to Neve.This is a hero section. </h1>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"white","style":{"typography":{"fontSize":23} } } -->
<p class="has-text-align-center has-white-color has-text-color" style="font-size:23px">This is a description text of the cover</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:buttons {"align":"center", "layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons aligncenter"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link">This is Primary Button</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->',
	'categories' => array( 'header' ),
);
PK      \P    :  compatibility/block-patterns/three-columns-images-text.phpnu W+A        <?php
/**
 * Three columns with images and text pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Three columns with images and text', 'neve' ),
	'content'    => '<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-12.jpg" alt="" /></figure>
<!-- /wp:image --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {} -->
<figure class="wp-block-image "><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-17.jpg" alt="" /></figure>
<!-- /wp:image --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center"} -->
<div class="wp-block-column is-vertically-aligned-center"><!-- wp:heading {"level":3} -->
<h3>The quick brown fox jumps over the lazy dog"</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->',
	'categories' => array( 'columns' ),
);
PK      \)<    7  compatibility/block-patterns/two-columns-image-text.phpnu W+A        <?php
/**
 * Two Columns with image and text pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Two columns with image and text', 'neve' ),
	'content'    => '<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-6.jpg" alt="" /></figure>
<!-- /wp:image --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center"} -->
<div class="wp-block-column is-vertically-aligned-center"><!-- wp:heading -->
<h2>The quick brown fox jumps over the lazy dog"</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter in the 15th century who is thought to have scrambled parts of Cicero\'s De Finibus Bonorum et Malorum for use in a type specimen book.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link">This is Primary Button</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->',
	'categories' => array( 'columns' ),
);
PK      \-A  A  B  compatibility/block-patterns/light-header-left-aligned-content.phpnu W+A        <?php
/**
 * Light header with left-aligned content pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Light header with left-aligned content', 'neve' ),
	'content'    => '<!-- wp:cover {"url":"' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-18.jpg","dimRatio":80,"overlayColor":"nv-light-bg","focalPoint":{"x":"0.16","y":"0.91"},"minHeight":500,"align":"full"} -->
<div class="wp-block-cover alignfull has-background-dim-80 has-nv-light-bg-background-color has-background-dim" style="min-height:500px"><img class="wp-block-cover__image-background" alt="" src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-18.jpg" style="object-position:16% 91%" data-object-fit="cover" data-object-position="16% 91%"/><div class="wp-block-cover__inner-container"><!-- wp:columns {"align":"wide"} -->
<div class="wp-block-columns alignwide"><!-- wp:column {"verticalAlignment":"center","width":"66.66%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:66.66%"><!-- wp:heading {"textAlign":"left","level":1,"textColor":"neve-text-color"} -->
<h1 class="has-text-align-left has-neve-text-color-color has-text-color">A Hero section over a light background</h1>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"left","textColor":"neve-text-color"} -->
<p class="has-text-align-left has-neve-text-color-color has-text-color">The quick brown fox jumps over the lazy dog" is an English-language pangram</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"contentJustification":"left"} -->
<div class="wp-block-buttons is-content-justification-left"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link">This is Primary Button</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons --></div>
<!-- /wp:column -->

<!-- wp:column {"width":"33.33%"} -->
<div class="wp-block-column" style="flex-basis:33.33%"></div>
<!-- /wp:column --></div>
<!-- /wp:columns --></div></div>
<!-- /wp:cover -->',
	'categories' => array( 'header' ),
);
PK      \$c]    =  compatibility/block-patterns/two-columns-centered-content.phpnu W+A        <?php
/**
 * Two columns with centered content pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Two columns with centered content', 'neve' ),
	'content'    => '<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-16.jpg" alt=""/></figure>
<!-- /wp:image -->

<!-- wp:heading {"align":"center","level":3} -->
<h3 class="has-text-align-center">Heading three</h3>
<!-- /wp:heading -->

<!-- wp:separator {"color":"neve-button-color","className":"is-style-default"} -->
<hr class="wp-block-separator has-text-color has-background has-neve-button-color-background-color has-neve-button-color-color is-style-default"/>
<!-- /wp:separator -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. </p>
<!-- /wp:paragraph --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-15.jpg" alt="" /></figure>
<!-- /wp:image -->

<!-- wp:heading {"align":"center","level":3} -->
<h3 class="has-text-align-center">Heading three</h3>
<!-- /wp:heading -->

<!-- wp:separator {"color":"neve-button-color","className":"is-style-default"} -->
<hr class="wp-block-separator has-text-color has-background has-neve-button-color-background-color has-neve-button-color-color is-style-default"/>
<!-- /wp:separator -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">The passage is attributed to an unknown typesetter in the 15th century who is thought to have scrambled parts.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->',
	'categories' => array( 'columns' ),
);
PK      \.»    5  compatibility/block-patterns/testimonials-columns.phpnu W+A        <?php
/**
 * Testimonial columns pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Testimonial columns', 'neve' ),
	'content'    => '<!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:heading {"align":"center"} -->
<h2 class="has-text-align-center">Testimonials</h2>
<!-- /wp:heading -->

<!-- wp:separator {"color":"neve-button-color","className":"is-style-default"} -->
<hr class="wp-block-separator has-text-color has-background has-neve-button-color-background-color has-neve-button-color-color is-style-default"/>
<!-- /wp:separator -->

<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {"align":"center","width":80,"height":80} -->
<div class="wp-block-image"><figure class="aligncenter is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/otter-blocks-img-03.png" alt="" width="80" height="80"/></figure></div>
<!-- /wp:image -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center"><strong>Jason Stobbard</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center"><em>"...Absolutely fantastic work, many thanks for the perfect collaboration so far, very much appreciated!..."</em></p>
<!-- /wp:paragraph --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {"align":"center","width":80,"height":80} -->
<div class="wp-block-image"><figure class="aligncenter is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/otter-blocks-img-02.png" alt=""  width="80" height="80"/></figure></div>
<!-- /wp:image -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center"><strong>Jane Austin</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center"><em>"... I love this team! They did fantastic work, many thanks for the perfect collaboration so far, very much appreciated!..."</em></p>
<!-- /wp:paragraph --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {"align":"center","width":80,"height":80} -->
<div class="wp-block-image"><figure class="aligncenter  is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/otter-blocks-img-03.png" alt="" width="80" height="80"/></figure></div>
<!-- /wp:image -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center"><strong>Jason Stobbard</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center"><em>"...Absolutely fantastic work, many thanks for the perfect collaboration so far, very much appreciated!..."</em></p>
<!-- /wp:paragraph --></div>
<!-- /wp:column --></div>
<!-- /wp:columns --></div></div>
<!-- /wp:group -->',
	'categories' => array( 'columns' ),
);
PK      \ܘm|<  <  :  compatibility/block-patterns/four-columns-team-members.phpnu W+A        <?php
/**
 * Four columns with team members pattern.
 *
 * @package Neve
 */

return array(
	'title'      => __( 'Four columns with team members', 'neve' ),
	'content'    => '<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {"align":"center","className":"is-style-default"} -->
<div class="wp-block-image is-style-default"><figure class="aligncenter"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-3.jpg" alt="" /></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"textAlign":"center","level":3} -->
<h3 class="has-text-align-center">Tim Jerris</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Founder</p>
<!-- /wp:paragraph -->

<!-- wp:social-links {"align":"center"} -->
<ul class="wp-block-social-links aligncenter"><!-- wp:social-link {"url":"#","service":"twitter"} /-->

<!-- wp:social-link {"url":"#","service":"facebook"} /-->

<!-- wp:social-link {"url":"#","service":"linkedin"} /--></ul>
<!-- /wp:social-links -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {"align":"center","className":"is-style-default"} -->
<div class="wp-block-image is-style-default"><figure class="aligncenter"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-4.jpg" alt="" /></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"textAlign":"center","level":3} -->
<h3 class="has-text-align-center">Erica Browser</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Client Service</p>
<!-- /wp:paragraph -->

<!-- wp:social-links {"align":"center"} -->
<ul class="wp-block-social-links aligncenter"><!-- wp:social-link {"url":"#","service":"twitter"} /-->

<!-- wp:social-link {"url":"#","service":"facebook"} /-->

<!-- wp:social-link {"url":"#","service":"linkedin"} /--></ul>
<!-- /wp:social-links -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {"align":"center","className":"is-style-default"} -->
<div class="wp-block-image is-style-default"><figure class="aligncenter"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-2.jpg" alt="" /></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"textAlign":"center","level":3} -->
<h3 class="has-text-align-center">Jack Nolston</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">CFO</p>
<!-- /wp:paragraph -->

<!-- wp:social-links {"align":"center"} -->
<ul class="wp-block-social-links aligncenter"><!-- wp:social-link {"url":"#","service":"twitter"} /-->

<!-- wp:social-link {"url":"#","service":"facebook"} /-->

<!-- wp:social-link {"url":"#","service":"linkedin"} /--></ul>
<!-- /wp:social-links -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {"align":"center","className":"is-style-default"} -->
<div class="wp-block-image is-style-default"><figure class="aligncenter"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/patterns/neve-patterns-1.jpg" alt="" /></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"textAlign":"center","level":3} -->
<h3 class="has-text-align-center">Jane Austin</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Marketing</p>
<!-- /wp:paragraph -->

<!-- wp:social-links {"align":"center"} -->
<ul class="wp-block-social-links aligncenter"><!-- wp:social-link {"url":"#","service":"twitter"} /-->

<!-- wp:social-link {"url":"#","service":"facebook"} /-->

<!-- wp:social-link {"url":"#","service":"linkedin"} /--></ul>
<!-- /wp:social-links -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->',
	'categories' => array( 'columns' ),
);
PK      \X@  @    compatibility/elementor.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      05/09/2018
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

use Neve\Core\Dynamic_Css;
use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;

/**
 * Class Elementor
 *
 * @package Neve\Compatibility
 */
class Elementor extends Page_Builder_Base {
	/**
	 * Stores Elementor templates meta
	 *
	 * @var array
	 */
	const ELEMENTOR_TEMPLATE_TYPES = [
		'single_product'  => [
			'location'            => 'single',
			'condition_indicator' => 'product',
		],
		'product_archive' => [
			'location'            => 'archive',
			'condition_indicator' => 'product_archive',
		],
	];

	/**
	 * Stores if the current page is overriden by Elementor or not (checks by ::is_elementor_template method) according to the location.
	 *
	 * @var array
	 */
	private static $cache_cp_has_template = [];

	/**
	 * Stores Elementor Pro Conditions_Manager instance.
	 *
	 * @var false|\ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager
	 */
	private static $elementor_conditions_manager = false;

	/**
	 * Custom global colors theme mod value
	 *
	 * @var array|null
	 */
	private static $custom_global_colors = null;

	/**
	 * Init function.
	 */
	public function init() {
		if ( ! defined( 'ELEMENTOR_VERSION' ) ) {
			return;
		}

		self::$custom_global_colors = self::$custom_global_colors ?? Mods::get( Config::MODS_GLOBAL_CUSTOM_COLORS, [] );

		add_filter( 'neve_dynamic_style_output', array( $this, 'fix_links' ), 99, 2 );
		add_action( 'wp', array( $this, 'add_theme_builder_hooks' ) );
		add_action( 'elementor/editor/before_enqueue_scripts', array( $this, 'maybe_set_page_template' ), 1 );
		add_filter( 'rest_request_after_callbacks', [ $this, 'alter_global_colors_in_picker' ], 999, 3 );
		add_filter( 'rest_request_after_callbacks', [ $this, 'alter_global_colors_front_end' ], 999, 3 );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue' ), 100 );
		/**
		* Elementor - Neve Pro Compatibility
		* add_filter call for "neve_pro_run_wc_view" hook.
		*
		* The callback, suspenses some WooCommerce modifications (especially customizer support) by Neve Pro if an Elementor template is applied on the current page.
		* That gives full capability to Elementor and removes Neve Pro customizations.
		*/
		add_filter( 'neve_pro_run_wc_view', array( $this, 'suspend_woo_customizations' ), 10, 2 );
	}

	/**
	 * Enqueue Global Colors
	 */
	public function enqueue() {
		$colors = $this->get_current_palette_colors();
		$css    = ':root{';
		foreach ( $colors as $slug => $color ) {
			$css .= '--e-global-color-' . str_replace( '-', '', $slug ) . ':' . $color . ';';
		}
		$css .= '}';
		/**
		 * Filters the css with base vars for elementor colors.
		 *
		 * @param string $css Single post page components.
		 *
		 * @since 3.1.0
		 */
		$css = apply_filters( 'neve_elementor_colors', $css );
		$css = Dynamic_Css::minify_css( $css );
		wp_add_inline_style( 'neve-style', $css );
	}

	/**
	 * Filter rest responses to add Neve Palette Colors to pages using Elementor.
	 *
	 * @param \WP_REST_Response $response request response.
	 * @param array             $handler request handler.
	 * @param \WP_REST_Request  $request rest request.
	 * @return \WP_REST_Response
	 */
	public function alter_global_colors_front_end( $response, $handler, \WP_REST_Request $request ) {
		$route         = $request->get_route();
		$rest_to_slugs = [
			'nvprimaryaccent'   => 'nv-primary-accent',
			'nvsecondaryaccent' => 'nv-secondary-accent',
			'nvsitebg'          => 'nv-site-bg',
			'nvlightbg'         => 'nv-light-bg',
			'nvdarkbg'          => 'nv-dark-bg',
			'nvtextcolor'       => 'nv-text-color',
			'nvtextdarkbg'      => 'nv-text-dark-bg',
			'nvc1'              => 'nv-c-1',
			'nvc2'              => 'nv-c-2',
		];

		// introduce custom global colors
		foreach ( array_keys( self::$custom_global_colors ) as $slug ) {
			$rest_to_slugs[ str_replace( '-', '', $slug ) ] = $slug;
		}

		$rest_id = substr( $route, strrpos( $route, '/' ) + 1 );

		if ( ! in_array( $rest_id, array_keys( $rest_to_slugs ), true ) ) {
			return $response;
		}

		$colors   = $this->get_current_palette_colors();
		$response = new \WP_REST_Response(
			[
				'id'    => esc_attr( $rest_id ),
				'title' => $this->get_global_color_prefix() . esc_html( $rest_to_slugs[ $rest_id ] ),
				'value' => neve_sanitize_colors( $colors[ $rest_to_slugs[ $rest_id ] ] ),
			]
		);
		return $response;
	}

	/**
	 * Filter rest responses to add Neve Palette Colors to Elementor.
	 *
	 * @param \WP_REST_Response $response request response.
	 * @param array             $handler request handler.
	 * @param \WP_REST_Request  $request rest request.
	 * @return \WP_REST_Response
	 */
	public function alter_global_colors_in_picker( $response, $handler, \WP_REST_Request $request ) {
		$route = $request->get_route();

		if ( $route !== '/elementor/v1/globals' ) {
			return $response;
		}

		$label_map = [
			'nv-primary-accent'   => __( 'Primary Accent', 'neve' ),
			'nv-secondary-accent' => __( 'Secondary Accent', 'neve' ),
			'nv-site-bg'          => __( 'Site Background', 'neve' ),
			'nv-light-bg'         => __( 'Light Background', 'neve' ),
			'nv-dark-bg'          => __( 'Dark Background', 'neve' ),
			'nv-text-color'       => __( 'Text Color', 'neve' ),
			'nv-text-dark-bg'     => __( 'Text Dark Background', 'neve' ),
			'nv-c-1'              => __( 'Extra Color 1', 'neve' ),
			'nv-c-2'              => __( 'Extra Color 2', 'neve' ),
		];

		foreach ( self::$custom_global_colors as $slug => $args ) {
			$label_map[ $slug ] = $args['label'];
		}

		$colors = $this->get_current_palette_colors();
		$data   = $response->get_data();

		foreach ( $colors as $slug => $color_value ) {
			$no_hyphens                    = str_replace( '-', '', $slug );
			$data['colors'][ $no_hyphens ] = [
				'id'    => esc_attr( $no_hyphens ),
				'title' => $this->get_global_color_prefix() . esc_html( $label_map[ $slug ] ),
				'value' => neve_sanitize_colors( $color_value ),
			];
		}

		$response->set_data( $data );

		return $response;
	}

	/**
	 * Add support for elementor theme locations.
	 */
	public function add_theme_builder_hooks() {
		if ( ! class_exists( '\ElementorPro\Modules\ThemeBuilder\Module', false ) ) {
			return;
		}

		// Elementor locations compatibility. (This action fires by Elementor Pro)
		add_action( 'elementor/theme/register_locations', array( $this, 'register_theme_locations' ) );

		if ( ! function_exists( 'elementor_theme_do_location' ) ) {
			return;
		}

		// Override theme templates.
		add_action( 'neve_do_top_bar', array( $this, 'do_header' ), 0 );
		add_action( 'neve_do_header', array( $this, 'do_header' ), 0 );
		add_action( 'neve_do_footer', array( $this, 'do_footer' ), 0 );
		add_action( 'neve_do_404', array( $this, 'do_404' ), 0 );
		add_action( 'neve_do_single_post', array( $this, 'do_single_post' ), 0 );
		add_action( 'neve_do_single_page', array( $this, 'do_single_page' ), 0 );
		add_action( 'neve_page_header', array( $this, 'remove_header_on_page' ), 0 );
	}

	/**
	 * Register Theme Location for Elementor
	 * see https://developers.elementor.com/theme-locations-api/
	 *
	 * @param \ElementorPro\Modules\ThemeBuilder\Classes\Locations_Manager $manager Elementor object.
	 */
	public function register_theme_locations( $manager ) {
		$manager->register_all_core_location();
	}

	/**
	 * Remove actions for elementor header to act properly.
	 */
	public function do_header() {
		$did_location = elementor_theme_do_location( 'header' );
		if ( $did_location ) {
			remove_all_actions( 'neve_do_top_bar' );
			remove_all_actions( 'neve_do_header' );
		}
	}

	/**
	 * Remove actions for elementor footer to act properly.
	 */
	public function do_footer() {
		$did_location = elementor_theme_do_location( 'footer' );
		if ( $did_location ) {
			remove_all_actions( 'neve_do_footer' );
		}
	}

	/**
	 * Remove actions for elementor 404 to act properly.
	 */
	public function do_404() {
		if ( ! is_404() ) {
			return;
		}
		$did_location = elementor_theme_do_location( 'single' );
		if ( $did_location ) {
			remove_all_actions( 'neve_do_404' );
		}
	}

	/**
	 * Remove actions for elementor single post to act properly.
	 */
	public function do_single_post() {
		$did_location = elementor_theme_do_location( 'single' );
		if ( $did_location ) {
			remove_all_actions( 'neve_do_single_post' );
		}
	}

	/**
	 * Remove actions for elementor single page to act properly.
	 */
	public function do_single_page() {
		$did_location = elementor_theme_do_location( 'single' );
		if ( $did_location ) {
			remove_all_actions( 'neve_do_single_page' );
		}
	}

	/**
	 * Remove title on single page.
	 */
	public function remove_header_on_page() {
		if ( ! is_singular( 'page' ) ) {
			return;
		}
		if ( elementor_theme_do_location( 'single' ) ) {
			remove_all_actions( 'neve_page_header' );
		}
	}

	/**
	 * Check if it page was edited with page builder.
	 *
	 * @param int $pid post id.
	 *
	 * @return bool
	 */
	protected function is_edited_with_builder( $pid ) {
		$post_meta = get_post_meta( $pid, '_elementor_edit_mode', true );
		if ( $post_meta === 'builder' ) {
			return true;
		}

		return false;
	}

	/**
	 * Fix the underline of links added by neve.
	 *
	 * @param string $css Current css.
	 * @param string $context Context.
	 *
	 * @return string
	 */
	public function fix_links( $css, $context = 'frontend' ) {
		if ( $context !== 'frontend' ) {
			return $css;
		}

		return $css . '.nv-content-wrap .elementor a:not(.button):not(.wp-block-file__button){
				text-decoration: none;
			}';
	}

	/**
	 * Get current palette colors.
	 *
	 * @return array
	 */
	private function get_current_palette_colors() {
		$customizer = get_theme_mod( 'neve_global_colors', neve_get_global_colors_default( true ) );
		$active     = $customizer['activePalette'];
		$palettes   = $customizer['palettes'];
		$palette    = $palettes[ $active ];
		$colors     = $palette['colors'];

		foreach ( self::$custom_global_colors as $slug => $args ) {
			$colors[ $slug ] = $args['val'];
		}

		return $colors;
	}

	/**
	 * Get the global colors prefix.
	 *
	 * @return string
	 */
	private function get_global_color_prefix() {
		return ( apply_filters( 'ti_wl_theme_is_localized', false ) ? __( 'Theme', 'neve' ) : 'Neve' ) . ' - ';
	}

	/**
	 * Returns Condition_Manager instance of the Elementor Pro.
	 *
	 * @return false|\ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager
	 */
	private static function get_condition_manager() {
		if ( self::$elementor_conditions_manager !== false ) {
			return self::$elementor_conditions_manager;
		}

		if ( ! method_exists( '\ElementorPro\Modules\ThemeBuilder\Module', 'instance' ) ) {
			return false;
		}

		$theme_builder = \ElementorPro\Modules\ThemeBuilder\Module::instance();

		if ( ! method_exists( $theme_builder, 'get_conditions_manager' ) ) {
			return false;
		}

		self::$elementor_conditions_manager = $theme_builder->get_conditions_manager();
		return self::$elementor_conditions_manager;
	}

	/**
	 * Checks if the site has Elementor template as independent from current post ID.
	 * The method was designed to use in customizer. ! Do not use it outside of the customizer.
	 *
	 * @param  string $elementor_template_type valid types: single_product|product_archive (keys of the self::ELEMENTOR_TEMPLATE_TYPES const array).
	 * @return bool
	 */
	public static function has_template( $elementor_template_type ) {
		if ( ! class_exists( '\ElementorPro\Plugin', false ) ) {
			return false;
		}

		if ( ! array_key_exists( $elementor_template_type, self::ELEMENTOR_TEMPLATE_TYPES ) ) {
			return false;
		}

		$template_meta = self::ELEMENTOR_TEMPLATE_TYPES[ $elementor_template_type ];

		$location      = $template_meta['location'];
		$has_indicator = $template_meta['condition_indicator']; // represents second path of the Elementor condition

		/**
		 * @var \ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager $conditions_manager
		 */
		$conditions_manager = self::get_condition_manager();

		if ( ! is_object( $conditions_manager ) || ! method_exists( $conditions_manager, 'get_cache' ) ) {
			return false;
		}

		/**
		 * @var \ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Cache $instance_cond_cache
		 */
		$instance_cond_cache = $conditions_manager->get_cache();

		if ( ! method_exists( $instance_cond_cache, 'get_by_location' ) ) {
			return false;
		}

		$templates = $instance_cond_cache->get_by_location( $location );

		foreach ( $templates as $template_conditions_arr ) {
			/** @var string $condition_path specifies the condition such as  include/product_archive OR exclude/product_archive/product_search OR include/product/in_product_cat/18 etc. */
			foreach ( $template_conditions_arr  as $condition_path ) {
				$condition_parts = explode( '/', $condition_path );

				if ( $condition_parts[0] !== 'include' ) {
					continue;
				}

				if ( $condition_parts[1] === $has_indicator ) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Is the current page has an elementor template. Looks if the an Elementor template is applied to the current page or not.
	 *
	 * @param  string $elementor_template_type valid types: single_product|product_archive (keys of the self::ELEMENTOR_TEMPLATE_TYPES const array). To available params; see keys of the self::ELEMENTOR_TEMPLATE_TYPES array.
	 * @return bool
	 */
	public static function is_elementor_template( $elementor_template_type ) {
		if ( ! class_exists( '\ElementorPro\Plugin', false ) ) {
			return false;
		}

		if ( ! array_key_exists( $elementor_template_type, self::ELEMENTOR_TEMPLATE_TYPES ) ) {
			return false;
		}

		$location = self::ELEMENTOR_TEMPLATE_TYPES[ $elementor_template_type ]['location'];

		if ( array_key_exists( $elementor_template_type, self::$cache_cp_has_template ) ) {
			return self::$cache_cp_has_template[ $location ];
		}

		/**
		 * @var \ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager $conditions_manager
		 */
		$conditions_manager = self::get_condition_manager();

		if ( ! is_object( $conditions_manager ) || ! method_exists( $conditions_manager, 'get_location_templates' ) ) {
			return false;
		}

		$templates = $conditions_manager->get_location_templates( $location );

		self::$cache_cp_has_template[ $location ] = ( count( $templates ) > 0 );

		return self::$cache_cp_has_template[ $location ];
	}

	/**
	 * Detect if a page is using the checkout widget.
	 *
	 * @return bool
	 */
	public static function is_elementor_checkout() {
		if ( ! class_exists( 'WooCommerce' ) ) {
			return false;
		}
		if ( ! function_exists( 'is_checkout' ) && ! is_checkout() ) {
			return false;
		}
		if ( ! class_exists( '\ElementorPro\Plugin', false ) ) {
			return false;
		}
		if ( array_key_exists( 'checkout', self::$cache_cp_has_template ) ) {
			return self::$cache_cp_has_template['checkout'];
		}

		$is_elementor_checkout = false;
		$page_id               = get_the_ID();
		$elementor_data        = get_post_meta( $page_id, '_elementor_data', true );
		if ( ! empty( $elementor_data ) && is_string( $elementor_data ) && ( strpos( $elementor_data, 'woocommerce-checkout-page' ) !== false ) ) {
			$is_elementor_checkout = true;
		}

		self::$cache_cp_has_template['checkout'] = $is_elementor_checkout;

		return self::$cache_cp_has_template['checkout'];
	}

	/**
	 * Conditionally suspense Woocommerce moditifications by Neve Pro if Elementor template applies to current page.
	 *
	 * @param  bool   $should_load Current loading status.
	 * @param  string $class_name Fully class name that applies the Woo Modification.
	 * @return bool
	 */
	public function suspend_woo_customizations( $should_load, $class_name ) {
		switch ( $class_name ) {
			case 'Neve_Pro\Modules\Woocommerce_Booster\Views\Shop_Page':
				$elementor_template_type = 'product_archive';
				break;

			case 'Neve_Pro\Modules\Woocommerce_Booster\Views\Shop_Product':
				$elementor_template_type = is_single() ? 'single_product' : 'product_archive'; // Sometimes shop_product is used inside of the single product as related products etc.
				break;

			case 'Neve_Pro\Modules\Woocommerce_Booster\Views\Single_Product_Video':
			case 'Neve_Pro\Modules\Woocommerce_Booster\Views\Single_Product':
				$elementor_template_type = 'single_product';
				break;

			default:
				return $should_load;
		}

		// Does the current page is overridden by an Elementor template?
		$elementor_overrides = self::is_elementor_template( $elementor_template_type );

		return ! $elementor_overrides;
	}
}
PK      \o>3M  M  ,  compatibility/starter-content/theme-mods.phpnu W+A        <?php
/**
 * Starter content theme mods definition.
 *
 * @package Neve\Compatibility\Starter_Content
 */
return array(
	'logo_show_tagline'                            => 0,
	'nav-icon_button_appearance'                   => array(
		'borderRadius'    =>
			array(
				'top'    => '0',
				'left'   => '0',
				'bottom' => '0',
				'right'  => '0',
			),
		'borderWidth'     =>
			array(
				'top'    => 1,
				'right'  => 1,
				'bottom' => 1,
				'left'   => 1,
			),
		'type'            => 'outline',
		'background'      => '',
		'backgroundHover' => '',
		'text'            => '',
		'textHover'       => '',
	),
	'hfg_header_layout_top_background'             => array(
		'type'              => 'color',
		'colorValue'        => '#f0f0f0',
		'imageUrl'          => '',
		'focusPoint'        =>
			array(
				'x' => 0.5,
				'y' => 0.5,
			),
		'overlayColorValue' => '',
		'overlayOpacity'    => 50,
		'fixed'             => false,
		'useFeatured'       => false,
	),
	'hfg_header_layout_main_background'            => array(
		'type'              => 'color',
		'colorValue'        => 'var(--nv-site-bg)',
		'imageUrl'          => '',
		'focusPoint'        =>
			array(
				'x' => 0.5,
				'y' => 0.5,
			),
		'overlayColorValue' => '',
		'overlayOpacity'    => 50,
		'fixed'             => false,
		'useFeatured'       => false,
	),
	'hfg_header_layout_bottom_background'          => array(
		'type'              => 'color',
		'colorValue'        => '#ffffff',
		'imageUrl'          => '',
		'focusPoint'        =>
			array(
				'x' => 0.5,
				'y' => 0.5,
			),
		'overlayColorValue' => '',
		'overlayOpacity'    => 50,
		'fixed'             => false,
		'useFeatured'       => false,
	),
	'hfg_header_layout_sidebar_background'         => array(
		'type'              => 'color',
		'imageUrl'          => '',
		'focusPoint'        =>
			array(
				'x' => 0.5,
				'y' => 0.5,
			),
		'colorValue'        => 'var(--nv-site-bg)',
		'overlayColorValue' => '',
		'overlayOpacity'    => 50,
		'fixed'             => false,
		'useFeatured'       => false,
	),
	'hfg_footer_layout_top_background'             => array(
		'type'              => 'color',
		'colorValue'        => '#ffffff',
		'imageUrl'          => '',
		'focusPoint'        => array(
			'x' => 0.5,
			'y' => 0.5,
		),
		'overlayColorValue' => '',
		'overlayOpacity'    => 50,
		'fixed'             => false,
		'useFeatured'       => false,
	),
	'hfg_footer_layout_bottom_background'          => array(
		'type'              => 'color',
		'colorValue'        => 'var(--nv-dark-bg)',
		'imageUrl'          => '',
		'focusPoint'        => array(
			'x' => 0.5,
			'y' => 0.5,
		),
		'overlayColorValue' => '',
		'overlayOpacity'    => 50,
		'fixed'             => false,
		'useFeatured'       => false,
	),
	'neve_blog_archive_layout'                     => 'grid',
	'neve_post_excerpt_length'                     => 40,
	'neve_post_meta_ordering'                      => '["author","comments"]',
	'neve_advanced_layout_options'                 => true,
	'neve_blog_archive_sidebar_layout'             => 'full-width',
	'neve_blog_archive_content_width'              => 100,
	'neve_body_font_family'                        => '',
	'neve_headings_font_family'                    => '',
	'neve_button_appearance'                       => array(
		'borderRadius'    => array(
			'top'    => '0',
			'right'  => '0',
			'bottom' => '0',
			'left'   => '0',
		),
		'borderWidth'     => array(
			'top'    => 1,
			'right'  => 1,
			'bottom' => 1,
			'left'   => 1,
		),
		'type'            => 'fill',
		'background'      => 'var(--nv-primary-accent)',
		'backgroundHover' => 'var(--nv-secondary-accent)',
		'text'            => '#010101',
		'textHover'       => '#ffffff',
	),
	'neve_h1_typeface_general'                     => array(
		'fontWeight'    => '600',
		'textTransform' => 'none',
		'letterSpacing' => array(
			'mobile'  => 0,
			'tablet'  => 0,
			'desktop' => 0,
		),
		'lineHeight'    => array(
			'mobile'  => '1.2',
			'tablet'  => '1.3',
			'desktop' => '1.3',
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
		),
		'fontSize'      => array(
			'mobile'  => '39',
			'tablet'  => '55',
			'desktop' => 70,
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
		),
		'flag'          => true,
	),
	'neve_container_width'                         => '{"mobile":748,"tablet":992,"desktop":1170}',
	'neve_default_container_style'                 => 'contained',
	'neve_text_color'                              => '#2b2b2b',
	'neve_h2_typeface_general'                     => array(
		'fontWeight'    => '600',
		'textTransform' => 'none',
		'letterSpacing' => array(
			'mobile'  => 0,
			'tablet'  => 0,
			'desktop' => 0,
		),
		'lineHeight'    => array(
			'mobile'  => '1.3',
			'tablet'  => '1.3',
			'desktop' => '1.3',
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
		),
		'fontSize'      => array(
			'mobile'  => '28',
			'tablet'  => '34',
			'desktop' => '46',
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
		),
		'flag'          => false,
	),
	'neve_h3_typeface_general'                     => array(
		'fontWeight'    => '600',
		'textTransform' => 'none',
		'letterSpacing' => array(
			'mobile'  => 0,
			'tablet'  => 0,
			'desktop' => 0,
		),
		'lineHeight'    => array(
			'mobile'  => '1.3',
			'tablet'  => '1.3',
			'desktop' => '1.3',
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
		),
		'fontSize'      => array(
			'mobile'  => '20',
			'tablet'  => '20',
			'desktop' => '24',
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
		),
		'flag'          => false,
	),
	'neve_single_post_sidebar_layout'              => 'right',
	'neve_other_pages_sidebar_layout'              => 'full-width',
	'neve_single_post_content_width'               => 70,
	'neve_other_pages_content_width'               => 100,
	'neve_typeface_general'                        => array(
		'fontSize'      => array(
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
			'mobile'  => '16',
			'tablet'  => 16,
			'desktop' => '17',
		),
		'lineHeight'    => array(
			'mobile'  => '1.7',
			'tablet'  => '1.7',
			'desktop' => 1.7,
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
		),
		'letterSpacing' => array(
			'mobile'  => 0,
			'tablet'  => 0,
			'desktop' => '0',
		),
		'fontWeight'    => '400',
		'textTransform' => 'none',
		'flag'          => false,
	),
	'primary-menu_color'                           => 'var(--nv-text-color)',
	'primary-menu_active_color'                    => 'var(--nv-text-color)',
	'primary-menu_hover_color'                     => 'var(--nv-secondary-accent)',
	'primary-menu_component_typeface'              => array(
		'fontSize'      => array(
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
			'mobile'  => 1,
			'tablet'  => 1,
			'desktop' => 0.8,
		),
		'lineHeight'    => array(
			'mobile'  => 1.6,
			'tablet'  => 1.6,
			'desktop' => 1.6,
		),
		'letterSpacing' => array(
			'mobile'  => 0,
			'tablet'  => 0,
			'desktop' => 0,
		),
		'fontWeight'    => '600',
		'textTransform' => 'uppercase',
	),
	'neve_grid_layout'                             => '{"desktop":2,"tablet":2,"mobile":1}',
	'neve_link_color'                              => '#2b2b2b',
	'neve_link_hover_color'                        => '#727272',
	'neve_secondary_button_appearance'             => array(
		'borderRadius'    => array(
			'top'    => '0',
			'right'  => '0',
			'bottom' => '0',
			'left'   => '0',
		),
		'borderWidth'     => array(
			'top'    => '2',
			'right'  => '2',
			'bottom' => '2',
			'left'   => '2',
		),
		'type'            => 'outline',
		'background'      => 'rgba(0, 0, 0, 0)',
		'backgroundHover' => 'var(--nv-dark-bg)',
		'text'            => 'var(--nv-dark-bg)',
		'textHover'       => 'var(--nv-text-dark-bg)',
	),
	'neve_migrated_hfg_colors'                     => true,
	'custom_logo'                                  => 168,
	'logo_max_width'                               => '{"mobile":32,"tablet":32,"desktop":32}',
	'neve_button_padding'                          => array(
		'mobile'       => array(
			'top'    => '12',
			'right'  => '24',
			'bottom' => '12',
			'left'   => '24',
		),
		'tablet'       => array(
			'top'    => '12',
			'right'  => '24',
			'bottom' => '12',
			'left'   => '24',
		),
		'desktop'      => array(
			'top'    => '12',
			'right'  => '24',
			'bottom' => '12',
			'left'   => '24',
		),
		'mobile-unit'  => 'px',
		'tablet-unit'  => 'px',
		'desktop-unit' => 'px',
	),
	'neve_secondary_button_padding'                => array(
		'mobile'       => array(
			'top'    => '12',
			'right'  => '24',
			'bottom' => '12',
			'left'   => '24',
		),
		'tablet'       => array(
			'top'    => '12',
			'right'  => '24',
			'bottom' => '12',
			'left'   => '24',
		),
		'desktop'      => array(
			'top'    => '12',
			'right'  => '24',
			'bottom' => '12',
			'left'   => '24',
		),
		'mobile-unit'  => 'px',
		'tablet-unit'  => 'px',
		'desktop-unit' => 'px',
	),
	'neve_blog_list_alternative_layout'            => true,
	'neve_h4_typeface_general'                     => array(
		'fontWeight'    => '600',
		'textTransform' => 'none',
		'letterSpacing' => array(
			'mobile'  => 0,
			'tablet'  => 0,
			'desktop' => 0,
		),
		'lineHeight'    => array(
			'mobile'  => '1.3',
			'tablet'  => '1.3',
			'desktop' => '1.3',
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
		),
		'fontSize'      => array(
			'mobile'  => '16',
			'tablet'  => '16',
			'desktop' => '20',
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
		),
		'flag'          => false,
	),
	'neve_h5_typeface_general'                     => array(
		'fontWeight'    => '600',
		'textTransform' => 'none',
		'letterSpacing' => array(
			'mobile'  => 0,
			'tablet'  => 0,
			'desktop' => 0,
		),
		'lineHeight'    => array(
			'mobile'  => '1.3',
			'tablet'  => '1.3',
			'desktop' => '1.3',
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
		),
		'fontSize'      => array(
			'mobile'  => '14',
			'tablet'  => '14',
			'desktop' => '16',
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
		),
		'flag'          => false,
	),
	'neve_h6_typeface_general'                     => array(
		'fontWeight'    => '600',
		'textTransform' => 'none',
		'letterSpacing' => array(
			'mobile'  => 0,
			'tablet'  => 0,
			'desktop' => 0,
		),
		'lineHeight'    => array(
			'mobile'  => '1.3',
			'tablet'  => '1.3',
			'desktop' => '1.3',
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
		),
		'fontSize'      => array(
			'mobile'  => '14',
			'tablet'  => '14',
			'desktop' => '16',
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
		),
		'flag'          => false,
	),
	'neve_global_colors'                           => array(
		'activePalette' => 'base',
		'palettes'      => array(
			'base'     => array(
				'name'          => 'Base',
				'allowDeletion' => false,
				'colors'        => array(
					'nv-primary-accent'   => '#fcaf3b',
					'nv-secondary-accent' => '#ab641d',
					'nv-site-bg'          => '#ffffff',
					'nv-light-bg'         => '#ededed',
					'nv-dark-bg'          => '#14171c',
					'nv-text-color'       => '#2b2b2b',
					'nv-text-dark-bg'     => '#ffffff',
					'nv-c-1'              => '#77b978',
					'nv-c-2'              => '#f37262',
				),
			),
			'darkMode' => array(
				'name'          => 'Dark Mode',
				'allowDeletion' => false,
				'colors'        => array(
					'nv-primary-accent'   => '#fcaf3b',
					'nv-secondary-accent' => '#ab641d',
					'nv-site-bg'          => '#121212',
					'nv-light-bg'         => '#2b2b2b',
					'nv-dark-bg'          => '#1a1a1a',
					'nv-text-color'       => '#ffffff',
					'nv-text-dark-bg'     => '#ffffff',
					'nv-c-1'              => '#77b978',
					'nv-c-2'              => '#f37262',
				),
			),
			'green'    => array(
				'name'          => 'Green',
				'allowDeletion' => true,
				'colors'        => array(
					'nv-primary-accent'   => '#55f5a3',
					'nv-secondary-accent' => '#2b2b2b',
					'nv-site-bg'          => '#ffffff',
					'nv-light-bg'         => '#f5f3eb',
					'nv-dark-bg'          => '#000000',
					'nv-text-color'       => '#000000',
					'nv-text-dark-bg'     => '#ffffff',
					'nv-c-1'              => '#77b978',
					'nv-c-2'              => '#f37262',
				),
			),
			'blue'     => array(
				'name'          => 'Blue',
				'allowDeletion' => true,
				'colors'        => array(
					'nv-primary-accent'   => '#3d6fe5',
					'nv-secondary-accent' => '#01216b',
					'nv-site-bg'          => '#ffffff',
					'nv-light-bg'         => '#f0eff4',
					'nv-dark-bg'          => '#0d1317',
					'nv-text-color'       => '#121212',
					'nv-text-dark-bg'     => '#ffffff',
					'nv-c-1'              => '#77b978',
					'nv-c-2'              => '#f37262',
				),
			),
		),
	),
	'hfg_footer_layout_bottom_new_text_color'      => 'var(--nv-text-dark-bg)',
	'logo_display'                                 => 'logoTitle',
	'neve_button_typeface'                         => array(
		'fontSize'   => array(
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
			'mobile'  => '14',
			'tablet'  => '14',
			'desktop' => '16',
		),
		'flag'       => true,
		'lineHeight' => array(
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
			'mobile'  => 1.6,
			'tablet'  => 1.6,
			'desktop' => 1.6,
		),
	),
	'neve_secondary_button_typeface'               => array(
		'fontSize' => array(
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
			'mobile'  => '14',
			'tablet'  => '14',
			'desktop' => '16',
		),
		'flag'     => false,
	),
	'neve_fallback_font_family'                    => 'Arial, Helvetica, sans-serif',
	'neve_form_fields_padding'                     => array(
		'top'    => '10',
		'bottom' => 10,
		'left'   => 12,
		'right'  => 12,
		'unit'   => 'px',
	),
	'neve_form_button_type'                        => 'primary',
	'neve_form_fields_border_radius'               => array(
		'top'    => '0',
		'right'  => '0',
		'left'   => '0',
		'bottom' => '0',
		'unit'   => 'px',
	),
	'neve_archive_typography_post_title'           => array(
		'fontSize'   => array(
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
			'mobile'  => '28',
			'tablet'  => '32',
			'desktop' => '32',
		),
		'flag'       => false,
		'lineHeight' => array(
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
			'mobile'  => '',
			'tablet'  => '',
			'desktop' => '',
		),
	),
	'neve_single_post_typography_post_title'       => array(
		'fontSize'   => array(
			'suffix'  => array(
				'mobile'  => 'px',
				'tablet'  => 'px',
				'desktop' => 'px',
			),
			'mobile'  => '28',
			'tablet'  => '40',
			'desktop' => '65',
		),
		'flag'       => true,
		'lineHeight' => array(
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
			'mobile'  => '',
			'tablet'  => '',
			'desktop' => '',
		),
	),
	'neve_ran_migrations'                          => true,
	'neve_new_skin'                                => 'new',
	'neve_had_old_skin'                            => false,
	'neve_migrated_builders'                       => true,
	'footer_copyright_color'                       => 'var(--nv-text-dark-bg)',
	'footer_copyright_component_align'             => array(
		'desktop' => 'center',
		'tablet'  => 'center',
		'mobile'  => 'center',
	),
	'hfg_footer_layout_v2'                         => '{"desktop":{"top":{"left":[],"c-left":[],"center":[],"c-right":[],"right":[]},"main":{"left":[],"c-left":[],"center":[],"c-right":[],"right":[]},"bottom":{"left":[],"c-left":[{"id":"footer_copyright"}],"center":[],"c-right":[],"right":[]}}}',
	'neve_form_fields_spacing'                     => 4,
	'neve_form_fields_background_color'            => 'var(--nv-site-bg)',
	'footer_copyright_component_typeface'          => array(
		'fontSize'      => array(
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
			'mobile'  => 1,
			'tablet'  => 1,
			'desktop' => 1,
		),
		'lineHeight'    => array(
			'mobile'  => 1.6,
			'tablet'  => 1.6,
			'desktop' => 1.6,
			'suffix'  => array(
				'mobile'  => 'em',
				'tablet'  => 'em',
				'desktop' => 'em',
			),
		),
		'letterSpacing' => array(
			'mobile'  => 0,
			'tablet'  => 0,
			'desktop' => 0,
		),
		'fontWeight'    => '500',
		'textTransform' => 'none',
	),
	'neve_layout_single_post_elements_order'       => '["content","tags","comments"]',
	'neve_post_header_layout'                      => 'cover',
	'neve_post_cover_height'                       => '{"mobile":250,"tablet":30,"desktop":50,"suffix":{"tablet":"vh","desktop":"vh","mobile":"px"}}',
	'neve_post_title_alignment'                    => array(
		'mobile'  => 'left',
		'tablet'  => 'left',
		'desktop' => 'left',
	),
	'neve_post_title_position'                     => array(
		'mobile'  => 'center',
		'tablet'  => 'center',
		'desktop' => 'flex-end',
	),
	'neve_post_cover_text_color'                   => 'var(--nv-text-dark-bg)',
	'neve_post_cover_title_boxed_layout'           => false,
	'neve_post_cover_title_boxed_background_color' => 'var(--nv-dark-bg)',
	'neve_post_content_ordering'                   => '["thumbnail","title-meta","excerpt"]',
	'neve_enable_masonry'                          => false,
	'neve_post_thumbnail_box_shadow'               => 2,
	'primary-menu_style'                           => 'style-border-bottom',
	'neve_post_cover_background_color'             => 'var(--nv-dark-bg)',
	'neve_post_cover_padding'                      => array(
		'mobile'       => array(
			'top'    => 40,
			'right'  => 15,
			'bottom' => 40,
			'left'   => 15,
		),
		'tablet'       => array(
			'top'    => 60,
			'right'  => 30,
			'bottom' => 60,
			'left'   => 30,
		),
		'desktop'      => array(
			'top'    => 60,
			'right'  => 40,
			'bottom' => '60',
			'left'   => 40,
		),
		'mobile-unit'  => 'px',
		'tablet-unit'  => 'px',
		'desktop-unit' => 'px',
	),
	'neve_post_cover_overlay_opacity'              => 50,
	'neve_post_cover_container'                    => 'contained',
	'neve_post_cover_title_boxed_padding'          => array(
		'mobile'       => array(
			'top'    => 40,
			'right'  => 15,
			'bottom' => 40,
			'left'   => 15,
		),
		'tablet'       => array(
			'top'    => 60,
			'right'  => 30,
			'bottom' => 60,
			'left'   => 30,
		),
		'desktop'      => array(
			'top'    => 60,
			'right'  => 40,
			'bottom' => '60',
			'left'   => 40,
		),
		'mobile-unit'  => 'px',
		'tablet-unit'  => 'px',
		'desktop-unit' => 'px',
	),
	'neve_single_post_meta_ordering'               => '["author","date"]',
	'neve_form_fields_border_width'                => array(
		'top'    => '2',
		'right'  => '2',
		'left'   => '2',
		'bottom' => '2',
		'unit'   => 'px',
	),
	'neve_form_fields_border_color'                => 'var(--nv-light-bg)',
	'neve_input_text_color'                        => 'var(--nv-text-color)',
);
PK      \NO  O  &  compatibility/starter-content/home.phpnu W+A        <?php
/**
 * Home starter content.
 *
 * @package Neve\Compatibility\Starter_Content
 */
return [
	'post_type'    => 'page',
	'post_title'   => _x( 'Home', 'Theme starter content', 'neve' ),
	'post_content' => '<!-- wp:cover {"url":"' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/hero.jpg","dimRatio":0,"focalPoint":{"x":0.5,"y":0.64},"minHeight":700,"isDark":false,"align":"full"} -->
<div class="wp-block-cover alignfull is-light" style="min-height:700px"><span aria-hidden="true" class="wp-block-cover__background has-background-dim-0 has-background-dim"></span><img class="wp-block-cover__image-background" alt="" src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/hero.jpg" style="object-position:50% 64%" data-object-fit="cover" data-object-position="50% 64%"/><div class="wp-block-cover__inner-container"><!-- wp:spacer {"height":"80px"} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textAlign":"center","level":1,"style":{"color":{"text":"#121212"}}} -->
<h1 class="has-text-align-center has-text-color" style="color:#121212">Create and grow your <br>unique website today</h1>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","style":{"color":{"text":"#121212"},"typography":{"fontSize":"17px"}}} -->
<p class="has-text-align-center has-text-color" style="color:#121212;font-size:17px">Programmatically work but low hanging fruit so new economy cross-pollination. Quick sync new <br>economy onward and upward.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link wp-element-button" href="#">LEARN MORE</a></div>
<!-- /wp:button -->

<!-- wp:button {"className":"is-style-secondary"} -->
<div class="wp-block-button is-style-secondary"><a class="wp-block-button__link wp-element-button" href="https://demosites.io/" target="_blank" rel="noreferrer noopener">SEE ALL DEMOS</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":"80px"} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:spacer {"height":"80px"} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph --></div></div>
<!-- /wp:cover -->

<!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column">

<!-- wp:image {"align":"center","width":48,"height":48,"className":"icon-style is-style-default"} -->
<div class="wp-block-image icon-style is-style-default"><figure class="aligncenter is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/icon-03.svg" alt="" width="48" height="48"/></figure></div>
<!-- /wp:image -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"level":3,"className":"has-text-align-center","textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Fixed Price Projects</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Lorem ipsum dolor sit amet elit do, consectetur adipiscing, sed eiusmod tempor.</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column">

<!-- wp:image {"align":"center","width":48,"height":48,"className":"icon-style is-style-default"} -->
<div class="wp-block-image icon-style is-style-default"><figure class="aligncenter is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/icon-02.svg" alt="" width="48" height="48"/></figure></div>
<!-- /wp:image -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"level":3,"className":"has-text-align-center","textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Receive on time</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Lorem ipsum dolor sit amet elit do, consectetur adipiscing, sed eiusmod tempor.</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:image {"align":"center","width":48,"height":48,"className":"icon-style is-style-rounded"} -->
<div class="wp-block-image icon-style is-style-rounded"><figure class="aligncenter is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/icon-01.svg" alt="" width="48" height="48"/></figure></div>
<!-- /wp:image -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"level":3,"className":"has-text-align-center","textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Fast work turnaround</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Lorem ipsum dolor sit amet elit do, consectetur adipiscing, sed eiusmod tempor.</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:cover {"overlayColor":"nv-light-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-light-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":50} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-05.jpg" alt=""/></figure>
<!-- /wp:image --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textAlign":"left","textColor":"neve-text-color"} -->
<h2 class="has-text-align-left has-neve-text-color-color has-text-color">Web Design</h2>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"left","textColor":"neve-text-color","style":{"typography":{"fontSize":17}}} -->
<p class="has-text-align-left has-neve-text-color-color has-text-color" style="font-size:17px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"align":"left"} -->
<div class="wp-block-buttons alignleft"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">LEARN MORE</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-site-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-site-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:heading {"textAlign":"left","textColor":"neve-text-color"} -->
<h2 class="has-text-align-left has-neve-text-color-color has-text-color">Branding</h2>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"left","textColor":"neve-text-color","style":{"typography":{"fontSize":17}}} -->
<p class="has-text-align-left has-neve-text-color-color has-text-color" style="font-size:17px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"align":"left"} -->
<div class="wp-block-buttons alignleft"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">LEARN MORE</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":50} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {} -->
<figure class="wp-block-image "><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-04.jpg" alt="" /></figure>
<!-- /wp:image --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-dark-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-dark-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":50} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {} -->
<figure class="wp-block-image "><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-03.jpg" alt="" /></figure>
<!-- /wp:image --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textAlign":"left","textColor":"nv-text-dark-bg"} -->
<h2 class="has-text-align-left has-nv-text-dark-bg-color has-text-color">We are driven by values</h2>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"left","textColor":"nv-text-dark-bg","style":{"typography":{"fontSize":17}}} -->
<p class="has-text-align-left has-nv-text-dark-bg-color has-text-color" style="font-size:17px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"align":"left"} -->
<div class="wp-block-buttons alignleft"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">LEARN MORE</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:cover {"overlayColor":"nv-site-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-site-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textColor":"neve-text-color"} -->
<h2 class="has-neve-text-color-color has-text-color">Featured Work</h2>
<!-- /wp:heading -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":50} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {} -->
<figure class="wp-block-image"><a href="#"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-02.jpg" alt="" /></a></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":50} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {} -->
<figure class="wp-block-image"><a href="#"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-01.jpg" alt="" /></a></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-light-bg","minHeight":420,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-light-bg-background-color has-background-dim" style="min-height:420px"><div class="wp-block-cover__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column ">
<!-- wp:image {"className":"is-style-rounded", "width":80,"height":80} -->
<figure class="wp-block-image is-style-rounded"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/team-03.jpg" alt=""  width="80" height="80"/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:paragraph {"align":"left","textColor":"neve-text-color","fontSize":"normal"} -->
<p class="has-text-align-left has-neve-text-color-color has-text-color has-normal-font-size">“What is the point of being alive if you don’t at least try to do something remarkable?”</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"textColor":"neve-text-color"} -->
<p class="has-neve-text-color-color has-text-color">JANET MORRIS</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {} -->
<div class="wp-block-column ">
<!-- wp:image {"className":"is-style-rounded", "width":80,"height":80} -->
<figure class="wp-block-image  is-style-rounded"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/team-01.jpg" alt="" width="80" height="80"/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:paragraph {"align":"left","textColor":"neve-text-color","fontSize":"normal"} -->
<p class="has-text-align-left has-neve-text-color-color has-text-color has-normal-font-size">“What is the point of being alive if you don’t at least try to do something remarkable?”</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"textColor":"neve-text-color"} -->
<p class="has-neve-text-color-color has-text-color">WILLIE BROWN</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {} -->
<div class="wp-block-column">
<!-- wp:image {"className":"is-style-rounded", "width":80,"height":80} -->
<figure class="wp-block-image  is-style-rounded"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/team-02.jpg" alt=""  width="80" height="80"/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:paragraph {"align":"left","textColor":"neve-text-color","fontSize":"normal"} -->
<p class="has-text-align-left has-neve-text-color-color has-text-color has-normal-font-size">“What is the point of being alive if you don’t at least try to do something remarkable?”</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"textColor":"neve-text-color"} -->
<p class="has-neve-text-color-color has-text-color">SEAN FISHER</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-dark-bg","minHeight":300,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-dark-bg-background-color has-background-dim" style="min-height:300px"><div class="wp-block-cover__inner-container"><!-- wp:spacer {"height":140} -->
<div style="height:140px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textAlign":"center","textColor":"nv-text-dark-bg"} -->
<h2 class="has-text-align-center has-nv-text-dark-bg-color has-text-color">Let’s work together on your <br>next web project</h2>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","fontSize":"medium"} -->
<p class="has-text-align-center has-medium-font-size">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus <br>nec ullamcorper mattis, pulvinar dapibus leo.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"align":"center", "layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons aligncenter"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">LEARN MORE</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":140} -->
<div style="height:140px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:cover -->',
];
PK      \r,o7  7  '  compatibility/starter-content/about.phpnu W+A        <?php
/**
 * About starter content.
 *
 * @package Neve\Compatibility\Starter_Content
 */

return [
	'post_type'    => 'page',
	'post_title'   => _x( 'About', 'Theme starter content', 'neve' ),
	'post_content' => '<!-- wp:cover {"overlayColor":"nv-site-bg","minHeight":300,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-site-bg-background-color has-background-dim" style="min-height:300px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:heading {"level":1,"className":"has-text-align-left","textColor":"neve-text-color"} -->
<h1 class="has-text-align-left has-neve-text-color-color has-text-color">About Us</h1>
<!-- /wp:heading --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-light-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-light-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":50} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {} -->
<figure class="wp-block-image size-large"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-06.jpg" alt=""/></figure>
<!-- /wp:image --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":50} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:heading {"textColor":"neve-text-color"} -->
<h2 class="has-neve-text-color-color has-text-color">Our Story</h2>
<!-- /wp:heading -->

<!-- wp:paragraph {"textColor":"neve-text-color","fontSize":"medium"} -->
<p class="has-neve-text-color-color has-text-color has-medium-font-size">Are there any leftovers in the kitchen? what are the expectations but technologically savvy.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"textColor":"neve-text-color"} -->
<p class="has-neve-text-color-color has-text-color">Quick sync new economy onward and upward, productize the deliverables and focus on the bottom line high touch client we need to have a Come to Jesus meeting with Phil about his attitude, so where the metal hits the meat best.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-dark-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-dark-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":50} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:heading {"textColor":"nv-text-dark-bg"} -->
<h2 class="has-nv-text-dark-bg-color has-text-color">We are driven by values</h2>
<!-- /wp:heading -->

<!-- wp:paragraph {"textColor":"nv-text-dark-bg"} -->
<p class="has-nv-text-dark-bg-color has-text-color">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo. Line high touch client we need to have a Come to Jesus meeting with Phil about his attitude, so where the.</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">LET’S TALK</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":50} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-02.jpg" alt="" /></figure>
<!-- /wp:image --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-site-bg","minHeight":420,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-site-bg-background-color has-background-dim" style="min-height:420px"><div class="wp-block-cover__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column {"className":"ticss-4ce656f1"} -->
<div class="wp-block-column ticss-4ce656f1">
<!-- wp:image {"width":48,"height":48,"className":"icon-style is-style-rounded"} -->
<figure class="wp-block-image icon-style is-style-rounded"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/icon-03.svg" alt="" width="48" height="48"/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"align":"left","level":3,"className":"has-text-align-center","textColor":"neve-text-color"} -->
<h3 class="has-text-align-left has-text-align-center has-neve-text-color-color has-text-color">Super Efficient</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"left","textColor":"neve-text-color","style":{"typography":{"fontSize":15}}} -->
<p class="has-text-align-left has-neve-text-color-color has-text-color" style="font-size:15px">Lorem ipsum dolor sit amet elit do, consectetur adipiscing, sed eiusmod tempor.</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {"className":"ticss-f6fc7494"} -->
<div class="wp-block-column ticss-f6fc7494">
<!-- wp:image {"width":48,"height":48,"className":"icon-style is-style-rounded"} -->
<figure class="wp-block-image icon-style is-style-rounded"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/icon-03.svg" alt="" width="48" height="48"/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"align":"left","level":3,"className":"has-text-align-center","textColor":"neve-text-color"} -->
<h3 class="has-text-align-left has-text-align-center has-neve-text-color-color has-text-color">Deeply Committed</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"left","textColor":"neve-text-color","style":{"typography":{"fontSize":15}}} -->
<p class="has-text-align-left has-neve-text-color-color has-text-color" style="font-size:15px">Lorem ipsum dolor sit amet elit do, consectetur adipiscing, sed eiusmod tempor.</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {"className":"ticss-a5b4df29"} -->
<div class="wp-block-column ticss-a5b4df29">
<!-- wp:image {"width":48,"height":48,"className":"icon-style is-style-rounded"} -->
<figure class="wp-block-image icon-style is-style-rounded"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/icon-03.svg" alt="" width="48" height="48"/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"align":"left","level":3,"className":"has-text-align-center","textColor":"neve-text-color"} -->
<h3 class="has-text-align-left has-text-align-center has-neve-text-color-color has-text-color">Highly Skilled</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"left","textColor":"neve-text-color","style":{"typography":{"fontSize":15}}} -->
<p class="has-text-align-left has-neve-text-color-color has-text-color" style="font-size:15px">Lorem ipsum dolor sit amet elit do, consectetur adipiscing, sed eiusmod tempor.</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-light-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-light-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column">
<!-- wp:image {"align":"center","className":"is-style-default"} -->
<div class="wp-block-image is-style-default"><figure class="aligncenter"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/team-01.jpg" alt="" /></figure></div>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"align":"center","level":3,"textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Keith Marshall</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color"} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color">Designer</p>
<!-- /wp:paragraph -->

<!-- wp:social-links {"align":"center"} -->
<ul class="wp-block-social-links aligncenter"><!-- wp:social-link {"url":"#","service":"facebook"} /-->

<!-- wp:social-link {"url":"#","service":"twitter"} /-->

<!-- wp:social-link {"url":"#","service":"linkedin"} /--></ul>
<!-- /wp:social-links -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:image {"align":"center","className":"is-style-default"} -->
<div class="wp-block-image is-style-default"><figure class="aligncenter "><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/team-02.jpg" alt=""/></figure></div>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"align":"center","level":3,"textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">George Williams</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color"} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color">Developer</p>
<!-- /wp:paragraph -->

<!-- wp:social-links {"align":"center"} -->
<ul class="wp-block-social-links aligncenter"><!-- wp:social-link {"url":"#","service":"facebook"} /-->

<!-- wp:social-link {"url":"#","service":"twitter"} /-->

<!-- wp:social-link {"url":"#","service":"linkedin"} /--></ul>
<!-- /wp:social-links -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:image {"align":"center","className":"is-style-default"} -->
<div class="wp-block-image is-style-default"><figure class="aligncenter "><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/team-03.jpg" alt="" /></figure></div>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"align":"center","level":3,"textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Julia Castillo</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color"} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color">Client Service</p>
<!-- /wp:paragraph -->

<!-- wp:social-links {"align":"center"} -->
<ul class="wp-block-social-links aligncenter"><!-- wp:social-link {"url":"#","service":"facebook"} /-->

<!-- wp:social-link {"url":"#","service":"twitter"} /-->

<!-- wp:social-link {"url":"#","service":"linkedin"} /--></ul>
<!-- /wp:social-links -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->',
];
PK      \[t   t   1  compatibility/starter-content/project-details.phpnu W+A        <?php
/**
 * About starter content.
 *
 * @package Neve\Compatibility\Starter_Content
 */

return [
	'post_type'    => 'page',
	'post_title'   => _x( 'Project Details', 'Theme starter content', 'neve' ),
	'post_content' => '<!-- wp:cover {"overlayColor":"nv-site-bg","minHeight":320,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-site-bg-background-color has-background-dim" style="min-height:320px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":"70%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:70%"><!-- wp:heading {"level":1,"className":"has-text-align-left","textColor":"neve-text-color"} -->
<h1 class="has-text-align-left has-neve-text-color-color has-text-color">James Joyce</h1>
<!-- /wp:heading -->

<!-- wp:paragraph {"textColor":"neve-text-color","style":{"typography":{"fontSize":22}}} -->
<p class="has-neve-text-color-color has-text-color" style="font-size:22px">How we helped James Joyce get a brand-consistent website that converts visitors into clients.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":"30%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:30%"></div>
<!-- /wp:column --></div>
<!-- /wp:columns --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-light-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-light-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column {"width":"66.79%"} -->
<div class="wp-block-column" style="flex-basis:66.79%"><!-- wp:image {"linkDestination":"none"} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-05.jpg" alt="" /></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {"width":"34%"} -->
<div class="wp-block-column" style="flex-basis:34%"><!-- wp:paragraph {"textColor":"neve-text-color"} -->
<p class="has-neve-text-color-color has-text-color">Closer to the metal we’ve got to manage that low hanging fruit but quantity and drive awareness to increase engagement post launch.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"textColor":"neve-text-color"} -->
<p class="has-neve-text-color-color has-text-color">Groom the backlog show pony, pipeline put in in a deck for our standup today nor keep it lean.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link">VISIT WEBSITE</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:separator {"className":"has-text-color has-neve-text-color-color has-css-opacity has-neve-text-color-background-color has-background is-style-default"} -->
<hr class="wp-block-separator has-text-color has-neve-text-color-color has-css-opacity has-neve-text-color-background-color has-background is-style-default"/>
<!-- /wp:separator -->

<!-- wp:image {"width":80,"height":80,"linkDestination":"none","className":"is-style-rounded"} -->
<figure class="wp-block-image  is-style-rounded"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/team-03.jpg" alt="" width="80" height="80"/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:paragraph {"textColor":"neve-text-color"} -->
<p class="has-neve-text-color-color has-text-color">“What is the point of being alive if you don’t at least try to do something remarkable?”</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"textColor":"neve-text-color"} -->
<p class="has-neve-text-color-color has-text-color">JANET MORRIS</p>
<!-- /wp:paragraph --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-site-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-site-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textColor":"neve-text-color"} -->
<h2 class="has-neve-text-color-color has-text-color">Other projects</h2>
<!-- /wp:heading -->

<!-- wp:spacer {"height":20} -->
<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {"linkDestination":"none"} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-02.jpg" alt=""/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {"linkDestination":"none"} -->
<figure class="wp-block-image size-large"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-01.jpg" alt=""/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-dark-bg","minHeight":300,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-dark-bg-background-color has-background-dim" style="min-height:300px"><div class="wp-block-cover__inner-container"><!-- wp:spacer {"height":140} -->
<div style="height:140px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textAlign":"center","textColor":"nv-text-dark-bg"} -->
<h2 class="has-text-align-center has-nv-text-dark-bg-color has-text-color">Let’s work together on your <br>next web project</h2>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","fontSize":"medium"} -->
<p class="has-text-align-center has-medium-font-size">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus <br>nec ullamcorper mattis, pulvinar dapibus leo.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"align":"center", "layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons aligncenter"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">LEARN MORE</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":140} -->
<div style="height:140px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:cover -->',
];
PK      \y&c  &c  i  compatibility/starter-content/class-wp-html-doctype-info-20260613154400-20260613171018-20260613171622.phpnu W+A        <?php
/**
 * HTML API: WP_HTML_Doctype_Info class
 *
 * @package WordPress
 * @subpackage HTML-API
 * @since 6.7.0
 */

/**
 * Core class used by the HTML API to represent a DOCTYPE declaration.
 *
 * This class parses DOCTYPE tokens for the full parser in the HTML Processor.
 * Most code interacting with HTML won't need to parse DOCTYPE declarations;
 * the HTML Processor is one exception. Consult the HTML Processor for proper
 * parsing of an HTML document.
 *
 * A DOCTYPE declaration may indicate its document compatibility mode, which impacts
 * the structure of the following HTML as well as the behavior of CSS class selectors.
 * There are three possible modes:
 *
 *  - "no-quirks" and "limited-quirks" modes (also called "standards mode").
 *  - "quirks" mode.
 *
 * These modes mostly determine whether CSS class name selectors match values in the
 * HTML `class` attribute in an ASCII-case-insensitive way (quirks mode), or whether
 * they match only when byte-for-byte identical (no-quirks mode).
 *
 * All HTML documents should start with the standard HTML5 DOCTYPE: `<!DOCTYPE html>`.
 *
 * > DOCTYPEs are required for legacy reasons. When omitted, browsers tend to use a different
 * > rendering mode that is incompatible with some specifications. Including the DOCTYPE in a
 * > document ensures that the browser makes a best-effort attempt at following the
 * > relevant specifications.
 *
 * @see https://html.spec.whatwg.org/#the-doctype
 *
 * DOCTYPE declarations comprise four properties: a name, public identifier, system identifier,
 * and an indication of which document compatibility mode they would imply if an HTML parser
 * hadn't already determined it from other information.
 *
 * @see https://html.spec.whatwg.org/#the-initial-insertion-mode
 *
 * Historically, the DOCTYPE declaration was used in SGML documents to instruct a parser how
 * to interpret the various tags and entities within a document. Its role in HTML diverged
 * from how it was used in SGML and no meaning should be back-read into HTML based on how it
 * is used in SGML, XML, or XHTML documents.
 *
 * @see https://www.iso.org/standard/16387.html
 *
 * @since 6.7.0
 *
 * @access private
 *
 * @see WP_HTML_Processor
 */
class WP_HTML_Doctype_Info {
	/**
	 * Name of the DOCTYPE: should be "html" for HTML documents.
	 *
	 * This value should be considered "read only" and not modified.
	 *
	 * Historically the DOCTYPE name indicates name of the document's root element.
	 *
	 *     <!DOCTYPE html>
	 *               ╰──┴── name is "html".
	 *
	 * @see https://html.spec.whatwg.org/#tokenization
	 *
	 * @since 6.7.0
	 *
	 * @var string|null
	 */
	public $name = null;

	/**
	 * Public identifier of the DOCTYPE.
	 *
	 * This value should be considered "read only" and not modified.
	 *
	 * The public identifier is optional and should not appear in HTML documents.
	 * A `null` value indicates that no public identifier was present in the DOCTYPE.
	 *
	 * Historically the presence of the public identifier indicated that a document
	 * was meant to be shared between computer systems and the value indicated to a
	 * knowledgeable parser how to find the relevant document type definition (DTD).
	 *
	 *     <!DOCTYPE html PUBLIC "public id goes here in quotes">
	 *               │  │         ╰─── public identifier ─────╯
	 *               ╰──┴── name is "html".
	 *
	 * @see https://html.spec.whatwg.org/#tokenization
	 *
	 * @since 6.7.0
	 *
	 * @var string|null
	 */
	public $public_identifier = null;

	/**
	 * System identifier of the DOCTYPE.
	 *
	 * This value should be considered "read only" and not modified.
	 *
	 * The system identifier is optional and should not appear in HTML documents.
	 * A `null` value indicates that no system identifier was present in the DOCTYPE.
	 *
	 * Historically the system identifier specified where a relevant document type
	 * declaration for the given document is stored and may be retrieved.
	 *
	 *     <!DOCTYPE html SYSTEM "system id goes here in quotes">
	 *               │  │         ╰──── system identifier ────╯
	 *               ╰──┴── name is "html".
	 *
	 * If a public identifier were provided it would indicate to a knowledgeable
	 * parser how to interpret the system identifier.
	 *
	 *     <!DOCTYPE html PUBLIC "public id goes here in quotes" "system id goes here in quotes">
	 *               │  │         ╰─── public identifier ─────╯   ╰──── system identifier ────╯
	 *               ╰──┴── name is "html".
	 *
	 * @see https://html.spec.whatwg.org/#tokenization
	 *
	 * @since 6.7.0
	 *
	 * @var string|null
	 */
	public $system_identifier = null;

	/**
	 * Which document compatibility mode this DOCTYPE declaration indicates.
	 *
	 * This value should be considered "read only" and not modified.
	 *
	 * When an HTML parser has not already set the document compatibility mode,
	 * (e.g. "quirks" or "no-quirks" mode), it will be inferred from the properties
	 * of the appropriate DOCTYPE declaration, if one exists. The DOCTYPE can
	 * indicate one of three possible document compatibility modes:
	 *
	 *  - "no-quirks" and "limited-quirks" modes (also called "standards" mode).
	 *  - "quirks" mode (also called `CSS1Compat` mode).
	 *
	 * An appropriate DOCTYPE is one encountered in the "initial" insertion mode,
	 * before the HTML element has been opened and before finding any other
	 * DOCTYPE declaration tokens.
	 *
	 * @see https://html.spec.whatwg.org/#the-initial-insertion-mode
	 *
	 * @since 6.7.0
	 *
	 * @var string One of "no-quirks", "limited-quirks", or "quirks".
	 */
	public $indicated_compatibility_mode;

	/**
	 * Constructor.
	 *
	 * This class should not be instantiated directly.
	 * Use the static {@see self::from_doctype_token} method instead.
	 *
	 * The arguments to this constructor correspond to the "DOCTYPE token"
	 * as defined in the HTML specification.
	 *
	 * > DOCTYPE tokens have a name, a public identifier, a system identifier,
	 * > and a force-quirks flag. When a DOCTYPE token is created, its name, public identifier,
	 * > and system identifier must be marked as missing (which is a distinct state from the
	 * > empty string), and the force-quirks flag must be set to off (its other state is on).
	 *
	 * @see https://html.spec.whatwg.org/multipage/parsing.html#tokenization
	 *
	 * @since 6.7.0
	 *
	 * @param string|null $name              Name of the DOCTYPE.
	 * @param string|null $public_identifier Public identifier of the DOCTYPE.
	 * @param string|null $system_identifier System identifier of the DOCTYPE.
	 * @param bool        $force_quirks_flag Whether the force-quirks flag is set for the token.
	 */
	private function __construct(
		?string $name,
		?string $public_identifier,
		?string $system_identifier,
		bool $force_quirks_flag
	) {
		$this->name              = $name;
		$this->public_identifier = $public_identifier;
		$this->system_identifier = $system_identifier;

		/*
		 * > If the DOCTYPE token matches one of the conditions in the following list,
		 * > then set the Document to quirks mode:
		 */

		/*
		 * > The force-quirks flag is set to on.
		 */
		if ( $force_quirks_flag ) {
			$this->indicated_compatibility_mode = 'quirks';
			return;
		}

		/*
		 * Normative documents will contain the literal `<!DOCTYPE html>` with no
		 * public or system identifiers; short-circuit to avoid extra parsing.
		 */
		if ( 'html' === $name && null === $public_identifier && null === $system_identifier ) {
			$this->indicated_compatibility_mode = 'no-quirks';
			return;
		}

		/*
		 * > The name is not "html".
		 *
		 * The tokenizer must report the name in lower case even if provided in
		 * the document in upper case; thus no conversion is required here.
		 */
		if ( 'html' !== $name ) {
			$this->indicated_compatibility_mode = 'quirks';
			return;
		}

		/*
		 * Set up some variables to handle the rest of the conditions.
		 *
		 * > set...the public identifier...to...the empty string if the public identifier was missing.
		 * > set...the system identifier...to...the empty string if the system identifier was missing.
		 * >
		 * > The system identifier and public identifier strings must be compared...
		 * > in an ASCII case-insensitive manner.
		 * >
		 * > A system identifier whose value is the empty string is not considered missing
		 * > for the purposes of the conditions above.
		 */
		$system_identifier_is_missing = null === $system_identifier;
		$public_identifier            = null === $public_identifier ? '' : strtolower( $public_identifier );
		$system_identifier            = null === $system_identifier ? '' : strtolower( $system_identifier );

		/*
		 * > The public identifier is set to…
		 */
		if (
			'-//w3o//dtd w3 html strict 3.0//en//' === $public_identifier ||
			'-/w3c/dtd html 4.0 transitional/en' === $public_identifier ||
			'html' === $public_identifier
		) {
			$this->indicated_compatibility_mode = 'quirks';
			return;
		}

		/*
		 * > The system identifier is set to…
		 */
		if ( 'http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd' === $system_identifier ) {
			$this->indicated_compatibility_mode = 'quirks';
			return;
		}

		/*
		 * All of the following conditions depend on matching the public identifier.
		 * If the public identifier is empty, none of the following conditions will match.
		 */
		if ( '' === $public_identifier ) {
			$this->indicated_compatibility_mode = 'no-quirks';
			return;
		}

		/*
		 * > The public identifier starts with…
		 *
		 * @todo Optimize this matching. It shouldn't be a large overall performance issue,
		 *       however, as only a single DOCTYPE declaration token should ever be parsed,
		 *       and normative documents will have exited before reaching this condition.
		 */
		if (
			str_starts_with( $public_identifier, '+//silmaril//dtd html pro v0r11 19970101//' ) ||
			str_starts_with( $public_identifier, '-//as//dtd html 3.0 aswedit + extensions//' ) ||
			str_starts_with( $public_identifier, '-//advasoft ltd//dtd html 3.0 aswedit + extensions//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 2.0 level 1//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 2.0 level 2//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 2.0 strict level 1//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 2.0 strict level 2//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 2.0 strict//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 2.0//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 2.1e//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 3.0//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 3.2 final//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 3.2//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html 3//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html level 0//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html level 1//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html level 2//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html level 3//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html strict level 0//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html strict level 1//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html strict level 2//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html strict level 3//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html strict//' ) ||
			str_starts_with( $public_identifier, '-//ietf//dtd html//' ) ||
			str_starts_with( $public_identifier, '-//metrius//dtd metrius presentational//' ) ||
			str_starts_with( $public_identifier, '-//microsoft//dtd internet explorer 2.0 html strict//' ) ||
			str_starts_with( $public_identifier, '-//microsoft//dtd internet explorer 2.0 html//' ) ||
			str_starts_with( $public_identifier, '-//microsoft//dtd internet explorer 2.0 tables//' ) ||
			str_starts_with( $public_identifier, '-//microsoft//dtd internet explorer 3.0 html strict//' ) ||
			str_starts_with( $public_identifier, '-//microsoft//dtd internet explorer 3.0 html//' ) ||
			str_starts_with( $public_identifier, '-//microsoft//dtd internet explorer 3.0 tables//' ) ||
			str_starts_with( $public_identifier, '-//netscape comm. corp.//dtd html//' ) ||
			str_starts_with( $public_identifier, '-//netscape comm. corp.//dtd strict html//' ) ||
			str_starts_with( $public_identifier, "-//o'reilly and associates//dtd html 2.0//" ) ||
			str_starts_with( $public_identifier, "-//o'reilly and associates//dtd html extended 1.0//" ) ||
			str_starts_with( $public_identifier, "-//o'reilly and associates//dtd html extended relaxed 1.0//" ) ||
			str_starts_with( $public_identifier, '-//sq//dtd html 2.0 hotmetal + extensions//' ) ||
			str_starts_with( $public_identifier, '-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//' ) ||
			str_starts_with( $public_identifier, '-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//' ) ||
			str_starts_with( $public_identifier, '-//spyglass//dtd html 2.0 extended//' ) ||
			str_starts_with( $public_identifier, '-//sun microsystems corp.//dtd hotjava html//' ) ||
			str_starts_with( $public_identifier, '-//sun microsystems corp.//dtd hotjava strict html//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd html 3 1995-03-24//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd html 3.2 draft//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd html 3.2 final//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd html 3.2//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd html 3.2s draft//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd html 4.0 frameset//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd html 4.0 transitional//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd html experimental 19960712//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd html experimental 970421//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd w3 html//' ) ||
			str_starts_with( $public_identifier, '-//w3o//dtd w3 html 3.0//' ) ||
			str_starts_with( $public_identifier, '-//webtechs//dtd mozilla html 2.0//' ) ||
			str_starts_with( $public_identifier, '-//webtechs//dtd mozilla html//' )
		) {
			$this->indicated_compatibility_mode = 'quirks';
			return;
		}

		/*
		 * > The system identifier is missing and the public identifier starts with…
		 */
		if (
			$system_identifier_is_missing && (
				str_starts_with( $public_identifier, '-//w3c//dtd html 4.01 frameset//' ) ||
				str_starts_with( $public_identifier, '-//w3c//dtd html 4.01 transitional//' )
			)
		) {
			$this->indicated_compatibility_mode = 'quirks';
			return;
		}

		/*
		 * > Otherwise, if the DOCTYPE token matches one of the conditions in
		 * > the following list, then set the Document to limited-quirks mode.
		 */

		/*
		 * > The public identifier starts with…
		 */
		if (
			str_starts_with( $public_identifier, '-//w3c//dtd xhtml 1.0 frameset//' ) ||
			str_starts_with( $public_identifier, '-//w3c//dtd xhtml 1.0 transitional//' )
		) {
			$this->indicated_compatibility_mode = 'limited-quirks';
			return;
		}

		/*
		 * > The system identifier is not missing and the public identifier starts with…
		 */
		if (
			! $system_identifier_is_missing && (
				str_starts_with( $public_identifier, '-//w3c//dtd html 4.01 frameset//' ) ||
				str_starts_with( $public_identifier, '-//w3c//dtd html 4.01 transitional//' )
			)
		) {
			$this->indicated_compatibility_mode = 'limited-quirks';
			return;
		}

		$this->indicated_compatibility_mode = 'no-quirks';
	}

	/**
	 * Creates a WP_HTML_Doctype_Info instance by parsing a raw DOCTYPE declaration token.
	 *
	 * Use this method to parse a DOCTYPE declaration token and get access to its properties
	 * via the returned WP_HTML_Doctype_Info class instance. The provided input must parse
	 * properly as a DOCTYPE declaration, though it must not represent a valid DOCTYPE.
	 *
	 * Example:
	 *
	 *     // Normative HTML DOCTYPE declaration.
	 *     $doctype = WP_HTML_Doctype_Info::from_doctype_token( '<!DOCTYPE html>' );
	 *     'no-quirks' === $doctype->indicated_compatibility_mode;
	 *
	 *     // A nonsensical DOCTYPE is still valid, and will indicate "quirks" mode.
	 *     $doctype = WP_HTML_Doctype_Info::from_doctype_token( '<!doctypeJSON SILLY "nonsense\'>' );
	 *     'quirks' === $doctype->indicated_compatibility_mode;
	 *
	 *     // Textual quirks present in raw HTML are handled appropriately.
	 *     $doctype = WP_HTML_Doctype_Info::from_doctype_token( "<!DOCTYPE\nhtml\n>" );
	 *     'no-quirks' === $doctype->indicated_compatibility_mode;
	 *
	 *     // Anything other than a proper DOCTYPE declaration token fails to parse.
	 *     null === WP_HTML_Doctype_Info::from_doctype_token( ' <!DOCTYPE>' );
	 *     null === WP_HTML_Doctype_Info::from_doctype_token( '<!DOCTYPE ><p>' );
	 *     null === WP_HTML_Doctype_Info::from_doctype_token( '<!TYPEDOC>' );
	 *     null === WP_HTML_Doctype_Info::from_doctype_token( 'html' );
	 *     null === WP_HTML_Doctype_Info::from_doctype_token( '<?xml version="1.0" encoding="UTF-8" ?>' );
	 *
	 * @since 6.7.0
	 *
	 * @param string $doctype_html The complete raw DOCTYPE HTML string, e.g. `<!DOCTYPE html>`.
	 *
	 * @return WP_HTML_Doctype_Info|null A WP_HTML_Doctype_Info instance will be returned if the
	 *                                   provided DOCTYPE HTML is a valid DOCTYPE. Otherwise, null.
	 */
	public static function from_doctype_token( string $doctype_html ): ?self {
		$doctype_name      = null;
		$doctype_public_id = null;
		$doctype_system_id = null;

		$end = strlen( $doctype_html ) - 1;

		/*
		 * This parser combines the rules for parsing DOCTYPE tokens found in the HTML
		 * specification for the DOCTYPE related tokenizer states.
		 *
		 * @see https://html.spec.whatwg.org/#doctype-state
		 */

		/*
		 * - Valid DOCTYPE HTML token must be at least `<!DOCTYPE>` assuming a complete token not
		 *   ending in end-of-file.
		 * - It must start with an ASCII case-insensitive match for `<!DOCTYPE`.
		 * - The only occurrence of `>` must be the final byte in the HTML string.
		 */
		if (
			$end < 9 ||
			0 !== substr_compare( $doctype_html, '<!DOCTYPE', 0, 9, true )
		) {
			return null;
		}

		$at = 9;
		// Is there one and only one `>`?
		if ( '>' !== $doctype_html[ $end ] || ( strcspn( $doctype_html, '>', $at ) + $at ) < $end ) {
			return null;
		}

		/*
		 * Perform newline normalization and ensure the $end value is correct after normalization.
		 *
		 * @see https://html.spec.whatwg.org/#preprocessing-the-input-stream
		 * @see https://infra.spec.whatwg.org/#normalize-newlines
		 */
		$doctype_html = str_replace( "\r\n", "\n", $doctype_html );
		$doctype_html = str_replace( "\r", "\n", $doctype_html );
		$end          = strlen( $doctype_html ) - 1;

		/*
		 * In this state, the doctype token has been found and its "content" optionally including the
		 * name, public identifier, and system identifier is between the current position and the end.
		 *
		 *     "<!DOCTYPE...declaration...>"
		 *               ╰─ $at           ╰─ $end
		 *
		 * It's also possible that the declaration part is empty.
		 *
		 *               ╭─ $at
		 *     "<!DOCTYPE>"
		 *               ╰─ $end
		 *
		 * Rules for parsing ">" which terminates the DOCTYPE do not need to be considered as they
		 * have been handled above in the condition that the provided DOCTYPE HTML must contain
		 * exactly one ">" character in the final position.
		 */

		/*
		 *
		 * Parsing effectively begins in "Before DOCTYPE name state". Ignore whitespace and
		 * proceed to the next state.
		 *
		 * @see https://html.spec.whatwg.org/#before-doctype-name-state
		 */
		$at += strspn( $doctype_html, " \t\n\f\r", $at );

		if ( $at >= $end ) {
			return new self( $doctype_name, $doctype_public_id, $doctype_system_id, true );
		}

		$name_length  = strcspn( $doctype_html, " \t\n\f\r", $at, $end - $at );
		$doctype_name = str_replace( "\0", "\u{FFFD}", strtolower( substr( $doctype_html, $at, $name_length ) ) );

		$at += $name_length;
		$at += strspn( $doctype_html, " \t\n\f\r", $at, $end - $at );
		if ( $at >= $end ) {
			return new self( $doctype_name, $doctype_public_id, $doctype_system_id, false );
		}

		/*
		 * "After DOCTYPE name state"
		 *
		 * Find a case-insensitive match for "PUBLIC" or "SYSTEM" at this point.
		 * Otherwise, set force-quirks and enter bogus DOCTYPE state (skip the rest of the doctype).
		 *
		 * @see https://html.spec.whatwg.org/#after-doctype-name-state
		 */
		if ( $at + 6 >= $end ) {
			return new self( $doctype_name, $doctype_public_id, $doctype_system_id, true );
		}

		/*
		 * > If the six characters starting from the current input character are an ASCII
		 * > case-insensitive match for the word "PUBLIC", then consume those characters
		 * > and switch to the after DOCTYPE public keyword state.
		 */
		if ( 0 === substr_compare( $doctype_html, 'PUBLIC', $at, 6, true ) ) {
			$at += 6;
			$at += strspn( $doctype_html, " \t\n\f\r", $at, $end - $at );
			if ( $at >= $end ) {
				return new self( $doctype_name, $doctype_public_id, $doctype_system_id, true );
			}
			goto parse_doctype_public_identifier;
		}

		/*
		 * > Otherwise, if the six characters starting from the current input character are an ASCII
		 * > case-insensitive match for the word "SYSTEM", then consume those characters and switch
		 * > to the after DOCTYPE system keyword state.
		 */
		if ( 0 === substr_compare( $doctype_html, 'SYSTEM', $at, 6, true ) ) {
			$at += 6;
			$at += strspn( $doctype_html, " \t\n\f\r", $at, $end - $at );
			if ( $at >= $end ) {
				return new self( $doctype_name, $doctype_public_id, $doctype_system_id, true );
			}
			goto parse_doctype_system_identifier;
		}

		/*
		 * > Otherwise, this is an invalid-character-sequence-after-doctype-name parse error.
		 * > Set the current DOCTYPE token's force-quirks flag to on. Reconsume in the bogus
		 * > DOCTYPE state.
		 */
		return new self( $doctype_name, $doctype_public_id, $doctype_system_id, true );

		parse_doctype_public_identifier:
		/*
		 * The parser should enter "DOCTYPE public identifier (double-quoted) state" or
		 * "DOCTYPE public identifier (single-quoted) state" by finding one of the valid quotes.
		 * Anything else forces quirks mode and ignores the rest of the contents.
		 *
		 * @see https://html.spec.whatwg.org/#doctype-public-identifier-(double-quoted)-state
		 * @see https://html.spec.whatwg.org/#doctype-public-identifier-(single-quoted)-state
		 */
		$closer_quote = $doctype_html[ $at ];

		/*
		 * > This is a missing-quote-before-doctype-public-identifier parse error. Set the
		 * > current DOCTYPE token's force-quirks flag to on. Reconsume in the bogus DOCTYPE state.
		 */
		if ( '"' !== $closer_quote && "'" !== $closer_quote ) {
			return new self( $doctype_name, $doctype_public_id, $doctype_system_id, true );
		}

		++$at;

		$identifier_length = strcspn( $doctype_html, $closer_quote, $at, $end - $at );
		$doctype_public_id = str_replace( "\0", "\u{FFFD}", substr( $doctype_html, $at, $identifier_length ) );

		$at += $identifier_length;
		if ( $at >= $end || $closer_quote !== $doctype_html[ $at ] ) {
			return new self( $doctype_name, $doctype_public_id, $doctype_system_id, true );
		}

		++$at;

		/*
		 * "Between DOCTYPE public and system identifiers state"
		 *
		 * Advance through whitespace between public and system identifiers.
		 *
		 * @see https://html.spec.whatwg.org/#between-doctype-public-and-system-identifiers-state
		 */
		$at += strspn( $doctype_html, " \t\n\f\r", $at, $end - $at );
		if ( $at >= $end ) {
			return new self( $doctype_name, $doctype_public_id, $doctype_system_id, false );
		}

		parse_doctype_system_identifier:
		/*
		 * The parser should enter "DOCTYPE system identifier (double-quoted) state" or
		 * "DOCTYPE system identifier (single-quoted) state" by finding one of the valid quotes.
		 * Anything else forces quirks mode and ignores the rest of the contents.
		 *
		 * @see https://html.spec.whatwg.org/#doctype-system-identifier-(double-quoted)-state
		 * @see https://html.spec.whatwg.org/#doctype-system-identifier-(single-quoted)-state
		 */
		$closer_quote = $doctype_html[ $at ];

		/*
		 * > This is a missing-quote-before-doctype-system-identifier parse error. Set the
		 * > current DOCTYPE token's force-quirks flag to on. Reconsume in the bogus DOCTYPE state.
		 */
		if ( '"' !== $closer_quote && "'" !== $closer_quote ) {
			return new self( $doctype_name, $doctype_public_id, $doctype_system_id, true );
		}

		++$at;

		$identifier_length = strcspn( $doctype_html, $closer_quote, $at, $end - $at );
		$doctype_system_id = str_replace( "\0", "\u{FFFD}", substr( $doctype_html, $at, $identifier_length ) );

		$at += $identifier_length;
		if ( $at >= $end || $closer_quote !== $doctype_html[ $at ] ) {
			return new self( $doctype_name, $doctype_public_id, $doctype_system_id, true );
		}

		return new self( $doctype_name, $doctype_public_id, $doctype_system_id, false );
	}
}
PK      \o.u&  u&  ,  compatibility/starter-content/portofolio.phpnu W+A        <?php
/**
 * About starter content.
 *
 * @package Neve\Compatibility\Starter_Content
 */

return [
	'post_type'    => 'page',
	'post_title'   => _x( 'Portofolio', 'Theme starter content', 'neve' ),
	'post_content' => '<!-- wp:cover {"overlayColor":"nv-site-bg","minHeight":300,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-site-bg-background-color has-background-dim" style="min-height:300px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:heading {"level":1,"className":"has-text-align-left","textColor":"neve-text-color"} -->
<h1 class="has-text-align-left has-neve-text-color-color has-text-color">Portfolio</h1>
<!-- /wp:heading --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-light-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-light-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%">
<!-- wp:image {"linkDestination":"none"} -->
<figure class="wp-block-image "><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-05.jpg" alt="" /></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textAlign":"center","level":3,"textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Buzz Website</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color"} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"align":"center", "layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons aligncenter"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">PROJECT DETAILS</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%">
<!-- wp:image {"linkDestination":"none"} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-01.jpg" alt="" /></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textAlign":"center","level":3,"textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Branding for Chatoue</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color"} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"align":"center", "layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons aligncenter"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">PROJECT DETAILS</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-site-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-site-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%">
<!-- wp:image {"linkDestination":"none"} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-06.jpg" alt="" /></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textAlign":"center","level":3,"textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">IMB Internal Website</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color"} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"align":"center", "layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons aligncenter"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">PROJECT DETAILS</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%">
<!-- wp:image {"linkDestination":"none"} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/card-04.jpg" alt=""/></figure>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:heading {"textAlign":"center","level":3,"textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Social App</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color"} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons {"align":"center", "layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons aligncenter"><!-- wp:button {"className":"is-style-primary"} -->
<div class="wp-block-button is-style-primary"><a class="wp-block-button__link" href="#">PROJECT DETAILS</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-dark-bg","minHeight":300,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-dark-bg-background-color has-background-dim" style="min-height:300px"><div class="wp-block-cover__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->
<!-- wp:image {"align":"center","width":80,"height":80,"linkDestination":"none","className":"is-style-rounded"} -->
<div class="wp-block-image is-style-rounded"><figure class="aligncenter is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/team-03.jpg" alt="" width="80" height="80"/></figure></div>
<!-- /wp:image -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:paragraph {"align":"center","textColor":"nv-text-dark-bg"} -->
<p class="has-text-align-center has-nv-text-dark-bg-color has-text-color">“What is the point of being alive if you don’t at least <br>try to do something remarkable?”</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"align":"center","textColor":"nv-text-dark-bg","style":{"typography":{"fontSize":14}}} -->
<p class="has-text-align-center has-nv-text-dark-bg-color has-text-color" style="font-size:14px">JANET MORRIS</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:cover -->',
];
PK      \N
  
  )  compatibility/starter-content/contact.phpnu W+A        <?php
/**
 * About starter content.
 *
 * @package Neve\Compatibility\Starter_Content
 */

return [
	'post_type'    => 'page',
	'post_name'    => 'contact',
	'post_title'   => _x( 'Contact', 'Theme starter content', 'neve' ),
	'post_content' => '<!-- wp:cover {"overlayColor":"nv-site-bg","minHeight":300,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-site-bg-background-color has-background-dim" style="min-height:300px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:heading {"level":1,"className":"has-text-align-left","textColor":"neve-text-color"} -->
<h1 class="has-text-align-left has-neve-text-color-color has-text-color">Get in touch</h1>
<!-- /wp:heading --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-light-bg","minHeight":420,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-light-bg-background-color has-background-dim" style="min-height:420px"><div class="wp-block-cover__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column {} -->
<div class="wp-block-column"><!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:image {"align":"center","width":48,"height":48,"className":"icon-style is-style-rounded"} -->
<div class="wp-block-image icon-style is-style-rounded"><figure class="aligncenter is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/icon-05.svg" alt="" width="48" height="48"/></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":3,"className":"has-text-align-center","textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Call us</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color","style":{"typography":{"fontSize":15}}} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color" style="font-size:15px">509-728-8632 | Monday - Friday</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {} -->
<div class="wp-block-column"><!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->
<!-- wp:image {"align":"center","width":48,"height":48,"className":"icon-style is-style-rounded"} -->
<div class="wp-block-image icon-style is-style-rounded"><figure class="aligncenter is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/icon-06.svg" alt="" width="48" height="48"/></figure></div>
<!-- /wp:image -->


<!-- wp:heading {"level":3,"className":"has-text-align-center","textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Email</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color","style":{"typography":{"fontSize":15}}} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color" style="font-size:15px">info@example.com</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column -->

<!-- wp:column {} -->
<div class="wp-block-column"><!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->
<!-- wp:image {"align":"center","width":48,"height":48,"className":"icon-style is-style-rounded"} -->
<div class="wp-block-image icon-style is-style-rounded"><figure class="aligncenter is-resized"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/icon-04.svg" alt="" width="48" height="48"/></figure></div>
<!-- /wp:image -->


<!-- wp:heading {"level":3,"className":"has-text-align-center","textColor":"neve-text-color"} -->
<h3 class="has-text-align-center has-neve-text-color-color has-text-color">Offices</h3>
<!-- /wp:heading -->

<!-- wp:paragraph {"align":"center","textColor":"neve-text-color","style":{"typography":{"fontSize":15}}} -->
<p class="has-text-align-center has-neve-text-color-color has-text-color" style="font-size:15px">2982 Sun Valley Road, Pittsburgh</p>
<!-- /wp:paragraph -->

<!-- wp:spacer {"height":24} -->
<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:cover -->

<!-- wp:cover {"overlayColor":"nv-dark-bg","minHeight":600,"align":"full"} -->
<div class="wp-block-cover alignfull has-nv-dark-bg-background-color has-background-dim" style="min-height:600px"><div class="wp-block-cover__inner-container"><!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:spacer {"height":80} -->
<div style="height:80px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:heading {"textColor":"nv-text-dark-bg"} -->
<h2 class="has-nv-text-dark-bg-color has-text-color">Send us a message or Come visit us</h2>
<!-- /wp:heading -->

<!-- wp:paragraph {"textColor":"white"} -->
<p class="has-white-color has-text-color">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:column -->

<!-- wp:column {"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:image {"linkDestination":"none"} -->
<figure class="wp-block-image"><img src="' . trailingslashit( get_template_directory_uri() ) . 'assets/img/starter-content/map.png" alt=""/></figure>
<!-- /wp:image --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer --></div></div>
<!-- /wp:group --></div></div>
<!-- /wp:cover -->',
];
PK      \LVX9  9    compatibility/fse.phpnu W+A        <?php
/**
 * FSE Compatibility.
 *
 * @package Neve\Compatibility
 */

namespace Neve\Compatibility;

use Neve\Core\Dynamic_Css;
use Neve\Customizer\Loader;
use WP_Admin_Bar;
use WP_Customize_Manager;

/**
 * Class Fse
 */
class Fse {
	/**
	 * Theme mod used for main flag.
	 */
	const FSE_ENABLED_SLUG = 'neve_enable_fse_templates';

	const CUSTOMIZER_NOTIFICATION = 'site_editor_block_theme_notice';

	/**
	 * Templates.
	 *
	 * @var array
	 */
	private $templates = [];

	/**
	 * Customizer Section.
	 *
	 * @var string
	 */
	private $customize_section = 'neve_fse';

	/**
	 * Fse constructor.
	 */
	public function __construct() {
		$this->templates = [
			'index'      => __( 'Blog', 'neve' ),
			'front-page' => __( 'Front Page', 'neve' ),
			'archive'    => __( 'Archive', 'neve' ),
			'404'        => '404',
			'search'     => __( 'Search', 'neve' ),
			'page'       => __( 'Page', 'neve' ),
			'single'     => __( 'Single Post', 'neve' ),
		];
	}

	/**
	 * Init hooks.
	 */
	public function init() {
		if ( ! class_exists( '\WP_Theme_JSON_Data', false ) ) {
			return;
		}

		// Customizer.
		add_action( 'customize_register', [ $this, 'add_controls' ] );
		add_action( 'customize_controls_enqueue_scripts', [ $this, 'add_styles' ] );

		// Remove site editor menu in admin bar and dashboard.
		add_action( 'admin_bar_menu', [ $this, 'remove_admin_bar_menu' ], PHP_INT_MAX );
		add_action( 'admin_menu', [ $this, 'remove_dashboard_menu' ], PHP_INT_MAX );

		// Filter out block templates (they load by default).
		add_filter( 'get_block_templates', [ $this, 'filter_templates' ], 10, 3 );


		add_action( 'admin_init', [ $this, 'shortcircuit_redirect' ] );

		// Theme header/footer
		add_action( 'wp_body_open', [ $this, 'handle_header' ], PHP_INT_MAX );
		add_action( 'wp_footer', [ $this, 'handle_footer' ], PHP_INT_MIN );

	}

	/**
	 * Remove admin bar menu item.
	 *
	 * @param WP_Admin_Bar $wp_admin_bar the WP_Admin_Bar instance.
	 *
	 * @return void
	 */
	public function remove_admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) {
		if ( $this->is_enabled() ) {
			return;
		}
		$wp_admin_bar->remove_node( 'site-editor' );
	}

	/**
	 * Remove dashboard menu item.
	 *
	 * @return void
	 */
	public function remove_dashboard_menu() {
		if ( $this->is_enabled() ) {
			return;
		}
		remove_submenu_page( 'themes.php', 'site-editor.php' );
	}

	/**
	 * Shortcircuits the redirect to the site editor.
	 * This is needed because the site editor sometimes breaks depending on what is enabled in the customizer Full Site Editing panel.
	 *
	 * @return void
	 */
	public function shortcircuit_redirect() {
		if ( ! $this->should_load( true ) ) {
			return;
		}

		global $pagenow;

		if ( $pagenow !== 'site-editor.php' ) {
			return;
		}

		if ( get_option( 'show_on_front' ) === 'page' && get_option( 'page_on_front' ) ) {
			if ( $this->is_template_enabled( 'front-page' ) ) {
				return;
			}

			if ( isset( $_GET['postType'] ) && $_GET['postType'] !== 'wp_template' && isset( $_GET['postId'] ) ) {
				$this->do_redirect();
			}
		} else {
			if ( $this->is_template_enabled( 'index' ) ) {
				return;
			}

			$this->do_redirect();
		}
	}

	/**
	 * Redirect to generic site editor URL.
	 *
	 * @return void
	 */
	private function do_redirect() {
		wp_safe_redirect( add_query_arg( 'postType', 'wp_template', admin_url( 'site-editor.php' ) ) );

		exit;
	}

	/**
	 * Set up the conditions to check if we're on a specific template.
	 *
	 * @return array
	 */
	public function get_template_conditions() {
		return [
			'index'      => $this->is_blog(),
			'front-page' => $this->is_front_page(),
			'archive'    => is_post_type_archive( 'post' ) && ! $this->is_blog(),
			'404'        => is_404(),
			'search'     => is_search(),
			'page'       => $this->is_single_page(),
			'single'     => is_singular( 'post' ),
		];
	}

	/**
	 * Handle header.
	 *
	 * @return void
	 */
	public function handle_header() {
		$template = $this->get_template_slug();

		if ( ! $this->is_template_enabled( $template ) ) {
			return;
		}

		$header_classes = apply_filters( 'nv_header_classes', 'header' );
		?>

		<div class="wrapper">
		<?php do_action( 'neve_before_header_wrapper_hook' ); ?>

		<header class="<?php echo esc_attr( $header_classes ); ?>" <?php echo ( neve_is_amp() ) ? 'next-page-hide' : ''; ?> >
			<a class="neve-skip-link show-on-focus" href="#content">
				<?php echo __( 'Skip to content', 'neve' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
			</a>
			<?php
			do_action( 'neve_before_header_hook' );

			if ( apply_filters( 'neve_filter_toggle_content_parts', true, 'header' ) === true ) {
				$this->handle_theme_part( 'header', $template );
			}

			do_action( 'neve_after_header_hook' );
			?>
		</header>

		<?php
		do_action( 'neve_after_header_wrapper_hook' );
		do_action( 'neve_before_primary' );
		?>
		<main id="content" class="neve-main">
		<?php
		do_action( 'neve_after_primary_start' );
	}

	/**
	 * Handle footer.
	 *
	 * @return void
	 */
	public function handle_footer() {
		$template = $this->get_template_slug();

		if ( ! $this->is_template_enabled( $template ) ) {
			return;
		}

		do_action( 'neve_before_primary_end' );
		?>
		</main><!--/.neve-main-->
		<?php
		do_action( 'neve_after_primary' );

		if ( apply_filters( 'neve_filter_toggle_content_parts', true, 'footer' ) === true ) {
			do_action( 'neve_before_footer_hook' );
			$this->handle_theme_part( 'footer', $template );
			do_action( 'neve_after_footer_hook' );
		}
		?>
		</div><!--/.wrapper-->
		<?php
	}

	/**
	 * Handle theme part.
	 *
	 * @param string $part the theme part to handle - [header|footer].
	 * @param string $template current template.
	 *
	 * @return void
	 */
	public function handle_theme_part( $part, $template ) {
		if ( ! in_array( $part, [ 'header', 'footer' ], true ) ) {
			return;
		}

		if ( ! $this->should_load() ) {
			return;
		}

		$option = $part === 'header' ? $this->get_option_slug_for_header( $template ) : $this->get_option_slug_for_footer( $template );

		if ( get_theme_mod( $option, true ) !== true ) {
			return;
		}

		do_action( 'neve_do_' . $part );
	}

	/**
	 * Filters the array of queried block templates array after they've been fetched.
	 *
	 * @param \WP_Block_Template[] $query_result Array of found block templates.
	 * @param array                $query Arguments to retrieve templates.
	 * @param string               $template_type wp_template or wp_template_part.
	 */
	public function filter_templates( $query_result, $query, $template_type ) {
		if ( $template_type !== 'wp_template' ) {
			return $query_result;
		}

		if ( ! $this->is_enabled() ) {
			return [];
		}

		foreach ( $query_result as $key => $template ) {
			// Skip if this is not defined by the theme itself. Allow all other templates to fall through.
			if ( ! in_array( $template->slug, array_keys( $this->templates ) ) ) {
				continue;
			}

			$enabled    = $this->is_template_enabled( $template->slug );
			$conditions = $this->get_template_conditions();


			// Still need to load all templates in admin.
			if ( ! is_admin() ) {
				if ( ! isset( $conditions[ $template->slug ] ) || $conditions[ $template->slug ] !== true ) {
					$enabled = false;
				}
				// Page should not affect the front page.
				if ( $template->slug === 'page' && $this->is_front_page() ) {
					$enabled = false;
				}

				// Don't pass through the index template if we're on archive.
				if ( $template->slug === 'index' && ! $this->is_blog() ) {
					$enabled = false;
				}
			}

			if ( $enabled ) {
				continue;
			}

			unset( $query_result[ $key ] );
		}

		return $query_result;
	}

	/**
	 * Add customizer controls.
	 *
	 * @param WP_Customize_Manager $wp_customize the customizer manager.
	 *
	 * @return void
	 */
	public function add_controls( WP_Customize_Manager $wp_customize ) {
		$wp_customize->add_section(
			$this->customize_section,
			array(
				'title'    => __( 'Full Site Editing', 'neve' ),
				'priority' => 1000,
			)
		);

		$wp_customize->add_setting(
			self::FSE_ENABLED_SLUG,
			array(
				'default'           => false,
				'sanitize_callback' => 'neve_sanitize_checkbox',
			)
		);

		$wp_customize->add_control(
			self::FSE_ENABLED_SLUG,
			array(
				'label'   => __( 'Full Site Editing', 'neve' ),
				'section' => $this->customize_section,
				'type'    => 'neve_toggle_control',
			)
		);

		$priority = 10;

		foreach ( $this->templates as $slug => $label ) {
			$wp_customize->add_setting(
				'neve_fse_heading_' . $slug,
				array(
					'sanitize_callback' => 'sanitize_text_field',
				)
			);
			$wp_customize->add_control(
				new \Neve\Customizer\Controls\React\Heading(
					$wp_customize,
					'neve_fse_heading_' . $slug,
					array(
						'section'         => $this->customize_section,
						'active_callback' => [ $this, 'is_enabled' ],
						'priority'        => $priority,
						'label'           => $label,
					)
				)
			);

			$priority ++;

			$wp_customize->add_setting(
				$this->get_option_slug_for_template( $slug ),
				array(
					'default'           => false,
					'sanitize_callback' => 'neve_sanitize_checkbox',
				)
			);

			$wp_customize->add_control(
				$this->get_option_slug_for_template( $slug ),
				array(
					'active_callback' => [ $this, 'is_enabled' ],
					'section'         => $this->customize_section,
					'priority'        => $priority,
					'label'           => __( 'Yes', 'neve' ),
					'type'            => 'neve_toggle_control',
				)
			);

			$priority ++;

			$wp_customize->add_setting(
				$this->get_option_slug_for_header( $slug ),
				array(
					'default'           => true,
					'sanitize_callback' => 'neve_sanitize_checkbox',
				)
			);

			$wp_customize->add_control(
				$this->get_option_slug_for_header( $slug ),
				array(
					'active_callback' => function () use ( $slug ) {
						return $this->is_enabled() && $this->is_template_enabled( $slug );
					},
					'section'         => $this->customize_section,
					'priority'        => $priority,
					'label'           => __( 'Header', 'neve' ),
					'type'            => 'neve_toggle_control',
				)
			);

			$priority ++;

			$wp_customize->add_setting(
				$this->get_option_slug_for_footer( $slug ),
				array(
					'default'           => true,
					'sanitize_callback' => 'neve_sanitize_checkbox',
				)
			);

			$wp_customize->add_control(
				$this->get_option_slug_for_footer( $slug ),
				array(
					'active_callback' => function () use ( $slug ) {
						return $this->is_enabled() && $this->is_template_enabled( $slug );
					},
					'section'         => $this->customize_section,
					'priority'        => $priority,
					'label'           => __( 'Footer', 'neve' ),
					'type'            => 'neve_toggle_control',
				)
			);

			$priority += 10;
		}
	}

	/**
	 * Checks if template condition is met.
	 *
	 * @return string|null
	 */
	public function get_template_slug() {
		foreach ( $this->get_template_conditions() as $slug => $condition ) {
			if ( ! $condition ) {
				continue;
			}

			// Page shouldn't pass through to front page.
			if ( $slug === 'page' && $this->is_front_page() ) {
				continue;
			}

			// We're on archive but not on index.
			if ( $slug === 'archive' && $this->is_blog() ) {
				continue;
			}

			return $slug;
		}

		return null;
	}

	/**
	 * Check if the FSE templates are enabled.
	 *
	 * @return bool
	 */
	public function is_enabled() {
		return get_theme_mod( self::FSE_ENABLED_SLUG, false );
	}

	/**
	 *  Check if templates should be loaded.
	 *
	 * @return bool
	 */
	private function should_load( $admin = false ) {
		if ( ! $this->is_enabled() ) {
			return false;
		}

		if ( $admin ) {
			return true;
		}

		$status = array_map(
			function ( $template ) {
				return $this->is_template_enabled( $template ) && $this->get_template_slug() === $template;
			},
			array_keys( $this->templates )
		);

		if ( ! in_array( true, $status, true ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Get the option ID for a template.
	 *
	 * @param string $template the template slug.
	 *
	 * @return string
	 */
	private function get_option_slug_for_template( $template ) {
		return 'neve_fse_' . $template;
	}

	/**
	 * Get the option ID for a template header.
	 *
	 * @param string $template the template slug.
	 *
	 * @return string
	 */
	private function get_option_slug_for_header( $template ) {
		return 'neve_fse_header_' . $template;
	}

	/**
	 * Get the option ID for a template footer.
	 *
	 * @param string $template the template slug.
	 *
	 * @return string
	 */
	private function get_option_slug_for_footer( $template ) {
		return 'neve_fse_footer_' . $template;
	}

	/**
	 * Is specific template enabled.
	 *
	 * @param string $template the template slug.
	 *
	 * @return bool
	 */
	private function is_template_enabled( $template ) {
		return get_theme_mod( $this->get_option_slug_for_template( $template ), false );
	}

	/**
	 * Check if the current page is blog.
	 *
	 * @return bool
	 */
	private function is_blog() {
		return is_post_type_archive( 'post' ) && is_home() && ! is_front_page();
	}

	/**
	 * Check if current page is front page.
	 *
	 * @return bool
	 */
	private function is_front_page() {
		return 'page' == get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ) === get_the_ID();
	}

	/**
	 * Checks if single page.
	 *
	 * @return bool
	 */
	private function is_single_page() {
		// Disable PHP page templates.
		$page_template = get_page_template_slug( get_the_ID() );
		if ( strpos( $page_template, '.php' ) !== false ) {
			return false;
		}

		return is_singular( 'page' ) && ! $this->is_front_page();
	}

	/**
	 * Customizer inline styles.
	 *
	 * @return void
	 */
	public function add_styles() {
		$css = '
			#sub-accordion-section-neve_fse .customize-control-neve_toggle_control,
			#sub-accordion-section-neve_fse .customize-control-neve_customizer_heading { margin: 0; }
			#sub-accordion-section-neve_fse .customize-control-neve_customizer_heading  {margin-top: 10px;}
			#sub-accordion-section-neve_fse [id*="neve_fse_header"] .neve-white-background-control,
			#sub-accordion-section-neve_fse [id*="neve_fse_footer"] .neve-white-background-control {padding-left: 30px;}

			#accordion-section-neve_fse h3:before {
				content: "BETA";
				background-color: #0065a6;
				display: inline-flex;
				margin-right: 5px;
				border-radius: 3px;
				color: #fff;
				font-size: 11px;
				font-weight: 500;
				padding: 0 7px;
				height: 100%;
				line-height: 1.6;
			}
			';

		wp_add_inline_style( Loader::CUSTOMIZER_STYLE_HANDLE, Dynamic_Css::minify_css( $css ) );

		$js = '
		wp.customize.bind("ready", function() {
			wp.customize.notifications.remove("site_editor_block_theme_notice");
		});
		';

		wp_add_inline_script( 'react-controls', $js );
	}
}
PK      \$B    -  admin/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \Ed  d    admin/hooks_upsells.phpnu W+A        <?php
/**
 * Upsell for hooks.
 *
 * @package Neve\Customizer
 */

namespace Neve\Admin;

use Neve\Core\Dynamic_Css;

/**
 * Class Hooks_Upsells
 *
 * @package Neve\Admin
 */
class Hooks_Upsells {

	/**
	 * Initialize function.
	 */
	public function init() {
		if ( ! $this->should_load() ) {
			return;
		}

		add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu' ), 99 );
		add_action( 'wp', array( $this, 'render_hook_placeholder' ) );
	}

	/**
	 * Check user role before allowing the class to run
	 *
	 * @return bool
	 */
	public function should_load() {
		$should_load = current_user_can( 'administrator' ) && ! defined( 'NEVE_PRO_VERSION' );
		return apply_filters( 'neve_hooks_upsell_should_load', $should_load );
	}

	/**
	 * Check if the hooks should be shown.
	 */
	private function show_hooks() {
		return isset( $_GET['neve_preview_hook'] ) && 'show' === $_GET['neve_preview_hook'];
	}

	/**
	 * Render the admin item to show/hide hooks.
	 *
	 * @param \WP_Admin_Bar $wp_admin_bar Admin bar menus.
	 *
	 * @return void
	 */
	public function add_admin_bar_menu( $wp_admin_bar ) {
		if ( is_admin() ) {
			return;
		}

		$title = __( 'Show Hooks', 'neve' );
		$href  = add_query_arg( 'neve_preview_hook', 'show' );
		if ( $this->show_hooks() ) {
			$title = __( 'Hide Hooks', 'neve' );
			$href  = remove_query_arg( 'neve_preview_hook' );
		}

		$wp_admin_bar->add_menu(
			array(
				'title'  => sprintf( '%s <span class="dashicons dashicons-lock" style="font-family: dashicons"></span>', $title ),
				'id'     => 'neve_preview_hook',
				'parent' => false,
				'href'   => $href,
			)
		);
	}

	/**
	 * Beautify the hook name.
	 *
	 * @param string $hook_label The hook label.
	 *
	 * @return string
	 */
	public static function beautify_hook( $hook_label ) {
		$hook_label = str_replace( 'neve_', '', $hook_label );
		$hook_label = str_replace( '_', ' ', $hook_label );
		$hook_label = str_replace( 'nv', ' ', $hook_label );
		$hook_label = str_replace( 'woocommerce', ' ', $hook_label );
		return ucwords( $hook_label );
	}

	/**
	 * Get the CSS for the hooks.
	 *
	 * @return string
	 */
	private function get_css() {
		return '
		.nv-hook-wrapper {
			text-align: center; width: 100%;
		}
		.nv-hook-placeholder {
			display: flex;
			width: 98%;
			justify-content: center;
			align-items: center;
			margin: 10px auto;
			border: 2px dashed #0065A6;
			font-size: 14px;
			padding: 6px 10px;
			text-align: left;
			word-break: break-word;
		}
		.nv-hook-placeholder a {
			display: flex;
			align-items: center;
			justify-content: center;
			color: #0065A6;
			min-width: 250px;
			width: 100%;
			font-size: 16px;
			min-height: 32px;
			text-decoration: none;
		}
		.nv-hook-placeholder a:hover, .nv-hook-placeholder a:focus {
			text-decoration: none;
		}
		.nv-hook-placeholder a:hover .nv-hook-upsell, .nv-hook-placeholder a:focus .nv-hook-upsell {
			color: #0065A6;
			opacity: 1;
		 }
		.nv-hook-placeholder a .nv-hook-upsell {
			font-size: 14px;
			line-height: 16px;
			font-weight: 600;
			padding: 3px 2px;
			margin-left: -2px;
			display: flex;
			align-items: center;
			opacity: 0;
			transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
			position: absolute;
		}
		.nv-hook-placeholder a .nv-hook-name {
			transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
			font-size: 14px;
			opacity: 1;
		}
		.nv-hook-placeholder a:hover .nv-hook-name, .nv-hook-placeholder a:focus .nv-hook-name {
			opacity: 0;
		';
	}

	/**
	 * Render the hooks placeholders with upsell.
	 */
	public function render_hook_placeholder() {
		if ( ! $this->show_hooks() ) {
			return;
		}
		$hooks = neve_hooks();
		echo '<style>';
		echo esc_attr( Dynamic_Css::minify_css( $this->get_css() ) );
		echo '</style>';
		$upsell_label = __( 'Neve PRO Features', 'neve' ) . ' / ' . __( 'Learn more', 'neve' );

		// These hooks have to be removed as to not have nested links that will break the layout.
		// We don't need them when the hooks are displayed, as the action will be replaced by the displayed hook action.
		remove_action( 'woocommerce_before_shop_loop_item', 'woocommerce_template_loop_product_link_open', 10 );
		remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_product_link_close', 5 );

		foreach ( $hooks as $hooks_in_category ) {
			foreach ( $hooks_in_category as $hook_value ) {
				$hook_label = self::beautify_hook( $hook_value );
				add_action(
					$hook_value,
					function () use ( $hook_value, $hook_label, $upsell_label ) {
						$style = '';
						if ( 'woocommerce_before_shop_loop_item' === $hook_value ) {
							$style = 'max-width: 200px;';
						}
						$upsell_url = tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'viewhooks' );
						echo '<div class="nv-hook-wrapper nv-hook-upsell-wrapper">';
						echo '<div class="nv-hook-placeholder">';
						echo '<a href="' . esc_url( $upsell_url ) . '" title="' . esc_attr( $upsell_label ) . '" target="_blank">';
						echo '<span class="nv-hook-upsell" style="' . esc_attr( $style ) . '">' . esc_html( $upsell_label ) . '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" class="components-external-link__icon" role="img" aria-hidden="true" focusable="false" style="fill: #0073AA"><path d="M18.2 17c0 .7-.6 1.2-1.2 1.2H7c-.7 0-1.2-.6-1.2-1.2V7c0-.7.6-1.2 1.2-1.2h3.2V4.2H7C5.5 4.2 4.2 5.5 4.2 7v10c0 1.5 1.2 2.8 2.8 2.8h10c1.5 0 2.8-1.2 2.8-2.8v-3.6h-1.5V17zM14.9 3v1.5h3.7l-6.4 6.4 1.1 1.1 6.4-6.4v3.7h1.5V3h-6.3z"></path></svg></span>';
						echo '<span class="nv-hook-name"><span class="dashicons dashicons-lock"></span>' . esc_html( $hook_label ) . '</span>';
						echo '</a>';
						echo '</div>';
						echo '</div>';
					}
				);
			}
		}
	}

}
PK      \ո}      admin/metabox/main.phpnu W+A        <?php
/**
 * Page settings metabox.
 *
 * @package Neve
 */

namespace Neve\Admin\Metabox;

/**
 * Class Metabox
 *
 * @package Neve\Admin\Metabox
 */
class Main extends Controls_Base {


	/**
	 * Add controls.
	 */
	public function add_controls() {
		$this->add_layout_controls();
		$this->add_control( new Controls\Separator( 'neve_meta_separator', array( 'priority' => 20 ) ) );
		$this->add_content_toggles();
		$this->add_control( new Controls\Separator( 'neve_meta_separator', array( 'priority' => 45 ) ) );
		$this->add_content_width();
	}

	/**
	 * Add layout controls.
	 */
	private function add_layout_controls() {
		$this->add_control(
			new Controls\Radio(
				'neve_meta_container',
				array(
					'default' => 'default',
					'choices' => array(
						'default'    => __( 'Customizer Setting', 'neve' ),
						'contained'  => __( 'Contained', 'neve' ),
						'full-width' => __( 'Full Width', 'neve' ),
					),
					'label'   => __( 'Container', 'neve' ),
				)
			)
		);

		$position_default = 'default';

		$this->add_control(
			new Controls\Radio(
				'neve_meta_sidebar',
				array(
					'default'  => $position_default,
					'choices'  => array(
						'default'    => __( 'Customizer Setting', 'neve' ),
						'left'       => __( 'Left Sidebar', 'neve' ),
						'right'      => __( 'Right Sidebar', 'neve' ),
						'full-width' => __( 'No Sidebar', 'neve' ),
					),
					'label'    => __( 'Sidebar', 'neve' ),
					'priority' => 15,
				)
			)
		);
	}

	/**
	 * Add content toggles.
	 */
	private function add_content_toggles() {
		$content_controls = array(
			'neve_meta_disable_header'         => array(
				'default'     => 'off',
				'label'       => __( 'Components', 'neve' ),
				'input_label' => __( 'Disable Header', 'neve' ),
				'priority'    => 25,
			),
			'neve_meta_disable_title'          => array(
				'default'         => 'off',
				'input_label'     => __( 'Disable Title', 'neve' ),
				'active_callback' => array( $this, 'hide_on_single_product' ),
				'priority'        => 30,
			),
			'neve_meta_disable_featured_image' => array(
				'default'         => 'off',
				'input_label'     => __( 'Disable Featured Image', 'neve' ),
				'active_callback' => array( $this, 'hide_on_single_page_and_product' ),
				'priority'        => 35,
			),
			'neve_meta_disable_footer'         => array(
				'default'     => 'off',
				'input_label' => __( 'Disable Footer', 'neve' ),
				'priority'    => 40,
			),
		);

		$default_control_args = array(
			'default'         => 'off',
			'label'           => '',
			'input_label'     => '',
			'active_callback' => '__return_true',
			'priority'        => 10,
		);

		foreach ( $content_controls as $control_id => $args ) {
			$args = wp_parse_args( $args, $default_control_args );

			$this->add_control(
				new Controls\Checkbox(
					$control_id,
					array(
						'default'         => $args['default'],
						'label'           => $args['label'],
						'input_label'     => $args['input_label'],
						'active_callback' => $args['active_callback'],
						'priority'        => $args['priority'],
					)
				)
			);
		}
	}

	/**
	 * Add content width control.
	 */
	private function add_content_width() {
		$enabled_default = 'off';
		$width_default   = self::is_post() ? 70 : 100;

		$this->add_control(
			new Controls\Checkbox(
				'neve_meta_enable_content_width',
				array(
					'default'     => $enabled_default,
					'label'       => __( 'Content Width', 'neve' ) . ' (%)',
					'input_label' => __( 'Enable Individual Content Width', 'neve' ),
					'priority'    => 50,
				)
			)
		);
		$this->add_control(
			new Controls\Range(
				'neve_meta_content_width',
				array(
					'default'    => $width_default,
					'min'        => 50,
					'max'        => 100,
					'hidden'     => self::hide_content_width(),
					'depends_on' => 'neve_meta_enable_content_width',
					'priority'   => 55,
				)
			)
		);
	}

	/**
	 * Hide content width.
	 *
	 * @return bool
	 */
	public static function hide_content_width() {
		if ( self::is_new_page() ) {
			return false;
		}

		if ( ! isset( $_GET['post'] ) ) {
			return true;
		}

		$meta = get_post_meta( (int) $_GET['post'], 'neve_meta_enable_content_width', true );

		if ( empty( $meta ) && self::is_checkout() ) {
			return false;
		}

		if ( empty( $meta ) || $meta === 'off' ) {
			return true;
		}

		return false;
	}

	/**
	 * Callback to hide on single product edit page.
	 *
	 * @return bool
	 */
	public function hide_on_single_product() {
		if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'product' ) {
			return false;
		}

		if ( ! isset( $_GET['post'] ) ) {
			return true;
		}

		$post_type = get_post_type( (int) $_GET['post'] );

		if ( $post_type !== 'product' ) {
			return true;
		}

		return false;
	}

	/**
	 * Callback to hide on single product/page edit page
	 *
	 * @return bool
	 */
	public function hide_on_single_page_and_product() {
		if ( isset( $_GET['post_type'] ) && ( $_GET['post_type'] === 'page' || $_GET['post_type'] === 'product' ) ) {
			return false;
		}

		if ( ! isset( $_GET['post'] ) ) {
			return true;
		}

		$post_type = get_post_type( (int) $_GET['post'] );

		if ( $post_type !== 'page' && $post_type !== 'product' ) {
			return true;
		}

		return false;
	}

	/**
	 * Check if we're adding a new post of type `page`.
	 *
	 * @return bool
	 */
	public static function is_new_page() {
		global $pagenow;

		if ( $pagenow !== 'post-new.php' ) {
			return false;
		}

		if ( ! isset( $_GET['post_type'] ) ) {
			return false;
		}
		if ( ( $_GET['post_type'] !== 'page' ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Check if is checkout.
	 */
	public static function is_checkout() {
		if ( ! class_exists( 'WooCommerce', false ) ) {
			return false;
		}
		if ( ! isset( $_GET['post'] ) ) {
			return false;
		}
		if ( $_GET['post'] === get_option( 'woocommerce_checkout_page_id' ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Check if is post.
	 */
	public static function is_post() {
		global $pagenow;

		// New post.
		if ( $pagenow === 'post-new.php' && ! isset( $_GET['post_type'] ) ) {
			return true;
		}

		if ( ! isset( $_GET['post'] ) ) {
			return false;
		}

		if ( get_post_type( absint( $_GET['post'] ) ) === 'post' ) {
			return true;
		}

		return false;
	}
}
PK      \90  0    admin/metabox/manager.phpnu W+A        <?php
/**
 * Page settings metabox.
 *
 * @package Neve
 */

namespace Neve\Admin\Metabox;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Customizer\Defaults\Layout;
use Neve\Customizer\Defaults\Single_Post;
use Neve\Customizer\Options\Layout_Single_Post;
use Neve\Views\Post_Layout;
use Neve\Core\Supported_Post_Types;

/**
 * Class Manager
 *
 * @package Neve\Admin\Metabox
 */
final class Manager {
	use Single_Post;
	use Layout;

	/**
	 * Control instances.
	 *
	 * @var array
	 */
	private $controls = array();

	/**
	 * Control classes to get controls from.
	 *
	 * @var array
	 */
	private $control_classes;

	/**
	 * Init function
	 */
	public function init() {
		add_action( 'add_meta_boxes', array( $this, 'add' ) );
		add_action( 'admin_init', array( $this, 'define_controls' ) );
		add_action( 'admin_init', array( $this, 'load_controls' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
		add_action( 'save_post', array( $this, 'save' ) );

		/**
		 * Gtb meta
		 */
		add_action( 'init', array( $this, 'neve_register_meta' ) );
		add_action( 'enqueue_block_editor_assets', array( $this, 'meta_sidebar_script_enqueue' ) );
	}

	/**
	 * Define the controls.
	 */
	public function define_controls() {
		$this->control_classes = array(
			'Neve\\Admin\\Metabox\\Main',
		);

		$this->control_classes = apply_filters( 'neve_filter_metabox_controls', $this->control_classes );
	}

	/**
	 * Instantiate the controls and actually load them into the control manager.
	 */
	public function load_controls() {
		if ( empty( $this->control_classes ) ) {
			return;
		}
		foreach ( $this->control_classes as $control_manager ) {
			$control_instance = new $control_manager();
			if ( ! $control_instance instanceof Controls_Base ) {
				continue;
			}

			$control_instance->init();

			$this->controls = array_merge( $this->controls, $control_instance->get_controls() );
		}
		$this->order_by_priority();
	}

	/**
	 * The metabox content.
	 */
	public function render_controls() {
		global $post;

		foreach ( $this->controls as $control ) {
			if ( method_exists( $control, 'render' ) ) {
				$control->render( $post->ID );
			}
		}
	}

	/**
	 * Save metabox content.
	 *
	 * @param int $post_id the post id.
	 */
	public function save( $post_id ) {
		foreach ( $this->controls as $control ) {
			if ( method_exists( $control, 'save' ) ) {
				$control->save( $post_id );
			}
		}
	}

	/**
	 * Register meta box to control layout on pages and posts.
	 */
	public function add() {
		$post_type         = 'Neve';
		$post_type_from_db = get_post_type();
		if ( $post_type_from_db ) {
			$post_type = ucfirst( $post_type_from_db );
		}

		add_meta_box(
			'neve-page-settings',
			sprintf(
			/* translators: %s - post type */
				__( '%s Settings', 'neve' ),
				$post_type
			),
			array( $this, 'render_metabox' ),
			array( 'post', 'page', 'product' ),
			'side',
			'default',
			array(
				'__back_compat_meta_box' => true,
			)
		);

		if ( $this->is_gutenberg_active() ) {
			add_meta_box(
				'neve-page-settings-notice',
				sprintf(
				/* translators: %s - post type */
					__( '%s Settings', 'neve' ),
					$post_type
				),
				array( $this, 'render_metabox_notice' ),
				Supported_Post_Types::get( 'block_editor' ),
				'side',
				'default',
				array(
					'__back_compat_meta_box' => false,
				)
			);
		}
	}

	/**
	 * Detect if is gutenberg editor.
	 *
	 * @return bool
	 */
	private function is_gutenberg_active() {
		global $current_screen;
		if ( method_exists( $current_screen, 'is_block_editor' ) ) {
			return $current_screen->is_block_editor();
		}
		return false;
	}

	/**
	 * The metabox content.
	 */
	public function render_metabox() {
		$this->render_controls();
	}

	/**
	 * Render the metabox notice.
	 */
	public function render_metabox_notice() {
		echo '<div class="nv-meta-notice-wrapper">';
		echo '<h4>' . esc_html__( 'Page Settings are now accessible from the top bar', 'neve' ) . '</h4>';
		printf(
		/* translators: %1$s - Keyboard shortcut.   %2&s - svg icon */
			esc_html__( 'Click the %1$s icon in the top bar or use the keyboard shortcut ( %2$s ) to customise the layout settings for this page', 'neve' ),
			apply_filters( 'ti_wl_theme_is_localized', false ) ?
				'<span class="dashicons dashicons-hammer"></span>' :
				'<svg width="17" height="24" viewBox="0 0 17 24" fill="none" xmlns="http://www.w3.org/2000/svg">
				<path d="M4.77822 10.2133V19.3287H0.118347V0.802224C0.118347 0.712594 0.145598 0.649854 0.200099 0.614002C0.254601 0.578149 0.354519 0.622964 0.499857 0.748446L12.1359 10.2133V1.04422H16.7958V19.5976C16.7958 19.7051 16.7685 19.7724 16.714 19.7992C16.6595 19.8261 16.5596 19.7768 16.4143 19.6514L4.77822 10.2133Z"/>
				<rect x="0.118347" y="22.3334" width="16.6774" height="1.51613"/>
				</svg>',
			'<strong>SHIFT + ALT + S</strong> ' . esc_html__( 'or', 'neve' ) . ' <strong>control + option + S</strong>'
		);
		echo '</div>';
	}

	/**
	 * Enqueue scripts and styles.
	 */
	public function enqueue() {

		if ( $this->is_gutenberg_active() ) {
			return;
		}

		$screen = get_current_screen();

		if ( ! is_object( $screen ) ) {
			return;
		}

		if ( $screen->base !== 'post' ) {
			return;
		}

		wp_register_script( 'neve-metabox', NEVE_ASSETS_URL . 'js/build/all/metabox.js', array( 'jquery' ), NEVE_VERSION, true );

		wp_localize_script( 'neve-metabox', 'neveMetabox', $this->get_localization() );

		wp_enqueue_script( 'neve-metabox' );
	}

	/**
	 * Localize the Metabox script.
	 *
	 * @return array
	 */
	private function get_localization() {
		return array();
	}

	/**
	 * Order the controls by given priority.
	 */
	private function order_by_priority() {
		$order = array();

		foreach ( $this->controls as $key => $control_object ) {
			$order[ $key ] = $control_object->priority;
		}
		array_multisort( $order, SORT_ASC, $this->controls );
	}

	/**
	 * Register meta
	 */
	public function neve_register_meta() {
		$meta_sidebar_controls = apply_filters(
			'neve_sidebar_meta_controls',
			[
				[
					'id'   => 'neve_meta_sidebar',
					'type' => 'radio',
				],
				[
					'id'   => 'neve_meta_container',
					'type' => 'button-group',
				],
				[
					'id'   => 'neve_meta_enable_content_width',
					'type' => 'checkbox',
				],
				[
					'id'   => 'neve_meta_content_width',
					'type' => 'range',
				],
				[
					'id'   => 'neve_meta_title_alignment',
					'type' => 'button-group',
				],
				[
					'id'   => 'neve_meta_author_avatar',
					'type' => 'checkbox',
				],
				[
					'id'   => 'neve_post_elements_order',
					'type' => 'sortable-list',
				],
				[
					'id'   => 'neve_meta_disable_header',
					'type' => 'checkbox',
				],
				[
					'id'   => 'neve_meta_disable_footer',
					'type' => 'checkbox',
				],
				[
					'id'   => 'neve_meta_disable_title',
					'type' => 'checkbox',
				],
			]
		);
		foreach ( $meta_sidebar_controls as $control ) {
			$type = 'string';
			if ( $control['type'] === 'range' ) {
				$type = 'integer';
			}

			$post_type = '';
			if ( array_key_exists( 'post_type', $control ) ) {
				$post_type = $control['post_type'];
			}

			$meta_settings = array(
				'show_in_rest'      => true,
				'type'              => $type,
				'single'            => true,
				'sanitize_callback' => 'sanitize_text_field',
				'auth_callback'     => function () {
					return current_user_can( 'edit_posts' );
				},
			);

			register_post_meta(
				$post_type,
				$control['id'],
				$meta_settings
			);
		}
	}

	/**
	 * Register the metabox sidebar.
	 */
	public function meta_sidebar_script_enqueue() {
		global $post_type, $pagenow;

		$do_not_load_on = [ 'widgets.php', 'customize.php' ];

		// $post_type returns "page" on widgets.php and on customize.php so we need to check this separately.
		if ( in_array( $pagenow, $do_not_load_on, true ) || ! in_array( $post_type, Supported_Post_Types::get( 'block_editor' ) ) ) {
			return false;
		}

		$dependencies = ( include get_template_directory() . '/assets/apps/metabox/build/index.asset.php' );

		wp_enqueue_script(
			'neve-meta-sidebar',
			trailingslashit( get_template_directory_uri() ) . 'assets/apps/metabox/build/index.js',
			$dependencies['dependencies'],
			$dependencies['version'],
			true
		);

		if ( function_exists( 'wp_set_script_translations' ) ) {
			wp_set_script_translations( 'neve-meta-sidebar', 'neve' );
		}

		$container    = $post_type === 'post' ? Mods::get( Config::MODS_SINGLE_POST_CONTAINER_STYLE, 'contained' ) : Mods::get( Config::MODS_DEFAULT_CONTAINER_STYLE, 'contained' );
		$editor_width = Mods::get( Config::MODS_CONTAINER_WIDTH );

		$advanced_layout = Mods::get( Config::MODS_ADVANCED_LAYOUT_OPTIONS, true );

		$single_width  = $post_type === 'post' ?
			Mods::get( Config::MODS_SINGLE_CONTENT_WIDTH, $this->sidebar_layout_width_default( Config::MODS_SINGLE_CONTENT_WIDTH ) ) :
			Mods::get( Config::MODS_OTHERS_CONTENT_WIDTH, $this->sidebar_layout_width_default( Config::MODS_OTHERS_CONTENT_WIDTH ) );
		$content_width = $advanced_layout ?
			$single_width :
			Mods::get( Config::MODS_SITEWIDE_CONTENT_WIDTH, $this->sidebar_layout_width_default( Config::MODS_SITEWIDE_CONTENT_WIDTH ) );

		$editor_width = isset( $editor_width['desktop'] ) ? (int) $editor_width['desktop'] : 1170;

		$post_elements_default_order = $this->get_post_elements_default_order();
		$show_avatar                 = $this->get_author_avatar_state();
		$reading_time                = $this->get_reading_time_state();

		$post_type_details = get_post_type_object( $post_type );
		$post_type_label   = esc_html( $post_type_details->labels->singular_name );

		$localized_data = apply_filters(
			'neve_meta_sidebar_localize_filter',
			array(
				'actions'                 => array(
					'neve_meta_content_width' => array(
						'container' => $container,
						'editor'    => $editor_width,
						'content'   => $content_width,
					),
				),
				'elementsDefaultOrder'    => $post_elements_default_order,
				'avatarDefaultState'      => $show_avatar,
				'readingTimeDefaultState' => $reading_time,
				'postTypeLabel'           => $post_type_label,
				'isCoverLayout'           => Layout_Single_Post::is_cover_layout(),
			)
		);
		wp_localize_script(
			'neve-meta-sidebar',
			'metaSidebar',
			$localized_data
		);

		wp_enqueue_style(
			'neve-meta-sidebar-css', // Handle.
			trailingslashit( get_template_directory_uri() ) . 'assets/apps/metabox/build/index.css',
			array( 'wp-edit-blocks' ),
			NEVE_VERSION
		);
	}

	/**
	 * Get the value of elements order from customizer.
	 *
	 * @return string
	 */
	private function get_post_elements_default_order() {
		$default_order = $this->post_ordering();

		$content_order = get_theme_mod( 'neve_layout_single_post_elements_order', wp_json_encode( $default_order ) );
		if ( ! is_string( $content_order ) ) {
			$content_order = wp_json_encode( $default_order );
		}
		$content_order = json_decode( $content_order, true );
		if ( empty( $content_order ) ) {
			return wp_json_encode( $content_order );
		}

		$is_cover_layout  = Layout_Single_Post::is_cover_layout();
		$title_meta_index = array_search( 'title-meta', $content_order );
		if ( $title_meta_index !== false && ! $is_cover_layout ) {
			$content_order[ $title_meta_index ] = 'title';
			$next_index                         = $title_meta_index + 1;
			$content_order                      = array_merge( array_slice( $content_order, 0, $next_index, true ), array( 'meta' ), array_slice( $content_order, $next_index, null, true ) );
		}

		return wp_json_encode( $content_order );
	}

	/**
	 * Get the value of author avatar display from customizer.
	 *
	 * @return bool
	 */
	private function get_author_avatar_state() {
		$show_avatar = get_theme_mod( 'neve_author_avatar', false );
		return get_theme_mod( 'neve_single_post_author_avatar', $show_avatar );
	}

	/**
	 * Get the value of Reading Time visibility from customizer.
	 *
	 * @return bool
	 */
	private function get_reading_time_state() {
		$meta_fields = get_theme_mod( 'neve_single_post_meta_fields', self::get_default_single_post_meta_fields() );

		if ( is_string( $meta_fields ) ) {
			$meta_fields = json_decode( $meta_fields, true );
		}

		if ( ! is_array( $meta_fields ) ) {
			return false;
		}

		foreach ( $meta_fields as $args ) {
			if ( ! array_key_exists( 'slug', $args ) || ! array_key_exists( 'visibility', $args ) || $args['slug'] !== 'reading' ) {
				continue;
			}

			return $args['visibility'] === 'yes';
		}

		return false;
	}
}
PK      \XH      admin/metabox/controls_base.phpnu W+A        <?php
/**
 * Page settings metabox.
 *
 * @package Neve
 */

namespace Neve\Admin\Metabox;

use Neve\Admin\Metabox\Controls\Control_Base;

/**
 * Class Metabox_Controls_Base
 *
 * @package Neve\Admin\Metabox
 */
abstract class Controls_Base {

	/**
	 * Controls.
	 *
	 * @var array
	 */
	private $controls = array();

	/**
	 * Init function
	 */
	public function init() {
		$this->add_controls();
	}

	/**
	 * Add controls.
	 */
	abstract protected function add_controls();

	/**
	 * Add the control.
	 *
	 * @param Controls\Control_Base|Object $control the control object.
	 */
	public function add_control( $control ) {
		array_push( $this->controls, $control );
	}

	/**
	 * Get the controls.
	 *
	 * @return array
	 */
	public function get_controls() {
		return $this->controls;
	}
}
PK      \!o  o     admin/metabox/controls/radio.phpnu W+A        <?php
/**
 * Metabox radio button control.
 *
 * @package Neve\Admin\Metabox\Controls
 */

namespace Neve\Admin\Metabox\Controls;

/**
 * Class Radio
 *
 * @package Neve\Admin\Metabox\Controls
 */
class Radio extends Control_Base {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'radio';

	/**
	 * Render control.
	 *
	 * @return void
	 */
	public function render_content( $post_id ) {
		$selected = $this->get_value( $post_id );
		$markup   = '<style>#neve-page-settings label{ display: block; margin-bottom: 5px;}</style>';

		$markup .= '<p>';
		foreach ( $this->settings['choices'] as $value => $choice ) {
			$markup .= '<label for="' . esc_attr( $this->id . '_' . $value ) . '">';
			$markup .= '<input type="radio" value="' . esc_attr( $value ) . '" id="' . esc_attr( $this->id . '_' . $value ) . '" name="' . esc_attr( $this->id ) . '"';
			$markup .= checked( $selected ? $selected : $this->settings['default'], $value, false );
			$markup .= '/>';
			$markup .= esc_html( $choice ) . '</label>';
		}
		$markup .= '</p>';

		echo $markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}
}
PK      \%,    #  admin/metabox/controls/checkbox.phpnu W+A        <?php
/**
 * Metabox radio button control.
 *
 * @package Neve\Admin\Metabox\Controls
 */

namespace Neve\Admin\Metabox\Controls;

/**
 * Class Checkbox
 *
 * @package Neve\Admin\Metabox\Controls
 */
class Checkbox extends Control_Base {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'checkbox';

	/**
	 * Render control.
	 *
	 * @return void
	 */
	public function render_content( $post_id ) {
		$value  = $this->get_value( $post_id );
		$markup = '';

		$markup .= '<p>';
		$markup .= '<div class="checkbox-toggle-wrap">';
		$markup .= '<label for="' . esc_attr( $this->id ) . '">';
		$markup .= '<input type="checkbox" id="' . esc_attr( $this->id ) . '" name="' . esc_attr( $this->id ) . '" ';
		if ( $value === 'on' ) {
			$markup .= ' checked="checked" ';
		}
		$markup .= '/>';
		$markup .= esc_html( $this->settings['input_label'] ) . '</label>';
		$markup .= '</div>';
		$markup .= '</p>';

		echo $markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}
}
PK      \؝hf    $  admin/metabox/controls/separator.phpnu W+A        <?php
/**
 * Metabox separator.
 *
 * @package Neve\Admin\Metabox\Controls
 */

namespace Neve\Admin\Metabox\Controls;

/**
 * Class Separator
 *
 * @package Neve\Admin\Metabox\Controls
 */
class Separator extends Control_Base {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'separator';

	/**
	 * Render control.
	 *
	 * @return void
	 */
	public function render_content( $post_id ) {
		echo '<hr/>';
	}
}
PK      \`L?       admin/metabox/controls/range.phpnu W+A        <?php
/**
 * Metabox range control.
 *
 * @package Neve\Admin\Metabox\Controls
 */

namespace Neve\Admin\Metabox\Controls;

/**
 * Class Range
 *
 * @package Neve\Admin\Metabox\Controls
 */
class Range extends Control_Base {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'range';

	/**
	 * Render control.
	 *
	 * @return void
	 */
	public function render_content( $post_id ) {
		$value      = $this->get_value( $post_id );
		$class      = 'neve-range-input ';
		$dependency = '';
		if ( $this->settings['hidden'] === true ) {
			$class .= ' neve-hidden';
		}
		if ( isset( $this->settings['depends_on'] ) ) {
			$dependency .= ' data-depends=' . esc_attr( $this->settings['depends_on'] );
			$class      .= ' neve-dependent';
		}

		$markup = '
<style>
.neve-range-input{display: flex; align-items: center;}
.neve-range-input .nv-range{flex-grow: 1; margin-right: 5px;}
.neve-range-input .nv-number{min-width: 0; margin-left: auto;}
.neve-range-input.neve-hidden{display: none;}
</style>';

		$markup .= '<p class="' . esc_attr( $class ) . '" ' . esc_attr( $dependency ) . ' >';
		$markup .= '<input type="range"
		value="' . esc_attr( $value ) . '"
		id="' . esc_attr( $this->id ) . '-range"
		class="nv-range"
		name="' . esc_attr( $this->id ) . '"
		min="' . esc_attr( $this->settings['min'] ) . '"
		max="' . esc_attr( $this->settings['max'] ) . '" >';
		$markup .= '<input type="number"
		value="' . esc_attr( $value ) . '"
		id="' . esc_attr( $this->id ) . '"
		class="nv-number"
		name="' . esc_attr( $this->id ) . '"
		min="' . esc_attr( $this->settings['min'] ) . '"
		max="' . esc_attr( $this->settings['max'] ) . '" >';
		$markup .= '</p>';

		echo $markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}


}
PK      \:    '  admin/metabox/controls/control_base.phpnu W+A        <?php
/**
 * Abstract class to be used as base for metabox controls.
 *
 * @package Neve\Admin\Metabox\Controls
 */

namespace Neve\Admin\Metabox\Controls;

/**
 * Class Control_Base
 *
 * @package Neve\Admin\Metabox\Controls
 */
abstract class Control_Base {

	/**
	 * Control id.
	 *
	 * @var string
	 */
	public $id = '';

	/**
	 * Control settings.
	 *
	 * @var array
	 */
	public $settings = array();

	/**
	 * Control type [MUST BE DEFINED]
	 *
	 * @var string
	 */
	public $type = '';

	/**
	 * Control Priority
	 *
	 * @var int
	 */
	public $priority = 10;

	/**
	 * Control_Base constructor.
	 *
	 * @param string $id       control id.
	 * @param array  $settings control settings.
	 */
	public function __construct( $id, $settings ) {
		if ( empty( $this->type ) ) {
			return;
		}
		$this->id       = $id;
		$this->settings = $settings;
		if ( isset( $settings['priority'] ) ) {
			$this->priority = $settings['priority'];
		}
	}

	/**
	 * Render function for the control.
	 */
	public function render( $post_id ) {
		if ( empty( $this->type ) ) {
			return;
		}
		if ( ! $this->should_render() ) {
			return;
		}

		$this->render_label();
		$this->render_content( $post_id );
		wp_nonce_field( 'neve_meta_box_nonce', 'neve_meta_box_process' );
	}

	/**
	 * Determine if a control should be visible or not.
	 *
	 * @return bool
	 */
	private function should_render() {
		if ( ! array_key_exists( 'active_callback', $this->settings ) ) {
			return true;
		}

		if ( empty( $this->settings['active_callback'] ) ) {
			return true;
		}

		$object = $this->settings['active_callback'][0];
		$method = $this->settings['active_callback'][1];
		if ( method_exists( $object, $method ) ) {
			return $object->$method();
		}

		return true;
	}

	/**
	 * Render control label.
	 *
	 * @return void
	 */
	protected function render_label() {
		$label = array_key_exists( 'label', $this->settings ) ? $this->settings['label'] : '';

		if ( empty( $label ) ) {
			return;
		}

		$control_label = '';

		$control_label .= '<p class="post-attributes-label-wrapper">';
		$control_label .= '<span class="post-attributes-label">' . esc_html( $label ) . '</span>';
		$control_label .= '</p>';

		echo wp_kses_post( $control_label );
	}

	/**
	 * Render control.
	 *
	 * @return void
	 */
	abstract public function render_content( $post_id );

	/**
	 * Save control data.
	 *
	 * @param int $post_id Post id.
	 *
	 * @return void
	 */
	final public function save( $post_id ) {
		if ( ! isset( $_POST['neve_meta_box_process'] ) ) {
			return;
		}
		if ( ! wp_verify_nonce( sanitize_key( $_POST['neve_meta_box_process'] ), 'neve_meta_box_nonce' ) ) {
			return;
		}
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
			return;
		}
		if ( ! current_user_can( 'edit_posts' ) ) {
			return;
		}
		if ( isset( $_POST[ $this->id ] ) ) {
			$value = $this->sanitize_value( wp_unslash( $_POST[ $this->id ] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
			// Remove post meta on default.
			if ( $value === $this->settings['default'] && $this->id !== 'neve_meta_content_width' ) {
				delete_post_meta( $post_id, $this->id );

				return;
			}
			// Update post meta on other values.
			update_post_meta( $post_id, $this->id, $value );

			return;
		} else {
			if ( $this->id === 'neve_meta_enable_content_width' ) {
				update_post_meta( $post_id, 'neve_meta_enable_content_width', 'off' );
			} else {
				delete_post_meta( $post_id, $this->id );
			}
			return;
		}
	}

	/**
	 * Sanitize the value.
	 *
	 * @param int|string $value the value to sanitize.
	 *
	 * @return int|string
	 */
	protected function sanitize_value( $value ) {
		switch ( $this->type ) {
			case 'radio':
				$allowed_values = $this->settings['choices'];
				if ( ! array_key_exists( $value, $allowed_values ) ) {
					return esc_html( $this->settings['default'] );
				}

				return sanitize_text_field( $value );
			case 'checkbox':
				$allowed_values = array( 'on', 'off' );
				if ( ! in_array( $value, $allowed_values, true ) ) {
					return esc_html( $this->settings['default'] );
				}

				return sanitize_text_field( $value );
			case 'range':
				return absint( $value );
			case 'input':
				return sanitize_text_field( $value );
			case 'separator':
			default:
				break;
		}

		return sanitize_text_field( $value );
	}

	/**
	 * Get the value.
	 *
	 * @param int $post_id the post id.
	 *
	 * @return mixed
	 */
	final protected function get_value( $post_id ) {
		$value = get_post_meta( $post_id, $this->id, true );

		return ! empty( $value ) ? $value : $this->settings['default'];
	}
}
PK      \76      admin/troubleshoot/main.phpnu W+A        <?php
/**
 * WordPress troubleshooting module.
 *
 * @package Neve
 */

namespace Neve\Admin\Troubleshoot;

/**
 * Class Main
 *
 * @package Neve\Admin\Troubleshoot
 */
final class Main {
	/**
	 * Init function
	 *
	 * @return void
	 */
	public function init() {
		add_filter( 'debug_information', [ $this, 'neve_add_debug_info' ] );

		add_filter( 'site_status_tests', [ $this, 'neve_add_tests' ] );
	}

	/**
	 * Register Neve Accordion on Debug Info page.
	 *
	 * @param array $debug_info Debug List.
	 *
	 * @return array
	 */
	public function neve_add_debug_info( $debug_info ) {
		$custom_customizer_css = wp_get_custom_css();

		$debug_info['neve'] = array(
			'label'  => __( 'Neve', 'neve' ),
			'fields' => array(
				'api'            => array(
					'label'   => __( 'API connectivity', 'neve' ),
					'value'   => $this->test_api_connectivity() ? __( 'Yes', 'neve' ) : __( 'No', 'neve' ) . ' ' . get_transient( 'neve_troubleshoot_api_reason' ),
					'private' => false,
				),
				'child'          => array(
					'label'   => __( 'Child theme files', 'neve' ),
					'value'   => is_child_theme() ? $this->list_files() : __( 'No', 'neve' ),
					'private' => false,
				),
				'customizer_css' => array(
					'label'   => __( 'Customizer Custom CSS', 'neve' ),
					'value'   => empty( $custom_customizer_css ) ? __( 'No', 'neve' ) : $custom_customizer_css,
					'private' => false,
				),
			),
		);

		return $debug_info;
	}

	/**
	 * List active theme files
	 *
	 * @return string
	 */
	public function list_files() {
		return implode( ",\n\r", list_files( get_stylesheet_directory(), 2 ) );
	}

	/**
	 * Register tests for the Status Page
	 *
	 * @param array $tests List of tests.
	 *
	 * @return array
	 */
	public function neve_add_tests( $tests ) {
		$tests['direct']['neve_api_test'] = array(
			'label' => __( 'Neve', 'neve' ) . ' ' . __( 'API connectivity', 'neve' ),
			'test'  => [ $this, 'neve_api_test' ],
		);

		return $tests;
	}

	/**
	 * Neve API test pretty response
	 *
	 * @return array
	 */
	public function neve_api_test() {
		$result = array(
			'label'       => __( 'Neve', 'neve' ) . ' ' . __( 'API connectivity', 'neve' ),
			'status'      => 'good',
			'badge'       => array(
				'label' => __( 'Neve', 'neve' ),
				'color' => 'blue',
			),
			'description' => sprintf(
				'<p>%s</p>',
				/* translators: Theme Name */
				sprintf( __( 'API for %s is reachable.', 'neve' ), __( 'Neve', 'neve' ) )
			),
			'actions'     => '',
			'test'        => 'neve_api_test',
		);

		if ( ! $this->test_api_connectivity() ) {
			$result['status']         = 'critical';
			$result['label']          = __( 'Can not connect to API', 'neve' );
			$result['badge']['color'] = 'red';
			$result['description']    = sprintf(
				'<p>%s</p>',
				/* translators: Theme Name */
				sprintf( __( 'API for %s is reachable on your site.', 'neve' ), __( 'Neve', 'neve' ) )
			);
		}

		return $result;
	}

	/**
	 * Test API connectivity to Themeisle
	 *
	 * @return bool
	 */
	public function test_api_connectivity() {
		$transient = get_transient( 'neve_troubleshoot_api_response' );
		if ( $transient !== false ) {
			return ( $transient === 'yes' );
		}
		$response = neve_safe_get( 'https://api.themeisle.com/health' );
		if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
			$reason = is_wp_error( $response ) ? $response->get_error_message() : $response['response']['message'];
			set_transient( 'neve_troubleshoot_api_reason', $reason, 10 * MINUTE_IN_SECONDS );
			set_transient( 'neve_troubleshoot_api_response', 'no', 10 * MINUTE_IN_SECONDS );

			return false;
		}
		set_transient( 'neve_troubleshoot_api_reason', '', 10 * MINUTE_IN_SECONDS );
		set_transient( 'neve_troubleshoot_api_response', 'yes', 10 * MINUTE_IN_SECONDS );

		return true;
	}
}
PK      \$B    7  admin/dashboard/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \}    %  admin/dashboard/changelog_handler.phpnu W+A        <?php
/**
 * Changleog Handler
 *
 * Handles parsing for Changelog files.
 *
 * @package Neve\Admin\Dashboard
 */

namespace Neve\Admin\Dashboard;

/**
 * Class Changelog_Handler
 *
 * @package Neve\Admin\Dashboard
 */
class Changelog_Handler {
	/**
	 * Get the parsed changelog.
	 *
	 * @param string $changelog_path the changelog path.
	 *
	 * @return array
	 */
	public function get_changelog( $changelog_path ) {

		if ( ! is_file( $changelog_path ) ) {
			return [];
		}

		if ( ! WP_Filesystem() ) {
			return [];
		}

		return $this->parse_changelog( $changelog_path );
	}

	/**
	 * Parse the changelog file.
	 *
	 * @param string $changelog_path the changelog path.
	 *
	 * @return array
	 */
	private function parse_changelog( $changelog_path ) {
		WP_Filesystem();
		global $wp_filesystem;
		$changelog = $wp_filesystem->get_contents( $changelog_path );
		if ( is_wp_error( $changelog ) ) {
			$changelog = '';
		}
		$changelog       = explode( PHP_EOL, $changelog );
		$releases        = [];
		$release_count   = 0;
		$current_section = ''; // Holds the current section ('Improvements', 'Bug Fixes', etc.)

		foreach ( $changelog as $changelog_line ) {
			if ( strpos( $changelog_line, '**Changes:**' ) !== false || empty( $changelog_line ) ) {
				continue;
			}
			if ( substr( ltrim( $changelog_line ), 0, 3 ) === '###' && ! preg_match( '/###\s?(New Features|Bug Fixes)/', $changelog_line ) ) {
				// Extract version and date
				preg_match( '/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $changelog_line, $found_v );
				preg_match( '/[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}/', $changelog_line, $found_d );
				if ( isset( $found_v[0] ) && isset( $found_d[0] ) ) {
					$release_count++;
					$releases[ $release_count ] = array(
						'version' => $found_v[0],
						'date'    => $found_d[0],
					);
				}

				$current_section = '';
				continue;
			}

			// Check for the new headers 'New Features', 'Bug Fixes', etc.
			if ( preg_match( '/###\s?(New Features|Bug Fixes)/', $changelog_line, $section_matches ) ) {
				$current_section = strtolower( $section_matches[1] );
				continue;
			}

			// Extracting items based on the new changelog structure
			if ( $current_section === 'bug fixes' ) {
				$changelog_line                        = preg_replace( '/-\s?\*\*(.*?):\*\*/', '', $changelog_line );
				$changelog_line                        = $this->parse_md_and_clean( $changelog_line );
				$releases[ $release_count ]['fixes'][] = $changelog_line;
				continue;
			}

			if ( $current_section === 'new features' ) {
				$changelog_line                           = preg_replace( '/-\s?\*\*(.*?):\*\*/', '', $changelog_line );
				$changelog_line                           = $this->parse_md_and_clean( $changelog_line );
				$releases[ $release_count ]['features'][] = $changelog_line;
				continue;
			}

			// Legacy structure handling for feats and fixes
			if ( preg_match( '/[*|-]?\s?(\[fix]|\[Fix]|fix|Fix)[:]?\s?(\b|(?=\[))/', $changelog_line ) ) {
				$changelog_line                        = preg_replace( '/[*|-]?\s?(\[fix]|\[Fix]|fix|Fix)[:]?\s?(\b|(?=\[))/', '', $changelog_line );
				$releases[ $release_count ]['fixes'][] = $this->parse_md_and_clean( $changelog_line );
				continue;
			}

			if ( preg_match( '/[*|-]?\s?(\[feat]|\[Feat]|feat|Feat)[:]?\s?(\b|(?=\[))/', $changelog_line ) ) {
				$changelog_line                           = preg_replace( '/[*|-]?\s?(\[feat]|\[Feat]|feat|Feat)[:]?\s?(\b|(?=\[))/', '', $changelog_line );
				$releases[ $release_count ]['features'][] = $this->parse_md_and_clean( $changelog_line );
				continue;
			}

			$changelog_line = $this->parse_md_and_clean( $changelog_line );
			if ( empty( $changelog_line ) ) {
				continue;
			}
			if ( ! isset( $releases[ $release_count ]['tweaks'] ) ) {
				$releases[ $release_count ]['tweaks'] = [];
			}
			$releases[ $release_count ]['tweaks'][] = $changelog_line;
		}

		return array_values( $releases );
	}

	/**
	 * Parse markdown links, convert bold markers (**) to <b> tags, and cleanup string.
	 *
	 * @param string $string changelog line.
	 *
	 * @return string
	 */
	private function parse_md_and_clean( $string ) {
		// Drop spaces, starting lines | asterisks.
		$string = trim( $string );
		$string = ltrim( $string, '*' );
		$string = ltrim( $string, '-' );

		// Replace markdown links with <a> tags.
		$string = preg_replace_callback(
			'/\[(.*?)]\((.*?)\)/',
			function ( $matches ) {
				return '<a href="' . $matches[2] . '" target="_blank" rel="noopener"><i class="dashicons dashicons-external"></i>' . $matches[1] . '</a>';
			},
			htmlspecialchars( $string )
		);

		// Convert bold markdown (**text**) to <b>text</b>.
		$string = preg_replace( '/\*\*(.*?)\*\*/', '<b>$1</b>', $string );

		return $string;
	}

}
PK      \Uk^    !  admin/dashboard/plugin_helper.phpnu W+A        <?php
/**
 * Plugin Action Helper
 *
 * @package Neve\Admin\Dashboard
 */

namespace Neve\Admin\Dashboard;

/**
 * Class Plugin_Helper
 *
 * @package Neve\Admin\Dashboard
 */
class Plugin_Helper {
	/**
	 * Check plugin state.
	 *
	 * @param string $slug - plugin slug.
	 *
	 * @return string
	 */
	public function get_plugin_state( $slug ) {
		$plugin_link_suffix = $this->get_plugin_path( $slug );
		if ( file_exists( WP_PLUGIN_DIR . '/' . $plugin_link_suffix ) ) {
			return is_plugin_active( $plugin_link_suffix ) ? 'deactivate' : 'activate';
		}

		return 'install';
	}

	/**
	 * Get plugin path based on plugin slug.
	 *
	 * @param string $slug - plugin slug.
	 *
	 * @return string
	 */
	public function get_plugin_path( $slug ) {
		switch ( $slug ) {
			case 'mailin':
				return $slug . '/sendinblue.php';
			case 'wpforms-lite':
				return $slug . '/wpforms.php';
			case 'intergeo-maps':
			case 'visualizer':
			case 'translatepress-multilingual':
				return $slug . '/index.php';
			case 'beaver-builder-lite-version':
				return $slug . '/fl-builder.php';
			case 'adblock-notify-by-bweb':
				return $slug . '/adblock-notify.php';
			case 'feedzy-rss-feeds':
				return $slug . '/feedzy-rss-feed.php';
			case 'wp-cloudflare-page-cache':
				return $slug . '/wp-cloudflare-super-page-cache.php';
			default:
				return $slug . '/' . $slug . '.php';
		}
	}

	/**
	 * Call plugin api
	 *
	 * @param string $slug plugin slug.
	 *
	 * @return object
	 */
	public function get_plugin_details( $slug ) {
		include_once ABSPATH . 'wp-admin/includes/plugin-install.php';

		return plugins_api(
			'plugin_information',
			array(
				'slug'   => $slug,
				'fields' => array(
					'downloaded'        => false,
					'rating'            => false,
					'description'       => false,
					'short_description' => true,
					'donate_link'       => false,
					'tags'              => false,
					'sections'          => false,
					'homepage'          => false,
					'added'             => false,
					'last_updated'      => false,
					'compatibility'     => false,
					'tested'            => false,
					'requires'          => false,
					'downloadlink'      => false,
					'icons'             => false,
					'banners'           => true,
				),
			)
		);
	}

	/**
	 * Get Plugin Action link.
	 *
	 * @param string $slug plugin slug.
	 * @param string $action action [activate, deactivate].
	 * @return string
	 */
	public function get_plugin_action_link( $slug, $action = 'activate' ) {
		if ( ! in_array( $action, [ 'activate', 'deactivate' ] ) ) {
			return '';
		}

		return add_query_arg(
			array(
				'action'        => $action,
				'plugin'        => rawurlencode( $this->get_plugin_path( $slug ) ),
				'plugin_status' => 'all',
				'paged'         => '1',
				'_wpnonce'      => wp_create_nonce( $action . '-plugin_' . $this->get_plugin_path( $slug ) ),
			),
			esc_url( 'plugins.php' )
		);
	}

	/**
	 * Get plugin version.
	 *
	 * @param string $slug plugin slug.
	 * @return string | bool
	 */
	public function get_plugin_version( $slug, $default = false ) {
		$plugin_file = $this->get_plugin_path( $slug );
		if ( ! is_plugin_active( $plugin_file ) ) {
			return $default;
		}

		$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_file );
		if ( ! array_key_exists( 'Version', $plugin_data ) ) {
			return $default;
		}
		return $plugin_data['Version'];
	}

	/**
	 * Check if current plugin is activated network-wide
	 *
	 * @param string $slug plugin slug.
	 * @return bool
	 */
	public function get_is_network_wide( $slug ) {
		if ( ! is_multisite() ) {
			return false;
		}
		$plugin_file = $this->get_plugin_path( $slug );
		return is_plugin_active_for_network( $plugin_file );
	}
}
PK      \bey}  y}    admin/dashboard/main.phpnu W+A        <?php
/**
 * Main class of the Neve Dashboard
 *
 * @package neve
 */

namespace Neve\Admin\Dashboard;

use Neve\Core\Limited_Offers;
use Neve\Core\Theme_Info;
/**
 * Class Main
 *
 * @package Neve\Admin\Dashboard
 */
class Main {

	use Theme_Info;
	/**
	 * Changelog Handler.
	 *
	 * @var Changelog_Handler
	 */
	private $cl_handler;
	/**
	 * Plugin Helper instance.
	 *
	 * @var Plugin_Helper
	 */
	private $plugin_helper;
	/**
	 * Current theme args.
	 *
	 * @var array
	 */
	private $theme_args = [];

	/**
	 * Useful plugins array.
	 *
	 * @var array
	 */
	private $useful_plugins = [
		'optimole-wp',
		'wp-landing-kit',
		'otter-blocks',
		'wp-cloudflare-page-cache',
		'templates-patterns-collection',
		'themeisle-companion',
		'translatepress-multilingual',
		'amp',
	];

	/**
	 * Plugins Cache key.
	 *
	 * @var string
	 */
	private $plugins_cache_key = 'neve_dash_useful_plugins';

	/**
	 * Plugins Cache Hash key.
	 *
	 * @var string
	 */
	private $plugins_cache_hash_key = 'neve_dash_useful_plugins_hash';

	/**
	 * Main constructor.
	 */
	public function __construct() {
		$this->plugin_helper = new Plugin_Helper();
		$this->cl_handler    = new Changelog_Handler();
	}

	/**
	 * Run WordPress attached to actions.
	 */
	public function init() {

		$this->setup_config();
		add_action( 'init', [ $this, 'setup_config' ] );
		add_action( 'admin_menu', [ $this, 'register' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
		add_action( 'init', array( $this, 'register_settings' ) );
		add_action( 'init', array( $this, 'register_about_page' ), 1 );
	}

	/**
	 * Add the about page with respect to the white label settings.
	 *
	 * @return void
	 */
	public function register_about_page() {
		$theme         = wp_get_theme();
		$filtered_name = apply_filters( 'ti_wl_theme_name', $theme->__get( 'Name' ) );
		$slug          = $theme->__get( 'stylesheet' );

		if ( empty( $slug ) || empty( $filtered_name ) ) {
			return;
		}

		// We check if the name is different from the filtered name,
		// if it is, the whitelabel is in use and we should not add the about page.
		// this check allows for child themes to use the about page.
		if ( $filtered_name !== $theme->__get( 'Name' ) ) {
			return;
		}

		add_filter(
			'neve_about_us_metadata',
			function () use ( $filtered_name ) {

				return [
					// Top-level page in the dashboard sidebar
					'location'         => 'neve-welcome',
					// Logo to display on the page
					'logo'             => get_template_directory_uri() . '/assets/img/dashboard/logo.svg',
					// Condition to show or hide the upgrade menu in the sidebar
					'has_upgrade_menu' => ! defined( 'NEVE_PRO_VERSION' ),
					// Add predefined product pages to the about page.
					'product_pages'    => [ 'otter-page' ],
					// Upgrade menu item link & text
					'upgrade_link'     => tsdk_utmify( esc_url( 'https://themeisle.com/themes/neve/upgrade/' ), 'aboutfilter', 'nevedashboard' ),
					'upgrade_text'     => __( 'Upgrade', 'neve' ) . ' ' . $filtered_name,
				];
			}
		);
	}

	/**
	 * Register Logger Setting
	 */
	public function register_settings() {
		register_setting(
			'neve_settings',
			'neve_logger_flag',
			[
				'type'         => 'string',
				'show_in_rest' => true,
				'default'      => '',
			]
		);
	}

	/**
	 * Setup the class props based on current theme.
	 */
	public function setup_config() {
		$theme = wp_get_theme();

		$this->theme_args['name']        = apply_filters( 'ti_wl_theme_name', $theme->__get( 'Name' ) );
		$this->theme_args['template']    = $theme->get( 'Template' );
		$this->theme_args['version']     = $theme->__get( 'Version' );
		$this->theme_args['description'] = apply_filters( 'ti_wl_theme_description', $theme->__get( 'Description' ) );
		$this->theme_args['slug']        = $theme->__get( 'stylesheet' );
	}

	/**
	 * Register theme options page.
	 *
	 * @return void
	 */
	public function register() {
		$theme = $this->theme_args;

		if ( empty( $theme['name'] ) || empty( $theme['slug'] ) ) {
			return;
		}

		$theme_page = ! empty( $theme['template'] ) ? $theme['template'] . '-welcome' : $theme['slug'] . '-welcome';

		$icon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjQuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzMiAzMiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzIgMzI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTAsMS42QzAsMC43LDAuNywwLDEuNiwwaDI4LjhDMzEuMywwLDMyLDAuNywzMiwxLjZWMzBjMCwwLjktMC43LDEuNi0xLjYsMS42SDEuNkMwLjcsMzEuNSwwLDMwLjgsMCwzMFYxLjZ6CgkgTTEzLDE1Ljh2Ny41SDkuMVY4YzAtMC4xLDAtMC4xLDAuMS0wLjJjMCwwLDAuMSwwLDAuMiwwLjFsOS42LDcuOFY4LjJoMy44djE1LjJjMCwwLjEsMCwwLjEtMC4xLDAuMmMwLDAtMC4xLDAtMC4yLTAuMUwxMywxNS44egoJIE0yMi44LDI1LjdIOS4xVjI3aDEzLjdWMjUuN3oiLz4KPC9zdmc+Cg==';
		if ( $theme['name'] !== 'Neve' ) {
			$icon = 'dashicons-admin-appearance';
		}
		$neve_icon  = apply_filters( 'neve_menu_icon', $icon );
		$priority   = apply_filters( 'neve_menu_priority', 59 );  // The position of the menu item, 60 is the position of the Appearance menu.
		$capability = 'manage_options';

		// Place a theme page in the Appearance menu, for older versions of Neve Pro or TPC. to maintain backwards compatibility.
		if (
			( defined( 'NEVE_PRO_VERSION' ) && version_compare( NEVE_PRO_VERSION, '2.6.1', '<=' ) ) ||
			( defined( 'TIOB_VERSION' ) && version_compare( TIOB_VERSION, '1.1.38', '<=' ) )
		) {
			add_theme_page(
				/* translators: %s - Theme name */
				sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ),
				/* translators: %s - Theme name */
				sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ),
				$capability,
				'admin.php?page=neve-welcome'
			);
		}

		add_menu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_menu_page
			wp_kses_post( $theme['name'] ),
			wp_kses_post( $theme['name'] ),
			$capability,
			$theme_page,
			[ $this, 'render' ],
			$neve_icon, // The URL to the icon to be used for this menu
			$priority
		);

		// Add Dashboard submenu. Same slug as parent to allow renaming the automatic submenu that is added.
		add_submenu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_submenu_page
			$theme_page,
			/* translators: %s - Theme name */
			sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ),
			/* translators: %s - Theme name */
			sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ),
			$capability,
			$theme_page,
			[ $this, 'render' ]
		);

		$this->copy_customizer_page( $theme_page );

		if ( ! defined( 'NEVE_PRO_VERSION' ) || 'valid' !== apply_filters( 'product_neve_license_status', false ) ) {
			// Add Custom Layout submenu for upsell.
			add_submenu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_submenu_page
				$theme_page,
				__( 'Custom Layouts', 'neve' ),
				__( 'Custom Layouts', 'neve' ),
				$capability,
				'admin.php?page=neve-welcome#custom-layouts'
			);
		}
	}

	/**
	 * Copy the customizer page to the dashboard.
	 *
	 * @param string $theme_page The theme page slug.
	 *
	 * @return void
	 */
	private function copy_customizer_page( $theme_page ) {
		global $submenu;
		if ( ! isset( $submenu['themes.php'] ) ) {
			return;
		}
		$themes_menu = $submenu['themes.php'];
		if ( empty( $themes_menu ) ) {
			return;
		}
		$customize_pos = array_search( 'customize', array_column( $themes_menu, 1 ) );
		if ( false === $customize_pos ) {
			return;
		}
		$themes_page_keys = array_keys( $themes_menu );
		if ( ! isset( $themes_page_keys[ $customize_pos ] ) ) {
			return;
		}

		$customizer_menu_item = array_splice( $themes_menu, $customize_pos, 1 );
		$customizer_menu_item = reset( $customizer_menu_item );
		if ( empty( $customizer_menu_item ) ) {
			return;
		}

		add_submenu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_submenu_page
			$theme_page,
			$customizer_menu_item[0],
			$customizer_menu_item[0],
			'manage_options',
			'customize.php'
		);
	}

	/**
	 * Render the application stub.
	 *
	 * @return void
	 */
	public function render() {
		echo '<div id="neve-dashboard"></div>';
	}

	/**
	 * Load css and scripts for the about page
	 */
	public function enqueue() {
		$screen = get_current_screen();
		if ( ! isset( $screen->id ) ) {
			return;
		}

		$theme      = $this->theme_args;
		$theme_page = ! empty( $theme['template'] ) ? $theme['template'] . '-welcome' : $theme['slug'] . '-welcome';

		if ( $screen->id !== 'toplevel_page_' . $theme_page ) {
			return;
		}

		$build_path   = get_template_directory_uri() . '/assets/apps/dashboard/build/';
		$dependencies = ( include get_template_directory() . '/assets/apps/dashboard/build/dashboard.asset.php' );

		wp_register_style( 'neve-dash-style', $build_path . 'style-dashboard.css', [ 'wp-components', 'neve-components' ], $dependencies['version'] );
		wp_style_add_data( 'neve-dash-style', 'rtl', 'replace' );
		wp_enqueue_style( 'neve-dash-style' );
		wp_register_script( 'neve-dash-script', $build_path . 'dashboard.js', array_merge( $dependencies['dependencies'], [ 'updates' ] ), $dependencies['version'], true );
		wp_localize_script( 'neve-dash-script', 'neveDash', apply_filters( 'neve_dashboard_page_data', $this->get_localization() ) );
		wp_enqueue_script( 'neve-dash-script' );

		if ( function_exists( 'wp_set_script_translations' ) ) {
			wp_set_script_translations( 'neve-dash-script', 'neve' );
		}
	}

	/**
	 * Get localization data for the dashboard script.
	 *
	 * @return array
	 */
	private function get_localization() {

		$offer = new Limited_Offers();

		$old_about_config  = apply_filters( 'ti_about_config_filter', [ 'useful_plugins' => true ] );
		$theme_name        = apply_filters( 'ti_wl_theme_name', $this->theme_args['name'] );
		$plugin_name       = apply_filters( 'ti_wl_plugin_name', 'Neve Pro' );
		$plugin_name_addon = apply_filters( 'ti_wl_plugin_name', 'Neve Pro Addon' );
		$data              = [
			'nonce'                   => wp_create_nonce( 'wp_rest' ),
			'version'                 => 'v' . NEVE_VERSION,
			'assets'                  => get_template_directory_uri() . '/assets/img/dashboard/',
			'hasOldPro'               => (bool) ( defined( 'NEVE_PRO_VERSION' ) && version_compare( NEVE_PRO_VERSION, '1.1.11', '<' ) ),
			'isRTL'                   => is_rtl(),
			'isValidLicense'          => $this->has_valid_addons(),
			'notifications'           => $this->get_notifications(),
			'customizerShortcuts'     => $this->get_customizer_shortcuts(),
			'plugins'                 => $this->get_useful_plugins(),
			'featureData'             => $this->get_free_pro_features(),
			'showFeedbackNotice'      => $this->should_show_feedback_notice(),
			'allfeaturesNeveProURL'   => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'seeallfeatures', 'freevspropage' ),
			'startSitesgetNeveProURL' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'welcomestartersitescard', 'nevedashboard' ),
			'customLayoutsNeveProURL' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'customlayoutscard', 'nevedashboard' ),
			'upgradeURL'              => apply_filters( 'neve_upgrade_link_from_child_theme_filter', tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'getpronow', 'freevspropage' ) ),
			'supportURL'              => esc_url( 'https://wordpress.org/support/theme/neve/' ),
			'docsURL'                 => esc_url( 'https://docs.themeisle.com/article/946-neve-doc' ),
			'codexURL'                => esc_url( 'https://codex.nevewp.com/' ),
			'strings'                 => [
				'proTabTitle'                   => wp_kses_post( $plugin_name ),
				/* translators: %s - Theme name */
				'header'                        => sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme_name ) ),
				/* translators: %s - Theme name */
				'starterSitesCardDescription'   => sprintf( __( '%s now comes with a sites library with various designs to pick from. Visit our collection of demos that are constantly being added.', 'neve' ), wp_kses_post( $theme_name ) ),
				'starterSitesCardUpsellMessage' => esc_html__( 'Upgrade to the Pro version and get instant access to all Premium Starter Sites — including Expert Sites — and much more.', 'neve' ),
				/* translators: %s - Theme name */
				'starterSitesTabDescription'    => sprintf( __( 'With %s, you can choose from multiple unique demos, specially designed for you, that can be installed with a single click. You just need to choose your favorite, and we will take care of everything else.', 'neve' ), wp_kses_post( $theme_name ) ),
				/* translators: 1 - Theme name, 2 - Cloud Templates & Patterns Collection */
				'starterSitesUnavailableActive' => sprintf( __( 'In order to be able to import any starter sites for %1$s you would need to have the %2$s plugin active.', 'neve' ), wp_kses_post( $theme_name ), 'Cloud Templates & Patterns Collection' ),
				/* translators: %s - Theme name */
				'starterSitesUnavailableUpdate' => sprintf( __( 'In order to be able to import any starter sites for %1$s you would need to have the %2$s plugin updated to the latest version.', 'neve' ), wp_kses_post( $theme_name ), 'Cloud Templates & Patterns Collection' ),
				/* translators: %s - Theme name */
				'supportCardDescription'        => sprintf( __( 'We want to make sure you have the best experience using %1$s, and that is why we have gathered all the necessary information here for you. We hope you will enjoy using %1$s as much as we enjoy creating great products.', 'neve' ), wp_kses_post( $theme_name ) ),
				/* translators: %s - Theme name */
				'docsCardDescription'           => sprintf( __( 'Need more details? Please check our full documentation for detailed information on how to use %s.', 'neve' ), wp_kses_post( $theme_name ) ),
				/* translators: %s - "Neve Pro Addon" */
				'licenseCardHeading'            => sprintf( __( '%s license', 'neve' ), wp_kses_post( $plugin_name_addon ) ),
				/* translators: %s - "Neve Pro Addon" */
				'updateOldPro'                  => sprintf( __( 'Please update %s to the latest version and then refresh this page to have access to the options.', 'neve' ), wp_kses_post( $plugin_name_addon ) ),
				/* translators: %1$s - Author link - Themeisle */
				'licenseCardDescription'        => sprintf(
				// translators: store name (Themeisle)
					__( 'Enter your license from %1$s purchase history in order to get plugin updates', 'neve' ),
					'<a target="_blank" rel="external noreferrer noopener" href="https://store.themeisle.com/">ThemeIsle<span class="components-visually-hidden">' . esc_html__( '(opens in a new tab)', 'neve' ) . '</span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" class="components-external-link__icon" role="img" aria-hidden="true" focusable="false" style="fill: #0073AA"><path d="M18.2 17c0 .7-.6 1.2-1.2 1.2H7c-.7 0-1.2-.6-1.2-1.2V7c0-.7.6-1.2 1.2-1.2h3.2V4.2H7C5.5 4.2 4.2 5.5 4.2 7v10c0 1.5 1.2 2.8 2.8 2.8h10c1.5 0 2.8-1.2 2.8-2.8v-3.6h-1.5V17zM14.9 3v1.5h3.7l-6.4 6.4 1.1 1.1 6.4-6.4v3.7h1.5V3h-6.3z"></path></svg></a>'
				),
			],
			'changelog'               => $this->cl_handler->get_changelog( get_template_directory() . '/CHANGELOG.md' ),
			'onboarding'              => [],
			'hasFileSystem'           => WP_Filesystem(),
			'hidePluginsTab'          => apply_filters( 'neve_hide_useful_plugins', ! array_key_exists( 'useful_plugins', $old_about_config ) ),
			'tpcPath'                 => defined( 'TIOB_PATH' ) ? TIOB_PATH . 'template-patterns-collection.php' : 'template-patterns-collection/template-patterns-collection.php',
			'tpcAdminURL'             => admin_url( 'admin.php?page=tiob-starter-sites' ),
			'pluginsURL'              => esc_url( admin_url( 'plugins.php' ) ),
			'getPluginStateBaseURL'   => esc_url( rest_url( '/nv/v1/dashboard/plugin-state/' ) ),
			'canInstallPlugins'       => current_user_can( 'install_plugins' ),
			'canActivatePlugins'      => current_user_can( 'activate_plugins' ),
			'deal'                    => ! defined( 'NEVE_PRO_VERSION' ) ? $offer->get_localized_data() : array(),
		];

		if ( defined( 'NEVE_PRO_PATH' ) ) {
			$installed_plugins                     = get_plugins();
			$is_otter_installed                    = array_key_exists( 'otter-pro/otter-pro.php', $installed_plugins );
			$is_sparks_installed                   = array_key_exists( 'sparks-for-woocommerce/sparks-for-woocommerce.php', $installed_plugins );
			$data['changelogPro']                  = $this->cl_handler->get_changelog( NEVE_PRO_PATH . '/CHANGELOG.md' );
			$data['isOtterProInstalled']           = $is_otter_installed;
			$data['otterProInstall']               = $is_otter_installed ? esc_url( wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=otter-pro%2Fotter-pro.php&plugin_status=all&paged=1&s' ), 'activate-plugin_otter-pro/otter-pro.php' ) ) : esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=install_otter_pro' ), 'install_otter_pro' ) );
			$data['sparksInstallActivateEndpoint'] = $is_sparks_installed ? esc_url( wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=sparks-for-woocommerce%2Fsparks-for-woocommerce.php&plugin_status=all&paged=1&s' ), 'activate-plugin_sparks-for-woocommerce/sparks-for-woocommerce.php' ) ) : esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=install_sparks' ), 'install_sparks' ) );
			$data['moduleObserver']                = array(
				'customLayouts' => array(
					'labelSubMenu' => __( 'Custom Layouts', 'neve' ),
					'linkSubMenu'  => 'edit.php?post_type=neve_custom_layouts',
				),
			);

		}

		if ( isset( $_GET['onboarding'] ) && $_GET['onboarding'] === 'yes' ) {
			$data['isOnboarding'] = true;
		}

		return $data;
	}

	/**
	 * Get the notifications for plugin and theme updates.
	 *
	 * @return array
	 */
	public function get_notifications() {
		$notifications = [];
		$slug          = 'neve';
		$themes_update = get_site_transient( 'update_themes' );

		$plugin_folder = defined( 'NEVE_PRO_BASEFILE' ) ? basename( dirname( NEVE_PRO_BASEFILE ) ) : null;
		$plugin_path   = $plugin_folder ? $plugin_folder . '/neve-pro-addon.php' : null;

		if ( isset( $themes_update->response[ $slug ] ) ) {
			$update                = $themes_update->response[ $slug ];
			$notifications['neve'] = [
				// translators: s - theme name (Neve).
				'text'   => sprintf( __( 'New theme update for %1$s! Please update to %2$s.', 'neve' ), wp_kses_post( $this->theme_args['name'] ), wp_kses_post( $update['new_version'] ) ),
				'update' => [
					'type' => 'theme',
					'slug' => $slug,
				],
				'cta'    => __( 'Update Now', 'neve' ),
				'type'   => ( $plugin_path && is_plugin_active( $plugin_path ) ) ? 'warning' : null,
			];
		}

		if ( $plugin_path ) {
			$plugins_update = get_site_transient( 'update_plugins' );
			if ( is_plugin_active( $plugin_path ) && isset( $plugins_update->response[ $plugin_path ] ) ) {
				$update                          = $plugins_update->response[ $plugin_path ];
				$notifications['neve-pro-addon'] = [
					'text'   => sprintf(
					// translators: s - Pro plugin name (Neve Pro)
						__( 'New plugin update for %1$s! Please update to %2$s.', 'neve' ),
						wp_kses_post( apply_filters( 'ti_wl_plugin_name', 'Neve Pro' ) ),
						wp_kses_post( $update->new_version )
					),
					'update' => [
						'type' => 'plugin',
						'slug' => 'neve-pro-addon',
						'path' => $plugin_path,
					],
					'cta'    => __( 'Update Now', 'neve' ),
					'type'   => 'warning',
				];
			}
		}

		if ( $this->show_branding_notice() ) {
			$notifications['branding-discount'] = [
				'text'        => sprintf(
				// translators: s - Discount Code
					__( 'From 3.3.0 we decided to remove the copyright component from the free version. You can continue using it if you rollback to 3.2.x or you can upgrade to pro, using a one time 50%% discount: %s', 'neve' ),
					wp_kses_post( '<code>NEVEBRANDING50</code>' )
				),
				'url'         => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'copyrightnotice', 'nevedashboard' ),
				'targetBlank' => true,
				'cta'         => __( 'Upgrade', 'neve' ),
			];
		}

		if ( count( $notifications ) === 1 && is_plugin_active( $plugin_path ) ) {
			foreach ( $notifications as $key => $notification ) {
				/* translators: 1 - Theme Name (Neve), 2 - Plugin Name (Neve Pro) */
				$notifications[ $key ]['text'] = sprintf( __( 'We recommend that both %1$s and %2$s are updated to the latest version to ensure optimal intercompatibility.', 'neve' ), wp_kses_post( $this->theme_args['name'] ), apply_filters( 'ti_wl_plugin_name', 'Neve Pro' ) );
			}
		}

		$notifications = apply_filters( 'neve_dashboard_notifications', $notifications );

		return $notifications;
	}

	/**
	 * Should branding notice be shown.
	 *
	 * @return bool
	 */
	private function show_branding_notice() {
		if ( $this->has_valid_addons() ) {
			return false;
		}

		return time() < strtotime( '2022-07-06' );
	}

	/**
	 * Get the Customizer Shortcut Links.
	 *
	 * @return array
	 */
	private function get_customizer_shortcuts() {
		return [
			[
				'text' => __( 'Upload Logo', 'neve' ),
				'link' => add_query_arg( [ 'autofocus[control]' => 'custom_logo' ], admin_url( 'customize.php' ) ),
			],
			[
				'text' => __( 'Set Colors', 'neve' ),
				'link' => add_query_arg( [ 'autofocus[section]' => 'neve_colors_background_section' ], admin_url( 'customize.php' ) ),
			],
			[
				'text' => __( 'Customize Fonts', 'neve' ),
				'link' => add_query_arg( [ 'autofocus[control]' => 'neve_headings_font_family' ], admin_url( 'customize.php' ) ),
			],
			[
				'text' => __( 'Layout Options', 'neve' ),
				'link' => add_query_arg( [ 'autofocus[panel]' => 'neve_layout' ], admin_url( 'customize.php' ) ),
			],
			[
				'text' => __( 'Header Options', 'neve' ),
				'link' => add_query_arg( [ 'autofocus[panel]' => 'hfg_header' ], admin_url( 'customize.php' ) ),
			],
			[
				'text' => __( 'Blog Layouts', 'neve' ),
				'link' => add_query_arg( [ 'autofocus[section]' => 'neve_blog_archive_layout' ], admin_url( 'customize.php' ) ),
			],
			[
				'text' => __( 'Footer Options', 'neve' ),
				'link' => add_query_arg( [ 'autofocus[panel]' => 'hfg_footer' ], admin_url( 'customize.php' ) ),
			],
			[
				'text' => __( 'Content / Sidebar', 'neve' ),
				'link' => add_query_arg( [ 'autofocus[section]' => 'neve_sidebar' ], admin_url( 'customize.php' ) ),
			],
		];
	}

	/**
	 * Get doc link.
	 *
	 * @param string $utm_term utm term to use for doc link.
	 * @param string $url url to doc.
	 * @return string
	 */
	private function get_doc_link( $utm_term, $url ) {

		return tsdk_utmify( $url, $utm_term, 'freevspropage' );
	}

	/**
	 * Get the pro features for the free v pro table.
	 *
	 * @return array
	 */
	private function get_free_pro_features() {
		return [
			[
				'title'       => __( 'Header/Footer builder', 'neve' ),
				'description' => __( 'Easily build your header and footer by dragging and dropping all the important elements in the real-time WordPress Customizer. More advanced options are available in PRO.', 'neve' ),
				'inLite'      => true,
				'docsLink'    => $this->get_doc_link( 'Header/Footer builder', 'https://docs.themeisle.com/category/1251-neve-header-builder' ),
			],
			[
				'title'       => __( 'Page Builder Compatibility', 'neve' ),
				'description' => __( 'Neve is fully compatible with Gutenberg, the new WordPress editor and for all of you page builder fans, Neve has full compatibility with Elementor, Beaver Builder, and all the other popular page builders.', 'neve' ),
				'inLite'      => true,
				'docsLink'    => $this->get_doc_link( 'Page Builder Compatibility', 'https://docs.themeisle.com/article/946-neve-doc#pagebuilders' ),
			],
			[
				'title'       => __( 'Header Booster', 'neve' ),
				'description' => __( 'Take the header builder to a new level with new awesome components: socials, contact, breadcrumbs, language switcher, multiple HTML, sticky and transparent menu, page header builder and many more.', 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'Header Booster', 'https://docs.themeisle.com/article/1057-header-booster-documentation' ),
			],
			[
				'title'       => __( 'Page Header Builder', 'neve' ),
				'description' => __( 'The Page Header is the horizontal area that sits directly below the header and contains the page/post title. Easily design an attractive Page Header area using our dedicated builder.', 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'Page Header Builder', 'https://docs.themeisle.com/article/1262-neve-page-header' ),
			],
			[
				'title'       => __( 'Custom Layouts', 'neve' ),
				'description' => __( 'Powerful Custom Layouts builder which allows you to easily create your own header, footer or custom content on any of the hook locations available in the theme.', 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'Custom Layouts', 'https://docs.themeisle.com/article/1062-custom-layouts-module' ),
			],
			[
				'title'       => __( 'Blog Booster', 'neve' ),
				'description' => __( 'Give a huge boost to your entire blogging experience with features specially designed for increased user experience.', 'neve' ) . ' ' . __( 'Sharing, custom article sorting, comments integrations, number of minutes needed to read an article and many more.', 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'Blog Booster', 'https://docs.themeisle.com/article/1059-blog-booster-documentation' ),
			],
			[
				'title'       => __( 'Elementor Booster', 'neve' ),
				'description' => __( 'Leverage the true flexibility of Elementor with powerful addons and templates that you can import with just one click.', 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'Elementor Booster', 'https://docs.themeisle.com/article/1063-elementor-booster-module-documentation' ),
			],
			[
				'title'       => __( 'WooCommerce Booster', 'neve' ),
				'description' => __( 'Empower your online store with awesome new features, specially designed for a smooth WooCommerce integration.', 'neve' ) . ' ' . __( 'Wishlist, quick view, video products, advanced reviews, multiple dedicated layouts and many more.', 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'WooCommerce Booster', 'https://docs.themeisle.com/article/1058-woocommerce-booster-documentation' ),
			],
			[
				'title'       => __( 'LifterLMS Booster', 'neve' ),
				'description' => __( 'Make your LifterLMS pages look stunning with our PRO design options. Specially created to help you set up your online courses with minimum customizations.', 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'LifterLMS Booster', 'https://docs.themeisle.com/article/1084-lifterlms-booster-documentation' ),
			],
			[
				'title'       => __( 'Typekit(Adobe) Fonts', 'neve' ),
				'description' => __( "The module allows for an easy way of enabling new awesome Adobe (previous Typekit) Fonts in Neve's Typography options.", 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'Typekit(Adobe) Fonts', 'https://docs.themeisle.com/article/1085-typekit-fonts-documentation' ),
			],
			[
				'title'       => __( 'White Label', 'neve' ),
				'description' => __( "For any developer or agency out there building websites for their own clients, we've made it easy to present the theme as your own.", 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'White Label', 'https://docs.themeisle.com/article/1061-white-label-module-documentation' ),
			],
			[
				'title'       => __( 'Scroll To Top', 'neve' ),
				'description' => __( 'Simple but effective module to help you navigate back to the top of the really long pages.', 'neve' ),
				'inLite'      => false,
				'docsLink'    => $this->get_doc_link( 'Scroll To Top', 'https://docs.themeisle.com/article/1060-scroll-to-top-module-documentation' ),
			],
			[
				'title'          => __( 'See all PRO features', 'neve' ),
				'presentational' => true,
			],
		];
	}

	/**
	 * Get the useful plugin data.
	 *
	 * @return array
	 */
	private function get_useful_plugins() {
		$available    = get_transient( $this->plugins_cache_key );
		$hash         = get_transient( $this->plugins_cache_hash_key );
		$current_hash = substr( md5( wp_json_encode( $this->useful_plugins ) ), 0, 5 );

		if ( $available !== false && $hash === $current_hash ) {
			$available = json_decode( $available, true );
			foreach ( $available as $slug => $args ) {
				$available[ $slug ]['cta']        = ( $args['cta'] === 'external' ) ? 'external' : $this->plugin_helper->get_plugin_state( $slug );
				$available[ $slug ]['path']       = $this->plugin_helper->get_plugin_path( $slug );
				$available[ $slug ]['activate']   = $this->plugin_helper->get_plugin_action_link( $slug );
				$available[ $slug ]['deactivate'] = $this->plugin_helper->get_plugin_action_link( $slug, 'deactivate' );
				$available[ $slug ]['network']    = $this->plugin_helper->get_is_network_wide( $slug );
				$available[ $slug ]['version']    = ! empty( $available[ $slug ]['version'] ) ? $this->plugin_helper->get_plugin_version( $slug, $available[ $slug ]['version'] ) : '';
			}

			return $available;
		}

		$data = [];
		foreach ( $this->useful_plugins as $slug ) {

			if ( array_key_exists( $slug, $this->get_external_plugins_data() ) ) {
				$data[ $slug ] = $this->get_external_plugins_data()[ $slug ];
				continue;
			}

			$current_plugin = $this->plugin_helper->get_plugin_details( $slug );
			if ( $current_plugin instanceof \WP_Error ) {
				continue;
			}
			$data[ $slug ] = [
				'banner'      => $current_plugin->banners['low'],
				'name'        => html_entity_decode( $current_plugin->name ),
				'description' => html_entity_decode( $current_plugin->short_description ),
				'version'     => $current_plugin->version,
				'author'      => html_entity_decode( wp_strip_all_tags( $current_plugin->author ) ),
				'cta'         => $this->plugin_helper->get_plugin_state( $slug ),
				'path'        => $this->plugin_helper->get_plugin_path( $slug ),
				'activate'    => $this->plugin_helper->get_plugin_action_link( $slug ),
				'deactivate'  => $this->plugin_helper->get_plugin_action_link( $slug, 'deactivate' ),
				'network'     => $this->plugin_helper->get_is_network_wide( $slug ),
			];
		}

		set_transient( $this->plugins_cache_hash_key, $current_hash );
		set_transient( $this->plugins_cache_key, wp_json_encode( $data ) );

		return $data;
	}

	/**
	 * Check if feedback notice should be shown after 14 days since activation.
	 *
	 * @return bool
	 */
	private function should_show_feedback_notice() {
		$activated_time = get_option( 'neve_install' );
		if ( ! empty( $activated_time ) ) {
			if ( time() - intval( $activated_time ) > 14 * DAY_IN_SECONDS ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Get data of external plugins that are not hosted on wp.org.
	 *
	 * @return array
	 */
	private function get_external_plugins_data() {

		$plugins = array(
			'wp-landing-kit' => array(
				'banner'      => NEVE_ASSETS_URL . 'img/dashboard/wp-landing.jpg',
				'name'        => 'WP Landing Kit',
				'description' => 'Turn WordPress into a landing page powerhouse with Landing Kit. Map domains to pages or any other published resource.',
				'author'      => 'Themeisle',
				'cta'         => 'external',
				'url'         => tsdk_utmify( 'https://wplandingkit.com/', 'recommendedplugins', 'nevedashboard' ),
				'premium'     => true,
			),

		);

		return $plugins;
	}

}
PK      \k    -  customizer/controls/simple_upsell_section.phpnu W+A        <?php
/**
 * Simple upsell customizer section.
 *
 * @package Neve
 */

namespace Neve\Customizer\Controls;

/**
 * Customizer section.
 *
 * @since  2.8.3
 * @see        WP_Customize_Section
 */
class Simple_Upsell_Section extends \WP_Customize_Section {
	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'nv_simple_upsell_section';

	/**
	 * Button text.
	 *
	 * @var   string
	 */
	public $button_text = '';

	/**
	 * Button link.
	 *
	 * @var   string
	 */
	public $link = '';

	/**
	 * List of features.
	 *
	 * @var   string
	 */
	public $text = '';

	/**
	 * Screen reader text.
	 *
	 * @var string
	 */
	public $screen_reader = '';

	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @return array The array to be exported to the client as JSON.
	 */
	public function json() {
		$json                  = parent::json();
		$json['button_text']   = $this->button_text;
		$json['link']          = $this->link;
		$json['text']          = $this->text;
		$json['screen_reader'] = __( '(opens in a new tab)', 'neve' );

		return $json;
	}

	/**
	 * Render template.
	 */
	protected function render_template() {
		?>
		<li id="accordion-section-{{ data.id }}" class="control-section-{{ data.type }}">
			<div class="nv-simple-upsell">
				<# if( data.text ) { #>
					<p>{{data.text}}</p>
				<# } #>
				<# if( data.link && data.button_text ) { #>
					<a rel="external noreferrer noopener" target="_blank" href="{{data.link}}" class='button button-secondary'>
						{{data.button_text}}
						<span class="components-visually-hidden">{{data.screen_reader}}</span>
					</a>
				<# } #>
			</div>
		</li>
		<?php
	}
}


PK      \kNW3k  k    customizer/controls/tabs.phpnu W+A        <?php
/**
 * The tabs customize control extends the WP_Customize_Control class. This class allows
 * developers to create tabs and hide the sections' settings easily.
 *
 * @package    Neve\Customizer\Controls
 * @since      1.1.45
 * @author     Andrei Baicus <andrei@themeisle.com>
 * @copyright  Copyright (c) 2017, Themeisle
 * @link       http://themeisle.com/
 * @license    http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 */

namespace Neve\Customizer\Controls;

/**
 * Radio image customize control.
 *
 * @since  1.1.45
 * @access public
 */
class Tabs extends \WP_Customize_Control {

	/**
	 * The type of customize control being rendered.
	 *
	 * @since 1.1.45
	 * @var   string
	 */
	public $type = 'interface-tabs';

	/**
	 * The tabs with keys of the controls that are under each tab.
	 *
	 * @since 1.1.45
	 * @var array
	 */
	public $tabs;

	/**
	 * Controls from tabs.
	 *
	 * @var array
	 */
	public $controls;

	/**
	 * Add custom JSON parameters to use in the JS template.
	 *
	 * @return array
	 */
	public function json() {
		$json             = parent::json();
		$json['tabs']     = $this->tabs;
		$json['controls'] = $this->controls;
		return $json;
	}

	/**
	 * Underscore JS template to handle the control's output.
	 *
	 * @return void
	 */
	public function content_template() {
		?>
		<# if ( ! data.tabs ) { return; } #>

		<div class="neve-tabs-control" id="">
		<# var i = 1;
			for( tab in data.tabs) { #>
				<#
				var allControlsInTabs = ''
				_.each( data.controls[tab], function( val, key ) {
					allControlsInTabs+= key + ' '
					if(val){
						var allvals = Object.keys(val).map(function(e) {
							return val[e]
						});
						allvals = _.uniq(_.flatten(allvals))
						allvals = allvals.join(' ')
						allControlsInTabs += allvals
					}
				});
				#>
			<div class="neve-customizer-tab <# if( i === 1 ){#> active <#}#>" data-tab="{{tab}}">
				<label class="{{allControlsInTabs}}">
					<# if(data.tabs[tab]['icon']) { #>
						<i class="dashicons dashicons-{{data.tabs[tab]['icon']}}"></i>
					<# } #>
					{{data.tabs[tab]['label']}}
				</label>
			</div>
		<# i++;} #>
		</div>


		<?php
	}
}

PK      \#4x  x     customizer/controls/ordering.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      29/08/2018
 *
 * @package Ordering.php
 */

namespace Neve\Customizer\Controls;

/**
 * Class Ordering
 *
 * @package Neve\Customizer\Controls
 */
class Ordering extends \WP_Customize_Control {

	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'ordering';

	/**
	 * Orderable components.
	 *
	 * @var array
	 */
	private $components = array();

	/**
	 * Ordering constructor.
	 *
	 * @param \WP_Customize_Manager $manager Wp customize.
	 * @param string                $id      control id.
	 * @param array                 $args    control args.
	 */
	public function __construct( \WP_Customize_Manager $manager, $id, array $args = array() ) {
		parent::__construct( $manager, $id, $args );
		$this->components = $args['components'] ? $args['components'] : array();

		$this->setup_components();
	}

	/**
	 * Get disabled components
	 * Add them at the end of all components in the customizer
	 */
	private function setup_components() {
		$val = $this->value();
		if ( ! is_string( $val ) ) {
			$val = '[]';
		}
		$val = json_decode( $val, true );

		if ( ! is_array( $val ) ) {
			$val = array();
		}

		$enabled          = array_combine( $val, $val );
		$disabled         = array_diff_assoc( $this->components, $enabled );
		$this->components = array_merge( $enabled, $disabled );
	}

	/**
	 * Render content of control.
	 */
	public function render_content() {
		$this->render_control_label();
		$this->render_sortable_list();
		$this->render_collector_input();
	}

	/**
	 * Render title and description.
	 *
	 * @return void
	 */
	private function render_control_label() {
		if ( empty( $this->label ) && empty( $this->description ) ) {
			return;
		}
		echo '<label>';
		if ( ! empty( $this->label ) ) {
			echo '<span class="customize-control-title">' . esc_html( $this->label ) . '</span>';
		}
		if ( ! empty( $this->description ) ) {
			echo '<span class="description customize-control-description">' . wp_kses_post( $this->description ) . '</span>';
		}
		echo '</label>';
	}

	/**
	 * Render sortable list.
	 *
	 * @return void
	 */
	private function render_sortable_list() {
		if ( empty( $this->components ) ) {
			return;
		}

		echo '<ul class="ti-order-sortable">';
		foreach ( $this->components as $component => $name ) {
			if ( $component === $name ) {
				continue;
			}
			echo '<li class="ui-state-default order-component' . esc_attr( $this->get_component_status_class( $component ) ) . '" data-id="' . esc_attr( $component ) . '">';
			echo '<span class="toggle-display"></span>';
			echo '<p>' . esc_html( $name ) . '</p>';
			echo '<span class="dashicons dashicons-menu drag"></span>';
			echo '</li>';
		}
		echo '</ul>';
	}

	/**
	 * Get the class for the component. (enabled/disabled)
	 *
	 * @param string $component the component to check.
	 *
	 * @return string
	 */
	private function get_component_status_class( $component ) {
		$value = $this->value();
		if ( empty( $value ) ) {
			return ' enabled';
		}
		$value = json_decode( $value, true );

		if ( ! is_array( $value ) ) {
			$value = array();
		}

		if ( ! in_array( $component, $value, true ) ) {
			return '';
		}

		return ' enabled';
	}

	/**
	 * Render the collector input.
	 *
	 * @return void
	 */
	private function render_collector_input() {
		echo '<input type="hidden" class="ti-order-collector"' . wp_kses_post( $this->get_link() ) . '>';
	}
}
PK      \m&k  k  $  customizer/controls/button_group.phpnu W+A        <?php
/**
 * Button group control.
 *
 * @package Neve\Customizer\Controls
 */

namespace Neve\Customizer\Controls;

/**
 * Class Button_Group
 *
 * @package Neve\Customizer\Controls
 */
class Button_Group extends \WP_Customize_Control {
	/**
	 * Render content for the control.
	 */
	public function render_content() {
		$this->render_control_header();
		$name     = 'nv_radio_' . $this->id;
		$input_id = 'nv_customize-input-' . $this->id;
		?>
		<div class="nv-button-group">
			<?php
			foreach ( $this->choices as $value => $icon_class ) {
				?>
				<input
						id="<?php echo esc_attr( $input_id . '-radio-' . $value ); ?>"
						type="radio"
						value="<?php echo esc_attr( $value ); ?>"
						name="<?php echo esc_attr( $name ); ?>"
					<?php $this->link(); ?>
					<?php checked( $this->value(), $value ); ?>
				/>
				<label for="<?php echo esc_attr( $input_id . '-radio-' . $value ); ?>" class="button">
					<i class="dashicons <?php echo esc_attr( $icon_class ); ?>"></i>
				</label>
			<?php } ?>
		</div>
		<?php
	}

	/**
	 * Render control header.
	 */
	private function render_control_header() {
		if ( empty( $this->label ) && empty( $this->description ) ) {
			return;
		} 
		?>
		<div class="nv-button-group-header">
			<?php if ( ! empty( $this->label ) ) { ?>
				<span class="customize-control-title">
				<span><?php echo esc_html( $this->label ); ?></span>
			</span>
				<?php 
			}
			if ( ! empty( $this->description ) ) { 
				?>
				<label class="nv-radio-description-info"
						for="<?php echo esc_attr( $this->id . '-description-toggle' ); ?>">
					<i class="dashicons dashicons-info"></i>
				</label>
				<input id="<?php echo esc_attr( $this->id . '-description-toggle' ); ?>" type="checkbox"
						class="expand-description">
				<span class="nv-radio-hidden-info"><?php echo esc_html( $this->description ); ?></span>
			<?php } ?>
		</div>
		<?php
	}
}
PK      \d    &  customizer/controls/upsell_control.phpnu W+A        <?php
/**
 * The upsell customize control extends the WP_Customize_Control class.
 *
 * @package    Neve\Customizer\Controls
 * @since      2.3.10
 * @copyright  Copyright (c) 2017, Themeisle
 * @link       http://themeisle.com/
 * @license    http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 */

namespace Neve\Customizer\Controls;

/**
 * Radio image customize control.
 *
 * @since  2.3.10
 * @access public
 */
class Upsell_Control extends \WP_Customize_Control {

	/**
	 * The type of customize control being rendered.
	 *
	 * @since 2.3.10
	 * @var   string
	 */
	public $type = 'neve-control-upsell';

	/**
	 * Button text.
	 *
	 * @since 2.3.10
	 * @var   string
	 */
	public $button_text = '';

	/**
	 * Button link.
	 *
	 * @since 2.3.10
	 * @var   string
	 */
	public $button_url = '';

	/**
	 * List of features.
	 *
	 * @since 2.3.10
	 * @var   array
	 */
	public $options = array();

	/**
	 * List of explained features.
	 *
	 * @since 2.3.10
	 * @var   array
	 */
	public $explained_features = array();

	/**
	 * Label text for each feature.
	 *
	 * @since 2.3.10
	 * @var   string
	 */
	public $pro_label = '';

	/**
	 * Screen reader text.
	 *
	 * @since 2.11.2
	 * @var   string
	 */
	public $screen_reader = '';

	/**
	 * Boolean to check if the pro label is displayed or not.
	 *
	 * @since 2.3.10
	 * @var   bool
	 */
	public $show_pro_label = true;

	/**
	 * Constructor.
	 *
	 * @param \WP_Customize_Manager $manager Customizer manager.
	 * @param string                $id      Control id.
	 * @param array                 $args    Argument.
	 */
	public function __construct( \WP_Customize_Manager $manager, $id, array $args ) {
		parent::__construct( $manager, $id, $args );
		$this->pro_label = esc_html__( 'PRO', 'neve' );
	}

	/**
	 * Add custom JSON parameters to use in the JS template.
	 *
	 * @return array
	 */
	public function json() {
		$json                       = parent::json();
		$json['button_text']        = $this->button_text;
		$json['button_url']         = $this->button_url;
		$json['options']            = $this->options;
		$json['explained_features'] = $this->explained_features;
		$json['show_pro_label']     = $this->show_pro_label;
		$json['pro_label']          = $this->pro_label;
		$json['screen_reader']      = $this->screen_reader;
		return $json;
	}

	/**
	 * Underscore JS template to handle the control's output.
	 *
	 * @return void
	 */
	public function content_template() {
		?>
		<div class="nv-upsell">
			<# if ( data.options ) { #>
			<ul class="nv-upsell-features">
				<# for (option in data.options) { #>
				<li>
					<# if( data.show_pro_label === true ) { #>
					<span class="upsell-pro-label">{{ data.pro_label }}</span>
					<# } #>
					{{ data.options[option] }}
				</li>
				<# } #>
			</ul>
			<# } #>

			<# if ( data.button_text && data.button_url ) { #>
			<a rel="external noreferrer noopener" target="_blank" href="{{ data.button_url }}" class="button button-primary">{{
				data.button_text }}
				<span class="components-visually-hidden">{{ data.screen_reader }}</span>
			</a>
			<# } #>

			<# if ( data.explained_features.length > 0 ) { #>
			<hr>
			<ul class="nv-upsell-feature-list">
				<# for (requirement in data.explained_features) { #>
				<li>* {{ data.explained_features[requirement] }}</li>
				<# } #>
			</ul>
			<# } #>
		</div>
		<?php
	}
}
PK      \8W'  '  ,  customizer/controls/react/upsell_section.phpnu W+A        <?php
/**
 * Description Upsell Section
 *
 * Author:      Bogdan Preda <bogdan.preda@themeisle.com>
 * Created on:  20-12-{2021}
 *
 * @package neve/neve-pro
 */
namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package    WordPress
 * @subpackage Customize
 * @since      4.1.0
 * @see        WP_Customize_Section
 */
class Upsell_Section extends \WP_Customize_Section {
	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'neve_upsell';

	/**
	 * Upgrade URL.
	 *
	 * @var string
	 */
	public $url = '';

	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @return array The array to be exported to the client as JSON.
	 * @since 4.1.0
	 */
	public function json() {
		$json        = parent::json();
		$json['url'] = $this->url;
		return $json;
	}

	/**
	 * Render template.
	 */
	protected function render_template() {
		?>
		<li id="accordion-section-{{ data.id }}"
			data-slug="{{data.id}}"
			class="control-section control-section-{{ data.type }} neve-upsell">
		</li>
		<?php
	}
}
PK      \B.	    2  customizer/controls/react/conditional_selector.phpnu W+A        <?php
/**
 * Color Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Button_Appearance
 *
 * @package Neve\Customizer\Controls\React
 */
class Conditional_Selector extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_context_conditional_selector';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var string
	 */
	public $default = '';
	/**
	 * Send to JS.
	 */
	public function json() {
		$json            = parent::json();
		$json['default'] = $this->default;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \rх    /  customizer/controls/react/button_appearance.phpnu W+A        <?php
/**
 * Button_Appearance Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Button_Appearance
 *
 * @package Neve\Customizer\Controls\React
 */
class Button_Appearance extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_button_appearance';
	/**
	 * Additional arguments passed to JS.
	 * Disables hover controls
	 *
	 * @var bool
	 */
	public $no_hover = false;
	/**
	 * Additional arguments passed to JS.
	 * Disables shadow controls
	 *
	 * @var bool
	 */
	public $no_shadow = false;
	/**
	 * Additional arguments passed to JS.
	 * Disables border radius, border width
	 *
	 * @var bool
	 */
	public $no_border = false;
	/**
	 * Default values.
	 *
	 * @var array
	 */
	public $default_vals = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                = parent::json();
		$json['no_hover']    = $this->no_hover;
		$json['no_shadow']   = $this->no_shadow;
		$json['no_border']   = $this->no_border;
		$json['defaultVals'] = $this->default_vals;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \$t  t  &  customizer/controls/react/ordering.phpnu W+A        <?php
/**
 * Radio Image Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Ordering
 *
 * @package Neve\Customizer\Controls\React
 */
class Ordering extends \WP_Customize_Control {

	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_ordering_control';

	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $components = [];

	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $default_order = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                 = parent::json();
		$json['components']   = $this->components;
		$json['defaultOrder'] = $this->default_order;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \+J	  	  (  customizer/controls/react/nr_spacing.phpnu W+A        <?php
/**
 * Non Responsive Spacing Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Spacing
 *
 * @package Neve\Customizer\Controls\React
 */
class Nr_Spacing extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_non_responsive_spacing';

	/**
	 * Min.
	 *
	 * @var int
	 */
	public $min = 0;

	/**
	 * Max.
	 *
	 * @var int
	 */
	public $max = 300;

	/**
	 * Units.
	 *
	 * @var array
	 */
	public $units = [ 'px', 'em', 'rem', '%' ];

	/**
	 * Default value.
	 *
	 * @var array
	 */
	public $default = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json               = parent::json();
		$json['min']        = $this->min;
		$json['max']        = $this->max;
		$json['units']      = $this->units;
		$json['defaultVal'] = $this->default;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \
$    /  customizer/controls/react/responsive_toggle.phpnu W+A        <?php
/**
 * Responsive Toggle. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Button_Appearance
 *
 * @package Neve\Customizer\Controls\React
 */
class Responsive_Toggle extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_responsive_toggle_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $excluded_devices = [];
	/**
	 * Send to JS.
	 */
	public function json() {
		$json             = parent::json();
		$json['excluded'] = $this->excluded_devices;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \.BL  L  6  customizer/controls/react/typography_extra_section.phpnu W+A        <?php
/**
 *
 * @package Neve
 */
namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package    WordPress
 * @subpackage Customize
 * @see        WP_Customize_Section
 */
class Typography_Extra_Section extends \WP_Customize_Section {
	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'typography_extra_section';

	/**
	 * Render template.
	 */
	protected function render_template() {
		?>
		<li id="accordion-section-{{ data.id }}"
			data-slug="{{data.id}}"
			class="control-section control-section-{{ data.type }}">
		</li>
		<?php
	}
}
PK      \p  p  +  customizer/controls/react/global_colors.phpnu W+A        <?php
/**
 * Global_Colors Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Global_Colors
 *
 * @package Neve\Customizer\Controls\React
 */
class Global_Colors extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_global_colors';
	/**
	 * Default values.
	 *
	 * @var string
	 */
	public $default_values = '';

	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                  = parent::json();
		$json['defaultValues'] = $this->default_values;
		$json['input_attrs']   = $this->input_attrs;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \    .  customizer/controls/react/form_token_field.phpnu W+A        <?php
/**
 * Form Token Field Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Form_Token_Field
 *
 * @package Neve\Customizer\Controls\React
 */
class Form_Token_Field extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_form_token_field';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $choices = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json            = parent::json();
		$json['choices'] = $this->choices;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \ƹ    %  customizer/controls/react/builder.phpnu W+A        <?php
/**
 * Builders Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package Neve\Customizer\Controls\React
 */
class Builder extends \WP_Customize_Control {
	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'neve_builder_control';

	/**
	 * Builder Type
	 *
	 * @var string
	 */
	public $builder_type = null;
	/**
	 * Columns Layout
	 *
	 * @var boolean
	 */
	public $columns_layout = false;

	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @return array The array to be exported to the client as JSON.
	 */
	public function json() {
		$json                  = parent::json();
		$json['builderType']   = $this->builder_type;
		$json['columnsLayout'] = $this->columns_layout;

		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \    (  customizer/controls/react/background.phpnu W+A        <?php
/**
 * Color Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Button_Appearance
 *
 * @package Neve\Customizer\Controls\React
 */
class Background extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_background_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var string
	 */
	public $default = '';
	/**
	 * Send to JS.
	 */
	public function json() {
		$json            = parent::json();
		$json['default'] = $this->default;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \Н    )  customizer/controls/react/multiselect.phpnu W+A        <?php
/**
 * Multiselect Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Button_Appearance
 *
 * @package Neve\Customizer\Controls\React
 */
class Multiselect extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_multiselect';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $choices = [];
	/**
	 * Send to JS.
	 */
	public function json() {
		$json            = parent::json();
		$json['choices'] = $this->choices;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \7    -  customizer/controls/react/builder_section.phpnu W+A        <?php
/**
 * Header Footer Builder Section.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package    WordPress
 * @subpackage Customize
 * @since      4.1.0
 * @see        WP_Customize_Section
 */
class Builder_Section extends \WP_Customize_Section {

	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'neve_header_footer_builder_section';
	/**
	 * Default options schema.
	 *
	 * @var array
	 */
	public $default_options = [
		'setting'      => '',
		'builder_type' => '',
	// 'quickLinks'  => [],
	];

	/**
	 * Options passed to section.
	 *
	 * @var array
	 */
	public $options = [];


	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @return array The array to be exported to the client as JSON.
	 */
	public function json() {
		$json            = parent::json();
		$json['options'] = wp_parse_args( $this->options, $this->default_options );

		return $json;
	}
}


PK      \]q{kT  T  *  customizer/controls/react/group_select.phpnu W+A        <?php
/**
 * Group_Select Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Group_Select
 *
 * @package Neve\Customizer\Controls\React
 */
class Group_Select extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_group_select';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $options = [];
	/**
	 * Mark controls as disabled.
	 *
	 * @var bool
	 */
	public $disabled = false;

	/**
	 * Send to JS.
	 */
	public function json() {
		$json             = parent::json();
		$json['options']  = $this->options;
		$json['disabled'] = $this->disabled;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \Ds    *  customizer/controls/react/link_control.phpnu W+A        <?php
/**
 * Author:          Bogdan Preda <bogdan.preda@themeisle.com>
 * Created on:      2022-02-10
 *
 * @package Neve
 */

namespace Neve\Customizer\Controls\React;

/**
 * Customizer link control React.
 *
 * @package    WordPress
 * @subpackage Customize
 * @since      4.1.0
 * @see        WP_Customize_Control
 */
class Link_Control extends \WP_Customize_Control {
	/**
	 * Type of this control.
	 *
	 * @var string
	 */
	public $type = 'neve_link';

	/**
	 * Control URL for link.
	 *
	 * @var string
	 */
	public $url = '#';

	/**
	 * Control text for link.
	 *
	 * @var string
	 */
	public $label = 'Link';

	/**
	 * Control description for link.
	 *
	 * @var string
	 */
	public $description = '';

	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @since 4.1.0
	 *
	 * @return array The array to be exported to the client as JSON.
	 */
	public function json() {
		$json                = parent::json();
		$json['url']         = $this->url;
		$json['label']       = $this->label;
		$json['description'] = $this->description;

		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \Zw    )  customizer/controls/react/font_family.phpnu W+A        <?php
/**
 * Font_Family Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Button_Appearance
 *
 * @package Neve\Customizer\Controls\React
 */
class Font_Family extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_font_family_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];
	/**
	 * Send to JS.
	 */
	public function json() {
		$json                = parent::json();
		$json['input_attrs'] = $this->input_attrs;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \4
    &  customizer/controls/react/textarea.phpnu W+A        <?php
/**
 * Textarea Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Textarea
 *
 * @package Neve\Customizer\Controls\React
 */
class Textarea extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_textarea';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                = parent::json();
		$json['input_attrs'] = $this->input_attrs;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \tɚ	  	  %  customizer/controls/react/heading.phpnu W+A        <?php
/**
 * Heading Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Heading control for react
 */
class Heading extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_customizer_heading';
	/**
	 * Control class.
	 *
	 * @var string
	 */
	public $class = '';

	/**
	 * Should be accordion?
	 *
	 * @var bool
	 */
	public $accordion = false;

	/**
	 * Initial state.
	 *
	 * @var bool
	 */
	public $expanded = true;

	/**
	 * How many controls to wrap.
	 *
	 * @var int
	 */
	public $controls_to_wrap = 1;

	/**
	 * Label before the accordion.
	 *
	 * @var string
	 */
	public $category_label = '';

	/**
	 * Send data to _s
	 *
	 * @return array
	 */
	public function json() {
		$json                  = parent::json();
		$json['classes']       = $this->class;
		$json['accordion']     = $this->accordion;
		$json['categoryLabel'] = $this->category_label;

		if ( $this->accordion === true ) {
			$json['classes'] .= ' accordion';
		}

		$json['style'] = $this->print_style();

		return $json;
	}

	/**
	 * Render the control.
	 */
	protected function render() {
		$id     = 'customize-control-' . str_replace( array( '[', ']' ), array( '-', '' ), $this->id );
		$class  = 'customize-control customize-control-' . $this->type;
		$class .= ' ' . $this->class;
		if ( $this->accordion ) {
			$class .= ' accordion';
		}

		if ( $this->expanded ) {
			$class .= ' expanded';
		}

		echo '<li id="' . esc_attr( $id ) . '" class="' . esc_attr( $class ) . '">';
		echo '</li>';
	}

	/**
	 * Print the style for the accordion.
	 */
	protected function print_style() {
		$style = '';
		for ( $i = 1; $i <= $this->controls_to_wrap; $i ++ ) {
			$style .= '.accordion.' . $this->class . ':not(.expanded)';
			for ( $j = 1; $j <= $i; $j ++ ) {
				$style .= ' + li';
			}
			if ( $i !== $this->controls_to_wrap ) {
				$style .= ',';
			}
		}
		$style .= '{max-height: 0;opacity: 0;margin: 0; overflow: hidden; padding:0 !important;}';

		return $style;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \PoW  W  (  customizer/controls/react/typography.phpnu W+A        <?php
/**
 * Typography control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Typography
 *
 * @package Neve\Customizer\Controls\React
 */
class Typography extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_typeface_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];
	/**
	 * Refresh on reset flag.
	 *
	 * @var bool
	 */
	public $refresh_on_reset = false;
	/**
	 * The control that holds the font family used by this control
	 *
	 * @var string
	 */
	public $font_family_control = '';

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                        = parent::json();
		$json['input_attrs']         = wp_json_encode( $this->input_attrs );
		$json['refresh_on_reset']    = $this->refresh_on_reset;
		$json['font_family_control'] = $this->font_family_control;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \.	    6  customizer/controls/react/responsive_radio_buttons.phpnu W+A        <?php
/**
 * Responsive_Radio_Buttons Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Responsive_Range
 *
 * @package Neve\Customizer\Controls\React
 */
class Responsive_Radio_Buttons extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_responsive_radio_buttons_control';

	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $choices = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json            = parent::json();
		$json['choices'] = $this->choices;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \fL  L  3  customizer/controls/react/documentation_section.phpnu W+A        <?php
/**
 * A section control for documentation.
 *
 * Author:      Bogdan Preda <bogdan.preda@themeisle.com>
 * Created on:  22-12-{2021}
 *
 * @package neve/neve-pro
 */
namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package    WordPress
 * @subpackage Customize
 * @since      4.1.0
 * @see        WP_Customize_Section
 */
class Documentation_Section extends \WP_Customize_Section {
	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'neve_documentation';

	/**
	 * Documentation URL.
	 *
	 * @var string
	 */
	public $url = '';

	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @return array The array to be exported to the client as JSON.
	 * @since 4.1.0
	 */
	public function json() {
		$json        = parent::json();
		$json['url'] = $this->url;
		return $json;
	}

	/**
	 * Render template.
	 */
	protected function render_template() {
		?>
		<li id="accordion-section-{{ data.id }}"
			data-slug="{{data.id}}"
			class="control-section control-section-{{ data.type }} neve-documentation">
		</li>
		<?php
	}
}
PK      \be    +  customizer/controls/react/font_pairings.phpnu W+A        <?php
/**
 * Font_Pairings Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Font_Pairings
 *
 * @package Neve\Customizer\Controls\React
 */
class Font_Pairings extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_font_pairings_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];
	/**
	 * Send to JS.
	 */
	public function json() {
		$json                = parent::json();
		$json['input_attrs'] = $this->input_attrs;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \?m  m  +  customizer/controls/react/upsell_banner.phpnu W+A        <?php
/**
 * Description Upsell Section
 *
 * Author:      Bogdan Preda <bogdan.preda@themeisle.com>
 * Created on:  20-12-{2021}
 *
 * @package neve/neve-pro
 */
namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package    WordPress
 * @subpackage Customize
 * @since      4.1.0
 * @see        WP_Customize_Section
 */
class Upsell_Banner extends \WP_Customize_Control {
	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'neve_upsell_banner';

	/**
	 * Upgrade URL.
	 *
	 * @var string
	 */
	public $url = '';

	/**
	 * Nonce.
	 *
	 * @var string
	 */
	public $nonce = '';

	/**
	 * Upsell text.
	 *
	 * @var string
	 */
	public $text = '';

	/**
	 * Upsell button text.
	 *
	 * @var string
	 */
	public $button_text = '';

	/**
	 * Upsell logo path.
	 *
	 * @var string
	 */
	public $logo_path = '';

	/**
	 * Upsell use logo.
	 *
	 * @var boolean
	 */
	public $use_logo = false;

	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @return array The array to be exported to the client as JSON.
	 * @since 4.1.0
	 */
	public function json() {
		$json               = parent::json();
		$json['url']        = $this->url;
		$json['nonce']      = $this->nonce;
		$json['text']       = $this->text;
		$json['buttonText'] = $this->button_text;
		$json['useLogo']    = $this->use_logo === true;
		$json['id']         = $this->id;
		$json['logoPath']   = ! empty( $this->logo_path ) ? $this->logo_path : get_template_directory_uri() . '/assets/img/dashboard/logo.svg';
		return $json;
	}

	/**
	 * Render template.
	 */
	protected function render() {
		?>
		<li id="customize-control-<?php echo esc_attr( $this->id ); ?>"
			data-slug="<?php echo esc_attr( $this->id ); ?>"
			class="customize-control customize-control-<?php echo esc_attr( $this->id ); ?>-control neve-upsell-banner" style="background: none;">
		</li>
		<?php
	}
}
PK      \5Nv  v  )  customizer/controls/react/radio_image.phpnu W+A        <?php
/**
 * Radio Image Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Radio_Image
 *
 * @package Neve\Customizer\Controls\React
 */
class Radio_Image extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_radio_image_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $choices = [];
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $documentation = [];
	/**
	 * Send to JS.
	 */
	public function json() {
		$json                  = parent::json();
		$json['choices']       = $this->choices;
		$json['documentation'] = $this->documentation;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \=  =  2  customizer/controls/react/instructions_control.phpnu W+A        <?php
/**
 * Created on:      2020-08-12
 *
 * @package Neve
 */

namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package    WordPress
 * @subpackage Customize
 * @since      4.1.0
 * @see        WP_Customize_Section
 */
class Instructions_Control extends \WP_Customize_Control {

	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'hfg_instructions';
	/**
	 * Default options schema.
	 *
	 * @var array
	 */
	public $default_options = [
		'description' => '',
		'image'       => '',
		'quickLinks'  => [],
	];

	/**
	 * Options passed to control.
	 *
	 * @var array
	 */
	public $options = [];



	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @since 4.1.0
	 *
	 * @return array The array to be exported to the client as JSON.
	 */
	public function json() {
		$json            = parent::json();
		$json['options'] = wp_parse_args( $this->options, $this->default_options );

		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}


PK      \݃Ū    &  customizer/controls/react/repeater.phpnu W+A        <?php
/**
 * Repeater Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Repeater
 *
 * @package Neve\Customizer\Controls\React
 */
class Repeater extends \WP_Customize_Control {

	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_repeater_control';

	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $fields = [];

	/**
	 * Additional arguments passed to JS.
	 *
	 * @var object
	 */
	public $new_item_fields;

	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $components = [];

	/**
	 * Additional arguments passed to JS.
	 *
	 * @var string
	 */
	public $allow_new_fields = 'yes';

	/**
	 * @param \WP_Customize_Manager $manager customize manager object.
	 * @param String                $id control ID.
	 * @param array                 $args control args.
	 */
	public function __construct( $manager, $id, $args = array() ) {
		$this->new_item_fields = new \stdClass();

		parent::__construct( $manager, $id, $args );
	}


	/**
	 * Send to JS.
	 */
	public function json() {
		$json                     = parent::json();
		$json['fields']           = $this->fields;
		$json['new_item_fields']  = $this->new_item_fields;
		$json['allow_new_fields'] = $this->allow_new_fields;
		$json['components']       = $this->components;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \$B    A  customizer/controls/react/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \    *  customizer/controls/react/logo_palette.phpnu W+A        <?php
/**
 * Logo_Palette Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Logo_Palette
 *
 * @package Neve\Customizer\Controls\React
 */
class Logo_Palette extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_logo_palette_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                = parent::json();
		$json['input_attrs'] = $this->input_attrs;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \c]t    -  customizer/controls/react/builder_columns.phpnu W+A        <?php
/**
 * Builder Columns Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package Neve\Customizer\Controls\React
 */
class Builder_Columns extends \WP_Customize_Control {
	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'neve_builder_columns';

	/**
	 * Layout Choices.
	 *
	 * @var array
	 */
	public $choices = [];

	/**
	 * Columns control slug.
	 *
	 * @var string|null
	 */
	public $columns_control = null;

	/**
	 * Refresh the parameters passed to the JavaScript via JSON.
	 */
	public function json() {
		$json = parent::json();

		$this->choices = [
			1 => [
				'equal' => [
					'url' => 'data:image/jpeg;base64,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',
				],
			],
			2 => [
				'equal'       => [
					'url' => 'data:image/jpeg;base64,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',
				],
				'right-third' => [
					'url' => 'data:image/jpeg;base64,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',
				],
				'left-third'  => [
					'url' => 'data:image/jpeg;base64,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',
				],
			],
			3 => [
				'equal'             => [
					'url' => 'data:image/jpeg;base64,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',
				],
				'left-half'         => [
					'url' => 'data:image/jpeg;base64,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',
				],
				'right-half'        => [
					'url' => 'data:image/jpeg;base64,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',
				],
				'center-half'       => [
					'url' => 'data:image/jpeg;base64,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',
				],
				'center-two-thirds' => [
					'url' => 'data:image/jpeg;base64,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',
				],
			],
			4 => [
				'equal'      => [
					'url' => 'data:image/jpeg;base64,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',
				],
				'left-half'  => [
					'url' => 'data:image/jpeg;base64,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',
				],
				'right-half' => [
					'url' => 'data:image/jpeg;base64,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',
				],
			],
			5 => [
				'equal' => [
					'url' => 'data:image/jpeg;base64,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',
				],
			],
		];

		$json['choices']        = $this->choices;
		$json['columnsControl'] = $this->columns_control;

		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \nV  V  #  customizer/controls/react/color.phpnu W+A        <?php
/**
 * Color Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Button_Appearance
 *
 * @package Neve\Customizer\Controls\React
 */
class Color extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_color_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var string
	 */
	public $default = '';
	/**
	 * Disable Alpha in colorpicker.
	 *
	 * @var bool
	 */
	public $disable_alpha = false;
	/**
	 * Allow gradient inside color picker.
	 *
	 * @var bool
	 */
	public $allow_gradient = false;
	/**
	 * Send to JS.
	 */
	public function json() {
		$json                  = parent::json();
		$json['default']       = $this->default;
		$json['disableAlpha']  = $this->disable_alpha;
		$json['allowGradient'] = isset( $this->input_attrs['allow_gradient'] ) ? $this->input_attrs['allow_gradient'] : $this->allow_gradient;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \"=5  5  3  customizer/controls/react/upsell_banner_section.phpnu W+A        <?php
/**
 * Description Upsell Section
 *
 * Author:      Bogdan Preda <bogdan.preda@themeisle.com>
 * Created on:  20-12-{2021}
 *
 * @package neve/neve-pro
 */
namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package    WordPress
 * @subpackage Customize
 * @since      4.1.0
 * @see        WP_Customize_Section
 */
class Upsell_Banner_Section extends \WP_Customize_Section {
	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'neve_upsell_banner_section';

	/**
	 * Upgrade URL.
	 *
	 * @var string
	 */
	public $url = '';

	/**
	 * Nonce.
	 *
	 * @var string
	 */
	public $nonce = '';

	/**
	 * Upsell text.
	 *
	 * @var string
	 */
	public $text = '';

	/**
	 * Upsell button text.
	 *
	 * @var string
	 */
	public $button_text = '';

	/**
	 * Upsell logo path.
	 *
	 * @var string
	 */
	public $logo_path = '';

	/**
	 * Upsell use logo.
	 *
	 * @var boolean
	 */
	public $use_logo = false;

	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @return array The array to be exported to the client as JSON.
	 * @since 4.1.0
	 */
	public function json() {
		$json               = parent::json();
		$json['url']        = $this->url;
		$json['nonce']      = $this->nonce;
		$json['text']       = $this->text;
		$json['buttonText'] = $this->button_text;
		$json['useLogo']    = $this->use_logo === true;
		$json['logoPath']   = ! empty( $this->logo_path ) ? $this->logo_path : get_template_directory_uri() . '/assets/img/dashboard/logo.svg';
		return $json;
	}

	/**
	 * Render template.
	 */
	protected function render() {
		?>
		<li id="acordion-section-<?php echo esc_attr( $this->id ); ?>"
			data-slug="<?php echo esc_attr( $this->id ); ?>"
			class="control-section control-section-<?php echo esc_attr( $this->type ); ?> neve-upsell-banner">
		</li>
		<?php
	}
}
PK      \
;    .  customizer/controls/react/responsive_range.phpnu W+A        <?php
/**
 * Responsive_Range Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Responsive_Range
 *
 * @package Neve\Customizer\Controls\React
 */
class Responsive_Range extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_responsive_range_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                = parent::json();
		$json['input_attrs'] = $this->input_attrs;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \_>    2  customizer/controls/react/global_custom_colors.phpnu W+A        <?php
/**
 * Global_Custom_Colors Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Global_Custom_Colors
 *
 * @package Neve\Customizer\Controls\React
 */
class Global_Custom_Colors extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_global_custom_colors';
	/**
	 * Default values.
	 *
	 * @var string
	 */
	public $default_values = '';

	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                  = parent::json();
		$json['defaultValues'] = $this->default_values;
		$json['input_attrs']   = $this->input_attrs;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \!I  I  +  customizer/controls/react/radio_buttons.phpnu W+A        <?php
/**
 * Radio Buttons Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Spacing
 *
 * @package Neve\Customizer\Controls\React
 */
class Radio_Buttons extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_radio_buttons_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $choices = [];
	/**
	 * Send context to the control that will handle the choices differently.
	 *
	 * @var bool|string
	 */
	public $is_for = false;
	/**
	 * Should have larger buttons.
	 *
	 * @var bool
	 */
	public $large_buttons = false;
	/**
	 * Show the labels.
	 *
	 * @var bool
	 */
	public $show_labels = false;

	/**
	 * Footer description.
	 *
	 * @var string
	 */
	public $footer_description = '';

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                      = parent::json();
		$json['choices']           = $this->choices;
		$json['is_for']            = $this->is_for;
		$json['large_buttons']     = $this->large_buttons;
		$json['showLabels']        = $this->show_labels;
		$json['footerDescription'] = $this->footer_description;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \k    +  customizer/controls/react/inline_select.phpnu W+A        <?php
/**
 * Inline Select Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Inline_Select
 *
 * @package Neve\Customizer\Controls\React
 */
class Inline_Select extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_inline_select';

	/**
	 * Options.
	 *
	 * @var array
	 */
	public $options = [];

	/**
	 * Default.
	 *
	 * @var string|int
	 */
	public $default;

	/**
	 * Link.
	 *
	 * @var array
	 */
	public $link;

	/**
	 * Allows listening to other components.
	 *
	 * @var string
	 */
	public $changes_on;
	/**
	 * Send to JS.
	 */
	public function json() {
		$json               = parent::json();
		$json['options']    = $this->options;
		$json['defaultVal'] = $this->default;
		$json['link']       = $this->link;
		$json['changesOn']  = $this->changes_on;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \S<    #  customizer/controls/react/range.phpnu W+A        <?php
/**
 * Range Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Spacing
 *
 * @package Neve\Customizer\Controls\React
 */
class Range extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_range_control';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                = parent::json();
		$json['input_attrs'] = $this->input_attrs;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \$R  R  .  customizer/controls/react/presets_selector.phpnu W+A        <?php
/**
 * Presets selector control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Spacing
 *
 * @package Neve\Customizer\Controls\React
 */
class Presets_Selector extends \WP_Customize_Control {

	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_presets_selector';

	/**
	 * Presets for the control.
	 *
	 * @var array
	 */
	public $presets = [];

	/**
	 * Builder Setting Slug.
	 *
	 * @var string | null
	 */
	public $builder = null;

	/**
	 * Send to JS.
	 */
	public function json() {
		$json            = parent::json();
		$json['presets'] = $this->presets;
		$json['builder'] = $this->builder;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \ W=    '  customizer/controls/react/rich_text.phpnu W+A        <?php
/**
 * Rich_Text Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Rich_Text
 *
 * @package Neve\Customizer\Controls\React
 */
class Rich_Text extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_rich_text';
	/**
	 * Send to JS.
	 */
	public function json() {
		$json                         = parent::json();
		$json['toolbars']             = [];
		$json['allowedDynamicFields'] = [];
		if ( isset( $this->input_attrs['toolbars'] ) && ! empty( $this->input_attrs['toolbars'] ) ) {
			$json['toolbars'] = $this->input_attrs['toolbars'];
		}
		if ( isset( $this->input_attrs['allowedDynamicFields'] ) && ! empty( $this->input_attrs['allowedDynamicFields'] ) ) {
			$json['allowedDynamicFields'] = $this->input_attrs['allowedDynamicFields'];
		}
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \-B  B  %  customizer/controls/react/spacing.phpnu W+A        <?php
/**
 * Spacing Control. Handles data passing from args to JS.
 *
 * @package Neve\Customizer\Controls\React
 */

namespace Neve\Customizer\Controls\React;

/**
 * Class Spacing
 *
 * @package Neve\Customizer\Controls\React
 */
class Spacing extends \WP_Customize_Control {
	/**
	 * Control type.
	 *
	 * @var string
	 */
	public $type = 'neve_spacing';
	/**
	 * Additional arguments passed to JS.
	 *
	 * @var array
	 */
	public $input_attrs = [];

	/**
	 * Default value.
	 *
	 * @var array
	 */
	public $default = [];

	/**
	 * Send to JS.
	 */
	public function json() {
		$json                = parent::json();
		$json['input_attrs'] = $this->input_attrs;
		$json['default']     = $this->default;
		return $json;
	}

	/**
	 * This method overrides the default render
	 * so that nothing is rendered.
	 * Previously it would try to put an input element where the value was `esc_attr()`
	 * This would trigger notices in PHP
	 * It is not required to have a render as it is being handled by React.
	 */
	final public function render_content() {
		// this is rendered from React
	}
}
PK      \Vs    2  customizer/controls/react/instructions_section.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      2019-10-16
 *
 * @package Neve
 */
namespace Neve\Customizer\Controls\React;

/**
 * Customizer section.
 *
 * @package    WordPress
 * @subpackage Customize
 * @since      4.1.0
 * @see        WP_Customize_Section
 */
class Instructions_Section extends \WP_Customize_Section {
	/**
	 * Type of this section.
	 *
	 * @var string
	 */
	public $type = 'hfg_instructions';
	/**
	 * Default options schema.
	 *
	 * @var array
	 */
	public $default_options = [
		'description'     => '',
		'image'           => '',
		'quickLinks'      => [],
		'builderMigrated' => false,
	];
	/**
	 * Options passed to control.
	 *
	 * @var array
	 */
	public $options = [];
	/**
	 * Gather the parameters passed to client JavaScript via JSON.
	 *
	 * @return array The array to be exported to the client as JSON.
	 * @since 4.1.0
	 */
	public function json() {
		$json            = parent::json();
		$json['options'] = wp_parse_args( $this->options, $this->default_options );
		return $json;
	}
	/**
	 * Render template.
	 */
	protected function render_template() {
		?>
		<li id="accordion-section-{{ data.id }}"
			data-slug="{{data.id}}"
			class="control-section control-section-{{ data.type }} neve-quick-links">
		</li>
		<?php
	}
}
PK      \`G      customizer/controls/range.phpnu W+A        <?php
/**
 * The range value customize control.
 *
 * @package    Neve/Customizer/Controls
 * @soundtrack I Still Haven't Found What I'm Looking For - U2 ( manily the sense of _js )
 */

namespace Neve\Customizer\Controls;

/**
 * Class Customizer_Range_Value_Control
 *
 * @package  Neve\Customizer\Controls
 */
class Range extends \WP_Customize_Control {

	/**
	 * Control type
	 *
	 * @var string
	 */
	public $type = 'range-value';

	/**
	 * Enable media queries
	 *
	 * @var bool
	 */
	public $media_query = false;

	/**
	 * Settings for range inputs.
	 *
	 * @var array
	 */
	public $input_attr = array();

	/**
	 * Step size.
	 *
	 * @var string
	 */
	public $step = '';

	/**
	 * Add/remove from fixed value flag
	 *
	 * @var bool
	 */
	public $sum_type = false;

	/**
	 * Hide the responsive switches
	 *
	 * @var bool
	 */
	public $hide_responsive_switches = false;

	/**
	 * Range constructor.
	 *
	 * @param \WP_Customize_Manager $manager Customize manager.
	 * @param string                $id      Control id.
	 * @param array                 $args    Control arguments.
	 */
	public function __construct( $manager, $id, $args = array() ) {
		parent::__construct( $manager, $id, $args );
		$this->args_to_props( $args );
	}

	/**
	 * Handles input value.
	 *
	 * @return array
	 */
	public function json() {
		$json = parent::json();

		$json['value']                    = json_decode( $this->value(), true );
		$json['link']                     = $this->get_link();
		$json['media_query']              = $this->media_query;
		$json['step']                     = $this->step;
		$json['sum_type']                 = $this->sum_type;
		$json['inputAttr']                = $this->input_attr;
		$json['hide_responsive_switches'] = $this->hide_responsive_switches;

		return $json;
	}

	/**
	 * Render the title for the control.
	 */
	private function render_title() {
		?>
		<# if ( data.label ) { #>
		<span class="customize-control-title">
				<span>{{ data.label }}</span>
				<# if ( data.description ) { #>
					<i class="dashicons dashicons-editor-help" style="vertical-align: text-bottom;"
							title="{{ data.description }}"></i>
				<# } #>
		</span>
		<# if( data.media_query === true && data.hide_responsive_switches === false ) { #>
		<?php $this->render_responsive_switches(); ?>
		<# } #>
		<# } #>
		<?php
	}

	/**
	 * Render the responsive switches.
	 */
	private function render_responsive_switches() {
		?>
		<ul class="responsive-switchers">
			<li class="desktop">
				<button type="button" class="preview-desktop active" data-device="desktop">
					<i class="dashicons dashicons-desktop"></i>
				</button>
			</li>
			<li class="tablet">
				<button type="button" class="preview-tablet" data-device="tablet">
					<i class="dashicons dashicons-tablet"></i>
				</button>
			</li>
			<li class="mobile">
				<button type="button" class="preview-mobile" data-device="mobile">
					<i class="dashicons dashicons-smartphone"></i>
				</button>
			</li>
		</ul>
		<?php
	}

	/**
	 * Render the input.
	 */
	private function render_input() {
		?>
		<#
		var type = data.sum_type ? 'text' : 'number';
		var value = data.value ? data.value[mediaQuery] : attr.default;
		if( ! data.media_query ) {
		value = data.value? data.value : attr.default
		}
		var active = mediaQuery === 'desktop' ? 'active' : '';

		if( data.sum_type ) { value = '+' + value; }
		#>
		<div class="{{mediaQuery}} control-wrap {{active}}">
			<input
					class="range-slider__range"
					type="range"
					min="{{attr.min}}"
					max="{{attr.max}}"
					step="{{data.step}}"
					data-query="{{mediaQuery}}"
					data-default="{{attr.default}}"
					value="{{ value }}"
			>
			<input
			<# if( data.sum_type ) { #>
			readonly
			<# } #>
			class="range-slider-value"
			type="{{type}}"
			title="{{data.label}}"
			min="{{attr.min}}"
			max="{{attr.max}}"
			step="{{data.step}}"
			value="{{ value }}"
			>
			<span class="range-reset-slider"><span class="dashicons dashicons-image-rotate"></span></span>
		</div>
		<?php
	}

	/**
	 * Render the control's content.
	 */
	protected function content_template() {
		$this->render_title();
		?>
		<# var wrapClass = data.media_query ? 'has-media-queries' : ''; #>
		<div class="range-slider {{wrapClass}}">
		<# if( data.media_query === true ) { #>
		<# _.each( data.inputAttr, function( attr, mediaQuery ) { #>
		<?php $this->render_input(); ?>
		<# } ) #>
		<# } else {
		var mediaQuery = 'desktop';
		var attr = data.inputAttr; #>
		<?php $this->render_input(); ?>
		<# } #>
		<input
				type="hidden"
				class="range-collector"
				title="{{data.label}}"
				value="{{data.value}}"
				{{{data.link}}}  <?php // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation ?>
		>
		<?php
	}

	/**
	 * Transform arguments to object properties
	 */
	private function args_to_props( $args ) {
		if ( ! empty( $args['input_attr'] ) ) {
			$this->input_attr = $args['input_attr'];
		}

		if ( ! empty( $args['media_query'] ) ) {
			$this->media_query = (bool) $args['media_query'];
		}

		if ( ! isset( $this->input_attr['mobile'] ) || ! isset( $this->input_attr['tablet'] ) || ! isset( $this->input_attr['desktop'] ) ) {
			$this->media_query = false;
		}

		if ( ! empty( $args['sum_type'] ) ) {
			$this->sum_type = $args['sum_type'];
		}

		if ( ! empty( $args['step'] ) ) {
			$this->step = $args['step'];
		}
	}
}
PK      \i|	  	  %  customizer/controls/simple_upsell.phpnu W+A        <?php
/**
 * Simple upsell control.
 *
 * @package Neve
 */

namespace Neve\Customizer\Controls;

/**
 * Simple Upsell Control.
 *
 * @since  2.8.3
 * @access public
 */
class Simple_Upsell extends \WP_Customize_Control {

	/**
	 * The type of customize control being rendered.
	 *
	 * @since  2.8.3
	 * @var   string
	 */
	public $type = 'nv_simple_upsell';

	/**
	 * Button text.
	 *
	 * @since  2.8.3
	 * @var   string
	 */
	public $button_text = '';

	/**
	 * Button link.
	 *
	 * @since  2.8.3
	 * @var   string
	 */
	public $link = '';

	/**
	 * List of features.
	 *
	 * @since  2.8.3
	 * @var   string
	 */
	public $text = '';

	/**
	 * Render Method
	 *
	 * @return void
	 */
	public function render_content() {
		?>
		<div class="nv-simple-upsell">
			<?php if ( ! empty( $this->text ) ) { ?>
				<p><?php echo esc_html( $this->text ); ?></p>
			<?php } ?>
			<?php if ( ! empty( $this->link ) && ! empty( $this->button_text ) ) { ?>
				<a target="_blank" rel="external noreferrer noopener" href="<?php echo esc_url( $this->link ); ?>" class='button button-secondary'>
					<?php echo esc_html( $this->button_text ); ?>
					<span class="components-visually-hidden"><?php echo esc_html__( '(opens in a new tab)', 'neve' ); ?></span>
				</a>
			<?php } ?>
		</div>
		<?php
	}
}
PK      \L       customizer/controls/button.phpnu W+A        <?php
/**
 * Customizer functionality for the Blog settings panel.
 *
 * @package Neve\Customizer\Controls
 * @since   1.0.0
 */

namespace Neve\Customizer\Controls;

/**
 * A customizer control to display text in customizer.
 *
 * @since 1.0.0
 */
class Button extends \WP_Customize_Control {

	/**
	 * Control id
	 *
	 * @var string $id Control id.
	 */
	public $id = '';
	/**
	 * Button class.
	 *
	 * @var mixed|string
	 */
	public $button_class = '';
	/**
	 * Icon class.
	 *
	 * @var mixed|string
	 */
	public $icon_class = '';
	/**
	 * Button text.
	 *
	 * @var mixed|string
	 */
	public $button_text = '';

	/**
	 * Text before.
	 *
	 * @var mixed|string
	 */
	public $text_before = '';


	/**
	 * Text after.
	 *
	 * @var mixed|string
	 */
	public $text_after = '';


	/**
	 * Is Button.
	 *
	 * @var bool
	 */
	public $is_button = true;


	/**
	 * Control to focus.
	 *
	 * @var string
	 */
	public $control_to_focus = '';
	/**
	 * Shortcut.
	 *
	 * @var bool
	 */
	public $shortcut = false;

	/**
	 * Constructor.
	 *
	 * @param \WP_Customize_Manager $manager Customizer manager.
	 * @param string                $id      Control id.
	 * @param array                 $args    Argument.
	 */
	public function __construct( $manager, $id, $args = array() ) {
		parent::__construct( $manager, $id, $args );
		$this->id = $id;
	}

	/**
	 * Render content for the control.
	 *
	 * @since 1.0.0
	 */
	public function render_content() {
		if ( empty( $this->button_text ) ) {
			return;
		}
		$control = $this->is_button ? '' : '<p>';
		if ( ! empty( $this->text_before ) ) {
			$control .= wp_kses_post( $this->text_before ) . ' ';
		}
		$control .= $this->is_button ? '<button ' : '<a ';
		if ( $this->control_to_focus ) {
			$control .= 'data-control-to-focus="' . esc_attr( $this->control_to_focus ) . '"';
		}
		$control .= ' class="' . esc_attr( $this->get_button_classes() ) . '"';
		$control .= $this->is_button ? ' style="display: flex; align-items: center;"' : ' style="cursor:pointer;"';
		$control .= '>';
		$control .= $this->get_icon();
		$control .= esc_html( $this->button_text );
		$control .= $this->is_button ? '</button>' : '</a>';
		if ( ! empty( $this->text_after ) ) {
			$control .= wp_kses_post( $this->text_after );
		}
		if ( $this->is_button ) {
			$control .= '</p>';
		}

		echo $control;  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Get the icon.
	 *
	 * @return string
	 */
	private function get_icon() {
		if ( empty( $this->icon_class ) ) {
			return '';
		}

		return '<i class="dashicons dashicons-' . esc_attr( $this->icon_class ) . '" style="margin-right: 10px"></i>';
	}

	/**
	 * Get the button classes.
	 *
	 * @return string
	 */
	private function get_button_classes() {
		$classes = '';

		if ( $this->is_button ) {
			$classes .= 'button button-secondary';
		}
		if ( $this->shortcut ) {
			$classes .= ' menu-shortcut ';
		}
		if ( $this->button_class ) {
			$classes .= $this->button_class;
		}
		if ( $this->control_to_focus ) {
			$classes .= ' neve-control-focus';
		}

		return $classes;
	}
}
PK      \$B    ;  customizer/controls/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \@(  (  )  customizer/controls/responsive_number.phpnu W+A        <?php
/**
 * Responsive number input control.
 *
 * @package    Neve\Customizer\Controls
 */

namespace Neve\Customizer\Controls;

/**
 * A text control with validation for CSS units.
 */
class Responsive_Number extends \WP_Customize_Control {

	/**
	 * Control type.
	 *
	 * @access public
	 * @var string
	 */
	public $type = 'responsive-number';

	/**
	 * Responsive flag.
	 *
	 * @access public
	 * @var bool
	 */
	public $responsive = true;

	/**
	 * Allowed Units.
	 *
	 * @access public
	 * @var array
	 */
	public $units = array();

	/**
	 * Settings for input.
	 *
	 * @var array
	 */
	public $input_attr = array();

	/**
	 * Responsive_Number constructor.
	 *
	 * @param \WP_Customize_Manager $manager Customize manager.
	 * @param string                $id      Control id.
	 * @param array                 $args    Control arguments.
	 */
	public function __construct( $manager, $id, $args = array() ) {
		parent::__construct( $manager, $id, $args );
		$this->args_to_props( $args );
	}

	/**
	 * Send the parameters via JSON.
	 */
	public function json() {
		$json = parent::json();

		$json['default'] = $this->setting->default;
		if ( isset( $this->default ) ) {
			$json['default'] = $this->default;
		}

		$json['value']      = json_decode( $this->value(), true );
		$json['link']       = $this->get_link();
		$json['id']         = $this->id;
		$json['label']      = esc_html( $this->label );
		$json['units']      = $this->units;
		$json['responsive'] = $this->responsive;
		$json['inputAttr']  = $this->input_attr;

		return $json;
	}

	/**
	 * Render the control
	 *
	 * @access protected
	 */
	protected function content_template() {
		$this->render_title();
		?>

		<# var wrapClass = data.responsive ? 'has-media-queries' : ''; #>
		<# wrapClass += data.units.length ? '' : 'no-units'; #>
		<div class="responsive-number {{wrapClass}}">
			<div class="controls--wrap">
				<# if( data.responsive === true ) { #>
				<# _.each( data.inputAttr, function( attr, mediaQuery ) { #>
				<?php $this->render_input(); ?>
				<# } ) #>
				<# } else {
				var mediaQuery = 'desktop';
				var attr = data.inputAttr; #>
				<?php $this->render_input(); ?>
				<# } #>
				<span class="reset-number-input"><span class="dashicons dashicons-image-rotate"></span></span>
			</div>
			<input
					type="hidden"
					class="responsive-number-collector"
					title="{{data.label}}"
					value="{{data.value}}"
					{{{data.link}}}  <?php // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation ?>
			>
		</div>
		<?php
	}

	/**
	 * Render the input.
	 */
	private function render_input() {
		?>
		<#
		var value = data.value ? data.value[mediaQuery] : attr.default;
		var suffix = '';
		if( data.value ) {
		suffix = data.value.suffix ? data.value.suffix[mediaQuery] : attr.default_unit;
		}

		if( ! data.responsive ) {
		value = data.value? data.value : attr.default
		}
		var active = mediaQuery === 'desktop' ? 'active' : '';
		#>

		<div class="{{mediaQuery}} control-wrap {{active}}">
			<input
					class="responsive-number--input <# if( ! data.units ){ #>no-units<# } #>"
					type="number"
			<# if( attr.min ) {	#>
			min="{{attr.min}}"
			<# }
			if ( attr.max ) { #>
			max="{{attr.max}}"
			<# } #>
			data-query="{{mediaQuery}}"
			data-default="{{attr.default}}"
			value="{{ value }}"
			/>
			<# if( data.units.length ) {
			var disabled = data.units.length === 1 ? 'disabled' : '';
			#>
			<select class="responsive-number--select" {{disabled}} data-default="{{attr.default_unit}}">
				<# _.each( data.units, function( val ) {
				var defaultUnit = val === suffix ? 'selected="selected"' : '';
				#>
				<option value="{{val}}" {{defaultUnit}}>{{val}}</option>
				<# } ) #>
			</select>
			<# } #>
		</div>
		<?php
	}

	/**
	 * Render the title for the control.
	 */
	private function render_title() {
		?>
		<# if ( data.label ) { #>
		<span class="customize-control-title" style="display: inline-block;">
				<span>{{ data.label }}</span>
				<# if ( data.description ) { #>
					<i class="dashicons dashicons-editor-help" style="vertical-align: text-bottom;"
							title="{{ data.description }}"></i>
				<# } #>
		</span>
		<# if( data.responsive === true ) { #>
		<?php $this->render_responsive_switches(); ?>
		<# } #>
		<# } #>
		<?php
	}

	/**
	 * Render the responsive switches.
	 */
	private function render_responsive_switches() {
		?>
		<ul class="responsive-switchers">
			<li class="desktop">
				<button type="button" class="preview-desktop active" data-device="desktop">
					<i class="dashicons dashicons-desktop"></i>
				</button>
			</li>
			<li class="tablet">
				<button type="button" class="preview-tablet" data-device="tablet">
					<i class="dashicons dashicons-tablet"></i>
				</button>
			</li>
			<li class="mobile">
				<button type="button" class="preview-mobile" data-device="mobile">
					<i class="dashicons dashicons-smartphone"></i>
				</button>
			</li>
		</ul>
		<?php
	}

	/**
	 * Transform arguments to object properties
	 */
	private function args_to_props( $args ) {
		if ( ! empty( $args['input_attr'] ) ) {
			$this->input_attr = $args['input_attr'];
		}

		if ( ! empty( $args['media_query'] ) ) {
			$this->responsive = (bool) $args['responsive'];
		}

		if ( ! isset( $this->input_attr['mobile'] ) || ! isset( $this->input_attr['tablet'] ) || ! isset( $this->input_attr['desktop'] ) ) {
			$this->responsive = false;
		}
	}
}
PK      \cX  X     customizer/controls/checkbox.phpnu W+A        <?php
/**
 * Customizer Heading.
 *
 * @since   1.0.0
 * @package Neve\Customizer\Controls
 */

namespace Neve\Customizer\Controls;

/**
 * Checkbox control
 */
class Checkbox extends \WP_Customize_Control {

	/**
	 * The control type.
	 *
	 * @access public
	 * @var string
	 */
	public $type = 'checkbox-toggle';

	/**
	 * Send to _js json.
	 *
	 * @return array
	 */
	public function json() {
		$json         = parent::json();
		$json['id']   = $this->id;
		$json['link'] = $this->get_link();

		return $json;
	}

	/**
	 * Render control.
	 */
	protected function content_template() {
		?>
		<div class="checkbox-toggle-wrap">
			<span>{{data.label}}</span>
			<input {{{data.link}}} type="checkbox" id="{{data.id}}"/><label for="{{data.id}}"></label>  <?php // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation ?>
		</div>
		<?php
	}
}
PK      \C    #  customizer/controls/radio_image.phpnu W+A        <?php
/**
 * Radio image controls.
 *
 * Note, the `$choices` array is slightly different than normal and should be in the form of
 * `array(
 *  $value => array( 'url' => $image_url, 'label' => $text_label ),
 *  $value => array( 'url' => $image_url, 'label' => $text_label ),
 * )`
 *
 * @package    Neve\Customizer\Controls
 */

namespace Neve\Customizer\Controls;

/**
 * Class Radio_Image
 */
class Radio_Image extends \WP_Customize_Control {

	/**
	 * The type of customize control being rendered.
	 *
	 * @since  1.0.0
	 * @access public
	 * @var    string
	 */
	public $type = 'radio-image';

	/**
	 * Flag to tell that this control is a tab
	 *
	 * @since 1.1.72
	 * @var   bool
	 */
	public $is_tab = false;

	/**
	 * Flag to tell that this control is a sub-tab in a tab
	 *
	 * @since 1.1.72
	 * @var   bool
	 */
	public $is_subtab = false;

	/**
	 * Controls in tabs.
	 *
	 * @since 1.1.72
	 * @var   array
	 */
	public $controls;

	/**
	 * Control data (tabs, names, icons, images)
	 *
	 * @since 1.1.72
	 * @var   array
	 */
	public $choices;

	/**
	 * Radio_Image constructor.
	 *
	 * @param \WP_Customize_Manager $manager Customizer manager object.
	 * @param string                $id      Control id.
	 * @param array                 $args    Control arguments.
	 */
	public function __construct( \WP_Customize_Manager $manager, $id, array $args = array() ) {
		parent::__construct( $manager, $id, apply_filters( $id . '_filter_args', $args ) );

		if ( ! empty( $args['is_tab'] ) && $args['is_tab'] === true ) {
			$this->is_tab = $args['is_tab'];
			if ( ! empty( $args['is_subtab'] ) && $args['is_subtab'] === true ) {
				$this->is_subtab = $args['is_subtab'];
			}

			if ( ! empty( $this->choices ) ) {
				foreach ( $this->choices as $value => $args ) {
					$this->controls[ $value ] = $args['controls'];
				}
			}
		}

	}

	/**
	 * Loads the jQuery UI Button script and custom scripts/styles.
	 *
	 * @return void
	 * @since  1.0.0
	 * @access public
	 */
	public function enqueue() {
		wp_enqueue_script( 'jquery-ui-button' );
	}

	/**
	 * Add custom JSON parameters to use in the JS template.
	 *
	 * @return array
	 * @since  1.0.0
	 * @access public
	 */
	public function json() {
		$json = parent::json();

		$json['is_tab']    = $this->is_tab;
		$json['is_subtab'] = $this->is_subtab;
		if ( $json['is_tab'] === true ) {
			$json['controls'] = $this->controls;
		}
		// We need to make sure we have the correct image URL.
		$json['choices'] = $this->choices;
		$json['width']   = 100;
		if ( ! empty( $this->choices ) ) {
			$json['width'] = number_format( 100 / count( $this->choices ), 2 );
		}
		$json['id']    = $this->id;
		$json['link']  = $this->get_link();
		$json['value'] = $this->value();

		return $json;
	}

	/**
	 * Underscore JS template to handle the control's output.
	 *
	 * @return void
	 * @since  1.0.0
	 * @access public
	 */
	public function content_template() {
		?>
		<#
		if ( ! data.choices ) {
		return;
		}
		#>
		<# if( !data.is_tab) {#>
		<# if ( data.label ) { #>
		<span class="customize-control-title">{{ data.label }}</span>
		<# } #>

		<# if ( data.description ) { #>
		<span class="description customize-control-description">{{{ data.description }}}</span> <?php // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation ?>
		<# } #>
		<#}#>


		<div class="buttonset <# if( data.is_tab) {#>customizer-tab <#}#> <# if( data.is_subtab) {#>customizer-subtab <#}#>">
			<# for ( key in data.choices ) { #>

			<input <# if( data.is_tab) {#>data-controls="{{data.controls[key]}}"<#}#> type="radio" value="{{ key }}"
			name="_customize-{{ data.type }}-{{ data.id }}" id="{{ data.id }}-{{ key }}" <# if ( key
			=== data.value && ( !data.is_tab || data.is_subtab) ) { #> checked="checked" <# } #>
			{{{ data.link }}} /> <?php // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation ?>
			<label for="{{ data.id }}-{{ key }}" style="width:{{data.width}}%">
				<# if( !data.is_tab) {#>
				<span class="screen-reader-text">{{ data.choices[ key ]['label'] }}</span>
				<img src="{{ data.choices[ key ]['url'] }}" alt="{{ data.choices[ key ]['label'] }}"/>
				<# } else { #>
				<# if( data.choices[ key ]['icon'] ){ #>
				<i class="fa fa-{{ data.choices[ key ]['icon'] }}"></i>
				<# }
				if( data.choices[ key ]['url'] ){
				#>
				<img src="{{ data.choices[ key ]['url'] }}" alt="{{ data.choices[ key ]['label'] }}"/>
				<# }
				if(data.choices[ key ]['label']){ #>
				<span class="tab-label">{{ data.choices[ key ]['label'] }}</span>
				<# } #>
				<# } #>
			</label>
			<# } #>

		</div>
		<?php
	}
}
PK      \fy  y    customizer/controls/heading.phpnu W+A        <?php
/**
 * Customizer Heading.
 *
 * @since   1.0.0
 * @package Neve\Customizer\Controls
 */

namespace Neve\Customizer\Controls;

/**
 * Heading control
 */
class Heading extends \WP_Customize_Control {

	/**
	 * The control type.
	 *
	 * @access public
	 * @var string
	 */
	public $type = 'customizer-heading';

	/**
	 * Control class.
	 *
	 * @var string
	 */
	public $class = '';

	/**
	 * Should be accordion?
	 *
	 * @var bool
	 */
	public $accordion = false;

	/**
	 * Initial state.
	 *
	 * @var bool
	 */
	public $expanded = true;

	/**
	 * How many controls to wrap.
	 *
	 * @var int
	 */
	public $controls_to_wrap = 1;

	/**
	 * Label before the accordion.
	 *
	 * @var string
	 */
	public $category_label = '';

	/**
	 * Send data to _s
	 *
	 * @return array
	 */
	public function json() {
		$json                   = parent::json();
		$json['classes']        = $this->class;
		$json['accordion']      = $this->accordion;
		$json['category_label'] = $this->category_label;

		if ( $this->accordion === true ) {
			$json['classes'] .= ' accordion';
		}

		$json['style'] = $this->print_style();

		return $json;
	}

	/**
	 * Render the control.
	 */
	protected function render() {
		$id     = 'customize-control-' . str_replace( array( '[', ']' ), array( '-', '' ), $this->id );
		$class  = 'customize-control customize-control-' . $this->type;
		$class .= ' ' . $this->class;
		if ( $this->accordion ) {
			$class .= ' accordion';
		}

		if ( $this->expanded ) {
			$class .= ' expanded';
		}

		echo '<li id="' . esc_attr( $id ) . '" class="' . esc_attr( $class ) . '">';
		echo '</li>';
	}

	/**
	 * An Underscore (JS) template for this control's content (but not its container).
	 *
	 * Class variables for this control class are available in the `data` JS object;
	 * export custom variables by overriding {@see WP_Customize_Control::to_json()}.
	 *
	 * @see    WP_Customize_Control::print_template()
	 *
	 * @access protected
	 */
	protected function content_template() {
		?>
		<# if(data.category_label) {#>
		<span class="customize-control-title">{{data.category_label}}</span>
		<# }#>
		<div class="neve-customizer-heading">
			<span class="accordion-heading">{{ data.label }}</span>
			<# if(data.accordion) { #>
			<span class="accordion-expand-button"></span>
			<# } #>
		</div>
		{{{data.style}}} <?php // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation ?>
		<?php
	}

	/**
	 * Print the style for the accordion.
	 */
	protected function print_style() {
		$style  = '';
		$style .= '<style>';
		for ( $i = 1; $i <= $this->controls_to_wrap; $i ++ ) {
			$style .= '.accordion.' . $this->class . ':not(.expanded)';
			for ( $j = 1; $j <= $i; $j ++ ) {
				$style .= ' + li';
			}
			if ( $i !== $this->controls_to_wrap ) {
				$style .= ',';
			}
		}
		$style .= '{max-height: 0;opacity: 0;margin: 0; overflow: hidden; padding:0 !important;}';
		$style .= '</style>';

		return $style;
	}
}
PK      \ϻC    )  customizer/options/layout_single_page.phpnu W+A        <?php
/**
 * Single page layout section.
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Customizer\Types\Control;

/**
 * Class Layout_Single_Page
 */
class Layout_Single_Page extends Base_Layout_Single {

	/**
	 * Returns the post type.
	 *
	 * @return string
	 */
	public function get_post_type() {
		return 'page';
	}

	/**
	 * @return string
	 */
	public function get_cover_selector() {
		return '.page .nv-post-cover';
	}

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		parent::add_controls();
		$this->add_control(
			new Control(
				'neve_page_hide_title',
				[
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => false,
				],
				[
					'label'    => esc_html__( 'Disable Title', 'neve' ),
					'section'  => $this->section,
					'type'     => 'neve_toggle_control',
					'priority' => 25,
				],
				'Neve\Customizer\Controls\Checkbox'
			)
		);
	}

	/**
	 * Fuction used for active_callback control property.
	 *
	 * @return bool
	 */
	public static function is_cover_layout() {
		return get_theme_mod( 'neve_page_header_layout' ) === 'cover';
	}
}
PK      \$B    :  customizer/options/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \Nb,<    '  customizer/options/layout_container.phpnu W+A        <?php
/**
 * Container layout section.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      20/08/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Section;
use Neve\Customizer\Defaults\Layout;
use Neve\Core\Settings\Config;

/**
 * Class Layout_Container
 *
 * @package Neve\Customizer\Options
 */
class Layout_Container extends Base_Customizer {
	use Layout;

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		$this->section_container();
		$this->control_container_width();
		$this->control_vertical_spacing();
		$this->control_container_style();
	}

	/**
	 * Add customize section
	 */
	private function section_container() {
		$this->add_section(
			new Section(
				'neve_container',
				array(
					'priority' => 25,
					'title'    => esc_html__( 'Container', 'neve' ),
					'panel'    => 'neve_layout',
				)
			)
		);
	}

	/**
	 * Add container width control
	 */
	private function control_container_width() {
		$this->add_control(
			new Control(
				'neve_container_width',
				[
					'sanitize_callback' => 'neve_sanitize_range_value',
					'transport'         => $this->selective_refresh,
					'default'           => '{ "mobile": 748, "tablet": 992, "desktop": 1170 }',
				],
				[
					'label'                 => esc_html__( 'Container width', 'neve' ),
					'section'               => 'neve_container',
					'type'                  => 'neve_responsive_range_control',
					'input_attrs'           => [
						'min'        => 200,
						'max'        => 2000,
						'units'      => [ 'px' ],
						'defaultVal' => [
							'mobile'  => 748,
							'tablet'  => 992,
							'desktop' => 1170,
							'suffix'  => [
								'mobile'  => 'px',
								'tablet'  => 'px',
								'desktop' => 'px',
							],
						],
					],
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'selector'   => 'body',
							'vars'       => '--container',
							'responsive' => true,
							'suffix'     => 'px',
						],
					],
					'priority'              => 25,
				],
				'\Neve\Customizer\Controls\React\Responsive_Range'
			)
		);
	}

	/**
	 * Add vertical spacing control
	 */
	private function control_vertical_spacing() {
		$this->add_control(
			new Control(
				Config::MODS_CONTENT_VSPACING,
				[
					'default'   => $this->content_vspacing_default(),
					'transport' => $this->selective_refresh,
				],
				[
					'label'                 => __( 'Content Vertical Spacing', 'neve' ),
					'sanitize_callback'     => [ $this, 'sanitize_spacing_array' ],
					'section'               => 'neve_container',
					'input_attrs'           => [
						'units' => [ 'px', 'vh' ],
						'axis'  => 'vertical',
					],
					'default'               => $this->content_vspacing_default(),
					'priority'              => 26,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar'      => [
							'vars'       => '--c-vspace',
							'selector'   => 'body.single:not(.single-product), body.page',
							'responsive' => true,
							'fallback'   => '',
						],
						'directional' => true,
					],
				],
				'\Neve\Customizer\Controls\React\Spacing'
			)
		);
	}

	/**
	 * Add container style controls
	 */
	private function control_container_style() {
		$container_style_controls = array(
			'neve_default_container_style'      => array(
				'priority' => 30,
				'label'    => __( 'Default Container Style', 'neve' ),
			),
			'neve_blog_archive_container_style' => array(
				'priority' => 35,
				'label'    => __( 'Blog / Archive Container Style', 'neve' ),
			),
			'neve_single_post_container_style'  => array(
				'priority' => 40,
				'label'    => __( 'Single Post Container Style', 'neve' ),
			),
		);

		if ( class_exists( 'WooCommerce', false ) ) {
			$container_style_controls = array_merge(
				$container_style_controls,
				array(
					'neve_shop_archive_container_style'   => array(
						'priority' => 45,
						'label'    => __( 'Shop / Archive Container Style', 'neve' ),
					),
					'neve_single_product_container_style' => array(
						'priority' => 50,
						'label'    => __( 'Single Product Container Style', 'neve' ),
					),
				)
			);
		}

		/**
		 * Filters the container style controls.
		 *
		 * @param array $container_style_controls Container style controls.
		 *
		 * @since 3.1.0
		 */
		$container_style_controls = apply_filters( 'neve_container_style_filter', $container_style_controls );

		foreach ( $container_style_controls as $control_id => $control ) {
			$this->add_control(
				new Control(
					$control_id,
					array(
						'sanitize_callback' => 'neve_sanitize_container_layout',
						'transport'         => $this->selective_refresh,
						'default'           => 'contained',
					),
					array(
						'label'    => $control['label'],
						'section'  => 'neve_container',
						'type'     => 'select',
						'priority' => $control['priority'],
						'choices'  => array(
							'contained'  => __( 'Contained', 'neve' ),
							'full-width' => __( 'Full Width', 'neve' ),
						),
					)
				)
			);
		}
	}
}
PK      \77P  P  )  customizer/options/base_layout_single.phpnu W+A        <?php
/**
 * Common functionalities for pages and posts.
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Defaults\Layout;
use Neve\Customizer\Defaults\Single_Post;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Section;
use Neve\Core\Settings\Config;

/**
 * Class Base_Layout_Single
 *
 * @package Neve\Customizer\Options
 */
abstract class Base_Layout_Single extends Base_Customizer {
	use Single_Post;
	use Layout;
	/**
	 * Post type slug
	 *
	 * @var string
	 */
	private $post_type;

	/**
	 * Controls section
	 *
	 * @var string
	 */
	public $section;

	/**
	 * Cover selector
	 *
	 * @var string
	 */
	private $cover_selector;

	/**
	 * Get the value for the $post_type.
	 *
	 * @return mixed
	 */
	abstract public function get_post_type();

	/**
	 * Get the value for the $post_type.
	 *
	 * @return mixed
	 */
	abstract public function get_cover_selector();

	/**
	 * Base_Layout_Single constructor.
	 */
	public function __construct() {
		$this->post_type      = $this->get_post_type();
		$this->cover_selector = $this->get_cover_selector();
		$this->section        = 'neve_single_' . $this->post_type . '_layout';
	}

	/**
	 * Get the label for sections.
	 */
	private function get_section_label() {
		$labels = [
			'post' => esc_html__( 'Single Post', 'neve' ),
			'page' => esc_html__( 'Page', 'neve' ),
		];

		if ( array_key_exists( $this->post_type, $labels ) ) {
			return $labels[ $this->post_type ];
		}

		return '';
	}

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		$this->create_section();
		$this->add_header_layout_subsection();
		$this->add_header_layout_controls();
		$this->content_vspacing();
	}

	/**
	 * Create the section
	 */
	private function create_section() {
		$this->add_section(
			new Section(
				'neve_single_' . $this->post_type . '_layout',
				[
					'priority' => 40,
					'title'    => $this->get_section_label(),
					'panel'    => 'neve_layout',
				]
			)
		);
	}

	/**
	 * Add header layout accordion.
	 */
	private function add_header_layout_subsection() {
		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_header_layout_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'            => esc_html__( 'Header Layout', 'neve' ),
					'section'          => $this->section,
					'priority'         => 5,
					'class'            => 'header_layout-accordion',
					'expanded'         => true,
					'accordion'        => true,
					'controls_to_wrap' => 15,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);
	}

	/**
	 * Add controls for header layout.
	 */
	private function add_header_layout_controls() {

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_header_layout',
				[
					'sanitize_callback' => 'wp_filter_nohtml_kses',
					'default'           => 'normal',
				],
				[
					'section'  => $this->section,
					'priority' => 10,
					'choices'  => [
						'normal' => [
							'name'  => esc_html__( 'Normal', 'neve' ),
							'image' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODUiIGhlaWdodD0iMTE4IiB2aWV3Qm94PSIwIDAgODUgMTE4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgPHJlY3QgeD0iMi4yNSIgeT0iMi40NjM4NyIgd2lkdGg9IjgwIiBoZWlnaHQ9IjExMyIgZmlsbD0id2hpdGUiLz4KICAgIDxyZWN0IHg9IjE3LjI1IiB5PSIxNC42MDQ1IiB3aWR0aD0iNTAiIGhlaWdodD0iMzQuNTUzNyIgZmlsbD0iIzc4QjZGRiIgZmlsbC1vcGFjaXR5PSIwLjQiLz4KICAgIDxsaW5lIHgxPSIxNy4yNSIgeTE9IjYyLjY5MjQiIHgyPSI2Ny4wNzkyIiB5Mj0iNjIuNjkyNCIgc3Ryb2tlPSIjQzRDNEM0IiBzdHJva2Utd2lkdGg9IjIiLz4KICAgIDxsaW5lIHgxPSIxNy4yNSIgeTE9IjY3Ljc2OTUiIHgyPSIyNS4yNSIgeTI9IjY3Ljc2OTUiIHN0cm9rZT0iI0M0QzRDNCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgICA8bGluZSB4MT0iMTcuMjUiIHkxPSI1Ny40OTcxIiB4Mj0iNTEuMDA1MyIgeTI9IjU3LjQ5NzEiIHN0cm9rZT0iI0M0QzRDNCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgICA8bGluZSB4MT0iMTcuMjUiIHkxPSI4Ny4zODA5IiB4Mj0iNjcuMDc5MiIgeTI9Ijg3LjM4MDkiIHN0cm9rZT0iI0M0QzRDNCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgICA8bGluZSB4MT0iMTcuMjUiIHkxPSI5Mi41NzYyIiB4Mj0iNjcuMDc5MiIgeTI9IjkyLjU3NjIiIHN0cm9rZT0iI0M0QzRDNCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgICA8bGluZSB4MT0iMTcuMjUiIHkxPSI5OC4wNjE1IiB4Mj0iNjcuMDc5MiIgeTI9Ijk4LjA2MTUiIHN0cm9rZT0iI0M0QzRDNCIgc3Ryb2tlLXdpZHRoPSIyIi8+Cjwvc3ZnPgo=',
						],
						'cover'  => [
							'name'  => esc_html__( 'Cover', 'neve' ),
							'image' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODYiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgODYgMTIwIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgPHJlY3QgeD0iMyIgeT0iMy40NjM4NyIgd2lkdGg9IjgwIiBoZWlnaHQ9IjExMyIgZmlsbD0id2hpdGUiLz4KICAgIDxyZWN0IHg9IjMiIHk9IjMuNDYzODciIHdpZHRoPSI4MCIgaGVpZ2h0PSI1MS4zNjM2IiBmaWxsPSIjNzhCNkZGIiBmaWxsLW9wYWNpdHk9IjAuNCIvPgogICAgPGxpbmUgeDE9IjE5IiB5MT0iNjcuNDI4NyIgeDI9IjY4LjgyOTIiIHkyPSI2Ny40Mjg3IiBzdHJva2U9IiNDNEM0QzQiIHN0cm9rZS13aWR0aD0iMiIvPgogICAgPGxpbmUgeDE9IjE5IiB5MT0iODMuNzExOSIgeDI9IjY4LjgyOTIiIHkyPSI4My43MTE5IiBzdHJva2U9IiNDNEM0QzQiIHN0cm9rZS13aWR0aD0iMiIvPgogICAgPGxpbmUgeDE9IjE5IiB5MT0iNzIuNjI0IiB4Mj0iNjguODI5MiIgeTI9IjcyLjYyNCIgc3Ryb2tlPSIjQzRDNEM0IiBzdHJva2Utd2lkdGg9IjIiLz4KICAgIDxsaW5lIHgxPSIxOSIgeTE9Ijg4LjkwNzIiIHgyPSI2OC44MjkyIiB5Mj0iODguOTA3MiIgc3Ryb2tlPSIjQzRDNEM0IiBzdHJva2Utd2lkdGg9IjIiLz4KICAgIDxsaW5lIHgxPSIxOSIgeTE9Ijc4LjEwODQiIHgyPSI2OC44MjkyIiB5Mj0iNzguMTA4NCIgc3Ryb2tlPSIjQzRDNEM0IiBzdHJva2Utd2lkdGg9IjIiLz4KICAgIDxsaW5lIHgxPSIxOSIgeTE9Ijk0LjM5MjYiIHgyPSI2OC44MjkyIiB5Mj0iOTQuMzkyNiIgc3Ryb2tlPSIjQzRDNEM0IiBzdHJva2Utd2lkdGg9IjIiLz4KICAgIDxsaW5lIHgxPSIxOSIgeTE9IjgzLjcxMTkiIHgyPSI0OCIgeTI9IjgzLjcxMTkiIHN0cm9rZT0iI0M0QzRDNCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgICA8bGluZSB4MT0iMTkiIHkxPSI5OS45OTYxIiB4Mj0iNDgiIHkyPSI5OS45OTYxIiBzdHJva2U9IiNDNEM0QzQiIHN0cm9rZS13aWR0aD0iMiIvPgogICAgPGxpbmUgeDE9IjE5IiB5MT0iNDQuNDg5MyIgeDI9IjUyLjc1NTMiIHkyPSI0NC40ODkzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz4KICAgIDxsaW5lIHgxPSIxOSIgeTE9IjM4Ljg4NTciIHgyPSI2OSIgeTI9IjM4Ljg4NTciIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMiIvPgogICAgPHJlY3QgeD0iMS41IiB5PSIxLjk2Mzg3IiB3aWR0aD0iODMiIGhlaWdodD0iMTE2IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjMiLz4KPC9zdmc+Cg==',
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Image'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_cover_height',
				[
					'sanitize_callback' => 'neve_sanitize_range_value',
					'transport'         => $this->selective_refresh,
					'default'           => '{ "mobile": 250, "tablet": 320, "desktop": 400 }',
				],
				[
					'label'                 => esc_html__( 'Cover height', 'neve' ),
					'section'               => $this->section,
					'type'                  => 'neve_responsive_range_control',
					'input_attrs'           => [
						'max'        => 700,
						'units'      => [ 'px', 'vh', 'em', 'rem' ],
						'defaultVal' => [
							'mobile'  => 250,
							'tablet'  => 320,
							'desktop' => 400,
							'suffix'  => [
								'mobile'  => 'px',
								'tablet'  => 'px',
								'desktop' => 'px',
							],
						],
					],
					'priority'              => 15,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'responsive' => true,
							'vars'       => '--height',
							'selector'   => $this->cover_selector,
							'suffix'     => 'px',
						],
					],
					'active_callback'       => [ $this, 'is_cover_layout' ],
				],
				'\Neve\Customizer\Controls\React\Responsive_Range'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_cover_padding',
				[
					'sanitize_callback' => [ $this, 'sanitize_spacing_array' ],
					'transport'         => $this->selective_refresh,
					'default'           => $this->padding_default( 'cover' ),
				],
				[
					'label'                 => esc_html__( 'Cover padding', 'neve' ),
					'section'               => $this->section,
					'input_attrs'           => [
						'units' => [ 'px', 'em', 'rem' ],
						'min'   => 0,
					],
					'default'               => $this->padding_default( 'cover' ),
					'priority'              => 20,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => array(
							'vars'       => '--padding',
							'selector'   => $this->cover_selector,
							'responsive' => true,
						),
					],
					'active_callback'       => [ $this, 'is_cover_layout' ],
				],
				'\Neve\Customizer\Controls\React\Spacing'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_title_alignment',
				[
					'sanitize_callback' => 'neve_sanitize_alignment',
					'transport'         => $this->selective_refresh,
					'default'           => self::post_title_alignment(),
				],
				[
					'label'                 => esc_html__( 'Title Alignment', 'neve' ),
					'section'               => $this->section,
					'priority'              => 30,
					'choices'               => [
						'left'   => [
							'tooltip' => esc_html__( 'Left', 'neve' ),
							'icon'    => 'editor-alignleft',
						],
						'center' => [
							'tooltip' => esc_html__( 'Center', 'neve' ),
							'icon'    => 'editor-aligncenter',
						],
						'right'  => [
							'tooltip' => esc_html__( 'Right', 'neve' ),
							'icon'    => 'editor-alignright',
						],
					],
					'show_labels'           => true,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'       => [
								'--textalign',
								'--justify',
							],
							'valueRemap' => [
								'--justify' => [
									'left'   => 'flex-start',
									'center' => 'center',
									'right'  => 'flex-end',
								],
							],
							'responsive' => true,
							'selector'   => $this->cover_selector . ',' . $this->cover_selector . ' .container, ' . ( $this->post_type === 'post' ? '.entry-header' : '.nv-page-title-wrap' ),
						],
					],
					'active_callback'       => $this->post_type === 'post' ? '__return_true' : function() {
						return ! get_theme_mod( 'neve_page_hide_title', false );
					},
				],
				'\Neve\Customizer\Controls\React\Responsive_Radio_Buttons'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_title_position',
				[
					'sanitize_callback' => 'neve_sanitize_position',
					'transport'         => $this->selective_refresh,
					'default'           => [
						'mobile'  => 'center',
						'tablet'  => 'center',
						'desktop' => 'center',
					],
				],
				[
					'label'                 => esc_html__( 'Title Position', 'neve' ),
					'section'               => $this->section,
					'priority'              => 35,
					'choices'               => [
						'flex-start' => [
							'tooltip' => esc_html__( 'Top', 'neve' ),
							'icon'    => 'arrow-up',
						],
						'center'     => [
							'tooltip' => esc_html__( 'Middle', 'neve' ),
							'icon'    => 'sort',
						],
						'flex-end'   => [
							'tooltip' => esc_html__( 'Bottom', 'neve' ),
							'icon'    => 'arrow-down',
						],
					],
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'       => '--valign',
							'responsive' => true,
							'selector'   => $this->cover_selector,
						],
					],
					'show_labels'           => true,
					'active_callback'       => function() {
						return $this->post_type === 'post' ? $this->is_cover_layout() : $this->is_cover_layout() && ! get_theme_mod( 'neve_page_hide_title', false );
					},
				],
				'\Neve\Customizer\Controls\React\Responsive_Radio_Buttons'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_cover_background_color',
				[
					'sanitize_callback' => 'neve_sanitize_colors',
					'default'           => 'var(--nv-dark-bg)',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'                 => esc_html__( 'Overlay color', 'neve' ),
					'section'               => $this->section,
					'priority'              => 45,
					'input_attrs'           => [
						'allow_gradient' => true,
					],
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => array(
							'vars'     => '--bgcolor',
							'selector' => $this->cover_selector . ' .nv-overlay',
						),
					],
					'active_callback'       => [ $this, 'is_cover_layout' ],
				],
				'Neve\Customizer\Controls\React\Color'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_cover_text_color',
				[
					'sanitize_callback' => 'neve_sanitize_colors',
					'default'           => 'var(--nv-text-dark-bg)',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'                 => esc_html__( 'Text color', 'neve' ),
					'section'               => $this->section,
					'priority'              => 50,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => '--color',
							'selector' => $this->cover_selector . ' .nv-title-meta-wrap',
						],
					],
					'active_callback'       => function() {
						return $this->post_type === 'post' ? $this->is_cover_layout() : $this->is_cover_layout() && ! get_theme_mod( 'neve_page_hide_title', false );
					},
				],
				'Neve\Customizer\Controls\React\Color'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_cover_overlay_opacity',
				[
					'sanitize_callback' => 'neve_sanitize_range_value',
					'transport'         => $this->selective_refresh,
					'default'           => 50,
				],
				[
					'label'                 => esc_html__( 'Overlay opacity', 'neve' ) . '(%)',
					'section'               => $this->section,
					'input_attrs'           => [
						'min'        => 0,
						'max'        => 100,
						'step'       => 1,
						'defaultVal' => 50,
					],
					'priority'              => 55,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => '--opacity',
							'selector' => $this->cover_selector . ' .nv-overlay',
						],
					],
					'active_callback'       => [ $this, 'is_cover_layout' ],
				],
				'Neve\Customizer\Controls\React\Range'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_cover_hide_thumbnail',
				[
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => false,
				],
				[
					'label'           => esc_html__( 'Hide featured image', 'neve' ),
					'section'         => $this->section,
					'type'            => 'neve_toggle_control',
					'priority'        => 60,
					'active_callback' => [ $this, 'is_cover_layout' ],
				],
				'Neve\Customizer\Controls\Checkbox'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_cover_blend_mode',
				[
					'default'           => 'normal',
					'sanitize_callback' => 'neve_sanitize_blend_mode',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'                 => esc_html__( 'Blend mode', 'neve' ),
					'section'               => $this->section,
					'priority'              => 65,
					'type'                  => 'select',
					'choices'               => [
						'normal'      => esc_html__( 'Normal', 'neve' ),
						'multiply'    => esc_html__( 'Multiply', 'neve' ),
						'screen'      => esc_html__( 'Screen', 'neve' ),
						'overlay'     => esc_html__( 'Overlay', 'neve' ),
						'darken'      => esc_html__( 'Darken', 'neve' ),
						'lighten'     => esc_html__( 'Lighten', 'neve' ),
						'color-dodge' => esc_html__( 'Color Dodge', 'neve' ),
						'saturation'  => esc_html__( 'Saturation', 'neve' ),
						'color'       => esc_html__( 'Color', 'neve' ),
						'difference'  => esc_html__( 'Difference', 'neve' ),
						'exclusion'   => esc_html__( 'Exclusion', 'neve' ),
						'hue'         => esc_html__( 'Hue', 'neve' ),
						'luminosity'  => esc_html__( 'Luminosity', 'neve' ),
					],
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => '--blendmode',
							'selector' => $this->cover_selector . ' .nv-overlay',
						],
					],
					'active_callback'       => [ $this, 'is_cover_layout' ],
				]
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_cover_container',
				[
					'default'           => 'contained',
					'sanitize_callback' => 'neve_sanitize_container_layout',
				],
				[
					'label'           => esc_html__( 'Cover container', 'neve' ),
					'section'         => $this->section,
					'priority'        => 70,
					'type'            => 'select',
					'choices'         => [
						'contained'  => esc_html__( 'Contained', 'neve' ),
						'full-width' => esc_html__( 'Full width', 'neve' ),
					],
					'active_callback' => function() {
						return $this->post_type === 'post' ? $this->is_cover_layout() : $this->is_cover_layout() && ! get_theme_mod( 'neve_page_hide_title', false );
					},
				]
			)
		);

		$this->add_boxed_layout_controls(
			$this->post_type . '_cover_title',
			[
				'priority'               => 75,
				'section'                => $this->section,
				'has_text_color'         => false,
				'padding_default'        => $this->padding_default( 'cover' ),
				'background_default'     => 'var(--nv-dark-bg)',
				'boxed_selector'         => $this->cover_selector . ' .nv-is-boxed.nv-title-meta-wrap',
				'toggle_active_callback' => function() {
					return $this->post_type === 'post' ? $this->is_cover_layout() : $this->is_cover_layout() && ! get_theme_mod( 'neve_page_hide_title', false );
				},
				'active_callback'        => function() {
					$is_cover = $this->post_type === 'post' ? $this->is_cover_layout() : $this->is_cover_layout() && ! get_theme_mod( 'neve_page_hide_title', false );
					return $is_cover && get_theme_mod( 'neve_' . $this->post_type . '_cover_title_boxed_layout', false );
				},
			]
		);
	}

	/**
	 * Add content spacing control.
	 */
	private function content_vspacing() {

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_page_settings_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'            => esc_html__( 'Page', 'neve' ) . ' ' . esc_html__( 'Settings', 'neve' ),
					'section'          => $this->section,
					'priority'         => 90,
					'class'            => 'page-settings-accordion',
					'expanded'         => false,
					'accordion'        => true,
					'controls_to_wrap' => 2,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_inherit_vspacing',
				[
					'sanitize_callback' => 'neve_sanitize_vspace_type',
					'default'           => 'inherit',
				],
				[
					'label'              => esc_html__( 'Content Vertical Spacing', 'neve' ),
					'section'            => $this->section,
					'priority'           => 95,
					'choices'            => [
						'inherit'  => [
							'tooltip' => esc_html__( 'Inherit', 'neve' ),
							'icon'    => 'text',
						],
						'specific' => [
							'tooltip' => esc_html__( 'Custom', 'neve' ),
							'icon'    => 'text',
						],
					],
					'footer_description' => [
						'inherit' => [
							'template'         => esc_html__( 'Customize the default vertical spacing <ctaButton>here</ctaButton>.', 'neve' ),
							'control_to_focus' => Config::MODS_CONTENT_VSPACING,
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Buttons'
			)
		);

		$default_value = get_theme_mod( Config::MODS_CONTENT_VSPACING, $this->content_vspacing_default() );
		$this->add_control(
			new Control(
				'neve_' . $this->post_type . '_content_vspacing',
				[
					'default'   => $default_value,
					'transport' => $this->selective_refresh,
				],
				[
					'label'                 => __( 'Custom Value', 'neve' ),
					'sanitize_callback'     => [ $this, 'sanitize_spacing_array' ],
					'section'               => $this->section,
					'input_attrs'           => [
						'units'     => [ 'px', 'vh' ],
						'axis'      => 'vertical',
						'dependsOn' => [ 'neve_' . $this->post_type . '_inherit_vspacing' => 'specific' ],
					],
					'default'               => $default_value,
					'priority'              => 100,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar'      => [
							'vars'       => '--c-vspace',
							'selector'   => 'body.' . $this->post_type . ' .neve-main',
							'responsive' => true,
							'fallback'   => '',
						],
						'directional' => true,
					],
				],
				'\Neve\Customizer\Controls\React\Spacing'
			)
		);
	}

	/**
	 * Fuction used for active_callback control property.
	 *
	 * @return bool
	 */
	abstract public static function is_cover_layout();
}
PK      \8H      customizer/options/main.phpnu W+A        <?php
/**
 * Handles main customzier setup like root panels.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      20/08/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Core\Settings\Mods;
use Neve\Customizer\Controls\React\Documentation_Section;
use Neve\Customizer\Controls\React\Instructions_Section;
use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Controls\Simple_Upsell;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Panel;
use Neve\Customizer\Types\Section;

/**
 * Main customizer handler.
 */
class Main extends Base_Customizer {
	/**
	 * Add controls.
	 */
	public function add_controls() {
		$this->register_types();
		$this->add_main_panels();
		$this->change_controls();
	}

	/**
	 * Register customizer controls type.
	 */
	private function register_types() {
		$this->register_type( 'Neve\Customizer\Controls\Radio_Image', 'control' );
		$this->register_type( 'Neve\Customizer\Controls\Range', 'control' );
		$this->register_type( 'Neve\Customizer\Controls\Responsive_Number', 'control' );
		$this->register_type( 'Neve\Customizer\Controls\Tabs', 'control' );
		$this->register_type( 'Neve\Customizer\Controls\Heading', 'control' );
		$this->register_type( 'Neve\Customizer\Controls\Checkbox', 'control' );
		$this->register_type( 'Neve\Customizer\Controls\Upsell_Control', 'control' );
	}

	/**
	 * Add main panels.
	 */
	private function add_main_panels() {
		$panels = array(
			'neve_layout'     => array(
				'priority' => 25,
				'title'    => __( 'Layout', 'neve' ),
			),
			'neve_typography' => array(
				'priority' => 35,
				'title'    => __( 'Typography', 'neve' ),
			),
		);

		foreach ( $panels as $panel_id => $panel ) {
			$this->add_panel(
				new Panel(
					$panel_id,
					array(
						'priority' => $panel['priority'],
						'title'    => $panel['title'],
					)
				)
			);
		}
		$this->wpc->add_section(
			new Instructions_Section(
				$this->wpc,
				'neve_typography_quick_links',
				array(
					'priority' => - 100,
					'panel'    => 'neve_typography',
					'type'     => 'hfg_instructions',
					'options'  => array(
						'quickLinks' => array(
							'neve_body_font_family'     => array(
								'label' => esc_html__( 'Change main font', 'neve' ),
								'icon'  => 'dashicons-editor-spellcheck',
							),
							'neve_headings_font_family' => array(
								'label' => esc_html__( 'Change headings font', 'neve' ),
								'icon'  => 'dashicons-heading',
							),
							'neve_h1_accordion_wrap'    => array(
								'label' => esc_html__( 'Change H1 font size', 'neve' ),
								'icon'  => 'dashicons-info-outline',
							),
							'neve_archive_typography_post_title' => array(
								'label' => esc_html__( 'Change archive font size', 'neve' ),
								'icon'  => 'dashicons-sticky',
							),
						),
					),
				)
			)
		);

		$this->wpc->add_section(
			new Documentation_Section(
				$this->wpc,
				'neve_documentation',
				[
					'priority' => PHP_INT_MAX,
					'title'    => esc_html__( 'Neve', 'neve' ),
					'url'      => tsdk_utmify( 'https://docs.themeisle.com/article/946-neve-doc', 'docsbtn' ),
				]
			)
		);
	}

	/**
	 * Change controls
	 */
	protected function change_controls() {
		$this->change_customizer_object( 'section', 'static_front_page', 'panel', 'neve_layout' );
		// Change default for shop columns WooCommerce option.
		$this->change_customizer_object( 'setting', 'woocommerce_catalog_columns', 'default', 3 );
	}
}
PK      \^A  A  !  customizer/options/typography.phpnu W+A        <?php
/**
 * Customizer typography controls.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      20/08/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Controls\React\Typography_Extra_Section;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Section;
use Neve\Core\Traits\Theme_Mods;

/**
 * Class Typography
 *
 * @package Neve\Customizer\Options
 */
class Typography extends Base_Customizer {
	use Theme_Mods;

	const HEADINGS_FONT_FAMILY_SELECTORS = 'h1:not(.site-title), .single h1.entry-title, h2, h3, .woocommerce-checkout h3, h4, h5, h6';

	/**
	 * Add controls
	 */
	public function add_controls() {
		$this->sections_typography();
		$this->controls_font_pairs();
		$this->controls_typography_general();
		$this->controls_typography_headings();
		$this->controls_typography_blog();
		$this->section_extra();
	}

	/**
	 * Add controls for font pair section
	 *
	 * @return void
	 */
	private function controls_font_pairs() {
		/**
		 * Filters the font pairs that are available inside Customizer.
		 *
		 * @param array $pairs The font pairs array.
		 *
		 * @since 3.5.0
		 */
		$pairs = apply_filters( 'neve_font_pairings', Mods::get( Config::MODS_TPOGRAPHY_FONT_PAIRS, Config::$typography_default_pairs ) );

		/**
		 * Font Pairs Control
		 */
		$this->add_control(
			new Control(
				Config::MODS_TPOGRAPHY_FONT_PAIRS,
				[
					'transport'         => $this->selective_refresh,
					'sanitize_callback' => 'sanitize_text_field',
					'default'           => $pairs,
				],
				array(
					'input_attrs' => array(
						'pairs'       => $pairs,
						'description' => array(
							'text' => __( 'Choose Font family presets for your Headings and Text.', 'neve' ),
							'link' => apply_filters( 'neve_external_link', 'https://docs.themeisle.com/article/1340-neve-typography', esc_html__( 'Learn more', 'neve' ) ),
						),
					),
					'label'       => esc_html__( 'Font presets', 'neve' ),
					'section'     => 'typography_font_pair_section',
					'priority'    => 10,
					'type'        => 'neve_font_pairings_control',
				),
				'\Neve\Customizer\Controls\React\Font_Pairings'
			)
		);
	}

	/**
	 * Add the customizer section.
	 */
	private function sections_typography() {
		$typography_sections = array(
			'typography_font_pair_section' => array(
				'title'    => __( 'Font presets', 'neve' ),
				'priority' => 15,
			),
			'neve_typography_general'      => array(
				'title'    => __( 'General', 'neve' ),
				'priority' => 25,
			),
			'neve_typography_headings'     => array(
				'title'    => __( 'Headings', 'neve' ),
				'priority' => 35,
			),
			'neve_typography_blog'         => array(
				'title'    => __( 'Blog', 'neve' ),
				'priority' => 45,
			),
		);

		foreach ( $typography_sections as $section_id => $section_data ) {
			$this->add_section(
				new Section(
					$section_id,
					array(
						'title'    => $section_data['title'],
						'panel'    => 'neve_typography',
						'priority' => $section_data['priority'],
					)
				)
			);
		}
	}

	/**
	 * Add general typography controls
	 */
	private function controls_typography_general() {

		/**
		 * Body font family
		 */
		$this->add_control(
			new Control(
				Config::MODS_FONT_GENERAL,
				[
					'transport'         => $this->selective_refresh,
					'sanitize_callback' => 'sanitize_text_field',
					'default'           => Mods::get_alternative_mod_default( Config::MODS_FONT_GENERAL ),
				],
				array(
					'settings'              => [
						'default'  => Config::MODS_FONT_GENERAL,
						'variants' => Config::MODS_FONT_GENERAL_VARIANTS,
					],
					'label'                 => esc_html__( 'Body', 'neve' ),
					'section'               => 'neve_typography_general',
					'priority'              => 10,
					'type'                  => 'neve_font_family_control',
					'live_refresh_selector' => apply_filters( 'neve_body_font_family_selectors', 'body, .site-title' ),
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => '--bodyfontfamily',
							'selector' => 'body',
							'fallback' => Mods::get_alternative_mod_default( Config::MODS_FONT_GENERAL ),
							'suffix'   => ', var(--nv-fallback-ff)',
						],
					],
				),
				'\Neve\Customizer\Controls\React\Font_Family'
			)
		);
		/**
		 * Body font family subsets.
		 */
		$this->wpc->add_setting(
			Config::MODS_FONT_GENERAL_VARIANTS,
			[
				'transport'         => $this->selective_refresh,
				'sanitize_callback' => 'neve_sanitize_font_variants',
				'default'           => [],
			]
		);


		$defaults = Mods::get_alternative_mod_default( Config::MODS_TYPEFACE_GENERAL );
		$this->add_control(
			new Control(
				Config::MODS_TYPEFACE_GENERAL,
				[
					'transport' => $this->selective_refresh,
					'default'   => $defaults,
				],
				[
					'priority'              => 11,
					'section'               => 'neve_typography_general',
					'input_attrs'           => array(
						'size_units'             => [ 'px', 'em', 'rem' ],
						'weight_default'         => 400,
						'size_default'           => $defaults['fontSize'],
						'line_height_default'    => $defaults['lineHeight'],
						'letter_spacing_default' => $defaults['letterSpacing'],
					),
					'type'                  => 'neve_typeface_control',
					'font_family_control'   => 'neve_body_font_family',
					'live_refresh_selector' => 'body, .site-title',
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => [
								'--bodytexttransform' => 'textTransform',
								'--bodyfontweight'    => 'fontWeight',
								'--bodyfontsize'      => [
									'key'        => 'fontSize',
									'responsive' => true,
								],
								'--bodylineheight'    => [
									'key'        => 'lineHeight',
									'responsive' => true,
								],
								'--bodyletterspacing' => [
									'key'        => 'letterSpacing',
									'suffix'     => 'px',
									'responsive' => true,
								],
							],
							'selector' => 'body',
						],
					],
				],
				'\Neve\Customizer\Controls\React\Typography'
			)
		);

		/**
		 * Fallback Font Family.
		 */
		$this->add_control(
			new Control(
				'neve_fallback_font_family',
				[
					'transport'         => $this->selective_refresh,
					'sanitize_callback' => 'sanitize_text_field',
					'default'           => 'Arial, Helvetica, sans-serif',
				],
				[
					'label'                 => esc_html__( 'Fallback Font', 'neve' ),
					'section'               => 'neve_typography_general',
					'priority'              => 12,
					'type'                  => 'neve_font_family_control',
					'input_attrs'           => [
						'system' => true,
						'link'   => [
							'string'  => __( 'Learn more about fallback fonts', 'neve' ),
							'url'     => esc_url( 'https://docs.themeisle.com/article/1319-fallback-fonts' ),
							'new_tab' => true,
						],
					],
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => '--nv-fallback-ff',
							'selector' => 'body',
						],
					],
				],
				'\Neve\Customizer\Controls\React\Font_Family'
			)
		);

	}

	/**
	 * Add controls for typography headings.
	 */
	private function controls_typography_headings() {
		/**
		 * Headings font family
		 */
		$this->add_control(
			new Control(
				'neve_headings_font_family',
				array(
					'transport'         => $this->selective_refresh,
					'sanitize_callback' => 'sanitize_text_field',
				),
				array(
					'section'               => 'neve_typography_headings',
					'priority'              => 10,
					'type'                  => 'neve_font_family_control',
					'live_refresh_selector' => apply_filters( 'neve_headings_font_family_selectors', self::HEADINGS_FONT_FAMILY_SELECTORS ),
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => '--headingsfontfamily',
							'selector' => 'body',
							'fallback' => 'var(--bodyfontfamily, var(--nv-fallback-ff))',
						],
						'type'   => 'svg-icon-size',
					],
					'input_attrs'           => [
						'default_is_inherit' => true,
					],
				),
				'\Neve\Customizer\Controls\React\Font_Family'
			)
		);

		$selectors = neve_get_headings_selectors();
		$priority  = 20;
		foreach ( [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ] as $heading_id ) {
			$this->add_control(
				new Control(
					'neve_' . $heading_id . '_accordion_wrap',
					array(
						'sanitize_callback' => 'sanitize_text_field',
						'transport'         => $this->selective_refresh,
					),
					array(
						'label'            => $heading_id,
						'section'          => 'neve_typography_headings',
						'priority'         => $priority += 1,
						'class'            => esc_attr( 'advanced-sidebar-accordion-' . $heading_id ),
						'accordion'        => true,
						'controls_to_wrap' => 2,
						'expanded'         => false,
					),
					'Neve\Customizer\Controls\React\Heading'
				)
			);

			$mod_key_font_family = $this->get_mod_key_heading_fontfamily( $heading_id );

			/**
			 * Headings font family
			 */
			$this->add_control(
				new Control(
					$mod_key_font_family,
					array(
						'transport'         => $this->selective_refresh,
						'sanitize_callback' => 'sanitize_text_field',
					),
					array(
						'section'               => 'neve_typography_headings',
						'priority'              => $priority += 1,
						'type'                  => 'neve_font_family_control',
						'live_refresh_selector' => apply_filters( $mod_key_font_family . '_selectors', self::HEADINGS_FONT_FAMILY_SELECTORS ),
						'live_refresh_css_prop' => [
							'cssVar' => [
								'vars'     => '--' . $heading_id . 'fontfamily',
								'selector' => 'body',
								'fallback' => 'var(--headingsfontfamily, var(--bodyfontfamily))',
							],
							'type'   => 'svg-icon-size',
						],
						'input_attrs'           => [
							'default_is_inherit' => true,
						],
					),
					'\Neve\Customizer\Controls\React\Font_Family'
				)
			);

			$mod_key        = 'neve_' . $heading_id . '_typeface_general';
			$default_values = Mods::get_alternative_mod_default( $mod_key );
			$this->add_control(
				new Control(
					$mod_key,
					[
						'transport' => $this->selective_refresh,
						'default'   => $default_values,
					],
					[
						'priority'              => $priority += 2,
						'section'               => 'neve_typography_headings',
						'input_attrs'           => array(
							'size_units'             => [ 'em', 'px', 'rem' ],
							'weight_default'         => $default_values['fontWeight'],
							'size_default'           => $default_values['fontSize'],
							'line_height_default'    => $default_values['lineHeight'],
							'letter_spacing_default' => $default_values['letterSpacing'],
						),
						'type'                  => 'neve_typeface_control',
						'font_family_control'   => $mod_key_font_family,
						'live_refresh_selector' => $selectors[ $heading_id ],
						'live_refresh_css_prop' => [
							'cssVar' => [
								'vars'     => [
									'--' . $heading_id . 'texttransform' => 'textTransform',
									'--' . $heading_id . 'fontweight'    => 'fontWeight',
									'--' . $heading_id . 'fontsize'      => [
										'key'        => 'fontSize',
										'responsive' => true,
									],
									'--' . $heading_id . 'lineheight'    => [
										'key'        => 'lineHeight',
										'responsive' => true,
									],
									'--' . $heading_id . 'letterspacing' => [
										'key'        => 'letterSpacing',
										'suffix'     => 'px',
										'responsive' => true,
									],
								],
								'selector' => 'body',
							],
						],
					],
					'\Neve\Customizer\Controls\React\Typography'
				)
			);
		}
	}

	/**
	 * Add controls for blog typography.
	 */
	private function controls_typography_blog() {
		$controls = array(
			'neve_archive_typography_post_title'         => array(
				'label'                 => __( 'Post title', 'neve' ),
				'category_label'        => __( 'Blog Archive', 'neve' ),
				'priority'              => 10,
				'font_family_control'   => 'neve_headings_font_family',
				'live_refresh_selector' => '.blog .blog-entry-title, .archive .blog-entry-title',
			),
			'neve_archive_typography_post_excerpt'       => array(
				'label'                 => __( 'Post excerpt', 'neve' ),
				'priority'              => 20,
				'font_family_control'   => 'neve_body_font_family',
				'live_refresh_selector' => '.blog .entry-summary, .archive .entry-summary, .blog .post-pages-links',
			),
			'neve_archive_typography_post_meta'          => array(
				'label'                 => __( 'Post meta', 'neve' ),
				'priority'              => 30,
				'font_family_control'   => 'neve_body_font_family',
				'live_refresh_selector' => '.blog .nv-meta-list li, .archive .nv-meta-list li',
			),
			'neve_single_post_typography_post_title'     => array(
				'label'                 => __( 'Post title', 'neve' ),
				'category_label'        => __( 'Single Post', 'neve' ),
				'priority'              => 40,
				'font_family_control'   => 'neve_headings_font_family',
				'live_refresh_selector' => '.single h1.entry-title',
			),
			'neve_single_post_typography_post_meta'      => array(
				'label'                 => __( 'Post meta', 'neve' ),
				'priority'              => 50,
				'font_family_control'   => 'neve_body_font_family',
				'live_refresh_selector' => '.single .nv-meta-list li',
			),
			'neve_single_post_typography_comments_title' => array(
				'label'                 => __( 'Comments reply title', 'neve' ),
				'priority'              => 60,
				'font_family_control'   => 'neve_headings_font_family',
				'live_refresh_selector' => '.single .comment-reply-title',
			),
		);

		foreach ( $controls as $control_id => $control_settings ) {
			$settings = array(
				'label'            => $control_settings['label'],
				'section'          => 'neve_typography_blog',
				'priority'         => $control_settings['priority'],
				'class'            => esc_attr( 'typography-blog-' . $control_id ),
				'accordion'        => true,
				'controls_to_wrap' => 1,
				'expanded'         => false,
			);
			if ( array_key_exists( 'category_label', $control_settings ) ) {
				$settings['category_label'] = $control_settings['category_label'];
			}

			$this->add_control(
				new Control(
					$control_id . '_accordion_wrap',
					array(
						'sanitize_callback' => 'sanitize_text_field',
						'transport'         => $this->selective_refresh,
					),
					$settings,
					'Neve\Customizer\Controls\Heading'
				)
			);

			$this->add_control(
				new Control(
					$control_id,
					[
						'transport' => $this->selective_refresh,
					],
					[
						'priority'              => $control_settings['priority'] += 1,
						'section'               => 'neve_typography_blog',
						'type'                  => 'neve_typeface_control',
						'font_family_control'   => $control_settings['font_family_control'],
						'live_refresh_selector' => true,
						'live_refresh_css_prop' => [
							'cssVar' => [
								'vars'     => [
									'--texttransform' => 'textTransform',
									'--fontweight'    => 'fontWeight',
									'--fontsize'      => [
										'key'        => 'fontSize',
										'responsive' => true,
									],
									'--lineheight'    => [
										'key'        => 'lineHeight',
										'responsive' => true,
									],
									'--letterspacing' => [
										'key'        => 'letterSpacing',
										'suffix'     => 'px',
										'responsive' => true,
									],
								],
								'selector' => $control_settings['live_refresh_selector'],
							],
						],
						'refresh_on_reset'      => true,
						'input_attrs'           => array(
							'default_is_empty'       => true,
							'size_units'             => [ 'em', 'px', 'rem' ],
							'weight_default'         => 'none',
							'size_default'           => array(
								'suffix'  => array(
									'mobile'  => 'px',
									'tablet'  => 'px',
									'desktop' => 'px',
								),
								'mobile'  => '',
								'tablet'  => '',
								'desktop' => '',
							),
							'line_height_default'    => array(
								'mobile'  => '',
								'tablet'  => '',
								'desktop' => '',
							),
							'letter_spacing_default' => array(
								'mobile'  => '',
								'tablet'  => '',
								'desktop' => '',
							),
						),
					],
					'\Neve\Customizer\Controls\React\Typography'
				)
			);
		}
	}

	/**
	 * Add section for extra inline controls
	 *
	 * @return void
	 */
	private function section_extra() {
		$this->wpc->add_section(
			new Typography_Extra_Section(
				$this->wpc,
				'typography_extra_section',
				[
					'priority' => 9999, // upsell priority(10000) - 1
					'panel'    => 'neve_typography',
				]
			)
		);
	}
}

PK      \wh|  |  "  customizer/options/layout_blog.phpnu W+A        <?php
/**
 * Blog layout section.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      20/08/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Defaults\Layout;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Section;

/**
 * Class Layout_Blog
 *
 * @package Neve\Customizer\Options
 */
class Layout_Blog extends Base_Customizer {
	use Layout;

	/**
	 * Holds the section name.
	 *
	 * @var string $section
	 */
	private $section = 'neve_blog_archive_layout';

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		$this->section_blog();
		$this->add_layout_controls();
		$this->add_featured_post();
		$this->add_content_ordering_controls();
		$this->add_post_meta_controls();
		$this->add_typography_shortcut();

		add_action( 'customize_register', [ $this, 'adapt_old_pro' ], PHP_INT_MAX );
	}

	/**
	 * Adapting old pro versions to make them still usable with the old theme version.
	 */
	public function adapt_old_pro() {
		if ( ! defined( 'NEVE_PRO_VERSION' ) ) {
			return;
		}

		if ( version_compare( NEVE_PRO_VERSION, '1.2.8', '>' ) ) {
			return;
		}

		$changes = [
			'neve_posts_order'                   => [ 'priority' => 51 ],
			'neve_post_content_ordering'         => [ 'active_callback' => '__return_true' ],
			'neve_blog_ordering_content_heading' => [ 'controls_to_wrap' => 6 ],
			'neve_blog_post_meta_heading'        => [ 'controls_to_wrap' => 3 ],
			'neve_read_more_options'             => [
				'accordion' => true,
				'expanded'  => false,
				'class'     => 'read-more-options-accordion',
			],
		];

		foreach ( $changes as $control_slug => $props ) {
			foreach ( $props as $prop => $new_value ) {
				$this->change_customizer_object( 'control', $control_slug, $prop, $new_value );
			}
		}

		$this->wpc->remove_control( 'neve_metadata_options' );
	}

	/**
	 * Add customize section
	 */
	private function section_blog() {
		$this->add_section(
			new Section(
				$this->section,
				array(
					'priority' => 35,
					'title'    => esc_html__( 'Blog / Archive', 'neve' ),
					'panel'    => 'neve_layout',
				)
			)
		);
	}

	/**
	 * Add blog layout controls.
	 */
	private function add_layout_controls() {
		$this->add_control(
			new Control(
				'neve_blog_layout_heading',
				array(
					'sanitize_callback' => 'sanitize_text_field',
				),
				array(
					'label'            => esc_html__( 'Blog Layout', 'neve' ),
					'section'          => $this->section,
					'priority'         => 10,
					'class'            => 'blog-layout-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 6,
				),
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				$this->section,
				[
					'default'           => 'grid',
					'sanitize_callback' => [ $this, 'sanitize_blog_layout' ],
				],
				[
					'section'  => $this->section,
					'priority' => 11,
					'choices'  => [
						'default' => [
							'name'  => __( 'List', 'neve' ),
							'image' => 'data:image/png;base64,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',
						],
						'covers'  => [
							'name'  => __( 'Covers', 'neve' ),
							'image' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAF4AAAB5CAYAAACwe5bgAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMvSURBVHgB7d1PTxNBGMfxp1JTLH8ExXAg8eDNd+Xr9eaNg8FEQxSpgIVSKBTccdMEaElnO535PYfvJ9lLQ8j0y3ZKuzu7rfuKobgXBgnCixBehPAihBchvAjhRQgvQngRwosQXqQd80PXt2a9gbmws27WWXn82O2d2dG5ubDdNeu+nP9z0eEP++bC5up0+PGdn/F12nHhmWpECC9CeBHCixBehPAihBchvAjhRQgvQngRwosQXoTwIoQXIbwI4UWijkA9tLNWbV3LZv/Ykmx0zPY2LZtvp/URuVSNw4fDbuHwm1fexzfROPxxddD7/Nrc6ldj2/9t2Sxjbw8ahx+N680r7+ObaBx++1W9LSr3KybM8e/WbGGnw3rLrXH4cOrCTsIT619V4S2fMMenjC9MJS7DnwzT5rl+5veH8PsP/tjCBjdWROPww5t68yrM771Lc48PUCKt2HWuy/o3KtVKNYe3W9OPex/fUy0WGGsw1YgQXoTwItHnx3v5fmarO/3mFRYmnBX40BMjfHLuRFSNDn9wYi58bNdP7qGwMMHL+D68iQvPVCNCeBHCixBehPAihBchvAjhRQgvQngRwosQXoTwIoQXIbwI4UUIL0J4kcan8LWrP9VKxj9X6olJ3sc30Tj87rrZ3mvL5vMPS7K1Wh33fGvZfPkpWooTjuh7OV1ulvG97/FNNA5/dFFvXpVaWJCKN1eRxnt8WMqYMseHRQM5z18PS0FT5vjDv2UuHsoeL9J4jw97g5fLyc4SXk2sCMGzovb48IHk6fmKKs99OPIyvk7kHMKKEBGmGhHCixBehPAiUe/BlyOz72fmwvvt6VtBhC/uvvbMhd2NuItsRIUPT6zvZA1UWHYz6zEv44u9gAVTjQjhRQgvQngRwosQXoTwIoQXIbwI4UUIL0J4EcKLEF6E8CKEFyG8SPGTVudJXZiQetLqPMtamMAeL8JJqyLs8SLF7wP16yLvVVFT7wP1/xUzsOyK3wcq95NKHV+pS/kWvw/UYGRZpd4H6qrQikHuAyXCm6sICxNE2ONFCC9CeBHCixBehPAihBchvAjhRQgvQniR8O3kJ0Nx/wAXONq4kNEpFQAAAABJRU5ErkJggg==',
						],
						'grid'    => [
							'name'  => __( 'Grid', 'neve' ),
							'image' => 'data:image/jpeg;base64,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',
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Image'
			)
		);

		$grid_layout_default = '{"desktop":3,"tablet":2,"mobile":1}';

		$this->add_control(
			new Control(
				'neve_grid_layout',
				array(
					'sanitize_callback' => 'neve_sanitize_range_value',
					'default'           => $grid_layout_default,
				),
				array(
					'label'           => esc_html__( 'Columns', 'neve' ),
					'section'         => $this->section,
					'units'           => array(
						'items',
					),
					'input_attrs'     => [
						'min'        => 1,
						'max'        => 4,
						'defaultVal' => json_decode( $grid_layout_default, true ),
					],
					'priority'        => 11,
					'active_callback' => array( $this, 'is_column_layout' ),
				),
				'Neve\Customizer\Controls\React\Responsive_Range'
			)
		);

		$this->add_control(
			new Control(
				'neve_blog_covers_text_color',
				array(
					'sanitize_callback' => 'neve_sanitize_colors',
					'default'           => '#ffffff',
					'transport'         => 'postMessage',
				),
				array(
					'label'                 => esc_html__( 'Text Color', 'neve' ),
					'section'               => $this->section,
					'priority'              => 15,
					'default'               => '#ffffff',
					'active_callback'       => function () {
						return get_theme_mod( $this->section ) === 'covers';
					},
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => '--color',
							'selector' => '.neve-main',
						],
					],
				),
				'Neve\Customizer\Controls\React\Color'
			)
		);

		$this->add_control(
			new Control(
				'neve_archive_hide_title',
				[
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => false,
				],
				[
					'label'           => esc_html__( 'Disable Title', 'neve' ),
					'section'         => $this->section,
					'type'            => 'neve_toggle_control',
					'priority'        => 16,
					'active_callback' => function() {
						return get_option( 'show_on_front' ) !== 'posts';
					},
				],
				'Neve\Customizer\Controls\Checkbox'
			)
		);

		$this->add_control(
			new Control(
				'neve_blog_list_alternative_layout',
				array(
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => false,
				),
				array(
					'type'            => 'neve_toggle_control',
					'priority'        => 17,
					'section'         => $this->section,
					'label'           => esc_html__( 'Alternating layout', 'neve' ),
					'active_callback' => function () {
						$is_list_layout = get_theme_mod( $this->section ) === 'default';
						$has_image      = true;
						if ( $is_list_layout && defined( 'NEVE_PRO_VERSION' ) ) {
							$has_image = get_theme_mod( 'neve_blog_list_image_position', 'left' ) !== 'no';
						}
						return $is_list_layout && $has_image;
					},
				)
			)
		);

		$this->add_control(
			new Control(
				'neve_enable_masonry',
				array(
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => false,
				),
				array(
					'type'            => 'neve_toggle_control',
					'priority'        => 35,
					'section'         => $this->section,
					'label'           => esc_html__( 'Enable Masonry', 'neve' ),
					'active_callback' => array( $this, 'should_show_masonry' ),
				)
			)
		);
	}

	/**
	 * Add featured post controls.
	 */
	private function add_featured_post() {
		$this->add_control(
			new Control(
				'neve_featured_post_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'            => esc_html__( 'Featured Post', 'neve' ),
					'section'          => $this->section,
					'priority'         => 40,
					'class'            => 'featured-post-accordion',
					'accordion'        => true,
					'expanded'         => false,
					'controls_to_wrap' => 2,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_enable_featured_post',
				[
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => false,
				],
				[
					'label'    => esc_html__( 'Enable featured post section', 'neve' ),
					'section'  => $this->section,
					'type'     => 'neve_toggle_control',
					'priority' => 41,
				],
				'Neve\Customizer\Controls\Checkbox'
			)
		);

		$this->add_control(
			new Control(
				'neve_featured_post_target',
				array(
					'default'           => 'latest',
					'sanitize_callback' => array( $this, 'sanitize_featured_post_target' ),
				),
				array(
					'label'           => esc_html__( 'Featured Post', 'neve' ),
					'section'         => $this->section,
					'priority'        => 42,
					'type'            => 'select',
					'choices'         => [
						'latest' => esc_html__( 'Latest Post', 'neve' ),
						'sticky' => esc_html__( 'Sticky Post', 'neve' ),
					],
					'active_callback' => function() {
						return get_theme_mod( 'neve_enable_featured_post', false );
					},
				)
			)
		);
	}

	/**
	 * Add content ordering and controls.
	 */
	private function add_content_ordering_controls() {
		$this->add_control(
			new Control(
				'neve_blog_ordering_content_heading',
				array(
					'sanitize_callback' => 'sanitize_text_field',
				),
				array(
					'label'            => esc_html__( 'Ordering and Content', 'neve' ),
					'section'          => $this->section,
					'priority'         => 50,
					'class'            => 'blog-layout-ordering-content-accordion',
					'accordion'        => true,
					'expanded'         => false,
					'controls_to_wrap' => 4,
				),
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_pagination_type',
				array(
					'default'           => 'number',
					'sanitize_callback' => array( $this, 'sanitize_pagination_type' ),
				),
				array(
					'label'    => esc_html__( 'Post Pagination', 'neve' ),
					'section'  => $this->section,
					'priority' => 53,
					'type'     => 'select',
					'choices'  => array(
						'number'   => esc_html__( 'Number', 'neve' ),
						'infinite' => esc_html__( 'Infinite Scroll', 'neve' ),
						'jump-to'  => esc_html__( 'Number', 'neve' ) . ' & ' . esc_html__( 'Search Field', 'neve' ),
					),
				)
			)
		);

		$order_default_components = array(
			'thumbnail',
			'title-meta',
			'excerpt',
		);

		$components = array(
			'thumbnail'  => __( 'Thumbnail', 'neve' ),
			'title-meta' => __( 'Title & Meta', 'neve' ),
			'excerpt'    => __( 'Excerpt', 'neve' ),
		);

		$this->add_control(
			new Control(
				'neve_post_content_ordering',
				array(
					'sanitize_callback' => array( $this, 'sanitize_post_content_ordering' ),
					'default'           => wp_json_encode( $order_default_components ),
				),
				array(
					'label'      => esc_html__( 'Post Content Order', 'neve' ),
					'section'    => $this->section,
					'components' => $components,
					'priority'   => 55,
				),
				'Neve\Customizer\Controls\React\Ordering'
			)
		);

		$this->add_control(
			new Control(
				'neve_post_excerpt_length',
				array(
					'sanitize_callback' => 'neve_sanitize_range_value',
					'default'           => 25,
				),
				array(
					'label'       => esc_html__( 'Excerpt Length', 'neve' ),
					'section'     => $this->section,
					'type'        => 'neve_range_control',
					'input_attrs' => [
						'min'        => 5,
						'max'        => 300,
						'defaultVal' => 25,
						'step'       => 5,
					],
					'priority'    => 58,
				),
				'Neve\Customizer\Controls\React\Range'
			)
		);

		$this->add_control(
			new Control(
				'neve_post_thumbnail_box_shadow',
				array(
					'sanitize_callback' => 'absint',
					'default'           => 0,
				),
				array(
					'label'       => esc_html__( 'Thumbnail Shadow', 'neve' ),
					'section'     => $this->section,
					'type'        => 'neve_range_control',
					'step'        => 1,
					'input_attrs' => [
						'min'        => 0,
						'max'        => 5,
						'defaultVal' => 0,
					],
					'priority'    => 59,
				),
				'Neve\Customizer\Controls\React\Range'
			)
		);
	}

	/**
	 * Add controls for post meta.
	 */
	private function add_post_meta_controls() {
		$this->add_control(
			new Control(
				'neve_blog_post_meta_heading',
				array(
					'sanitize_callback' => 'sanitize_text_field',
				),
				array(
					'label'            => esc_html__( 'Post Meta', 'neve' ),
					'section'          => $this->section,
					'priority'         => 70,
					'class'            => 'blog-layout-post-meta-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 5,
					'expanded'         => false,
				),
				'Neve\Customizer\Controls\Heading'
			)
		);


		$default       = wp_json_encode( [ 'author', 'date', 'comments' ] );
		$default_value = neve_get_default_meta_value( 'neve_post_meta_ordering', $default );
		$this->add_control(
			new Control(
				'neve_blog_post_meta_fields',
				[
					'sanitize_callback' => 'neve_sanitize_meta_repeater',
					'default'           => wp_json_encode( $default_value ),
				],
				[
					'label'            => esc_html__( 'Meta Order', 'neve' ),
					'section'          => $this->section,
					'fields'           => [
						'hide_on_mobile' => [
							'type'  => 'checkbox',
							'label' => __( 'Hide on mobile', 'neve' ),
						],
					],
					'components'       => apply_filters(
						'neve_meta_filter',
						array(
							'author'   => __( 'Author', 'neve' ),
							'category' => __( 'Category', 'neve' ),
							'date'     => __( 'Date', 'neve' ),
							'comments' => __( 'Comments', 'neve' ),
						)
					),
					'allow_new_fields' => 'no',
					'priority'         => 71,
					'active_callback'  => [ $this, 'should_show_meta_order' ],
				],
				'\Neve\Customizer\Controls\React\Repeater'
			)
		);

		$this->add_control(
			new Control(
				'neve_metadata_separator',
				array(
					'sanitize_callback' => 'sanitize_text_field',
					'default'           => esc_html( '/' ),
				),
				array(
					'priority'    => 72,
					'section'     => $this->section,
					'label'       => esc_html__( 'Separator', 'neve' ),
					'description' => esc_html__( 'For special characters make sure to use Unicode. For example > can be displayed using \003E.', 'neve' ),
					'type'        => 'text',
				)
			)
		);

		$this->add_control(
			new Control(
				'neve_author_avatar',
				array(
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => false,
				),
				array(
					'label'    => esc_html__( 'Show Author Avatar', 'neve' ),
					'section'  => $this->section,
					'type'     => 'neve_toggle_control',
					'priority' => 73,
				)
			)
		);

		$this->add_control(
			new Control(
				'neve_author_avatar_size',
				array(
					'sanitize_callback' => 'neve_sanitize_range_value',
					'default'           => '{ "mobile": 20, "tablet": 20, "desktop": 20 }',
				),
				array(
					'label'           => esc_html__( 'Avatar Size', 'neve' ),
					'section'         => $this->section,
					'units'           => array(
						'px',
					),
					'input_attr'      => array(
						'mobile'  => array(
							'min'          => 20,
							'max'          => 50,
							'default'      => 20,
							'default_unit' => 'px',
						),
						'tablet'  => array(
							'min'          => 20,
							'max'          => 50,
							'default'      => 20,
							'default_unit' => 'px',
						),
						'desktop' => array(
							'min'          => 20,
							'max'          => 50,
							'default'      => 20,
							'default_unit' => 'px',
						),
					),
					'input_attrs'     => [
						'min'        => self::RELATIVE_CSS_UNIT_SUPPORTED_MIN_VALUE,
						'max'        => 50,
						'defaultVal' => [
							'mobile'  => 20,
							'tablet'  => 20,
							'desktop' => 20,
							'suffix'  => [
								'mobile'  => 'px',
								'tablet'  => 'px',
								'desktop' => 'px',
							],
						],
						'units'      => [ 'px', 'em', 'rem' ],
					],
					'priority'        => 74,
					'active_callback' => function () {
						return get_theme_mod( 'neve_author_avatar', false );
					},
					'responsive'      => true,
				),
				'Neve\Customizer\Controls\React\Responsive_Range'
			)
		);

		$this->add_control(
			new Control(
				'neve_show_last_updated_date',
				array(
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => false,
				),
				array(
					'label'    => esc_html__( 'Use last updated date instead of the published one', 'neve' ),
					'section'  => $this->section,
					'type'     => 'neve_toggle_control',
					'priority' => 85,
				)
			)
		);
	}

	/**
	 * Sanitize the container layout value
	 *
	 * @param string $value value from the control.
	 *
	 * @return string
	 */
	public function sanitize_blog_layout( $value ) {
		$allowed_values = array( 'default', 'covers', 'grid' );
		if ( ! in_array( $value, $allowed_values, true ) ) {
			return 'grid';
		}

		return sanitize_text_field( $value );
	}

	/**
	 * Sanitize the pagination type
	 *
	 * @param string $value value from the control.
	 *
	 * @return string
	 */
	public function sanitize_pagination_type( $value ) {
		$allowed_values = array( 'number', 'infinite', 'jump-to' );
		if ( ! in_array( $value, $allowed_values, true ) ) {
			return 'number';
		}

		return esc_html( $value );
	}

	/**
	 * Sanitize featured post target
	 *
	 * @param string $value value from the control.
	 *
	 * @return string
	 */
	public function sanitize_featured_post_target( $value ) {
		$allowed_values = [ 'sticky', 'latest' ];
		if ( ! in_array( $value, $allowed_values, true ) ) {
			return 'latest';
		}

		return esc_html( $value );
	}

	/**
	 * Sanitize content order control.
	 */
	public function sanitize_post_content_ordering( $value ) {
		$allowed = array(
			'thumbnail',
			'title-meta',
			'excerpt',
		);

		if ( empty( $value ) ) {
			return wp_json_encode( $allowed );
		}

		$decoded = json_decode( $value, true );

		foreach ( $decoded as $val ) {
			if ( ! in_array( $val, $allowed, true ) ) {
				return wp_json_encode( $allowed );
			}
		}

		return $value;
	}

	/**
	 * Callback to show the meta order control.
	 *
	 * @return bool
	 */
	public function should_show_meta_order() {
		$default       = array(
			'thumbnail',
			'title-meta',
			'excerpt',
		);
		$content_order = get_theme_mod( 'neve_post_content_ordering', wp_json_encode( $default ) );
		$content_order = json_decode( $content_order, true );
		if ( ! in_array( 'title-meta', $content_order, true ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Callback to show grid columns control.
	 *
	 * @return bool
	 */
	public function is_column_layout() {
		$blog_layout = get_theme_mod( $this->section, 'grid' );

		return in_array( $blog_layout, [ 'grid', 'covers' ], true );
	}

	/**
	 * Callback to show masonry control.
	 *
	 * @return bool
	 */
	public function should_show_masonry() {
		if ( ! $this->is_column_layout() ) {
			return false;
		}

		$columns = json_decode( get_theme_mod( 'neve_grid_layout', $this->grid_columns_default() ), true );
		$columns = array_filter(
			array_values( $columns ),
			function ( $value ) {
				return $value > 1;
			}
		);

		if ( empty( $columns ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Add typography shortcut.
	 */
	private function add_typography_shortcut() {
		$this->add_control(
			new Control(
				'neve_blog_typography_shortcut',
				array(
					'sanitize_callback' => 'neve_sanitize_text_field',
				),
				array(
					'button_class'     => 'nv-top-bar-menu-shortcut',
					'text_before'      => __( 'Customize Typography for the Archive page', 'neve' ),
					'text_after'       => '.',
					'button_text'      => __( 'here', 'neve' ),
					'is_button'        => false,
					'control_to_focus' => 'neve_archive_typography_post_title_accordion_wrap',
					'shortcut'         => true,
					'section'          => $this->section,
					'priority'         => 1000,
				),
				'\Neve\Customizer\Controls\Button'
			)
		);
	}
}
PK      \$B    =  customizer/options/js/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \`#Z    $  customizer/options/js/0_utilities.jsnu W+A        (function ( $ ) {
	$.neveCustomizeUtilities = {
		'setLiveCss': function ( settings, to ) {
			'use strict';
			var result = '';
			var styleClass = $( '.' + settings.styleClass );
			if ( typeof to !== 'object' ) {
				$( settings.selectors ).css( settings.cssProperty, to.toString() + settings.propertyUnit );
				return false;
			}

			$.each( to, function ( key, value ) {
				var style_to_add;
				if ( key === 'suffix' ) {
					return true;
				}
				var unit = settings.propertyUnit;
				if ( typeof settings.propertyUnit === 'object' ) {
					unit = settings.propertyUnit[ key ];
				}

				style_to_add = settings.selectors + '{ ' + settings.cssProperty + ':' + value + unit + '}';
				switch ( key ) {
					default:
					case 'mobile':
						result += style_to_add;
						break;
					case 'desktop':
						result += '@media(min-width: 960px) {' + style_to_add + '}';
						break;
					case 'tablet':
						result += '@media (min-width: 576px){' + style_to_add + '}';
						break;
				}
			} );
			if ( styleClass.length > 0 ) {
				styleClass.text( result );
			} else {
				$( 'head' ).append( '<style type="text/css" class="' + settings.styleClass + '">' + result + '</style>' );
			}
		}
	};
}( jQuery ));
PK      \xP  P  !  customizer/options/js/2_layout.jsnu W+A        /**
 * Handle the layout preview.
 */
(function ( $ ) {
	$.neveLayoutPreview = {

		/**
		 * Initialize the layout preview functions.
		 */
		init: function () {
			this.contentWidthsPreview();
			this.containersLivePreview();
		},

		/**
		 * Define content width controls and correspondent properties.
		 */
		contentWidths: {
			'neve_sitewide_content_width': {
				content: '.neve-main > .container .col, .neve-main > .container-fluid .col',
				sidebar: '.nv-sidebar-wrap'
			},
			'neve_blog_archive_content_width': {
				content: '.archive-container .nv-index-posts',
				sidebar: '.archive-container .nv-sidebar-wrap'
			},
			'neve_single_post_content_width': {
				content: '.single-post-container .nv-single-post-wrap',
				sidebar: '.single-post-container .nv-sidebar-wrap'
			},
			'neve_shop_archive_content_width': {
				content: '.archive.woocommerce .shop-container .nv-shop.col',
				sidebar: '.archive.woocommerce .shop-container .nv-sidebar-wrap'
			},
			'neve_single_product_content_width': {
				content: '.single-product .shop-container .nv-shop.col',
				sidebar: '.single-product .shop-container .nv-sidebar-wrap'
			},
			'neve_other_pages_content_width': {
				content: 'body:not(.single):not(.archive):not(.blog):not(.search) .neve-main > .container .col',
				sidebar: 'body:not(.single):not(.archive):not(.blog):not(.search) .nv-sidebar-wrap'
			},
		},

		/**
		 * Run the content width previews.
		 */
		contentWidthsPreview: function () {
			$.each( this.contentWidths, function ( id, args ) {
				wp.customize( id, function ( value ) {
					value.bind( function ( newval ) {
						jQuery( args.content ).css( 'max-width', newval + '%' );
						jQuery( args.sidebar ).css( 'max-width', 100 - newval + '%' );
					} );
				} );
			} );
		},

		/**
		 * Define container theme mods and correspondent selectors.
		 */
		containersLayoutMap: {
			'neve_default_container_style': '.page:not(.woocommerce) .single-page-container',
			'neve_blog_archive_container_style': '.archive-container',
			'neve_single_post_container_style': '.single-post-container',
			'neve_shop_archive_container_style': '.woocommerce-page.post-type-archive .neve-main > div',
			'neve_single_product_container_style': '.single-product .neve-main > div',
		},

		/**
		 * Run the containers live preview.
		 */
		containersLivePreview: function () {
			'use strict';
			$.each( this.containersLayoutMap, function ( controlId, cssSelector ) {
				cssSelector += ':not(.set-in-metabox)';
				wp.customize( controlId, function ( value ) {
					value.bind( function ( newval ) {
						if ( newval === 'contained' ) {
							$( cssSelector ).removeClass( 'container-fluid' ).addClass( 'container' );
							return false;
						}
						$( cssSelector ).removeClass( 'container' ).addClass( 'container-fluid' );
					} );
				} );
			} );
		},
	};
})( jQuery );

jQuery.neveLayoutPreview.init();


PK      \=wC  C  (  customizer/options/colors_background.phpnu W+A        <?php
/**
 * Colors / Background section.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      20/08/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Section;

/**
 * Class Colors_Background
 *
 * @package Neve\Customizer\Options
 */
class Colors_Background extends Base_Customizer {
	const CUSTOM_COLOR_LIMIT            = 30;
	const CUSTOM_COLOR_LABEL_MAX_LENGTH = 16;

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		$this->wpc->remove_control( 'background_color' );
		$this->section_colors_background();
		$this->controls_colors();
	}

	/**
	 * Add customize section
	 */
	private function section_colors_background() {
		$this->add_section(
			new Section(
				'neve_colors_background_section',
				array(
					'priority' => 27,
					'title'    => esc_html__( 'Colors & Background', 'neve' ),
				)
			)
		);
	}

	/**
	 * Add colors controls.
	 */
	private function controls_colors() {
		$this->add_global_colors();
	}

	/**
	 * Change controls.
	 */
	public function change_controls() {
		$priority         = 30;
		$controls_to_move = array(
			'background_image',
			'background_preset',
			'background_position',
			'background_size',
			'background_repeat',
			'background_attachment',
		);

		foreach ( $controls_to_move as $control_slug ) {
			$control           = $this->get_customizer_object( 'control', $control_slug );
			$control->priority = $priority;
			$control->section  = 'neve_colors_background_section';
			$priority         += 5;
		}
	}

	/**
	 * Add global colors.
	 */
	private function add_global_colors() {
		$this->add_control(
			new Control(
				'neve_global_colors',
				[
					'sanitize_callback' => [ $this, 'sanitize_global_colors' ],
					'default'           => neve_get_global_colors_default( true ),
					'transport'         => 'postMessage',
				],
				[
					'label'                 => __( 'Global Colors', 'neve' ),
					'priority'              => 10,
					'section'               => 'neve_colors_background_section',
					'type'                  => 'neve_global_colors',
					'default_values'        => neve_get_global_colors_default(),
					'input_attrs'           => [
						'link' => [
							'url'     => esc_url( 'https://docs.themeisle.com/article/1314-global-colors-in-neve' ),
							'string'  => esc_html__( 'How the color system works', 'neve' ),
							'new_tab' => true,
						],
					],
					'live_refresh_selector' => true,
				],
				'Neve\Customizer\Controls\React\Global_Colors'
			)
		);

		$this->add_control(
			new Control(
				'neve_global_custom_colors',
				[
					'sanitize_callback' => [ $this, 'sanitize_global_custom_colors' ],
					'default'           => [],
					'transport'         => 'refresh',
				],
				[
					'label'                 => __( 'Custom Colors', 'neve' ),
					'priority'              => 10,
					'section'               => 'neve_colors_background_section',
					'type'                  => 'neve_global_custom_colors',
					'default_values'        => [],
					'live_refresh_selector' => true,
				],
				'Neve\Customizer\Controls\React\Global_Custom_Colors'
			)
		);
	}

	/**
	 * Sanitize Global Colors Setting
	 *
	 * @param array $value recieved value.
	 * @return array
	 */
	public function sanitize_global_colors( $value ) {
		// `flag` key is used to trigger setting change on deep state changes inside the palettes.
		if ( isset( $value['flag'] ) ) {
			unset( $value['flag'] );
		}

		$default = neve_get_global_colors_default();
		if ( ! isset( $value['activePalette'] ) || ! isset( $value['palettes'] ) ) {
			return $default;
		}

		foreach ( $value['palettes'] as $slug => $args ) {
			foreach ( $args['colors'] as $key => $color_val ) {
				$value['palettes'][ $slug ]['colors'][ $key ] = neve_sanitize_colors( $color_val );
			}
		}

		return $value;
	}

	/**
	 * Sanitize Global Custom Colors Setting
	 *
	 * @param array $value recieved value.
	 * @return array
	 */
	public function sanitize_global_custom_colors( $value ) {
		// `flag` key is used to trigger setting change on deep state changes inside the palettes.
		if ( isset( $value['flag'] ) ) {
			unset( $value['flag'] );
		}

		if ( count( $value ) > self::CUSTOM_COLOR_LIMIT ) {
			$value = array_slice( $value, 0, self::CUSTOM_COLOR_LIMIT );
		}

		foreach ( $value as $slug => $options ) {
			$color = neve_sanitize_colors( $options['val'] );

			if ( ! $color ) {
				unset( $value[ $slug ] );
				continue;
			}

			$label = sanitize_text_field( $options['label'] );

			if ( strlen( $label ) > self::CUSTOM_COLOR_LABEL_MAX_LENGTH ) {
				$label = substr( $label, 0, self::CUSTOM_COLOR_LABEL_MAX_LENGTH );
			}

			$value[ $slug ]['label'] = $label;
			$value[ $slug ]['val']   = $color;
		}

		return $value;
	}
}
PK      \G      customizer/options/checkout.phpnu W+A        <?php
/**
 * Customizer checkout controls.
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Types\Section;
use Neve\Customizer\Types\Control;

/**
 * Class Checkout
 *
 * @package Neve\Customizer\Options
 */
class Checkout extends Base_Customizer {

	/**
	 * Init function
	 *
	 * @return bool|void
	 */
	public function init() {
		if ( defined( 'NEVE_PRO_VERSION' ) ) {
			return false;
		}

		parent::init();
	}

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		$this->group_controls();
	}

	/**
	 * Change the controls added from WooCommerce.
	 */
	public function change_controls() {
		$changes = [
			'woocommerce_checkout_company_field'       => [ 'priority' => 110 ],
			'woocommerce_checkout_address_2_field'     => [ 'priority' => 120 ],
			'woocommerce_checkout_phone_field'         => [ 'priority' => 130 ],
			'woocommerce_checkout_highlight_required_fields' => [
				'priority' => 140,
				'type'     => 'neve_toggle_control',
			],
			'wp_page_for_privacy_policy'               => [ 'priority' => 190 ],
			'woocommerce_terms_page_id'                => [ 'priority' => 200 ],
			'woocommerce_checkout_privacy_policy_text' => [ 'priority' => 210 ],
			'woocommerce_checkout_terms_and_conditions_checkbox_text' => [ 'priority' => 220 ],

		];

		foreach ( $changes as $control_slug => $props ) {
			foreach ( $props as $prop => $new_value ) {
				$this->change_customizer_object( 'control', $control_slug, $prop, $new_value );
			}
		}
	}

	/**
	 * Add control groups to better organize the customizer.
	 */
	private function group_controls() {
		$checkout_section = $this->wpc->get_section( 'woocommerce_checkout' );
		
		$this->wpc->add_section( $checkout_section );

		$this->add_control(
			new Control(
				'neve_checkout_settings_heading',
				array(
					'sanitize_callback' => 'sanitize_text_field',
					'transport'         => $this->selective_refresh,
				),
				array(
					'label'            => esc_html__( 'Checkout Style', 'neve' ),
					'section'          => 'woocommerce_checkout',
					'priority'         => 0,
					'class'            => 'neve-checkout-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 1,
					'expanded'         => true,
				),
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_woo_checkout_settings_heading',
				array(
					'sanitize_callback' => 'sanitize_text_field',
					'transport'         => $this->selective_refresh,
				),
				array(
					'label'            => esc_html__( 'General', 'neve' ),
					'section'          => 'woocommerce_checkout',
					'priority'         => 100,
					'class'            => 'woo-checkout-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 7,
					'expanded'         => true,
				),
				'Neve\Customizer\Controls\Heading'
			)
		);
	}
}
PK      \fv%  %  %  customizer/options/layout_sidebar.phpnu W+A        <?php
/**
 * Sidebar layout section.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      20/08/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Defaults\Layout;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Section;

/**
 * Class Layout_Sidebar
 *
 * @package Neve\Customizer\Options
 */
class Layout_Sidebar extends Base_Customizer {

	use Layout;

	/**
	 * Advanced controls.
	 *
	 * @var array
	 */
	private $advanced_controls = [];

	/**
	 * Layout_Sidebar constructor.
	 */
	public function __construct() {

		$this->advanced_controls = [
			'blog_archive' => __( 'Blog / Archive', 'neve' ),
			'single_post'  => __( 'Single Post', 'neve' ),
		];

		$this->add_woocommerce_controls();

		$this->advanced_controls['other_pages'] = __( 'Others', 'neve' );
	}

	/**
	 * Add woo controls.
	 */
	private function add_woocommerce_controls() {
		if ( ! class_exists( 'WooCommerce', false ) ) {
			return;
		}

		$this->advanced_controls = array_merge(
			$this->advanced_controls,
			[
				'shop_archive'   => __( 'Shop / Archive', 'neve' ),
				'single_product' => __( 'Single Product', 'neve' ),
			]
		);
	}


	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		$this->section_sidebar();
		$this->add_main_controls();
		$this->add_advanced_controls();
	}

	/**
	 * Add customize section
	 */
	private function section_sidebar() {
		$this->add_section(
			new Section(
				'neve_sidebar',
				array(
					'priority' => 30,
					'title'    => esc_html__( 'Content / Sidebar', 'neve' ),
					'panel'    => 'neve_layout',
				)
			)
		);
	}

	/**
	 * Site-wide controls that will affect all pages.
	 */
	private function add_main_controls() {
		$this->add_control(
			new Control(
				'neve_default_sidebar_layout',
				array(
					'sanitize_callback' => array( $this, 'sanitize_sidebar_layout' ),
					'default'           => 'right',
				),
				array(
					'label'           => __( 'Sitewide Sidebar Layout', 'neve' ),
					'section'         => 'neve_sidebar',
					'priority'        => 10,
					'choices'         => $this->sidebar_layout_choices( 'neve_default_sidebar_layout' ),
					'active_callback' => array( $this, 'sidewide_options_active_callback' ),
				),
				'\Neve\Customizer\Controls\React\Radio_Image'
			)
		);

		$this->add_control(
			new Control(
				'neve_sitewide_content_width',
				[
					'sanitize_callback' => 'absint',
					'transport'         => $this->selective_refresh,
					'default'           => 70,
				],
				[
					'label'           => esc_html__( 'Sitewide Content Width (%)', 'neve' ),
					'section'         => 'neve_sidebar',
					'type'            => 'neve_range_control',
					'input_attrs'     => [
						'min'        => 50,
						'max'        => 100,
						'defaultVal' => 70,
					],
					'priority'        => 20,
					'active_callback' => [ $this, 'sidewide_options_active_callback' ],
				],
				'Neve\Customizer\Controls\React\Range'
			)
		);

		$this->add_control(
			new Control(
				'neve_advanced_layout_options',
				array(
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => true,
				),
				array(
					'label'    => esc_html__( 'Enable Advanced Options', 'neve' ),
					'section'  => 'neve_sidebar',
					'type'     => 'neve_toggle_control',
					'priority' => 30,
				)
			)
		);
	}

	/**
	 * Get the sidebar layout choices.
	 *
	 * @param string $control_id Name of the control.
	 *
	 * @return array
	 */
	private function sidebar_layout_choices( $control_id ) {
		$options = apply_filters(
			'neve_sidebar_layout_choices',
			array(
				'full-width' => array(
					'name' => __( 'Full Width', 'neve' ),
					'url'  => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqAQMAAABknzrDAAAABlBMVEX////V1dXUdjOkAAAAPUlEQVRIx2NgGAUkAcb////Y/+d/+P8AdcQoc8vhH/X/5P+j2kG+GA3CCgrwi43aMWrHqB2jdowEO4YpAACyKSE0IzIuBgAAAABJRU5ErkJggg==',
				),
				'left'       => array(
					'name' => __( 'Left', 'neve' ),
					'url'  => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqAgMAAAAjP0ATAAAACVBMVEX///8+yP/V1dXG9YqxAAAAWElEQVR42mNgGAXDE4RCQMDAKONaBQINWqtWrWBatQDIaxg8ygYqQIAOYwC6bwHUmYNH2eBPSMhgBQXKRr0w6oVRL4x6YdQLo14Y9cKoF0a9QCO3jYLhBADvmFlNY69qsQAAAABJRU5ErkJggg==',
				),
				'right'      => array(
					'name' => __( 'Right', 'neve' ),
					'url'  => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqAgMAAAAjP0ATAAAACVBMVEX///8+yP/V1dXG9YqxAAAAWUlEQVR42mNgGAUjB4iGgkEIzZStAoEVTECiQWsVkLdiECkboAABOmwBF9BtUGcOImUDEiCkJCQU0ECBslEvjHph1AujXhj1wqgXRr0w6oVRLwyEF0bBUAUAz/FTNXm+R/MAAAAASUVORK5CYII=',
				),
			),
			$control_id
		);

		foreach ( $options as $slug => $args ) {
			if ( ! isset( $args['name'] ) ) {
				$options[ $slug ]['name'] = ucwords( str_replace( '-', ' ', $slug ) );
			}
		}

		return $options;
	}

	/**
	 * Advanced controls.
	 */
	private function add_advanced_controls() {
		$priority = 40;

		/**
		 * Filters the sidebar advanced controls.
		 *
		 * @param array $advanced_controls Container style controls.
		 *
		 * @since 3.1.0
		 */
		$this->advanced_controls = apply_filters( 'neve_sidebar_controls_filter', $this->advanced_controls );
		foreach ( $this->advanced_controls as $id => $heading_label ) {
			$heading_id = 'neve_' . $id . '_heading';
			$layout_id  = 'neve_' . $id . '_sidebar_layout';
			$width_id   = 'neve_' . $id . '_content_width';

			$this->add_control(
				new Control(
					$heading_id,
					array(
						'sanitize_callback' => 'sanitize_text_field',
						'transport'         => $this->selective_refresh,
					),
					array(
						'label'            => $heading_label,
						'section'          => 'neve_sidebar',
						'priority'         => $priority,
						'class'            => esc_attr( 'advanced-sidebar-accordion-' . $heading_id ),
						'accordion'        => true,
						'controls_to_wrap' => 2,
						'expanded'         => ( $priority === 40 ), // true or false
						'active_callback'  => array( $this, 'advanced_options_active_callback' ),
					),
					'Neve\Customizer\Controls\Heading'
				)
			);

			$priority += 30;

			$this->add_control(
				new Control(
					$layout_id,
					array(
						'sanitize_callback' => array( $this, 'sanitize_sidebar_layout' ),
						'default'           => $this->sidebar_layout_alignment_default( $layout_id ),
					),
					array(
						'label'           => __( 'Sidebar Layout', 'neve' ),
						'description'     => $this->get_sidebar_control_description( $layout_id ),
						'section'         => 'neve_sidebar',
						'priority'        => $priority,
						'choices'         => $this->sidebar_layout_choices( $layout_id ),
						'active_callback' => array( $this, 'advanced_options_active_callback' ),
					),
					'\Neve\Customizer\Controls\React\Radio_Image'
				)
			);

			$priority += 30;

			$width_default = $this->sidebar_layout_width_default( $width_id );

			$this->add_control(
				new Control(
					$width_id,
					array(
						'sanitize_callback' => 'absint',
						'transport'         => $this->selective_refresh,
						'default'           => $width_default,
					),
					array(
						'label'           => esc_html__( 'Content Width (%)', 'neve' ),
						'section'         => 'neve_sidebar',
						'type'            => 'neve_range_control',
						'input_attrs'     => [
							'min'        => 50,
							'max'        => 100,
							'defaultVal' => $width_default,
						],
						'priority'        => $priority,
						'active_callback' => array( $this, 'advanced_options_active_callback' ),
					),
					'Neve\Customizer\Controls\React\Range'
				)
			);

			$priority += 30;
		}
	}

	/**
	 * Get the description for sidebar position.
	 *
	 * @param string $control_id Control id.
	 *
	 * @return string
	 */
	private function get_sidebar_control_description( $control_id ) {
		if ( $control_id !== 'neve_shop_archive_sidebar_layout' ) {
			return '';
		}

		$shop_id = get_option( 'woocommerce_shop_page_id' );
		if ( empty( $shop_id ) ) {
			return '';
		}

		$meta = get_post_meta( $shop_id, 'neve_meta_sidebar', true );
		if ( empty( $meta ) || $meta === 'default' ) {
			return '';
		}

		/* translators: %s is Notice text */
		$template = '<p class="notice notice-info">%s</p>';

		return sprintf(
			$template,
			sprintf(
			/* translators: %s is edit page link */
				esc_html__( 'Note: It seems that the shop page has an individual sidebar layout already set. To be able to control the layout from here, %s your page and set the sidebar to "Inherit".', 'neve' ),
				sprintf(
				/* translators: %s is edit label */
					'<a target="_blank" href="' . get_edit_post_link( $shop_id ) . '">%s</a>',
					__( 'edit', 'neve' )
				)
			)
		);

	}

	/**
	 * Sanitize the sidebar layout value
	 *
	 * @param string $value value from the control.
	 *
	 * @return string
	 */
	public function sanitize_sidebar_layout( $value ) {
		$allowed_values = array( 'left', 'right', 'full-width', 'off-canvas' );
		if ( ! in_array( $value, $allowed_values, true ) ) {
			return 'right';
		}

		return esc_html( $value );
	}

	/**
	 * Active callback function for site-wide options
	 */
	public function sidewide_options_active_callback() {
		return ! $this->advanced_options_active_callback();
	}

	/**
	 * Active callback function for advanced controls
	 */
	public function advanced_options_active_callback() {
		return get_theme_mod( 'neve_advanced_layout_options', false );
	}
}
PK      \_sɺ  ɺ    customizer/options/upsells.phpnu W+A        <?php
/**
 * Customizer upsells controls.
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Core\Theme_Info;
use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Types\Section;
use Neve\Customizer\Types\Control;

/**
 * Class Upsells
 *
 * @package Neve\Customizer\Options
 */
class Upsells extends Base_Customizer {

	use Theme_Info;

	/**
	 * Holds the main upsell url
	 *
	 * @var string
	 */
	private $upsell_url = '';

	/**
	 * Init function
	 *
	 * @return bool|void
	 */
	public function init() {
		if ( $this->has_valid_addons() ) {
			return;
		}

		$this->upsell_url = esc_url_raw( apply_filters( 'neve_upgrade_link_from_child_theme_filter', tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'learnmorebtn' ) ) );

		parent::init();
		add_action( 'customize_controls_enqueue_scripts', array( $this, 'localize_upsell' ) );

		add_filter( 'theme_mod_neve_checkout_page_layout', array( $this, 'override_neve_checkout_page_layout_theme_mod' ), 10, 1 );
	}


	/**
	 * Dismiss the upsell banner for 7 days.
	 *
	 * @return void
	 */
	public static function remove_customizer_upsell_notice() {
		check_ajax_referer( 'neve-upsell-banner-nonce', 'nonce' );

		set_transient( 'upsell_dismiss_banner_customizer', true, 7 * DAY_IN_SECONDS );
		wp_send_json( [] );
	}

	/**
	 * Overrides neve_checkout_page_layout theme mod as 'standard' if Neve Pro addon is disabled or not activated.
	 *
	 * @param  string $value current value.
	 * @return string
	 */
	public function override_neve_checkout_page_layout_theme_mod( $value ) {
		return 'standard';
	}

	/**
	 * Localize upsell script and send strings.
	 */
	public function localize_upsell() {
		wp_localize_script(
			'neve-customizer-controls',
			'upsellConfig',
			array(
				'button_url'    => esc_url_raw( apply_filters( 'neve_upgrade_link_from_child_theme_filter', tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'proheader' ) ) ),
				'button_text'   => esc_html__( 'Get the PRO version!', 'neve' ),
				'text'          => esc_html__( 'Extend your header with more components and settings, build sticky/transparent headers or display them conditionally.', 'neve' ),
				'screen_reader' => esc_html__( '(opens in a new tab)', 'neve' ),
			)
		);
	}

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		$this->wpc->register_section_type( '\Neve\Customizer\Controls\Simple_Upsell_Section' );
		$this->wpc->register_section_type( '\Neve\Customizer\Controls\React\Upsell_Banner_Section' );
		$this->wpc->register_control_type( '\Neve\Customizer\Controls\React\Upsell_Banner' );
		$this->section_upsells();
		$this->control_upsells();
		$this->checkout_locked_layout();
		$this->product_catalog_upsells();
	}

	/**
	 * Locked Checkout Layouts
	 *
	 * @return void
	 */
	private function checkout_locked_layout() {
		$this->add_control(
			new Control(
				'neve_checkout_page_layout',
				[
					'default'           => 'standard',
					'sanitize_callback' => [ $this, 'sanitize_checkout_layout' ],
				],
				[
					'section'  => 'woocommerce_checkout',
					'priority' => 5,
					'choices'  => [
						'standard' => [
							'name'  => esc_html__( 'Standard', 'neve' ),
							'image' => 'data:image/png;base64,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',
						],
						'vertical' => [
							'name'      => esc_html__( 'Vertical', 'neve' ),
							'image'     => 'data:image/png;base64,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',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'woocheckoutvertical' ),
						],
						'stepped'  => [
							'name'      => esc_html__( 'Stepped', 'neve' ),
							'image'     => 'data:image/png;base64,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',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'woocheckoutstepped' ),
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Image'
			)
		);
	}

	/**
	 * Locked options on Product Catalog
	 *
	 * @return void
	 */
	private function product_catalog_upsells() {
		$this->add_control(
			new Control(
				'neve_product_card_layout_ui_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'            => esc_html__( 'Layout', 'neve' ),
					'section'          => 'woocommerce_product_catalog',
					'priority'         => 200,
					'class'            => 'card-layout-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 2,
					'expanded'         => false,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_product_card_layout',
				[
					'transport'         => 'postMessage',
					'default'           => 'grid',
					'sanitize_callback' => function() {
						return 'grid';
					},
				],
				[
					'label'    => esc_html__( 'Layout', 'neve' ),
					'section'  => 'woocommerce_product_catalog',
					'priority' => 210,
					'choices'  => [
						'grid' => [
							'name' => __( 'Grid', 'neve' ),
							'url'  => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS8AAADYCAAAAACfVToRAAAEtElEQVR42u3cIXPjOhSG4f3/9KOGgoFmhmZlspvUabITsp5LjMyMzgWSHadNE7fJve1G74fSmbTgGUk+OpL7y8hn8gsCvPDCCy+8CF544YUXXngRvPDCCy+8yP/g9c/vbwteeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeP0Ur9dbsk/Ma9/U1U2pX1Py2j9XN6dJyOsOXFW1TcZrew+uqk7Ga3MXrwsD7MG87sN1YQV7SK96+9VqYpOk1/rL5egrXnjhhddP8drjtdxrv6mrqm72eC3y2sWd9/MeryVeU6Nig9cCr9leco/Xda/N5f3NS1Xv8Jp5rS96barqDFjKXi+X+g1h8L0DY/06v36Nc/UtWNLPx6nX+vJxm+wNWNJeYy9/c6mreAqWeH3fPFfVenu5CXsCxn77es96DobXghb/DAyvJSciRzC8Fh0gTWB47ep6e/28bQRL3mtXz8v7j48n6z1exxbY9vpp7itex45hALt4+I3XjKuqttfuCuA156qq7ZWrFXj9bj5zZQIvvPDCCy+8EvHarj+RHV7cz8ELL7zwwguv+fsK66+mTsyr5v2OT3nd6f2hXSpeu7twrdN5/7G5A1e9S+h95OY/5XrA9913m5sW/efL1/Ufz4v/p4AXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXkl6dX++LX+l1yMGL7zwwgsvvAheeOGFF1543T9OMfnh+pe9lOM1przdq1zyVx7FSx6vJV65mVmTSSu8FnuZlzSY937ocpVmvZck34evNSvJNee9vJNUHhLzaiT1JqmRVFo7TtLRQZIyKTeT1JpZL6k36920/MVvKaHxFVBU9mE585K6YHkIau+8nFSYHTKpSckrrl+SVA5mRZxYpVSYufgsyN97NZIzM3uSigSfj4p8WZyIraShD6NsXO9PvArpKdF6ojQzSc1xrsUP7TjHmvdeLvxCel6hvj9dm/D6eL0PiRan83E4Ox87Sb3lcT723vuEvWbrfW62iuv9avTyUwHi43rv01nvz3l17+oJP9UTK8n11jlJvQ2xngi/WP6IDfk3eNnhXL0axlcTfghewW18XPh06tU3Xmf3Q733jZkdVpL8MD4Twn6onareh/Z6xOCFF1544YUXwQsvvPDC6+b4HC+8bkyzmvUhfCa5cC+gfZo3KPCKyWP7qrXj2avrx1bW2ADDK+ZpOh7qzVbjZ3fsF8YmF15mFg55ijCYnsa2cyHpUEiu83nvfkRH/sd4jccVhVRMhxyZ5J3kzefmpQyvKWU8DiulwlxcrJzkV9GL5+NJDt63I5F57/s4RzsvqcgdXh8NMzfEz8NKKqYHQeYHvN6ky0MJEUbceGLUxYoia/F6u+ariKNoKOb3WL3zkrIer1lFsZLcWJO2mZR38/3Q4BbcA07Iq8uOg2sswCzcBxjM52Y5XrMM2awebXW8nJTFeqLP8Dq7HZI7boekMtYTrF+nyWZeneZ3DfPpM8/HY2ZErj3xsiY0K8q/r6FDvxCvh/T6S4MXXnjhhRdeBC+88MILL7wIXnjhhdcj51987R/KzzMKWAAAAABJRU5ErkJggg==',
						],
						'list' => [
							'name'      => __( 'List', 'neve' ),
							'url'       => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS8AAADYCAAAAACfVToRAAAE0UlEQVR42u3csVPqTBTG4fv/t2+bckvKdCnT2W2iGASH5ma+JlW6VOcrdkOCIKBBwJvfW+GMOvLM7ubsctY/Rr6SPxDghRdeeOFF8MILL7zwwovghRdeeOFF8MILL7zwwovghRdeeOGFF8ELL7zwwovghRdeeOGFF8ELL7zwwovg9RNe/73fLXjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjh9Sheb1OymZnXpiqLSSnf5uS1eS4mp5qR1xW4imL1bS+nmHR7/n16Kb2z1+oaXEU53UvKp3vll/yWSV7Lq3idGGCXe8n/Aq/rcJ1Ywc57pWZmVSItfo1XufpuNbG8kpd5SZ1577smVW7Weknybfi2aiG56riXd5Ly7S29Xr5djr5dy6uS1JqkSlJudT9JewdJSqTUTFJtZq2k1qx1u+Uvfpdm4RXGV0BR3oblzEtqguU2qB14OSkz2yZSNSevuH5JUt6ZZXFi5VJm5uKzID30qiRnZvYkZTOZj+PnoyJfEidiLalrwyjr1/s9r0x6uv16P/La3M0rNzNJ1TDX4ou6n2PVoZcLP3Anr82yLIqy2tzBK9T3+2vTo3ut4877eXNLr3GJEC3252N3dD42klpL43xsvfc399odVCzv7TVa71OzRVzvF72X3xUgPq73/g7r/WgvubmzV3NQT/hdPbGQXGuNk9RaF+uJ8IP5ZRvyK3ktT+9vXotyfSsv2x6rV8P4qsIXwSu49Y8Lf9P66+Wk17IojoD9lNfR/VDrfWVm24Uk3/XPhLAfqndV7828Xk+dN4TBdwA21esuucH61c/Vj2Bz9hrOWl8/4zoAm7VXf5a//JzrI9isvd431XNRvKxOcX0Am7fX8Xw8sx6D4XWWaw8Mr/NcYzC8LuAageG1LsvVOa4BbPZe63Jc3n/+8WS5wet9dwS2OsdVFG94DSeGAezkh994jbiKYnWuVwCvMVdRrM60VuD1Xn2lZQIvvPDCCy+8ZuK1evlC1nh9I3jhhddMvHyKF14TvarFqE/CJ5IL9xbqp3EDxWN5FS/fTTnVK43tNbUNveGu7Vtt+gadh/GaeJdv8v2Op137amu26F+7oZ8pNuE8iteV7g+tv+nVSsrCYHrq2+IySdtMco1PW3dZx+DNvNZX4Tqx/p1Z0WM7ZSZluybMRPJO8uZT81LySF5f2zOe+XDt6155bNfNpcxcXKyc5BfR69Gej9cAO8V1xmvrfd0Tmfe+jXO08ZKy1D2g1/t6OWnRfz7drn/RG8gl18XX3ULKdg+CxHeP5vWzueDPb9JQQoQR13e0NrGiSGq8Pq75yuIo6rLxPVvvvKSkxWtUUSwkt41f1ImUNoNkap274J7yjLyaZBhcfQFm4b5CZz41S/EapUtG9Wit4fJUEuuJNsFrlGE7JDdsh6Q81hOsX/tJRl6Nxnch091rno9DRkSu3vOyKhxW5D9woPOv/j+rh9wP4fXPeP1U8MILL7zwwgsvvPDCCy+88MILL7zwwgsvvPDCCy+88MILrwlezd+75Vd6EbzwwgsvvPAieOGFF154EbzwwgsvvPAieOGFF1544UXwwgsvvPAieOGFF1544UXwwgsvvPAieOGFF1544UXwmpz/AdpnH8pqwZaBAAAAAElFTkSuQmCC',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'woocardlistlayout' ),
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Image'
			)
		);

		$this->add_control(
			new Control(
				'neve_add_to_cart_display',
				[
					'default'           => 'none',
					'sanitize_callback' => function() {
						return 'none';
					},
				],
				[
					'label'    => esc_html__( 'Button Style', 'neve' ),
					'section'  => 'woocommerce_product_catalog',
					'priority' => 230,
					'choices'  => [
						'none'     => [
							'name' => __( 'None', 'neve' ),
							'url'  => 'data:image/png;base64,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',
						],
						'after'    => [
							'name'      => __( 'Bottom', 'neve' ),
							'url'       => 'data:image/png;base64,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',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'wooaddtocartafter' ),
						],
						'on_image' => [
							'name'      => __( 'Overlay', 'neve' ),
							'url'       => 'data:image/png;base64,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',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'wooaddtocartonimage' ),
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Image'
			)
		);

		$this->add_control(
			new Control(
				'neve_category_card_layout_ui_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'            => esc_html__( 'Category', 'neve' ),
					'section'          => 'woocommerce_product_catalog',
					'priority'         => 300,
					'class'            => 'category-layout-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 1,
					'expanded'         => false,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_category_card_layout',
				[
					'default'           => 'default',
					'sanitize_callback' => function() {
						return 'default';
					},
				],
				[
					'label'    => esc_html__( 'Layout', 'neve' ),
					'section'  => 'woocommerce_product_catalog',
					'priority' => 310,
					'choices'  => [
						'default' => [
							'name' => 'Simple',
							'url'  => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA5MCA2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjkwIiBoZWlnaHQ9IjYwIiByeD0iMSIgZmlsbD0iI0YzRjRGNSIvPgo8cmVjdCB3aWR0aD0iNzEuNzUiIGhlaWdodD0iNDEuODM3OSIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgODAgMCkiIGZpbGw9IiNEREREREQiLz4KPHBhdGggZD0iTTIzLjU2NjQgNTBINTEuMTk2NCIgc3Ryb2tlPSIjQzRDNEM0IiBzdHJva2Utd2lkdGg9IjIiLz4KPHBhdGggZD0iTTU1LjMxNjQgNTBINjQuNjg1NSIgc3Ryb2tlPSIjQzRDNEM0IiBzdHJva2Utd2lkdGg9IjIiLz4KPC9zdmc+Cg==',
						],
						'style-3' => [
							'name'      => 'Boxed',
							'url'       => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTEiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA5MSA2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3QgeD0iMC4xODU1NDciIHdpZHRoPSI5MCIgaGVpZ2h0PSI2MCIgcng9IjEiIGZpbGw9IiNGM0Y0RjUiLz4KPHJlY3Qgd2lkdGg9IjcxLjc1IiBoZWlnaHQ9IjUwIiB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSA4MC4xODU1IDApIiBmaWxsPSIjREREREREIi8+CjxyZWN0IHg9IjI1LjY4NTUiIHk9IjEzLjgzNzkiIHdpZHRoPSIzOSIgaGVpZ2h0PSIyMS4xNjIxIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzAuMzE0NSAyNEw0OS4zMDk0IDI0LjI2NDIiIHN0cm9rZT0iIzk5OTk5OSIgc3Ryb2tlLXdpZHRoPSIyIi8+CjxwYXRoIGQ9Ik01Mi4wNjA1IDI0LjMxODRMNTguOTM2NSAyNC40MTkiIHN0cm9rZT0iIzk5OTk5OSIgc3Ryb2tlLXdpZHRoPSIyIi8+Cjwvc3ZnPgo=',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'woocategorycardboxed' ),
						],
						'style-2' => [
							'name'      => 'Hover',
							'url'       => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTEiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA5MSA2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3QgeD0iMC44NjkxNDEiIHdpZHRoPSI5MCIgaGVpZ2h0PSI2MCIgcng9IjEiIGZpbGw9IiNGM0Y0RjUiLz4KPHJlY3Qgd2lkdGg9IjcxLjc1IiBoZWlnaHQ9IjUwIiB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSA4MC44NjkxIDApIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0yNC4xODE2IDI0SDUxLjgwNjYiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMiIvPgo8cGF0aCBkPSJNNTUuODA2NiAyNEw2NS44MDY2IDI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz4KPC9zdmc+Cg==',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'woocategorycardhover' ),
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Image'
			)
		);

		$this->add_control(
			new Control(
				'neve_sale_tag_ui_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'            => esc_html__( 'Tags', 'neve' ),
					'section'          => 'woocommerce_product_catalog',
					'priority'         => 600,
					'class'            => 'sale-tag-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 1,
					'expanded'         => false,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_sale_tag_position',
				[
					'default'           => 'inside',
					'sanitize_callback' => function() {
						return 'inside';
					},
				],
				[
					'label'         => esc_html__( 'Style', 'neve' ),
					'section'       => 'woocommerce_product_catalog',
					'priority'      => 610,
					'documentation' => [
						'link' => 'https://docs.themeisle.com/article/1058-woocommerce-booster-documentation#sale-tag',
					],
					'choices'       => [
						'inside'  => [
							'url' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS8AAADYCAMAAACN4JX/AAAAVFBMVEU/xPtTyPtkzPx00fyD1fyR2fye3f2q4f225f3B6f3M7P7S0tLT09PU1NTV1dXW1tbX19fX8P7Y2NjZ2dna2trb29vc3Nzd3d3i9P7s+P71+/////8xOQAQAAADLElEQVR4AezQMQ0AMAwDsLDYMf48i6Gq8tkQnL+BL1++fPnyhS9fvnz58oUvX758+cKXL1++fPnCly9fvnz5ov3lC1++fPnKwWvx5cuXL1++fPny5cuXL1++fPny5cuXL1++fPnyNezXsW7EIAyAYRPiM8eWjfh///dsiNOrVJW1k/8BmfUTIPEPXsUc3GJTwe/h5JTo5GlPr8n1eIwydw3Q9GLt1UDv1SaeA/2XV0xRevW4gM77Wl/QoKTX2stgWL2GqXTQC1h6rb3kYNb1GjdQ6Yw/368jvaLdHMBEDBdRqOm19IrMoXxoWt7HlVeBdoPBtvPk6bU8Xwe8ROrA5R0yCppeK6/qRPp91MTpP6/WZ5qsUf6HoFUx2GXWYftqx25zrIZhMAovIElTtfnY/05B/LhKecHcMEMYxefsoI/qSPb/8mLfxuv42/LVPHqFD5QbXlPFgtdcBa+pYsNrqozXXA2vqW68pjrxmiqzD73R+fI68HqjhhdeeOGFF1544YUXXu/Wf5l+UW8xvEp/NMEryeJnhNcdHlWbBK8klwUjvFp4Fk2QI8Tbt1cNP2VxpfC9C68xm0vBmEebS8FcefX49DosLgFz6HU+vW6DS8A8erU4QiSDS8BcevUyMMRqcAmYT69+xwkuAfPn1Vv+IRbPZnEJmDevsVpK7d3mEjDHXppyKRheE1wChpfJJWB42VwChpdyCZhbr5LSNcElYM68ilxXDS4Bc+AlXAOYxSVVH17CNYJNcIXiw0u4RjDhwku4RjDhwku4BjDhwku4BjCDC6+iAvn3XHjlGRu88MILL7zwwgsvvPBa7HXnD1a538+HF1544YUXXnjhZYdXbJ/vVTb2CvH49MKrvItXDEs6d/FKa7yuXbzONV51F6+2hOvoG3gt/MHqPl49rXi9NvJqaQHXBl6rRjKWvplXb2eK/wjruHvfwGthG3jhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOFFX9YLL7wIL7zwwgsvvAgvvPDCCy+8CC+88MILL8ILL7zwwgsvwgsvvPDCi74BKevIZRVAXf8AAAAASUVORK5CYII=',
						],
						'outside' => [
							'url'       => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS8AAADYCAMAAACN4JX/AAAAVFBMVEU/xPtTyPtkzPx00fyD1fyR2fye3f2q4f225f3B6f3M7P7S0tLT09PU1NTV1dXW1tbX19fX8P7Y2NjZ2dna2trb29vc3Nzd3d3i9P7s+P71+/////8xOQAQAAADR0lEQVR4AezXMWvDMBBA4XNsRY62THX0/v//bA8lKQQ0lIZ2eW8w5/XDZ7jAfpJeeumll156mV566fWW9NJLL71ML7300kuv+EV6PdJLL7300ksvvfTSSy+99NJLL7300ksvvT7+ML30eld66aXXUjv0Ol5W6JHduMXoxr1Nr+S6exxLfLUDRa+5VwqVfFITrwPtxWtMI73aWMDOJSLOsMOi19yrwlHXiEilK22BqtfUK42yViLiBCUax4vX6KrXaKsdoEZUekSBVa+p1yjJlifNPttHvZbUSTA4bdzres284grniPWgx2XIFCh6zbzWzqg8PrXotO+/1nNK1pH3EOxrVNgia3DS67/vbb300ksvvfTSSy+99NJLL7300ksvvfTSSy+9Ptu511y3QSiKwgPggWXzmP9MW/UPanfvaaISdHVYawif4sTaKOSFlbu79wprKx2vt4p1v9eCxhdd4eNVR15P+Hyxu/HqMWyouPG6w5a6F6+8x+vx4hX3eF1evMKeij+vNpalryrZnVce6+t44YUXXnjhhde5XrWkGNPVXkHBq2frtXyGl0w8Ca9/eqWXliu8/j64Nrxsr2QsCxJePfxeNEFyiM/ZXi38kcWVws9uwwsv4TLAeB6FS8AO85JFP1tcAnaal553PwaXgJ3mpSe4yeASsCO9Rg2z2AwuATvTazzxFS4BO9Zr9PJLLF7d5lKwY/fCVmuzfxltMPZVm0vB8LK5BAwvm0vA8LK5BAwv5RKwY71qSrfBZYAd6VV1XbW5FMy3l3BNMJNLakd4CdcEs7mkeo6XcE0w5cJLuCaYcuElXBNMuPASrglmcOFVVaB8zYVXeRlmvxdeeOGFF1544YUXXng95T9rq734fwdeeOGFF1544YUXXrGv96qOvULMy3N4Hwz3Db3nlfZ43V68rj1ezYtX38KVhxevPR+w5sdrpJ3fXg68etrF5cBrwyMZ63DmNfqV4oew8jOGK6/94YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjh5cCL8MILL7zwwovwwgsvvPDCi/DCCy+88CK88MILL7zwIrzwwgsvvPAivPD6Lv0ALgqZQ3JkuCMAAAAASUVORK5CYII=',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'woosaletagoutside' ),
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Image'
			)
		);

		$this->add_control(
			new Control(
				'neve_card_content_ui_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'            => esc_html__( 'Content', 'neve' ),
					'section'          => 'woocommerce_product_catalog',
					'priority'         => 500,
					'class'            => 'card-content-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 1,
					'expanded'         => false,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_product_content_alignment',
				[
					'default'           => 'left',
					'sanitize_callback' => function() {
						return 'left';
					},
				],
				[
					'label'    => esc_html__( 'Alignment', 'neve' ),
					'section'  => 'woocommerce_product_catalog',
					'priority' => 520,
					'choices'  => [
						'left'   => [
							'name' => __( 'Left', 'neve' ),
							'url'  => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS8AAADYCAAAAACfVToRAAAEtElEQVR42u3cIXPjOhSG4f3/9KOGgoFmhmZlspvUabITsp5LjMyMzgWSHadNE7fJve1G74fSmbTgGUk+OpL7y8hn8gsCvPDCCy+8CF544YUXXngRvPDCCy+8yP/g9c/vbwteeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeP0Ur9dbsk/Ma9/U1U2pX1Py2j9XN6dJyOsOXFW1TcZrew+uqk7Ga3MXrwsD7MG87sN1YQV7SK96+9VqYpOk1/rL5egrXnjhhddP8drjtdxrv6mrqm72eC3y2sWd9/MeryVeU6Nig9cCr9leco/Xda/N5f3NS1Xv8Jp5rS96barqDFjKXi+X+g1h8L0DY/06v36Nc/UtWNLPx6nX+vJxm+wNWNJeYy9/c6mreAqWeH3fPFfVenu5CXsCxn77es96DobXghb/DAyvJSciRzC8Fh0gTWB47ep6e/28bQRL3mtXz8v7j48n6z1exxbY9vpp7itex45hALt4+I3XjKuqttfuCuA156qq7ZWrFXj9bj5zZQIvvPDCCy+8EvHarj+RHV7cz8ELL7zwwguv+fsK66+mTsyr5v2OT3nd6f2hXSpeu7twrdN5/7G5A1e9S+h95OY/5XrA9913m5sW/efL1/Ufz4v/p4AXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXkl6dX++LX+l1yMGL7zwwgsvvAheeOGFF1543T9OMfnh+pe9lOM1przdq1zyVx7FSx6vJV65mVmTSSu8FnuZlzSY937ocpVmvZck34evNSvJNee9vJNUHhLzaiT1JqmRVFo7TtLRQZIyKTeT1JpZL6k36920/MVvKaHxFVBU9mE585K6YHkIau+8nFSYHTKpSckrrl+SVA5mRZxYpVSYufgsyN97NZIzM3uSigSfj4p8WZyIraShD6NsXO9PvArpKdF6ojQzSc1xrsUP7TjHmvdeLvxCel6hvj9dm/D6eL0PiRan83E4Ox87Sb3lcT723vuEvWbrfW62iuv9avTyUwHi43rv01nvz3l17+oJP9UTK8n11jlJvQ2xngi/WP6IDfk3eNnhXL0axlcTfghewW18XPh06tU3Xmf3Q733jZkdVpL8MD4Twn6onareh/Z6xOCFF1544YUXwQsvvPDC6+b4HC+8bkyzmvUhfCa5cC+gfZo3KPCKyWP7qrXj2avrx1bW2ADDK+ZpOh7qzVbjZ3fsF8YmF15mFg55ijCYnsa2cyHpUEiu83nvfkRH/sd4jccVhVRMhxyZ5J3kzefmpQyvKWU8DiulwlxcrJzkV9GL5+NJDt63I5F57/s4RzsvqcgdXh8NMzfEz8NKKqYHQeYHvN6ky0MJEUbceGLUxYoia/F6u+ariKNoKOb3WL3zkrIer1lFsZLcWJO2mZR38/3Q4BbcA07Iq8uOg2sswCzcBxjM52Y5XrMM2awebXW8nJTFeqLP8Dq7HZI7boekMtYTrF+nyWZeneZ3DfPpM8/HY2ZErj3xsiY0K8q/r6FDvxCvh/T6S4MXXnjhhRdeBC+88MILL7wIXnjhhdcj51987R/KzzMKWAAAAABJRU5ErkJggg==',
						],
						'center' => [
							'name'      => __( 'Center', 'neve' ),
							'url'       => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS8AAADYCAAAAACfVToRAAAEtElEQVR42u3cLXPjPBSG4f3/9KGGgoFmhmZlspvUabITsp6XGJkZnRdIdpw2X91ku9noflA6kxZcI8lHR3J/GPlKfkCAF1544YUXwQsvvPDCCy+CF1544YUX+fNe//38a8ELL7zwwgsvvPDCCy+88MILL7zwwgsvvPDCCy+88MILL7zwwgsvvPDCCy+88MILr4fxer8l28S8tk1d3ZT6PSWv7Wt1c5qEvO7AVVXrZLzW9+Cq6mS8VnfxOj3Ans3rPlynV7Dn9KrXv1tNrJL0Wv52PfqOF1544fUoXlu8rvfaruqqqpstXld5beLO+3WL1zVeU6NihdcVXrO95Bavy16r8/ubt6re4DXzWp71WlXVEbCUvd7O9RvC4PsExvp1fP0a5+pHsKSfj1Ov9e10m+wDWNJeYy9/da6reAiWeH3fvFbVcn2+CXsAxn77cs96DobXFS3+GRhe15yI7MHwuuoAaQLDa1PX68vnbSNY8l6bel7enz6erLd47Vtg68unue947TuGAezs4TdeM66qWl+6K4DXnKuq1heuVuD1s/nKlQm88MILL7zwSsRrvfxCNnhxPwcvvPDCCy+85u8rLH83dWJeNe93fMnrTu8PbVLx2tyFa5nO+4/NHbjqTULvIzd/kusZ33ffrG5a9F/PXtd/Qi/+nwJeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeKXo1f36a/knvZ4yeOGFF1544UXwwgsvvPDC61viFJPvLn/ZSzleY8rbvcpr/sqzeMnjdY1XbmbWZNICr6u9zEsazHs/dLlKs95Lku/D15qF5JrjXt5JKneJeTWSepPUSCqtHSfp6CBJmZSbSWrNrJfUm/VuWv7it5TQ+AooKvuwnHlJXbDcBbVPXk4qzHaZ1KTkFdcvSSoHsyJOrFIqzFx8FuSfvRrJmZm9SEWCz0dFvixOxFbS0IdRNq73B16F9JJoPVGamaRmP9fih3acY81nLxd+IT2vUN8frk14nV7vQ6LF4Xwcjs7HTlJveZyPvfc+Ya/Zep+bLeJ6vxi9/FSA+Lje+3TW+2Ne3ad6wk/1xEJyvXVOUm9DrCfCL5bfvyF/DC/bHatXw/hqwg/BK7iNjwufTr36wevofqj3vjGz3UKSH8ZnQtgPtVPV+9Re/3zwwgsvvPDCi+CFF1544fWd8TleeN2YZjHrT/hMcuG+QPsyb1zgFZPHtlZr+zNZ148trrExhlfMy3Rs1Jstxs9u30eMzS+8zCwc/hRhML2M7ehC0q6QXOfz3n1/p/6RvcZjjEIqpsOPTPJO8uZz81KG15QyHpOVUmEuLlZO8ovoxfPxIDvv25HIvPd9nKOdl1TkDq9Tw8wN8fOwkIrpQZD5Aa8P6fJQQoQRN54kdbGiyFq8Pq75KuIoGor5/VbvvKSsx2tWUSwkN9akbSbl3Xw/NLgr7gcn5NVl+8E1FmAW7gkM5nOzHK9ZhmxWj7baX1rKYj3RZ3gd3Q7J7bdDUhnrCdavw2Qzr07zO4j59Jnn4z4zItceeFkTmhXlgzR06BfilbrXIwUvvPDCCy+8CF544YUXXngRvPDCC69nzv/F6x/Kmshd1QAAAABJRU5ErkJggg==',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'wooscardcontentalingcenter' ),
						],
						'right'  => [
							'name'      => __( 'Right', 'neve' ),
							'url'       => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS8AAADYCAAAAACfVToRAAAEs0lEQVR42u3cLXPrOhSF4fP/6aKGgoFmhmZlspvUaXIm5HguMTIz2hdIdpw2X23SaRu9C6UzacEzkry1JfePkY/kDwR44YUXXngRvPDCCy+88CJ44YUXXniRr/f67++3BS+88MILL7zwwgsvvPDCCy+88MILL7zwwgsvvPDCCy+88MILL7zwwgsvvPDCCy+8fozX6y3ZJua1berqptSvKXltn6ub0yTkdQeuqlon47W+B1dVJ+O1uovX6QH2aF734Tq9gj2mV73+bDWxStJr+el69BUvvPDC66d4bfG63mu7qquqbrZ4XeW1iTvv5y1e13hNjYoVXld4zfaSW7wue63O729eqnqD18xredZrVVVHwFL2ejnXbwiD7x0Y69fx9Wucq2/Bkn4+Tr3Wl9NtsjdgSXuNvfzVua7iIVji9X3zXFXL9fkm7AEY++3LPes5GF5XtPhnYHhdcyKyB8PrqgOkCQyvTV2vL5+3jWDJe23qeXl/+niy3uK1b4GtL5/mvuK17xgGsLOH33jNuKpqfemuAF5zrqpaX7hagdff5iNXJvDCCy+88MIrEa/18gPZ4MX9HLzwwgsvvPCav6+w/GzqxLxq3u/4kNed3h/apOK1uQvXMp33H5s7cNWbhN5Hbr6S6xHfd9+sblr0n89e139AL/6fAl544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF1544YUXXnjhhRdeeOGFF154pejV/fu2/EqvhwxeeOGFF154EbzwwgsvvPD6rjjF5LvLX/ZSjteY8nav8pq/8ihe8nhd45WbmTWZtMDrai/zkgbz3g9drtKs95Lk+/C1ZiG55riXd5LKXWJejaTeJDWSSmvHSTo6SFIm5WaSWjPrJfVmvZuWv/gtJTS+AorKPixnXlIXLHdB7Z2XkwqzXSY1KXnF9UuSysGsiBOrlAozF58F+XuvRnJmZk9SkeDzUZEvixOxlTT0YZSN6/2BVyE9JVpPlGYmqdnPtfihHedY897LhV9IzyvU94drE16n1/uQaHE4H4ej87GT1Fse52PvvU/Ya7be52aLuN4vRi8/FSA+rvc+nfX+mFf3rp7wUz2xkFxvnZPU2xDrifCL5RdvyH+sl+2O1athfDXhh+AV3MbHhU+nXn3jdXQ/1HvfmNluIckP4zMh7Ifaqep9aK/fFbzwwgsvvPAieOGFF154/dD4HC+8bkyzmPUtfCa5cI+gfZo3NPCKyWO7q7X9Wa3rx9bX2DDDK+ZpOk7qzRbjZ7fvL8amGF5mFg6FijCYnsY2dSFpV0iu83nvvrSD/+u8xuONQiqmQ5FM8k7y5nPzUobXlDIen5VSYS4uVk7yi+jF8/EgO+/bkci8932co52XVOQOr1PDzA3x87CQiulBkPkBrzfp8lBChBE3njB1saLIWrzervkq4igaivm9V++8pKzHa1ZRLCQ31qRtJuXdfD80uCvuDSfk1WX7wTUWYBbuDwzmc7Mcr1mGbFaPttpfZspiPdFneB3dDsntt0NSGesJ1q/DZDOvTvO7ifn0mefjPjMi1x54WROaFeVXNnToF+KF188JXnjhhRdeeBG88MILL7zwInjhhRdej5z/AdA+H8o+3vutAAAAAElFTkSuQmCC',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'wooscardcontentalingright' ),
						],
						'inline' => [
							'name'      => __( 'Left', 'neve' ) . ' / ' . __( 'Right', 'neve' ),
							'url'       => 'data:image/png;base64,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',
							'upsellUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'wooscardcontentalinginline' ),
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Image'
			)
		);
	}

	/**
	 * Sanitize checkout layout that forcefully updates the selection as standard.
	 *
	 * @return string
	 */
	public function sanitize_checkout_layout() {
		return 'standard';
	}

	/**
	 * Add upsells section
	 */
	private function section_upsells() {

		$this->add_section(
			new Section(
				'neve_free_pro_upsell',
				array(
					'priority' => -100,
					'title'    => esc_html__( 'Neve PRO Features', 'neve' ),
					'url'      => $this->upsell_url,
				),
				'Neve\Customizer\Controls\React\Upsell_Section'
			)
		);
	}

	/**
	 * Add upsells controls
	 */
	private function control_upsells() {
		$this->add_control(
			new Control(
				'neve_upsell_main_control',
				[ 'sanitize_callback' => 'sanitize_text_field' ],
				[
					'text'        => esc_html__( 'Neve PRO Features', 'neve' ),
					'button_text' => esc_html__( 'Learn More', 'neve' ),
					'section'     => 'neve_free_pro_upsell',
					'priority'    => PHP_INT_MIN,
					'link'        => $this->upsell_url,
				],
				'Neve\Customizer\Controls\Simple_Upsell'
			)
		);

		$upsells         = [];
		$upsells_banners = [];

		$upsells_banners['blog_archive'] = [
			'text'        => __( 'More blog layout customization options available in PRO', 'neve' ),
			'button_text' => __( 'Learn More', 'neve' ),
			'use_logo'    => true,
			'section'     => 'neve_blog_archive_layout',
		];
		$upsells_banners['single_post']  = [
			'text'        => __( 'More single post components available in PRO', 'neve' ),
			'button_text' => __( 'Learn More', 'neve' ),
			'use_logo'    => true,
			'section'     => 'neve_single_post_layout',
		];


		$hfg_header                     = 'hfg_header';
		$hfg_header_text                = __( 'Extend your header with more components and settings, build sticky/transparent headers or display them conditionally.', 'neve' );
		$hfg_header_button              = __( 'Get the PRO version!', 'neve' );
		$upsells_banners[ $hfg_header ] = [
			'text'        => $hfg_header_text,
			'button_text' => $hfg_header_button,
			'use_logo'    => true,
			'panel'       => $hfg_header,
			'type'        => 'section',
		];

		foreach ( [ 'top', 'main', 'bottom', 'sidebar' ] as $section ) {
			$section_id                     = $hfg_header . '_layout_' . $section;
			$upsells_banners[ $section_id ] = [
				'text'        => $hfg_header_text,
				'button_text' => $hfg_header_button,
				'use_logo'    => true,
				'section'     => $section_id,
			];
		}

		$hfg_footer                     = 'hfg_footer';
		$hfg_footer_text                = __( 'Neve PRO Features', 'neve' );
		$hfg_footer_button              = __( 'Get the PRO version!', 'neve' );
		$upsells_banners[ $hfg_footer ] = [
			'text'        => $hfg_footer_text,
			'button_text' => $hfg_footer_button,
			'use_logo'    => true,
			'panel'       => $hfg_footer,
			'type'        => 'section',
		];
		foreach ( [ 'top', 'main', 'bottom' ] as $section ) {
			$section_id                     = $hfg_footer . '_layout_' . $section;
			$upsells_banners[ $section_id ] = [
				'text'        => $hfg_footer_text,
				'button_text' => $hfg_footer_button,
				'use_logo'    => true,
				'section'     => $section_id,
			];
		}


		if ( class_exists( 'WooCommerce', false ) ) {
			$upsells['product_catalog']       = [
				'text'        => __( 'More product catalog options available in PRO', 'neve' ),
				'button_text' => __( 'Learn More', 'neve' ),
				'section'     => 'woocommerce_product_catalog',
			];
			$upsells['woocommerce_checkout']  = [
				'text'        => __( 'More checkout options available in PRO', 'neve' ),
				'button_text' => __( 'Learn More', 'neve' ),
				'section'     => 'woocommerce_checkout',
			];
			$upsells['single_product_layout'] = [
				'text'        => __( 'More single product options available in PRO', 'neve' ),
				'button_text' => __( 'Learn More', 'neve' ),
				'section'     => 'neve_single_product_layout',
			];
			$upsells['typography']            = [
				'text'        => __( 'WooCommerce typography options available in PRO', 'neve' ),
				'button_text' => __( 'Learn More', 'neve' ),
				'panel'       => 'neve_typography',
				'type'        => 'section',
			];
		}

		$is_dismissed = get_transient( 'upsell_dismiss_banner_customizer' );
		if ( $is_dismissed === false ) {
			foreach ( $upsells_banners as $id => $args ) {
				if ( isset( $args['type'] ) && $args['type'] === 'section' ) {
					$section_id = 'neve_' . $id . '_upsell_section';
					$this->add_section(
						new Section(
							$section_id,
							array_merge(
								$args,
								[
									'type'     => 'neve_upsell_banner_section',
									'priority' => 10000,
									'nonce'    => wp_create_nonce( 'neve-upsell-banner-nonce' ),
									'url'      => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'panel-' . $args['panel'] ),
								]
							),
							'\Neve\Customizer\Controls\React\Upsell_Banner_Section'
						)
					);

					continue;
				}
				$control_id = 'neve_' . $id . '_upsell_banner_control';
				$this->add_control(
					new Control(
						$control_id,
						[ 'sanitize_callback' => 'sanitize_text_field' ],
						array_merge(
							$args,
							[
								'type'     => 'neve_upsell_banner',
								'priority' => 10000,
								'nonce'    => wp_create_nonce( 'neve-upsell-banner-nonce' ),
								'url'      => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'upsell-banner-customizer-section-' . $args['section'] ),
							]
						),
						'\Neve\Customizer\Controls\React\Upsell_Banner'
					)
				);
			}
		}

		foreach ( $upsells as $id => $args ) {
			if ( isset( $args['type'] ) && $args['type'] === 'section' ) {
				$this->add_section(
					new Section(
						'neve_' . $id . '_upsell_section',
						array_merge(
							$args,
							[
								'type'     => 'nv_simple_upsell_section',
								'priority' => 10000,
								'link'     => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'panel-' . $args['panel'] ),
							]
						),
						'\Neve\Customizer\Controls\Simple_Upsell_Section'
					)
				);

				return false;
			}
			$this->add_control(
				new Control(
					'neve_' . $id . '_upsell',
					[ 'sanitize_callback' => 'sanitize_text_field' ],
					array_merge(
						$args,
						[
							'priority' => 10000,
							'link'     => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'section-' . $args['section'] ),
						]
					),
					'Neve\Customizer\Controls\Simple_Upsell'
				)
			);
		}
	}
}
PK      \:G  :G  "  customizer/options/form_fields.phpnu W+A        <?php
/**
 * Form Fields section.
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use HFG\Traits\Core;
use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Section;

/**
 * Class Form_Fields
 *
 * @package Neve\Customizer\Options
 */
class Form_Fields extends Base_Customizer {
	use Core;

	/**
	 * Customizer section slug.
	 *
	 * @var string
	 */
	private $section_id = 'neve_form_fields_section';

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		$this->add_form_section();
		$this->add_form_fields_controls();
		$this->add_input_text_controls();
		$this->add_form_labels_controls();
		$this->add_button_controls();
	}

	/**
	 * Add section.
	 */
	private function add_form_section() {
		$this->add_section(
			new Section(
				$this->section_id,
				[
					'priority'           => 45,
					'description_hidden' => true,
					'description'        => __( 'Customize the general design of the form elements across the site.', 'neve' ) . ' ' . neve_external_link( 'https://docs.themeisle.com/article/1341-neve-form-fields', 'Learn More' ),
					'title'              => esc_html__( 'Form Fields', 'neve' ),
				]
			)
		);
	}

	/**
	 * Form fields controls.
	 */
	private function add_form_fields_controls() {
		$this->add_control(
			new Control(
				'neve_form_fields_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'            => esc_html__( 'Form Fields', 'neve' ),
					'section'          => $this->section_id,
					'priority'         => 10,
					'class'            => 'form-fields-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 5,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		// Padding
		$default_padding = Mods::get_alternative_mod_default( Config::MODS_FORM_FIELDS_PADDING );
		$this->add_control(
			new Control(
				Config::MODS_FORM_FIELDS_PADDING,
				[
					'sanitize_callback' => [ $this, 'sanitize_spacing_array' ],
					'transport'         => $this->selective_refresh,
					'default'           => $default_padding,
				],
				[
					'label'                 => esc_html__( 'Field Padding', 'neve' ),
					'section'               => $this->section_id,
					'priority'              => 15,
					'units'                 => [ 'px', 'em', 'rem' ],
					'default'               => $default_padding,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar'      => [
							'selector' => 'body',
							'vars'     => '--formfieldpadding',
							'suffix'   => 'px',
						],
						'responsive'  => false,
						'directional' => true,
						'template'    =>
							'
							body form input:read-write,
							body form textarea,
							body form select,
							body form select option,
							body form.wp-block-search input.wp-block-search__input,
							.woocommerce-cart table.cart td.actions .coupon .input-text,
							.woocommerce-page .select2-container--default .select2-selection--single,
							.woocommerce-page .woocommerce form .form-row input.input-text,
							.woocommerce-page .woocommerce form .form-row textarea,
							.widget select,
							.wc-block-product-search form input.wc-block-product-search__field {
								 padding-top: {{value.top}};
								 padding-right: {{value.right}};
								 padding-bottom: {{value.bottom}};
								 padding-left: {{value.left}};
					        }
					        form.search-form input[type="search"],
					        form.woocommerce-product-search input[type="search"] {
					             padding-right: calc({{value.right}} + 33px);
					        }',
					],
				],
				'\Neve\Customizer\Controls\React\Nr_Spacing'
			)
		);

		// Background color
		$this->add_control(
			new Control(
				Config::MODS_FORM_FIELDS_BACKGROUND_COLOR,
				[
					'sanitize_callback' => 'neve_sanitize_colors',
					'default'           => 'var(--nv-site-bg)',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'                 => esc_html__( 'Field Background Color', 'neve' ),
					'section'               => $this->section_id,
					'priority'              => 17,
					'default'               => 'var(--nv-site-bg)',
					'input_attrs'           => [
						'allow_gradient' => true,
					],
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar'   => [
							'selector' => 'body',
							'vars'     => '--formfieldbgcolor',
						],
						'template' => '
							body form input:read-write,
							body form textarea,
							body form select,
							body form select option,
							body form.wp-block-search input.wp-block-search__input,
							.woocommerce-cart table.cart td.actions .coupon .input-text,
							.woocommerce-page .select2-container--default .select2-selection--single,
							.woocommerce-page .woocommerce form .form-row input.input-text,
							.woocommerce-page .woocommerce form .form-row textarea,
							.widget select,
							.wc-block-product-search form input.wc-block-product-search__field {
							    background-color: {{value}};
						    }',

					],
				],
				'Neve\Customizer\Controls\React\Color'
			)
		);

		// Border width
		$default_width = Mods::get_alternative_mod_default( Config::MODS_FORM_FIELDS_BORDER_WIDTH );
		$this->add_control(
			new Control(
				Config::MODS_FORM_FIELDS_BORDER_WIDTH,
				[
					'sanitize_callback' => [ $this, 'sanitize_spacing_array' ],
					'transport'         => $this->selective_refresh,
					'default'           => $default_width,
				],
				[
					'label'                 => esc_html__( 'Border Width', 'neve' ),
					'section'               => $this->section_id,
					'priority'              => 18,
					'units'                 => [ 'px', 'em', 'rem' ],
					'default'               => $default_width,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar'      => [
							'selector' => 'body',
							'vars'     => '--formfieldborderwidth',
							'suffix'   => 'px',
						],
						'responsive'  => false,
						'directional' => true,
						'template'    => '
							body form input:read-write,
							body form textarea,
							body form select,
							body form select option,
							body form.wp-block-search input.wp-block-search__input,
							.woocommerce-cart table.cart td.actions .coupon .input-text,
							.woocommerce-page .select2-container--default .select2-selection--single,
							.woocommerce-page .woocommerce form .form-row input.input-text,
							.woocommerce-page .woocommerce form .form-row textarea,
							.widget select,
							.wc-block-product-search form input.wc-block-product-search__field {
                                border-top-width: {{value.top}};
							    border-right-width: {{value.right}};
						        border-bottom-width: {{value.bottom}};
						        border-left-width: {{value.left}};
					         }',
					],
				],
				'\Neve\Customizer\Controls\React\Nr_Spacing'
			)
		);

		// Border radius
		$this->add_control(
			new Control(
				Config::MODS_FORM_FIELDS_BORDER_RADIUS,
				[
					'sanitize_callback' => [ $this, 'sanitize_spacing_array' ],
					'transport'         => $this->selective_refresh,
					'default'           => false,
				],
				[
					'label'                 => esc_html__( 'Border Radius', 'neve' ),
					'section'               => $this->section_id,
					'priority'              => 19,
					'units'                 => [ 'px', 'em', 'rem' ],
					'default'               => [
						'top'    => '3',
						'right'  => '3',
						'left'   => '3',
						'bottom' => '3',
						'unit'   => 'px',
					],
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar'      => [
							'selector' => 'body',
							'vars'     => '--formfieldborderradius',
							'suffix'   => 'px',
						],
						'responsive'  => false,
						'directional' => true,
						'template'    => '
							body form input:read-write,
							body form textarea,
							body form select,
							body form select option,
							body form.wp-block-search input.wp-block-search__input,
							.woocommerce-cart table.cart td.actions .coupon .input-text,
							.woocommerce-page .select2-container--default .select2-selection--single,
							.woocommerce-page .woocommerce form .form-row input.input-text,
							.woocommerce-page .woocommerce form .form-row textarea,
							.widget select,
							.wc-block-product-search form input.wc-block-product-search__field {
								border-top-right-radius: {{value.top}};
								border-bottom-right-radius: {{value.right}};
								border-bottom-left-radius: {{value.bottom}};
								border-top-left-radius: {{value.left}};
							}',
					],
				],
				'\Neve\Customizer\Controls\React\Nr_Spacing'
			)
		);

		// Border color
		$this->add_control(
			new Control(
				Config::MODS_FORM_FIELDS_BORDER_COLOR,
				[
					'sanitize_callback' => 'neve_sanitize_colors',
					'default'           => '#dddddd',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'                 => esc_html__( 'Border Color', 'neve' ),
					'section'               => $this->section_id,
					'priority'              => 20,
					'default'               => '#dddddd',
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar'   => [
							'selector' => 'body',
							'vars'     => '--formfieldbordercolor',
						],
						'template' => '
							body form input:read-write,
							body form textarea,
							body form select,
							body form select option,
							body form.wp-block-search input.wp-block-search__input,
							.woocommerce-cart table.cart td.actions .coupon .input-text,
							.woocommerce-page .select2-container--default .select2-selection--single,
							.woocommerce-page .woocommerce form .form-row input.input-text,
							.woocommerce-page .woocommerce form .form-row textarea,
							.widget select,
							.wc-block-product-search form input.wc-block-product-search__field {
								border-color: {{value}};
							}',
					],
				],
				'Neve\Customizer\Controls\React\Color'
			)
		);
	}

	/**
	 * Form inputs controls.
	 */
	private function add_input_text_controls() {
		$this->add_control(
			new Control(
				'neve_input_text_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'            => esc_html__( 'Input Text', 'neve' ),
					'section'          => $this->section_id,
					'priority'         => 30,
					'class'            => 'form-input-accordion',
					'accordion'        => true,
					'expanded'         => false,
					'controls_to_wrap' => 2,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		// Field text color
		$this->add_control(
			new Control(
				Config::MODS_FORM_FIELDS_COLOR,
				[
					'sanitize_callback' => 'neve_sanitize_colors',
					'default'           => 'var(--nv-text-color)',
					'transport'         => $this->selective_refresh,
				],
				[
					'label'                 => esc_html__( 'Color', 'neve' ),
					'section'               => $this->section_id,
					'priority'              => 31,
					'default'               => 'var(--nv-text-color)',
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar'   => [
							'selector' => 'body',
							'vars'     => '--formfieldcolor',
						],
						'template' => '
							body form input:read-write,
							body form textarea,
							body form select,
							body form select option,
							body form.wp-block-search input.wp-block-search__input,
							.woocommerce-cart table.cart td.actions .coupon .input-text,
							.woocommerce-page .select2-container--default .select2-selection--single,
							.woocommerce-page .woocommerce form .form-row input.input-text,
							.woocommerce-page .woocommerce form .form-row textarea,
							.widget select,
							.wc-block-product-search form input.wc-block-product-search__field {
								color: {{value}};
							}',
					],
				],
				'Neve\Customizer\Controls\React\Color'
			)
		);

		// Field typeface
		$this->add_control(
			new Control(
				Config::MODS_FORM_FIELDS_TYPEFACE,
				[
					'transport' => $this->selective_refresh,
				],
				[
					'priority'              => 32,
					'section'               => $this->section_id,
					'input_attrs'           => array(
						'disable_transform'      => true,
						'default_is_empty'       => true,
						'size_units'             => [ 'px', 'em', 'rem' ],
						'weight_default'         => 'none',
						'size_default'           => array(
							'suffix'  => array(
								'mobile'  => 'px',
								'tablet'  => 'px',
								'desktop' => 'px',
							),
							'mobile'  => '',
							'tablet'  => '',
							'desktop' => '',
						),
						'line_height_default'    => array(
							'mobile'  => '',
							'tablet'  => '',
							'desktop' => '',
						),
						'letter_spacing_default' => array(
							'mobile'  => '',
							'tablet'  => '',
							'desktop' => '',
						),
					),
					'type'                  => 'neve_typeface_control',
					'font_family_control'   => 'neve_body_font_family',
					'refresh_on_reset'      => true,
					'live_refresh_selector' => '
						form input:read-write,
						form textarea,
						form select,
						form select option,
						form.wp-block-search input.wp-block-search__input,
						.woocommerce-cart table.cart td.actions .coupon .input-text,
						.woocommerce-page .select2-container--default .select2-selection--single,
						.woocommerce-page .woocommerce form .form-row input.input-text,
						.woocommerce-page .woocommerce form .form-row textarea,
						.widget select,
						.wc-block-product-search form input.wc-block-product-search__field
					',
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => [
								'--formfieldtexttransform' => 'textTransform',
								'--formfieldfontweight'    => 'fontWeight',
								'--formfieldfontsize'      => [
									'key'        => 'fontSize',
									'responsive' => true,
								],
								'--formfieldlineheight'    => [
									'key'        => 'lineHeight',
									'responsive' => true,
								],
								'--formfieldletterspacing' => [
									'key'        => 'letterSpacing',
									'suffix'     => 'px',
									'responsive' => true,
								],
							],
							'selector' => 'body',
						],
					],
				],
				'\Neve\Customizer\Controls\React\Typography'
			)
		);
	}

	/**
	 * Form labels controls.
	 */
	private function add_form_labels_controls() {
		$this->add_control(
			new Control(
				'neve_form_labels_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'            => esc_html__( 'Form Labels', 'neve' ),
					'section'          => $this->section_id,
					'priority'         => 50,
					'class'            => 'form-labels-accordion',
					'accordion'        => true,
					'expanded'         => false,
					'controls_to_wrap' => 1,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				Config::MODS_FORM_FIELDS_LABELS_TYPEFACE,
				[
					'transport' => $this->selective_refresh,
				],
				[
					'priority'              => 52,
					'section'               => $this->section_id,
					'input_attrs'           => [
						'default_is_empty'       => true,
						'size_units'             => [ 'px', 'em', 'rem' ],
						'weight_default'         => 'none',
						'size_default'           => [
							'suffix'  => [
								'mobile'  => 'px',
								'tablet'  => 'px',
								'desktop' => 'px',
							],
							'mobile'  => '',
							'tablet'  => '',
							'desktop' => '',
						],
						'line_height_default'    => [
							'mobile'  => '',
							'tablet'  => '',
							'desktop' => '',
						],
						'letter_spacing_default' => [
							'mobile'  => '',
							'tablet'  => '',
							'desktop' => '',
						],
					],
					'type'                  => 'neve_typeface_control',
					'font_family_control'   => 'neve_body_font_family',
					'refresh_on_reset'      => true,
					'live_refresh_selector' => 'form label, body .wpforms-container .wpforms-field-label, .woocommerce form .form-row label',
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => [
								'--formlabeltexttransform' => 'textTransform',
								'--formlabelfontweight'    => 'fontWeight',
								'--formlabelfontsize'      => [
									'key'        => 'fontSize',
									'responsive' => true,
								],
								'--formlabellineheight'    => [
									'key'        => 'lineHeight',
									'responsive' => true,
								],
								'--formlabelletterspacing' => [
									'key'        => 'letterSpacing',
									'suffix'     => 'px',
									'responsive' => true,
								],
							],
							'selector' => 'body',
						],
					],
				],
				'\Neve\Customizer\Controls\React\Typography'
			)
		);
	}

	/**
	 * Form button controls.
	 */
	private function add_button_controls() {
		$this->add_control(
			new Control(
				'neve_form_button_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				array(
					'label'            => esc_html__( 'Button', 'neve' ),
					'section'          => $this->section_id,
					'priority'         => 70,
					'class'            => 'form-button-accordion',
					'accordion'        => true,
					'expanded'         => false,
					'controls_to_wrap' => 1,
				),
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_form_button_type',
				[
					'sanitize_callback' => 'neve_sanitize_button_type',
					'default'           => 'primary',
				],
				[
					'label'    => __( 'Button Style', 'neve' ),
					'priority' => 71,
					'section'  => $this->section_id,
					'type'     => 'neve_inline_select',
					'options'  => [
						'primary'   => __( 'Primary', 'neve' ),
						'secondary' => __( 'Secondary', 'neve' ),
					],
					'default'  => 'primary',
					'link'     => [
						'focus'  => [ 'section', 'neve_buttons_section' ],
						'string' => esc_html__( 'Customize the default button styles', 'neve' ),
					],
				],
				'Neve\Customizer\Controls\React\Inline_Select'
			)
		);
	}
}
PK      \ŉ      customizer/options/buttons.phpnu W+A        <?php
/**
 * Buttons section.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      20/08/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Core\Settings\Config;
use Neve\Core\Settings\Mods;
use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Section;

/**
 * Class Buttons
 *
 * @package Neve\Customizer\Options
 */
class Buttons extends Base_Customizer {

	/**
	 * Customizer section slug.
	 *
	 * @var string
	 */
	private $section_id = 'neve_buttons_section';

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		$this->add_section(
			new Section(
				$this->section_id,
				array(
					'priority' => 40,
					'title'    => esc_html__( 'Buttons', 'neve' ),
				)
			)
		);

		$this->add_control(
			new Control(
				'neve_buttons_generic_heading',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'            => esc_html__( 'General', 'neve' ),
					'section'          => $this->section_id,
					'class'            => 'buttons-general-accordion',
					'accordion'        => true,
					'expanded'         => true,
					'controls_to_wrap' => 2,
				],
				'Neve\Customizer\Controls\Heading'
			)
		);

		$mod_key  = Config::MODS_BUTTON_PRIMARY_PADDING;
		$defaults = Mods::get_alternative_mod_default( Config::MODS_BUTTON_PRIMARY_PADDING );
		$this->add_control(
			new Control(
				$mod_key,
				array(
					'default' => $defaults,
				),
				array(
					'label'             => __( 'Padding', 'neve' ),
					'sanitize_callback' => array( $this, 'sanitize_spacing_array' ),
					'section'           => $this->section_id,
					'input_attrs'       => [
						'units' => [ 'px', 'em', 'rem' ],
						'min'   => 0,
					],
					'default'           => $defaults,
				),
				'\Neve\Customizer\Controls\React\Spacing'
			)
		);

		$this->add_control(
			new Control(
				Config::MODS_BUTTON_TYPEFACE,
				[
					'transport' => $this->selective_refresh,
				],
				[
					'label'                 => esc_html__( 'Button Text', 'neve' ),
					'section'               => $this->section_id,
					'input_attrs'           => array(
						'size_units'             => [ 'px', 'em', 'rem' ],
						'weight_default'         => 700,
						'size_default'           => array(
							'suffix'  => array(
								'mobile'  => 'px',
								'tablet'  => 'px',
								'desktop' => 'px',
							),
							'mobile'  => '',
							'tablet'  => '',
							'desktop' => '',
						),
						'line_height_default'    => array(
							'mobile'  => '',
							'tablet'  => '',
							'desktop' => '',
						),
						'letter_spacing_default' => array(
							'mobile'  => '',
							'tablet'  => '',
							'desktop' => '',
						),
					),
					'type'                  => 'neve_typeface_control',
					'font_family_control'   => 'neve_body_font_family',
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'vars'     => [
								'--btnfs'            => [
									'key'        => 'fontSize',
									'responsive' => true,
									'suffix'     => 'px',
								],
								'--btnlineheight'    => [
									'key'        => 'lineHeight',
									'responsive' => true,
								],
								'--btnletterspacing' => [
									'key'        => 'letterSpacing',
									'responsive' => true,
									'suffix'     => 'px',
								],
								'--btntexttransform' => [
									'key' => 'textTransform',
								],
								'--btnfontweight'    => [
									'key' => 'fontWeight',
								],
							],
							'selector' => 'body',
						],
					],
					'refresh_on_reset'      => true,
				],
				'\Neve\Customizer\Controls\React\Typography'
			)
		);

		$buttons = [
			'button'           => __( 'Primary Button', 'neve' ),
			'secondary_button' => __( 'Secondary Button', 'neve' ),
		];

		foreach ( $buttons as $button => $heading_text ) {
			$this->add_control(
				new Control(
					'neve_' . $button . '_appearance_heading',
					[
						'sanitize_callback' => 'sanitize_text_field',
					],
					[
						'label'            => esc_html( $heading_text ),
						'section'          => $this->section_id,
						'class'            => 'buttons-' . $button . '-appearance-accordion',
						'accordion'        => true,
						'controls_to_wrap' => 1,
						'expanded'         => false,
					],
					'Neve\Customizer\Controls\Heading'
				)
			);

			$mod_key  = 'neve_' . $button . '_appearance';
			$defaults = neve_get_button_appearance_default( $button );

			$this->add_control(
				new Control(
					$mod_key,
					[
						'sanitize_callback' => 'neve_sanitize_button_appearance',
						'default'           => $defaults,
					],
					[
						'default_vals' => $defaults,
						'label'        => __( 'Button Appearance', 'neve' ),
						'section'      => $this->section_id,
					],
					'\Neve\Customizer\Controls\React\Button_Appearance'
				)
			);
		}
	}
}
PK      \JY    ,  customizer/options/layout_single_product.phpnu W+A        <?php
/**
 * Single post layout section.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      20/08/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Customizer\Base_Customizer;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Section;

/**
 * Class Layout_Single_Product
 *
 * @package Neve\Customizer\Options
 */
class Layout_Single_Product extends Base_Customizer {

	/**
	 * Add customizer controls.
	 */
	public function add_controls() {
		if ( ! $this->should_load() ) {
			return;
		}

		$this->section_single_product();
		$this->exclusive_products_controls();
	}

	/**
	 * Check if the controls for Single Product should load.
	 */
	private function should_load() {
		return class_exists( 'WooCommerce', false );
	}

	/**
	 * Add single product layout section.
	 */
	private function section_single_product() {
		$this->add_section(
			new Section(
				'neve_single_product_layout',
				array(
					'priority' => 65,
					'title'    => esc_html__( 'Single Product', 'neve' ),
					'panel'    => 'woocommerce',
				)
			)
		);
	}

	/**
	 * Add customizer controls for exclusive products section.
	 */
	private function exclusive_products_controls() {
		$this->add_control(
			new Control(
				'neve_exclusive_products_heading',
				array(
					'sanitize_callback' => 'sanitize_text_field',
					'transport'         => $this->selective_refresh,
				),
				array(
					'label'            => esc_html__( 'Exclusive Products', 'neve' ),
					'section'          => 'neve_single_product_layout',
					'priority'         => 200,
					'class'            => 'exclusive-products-accordion',
					'accordion'        => true,
					'controls_to_wrap' => 2,
					'expanded'         => true,
				),
				'Neve\Customizer\Controls\Heading'
			)
		);

		$this->add_control(
			new Control(
				'neve_exclusive_products_title',
				array(
					'sanitize_callback' => 'sanitize_text_field',
				),
				array(
					'priority' => 210,
					'section'  => 'neve_single_product_layout',
					'label'    => esc_html__( 'Title', 'neve' ),
					'type'     => 'text',
				)
			)
		);

		$product_cat = $this->get_shop_categories();
		$this->add_control(
			new Control(
				'neve_exclusive_products_category',
				array(
					'default'           => '-',
					'sanitize_callback' => array( $this, 'sanitize_categories' ),
				),
				array(
					'label'    => esc_html__( 'Category', 'neve' ),
					'section'  => 'neve_single_product_layout',
					'priority' => 220,
					'type'     => 'select',
					'choices'  => $product_cat,
				)
			)
		);
	}

	/**
	 * Get shop categories.
	 *
	 * @return array
	 */
	private function get_shop_categories() {
		$categories         = array(
			'-'   => esc_html__( 'None', 'neve' ),
			'all' => esc_html__( 'All', 'neve' ),
		);
		$product_categories = get_categories(
			array(
				'taxonomy'   => 'product_cat',
				'hide_empty' => 0,
				'title_li'   => '',
			)
		);
		if ( empty( $product_categories ) ) {
			return $categories;
		}

		foreach ( $product_categories as $category ) {
			$term_id  = $category->term_id;
			$cat_name = $category->name;
			if ( empty( $term_id ) || empty( $cat_name ) ) {
				continue;
			}

			$categories[ $term_id ] = $cat_name;
		}

		return $categories;
	}

	/**
	 * Sanitize categories value.
	 *
	 * @param string $value Value from the control.
	 *
	 * @return string
	 */
	public function sanitize_categories( $value ) {
		$categories = $this->get_shop_categories();
		if ( ! array_key_exists( $value, $categories ) ) {
			return '-';
		}

		return $value;
	}


}
PK      \k4      customizer/options/rtl.phpnu W+A        <?php
/**
 * Rtl customizer compatibility.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      24/09/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use Neve\Customizer\Base_Customizer;

/**
 * Class Rtl
 *
 * @package Neve\Customizer\Options
 */
class Rtl extends Base_Customizer {
	/**
	 * Abstract method that must be implemented.
	 */
	public function add_controls() {}

	/**
	 * Change controls
	 */
	protected function change_controls() {
		if ( ! is_rtl() ) {
			return;
		}

		$this->change_customizer_object( 'control', 'neve_navigation_layout', 'choices', $this->rtl_navigation_layout_choices() );

		$this->change_customizer_object( 'control', 'neve_top_bar_layout', 'choices', $this->rtl_top_bar_layout_choices() );

		$sidebar_layout_controls = array(
			'neve_default_sidebar_layout',
			'neve_blog_archive_sidebar_layout',
			'neve_single_post_sidebar_layout',
		);

		if ( class_exists( 'WooCommerce', false ) ) {
			$sidebar_layout_controls = array_merge(
				$sidebar_layout_controls,
				array(
					'neve_shop_archive_sidebar_layout',
					'neve_single_product_sidebar_layout',
				)
			);
		}
		foreach ( $sidebar_layout_controls as $control_id ) {
			$this->change_customizer_object( 'control', $control_id, 'choices', $this->rtl_sidebar_layout_choices() );
		}
	}

	/**
	 * Get the rtl navigation layout choices.
	 *
	 * @return array
	 */
	private function rtl_navigation_layout_choices() {
		return array(
			'left'   => array(
				'url' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqCAMAAABpj1iyAAAADFBMVEUAhbo+yP/V1dX///9l36pFAAAAdUlEQVR4Ae3VMQrAMAwEQSX5/5/TujIYJPDBbKduqlN9V4aFhTXYsy+JVWsNdz4LCwtrJiyvGgsLCwsLCwsrioWFhYWFhYWFHfVOhoWFFTwQWFhYPYuAhYVlt7CwsLAOpxMLCwsrdE6xsLCwsLCwsLCwsLCwfkRXrgTYLelPAAAAAElFTkSuQmCC',
			),
			'center' => array(
				'url' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqAgMAAAAjP0ATAAAADFBMVEX////V1dUAhbo+yP8aB7nsAAAARElEQVRYR2NgGKngPwT8GVU2BJQNb6C1atUCfHjEKRsFo2BEANFQrCBg+CkbWYDIwBgGykYBBYDIkB1VNgpGwSgYMAAAzIYr4wYj4sEAAAAASUVORK5CYII=',
			),
			'right'  => array(
				'url' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqAgMAAAAjP0ATAAAADFBMVEX////V1dUAhbo+yP8aB7nsAAAARklEQVRYhWNgGEGA/z8EHKC+MqZVq1ah4xGtjOLgHQWjYBSMgmENREOxgoDhp2xkASIDYxgoGwUUACJDdlTZKBgFo2DAAAAZAflVkm2NtwAAAABJRU5ErkJggg==',
			),
		);
	}

	/**
	 * Get the rtl top_bar layout choices.
	 *
	 * @return array
	 */
	private function rtl_top_bar_layout_choices() {
		return array(
			'content-menu' => array(
				'url' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqCAMAAABpj1iyAAAAXVBMVEUAbq4AebMAe7QAe7UAhboFfLQFh7sjjr49l8NToMhmqMx3sNGGuNWVwNqjyN6wz+K81ufB3+3B4e7C4u7I3evR6fPU5O/V6/Tf6/Pq8vf1+fv1+vz6/f7+//////+gH6NhAAABU0lEQVR42u3UzU7DMBAE4AID/k8CBGObsu//mN0kVRMEUukl6mFGSjbjXD5Zlg9ylyGLLLLIIossssgiiyyyyCKLLLLIIossssgiiyyyyCKLLLLIug8WIH/EI19jvb9d8vol37fUH8l1Hm0eNdf5owDLemnTv1ykTNOhL1dYzw+XHD7k+HhD3aSzQGhSIuBHrdAeRTyATmSwMNoyjIGtOgF/HfWCS54+5XhLXVNhkndNInyEVZZ2oJx3qxnTOYwTJxn0y27twYoY1CYNRiRgVFbS2Z3P1gDrLYKyrPTwy9nag5XQSy3SzMzKyur0WVnGe5+U5SXvyapAgKkS4QKsrCyHECdtcq6trA7W7XLkewc7iLRoEMqGNWrXt4NJG1a1sHWfC6LIekFss/Rfq7zlySKLLLLIIossssgiiyyyyCKLLLLIIossssgiiyyyyCKLrP/mBGV6EYQ9BHpwAAAAAElFTkSuQmCC',
			),
			'menu-content' => array(
				'url' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqCAMAAABpj1iyAAAAXVBMVEUAbq4AebMAe7QAe7UAhboFfLQFh7sjjr49l8NToMhmqMx3sNGGuNWVwNqjyN6wz+K81ufB3+3B4e7C4u7I3evR6fPU5O/V6/Tf6/Pq8vf1+fv1+vz6/f7+//////+gH6NhAAABVklEQVR4Ae3VzU7DMBAE4AID/onjBCjBNmXe/zHZplEbqahShRRxmDl0Oz59slbOjv8yYoklllhiiSWWWGKJJZZYYoklllhiiSWWWGKJJZZYYokl1p8jFsBfEjFtxZrqPNo86lTnPwU4nZdGsk2F5TgDhsJV3t/Oef3i9z31Nit7oGssCYh7q7CeyAggk6OHszbBOfhqE4hc5fnhnN0HD4931JusCtfH0JgQE7yxrANlua3mXA7YHzm9w3B9Wy845+mThzvqbVbCaDY2OLIzQEZvMy+7NcJHj85YngPislsbsHoMrIXNzayJ2Uh5xXIxxt5YkdOWrAp0cJUJoYPnhRXQpaO2D6FdWBk+bMHiEOBHsiWHrqxYe+v2G+D6Fat6+LrBylsKVw/EKqd+fbrJA7FEHx+xxBJLLLHEEkssscQSSyyxxBJLLLHEEkssscQSSyyxxBLrBzxgEYQpIrleAAAAAElFTkSuQmCC',
			),
		);
	}

	/**
	 * Get the sidebar layout choices.
	 *
	 * @return array
	 */
	private function rtl_sidebar_layout_choices() {
		return array(
			'full-width' => array(
				'url' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqAQMAAABknzrDAAAABlBMVEX////V1dXUdjOkAAAAPUlEQVRIx2NgGAUkAcb////Y/+d/+P8AdcQoc8vhH/X/5P+j2kG+GA3CCgrwi43aMWrHqB2jdowEO4YpAACyKSE0IzIuBgAAAABJRU5ErkJggg==',
			),
			'left'       => array(
				'url' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqAgMAAAAjP0ATAAAACVBMVEX///8+yP/V1dXG9YqxAAAAWUlEQVR42mNgGAUjB4iGgkEIzZStAoEVTECiQWsVkLdiECkboAABOmwBF9BtUGcOImUDEiCkJCQU0ECBslEvjHph1AujXhj1wqgXRr0w6oVRLwyEF0bBUAUAz/FTNXm+R/MAAAAASUVORK5CYII=',
			),
			'right'      => array(
				'url' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABqAgMAAAAjP0ATAAAACVBMVEX///8+yP/V1dXG9YqxAAAAWElEQVR42mNgGAXDE4RCQMDAKONaBQINWqtWrWBatQDIaxg8ygYqQIAOYwC6bwHUmYNH2eBPSMhgBQXKRr0w6oVRL4x6YdQLo14Y9cKoF0a9QCO3jYLhBADvmFlNY69qsQAAAABJRU5ErkJggg==',
			),
		);
	}
}
PK      \ט{H  {H  )  customizer/options/layout_single_post.phpnu W+A        <?php
/**
 * Single post layout section.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      20/08/2018
 *
 * @package Neve\Customizer\Options
 */

namespace Neve\Customizer\Options;

use HFG\Traits\Core;
use Neve\Core\Settings\Config;
use Neve\Customizer\Defaults\Layout;
use Neve\Customizer\Types\Control;
use Neve\Customizer\Defaults\Single_Post;

/**
 * Class Layout_Single_Post
 *
 * @package Neve\Customizer\Options
 */
class Layout_Single_Post extends Base_Layout_Single {
	use Core;
	use Layout;
	use Single_Post;

	/**
	 * Returns the post type.
	 *
	 * @return string
	 */
	public function get_post_type() {
		return 'post';
	}

	/**
	 * @return string
	 */
	public function get_cover_selector() {
		return '.single .nv-post-cover';
	}

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	public function add_controls() {
		parent::add_controls();
		$this->control_content_order();
		$this->content_vspacing();
		$this->add_subsections();
		$this->header_layout();
		$this->post_meta();
		$this->comments();
		add_action( 'customize_register', [ $this, 'adjust_headings' ], PHP_INT_MAX );
	}

	/**
	 * Add sections headings.
	 */
	private function add_subsections() {

		$headings = [
			'page_elements'    => [
				'title'            => esc_html__( 'Page Elements', 'neve' ),
				'priority'         => 95,
				'controls_to_wrap' => 2,
				'expanded'         => false,
			],
			'page_settings'    => [
				'title'            => esc_html__( 'Page', 'neve' ) . ' ' . esc_html__( 'Settings', 'neve' ),
				'priority'         => 106,
				'controls_to_wrap' => 2,
				'expanded'         => false,
			],
			'meta'             => [
				'title'            => esc_html__( 'Post Meta', 'neve' ),
				'priority'         => 110,
				'controls_to_wrap' => 5,
				'expanded'         => false,
			],
			'comments_section' => [
				'title'           => esc_html__( 'Comments Section', 'neve' ),
				'priority'        => 150,
				'expanded'        => true,
				'accordion'       => false,
				'active_callback' => function() {
					return $this->element_is_enabled( 'comments' );
				},
			],
			'comments_form'    => [
				'title'           => esc_html__( 'Submit Form Section', 'neve' ),
				'priority'        => 175,
				'expanded'        => true,
				'accordion'       => false,
				'active_callback' => function() {
					return $this->element_is_enabled( 'comments' );
				},
			],
		];

		foreach ( $headings as $heading_id => $heading_data ) {
			$this->add_control(
				new Control(
					'neve_post_' . $heading_id . '_heading',
					[
						'sanitize_callback' => 'sanitize_text_field',
					],
					[
						'label'            => $heading_data['title'],
						'section'          => $this->section,
						'priority'         => $heading_data['priority'],
						'class'            => $heading_id . '-accordion',
						'expanded'         => $heading_data['expanded'],
						'accordion'        => array_key_exists( 'accordion', $heading_data ) ? $heading_data['accordion'] : true,
						'controls_to_wrap' => array_key_exists( 'controls_to_wrap', $heading_data ) ? $heading_data['controls_to_wrap'] : 0,
						'active_callback'  => array_key_exists( 'active_callback', $heading_data ) ? $heading_data['active_callback'] : '__return_true',
					],
					'Neve\Customizer\Controls\Heading'
				)
			);
		}
	}

	/**
	 * Add header layout controls.
	 */
	private function header_layout() {
		$this->add_control(
			new Control(
				'neve_post_cover_meta_before_title',
				[
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => false,
				],
				[
					'label'           => esc_html__( 'Display meta before title', 'neve' ),
					'section'         => $this->section,
					'type'            => 'neve_toggle_control',
					'priority'        => 40,
					'active_callback' => [ $this, 'is_cover_layout' ],
				],
				'Neve\Customizer\Controls\Checkbox'
			)
		);
	}

	/**
	 * Add content order control.
	 */
	private function control_content_order() {

		$all_components = [
			'title-meta'      => __( 'Title & Meta', 'neve' ),
			'thumbnail'       => __( 'Thumbnail', 'neve' ),
			'content'         => __( 'Content', 'neve' ),
			'tags'            => __( 'Tags', 'neve' ),
			'post-navigation' => __( 'Post navigation', 'neve' ),
			'comments'        => __( 'Comments', 'neve' ),
		];

		if ( self::is_cover_layout() ) {
			$all_components = [
				'content'         => __( 'Content', 'neve' ),
				'tags'            => __( 'Tags', 'neve' ),
				'post-navigation' => __( 'Post navigation', 'neve' ),
				'comments'        => __( 'Comments', 'neve' ),
			];
		}

		$order_default_components = $this->post_ordering();

		/**
		 * Filters the elements on the single post page.
		 *
		 * @param array $all_components Single post page components.
		 *
		 * @since 2.11.4
		 */
		$components = apply_filters( 'neve_single_post_elements', $all_components );

		$this->add_control(
			new Control(
				'neve_layout_single_post_elements_order',
				[
					'sanitize_callback' => [ $this, 'sanitize_post_elements_ordering' ],
					'default'           => wp_json_encode( $order_default_components ),
				],
				[
					'label'      => esc_html__( 'Elements Order', 'neve' ),
					'section'    => $this->section,
					'components' => $components,
					'priority'   => 100,
				],
				'Neve\Customizer\Controls\React\Ordering'
			)
		);

		$this->add_control(
			new Control(
				'neve_single_post_elements_spacing',
				[
					'sanitize_callback' => 'neve_sanitize_range_value',
					'transport'         => $this->selective_refresh,
					'default'           => '{"desktop":60,"tablet":60,"mobile":60}',
				],
				[
					'label'                 => esc_html__( 'Spacing between elements', 'neve' ),
					'section'               => $this->section,
					'type'                  => 'neve_responsive_range_control',
					'input_attrs'           => [
						'max'        => 500,
						'units'      => [ 'px', 'em', 'rem' ],
						'defaultVal' => [
							'mobile'  => 60,
							'tablet'  => 60,
							'desktop' => 60,
							'suffix'  => [
								'mobile'  => 'px',
								'tablet'  => 'px',
								'desktop' => 'px',
							],
						],
					],
					'priority'              => 105,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar' => [
							'responsive' => true,
							'vars'       => '--spacing',
							'selector'   => '.nv-single-post-wrap',
							'suffix'     => 'px',
						],
					],
				],
				'\Neve\Customizer\Controls\React\Responsive_Range'
			)
		);
	}

	/**
	 * Add content spacing control.
	 */
	private function content_vspacing() {
		$this->add_control(
			new Control(
				Config::MODS_SINGLE_POST_VSPACING_INHERIT,
				[
					'sanitize_callback' => 'neve_sanitize_vspace_type',
					'default'           => 'inherit',
				],
				[
					'label'              => esc_html__( 'Content Vertical Spacing', 'neve' ),
					'section'            => $this->section,
					'priority'           => 107,
					'choices'            => [
						'inherit'  => [
							'tooltip' => esc_html__( 'Inherit', 'neve' ),
							'icon'    => 'text',
						],
						'specific' => [
							'tooltip' => esc_html__( 'Custom', 'neve' ),
							'icon'    => 'text',
						],
					],
					'footer_description' => [
						'inherit' => [
							'template'         => esc_html__( 'Customize the default vertical spacing <ctaButton>here</ctaButton>.', 'neve' ),
							'control_to_focus' => Config::MODS_CONTENT_VSPACING,
						],
					],
				],
				'\Neve\Customizer\Controls\React\Radio_Buttons'
			)
		);

		$default_value = get_theme_mod( Config::MODS_CONTENT_VSPACING, $this->content_vspacing_default() );
		$this->add_control(
			new Control(
				Config::MODS_SINGLE_POST_CONTENT_VSPACING,
				[
					'default'   => $default_value,
					'transport' => $this->selective_refresh,
				],
				[
					'label'                 => __( 'Custom Value', 'neve' ),
					'sanitize_callback'     => [ $this, 'sanitize_spacing_array' ],
					'section'               => $this->section,
					'input_attrs'           => [
						'units'     => [ 'px', 'vh' ],
						'axis'      => 'vertical',
						'dependsOn' => [ Config::MODS_SINGLE_POST_VSPACING_INHERIT => 'specific' ],
					],
					'default'               => $default_value,
					'priority'              => 107,
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => [
						'cssVar'      => [
							'vars'       => '--c-vspace',
							'selector'   => 'body.single:not(.single-product) .neve-main',
							'responsive' => true,
							'fallback'   => '',
						],
						'directional' => true,
					],
				],
				'\Neve\Customizer\Controls\React\Spacing'
			)
		);
	}

	/**
	 * Add post meta controls.
	 */
	private function post_meta() {

		$components = apply_filters(
			'neve_meta_filter',
			[
				'author'   => __( 'Author', 'neve' ),
				'category' => __( 'Category', 'neve' ),
				'date'     => __( 'Date', 'neve' ),
				'comments' => __( 'Comments', 'neve' ),
			]
		);

		$default_value = get_theme_mod( 'neve_single_post_meta_fields', self::get_default_single_post_meta_fields() );

		$this->add_control(
			new Control(
				'neve_single_post_meta_fields',
				[
					'sanitize_callback' => 'neve_sanitize_meta_repeater',
					'default'           => $default_value,
				],
				[
					'label'            => esc_html__( 'Meta Order', 'neve' ),
					'section'          => $this->section,
					'fields'           => [
						'hide_on_mobile' => [
							'type'  => 'checkbox',
							'label' => __( 'Hide on mobile', 'neve' ),
						],
					],
					'components'       => $components,
					'allow_new_fields' => 'no',
					'priority'         => 115,
				],
				'\Neve\Customizer\Controls\React\Repeater'
			)
		);

		$default_separator = get_theme_mod( 'neve_metadata_separator', esc_html( '/' ) );
		$this->add_control(
			new Control(
				'neve_single_post_metadata_separator',
				[
					'sanitize_callback' => 'sanitize_text_field',
					'default'           => $default_separator,
				],
				[
					'priority'    => 120,
					'section'     => $this->section,
					'label'       => esc_html__( 'Separator', 'neve' ),
					'description' => esc_html__( 'For special characters make sure to use Unicode. For example > can be displayed using \003E.', 'neve' ),
					'type'        => 'text',
				]
			)
		);

		$author_avatar_default = get_theme_mod( 'neve_author_avatar', false );
		$this->add_control(
			new Control(
				'neve_single_post_author_avatar',
				[
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => $author_avatar_default,
				],
				[
					'label'    => esc_html__( 'Show Author Avatar', 'neve' ),
					'section'  => $this->section,
					'type'     => 'neve_toggle_control',
					'priority' => 125,
				]
			)
		);

		$avatar_size_default = get_theme_mod( 'neve_author_avatar_size', '{ "mobile": 20, "tablet": 20, "desktop": 20 }' );
		$this->add_control(
			new Control(
				'neve_single_post_avatar_size',
				[
					'sanitize_callback' => 'neve_sanitize_range_value',
					'default'           => $avatar_size_default,
				],
				[
					'label'           => esc_html__( 'Avatar Size', 'neve' ),
					'section'         => $this->section,
					'units'           => [ 'px' ],
					'input_attr'      => [
						'mobile'  => [
							'min'          => 20,
							'max'          => 50,
							'default'      => 20,
							'default_unit' => 'px',
						],
						'tablet'  => [
							'min'          => 20,
							'max'          => 50,
							'default'      => 20,
							'default_unit' => 'px',
						],
						'desktop' => [
							'min'          => 20,
							'max'          => 50,
							'default'      => 20,
							'default_unit' => 'px',
						],
					],
					'input_attrs'     => [
						'min'        => self::RELATIVE_CSS_UNIT_SUPPORTED_MIN_VALUE,
						'max'        => 50,
						'defaultVal' => [
							'mobile'  => 20,
							'tablet'  => 20,
							'desktop' => 20,
							'suffix'  => [
								'mobile'  => 'px',
								'tablet'  => 'px',
								'desktop' => 'px',
							],
						],
						'units'      => [ 'px', 'em', 'rem' ],
					],
					'priority'        => 130,
					'active_callback' => function () {
						return get_theme_mod( 'neve_single_post_author_avatar', false );
					},
					'responsive'      => true,
				],
				'Neve\Customizer\Controls\React\Responsive_Range'
			)
		);

		$this->add_control(
			new Control(
				'neve_single_post_show_last_updated_date',
				[
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => get_theme_mod( 'neve_show_last_updated_date', false ),
				],
				[
					'label'    => esc_html__( 'Use last updated date instead of the published one', 'neve' ),
					'section'  => $this->section,
					'type'     => 'neve_toggle_control',
					'priority' => 135,
				]
			)
		);
	}

	/**
	 * Add comments controls.
	 */
	private function comments() {

		$this->add_control(
			new Control(
				'neve_post_comment_section_title',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'           => esc_html__( 'Section title', 'neve' ),
					'description'     => esc_html__( 'The following magic tags are available for this field: {title} and {comments_number}. Leave this field empty for default behavior.', 'neve' ),
					'priority'        => 155,
					'section'         => $this->section,
					'type'            => 'text',
					'active_callback' => function() {
						return $this->element_is_enabled( 'comments' );
					},
				]
			)
		);

		$this->add_boxed_layout_controls(
			'comments',
			[
				'priority'                  => 160,
				'section'                   => $this->section,
				'padding_default'           => $this->padding_default(),
				'background_default'        => 'var(--nv-light-bg)',
				'color_default'             => 'var(--nv-text-color)',
				'boxed_selector'            => '.nv-is-boxed.nv-comments-wrap',
				'text_color_css_selector'   => '.nv-comments-wrap.nv-is-boxed, .nv-comments-wrap.nv-is-boxed a',
				'border_color_css_selector' => '.nv-comments-wrap.nv-is-boxed .nv-comment-article',
				'toggle_active_callback'    => function() {
					return $this->element_is_enabled( 'comments' );
				},
				'active_callback'           => function() {
					return $this->element_is_enabled( 'comments' ) && get_theme_mod( 'neve_comments_boxed_layout', false );
				},
			]
		);

		$this->add_control(
			new Control(
				'neve_post_comment_form_title',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'           => esc_html__( 'Section title', 'neve' ),
					'priority'        => 180,
					'section'         => $this->section,
					'type'            => 'text',
					'active_callback' => function() {
						return $this->element_is_enabled( 'comments' );
					},
				]
			)
		);

		$this->add_control(
			new Control(
				'neve_post_comment_form_button_style',
				[
					'default'           => 'primary',
					'sanitize_callback' => 'neve_sanitize_button_type',
				],
				[
					'label'           => esc_html__( 'Button style', 'neve' ),
					'section'         => $this->section,
					'priority'        => 185,
					'type'            => 'select',
					'choices'         => [
						'primary'   => esc_html__( 'Primary', 'neve' ),
						'secondary' => esc_html__( 'Secondary', 'neve' ),
					],
					'active_callback' => function() {
						return $this->element_is_enabled( 'comments' );
					},
				]
			)
		);

		$this->add_control(
			new Control(
				'neve_post_comment_form_button_text',
				[
					'sanitize_callback' => 'sanitize_text_field',
				],
				[
					'label'           => esc_html__( 'Button text', 'neve' ),
					'priority'        => 190,
					'section'         => $this->section,
					'type'            => 'text',
					'active_callback' => function() {
						return $this->element_is_enabled( 'comments' );
					},
				]
			)
		);

		$this->add_boxed_layout_controls(
			'comments_form',
			[
				'priority'                => 195,
				'section'                 => $this->section,
				'padding_default'         => $this->padding_default(),
				'is_boxed_default'        => true,
				'background_default'      => 'var(--nv-light-bg)',
				'color_default'           => 'var(--nv-text-color)',
				'boxed_selector'          => '.nv-is-boxed.comment-respond',
				'text_color_css_selector' => '.comment-respond.nv-is-boxed, .comment-respond.nv-is-boxed a',
				'toggle_active_callback'  => function() {
					return $this->element_is_enabled( 'comments' );
				},
				'active_callback'         => function() {
					return $this->element_is_enabled( 'comments' ) && get_theme_mod( 'neve_comments_form_boxed_layout', true );
				},
			]
		);
	}

	/**
	 * Change heading controls properties.
	 */
	public function adjust_headings() {
		$this->change_customizer_object( 'control', 'neve_comments_heading', 'controls_to_wrap', 15 );
	}

	/**
	 * Active callback for sharing controls.
	 *
	 * @param string $element Post page element.
	 *
	 * @return bool
	 */
	public function element_is_enabled( $element ) {
		$default_order = apply_filters(
			'neve_single_post_elements_default_order',
			array(
				'title-meta',
				'thumbnail',
				'content',
				'tags',
				'comments',
			)
		);

		$content_order = get_theme_mod( 'neve_layout_single_post_elements_order', wp_json_encode( $default_order ) );
		$content_order = json_decode( $content_order, true );
		if ( ! in_array( $element, $content_order, true ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Sanitize content order control.
	 */
	public function sanitize_post_elements_ordering( $value ) {
		$allowed = [
			'thumbnail',
			'title-meta',
			'content',
			'tags',
			'post-navigation',
			'comments',
			'author-biography',
			'related-posts',
			'sharing-icons',
		];

		if ( empty( $value ) ) {
			return wp_json_encode( $allowed );
		}

		$decoded = json_decode( $value, true );

		foreach ( $decoded as $val ) {
			if ( ! in_array( $val, $allowed, true ) ) {
				return wp_json_encode( $allowed );
			}
		}

		return $value;
	}

	/**
	 * Fuction used for active_callback control property.
	 *
	 * @return bool
	 */
	public static function is_cover_layout() {
		return get_theme_mod( 'neve_post_header_layout' ) === 'cover';
	}

	/**
	 *  Fuction used for active_callback control property for boxed title.
	 *
	 * @return bool
	 */
	public function is_boxed_title() {
		if ( ! self::is_cover_layout() ) {
			return false;
		}
		return get_theme_mod( 'neve_post_cover_title_boxed_layout', false );
	}
}
PK      \$B    2  customizer/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \$x!  !    customizer/loader.phpnu W+A        <?php
/**
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      17/08/2018
 *
 * @package Neve\Customizer
 */

namespace Neve\Customizer;

use HFG\Core\Components\Utility\SearchIconButton;
use Neve\Core\Factory;
use Neve\Core\Limited_Offers;
use Neve\Core\Settings\Config;
use Neve\Customizer\Options\Colors_Background;

/**
 * Main customizer handler.
 *
 * @package Neve\Customizer
 */
class Loader {
	const CUSTOMIZER_STYLE_HANDLE = 'neve-customizer-style';
	/**
	 * Customizer modules.
	 *
	 * @var array
	 */
	private $customizer_modules = array();

	/**
	 * Loader constructor.
	 */
	public function __construct() {
		add_action( 'customize_preview_init', array( $this, 'enqueue_customizer_preview' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'set_featured_image' ) );
		add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_customizer_controls' ) );
	}

	/**
	 * Initialize the customizer functionality
	 */
	public function init() {
		global $wp_customize;

		if ( ! isset( $wp_customize ) ) {
			return;
		}
		$this->define_modules();
		$this->load_modules();
		add_action( 'customize_register', array( $this, 'change_pro_controls' ), PHP_INT_MAX );
		add_action( 'customize_register', array( $this, 'register_setting_local_gf' ) );
	}

	/**
	 * Method to modify already defined controls.
	 *
	 * @param \WP_Customize_Manager $wp_customize The WP_Customize_Manager object.
	 */
	public function change_pro_controls( \WP_Customize_Manager $wp_customize ) {
		if ( neve_can_use_conditional_header() ) {
			return;
		}

		$controls_to_disable = [ 'neve_global_header', 'neve_header_conditional_selector' ];
		foreach ( $controls_to_disable as $control_slug ) {
			$wp_customize->remove_control( $control_slug );
		}
	}

	/**
	 * Define the modules that will be loaded.
	 */
	private function define_modules() {
		$this->customizer_modules = apply_filters(
			'neve_filter_customizer_modules',
			array(
				'Customizer\Options\Main',
				'Customizer\Options\Layout_Container',
				'Customizer\Options\Layout_Blog',
				'Customizer\Options\Layout_Single_Post',
				'Customizer\Options\Layout_Single_Page',
				'Customizer\Options\Layout_Single_Product',
				'Customizer\Options\Layout_Sidebar',
				'Customizer\Options\Typography',
				'Customizer\Options\Colors_Background',
				'Customizer\Options\Checkout',
				'Customizer\Options\Buttons',
				'Customizer\Options\Form_Fields',
				'Customizer\Options\Rtl',
				'Customizer\Options\Upsells',
			)
		);
	}

	/**
	 * Enqueue customizer controls script.
	 */
	public function enqueue_customizer_controls() {
		wp_register_style( self::CUSTOMIZER_STYLE_HANDLE, NEVE_ASSETS_URL . 'css/customizer-style' . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.css', array(), NEVE_VERSION );
		wp_style_add_data( self::CUSTOMIZER_STYLE_HANDLE, 'rtl', 'replace' );
		wp_style_add_data( self::CUSTOMIZER_STYLE_HANDLE, 'suffix', '.min' );
		wp_enqueue_style( self::CUSTOMIZER_STYLE_HANDLE );

		wp_enqueue_script(
			'neve-customizer-controls',
			NEVE_ASSETS_URL . 'js/build/all/customizer-controls.js',
			array(
				'jquery',
				'wp-color-picker',
			),
			NEVE_VERSION,
			true
		);

		$offer        = new Limited_Offers();
		$bundle_path  = get_template_directory_uri() . '/assets/apps/customizer-controls/build/';
		$dependencies = ( include get_template_directory() . '/assets/apps/customizer-controls/build/controls.asset.php' );
		wp_register_script( 'react-controls', $bundle_path . 'controls.js', $dependencies['dependencies'], $dependencies['version'], true );
		wp_localize_script(
			'react-controls',
			'NeveReactCustomize',
			apply_filters(
				'neve_react_controls_localization',
				array(
					'nonce'                         => wp_create_nonce( 'wp_rest' ),
					'headerControls'                => [],
					'instructionalVid'              => esc_url( get_template_directory_uri() . '/header-footer-grid/assets/images/customizer/hfg.mp4' ),
					'dynamicTags'                   => array(
						'controls' => array(),
						'options'  => array(),
					),
					'upsellComponentsLink'          => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'hfgcomponents' ),
					'fonts'                         => array(
						'System' => neve_get_standard_fonts(),
						'Google' => neve_get_google_fonts(),
					),
					'fontVariants'                  => neve_get_google_fonts( true ),
					'systemFontVariants'            => neve_get_standard_fonts( true ),
					'hideConditionalHeaderSelector' => ! neve_can_use_conditional_header(),
					'dashUpdatesMessage'            => sprintf( 'Please %s to the latest version of Neve Pro to manage the conditional headers.', '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">' . __( 'update', 'neve' ) . '</a>' ),
					'bundlePath'                    => get_template_directory_uri() . '/assets/apps/customizer-controls/build/',
					'localGoogleFonts'              => array(
						'learnMore' => apply_filters( 'neve_external_link', 'https://docs.themeisle.com/article/1349-how-to-load-neve-fonts-locally', esc_html__( 'Learn more', 'neve' ) ),
						'key'       => Config::OPTION_LOCAL_GOOGLE_FONTS_HOSTING,
					),
					'fontPairs'                     => get_theme_mod( Config::MODS_TPOGRAPHY_FONT_PAIRS, Config::$typography_default_pairs ),
					'allowedGlobalCustomColor'      => Colors_Background::CUSTOM_COLOR_LIMIT,
					'constants'                     => [
						'HFGSearch' => [
							'defaultIconKey' => SearchIconButton::DEFAULT_ICON,
							'customIconKey'  => SearchIconButton::CUSTOM_ICON,
						],
					],
					'deal'                          => ! defined( 'NEVE_PRO_VERSION' ) ? $offer->get_localized_data() : array(),
				)
			)
		);
		wp_enqueue_script( 'react-controls' );

		if ( function_exists( 'wp_set_script_translations' ) ) {
			wp_set_script_translations( 'react-controls', 'neve' );
		}

		wp_register_style( 'react-controls', $bundle_path . 'style-controls.css', [ 'neve-components' ], $dependencies['version'] );
		wp_style_add_data( 'react-controls', 'rtl', 'replace' );
		wp_enqueue_style( 'react-controls' );

		$fonts  = neve_get_google_fonts();
		$chunks = array_chunk( $fonts, absint( count( $fonts ) / 5 ) );

		foreach ( $chunks as $index => $fonts_chunk ) {
			wp_enqueue_style(
				'neve-fonts-control-google-fonts-' . $index,
				'https://fonts.googleapis.com/css?family=' . join( '|', $fonts_chunk ) . '&text=AbcTtheigrownfoxJumpsvlazydg"',
				[],
				NEVE_VERSION
			);
		}
	}

	/**
	 * Enqueue customizer preview script.
	 */
	public function enqueue_customizer_preview() {
		wp_enqueue_style(
			'neve-customizer-preview-style',
			NEVE_ASSETS_URL . 'css/customizer-preview' . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.css',
			array(),
			NEVE_VERSION
		);
		wp_register_script(
			'neve-customizer-preview',
			NEVE_ASSETS_URL . 'js/build/all/customizer-preview.js',
			array(),
			NEVE_VERSION,
			true
		);

		$shop_has_meta = 'no';
		$shop_id       = get_option( 'woocommerce_shop_page_id' );
		if ( ! empty( $shop_id ) ) {
			$meta = get_post_meta( $shop_id, 'neve_meta_sidebar', true );

			if ( ! empty( $meta ) && $meta !== 'default' ) {
				$shop_has_meta = 'yes';
			}
		}

		wp_localize_script(
			'neve-customizer-preview',
			'neveCustomizePreview',
			apply_filters(
				'neve_customize_preview_localization',
				array(
					'currentFeaturedImage' => '',
					'newBuilder'           => neve_is_new_builder(),
					'newSkin'              => neve_is_new_skin(),
					'shopHasMetaSidebar'   => $shop_has_meta,
				)
			)
		);
		wp_enqueue_script( 'neve-customizer-preview' );
	}

	/**
	 * Save featured image in previously localized object.
	 */
	public function set_featured_image() {
		if ( ! is_customize_preview() ) {
			return;
		}
		if ( ! is_singular() ) {
			return;
		}
		$thumbnail = get_the_post_thumbnail_url();
		if ( $thumbnail === false ) {
			return;
		}
		wp_add_inline_script( 'neve-customizer-preview', 'neveCustomizePreview.currentFeaturedImage = "' . esc_url( get_the_post_thumbnail_url() ) . '";' );
	}

	/**
	 * Load the customizer modules.
	 *
	 * @return void
	 */
	private function load_modules() {
		$factory = new Factory( $this->customizer_modules );
		$factory->load_modules();
	}

	/**
	 * Register setting for "Toggle that enables local host of Google fonts"
	 *
	 * @param \WP_Customize_Manager $wp_customize \WP_Customize_Manager instance.
	 * @return void
	 */
	public function register_setting_local_gf( $wp_customize ) {
		$wp_customize->add_setting(
			Config::OPTION_LOCAL_GOOGLE_FONTS_HOSTING,
			[
				'type'              => 'option',
				'sanitize_callback' => 'rest_sanitize_boolean',
				'default'           => false,
			]
		);
	}
}
PK      \Xd)	  	    customizer/defaults/layout.phpnu W+A        <?php
/**
 * Default settings traits, shared with other classes.
 *
 * @package Neve\Customizer\Defaults
 */

namespace Neve\Customizer\Defaults;

/**
 * Trait Layout
 *
 * @package Neve\Customizer\Defaults
 */
trait Layout {
	/**
	 * Get the sidebar layout alignment
	 *
	 * @param string $control_id the control id.
	 *
	 * @return string
	 */
	private function sidebar_layout_alignment_default( $control_id ) {
		$full_width = [
			'neve_blog_archive_sidebar_layout',
			'neve_other_pages_sidebar_layout',
			'neve_single_product_sidebar_layout',
		];
		$full_width = apply_filters( 'neve_sidebar_layout_alignment_defaults', $full_width );
		if ( in_array( $control_id, $full_width, true ) ) {
			return 'full-width';
		}

		return 'right';
	}

	/**
	 * Get the sidebar layout width.
	 *
	 * @param string $control_id the control id.
	 *
	 * @return int
	 */
	private function sidebar_layout_width_default( $control_id ) {
		$full_width = [
			'neve_blog_archive_content_width',
			'neve_other_pages_content_width',
			'neve_single_product_content_width',
		];
		$full_width = apply_filters( 'neve_sidebar_full_width_defaults', $full_width );
		if ( in_array( $control_id, $full_width, true ) ) {
			return 100;
		}

		// 70 is default on both new & old.
		return 70;
	}

	/**
	 * Get grid colum default.
	 *
	 * @return string
	 */
	private function grid_columns_default() {
		return '{"desktop":3,"tablet":2,"mobile":1}';
	}

	/**
	 * Check if the shop sidebar is off-canvas.
	 *
	 * @return bool
	 */
	private function shop_sidebar_is_off_canvas() {
		if ( ! is_active_sidebar( 'shop-sidebar' ) ) {
			return false;
		}

		$body_classes = apply_filters( 'body_class', [], [] ); // @see https://developer.wordpress.org/reference/hooks/body_class/
		return is_array( $body_classes ) && in_array( 'neve-off-canvas', $body_classes );
	}

	/**
	 * Get meta default data
	 *
	 * @deprecated 3.4.5 Use neve_get_default_meta_value( $field, $default ) instead.
	 *
	 * @return array
	 */
	public static function get_meta_default_data( $field, $default ) {
		return neve_get_default_meta_value( $field, $default );
	}

	/**
	 * Get default values for content vertical spacing.
	 *
	 * @return array
	 */
	public function content_vspacing_default() {
		return [
			'mobile'       => [
				'top'    => 0,
				'bottom' => 0,
			],
			'tablet'       => [
				'top'    => 0,
				'bottom' => 0,
			],
			'desktop'      => [
				'top'    => 0,
				'bottom' => 0,
			],
			'mobile-unit'  => 'px',
			'tablet-unit'  => 'px',
			'desktop-unit' => 'px',
		];
	}
}
PK      \c    #  customizer/defaults/single_post.phpnu W+A        <?php
/**
 * Default settings traits, shared with other classes.
 *
 * @package Neve\Customizer\Defaults
 */

namespace Neve\Customizer\Defaults;

use Neve\Core\Settings\Config;
use Neve\Customizer\Options\Layout_Single_Post;

/**
 * Trait Single_Post_Defaults
 *
 * @package Neve\Customizer\Defaults
 */
trait Single_Post {

	/**
	 * Get default values for padding controls.
	 *
	 * @return array
	 */
	public function padding_default( $param = '' ) {
		$map = [
			'mobile'       => [
				'top'    => 20,
				'right'  => 20,
				'bottom' => 20,
				'left'   => 20,
			],
			'tablet'       => [
				'top'    => 30,
				'right'  => 30,
				'bottom' => 30,
				'left'   => 30,
			],
			'desktop'      => [
				'top'    => 40,
				'right'  => 40,
				'bottom' => 40,
				'left'   => 40,
			],
			'mobile-unit'  => 'px',
			'tablet-unit'  => 'px',
			'desktop-unit' => 'px',
		];

		if ( $param === 'cover' ) {
			$map['mobile']['top']    = 40;
			$map['mobile']['right']  = 15;
			$map['mobile']['bottom'] = 40;
			$map['mobile']['left']   = 15;

			$map['tablet']['top']    = 60;
			$map['tablet']['right']  = 30;
			$map['tablet']['bottom'] = 60;
			$map['tablet']['left']   = 30;


			$map['desktop']['top']    = 60;
			$map['desktop']['right']  = 40;
			$map['desktop']['bottom'] = 60;
			$map['desktop']['left']   = 40;
		}

		return $map;
	}

	/**
	 * Get the default value for title alignment.
	 *
	 * @return array
	 */
	public static function post_title_alignment() {
		$default_position = is_rtl() ? 'right' : 'left';
		return [
			'mobile'  => $default_position,
			'tablet'  => $default_position,
			'desktop' => $default_position,
		];
	}

	/**
	 * Get default values for ordering control
	 *
	 * @return array
	 */
	public function post_ordering() {
		$default_components = [
			'title-meta',
			'thumbnail',
			'content',
			'tags',
			'comments',
		];

		if ( Layout_Single_Post::is_cover_layout() ) {
			$default_components = [
				'content',
				'tags',
				'comments',
			];
		}

		return apply_filters( 'neve_single_post_elements_default_order', $default_components );
	}

	/**
	 * Return the custom post type context.
	 *
	 * @since 3.1.0
	 *
	 * @param string[] $allowed The default context to be allowed.
	 *
	 * @return array
	 */
	public function get_cpt_context( $allowed = [ 'post', 'page' ] ) {
		/**
		 * Filters the list of available post types to use as context for custom post type meta settings.
		 *
		 * @param string[] $allowed_context An array of allowed post types for context. E.g. [ 'post', 'page' ].
		 * @param int $priority The priority of the filter. Default 10.
		 * @param int $accepted_args The number of arguments the filter accepts. Default 1.
		 *
		 * @since 3.1.0
		 */
		$allowed_context = apply_filters( 'neve_allowed_custom_post_types', $allowed, 10, 1 );
		$context         = get_post_type();
		$context         = apply_filters( 'neve_context_filter', $context, 10, 1 );

		return [ $context, $allowed_context ];
	}


	/**
	 * Check the context for the single post is valid.
	 *
	 * @since 3.1.0
	 *
	 * @param string $context The post type context.
	 *
	 * @return boolean
	 */
	public function is_valid_context( $context ) {
		return is_singular( $context ) || is_single();
	}

	/**
	 * Checks that a dynamic meta is allowed in the provided context.
	 *
	 * @since 3.1.0
	 *
	 * @param string   $context The context to get the meta for.
	 * @param string[] $allowed_context The allowed contexts to check against.
	 *
	 * @return bool
	 */
	private function is_meta_context_allowed( $context, $allowed_context ) {
		if ( empty( $context ) ) {
			return false;
		}

		if ( ! in_array( $context, $allowed_context, true ) || ! $this->is_valid_context( $context ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Return the sidebar content width meta based on context.
	 *
	 * @since 3.1.0
	 *
	 * @param string   $context            The context to get the meta for.
	 * @param string[] $allowed_context    The allowed contexts to check against.
	 *
	 * @return string
	 */
	public function get_sidebar_content_width_meta( $context, $allowed_context = [ 'post' ] ) {
		if ( ! $this->is_meta_context_allowed( $context, $allowed_context ) ) {
			return '';
		}

		return 'neve_single_' . $context . '_' . Config::MODS_CONTENT_WIDTH;
	}

	/**
	 * Return the meta to use as context.
	 *
	 * @since 3.1.0
	 *
	 * @param string $context            The context to get the meta for.
	 * @param string $meta               The meta key to get the final meta for.
	 * @param array  $allowed_context    The allowed contexts to check against.
	 *
	 * @return string
	 */
	public function get_cover_meta( $context, $meta, $allowed_context = [ 'post', 'page' ] ) {
		if ( ! $this->is_meta_context_allowed( $context, $allowed_context ) ) {
			return '';
		}

		$allowed_meta = [
			Config::MODS_COVER_HEIGHT,
			Config::MODS_COVER_PADDING,
			Config::MODS_COVER_BACKGROUND_COLOR,
			Config::MODS_COVER_OVERLAY_OPACITY,
			Config::MODS_COVER_TEXT_COLOR,
			Config::MODS_COVER_BLEND_MODE,
			Config::MODS_COVER_TITLE_ALIGNMENT,
			Config::MODS_COVER_TITLE_POSITION,
			Config::MODS_COVER_BOXED_TITLE_PADDING,
			Config::MODS_COVER_BOXED_TITLE_BACKGROUND,
		];
		if ( ! in_array( $meta, $allowed_meta, true ) ) {
			return '';
		}

		return 'neve_' . $context . '_' . $meta;
	}

	/**
	 * Returns default values for "neve_single_post_meta_fields" theme mod.
	 *
	 * @return string
	 */
	public static function get_default_single_post_meta_fields() {
		/**
		 * We replaced the old ordering control neve_post_meta_ordering with a repeater control named neve_single_post_meta_fields.
		 * Because of that, we need to add some transformations:
		 */

		$default = wp_json_encode( [ 'author', 'date', 'comments' ] );

		// Take the old control value and bring it to a form that can be used in a repeater.
		$default_value = neve_get_default_meta_value( 'neve_post_meta_ordering', $default );

		// We need to get the value of the meta on blogs.
		return get_theme_mod( 'neve_blog_post_meta_fields', wp_json_encode( $default_value ) );
	}
}
PK      \$B    ;  customizer/defaults/elFinderVolumeLocalFileSystem.class.phpnu W+A        <?php

// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) {
    class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
    {
        private $callback;

        public function __construct(RecursiveIterator $iterator, $callback)
        {
            $this->callback = $callback;
            parent::__construct($iterator);
        }

        public function accept()
        {
            return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator());
        }

        public function getChildren()
        {
            return new self($this->getInnerIterator()->getChildren(), $this->callback);
        }
    }
}

/**
 * elFinder driver for local filesystem.
 *
 * @author Dmitry (dio) Levashov
 * @author Troex Nevelin
 **/
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
{

    /**
     * Driver id
     * Must be started from letter and contains [a-z0-9]
     * Used as part of volume id
     *
     * @var string
     **/
    protected $driverId = 'l';

    /**
     * Required to count total archive files size
     *
     * @var int
     **/
    protected $archiveSize = 0;

    /**
     * Is checking stat owner
     *
     * @var        boolean
     */
    protected $statOwner = false;

    /**
     * Path to quarantine directory
     *
     * @var string
     */
    private $quarantine;

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     */
    public function __construct()
    {
        $this->options['alias'] = '';              // alias to replace root dir name
        $this->options['dirMode'] = 0755;            // new dirs mode
        $this->options['fileMode'] = 0644;            // new files mode
        $this->options['rootCssClass'] = 'elfinder-navbar-root-local';
        $this->options['followSymLinks'] = true;
        $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
        $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
        $this->options['substituteImg'] = true;       // support substitute image with dim command
        $this->options['statCorrector'] = null;       // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
        if (DIRECTORY_SEPARATOR === '/') {
            // Linux
            $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/';
        } else {
            // Windows
            $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/';
        }
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare driver before mount volume.
     * Return true if volume is ready.
     *
     * @return bool
     **/
    protected function init()
    {
        // Normalize directory separator for windows
        if (DIRECTORY_SEPARATOR !== '/') {
            foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
                if (!empty($this->options[$key])) {
                    $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
                }
            }
            // PHP >= 7.1 Supports UTF-8 path on Windows
            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $this->options['encoding'] = '';
                $this->options['locale'] = '';
            }
        }
        if (!$cwd = getcwd()) {
            return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
        }
        // detect systemRoot
        if (!isset($this->options['systemRoot'])) {
            if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
                $this->systemRoot = DIRECTORY_SEPARATOR;
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) {
                $this->systemRoot = $m[1];
            } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) {
                $this->systemRoot = $m[1];
            }
        }
        $this->root = $this->getFullPath($this->root, $cwd);
        if (!empty($this->options['startPath'])) {
            $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root);
        }

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }
        if (is_null($this->options['syncCheckFunc'])) {
            $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
        }
        // check 'statCorrector'
        if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
            $this->options['statCorrector'] = null;
        }

        return true;
    }

    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        $hiddens = array();
        $root = $this->stat($this->root);

        // check thumbnails path
        if (!empty($this->options['tmbPath'])) {
            if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['tmb'] = $this->options['tmbPath'];
                $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']);
            } else {
                $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
            }
        }
        // check temp path
        if (!empty($this->options['tmpPath'])) {
            if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
                $hiddens['temp'] = $this->options['tmpPath'];
                $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']);
            } else {
                $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
            }
        }
        // check quarantine path
        $_quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
                $_quarantine = $this->_abspath($this->options['quarantine']);
                $this->options['quarantine'] = '';
            } else {
                $this->options['quarantine'] = $this->_normpath($this->options['quarantine']);
            }
        } else {
            $_quarantine = $this->_abspath('.quarantine');
        }
        is_dir($_quarantine) && self::localRmdirRecursive($_quarantine);

        parent::configure();

        // check tmbPath
        if (!$this->tmbPath && isset($hiddens['tmb'])) {
            unset($hiddens['tmb']);
        }

        // if no thumbnails url - try detect it
        if ($root['read'] && !$this->tmbURL && $this->URL) {
            if (strpos($this->tmbPath, $this->root) === 0) {
                $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
                if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
                    $this->tmbURL .= '/';
                }
            }
        }

        // set $this->tmp by options['tmpPath']
        $this->tmp = '';
        if (!empty($this->options['tmpPath'])) {
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
                $this->tmp = $this->options['tmpPath'];
            } else {
                if (isset($hiddens['temp'])) {
                    unset($hiddens['temp']);
                }
            }
        }
        if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
            $this->tmp = $tmp;
        }

        // check quarantine dir
        $this->quarantine = '';
        if (!empty($this->options['quarantine'])) {
            if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
                $this->quarantine = $this->options['quarantine'];
            } else {
                if (isset($hiddens['quarantine'])) {
                    unset($hiddens['quarantine']);
                }
            }
        } else if ($_path = elFinder::getCommonTempPath()) {
            $this->quarantine = $_path;
        }

        if (!$this->quarantine) {
            if (!$this->tmp) {
                $this->archivers['extract'] = array();
                $this->disabled[] = 'extract';
            } else {
                $this->quarantine = $this->tmp;
            }
        }

        if ($hiddens) {
            foreach ($hiddens as $hidden) {
                $this->attributes[] = array(
                    'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~',
                    'read' => false,
                    'write' => false,
                    'locked' => true,
                    'hidden' => true
                );
            }
        }

        if (!empty($this->options['keepTimestamp'])) {
            $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']);
        }

        $this->statOwner = (!empty($this->options['statOwner']));

        // enable WinRemoveTailDots plugin on Windows server
        if (DIRECTORY_SEPARATOR !== '/') {
            if (!isset($this->options['plugin'])) {
                $this->options['plugin'] = array();
            }
            $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true);
        }
    }

    /**
     * Long pooling sync checker
     * This function require server command `inotifywait`
     * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
     *
     * @param string $path
     * @param int    $standby
     * @param number $compare
     *
     * @return number|bool
     * @throws elFinderAbortException
     */
    public function localFileSystemInotify($path, $standby, $compare)
    {
        if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
            return false;
        }
        $path = realpath($path);
        $mtime = filemtime($path);
        if (!$mtime) {
            return false;
        }
        if ($mtime != $compare) {
            return $mtime;
        }
        $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
        $standby = max(1, intval($standby));
        $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
        $this->procExec($cmd, $o, $r);
        if ($r === 0) {
            // changed
            clearstatcache();
            if (file_exists($path)) {
                $mtime = filemtime($path); // error on busy?
                return $mtime ? $mtime : time();
            } else {
                // target was removed
                return 0;
            }
        } else if ($r === 2) {
            // not changed (timeout)
            return $compare;
        }
        // error
        // cache to $_SESSION
        $this->sessionCache['localFileSystemInotify_disable'] = true;
        $this->session->set($this->id, $this->sessionCache);
        return false;
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /*********************** paths/urls *************************/

    /**
     * Return parent directory path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dirname($path)
    {
        return dirname($path);
    }

    /**
     * Return file name
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _basename($path)
    {
        return basename($path);
    }

    /**
     * Join dir name and file name and retur full path
     *
     * @param  string $dir
     * @param  string $name
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _joinPath($dir, $name)
    {
        $dir = rtrim($dir, DIRECTORY_SEPARATOR);
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        // realpath() returns FALSE if the file does not exist
        if ($path === false || strpos($path, $this->root) !== 0) {
            if (DIRECTORY_SEPARATOR !== '/') {
                $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
                $name = str_replace('/', DIRECTORY_SEPARATOR, $name);
            }
            // Directory traversal measures
            if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') {
                $dir = $this->root;
            }
            if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) {
                $name = basename($name);
            }
            $path = $dir . DIRECTORY_SEPARATOR . $name;
        }
        return $path; 
    }

    /**
     * Return normalized path, this works the same as os.path.normpath() in Python
     *
     * @param  string $path path
     *
     * @return string
     * @author Troex Nevelin
     **/
    protected function _normpath($path)
    {
        if (empty($path)) {
            return '.';
        }

        $changeSep = (DIRECTORY_SEPARATOR !== '/');
        if ($changeSep) {
            $drive = '';
            if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) {
                $drive = $m[1];
                $path = $m[2] ? $m[2] : '/';
            }
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }

        if (strpos($path, '/') === 0) {
            $initial_slashes = true;
        } else {
            $initial_slashes = false;
        }

        if (($initial_slashes)
            && (strpos($path, '//') === 0)
            && (strpos($path, '///') === false)) {
            $initial_slashes = 2;
        }

        $initial_slashes = (int)$initial_slashes;

        $comps = explode('/', $path);
        $new_comps = array();
        foreach ($comps as $comp) {
            if (in_array($comp, array('', '.'))) {
                continue;
            }

            if (($comp != '..')
                || (!$initial_slashes && !$new_comps)
                || ($new_comps && (end($new_comps) == '..'))) {
                array_push($new_comps, $comp);
            } elseif ($new_comps) {
                array_pop($new_comps);
            }
        }
        $comps = $new_comps;
        $path = implode('/', $comps);
        if ($initial_slashes) {
            $path = str_repeat('/', $initial_slashes) . $path;
        }

        if ($changeSep) {
            $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path);
        }

        return $path ? $path : '.';
    }

    /**
     * Return file path related to root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _relpath($path)
    {
        if ($path === $this->root) {
            return '';
        } else {
            if (strpos($path, $this->root) === 0) {
                return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
            } else {
                // for link
                return $path;
            }
        }
    }

    /**
     * Convert path related to root dir into real path
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _abspath($path)
    {
        if ($path === DIRECTORY_SEPARATOR) {
            return $this->root;
        } else {
            $path = $this->_normpath($path);
            if (strpos($path, $this->systemRoot) === 0) {
                return $path;
            } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) {
                return $path;
            } else {
                return $this->_joinPath($this->root, $path);
            }
        }
    }

    /**
     * Return fake path started from root dir
     *
     * @param  string $path file path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _path($path)
    {
        return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
    }

    /**
     * Return true if $path is children of $parent
     *
     * @param  string $path   path to check
     * @param  string $parent parent path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _inpath($path, $parent)
    {
        $cwd = getcwd();
        $real_path = $this->getFullPath($path, $cwd);
        $real_parent = $this->getFullPath($parent, $cwd);
        if ($real_path && $real_parent) {
            return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0;
        }
        return false;
    }



    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $stat = array();

        if (!file_exists($path) && !is_link($path)) {
            return $stat;
        }

        //Verifies the given path is the root or is inside the root. Prevents directory traveral.
        if (!$this->_inpath($path, $this->root)) {
            return $stat;
        }

        $stat['isowner'] = false;
        $linkreadable = false;
        if ($path != $this->root && is_link($path)) {
            if (!$this->options['followSymLinks']) {
                return array();
            }
            if (!($target = $this->readlink($path))
                || $target == $path) {
                if (is_null($target)) {
                    $stat = array();
                    return $stat;
                } else {
                    $stat['mime'] = 'symlink-broken';
                    $target = readlink($path);
                    $lstat = lstat($path);
                    $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                    $linkreadable = !empty($ostat['isowner']);
                }
            }
            $stat['alias'] = $this->_path($target);
            $stat['target'] = $target;
        }

        $readable = is_readable($path);

        if ($readable) {
            $size = sprintf('%u', filesize($path));
            $stat['ts'] = filemtime($path);
            if ($this->statOwner) {
                $fstat = stat($path);
                $uid = $fstat['uid'];
                $gid = $fstat['gid'];
                $stat['perm'] = substr((string)decoct($fstat['mode']), -4);
                $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
            }
        }

        if (($dir = is_dir($path)) && $this->options['detectDirIcon']) {
            $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
            if ($this->URL && file_exists($favicon)) {
                $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
            }
        }

        if (!isset($stat['mime'])) {
            $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
        }
        //logical rights first
        $stat['read'] = ($linkreadable || $readable) ? null : false;
        $stat['write'] = is_writable($path) ? null : false;

        if (is_null($stat['read'])) {
            if ($dir) {
                $stat['size'] = 0;
            } else if (isset($size)) {
                $stat['size'] = $size;
            }
        }

        if ($this->options['statCorrector']) {
            call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this));
        }

        return $stat;
    }

    /**
     * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
     * Sub-fuction of _stat() and _scandir()
     *
     * @param integer $uid
     * @param integer $gid
     *
     * @return array  stat
     */
    protected function getOwnerStat($uid, $gid)
    {
        static $names = null;
        static $phpuid = null;

        if (is_null($names)) {
            $names = array('uid' => array(), 'gid' => array());
        }
        if (is_null($phpuid)) {
            if (is_callable('posix_getuid')) {
                $phpuid = posix_getuid();
            } else {
                $phpuid = 0;
            }
        }

        $stat = array();

        if ($uid) {
            $stat['isowner'] = ($phpuid == $uid);
            if (isset($names['uid'][$uid])) {
                $stat['owner'] = $names['uid'][$uid];
            } else if (is_callable('posix_getpwuid')) {
                $pwuid = posix_getpwuid($uid);
                $stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
            } else {
                $stat['owner'] = $names['uid'][$uid] = $uid;
            }
        }
        if ($gid) {
            if (isset($names['gid'][$gid])) {
                $stat['group'] = $names['gid'][$gid];
            } else if (is_callable('posix_getgrgid')) {
                $grgid = posix_getgrgid($gid);
                $stat['group'] = $names['gid'][$gid] = $grgid['name'];
            } else {
                $stat['group'] = $names['gid'][$gid] = $gid;
            }
        }

        return $stat;
    }

    /**
     * Return true if path is dir and has at least one childs directory
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _subdirs($path)
    {

        $dirs = false;
        if (is_dir($path) && is_readable($path)) {
            if (class_exists('FilesystemIterator', false)) {
                $dirItr = new ParentIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::SKIP_DOTS |
                        FilesystemIterator::CURRENT_AS_SELF |
                        (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    )
                );
                $dirItr->rewind();
                if ($dirItr->hasChildren()) {
                    $dirs = true;
                    $name = $dirItr->getSubPathName();
                    while ($dirItr->valid()) {
                        if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
                            $dirs = false;
                            $dirItr->next();
                            $name = $dirItr->getSubPathName();
                            continue;
                        }
                        $dirs = true;
                        break;
                    }
                }
            } else {
                $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?'));
                return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
            }
        }
        return $dirs;
    }

    /**
     * Return object width and height
     * Usualy used for images, but can be realize for video etc...
     *
     * @param  string $path file path
     * @param  string $mime file mime type
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function _dimensions($path, $mime)
    {
        clearstatcache();
        return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false
            ? $s[0] . 'x' . $s[1]
            : false;
    }
    /******************** file/dir content *********************/

    /**
     * Return symlink target file
     *
     * @param  string $path link path
     *
     * @return string
     * @author Dmitry (dio) Levashov
     **/
    protected function readlink($path)
    {
        if (!($target = readlink($path))) {
            return null;
        }

        if (strpos($target, $this->systemRoot) !== 0) {
            $target = $this->_joinPath(dirname($path), $target);
        }

        if (!file_exists($target)) {
            return false;
        }

        return $target;
    }

    /**
     * Return files list in directory.
     *
     * @param  string $path dir path
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function _scandir($path)
    {
        elFinder::checkAborted();
        $files = array();
        $cache = array();
        $dirWritable = is_writable($path);
        $dirItr = array();
        $followSymLinks = $this->options['followSymLinks'];
        try {
            $dirItr = new DirectoryIterator($path);
        } catch (UnexpectedValueException $e) {
        }

        foreach ($dirItr as $file) {
            try {
                if ($file->isDot()) {
                    continue;
                }

                $files[] = $fpath = $file->getPathname();

                $br = false;
                $stat = array();

                $stat['isowner'] = false;
                $linkreadable = false;
                if ($file->isLink()) {
                    if (!$followSymLinks) {
                        continue;
                    }
                    if (!($target = $this->readlink($fpath))
                        || $target == $fpath) {
                        if (is_null($target)) {
                            $stat = array();
                            $br = true;
                        } else {
                            $_path = $fpath;
                            $stat['mime'] = 'symlink-broken';
                            $target = readlink($_path);
                            $lstat = lstat($_path);
                            $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
                            $linkreadable = !empty($ostat['isowner']);
                            $dir = false;
                            $stat['alias'] = $this->_path($target);
                            $stat['target'] = $target;
                        }
                    } else {
                        $dir = is_dir($target);
                        $stat['alias'] = $this->_path($target);
                        $stat['target'] = $target;
                        $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
                    }
                } else {
                    if (($dir = $file->isDir()) && $this->options['detectDirIcon']) {
                        $path = $file->getPathname();
                        $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon'];
                        if ($this->URL && file_exists($favicon)) {
                            $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1));
                        }
                    }
                    $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
                }
                $size = sprintf('%u', $file->getSize());
                $stat['ts'] = $file->getMTime();
                if (!$br) {
                    if ($this->statOwner && !$linkreadable) {
                        $uid = $file->getOwner();
                        $gid = $file->getGroup();
                        $stat['perm'] = substr((string)decoct($file->getPerms()), -4);
                        $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
                    }

                    //logical rights first
                    $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
                    $stat['write'] = $file->isWritable() ? null : false;
                    $stat['locked'] = $dirWritable ? null : true;

                    if (is_null($stat['read'])) {
                        $stat['size'] = $dir ? 0 : $size;
                    }

                    if ($this->options['statCorrector']) {
                        call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this));
                    }
                }

                $cache[] = array($fpath, $stat);
            } catch (RuntimeException $e) {
                continue;
            }
        }

        if ($cache) {
            $cache = $this->convEncOut($cache, false);
            foreach ($cache as $d) {
                $this->updateCache($d[0], $d[1]);
            }
        }

        return $files;
    }

    /**
     * Open file and return file pointer
     *
     * @param  string $path file path
     * @param string  $mode
     *
     * @return false|resource
     * @internal param bool $write open file for writing
     * @author   Dmitry (dio) Levashov
     */
    protected function _fopen($path, $mode = 'rb')
    {
        return fopen($path, $mode);
    }

    /**
     * Close opened file
     *
     * @param  resource $fp file pointer
     * @param string    $path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     */
    protected function _fclose($fp, $path = '')
    {
        return (is_resource($fp) && fclose($fp));
    }

    /********************  file/dir manipulations *************************/

    /**
     * Create dir and return created dir path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new directory name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkdir($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (mkdir($path)) {
            chmod($path, $this->options['dirMode']);
            return $path;
        }

        return false;
    }

    /**
     * Create file and return it's path or false on failed
     *
     * @param  string $path parent dir path
     * @param string  $name new file name
     *
     * @return string|bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _mkfile($path, $name)
    {
        $path = $this->_joinPath($path, $name);

        if (($fp = fopen($path, 'w'))) {
            fclose($fp);
            chmod($path, $this->options['fileMode']);
            return $path;
        }
        return false;
    }

    /**
     * Create symlink
     *
     * @param  string $source    file to link to
     * @param  string $targetDir folder to create link in
     * @param  string $name      symlink name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _symlink($source, $targetDir, $name)
    {
        return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name));
    }

    /**
     * Copy file into another file
     *
     * @param  string $source    source file path
     * @param  string $targetDir target directory path
     * @param  string $name      new file name
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _copy($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = copy($source, $target)) {
            isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Move file into another parent dir.
     * Return new file path or false.
     *
     * @param  string $source source file path
     * @param         $targetDir
     * @param  string $name   file name
     *
     * @return bool|string
     * @internal param string $target target dir path
     * @author   Dmitry (dio) Levashov
     */
    protected function _move($source, $targetDir, $name)
    {
        $mtime = filemtime($source);
        $target = $this->_joinPath($targetDir, $name);
        if ($ret = rename($source, $target) ? $target : false) {
            isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime);
        }
        return $ret;
    }

    /**
     * Remove file
     *
     * @param  string $path file path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _unlink($path)
    {
        return is_file($path) && unlink($path);
    }

    /**
     * Remove dir
     *
     * @param  string $path dir path
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _rmdir($path)
    {
        return rmdir($path);
    }

    /**
     * Create new file and write into it from file pointer.
     * Return new file path or false on error.
     *
     * @param  resource $fp   file pointer
     * @param  string   $dir  target dir path
     * @param  string   $name file name
     * @param  array    $stat file stat (required by some virtual fs)
     *
     * @return bool|string
     * @author Dmitry (dio) Levashov
     **/
    protected function _save($fp, $dir, $name, $stat)
    {
        $path = $this->_joinPath($dir, $name);

        $meta = stream_get_meta_data($fp);
        $uri = isset($meta['uri']) ? $meta['uri'] : '';
        if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) {
            fclose($fp);
            $mtime = filemtime($uri);
            $isCmdPaste = ($this->ARGS['cmd'] === 'paste');
            $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
            if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
                return false;
            }
            // keep timestamp on upload
            if ($mtime && $this->ARGS['cmd'] === 'upload') {
                touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
            }
        } else {
            if (file_put_contents($path, $fp, LOCK_EX) === false) {
                return false;
            }
        }

        chmod($path, $this->options['fileMode']);
        return $path;
    }

    /**
     * Get file contents
     *
     * @param  string $path file path
     *
     * @return string|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _getContents($path)
    {
        return file_get_contents($path);
    }

    /**
     * Write a string to a file
     *
     * @param  string $path    file path
     * @param  string $content new file content
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     **/
    protected function _filePutContents($path, $content)
    {
        return (file_put_contents($path, $content, LOCK_EX) !== false);
    }

    /**
     * Detect available archivers
     *
     * @return void
     * @throws elFinderAbortException
     */
    protected function _checkArchivers()
    {
        $this->archivers = $this->getArchivers();
        return;
    }

    /**
     * chmod availability
     *
     * @param string $path
     * @param string $mode
     *
     * @return bool
     */
    protected function _chmod($path, $mode)
    {
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode));
        return chmod($path, $modeOct);
    }

    /**
     * Recursive symlinks search
     *
     * @param  string $path file/dir path
     *
     * @return bool
     * @throws Exception
     * @author Dmitry (dio) Levashov
     */
    protected function _findSymlinks($path)
    {
        return self::localFindSymlinks($path);
    }

    /**
     * Extract files from archive
     *
     * @param  string $path archive path
     * @param  array  $arc  archiver command and arguments (same as in $this->archivers)
     *
     * @return array|string|boolean
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _extract($path, $arc)
    {

        if ($this->quarantine) {

            $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand());
            $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path);

            if (!mkdir($dir)) {
                return false;
            }

            // insurance unexpected shutdown
            register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));

            chmod($dir, 0777);

            // copy in quarantine
            if (!is_readable($path) || ($archive && !copy($path, $archive))) {
                return false;
            }

            // extract in quarantine
            try {
                $this->unpackArchive($path, $arc, $archive ? true : $dir);
            } catch(Exception $e) {
                return $this->setError($e->getMessage());
            }

            // get files list
            try {
                $ls = self::localScandir($dir);
            } catch (Exception $e) {
                return false;
            }

            // no files - extract error ?
            if (empty($ls)) {
                return false;
            }

            $this->archiveSize = 0;

            // find symlinks and check extracted items
            $checkRes = $this->checkExtractItems($dir);
            if ($checkRes['symlinks']) {
                self::localRmdirRecursive($dir);
                return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
            }
            $this->archiveSize = $checkRes['totalSize'];
            if ($checkRes['rmNames']) {
                foreach ($checkRes['rmNames'] as $name) {
                    $this->addError(elFinder::ERROR_SAVE, $name);
                }
            }

            // check max files size
            if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
                $this->delTree($dir);
                return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
            }

            $extractTo = $this->extractToNewdir; // 'auto', ture or false

            // archive contains one item - extract in archive dir
            $name = '';
            $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
            if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
                $name = $ls[0];
            } else if ($extractTo === 'auto' || $extractTo) {
                // for several files - create new directory
                // create unique name for directory
                $src = $dir;
                $splits = elFinder::splitFileExtention(basename($path));
                $name = $splits[0];
                $test = dirname($path) . DIRECTORY_SEPARATOR . $name;
                if (file_exists($test) || is_link($test)) {
                    $name = $this->uniqueName(dirname($path), $name, '-', false);
                }
            }

            if ($name !== '') {
                $result = dirname($path) . DIRECTORY_SEPARATOR . $name;

                if (!rename($src, $result)) {
                    $this->delTree($dir);
                    return false;
                }
            } else {
                $dstDir = dirname($path);
                $result = array();
                foreach ($ls as $name) {
                    $target = $dstDir . DIRECTORY_SEPARATOR . $name;
                    if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) {
                        $result[] = $target;
                    }
                }
                if (!$result) {
                    $this->delTree($dir);
                    return false;
                }
            }

            is_dir($dir) && $this->delTree($dir);

            return (is_array($result) || file_exists($result)) ? $result : false;
        }
        //TODO: Add return statement here
        return false;
    }

    /**
     * Create archive and return its path
     *
     * @param  string $dir   target dir
     * @param  array  $files files names list
     * @param  string $name  archive name
     * @param  array  $arc   archiver options
     *
     * @return string|bool
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov,
     * @author Alexey Sukhotin
     */
    protected function _archive($dir, $files, $name, $arc)
    {
        return $this->makeArchive($dir, $files, $name, $arc);
    }

    /******************** Over write functions *************************/

    /**
     * File path of local server side work file path
     *
     * @param  string $path
     *
     * @return string
     * @author Naoki Sawada
     */
    protected function getWorkFile($path)
    {
        return $path;
    }

    /**
     * Delete dirctory trees
     *
     * @param string $localpath path need convert encoding to server encoding
     *
     * @return boolean
     * @throws elFinderAbortException
     * @author Naoki Sawada
     */
    protected function delTree($localpath)
    {
        return $this->rmdirRecursive($localpath);
    }

    /**
     * Return fileinfo based on filename
     * For item ID based path file system
     * Please override if needed on each drivers
     *
     * @param  string $path file cache
     *
     * @return array|boolean false
     */
    protected function isNameExists($path)
    {
        $exists = file_exists($this->convEncIn($path));
        // restore locale
        $this->convEncOut();
        return $exists ? $this->stat($path) : false;
    }

    /******************** Over write (Optimized) functions *************************/

    /**
     * Recursive files search
     *
     * @param  string $path dir path
     * @param  string $q    search string
     * @param  array  $mimes
     *
     * @return array
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     * @author Naoki Sawada
     */
    protected function doSearch($path, $q, $mimes)
    {
        if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
            // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
            return parent::doSearch($path, $q, $mimes);
        }

        $result = array();

        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
        if ($timeout && $timeout < time()) {
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
            return $result;
        }
        elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);

        $match = array();
        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveCallbackFilterIterator(
                    new RecursiveDirectoryIterator($path,
                        FilesystemIterator::KEY_AS_PATHNAME |
                        FilesystemIterator::SKIP_DOTS |
                        ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ?
                            RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
                    ),
                    array($this, 'localFileSystemSearchIteratorFilter')
                ),
                RecursiveIteratorIterator::SELF_FIRST,
                RecursiveIteratorIterator::CATCH_GET_CHILD
            );
            foreach ($iterator as $key => $node) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath)));
                    break;
                }
                if ($node->isDir()) {
                    if ($this->stripos($node->getFilename(), $q) !== false) {
                        $match[] = $key;
                    }
                } else {
                    $match[] = $key;
                }
            }
        } catch (Exception $e) {
        }

        if ($match) {
            foreach ($match as $p) {
                if ($timeout && ($this->error || $timeout < time())) {
                    !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p))));
                    break;
                }

                $stat = $this->stat($p);

                if (!$stat) { // invalid links
                    continue;
                }

                if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
                    continue;
                }

                if ((!$mimes || $stat['mime'] !== 'directory')) {
                    $stat['path'] = $this->path($stat['hash']);
                    if ($this->URL && !isset($stat['url'])) {
                        $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
                        $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path));
                    }

                    $result[] = $stat;
                }
            }
        }

        return $result;
    }

    /******************** Original local functions ************************
     *
     * @param $file
     * @param $key
     * @param $iterator
     *
     * @return bool
     */

    public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
    {
        /* @var FilesystemIterator $file */
        /* @var RecursiveDirectoryIterator $iterator */
        $name = $file->getFilename();
        if ($this->doSearchCurrentQuery['excludes']) {
            foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
                if ($this->stripos($name, $exclude) !== false) {
                    return false;
                }
            }
        }
        if ($iterator->hasChildren()) {
            if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) {
                return false;
            }
            return (bool)$this->attr($key, 'read', null, true);
        }
        return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true;
    }

    /**
     * Creates a symbolic link
     *
     * @param      string   $target  The target
     * @param      string   $link    The link
     *
     * @return     boolean  ( result of symlink() )
     */
    protected function localFileSystemSymlink($target, $link)
    {
        $res = false;
        if (function_exists('symlink') and is_callable('symlink')) {
            $errlev = error_reporting();
            error_reporting($errlev ^ E_WARNING);
            if ($res = symlink(realpath($target), $link)) {
                $res = is_readable($link);
            }
            error_reporting($errlev);
        }
        return $res;
    }
} // END class PK      \ҫ      customizer/types/section.phpnu W+A        <?php
/**
 * Customizer Section type Enforcing.
 *
 * @package Neve\Customizer\Types
 */

namespace Neve\Customizer\Types;

/**
 * Class Section
 *
 * @package Neve\Customizer\Types
 */
class Section {
	/**
	 * ID of section
	 *
	 * @var string the control ID.
	 */
	public $id;

	/**
	 * Args for section instance.
	 *
	 * @var array args passed into section.
	 */
	public $args = array();

	/**
	 * Custom section ( string of class name | null)
	 *
	 * @var null|string
	 */
	public $custom_section = null;

	/**
	 * Section constructor.
	 *
	 * @param string $id             the control id.
	 * @param array  $args           the add_section array.
	 * @param string $custom_section [optional] this should be added if the section is a custom section.
	 */
	public function __construct( $id, $args, $custom_section = null ) {
		$this->id             = $id;
		$this->args           = $args;
		$this->custom_section = $custom_section;
	}
}
PK      \{d  d    customizer/types/control.phpnu W+A        <?php
/**
 * Customizer control type enforcement.
 *
 * @package Neve\Customizer\Types
 */

namespace Neve\Customizer\Types;

/**
 * Class Control
 *
 * @package Neve\Customizer\Types
 */
class Control {
	/**
	 * Control ID
	 *
	 * @var string the control ID.
	 */
	public $id;

	/**
	 * Setting arguments.
	 *
	 * @var array args passed into settings.
	 */
	public $setting_args = array();

	/**
	 * Control arguments.
	 *
	 * @var array args passed into controls.
	 */
	public $control_args = array();

	/**
	 * Custom control if applies.
	 *
	 * @var null|string
	 */
	public $custom_control = null;

	/**
	 * The Partials array
	 *
	 * @var null|array
	 */
	public $partial = null;

	/**
	 * Cnstructor.
	 *
	 * @param string $id the control id.
	 * @param array  $setting_args the add_setting array.
	 * @param array  $control_args the add_control array.
	 * @param string $custom_control [optional] this should be added if the control is a custom control.
	 * @param array  $partial [optional] this should be added if the control is a selective refresh control..
	 */
	public function __construct( $id, $setting_args, $control_args, $custom_control = null, $partial = null ) {
		$this->id             = $id;
		$this->setting_args   = $setting_args;
		$this->control_args   = $control_args;
		$this->custom_control = $custom_control;
		$this->partial        = $partial;
	}
}
PK      \7S  S    customizer/types/panel.phpnu W+A        <?php
/**
 * Customizer panel type enforcement
 *
 * @package Neve\Customizer\Types
 */

namespace Neve\Customizer\Types;

/**
 * Class Panel
 *
 * @package Neve\Customizer\Types
 */
class Panel {
	/**
	 * ID of panel
	 *
	 * @var string the control ID.
	 */
	public $id;

	/**
	 * Args for panel instance.
	 *
	 * @var array args passed into panel instance.
	 */
	public $args = array();

	/**
	 * Constructor.
	 *
	 * @param string $id   the control id.
	 * @param array  $args the panel args.
	 */
	public function __construct( $id, $args ) {
		$this->id   = $id;
		$this->args = $args;
	}
}
PK      \BC       customizer/types/partial.phpnu W+A        <?php
/**
 * Customizer partial type enforcing.
 *
 * @package Neve\Customizer\Types
 */

namespace Neve\Customizer\Types;

/**
 * Class Partial
 *
 * @package Neve\Customizer\Types
 */
class Partial {
	/**
	 * ID of control that will be attached to. Also ID of the partial itself.
	 *
	 * @var string the control ID.
	 */
	public $id;

	/**
	 * Args for the partial.
	 *
	 * @var array args passed into partial.
	 */
	public $args = array();

	/**
	 * Constructor.
	 *
	 * @param string $id the control id.
	 * @param array  $args       the partial args.
	 */
	public function __construct( $id, $args ) {
		$this->id   = $id;
		$this->args = $args;
	}
}
PK      \ɽ5  5    customizer/base_customizer.phpnu W+A        <?php
/**
 * Abstract base class for customizer module.
 *
 * Author:          Andrei Baicus <andrei@themeisle.com>
 * Created on:      17/08/2018
 *
 * @package Neve\Customizer\Abstracts
 */

namespace Neve\Customizer;

use Neve\Customizer\Types\Control;
use Neve\Customizer\Types\Panel;
use Neve\Customizer\Types\Partial;
use Neve\Customizer\Types\Section;
use HFG\Traits\Core;
use WP_Customize_Manager;

/**
 * Customizer module base.
 *
 * @package Neve\Customizer\Abstracts
 */
abstract class Base_Customizer {
	use Core;

	/**
	 * The minimum value of some customizer controls is 0 to able to allow usability relative to CSS units.
	 * That can be removed after the https://github.com/Codeinwp/neve/issues/3609 issue is handled.
	 */
	const RELATIVE_CSS_UNIT_SUPPORTED_MIN_VALUE = 0;

	/**
	 * WP_Customize object
	 *
	 * @var WP_Customize_Manager $wp_customize object
	 */
	protected $wpc;

	/**
	 * Selective refresh.
	 *
	 * @var string transport or postMessage
	 */
	protected $selective_refresh;

	/**
	 * Controls to register.
	 *
	 * @var array  Controls that will be registered. (list of \Neve\Customizer\Types\Control instances.)
	 */
	private $controls_to_register = array();

	/**
	 * Sections to register
	 *
	 * @var array  Controls that will be registered.
	 */
	private $sections_to_register = array();

	/**
	 * Panels to register
	 *
	 * @var array  Panels that will be registered.
	 */
	private $panels_to_register = array();

	/**
	 * Partials to register.
	 *
	 * @var array Partials that will be registered.
	 */
	private $partials_to_register = array();

	/**
	 * Control types to register.
	 *
	 * @var array  Control types that will be registered for use with the content_template Underscores template.
	 */
	private $types_to_register = array();

	/**
	 * Base initialization.
	 */
	public function init() {
		add_action( 'customize_register', array( $this, 'register_controls_callback' ) );
	}

	/**
	 * The function tied to customize_register.
	 *
	 * @param object $wp_customize the customizer manager.
	 */
	public function register_controls_callback( $wp_customize ) {
		$this->wpc = $wp_customize;
		$this->set_selective_refresh();
		$this->add_controls();
		$this->after_add_controls();
		$this->register_controls();
		$this->register_panels();
		$this->register_sections();
		$this->register_types();
		$this->change_controls();
		$this->register_partials();
	}

	/**
	 * Function that should be extended to add customizer controls.
	 *
	 * @return void
	 */
	abstract public function add_controls();

	/**
	 * Hook after controls are defined.
	 *
	 * @return void
	 */
	protected function after_add_controls() {
	}

	/**
	 * Change controls function.
	 *
	 * @return void
	 */
	protected function change_controls() {
	}

	/**
	 * Check selective refresh.
	 */
	private function set_selective_refresh() {
		$this->selective_refresh = isset( $this->wpc->selective_refresh ) ? 'postMessage' : 'refresh';
	}

	/**
	 * Register all the defined sections.
	 */
	private function register_panels() {
		$panels = $this->panels_to_register;
		foreach ( $panels as $panel ) {
			$this->wpc->add_panel( $panel->id, $panel->args );
		}
	}

	/**
	 * Register all the defined sections.
	 */
	private function register_sections() {
		$sections = $this->sections_to_register;
		foreach ( $sections as $section ) {
			if ( $section->custom_section !== null ) {
				$this->wpc->add_section( new $section->custom_section( $this->wpc, $section->id, $section->args ) );
			} else {
				$this->wpc->add_section( $section->id, $section->args );
			}
		}
	}

	/**
	 * Register all the defined controls.
	 */
	private function register_controls() {
		$controls = $this->controls_to_register;
		/** @var \Neve\Customizer\Types\Control $control */
		foreach ( $controls as $control ) {
			$this->wpc->add_setting( $control->id, $control->setting_args );
			$control_type = null;
			if ( $control->custom_control !== null ) {
				$this->wpc->add_control( new $control->custom_control( $this->wpc, $control->id, $control->control_args ) );
				$control_type = $control->custom_control;
			} else {
				$new_control  = $this->wpc->add_control( $control->id, $control->control_args );
				$control_type = isset( $control->control_args['type'] ) ? $control->control_args['type'] : $new_control->type;
			}
			if ( isset( $control->control_args['live_refresh_selector'] ) && $control->control_args['live_refresh_selector'] !== false ) {
				$control_args = array(
					'selector'   => $control->control_args['live_refresh_selector'],
					'id'         => $control->id,
					'type'       => $control_type,
					'additional' => isset( $control->control_args['live_refresh_css_prop'] ) ? $control->control_args['live_refresh_css_prop'] : false,
				);

				add_filter(
					'neve_customize_preview_localization',
					function ( $array ) use ( $control_args ) {
						if ( ! isset( $array[ $control_args['type'] ] ) ) {
							$array[ $control_args['type'] ] = [];
						}
						$array[ $control_args['type'] ][ $control_args['id'] ] = [
							'selector'   => $control_args['selector'],
							'additional' => $control_args['additional'],
						];
						return $array;
					}
				);
			}
			if ( isset( $control->control_args['conditional_header'] ) && $control->control_args['conditional_header'] ) {
				$id = $control->id;
				add_filter(
					'neve_pro_react_controls_localization',
					function ( $array ) use ( $id ) {
						$array['headerControls'][] = $id;

						return $array;
					}
				);
			}
			if ( isset( $control->partial ) ) {
				$this->add_partial( new Partial( $control->id, $control->partial ) );
			}
		}
	}

	/**
	 * Register control types defined to work with Underscores template.
	 */
	private function register_types() {
		$types = $this->types_to_register;
		foreach ( $types as $object => $type ) {
			call_user_func_array( array( $this->wpc, 'register_' . $type . '_type' ), array( $object ) );
		}
	}

	/**
	 * Register all the defined controls.
	 */
	private function register_partials() {
		$partials = $this->partials_to_register;
		foreach ( $partials as $partial ) {
			if ( empty( $partial ) ) {
				continue;
			}
			$this->wpc->selective_refresh->add_partial( $partial->id, $partial->args );
		}
	}

	/**
	 * Add the controls to load.
	 *
	 * @param Control $control control to add.
	 */
	public function add_control( Control $control ) {
		array_push( $this->controls_to_register, $control );
	}

	/**
	 * Add the sections to load.
	 *
	 * @param Section $section section to add.
	 */
	public function add_section( Section $section ) {
		array_push( $this->sections_to_register, $section );
	}

	/**
	 * Add the panels to load.
	 *
	 * @param Panel $panel panel to add.
	 */
	public function add_panel( Panel $panel ) {
		array_push( $this->panels_to_register, $panel );

	}

	/**
	 * Add types that will be registered for .
	 *
	 * @param string $object_name the object name that will be registered.
	 * @param string $type        the type of object to register [panel, section, control].
	 */
	public function register_type( $object_name, $type ) {
		$accepted_types = array( 'panel', 'section', 'control' );
		if ( ! in_array( $type, $accepted_types, true ) ) {
			return;
		}
		$this->types_to_register[ $object_name ] = $type;
	}

	/**
	 * Add the partials to load.
	 *
	 * @param Partial $partial partial to add.
	 */
	public function add_partial( Partial $partial ) {
		if ( empty( $partial->args ) ) {
			return;
		}
		array_push( $this->partials_to_register, $partial );
	}

	/**
	 * Get customizer object.
	 *
	 * @param string $type object type [ section, control, setting, panel ].
	 * @param string $id   the id of the customizer object.
	 *
	 * @return mixed|null
	 */
	public function get_customizer_object( $type, $id ) {
		$accepted_types = array( 'setting', 'control', 'section', 'panel' );
		if ( ! in_array( $type, $accepted_types, true ) ) {
			return null;
		}
		$object = call_user_func_array( array( $this->wpc, 'get_' . $type ), array( $id ) );
		if ( empty( $object ) ) {
			return null;
		}

		return $object;
	}

	/**
	 * Change a customizer object.
	 *
	 * @param string               $type     object type [ section, control, setting, panel ].
	 * @param string               $id       id of object.
	 * @param string               $property property to change.
	 * @param string|integer|array $value    the value.
	 */
	public function change_customizer_object( $type, $id, $property, $value ) {
		$accepted_types = array( 'setting', 'control', 'section', 'panel' );
		if ( ! in_array( $type, $accepted_types, true ) ) {
			return;
		}
		$object = call_user_func_array( array( $this->wpc, 'get_' . $type ), array( $id ) );

		if ( empty( $object ) ) {
			return;
		}

		$object->$property = $value;
	}

	/**
	 * Function to add controls that form the boxed layout.
	 *
	 * @param string $id       Controls short id.
	 * @param array  $settings Controls settings.
	 */
	public function add_boxed_layout_controls( $id, $settings ) {
		$this->add_control(
			new Control(
				'neve_' . $id . '_boxed_layout',
				[
					'sanitize_callback' => 'neve_sanitize_checkbox',
					'default'           => array_key_exists( 'is_boxed_default', $settings ) ? $settings['is_boxed_default'] : false,
				],
				[
					'label'           => esc_html__( 'Boxed layout', 'neve' ),
					'section'         => $settings['section'],
					'type'            => 'neve_toggle_control',
					'priority'        => $settings['priority'],
					'active_callback' => array_key_exists( 'toggle_active_callback', $settings ) ? $settings['toggle_active_callback'] : '__return_true',
				],
				'Neve\Customizer\Controls\Checkbox'
			)
		);

		$padding_live_refresh_settings = [
			'responsive'  => true,
			'directional' => true,
			'template'    =>
				$settings['boxed_selector'] . '{
							padding-top: {{value.top}};
							padding-right: {{value.right}};
							padding-bottom: {{value.bottom}};
							padding-left: {{value.left}};
						}',
		];

		$background_live_refresh_settings = [
			'template' =>
				$settings['boxed_selector'] . '{
				   background-color: {{value}};
			    }',

		];

		$has_text_color = isset( $settings['has_text_color'] ) ? $settings['has_text_color'] : true;
		if ( $has_text_color ) {
			$template = $settings['text_color_css_selector'] . '{ color: {{value}}; }';
			if ( array_key_exists( 'border_color_css_selector', $settings ) ) {
				$template .= $settings['border_color_css_selector'] . '{ border-color: {{value}}; }';
			}
			$color_live_refresh_settings = [
				'template' => $template,
				'cssVar'   => array(
					'vars'     => '--color',
					'selector' => $settings['boxed_selector'],
				),
			];
		}

		$padding_live_refresh_settings = [
			'cssVar' => array(
				'vars'       => '--padding',
				'selector'   => $settings['boxed_selector'],
				'responsive' => true,
			),
		];

		$background_live_refresh_settings = [
			'cssVar' => array(
				'vars'     => '--bgcolor',
				'selector' => $settings['boxed_selector'],
			),
		];

		$this->add_control(
			new Control(
				'neve_' . $id . '_boxed_padding',
				[
					'sanitize_callback' => [ $this, 'sanitize_spacing_array' ],
					'transport'         => $this->selective_refresh,
					'default'           => array_key_exists( 'padding_default', $settings ) ? $settings['padding_default'] : false,
				],
				[
					'label'                 => esc_html__( 'Section padding', 'neve' ),
					'section'               => $settings['section'],
					'input_attrs'           => [
						'units' => [ 'px', 'em', 'rem' ],
						'min'   => 0,
					],
					'default'               => array_key_exists( 'padding_default', $settings ) ? $settings['padding_default'] : false,
					'priority'              => $settings['priority'],
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => $padding_live_refresh_settings,
					'active_callback'       => array_key_exists( 'active_callback', $settings ) ? $settings['active_callback'] : false,
				],
				'\Neve\Customizer\Controls\React\Spacing'
			)
		);

		$this->add_control(
			new Control(
				'neve_' . $id . '_boxed_background_color',
				[
					'sanitize_callback' => 'neve_sanitize_colors',
					'transport'         => $this->selective_refresh,
					'default'           => array_key_exists( 'background_default', $settings ) ? $settings['background_default'] : false,
				],
				[
					'label'                 => esc_html__( 'Background color', 'neve' ),
					'section'               => $settings['section'],
					'priority'              => $settings['priority'],
					'input_attrs'           => [
						'allow_gradient' => true,
					],
					'live_refresh_selector' => true,
					'live_refresh_css_prop' => $background_live_refresh_settings,
					'active_callback'       => array_key_exists( 'active_callback', $settings ) ? $settings['active_callback'] : false,
				],
				'Neve\Customizer\Controls\React\Color'
			)
		);

		if ( $has_text_color ) {
			$this->add_control(
				new Control(
					'neve_' . $id . '_boxed_text_color',
					[
						'sanitize_callback' => 'neve_sanitize_colors',
						'transport'         => $this->selective_refresh,
						'default'           => array_key_exists( 'color_default', $settings ) ? $settings['color_default'] : false,
					],
					[
						'label'                 => esc_html__( 'Text color', 'neve' ),
						'section'               => $settings['section'],
						'priority'              => $settings['priority'],
						'live_refresh_selector' => true,
						'live_refresh_css_prop' => $color_live_refresh_settings,
						'active_callback'       => array_key_exists( 'active_callback', $settings ) ? $settings['active_callback'] : false,
					],
					'Neve\Customizer\Controls\React\Color'
				)
			);
		}
	}
}
PK      `\f	  	    block-styles.phpnu W+A        <?php
/**
 * Block Styles
 *
 * @link https://developer.wordpress.org/reference/functions/register_block_style/
 *
 * @package WordPress
 * @subpackage Twenty_Twenty_One
 * @since Twenty Twenty-One 1.0
 */

if ( function_exists( 'register_block_style' ) ) {
	/**
	 * Register block styles.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @return void
	 */
	function twenty_twenty_one_register_block_styles() {
		// Columns: Overlap.
		register_block_style(
			'core/columns',
			array(
				'name'  => 'twentytwentyone-columns-overlap',
				'label' => esc_html__( 'Overlap', 'twentytwentyone' ),
			)
		);

		// Cover: Borders.
		register_block_style(
			'core/cover',
			array(
				'name'  => 'twentytwentyone-border',
				'label' => esc_html__( 'Borders', 'twentytwentyone' ),
			)
		);

		// Group: Borders.
		register_block_style(
			'core/group',
			array(
				'name'  => 'twentytwentyone-border',
				'label' => esc_html__( 'Borders', 'twentytwentyone' ),
			)
		);

		// Image: Borders.
		register_block_style(
			'core/image',
			array(
				'name'  => 'twentytwentyone-border',
				'label' => esc_html__( 'Borders', 'twentytwentyone' ),
			)
		);

		// Image: Frame.
		register_block_style(
			'core/image',
			array(
				'name'  => 'twentytwentyone-image-frame',
				'label' => esc_html__( 'Frame', 'twentytwentyone' ),
			)
		);

		// Latest Posts: Dividers.
		register_block_style(
			'core/latest-posts',
			array(
				'name'  => 'twentytwentyone-latest-posts-dividers',
				'label' => esc_html__( 'Dividers', 'twentytwentyone' ),
			)
		);

		// Latest Posts: Borders.
		register_block_style(
			'core/latest-posts',
			array(
				'name'  => 'twentytwentyone-latest-posts-borders',
				'label' => esc_html__( 'Borders', 'twentytwentyone' ),
			)
		);

		// Media & Text: Borders.
		register_block_style(
			'core/media-text',
			array(
				'name'  => 'twentytwentyone-border',
				'label' => esc_html__( 'Borders', 'twentytwentyone' ),
			)
		);

		// Separator: Thick.
		register_block_style(
			'core/separator',
			array(
				'name'  => 'twentytwentyone-separator-thick',
				'label' => esc_html__( 'Thick', 'twentytwentyone' ),
			)
		);

		// Social icons: Dark gray color.
		register_block_style(
			'core/social-links',
			array(
				'name'  => 'twentytwentyone-social-icons-color',
				'label' => esc_html__( 'Dark gray', 'twentytwentyone' ),
			)
		);
	}
	add_action( 'init', 'twenty_twenty_one_register_block_styles' );
}
PK      `\j"  "    starter-content.phpnu W+A        <?php
/**
 * Twenty Twenty-One Starter Content
 *
 * @link https://make.wordpress.org/core/2016/11/30/starter-content-for-themes-in-4-7/
 *
 * @package WordPress
 * @subpackage Twenty_Twenty_One
 * @since Twenty Twenty-One 1.0
 */

/**
 * Function to return the array of starter content for the theme.
 *
 * Passes it through the `twenty_twenty_one_starter_content` filter before returning.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @return array A filtered array of args for the starter_content.
 */
function twenty_twenty_one_get_starter_content() {

	// Define and register starter content to showcase the theme on new sites.
	$starter_content = array(

		// Specify the core-defined pages to create and add custom thumbnails to some of them.
		'posts'     => array(
			'front' => array(
				'post_type'    => 'page',
				'post_title'   => esc_html_x( 'Create your website with blocks', 'Theme starter content', 'twentytwentyone' ),
				'post_content' => '
					<!-- wp:heading {"align":"wide","fontSize":"gigantic","style":{"typography":{"lineHeight":"1.1"}}} -->
					<h2 class="alignwide has-text-align-wide has-gigantic-font-size" style="line-height:1.1">' . esc_html_x( 'Create your website with blocks', 'Theme starter content', 'twentytwentyone' ) . '</h2>
					<!-- /wp:heading -->

					<!-- wp:spacer -->
					<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>
					<!-- /wp:spacer -->

					<!-- wp:columns {"verticalAlignment":"center","align":"wide","className":"is-style-twentytwentyone-columns-overlap"} -->
					<div class="wp-block-columns alignwide are-vertically-aligned-center is-style-twentytwentyone-columns-overlap"><!-- wp:column {"verticalAlignment":"center"} -->
					<div class="wp-block-column is-vertically-aligned-center"><!-- wp:image {"align":"full","sizeSlug":"large"} -->
					<figure class="wp-block-image alignfull size-large"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/roses-tremieres-hollyhocks-1884.jpg" alt="' . esc_attr__( '&#8220;Roses Trémières&#8221; by Berthe Morisot', 'twentytwentyone' ) . '"/></figure>
					<!-- /wp:image -->

					<!-- wp:spacer -->
					<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>
					<!-- /wp:spacer -->

					<!-- wp:image {"align":"full","sizeSlug":"large","className":"is-style-twentytwentyone-image-frame"} -->
					<figure class="wp-block-image alignfull size-large is-style-twentytwentyone-image-frame"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/in-the-bois-de-boulogne.jpg" alt="' . esc_attr__( '&#8220;In the Bois de Boulogne&#8221; by Berthe Morisot', 'twentytwentyone' ) . '"/></figure>
					<!-- /wp:image --></div>
					<!-- /wp:column -->

					<!-- wp:column {"verticalAlignment":"center"} -->
					<div class="wp-block-column is-vertically-aligned-center"><!-- wp:spacer -->
					<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>
					<!-- /wp:spacer -->

					<!-- wp:image {"sizeSlug":"large","className":"alignfull size-full is-style-twentytwentyone-border"} -->
					<figure class="wp-block-image size-large alignfull size-full is-style-twentytwentyone-border"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/young-woman-in-mauve.jpg" alt="' . esc_attr__( '&#8220;Young Woman in Mauve&#8221; by Berthe Morisot', 'twentytwentyone' ) . '"/></figure>
					<!-- /wp:image --></div>
					<!-- /wp:column --></div>
					<!-- /wp:columns -->

					<!-- wp:spacer {"height":50} -->
					<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>
					<!-- /wp:spacer -->

					<!-- wp:columns {"verticalAlignment":"top","align":"wide"} -->
					<div class="wp-block-columns alignwide are-vertically-aligned-top"><!-- wp:column {"verticalAlignment":"top"} -->
					<div class="wp-block-column is-vertically-aligned-top"><!-- wp:heading {"level":3} -->
					<h3>' . esc_html_x( 'Add block patterns', 'Theme starter content', 'twentytwentyone' ) . '</h3>
					<!-- /wp:heading -->

					<!-- wp:paragraph -->
					<p>' . esc_html_x( 'Block patterns are pre-designed groups of blocks. To add one, select the Add Block button [+] in the toolbar at the top of the editor. Switch to the Patterns tab underneath the search bar, and choose a pattern.', 'Theme starter content', 'twentytwentyone' ) . '</p>
					<!-- /wp:paragraph --></div>
					<!-- /wp:column -->

					<!-- wp:column {"verticalAlignment":"top"} -->
					<div class="wp-block-column is-vertically-aligned-top"><!-- wp:heading {"level":3} -->
					<h3>' . esc_html_x( 'Frame your images', 'Theme starter content', 'twentytwentyone' ) . '</h3>
					<!-- /wp:heading -->

					<!-- wp:paragraph -->
					<p>' . esc_html_x( 'Twenty Twenty-One includes stylish borders for your content. With an Image block selected, open the "Styles" panel within the Editor sidebar. Select the "Frame" block style to activate it.', 'Theme starter content', 'twentytwentyone' ) . '</p>
					<!-- /wp:paragraph --></div>
					<!-- /wp:column -->

					<!-- wp:column {"verticalAlignment":"top"} -->
					<div class="wp-block-column is-vertically-aligned-top"><!-- wp:heading {"level":3} -->
					<h3>' . esc_html_x( 'Overlap columns', 'Theme starter content', 'twentytwentyone' ) . '</h3>
					<!-- /wp:heading -->

					<!-- wp:paragraph -->
					<p>' . esc_html_x( 'Twenty Twenty-One also includes an overlap style for column blocks. With a Columns block selected, open the "Styles" panel within the Editor sidebar. Choose the "Overlap" block style to try it out.', 'Theme starter content', 'twentytwentyone' ) . '</p>
					<!-- /wp:paragraph --></div>
					<!-- /wp:column --></div>
					<!-- /wp:columns -->

					<!-- wp:spacer -->
					<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>
					<!-- /wp:spacer -->

					<!-- wp:cover {"overlayColor":"green","contentPosition":"center center","align":"wide","className":"is-style-twentytwentyone-border"} -->
					<div class="wp-block-cover alignwide has-green-background-color has-background-dim is-style-twentytwentyone-border"><div class="wp-block-cover__inner-container"><!-- wp:spacer {"height":20} -->
					<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
					<!-- /wp:spacer -->

					<!-- wp:paragraph {"fontSize":"huge"} -->
					<p class="has-huge-font-size">' . esc_html_x( 'Need help?', 'Theme starter content', 'twentytwentyone' ) . '</p>
					<!-- /wp:paragraph -->

					<!-- wp:spacer {"height":75} -->
					<div style="height:75px" aria-hidden="true" class="wp-block-spacer"></div>
					<!-- /wp:spacer -->

					<!-- wp:columns -->
					<div class="wp-block-columns"><!-- wp:column -->
					<div class="wp-block-column"><!-- wp:paragraph -->
					<p><a href="https://wordpress.org/documentation/article/twenty-twenty-one/">' . esc_html_x( 'Read the Theme Documentation', 'Theme starter content', 'twentytwentyone' ) . '</a></p>
					<!-- /wp:paragraph --></div>
					<!-- /wp:column -->

					<!-- wp:column -->
					<div class="wp-block-column"><!-- wp:paragraph -->
					<p><a href="https://wordpress.org/support/theme/twentytwentyone/">' . esc_html_x( 'Check out the Support Forums', 'Theme starter content', 'twentytwentyone' ) . '</a></p>
					<!-- /wp:paragraph --></div>
					<!-- /wp:column --></div>
					<!-- /wp:columns -->

					<!-- wp:spacer {"height":20} -->
					<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>
					<!-- /wp:spacer --></div></div>
					<!-- /wp:cover -->',
			),
			'about',
			'contact',
			'blog',
		),

		// Default to a static front page and assign the front and posts pages.
		'options'   => array(
			'show_on_front'  => 'page',
			'page_on_front'  => '{{front}}',
			'page_for_posts' => '{{blog}}',
		),

		// Set up nav menus for each of the two areas registered in the theme.
		'nav_menus' => array(
			// Assign a menu to the "primary" location.
			'primary' => array(
				'name'  => esc_html__( 'Primary menu', 'twentytwentyone' ),
				'items' => array(
					'link_home', // Note that the core "home" page is actually a link in case a static front page is not used.
					'page_about',
					'page_blog',
					'page_contact',
				),
			),

			// Assign a menu to the "footer" location.
			'footer'  => array(
				'name'  => esc_html__( 'Secondary menu', 'twentytwentyone' ),
				'items' => array(
					'link_facebook',
					'link_twitter',
					'link_instagram',
					'link_email',
				),
			),
		),
	);

	/**
	 * Filters the array of starter content.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @param array $starter_content Array of starter content.
	 */
	return apply_filters( 'twenty_twenty_one_starter_content', $starter_content );
}
PK      `\~{)P  )P    block-patterns.phpnu W+A        <?php
/**
 * Block Patterns
 *
 * @link https://developer.wordpress.org/reference/functions/register_block_pattern/
 * @link https://developer.wordpress.org/reference/functions/register_block_pattern_category/
 *
 * @package WordPress
 * @subpackage Twenty_Twenty_One
 * @since Twenty Twenty-One 1.0
 */

if ( function_exists( 'register_block_pattern_category' ) ) {
	/**
	 * Register Block Pattern Category.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @return void
	 */
	function twenty_twenty_one_register_block_pattern_category() {
		register_block_pattern_category(
			'twentytwentyone',
			array( 'label' => esc_html__( 'Twenty Twenty-One', 'twentytwentyone' ) )
		);
	}
	add_action( 'init', 'twenty_twenty_one_register_block_pattern_category' );
}

/**
 * Register Block Patterns.
 */
if ( function_exists( 'register_block_pattern' ) ) {
	/**
	 * Register Block Pattern.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @return void
	 */
	function twenty_twenty_one_register_block_pattern() {

		// Large Text.
		register_block_pattern(
			'twentytwentyone/large-text',
			array(
				'title'         => esc_html__( 'Large text', 'twentytwentyone' ),
				'categories'    => array( 'twentytwentyone' ),
				'viewportWidth' => 1440,
				'blockTypes'    => array( 'core/heading' ),
				'content'       => '<!-- wp:heading {"align":"wide","fontSize":"gigantic","style":{"typography":{"lineHeight":"1.1"}}} --><h2 class="alignwide has-text-align-wide has-gigantic-font-size" style="line-height:1.1">' . esc_html__( 'A new portfolio default theme for WordPress', 'twentytwentyone' ) . '</h2><!-- /wp:heading -->',
			)
		);

		// Links Area.
		register_block_pattern(
			'twentytwentyone/links-area',
			array(
				'title'         => esc_html__( 'Links area', 'twentytwentyone' ),
				'categories'    => array( 'twentytwentyone' ),
				'viewportWidth' => 1440,
				'blockTypes'    => array( 'core/cover' ),
				'description'   => esc_html_x( 'A huge text followed by social networks and email address links.', 'Block pattern description', 'twentytwentyone' ),
				'content'       => '<!-- wp:cover {"overlayColor":"green","contentPosition":"center center","align":"wide","className":"is-style-twentytwentyone-border"} --><div class="wp-block-cover alignwide has-green-background-color has-background-dim is-style-twentytwentyone-border"><div class="wp-block-cover__inner-container"><!-- wp:spacer {"height":20} --><div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div><!-- /wp:spacer --><!-- wp:paragraph {"fontSize":"huge"} --><p class="has-huge-font-size">' . wp_kses_post( __( 'Let&#8217;s Connect.', 'twentytwentyone' ) ) . '</p><!-- /wp:paragraph --><!-- wp:spacer {"height":75} --><div style="height:75px" aria-hidden="true" class="wp-block-spacer"></div><!-- /wp:spacer --><!-- wp:columns --><div class="wp-block-columns"><!-- wp:column --><div class="wp-block-column"><!-- wp:paragraph --><p><a href="#" data-type="URL">' . esc_html__( 'Twitter', 'twentytwentyone' ) . '</a> / <a href="#" data-type="URL">' . esc_html__( 'Instagram', 'twentytwentyone' ) . '</a> / <a href="#" data-type="URL">' . esc_html__( 'Dribbble', 'twentytwentyone' ) . '</a></p><!-- /wp:paragraph --></div><!-- /wp:column --><!-- wp:column --><div class="wp-block-column"><!-- wp:paragraph --><p><a href="#">' . esc_html__( 'example@example.com', 'twentytwentyone' ) . '</a></p><!-- /wp:paragraph --></div><!-- /wp:column --></div><!-- /wp:columns --><!-- wp:spacer {"height":20} --><div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div><!-- /wp:spacer --></div></div><!-- /wp:cover --><!-- wp:paragraph --><p></p><!-- /wp:paragraph -->',
			)
		);

		// Media & Text Article Title.
		register_block_pattern(
			'twentytwentyone/media-text-article-title',
			array(
				'title'         => esc_html__( 'Media and text article title', 'twentytwentyone' ),
				'categories'    => array( 'twentytwentyone' ),
				'viewportWidth' => 1440,
				'description'   => esc_html_x( 'A Media & Text block with a big image on the left and a heading on the right. The heading is followed by a separator and a description paragraph.', 'Block pattern description', 'twentytwentyone' ),
				'content'       => '<!-- wp:media-text {"mediaId":1752,"mediaLink":"' . esc_url( get_template_directory_uri() ) . '/assets/images/playing-in-the-sand.jpg","mediaType":"image","className":"is-style-twentytwentyone-border"} --><div class="wp-block-media-text alignwide is-stacked-on-mobile is-style-twentytwentyone-border"><figure class="wp-block-media-text__media"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/playing-in-the-sand.jpg" alt="' . esc_attr__( '&#8220;Playing in the Sand&#8221; by Berthe Morisot', 'twentytwentyone' ) . '" class="wp-image-1752"/></figure><div class="wp-block-media-text__content"><!-- wp:heading {"align":"center"} --><h2 class="has-text-align-center">' . esc_html__( 'Playing in the Sand', 'twentytwentyone' ) . '</h2><!-- /wp:heading --><!-- wp:separator {"className":"is-style-dots"} --><hr class="wp-block-separator is-style-dots"/><!-- /wp:separator --><!-- wp:paragraph {"align":"center","fontSize":"small"} --><p class="has-text-align-center has-small-font-size">' . wp_kses_post( __( 'Berthe Morisot<br>(French, 1841-1895)', 'twentytwentyone' ) ) . '</p><!-- /wp:paragraph --></div></div><!-- /wp:media-text -->',
			)
		);

		// Overlapping Images.
		register_block_pattern(
			'twentytwentyone/overlapping-images',
			array(
				'title'         => esc_html__( 'Overlapping images', 'twentytwentyone' ),
				'categories'    => array( 'twentytwentyone' ),
				'viewportWidth' => 1024,
				'blockTypes'    => array( 'core/columns' ),
				'description'   => esc_html_x( 'Three images inside an overlapping columns block.', 'Block pattern description', 'twentytwentyone' ),
				'content'       => '<!-- wp:columns {"verticalAlignment":"center","align":"wide","className":"is-style-twentytwentyone-columns-overlap"} --><div class="wp-block-columns alignwide are-vertically-aligned-center is-style-twentytwentyone-columns-overlap"><!-- wp:column {"verticalAlignment":"center"} --><div class="wp-block-column is-vertically-aligned-center"><!-- wp:image {"align":"full","sizeSlug":"full"} --><figure class="wp-block-image alignfull size-full"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/roses-tremieres-hollyhocks-1884.jpg" alt="' . esc_attr__( '&#8220;Roses Trémières&#8221; by Berthe Morisot', 'twentytwentyone' ) . '"/></figure><!-- /wp:image --><!-- wp:spacer --><div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div><!-- /wp:spacer --><!-- wp:image {"align":"full","sizeSlug":"full"} --><figure class="wp-block-image alignfull size-full"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/in-the-bois-de-boulogne.jpg" alt="' . esc_attr__( '&#8220;In the Bois de Boulogne&#8221; by Berthe Morisot', 'twentytwentyone' ) . '"/></figure><!-- /wp:image --></div><!-- /wp:column --><!-- wp:column {"verticalAlignment":"center"} --><div class="wp-block-column is-vertically-aligned-center"><!-- wp:spacer --><div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div><!-- /wp:spacer --><!-- wp:image {"align":"full",sizeSlug":"full"} --><figure class="wp-block-image alignfull size-full"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/young-woman-in-mauve.jpg" alt="' . esc_attr__( '&#8220;Young Woman in Mauve&#8221; by Berthe Morisot', 'twentytwentyone' ) . '"/></figure><!-- /wp:image --></div><!-- /wp:column --></div><!-- /wp:columns -->',
			)
		);

		// Two Images Showcase.
		register_block_pattern(
			'twentytwentyone/two-images-showcase',
			array(
				'title'         => esc_html__( 'Two images showcase', 'twentytwentyone' ),
				'categories'    => array( 'twentytwentyone' ),
				'viewportWidth' => 1440,
				'description'   => esc_html_x( 'A media & text block with a big image on the left and a smaller one with bordered frame on the right.', 'Block pattern description', 'twentytwentyone' ),
				'content'       => '<!-- wp:media-text {"mediaId":1747,"mediaLink":"' . esc_url( get_template_directory_uri() ) . '/assets/images/Daffodils.jpg","mediaType":"image"} --><div class="wp-block-media-text alignwide is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/Daffodils.jpg" alt="' . esc_attr__( '&#8220;Daffodils&#8221; by Berthe Morisot', 'twentytwentyone' ) . '" size-full"/></figure><div class="wp-block-media-text__content"><!-- wp:image {"align":"center","width":400,"height":512,"sizeSlug":"large","className":"is-style-twentytwentyone-image-frame"} --><figure class="wp-block-image aligncenter size-large is-resized is-style-twentytwentyone-image-frame"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/self-portrait-1885.jpg" alt="' . esc_attr__( '&#8220;Self portrait&#8221; by Berthe Morisot', 'twentytwentyone' ) . '" width="400" height="512"/></figure><!-- /wp:image --></div></div><!-- /wp:media-text -->',
			)
		);

		// Overlapping Images and Text.
		register_block_pattern(
			'twentytwentyone/overlapping-images-and-text',
			array(
				'title'         => esc_html__( 'Overlapping images and text', 'twentytwentyone' ),
				'categories'    => array( 'twentytwentyone' ),
				'viewportWidth' => 1440,
				'blockTypes'    => array( 'core/columns' ),
				'description'   => esc_html_x( 'An overlapping columns block with two images and a text description.', 'Block pattern description', 'twentytwentyone' ),
				'content'       => '<!-- wp:columns {"verticalAlignment":null,"align":"wide","className":"is-style-twentytwentyone-columns-overlap"} --> <div class="wp-block-columns alignwide is-style-twentytwentyone-columns-overlap"><!-- wp:column --> <div class="wp-block-column"><!-- wp:image {sizeSlug":"full"} --> <figure class="wp-block-image size-full"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/the-garden-at-bougival-1884.jpg" alt="' . esc_attr__( '&#8220;The Garden at Bougival&#8221; by Berthe Morisot', 'twentytwentyone' ) . '"/></figure> <!-- /wp:image --></div> <!-- /wp:column --> <!-- wp:column {"verticalAlignment":"bottom"} --> <div class="wp-block-column is-vertically-aligned-bottom"><!-- wp:group {"className":"is-style-twentytwentyone-border","backgroundColor":"green"} --> <div class="wp-block-group is-style-twentytwentyone-border has-green-background-color has-background"><div class="wp-block-group__inner-container"><!-- wp:paragraph {"fontSize":"extra-large","style":{"typography":{"lineHeight":"1.4"}}} --> <p class="has-extra-large-font-size" style="line-height:1.4">' . esc_html__( 'Beautiful gardens painted by Berthe Morisot in the late 1800s', 'twentytwentyone' ) . '</p> <!-- /wp:paragraph --></div></div> <!-- /wp:group --></div> <!-- /wp:column --> <!-- wp:column --> <div class="wp-block-column"><!-- wp:image {sizeSlug":"full"} --> <figure class="wp-block-image size-full"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/villa-with-orange-trees-nice.jpg" alt="' . esc_attr__( '&#8220;Villa with Orange Trees, Nice&#8221; by Berthe Morisot', 'twentytwentyone' ) . '"/></figure> <!-- /wp:image --></div> <!-- /wp:column --></div> <!-- /wp:columns -->',
			)
		);

		// Portfolio List.
		register_block_pattern(
			'twentytwentyone/portfolio-list',
			array(
				'title'       => esc_html__( 'Portfolio list', 'twentytwentyone' ),
				'categories'  => array( 'twentytwentyone' ),
				'description' => esc_html_x( 'A list of projects with thumbnail images.', 'Block pattern description', 'twentytwentyone' ),
				'content'     => '<!-- wp:separator {"className":"is-style-twentytwentyone-separator-thick"} --> <hr class="wp-block-separator is-style-twentytwentyone-separator-thick"/> <!-- /wp:separator --> <!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column {"verticalAlignment":"center","width":80} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:80%"><!-- wp:paragraph {"fontSize":"extra-large"} --> <p class="has-extra-large-font-size"><a href="#">' . esc_html__( 'Roses Trémières', 'twentytwentyone' ) . '</a></p> <!-- /wp:paragraph --></div> <!-- /wp:column --> <!-- wp:column {"verticalAlignment":"center"} --> <div class="wp-block-column is-vertically-aligned-center"><!-- wp:image {"align":"right","width":85,"height":67,"sizeSlug":"large"} --> <figure class="wp-block-image alignright size-large is-resized"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/roses-tremieres-hollyhocks-1884.jpg" alt="' . esc_attr__( '&#8220;Roses Trémières&#8221; by Berthe Morisot', 'twentytwentyone' ) . '" width="85" height="67"/></figure> <!-- /wp:image --></div> <!-- /wp:column --></div> <!-- /wp:columns --> <!-- wp:separator {"className":"is-style-default"} --> <hr class="wp-block-separator is-style-default"/> <!-- /wp:separator --> <!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column {"verticalAlignment":"center","width":80} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:80%"><!-- wp:paragraph {"fontSize":"extra-large"} --> <p class="has-extra-large-font-size"><a href="#">' . esc_html__( 'Villa with Orange Trees, Nice', 'twentytwentyone' ) . '</a></p> <!-- /wp:paragraph --></div> <!-- /wp:column --> <!-- wp:column {"verticalAlignment":"center"} --> <div class="wp-block-column is-vertically-aligned-center"><!-- wp:image {"align":"right","width":53,"height":67,"className":"alignright size-large is-resized"} --><figure class="wp-block-image is-resized alignright size-large"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/villa-with-orange-trees-nice.jpg" alt="&#8220;Villa with Orange Trees, Nice&#8221; by Berthe Morisot" width="53" height="67"/></figure><!-- /wp:image --></div> <!-- /wp:column --></div> <!-- /wp:columns --> <!-- wp:separator {"className":"is-style-default"} --> <hr class="wp-block-separator is-style-default"/> <!-- /wp:separator --> <!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column {"verticalAlignment":"center","width":80} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:80%"><!-- wp:paragraph {"fontSize":"extra-large"} --> <p class="has-extra-large-font-size"><a href="#">' . esc_html__( 'In the Bois de Boulogne', 'twentytwentyone' ) . '</a></p> <!-- /wp:paragraph --></div> <!-- /wp:column --> <!-- wp:column {"verticalAlignment":"center"} --> <div class="wp-block-column is-vertically-aligned-center"><!-- wp:image {"align":"right","width":81,"height":67,"sizeSlug":"large"} --> <figure class="wp-block-image alignright size-large is-resized"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/in-the-bois-de-boulogne.jpg" alt="' . esc_attr__( '&#8220;In the Bois de Boulogne&#8221; by Berthe Morisot', 'twentytwentyone' ) . '" width="81" height="67"/></figure> <!-- /wp:image --></div> <!-- /wp:column --></div> <!-- /wp:columns --> <!-- wp:separator {"className":"is-style-default"} --> <hr class="wp-block-separator is-style-default"/> <!-- /wp:separator --> <!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column {"verticalAlignment":"center","width":80} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:80%"><!-- wp:paragraph {"fontSize":"extra-large"} --> <p class="has-extra-large-font-size"><a href="#">' . esc_html__( 'The Garden at Bougival', 'twentytwentyone' ) . '</a></p> <!-- /wp:paragraph --></div> <!-- /wp:column --> <!-- wp:column {"verticalAlignment":"center"} --> <div class="wp-block-column is-vertically-aligned-center"><!-- wp:image {"align":"right","width":85,"height":67,"sizeSlug":"large"} --> <figure class="wp-block-image alignright size-large is-resized"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/the-garden-at-bougival-1884.jpg" alt="' . esc_attr__( '&#8220;The Garden at Bougival&#8221; by Berthe Morisot', 'twentytwentyone' ) . '" width="85" height="67"/></figure> <!-- /wp:image --></div> <!-- /wp:column --></div> <!-- /wp:columns --> <!-- wp:separator {"className":"is-style-default"} --> <hr class="wp-block-separator is-style-default"/> <!-- /wp:separator --> <!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column {"verticalAlignment":"center","width":80} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:80%"><!-- wp:paragraph {"fontSize":"extra-large"} --> <p class="has-extra-large-font-size"><a href="#">' . esc_html__( 'Young Woman in Mauve', 'twentytwentyone' ) . '</a></p> <!-- /wp:paragraph --></div> <!-- /wp:column --> <!-- wp:column {"verticalAlignment":"center"} --> <div class="wp-block-column is-vertically-aligned-center"><!-- wp:image {"align":"right","width":54,"height":67,"sizeSlug":"large"} --> <figure class="wp-block-image alignright size-large is-resized"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/young-woman-in-mauve.jpg" alt="' . esc_attr__( '&#8220;Young Woman in Mauve&#8221; by Berthe Morisot', 'twentytwentyone' ) . '" width="54" height="67"/></figure> <!-- /wp:image --></div> <!-- /wp:column --></div> <!-- /wp:columns --> <!-- wp:separator {"className":"is-style-default"} --> <hr class="wp-block-separator is-style-default"/> <!-- /wp:separator --> <!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column {"verticalAlignment":"center","width":80} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:80%"><!-- wp:paragraph {"fontSize":"extra-large"} --> <p class="has-extra-large-font-size"><a href="#">' . esc_html__( 'Reading', 'twentytwentyone' ) . '</a></p> <!-- /wp:paragraph --></div> <!-- /wp:column --> <!-- wp:column {"verticalAlignment":"center"} --> <div class="wp-block-column is-vertically-aligned-center"><!-- wp:image {"align":"right","width":84,"height":67,"sizeSlug":"large"} --> <figure class="wp-block-image alignright size-large is-resized"><img src="' . esc_url( get_template_directory_uri() ) . '/assets/images/Reading.jpg" alt="' . esc_attr__( '&#8220;Reading&#8221; by Berthe Morisot', 'twentytwentyone' ) . '" width="84" height="67"/></figure> <!-- /wp:image --></div> <!-- /wp:column --></div> <!-- /wp:columns --> <!-- wp:separator {"className":"is-style-twentytwentyone-separator-thick"} --> <hr class="wp-block-separator is-style-twentytwentyone-separator-thick"/> <!-- /wp:separator -->',
			)
		);

		register_block_pattern(
			'twentytwentyone/contact-information',
			array(
				'title'       => esc_html__( 'Contact information', 'twentytwentyone' ),
				'categories'  => array( 'twentytwentyone' ),
				'blockTypes'  => array( 'core/columns' ),
				'description' => esc_html_x( 'A block with 3 columns that display contact information and social media links.', 'Block pattern description', 'twentytwentyone' ),
				'content'     => '<!-- wp:columns {"align":"wide"} --><div class="wp-block-columns alignwide"><!-- wp:column --><div class="wp-block-column"><!-- wp:paragraph --><p><a href="mailto:#">' . esc_html_x( 'example@example.com', 'Block pattern sample content', 'twentytwentyone' ) . '<br></a>' . esc_html_x( '123-456-7890', 'Block pattern sample content', 'twentytwentyone' ) . '</p><!-- /wp:paragraph --></div><!-- /wp:column --><!-- wp:column --><div class="wp-block-column"><!-- wp:paragraph {"align":"center"} --><p class="has-text-align-center">' . esc_html_x( '123 Main Street', 'Block pattern sample content', 'twentytwentyone' ) . '<br>' . esc_html_x( 'Cambridge, MA, 02139', 'Block pattern sample content', 'twentytwentyone' ) . '</p><!-- /wp:paragraph --></div><!-- /wp:column --><!-- wp:column {"verticalAlignment":"center"} --><div class="wp-block-column is-vertically-aligned-center"><!-- wp:social-links {"align":"right","className":"is-style-twentytwentyone-social-icons-color"} --><ul class="wp-block-social-links alignright is-style-twentytwentyone-social-icons-color"><!-- wp:social-link {"url":"https://wordpress.org","service":"wordpress"} /--><!-- wp:social-link {"url":"https://www.facebook.com/WordPress/","service":"facebook"} /--><!-- wp:social-link {"url":"https://twitter.com/WordPress","service":"twitter"} /--><!-- wp:social-link {"url":"https://www.youtube.com/wordpress","service":"youtube"} /--></ul><!-- /wp:social-links --></div><!-- /wp:column --></div><!-- /wp:columns -->',
			)
		);
	}
	add_action( 'init', 'twenty_twenty_one_register_block_pattern' );
}
PK      `\>!	  	    back-compat.phpnu W+A        <?php
/**
 * Back compat functionality
 *
 * Prevents the theme from running on WordPress versions prior to 5.3,
 * since this theme is not meant to be backward compatible beyond that and
 * relies on many newer functions and markup changes introduced in 5.3.
 *
 * @package WordPress
 * @subpackage Twenty_Twenty_One
 * @since Twenty Twenty-One 1.0
 */

/**
 * Display upgrade notice on theme switch.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @return void
 */
function twenty_twenty_one_switch_theme() {
	add_action( 'admin_notices', 'twenty_twenty_one_upgrade_notice' );
}
add_action( 'after_switch_theme', 'twenty_twenty_one_switch_theme' );

/**
 * Adds a message for unsuccessful theme switch.
 *
 * Prints an update nag after an unsuccessful attempt to switch to
 * the theme on WordPress versions prior to 5.3.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @global string $wp_version WordPress version.
 *
 * @return void
 */
function twenty_twenty_one_upgrade_notice() {
	echo '<div class="error"><p>';
	printf(
		/* translators: %s: WordPress Version. */
		esc_html__( 'This theme requires WordPress 5.3 or newer. You are running version %s. Please upgrade.', 'twentytwentyone' ),
		esc_html( $GLOBALS['wp_version'] )
	);
	echo '</p></div>';
}

/**
 * Prevents the Customizer from being loaded on WordPress versions prior to 5.3.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @global string $wp_version WordPress version.
 *
 * @return void
 */
function twenty_twenty_one_customize() {
	wp_die(
		sprintf(
			/* translators: %s: WordPress Version. */
			esc_html__( 'This theme requires WordPress 5.3 or newer. You are running version %s. Please upgrade.', 'twentytwentyone' ),
			esc_html( $GLOBALS['wp_version'] )
		),
		'',
		array(
			'back_link' => true,
		)
	);
}
add_action( 'load-customize.php', 'twenty_twenty_one_customize' );

/**
 * Prevents the Theme Preview from being loaded on WordPress versions prior to 5.3.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @global string $wp_version WordPress version.
 *
 * @return void
 */
function twenty_twenty_one_preview() {
	if ( isset( $_GET['preview'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
		wp_die(
			sprintf(
				/* translators: %s: WordPress Version. */
				esc_html__( 'This theme requires WordPress 5.3 or newer. You are running version %s. Please upgrade.', 'twentytwentyone' ),
				esc_html( $GLOBALS['wp_version'] )
			)
		);
	}
}
add_action( 'template_redirect', 'twenty_twenty_one_preview' );
PK      `\t&  &    template-tags.phpnu W+A        <?php
/**
 * Custom template tags for this theme
 *
 * @package WordPress
 * @subpackage Twenty_Twenty_One
 * @since Twenty Twenty-One 1.0
 */

if ( ! function_exists( 'twenty_twenty_one_posted_on' ) ) {
	/**
	 * Prints HTML with meta information for the current post-date/time.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @return void
	 */
	function twenty_twenty_one_posted_on() {
		$time_string = '<time class="entry-date published updated" datetime="%1$s">%2$s</time>';

		$time_string = sprintf(
			$time_string,
			esc_attr( get_the_date( DATE_W3C ) ),
			esc_html( get_the_date() )
		);
		echo '<span class="posted-on">';
		printf(
			/* translators: %s: Publish date. */
			esc_html__( 'Published %s', 'twentytwentyone' ),
			$time_string // phpcs:ignore WordPress.Security.EscapeOutput
		);
		echo '</span>';
	}
}

if ( ! function_exists( 'twenty_twenty_one_posted_by' ) ) {
	/**
	 * Prints HTML with meta information about theme author.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @return void
	 */
	function twenty_twenty_one_posted_by() {
		if ( ! get_the_author_meta( 'description' ) && post_type_supports( get_post_type(), 'author' ) ) {
			echo '<span class="byline">';
			printf(
				/* translators: %s: Author name. */
				esc_html__( 'By %s', 'twentytwentyone' ),
				'<a href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '" rel="author">' . esc_html( get_the_author() ) . '</a>'
			);
			echo '</span>';
		}
	}
}

if ( ! function_exists( 'twenty_twenty_one_entry_meta_footer' ) ) {
	/**
	 * Prints HTML with meta information for the categories, tags and comments.
	 * Footer entry meta is displayed differently in archives and single posts.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @return void
	 */
	function twenty_twenty_one_entry_meta_footer() {

		// Early exit if not a post.
		if ( 'post' !== get_post_type() ) {
			return;
		}

		// Hide meta information on pages.
		if ( ! is_single() ) {

			if ( is_sticky() ) {
				echo '<p>' . esc_html_x( 'Featured post', 'Label for sticky posts', 'twentytwentyone' ) . '</p>';
			}

			$post_format = get_post_format();
			if ( 'aside' === $post_format || 'status' === $post_format ) {
				echo '<p><a href="' . esc_url( get_permalink() ) . '">' . twenty_twenty_one_continue_reading_text() . '</a></p>'; // phpcs:ignore WordPress.Security.EscapeOutput
			}

			// Posted on.
			twenty_twenty_one_posted_on();

			// Edit post link.
			edit_post_link(
				sprintf(
					/* translators: %s: Post title. Only visible to screen readers. */
					esc_html__( 'Edit %s', 'twentytwentyone' ),
					'<span class="screen-reader-text">' . get_the_title() . '</span>'
				),
				'<span class="edit-link">',
				'</span><br>'
			);

			if ( has_category() || has_tag() ) {

				echo '<div class="post-taxonomies">';

				$categories_list = get_the_category_list( wp_get_list_item_separator() );
				if ( $categories_list ) {
					printf(
						/* translators: %s: List of categories. */
						'<span class="cat-links">' . esc_html__( 'Categorized as %s', 'twentytwentyone' ) . ' </span>',
						$categories_list // phpcs:ignore WordPress.Security.EscapeOutput
					);
				}

				$tags_list = get_the_tag_list( '', wp_get_list_item_separator() );
				if ( $tags_list ) {
					printf(
						/* translators: %s: List of tags. */
						'<span class="tags-links">' . esc_html__( 'Tagged %s', 'twentytwentyone' ) . '</span>',
						$tags_list // phpcs:ignore WordPress.Security.EscapeOutput
					);
				}
				echo '</div>';
			}
		} else {

			echo '<div class="posted-by">';
			// Posted on.
			twenty_twenty_one_posted_on();
			// Posted by.
			twenty_twenty_one_posted_by();
			// Edit post link.
			edit_post_link(
				sprintf(
					/* translators: %s: Post title. Only visible to screen readers. */
					esc_html__( 'Edit %s', 'twentytwentyone' ),
					'<span class="screen-reader-text">' . get_the_title() . '</span>'
				),
				'<span class="edit-link">',
				'</span>'
			);
			echo '</div>';

			if ( has_category() || has_tag() ) {

				echo '<div class="post-taxonomies">';

				$categories_list = get_the_category_list( wp_get_list_item_separator() );
				if ( $categories_list ) {
					printf(
						/* translators: %s: List of categories. */
						'<span class="cat-links">' . esc_html__( 'Categorized as %s', 'twentytwentyone' ) . ' </span>',
						$categories_list // phpcs:ignore WordPress.Security.EscapeOutput
					);
				}

				$tags_list = get_the_tag_list( '', wp_get_list_item_separator() );
				if ( $tags_list ) {
					printf(
						/* translators: %s: List of tags. */
						'<span class="tags-links">' . esc_html__( 'Tagged %s', 'twentytwentyone' ) . '</span>',
						$tags_list // phpcs:ignore WordPress.Security.EscapeOutput
					);
				}
				echo '</div>';
			}
		}
	}
}

if ( ! function_exists( 'twenty_twenty_one_post_thumbnail' ) ) {
	/**
	 * Displays an optional post thumbnail.
	 *
	 * Wraps the post thumbnail in an anchor element on index views, or a div
	 * element when on single views.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @return void
	 */
	function twenty_twenty_one_post_thumbnail() {
		if ( ! twenty_twenty_one_can_show_post_thumbnail() ) {
			return;
		}
		?>

		<?php if ( is_singular() ) : ?>

			<figure class="post-thumbnail">
				<?php
				// Lazy-loading attributes should be skipped for thumbnails since they are immediately in the viewport.
				the_post_thumbnail( 'post-thumbnail', array( 'loading' => false ) );
				?>
				<?php if ( wp_get_attachment_caption( get_post_thumbnail_id() ) ) : ?>
					<figcaption class="wp-caption-text"><?php echo wp_kses_post( wp_get_attachment_caption( get_post_thumbnail_id() ) ); ?></figcaption>
				<?php endif; ?>
			</figure><!-- .post-thumbnail -->

		<?php else : ?>

			<figure class="post-thumbnail">
				<a class="post-thumbnail-inner alignwide" href="<?php the_permalink(); ?>" aria-hidden="true" tabindex="-1">
					<?php the_post_thumbnail( 'post-thumbnail' ); ?>
				</a>
				<?php if ( wp_get_attachment_caption( get_post_thumbnail_id() ) ) : ?>
					<figcaption class="wp-caption-text"><?php echo wp_kses_post( wp_get_attachment_caption( get_post_thumbnail_id() ) ); ?></figcaption>
				<?php endif; ?>
			</figure><!-- .post-thumbnail -->

		<?php endif; ?>
		<?php
	}
}

if ( ! function_exists( 'twenty_twenty_one_the_posts_navigation' ) ) {
	/**
	 * Print the next and previous posts navigation.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @return void
	 */
	function twenty_twenty_one_the_posts_navigation() {
		the_posts_pagination(
			array(
				'before_page_number' => esc_html__( 'Page', 'twentytwentyone' ) . ' ',
				'mid_size'           => 0,
				'prev_text'          => sprintf(
					'%s <span class="nav-prev-text">%s</span>',
					is_rtl() ? twenty_twenty_one_get_icon_svg( 'ui', 'arrow_right' ) : twenty_twenty_one_get_icon_svg( 'ui', 'arrow_left' ),
					wp_kses(
						__( 'Newer <span class="nav-short">posts</span>', 'twentytwentyone' ),
						array(
							'span' => array(
								'class' => array(),
							),
						)
					)
				),
				'next_text'          => sprintf(
					'<span class="nav-next-text">%s</span> %s',
					wp_kses(
						__( 'Older <span class="nav-short">posts</span>', 'twentytwentyone' ),
						array(
							'span' => array(
								'class' => array(),
							),
						)
					),
					is_rtl() ? twenty_twenty_one_get_icon_svg( 'ui', 'arrow_left' ) : twenty_twenty_one_get_icon_svg( 'ui', 'arrow_right' )
				),
			)
		);
	}
}
PK      `\9 E  E    template-functions.phpnu W+A        <?php
/**
 * Functions which enhance the theme by hooking into WordPress
 *
 * @package WordPress
 * @subpackage Twenty_Twenty_One
 * @since Twenty Twenty-One 1.0
 */

/**
 * Adds custom classes to the array of body classes.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param array $classes Classes for the body element.
 * @return array
 */
function twenty_twenty_one_body_classes( $classes ) {

	// Helps detect if JS is enabled or not.
	$classes[] = 'no-js';

	// Adds `singular` to singular pages, and `hfeed` to all other pages.
	$classes[] = is_singular() ? 'singular' : 'hfeed';

	// Add a body class if main navigation is active.
	if ( has_nav_menu( 'primary' ) ) {
		$classes[] = 'has-main-navigation';
	}

	// Add a body class if there are no footer widgets.
	if ( ! is_active_sidebar( 'sidebar-1' ) ) {
		$classes[] = 'no-widgets';
	}

	return $classes;
}
add_filter( 'body_class', 'twenty_twenty_one_body_classes' );

/**
 * Adds custom class to the array of posts classes.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param array $classes An array of CSS classes.
 * @return array
 */
function twenty_twenty_one_post_classes( $classes ) {
	$classes[] = 'entry';

	return $classes;
}
add_filter( 'post_class', 'twenty_twenty_one_post_classes', 10, 3 );

/**
 * Add a pingback url auto-discovery header for single posts, pages, or attachments.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @return void
 */
function twenty_twenty_one_pingback_header() {
	if ( is_singular() && pings_open() ) {
		echo '<link rel="pingback" href="', esc_url( get_bloginfo( 'pingback_url' ) ), '">';
	}
}
add_action( 'wp_head', 'twenty_twenty_one_pingback_header' );

/**
 * Remove the `no-js` class from body if JS is supported.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @return void
 */
function twenty_twenty_one_supports_js() {
	echo '<script>document.body.classList.remove("no-js");</script>';
}
add_action( 'wp_footer', 'twenty_twenty_one_supports_js' );

/**
 * Changes comment form default fields.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param array $defaults The form defaults.
 * @return array
 */
function twenty_twenty_one_comment_form_defaults( $defaults ) {

	// Adjust height of comment form.
	$defaults['comment_field'] = preg_replace( '/rows="\d+"/', 'rows="5"', $defaults['comment_field'] );

	return $defaults;
}
add_filter( 'comment_form_defaults', 'twenty_twenty_one_comment_form_defaults' );

/**
 * Determines if post thumbnail can be displayed.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @return bool
 */
function twenty_twenty_one_can_show_post_thumbnail() {
	/**
	 * Filters whether post thumbnail can be displayed.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @param bool $show_post_thumbnail Whether to show post thumbnail.
	 */
	return apply_filters(
		'twenty_twenty_one_can_show_post_thumbnail',
		! post_password_required() && ! is_attachment() && has_post_thumbnail()
	);
}

/**
 * Returns the size for avatars used in the theme.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @return int
 */
function twenty_twenty_one_get_avatar_size() {
	return 60;
}

/**
 * Creates continue reading text.
 *
 * @since Twenty Twenty-One 1.0
 */
function twenty_twenty_one_continue_reading_text() {
	$continue_reading = sprintf(
		/* translators: %s: Post title. Only visible to screen readers. */
		esc_html__( 'Continue reading %s', 'twentytwentyone' ),
		the_title( '<span class="screen-reader-text">', '</span>', false )
	);

	return $continue_reading;
}

/**
 * Creates the continue reading link for excerpt.
 *
 * @since Twenty Twenty-One 1.0
 */
function twenty_twenty_one_continue_reading_link_excerpt() {
	if ( ! is_admin() ) {
		return '&hellip; <a class="more-link" href="' . esc_url( get_permalink() ) . '">' . twenty_twenty_one_continue_reading_text() . '</a>';
	}
}

// Filter the excerpt more link.
add_filter( 'excerpt_more', 'twenty_twenty_one_continue_reading_link_excerpt' );

/**
 * Creates the continue reading link.
 *
 * @since Twenty Twenty-One 1.0
 */
function twenty_twenty_one_continue_reading_link() {
	if ( ! is_admin() ) {
		return '<div class="more-link-container"><a class="more-link" href="' . esc_url( get_permalink() ) . '#more-' . esc_attr( get_the_ID() ) . '">' . twenty_twenty_one_continue_reading_text() . '</a></div>';
	}
}

// Filter the content more link.
add_filter( 'the_content_more_link', 'twenty_twenty_one_continue_reading_link' );

if ( ! function_exists( 'twenty_twenty_one_post_title' ) ) {
	/**
	 * Adds a title to posts and pages that are missing titles.
	 *
	 * @since Twenty Twenty-One 1.0
	 *
	 * @param string $title The title.
	 * @return string
	 */
	function twenty_twenty_one_post_title( $title ) {
		return '' === $title ? esc_html_x( 'Untitled', 'Added to posts and pages that are missing titles', 'twentytwentyone' ) : $title;
	}
}
add_filter( 'the_title', 'twenty_twenty_one_post_title' );

/**
 * Gets the SVG code for a given icon.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param string $group The icon group.
 * @param string $icon  The icon.
 * @param int    $size  The icon size in pixels.
 * @return string
 */
function twenty_twenty_one_get_icon_svg( $group, $icon, $size = 24 ) {
	return Twenty_Twenty_One_SVG_Icons::get_svg( $group, $icon, $size );
}

/**
 * Changes the default navigation arrows to svg icons
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param string $calendar_output The generated HTML of the calendar.
 * @return string
 */
function twenty_twenty_one_change_calendar_nav_arrows( $calendar_output ) {
	$calendar_output = str_replace( '&laquo; ', is_rtl() ? twenty_twenty_one_get_icon_svg( 'ui', 'arrow_right' ) : twenty_twenty_one_get_icon_svg( 'ui', 'arrow_left' ), $calendar_output );
	$calendar_output = str_replace( ' &raquo;', is_rtl() ? twenty_twenty_one_get_icon_svg( 'ui', 'arrow_left' ) : twenty_twenty_one_get_icon_svg( 'ui', 'arrow_right' ), $calendar_output );
	return $calendar_output;
}
add_filter( 'get_calendar', 'twenty_twenty_one_change_calendar_nav_arrows' );

/**
 * Get custom CSS.
 *
 * Return CSS for non-latin language, if available, or null
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param string $type Whether to return CSS for the "front-end", "block-editor", or "classic-editor".
 * @return string
 */
function twenty_twenty_one_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-One 1.0
	 *
	 * @param array $font_family An array of locales and font families.
	 */
	$font_family = apply_filters(
		'twenty_twenty_one_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-One 1.0
	 *
	 * @param array $elements An array of elements for "front-end", "block-editor", or "classic-editor".
	 */
	$elements = apply_filters(
		'twenty_twenty_one_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', '.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 '';
	}

	// Include file if function doesn't exist.
	if ( ! function_exists( 'twenty_twenty_one_generate_css' ) ) {
		require_once get_theme_file_path( 'inc/custom-css.php' ); // phpcs:ignore WPThemeReview.CoreFunctionality.FileInclude.FileIncludeFound
	}

	// Return the specified styles.
	return twenty_twenty_one_generate_css( // @phpstan-ignore-line.
		implode( ',', $elements[ $type ] ),
		'font-family',
		implode( ',', $font_family[ $locale ] ),
		null,
		null,
		false
	);
}

/**
 * Print the first instance of a block in the content, and then break away.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param string      $block_name The full block type name, or a partial match.
 *                                Example: `core/image`, `core-embed/*`.
 * @param string|null $content    The content to search in. Use null for get_the_content().
 * @param int         $instances  How many instances of the block will be printed (max). Default  1.
 * @return bool Returns true if a block was located & printed, otherwise false.
 */
function twenty_twenty_one_print_first_instance_of_block( $block_name, $content = null, $instances = 1 ) {
	$instances_count = 0;
	$blocks_content  = '';

	if ( ! $content ) {
		$content = get_the_content();
	}

	// Parse blocks in the content.
	$blocks = parse_blocks( $content );

	// Loop blocks.
	foreach ( $blocks as $block ) {

		// Sanity check.
		if ( ! isset( $block['blockName'] ) ) {
			continue;
		}

		// Check if this the block matches the $block_name.
		$is_matching_block = false;

		// If the block ends with *, try to match the first portion.
		if ( '*' === $block_name[-1] ) {
			$is_matching_block = 0 === strpos( $block['blockName'], rtrim( $block_name, '*' ) );
		} else {
			$is_matching_block = $block_name === $block['blockName'];
		}

		if ( $is_matching_block ) {
			// Increment count.
			++$instances_count;

			// Add the block HTML.
			$blocks_content .= render_block( $block );

			// Break the loop if the $instances count was reached.
			if ( $instances_count >= $instances ) {
				break;
			}
		}
	}

	if ( $blocks_content ) {
		/** This filter is documented in wp-includes/post-template.php */
		echo apply_filters( 'the_content', $blocks_content ); // phpcs:ignore WordPress.Security.EscapeOutput
		return true;
	}

	return false;
}

/**
 * Retrieve protected post password form content.
 *
 * @since Twenty Twenty-One 1.0
 * @since Twenty Twenty-One 1.4 Corrected parameter name for `$output`,
 *                              added the `$post` parameter.
 *
 * @param string      $output The password form HTML output.
 * @param int|WP_Post $post   Optional. Post ID or WP_Post object. Default is global $post.
 * @return string HTML content for password form for password protected post.
 */
function twenty_twenty_one_password_form( $output, $post = 0 ) {
	$post   = get_post( $post );
	$label  = 'pwbox-' . ( empty( $post->ID ) ? wp_rand() : $post->ID );
	$output = '<p class="post-password-message">' . esc_html__( 'This content is password protected. Please enter a password to view.', 'twentytwentyone' ) . '</p>
	<form action="' . esc_url( site_url( 'wp-login.php?action=postpass', 'login_post' ) ) . '" class="post-password-form" method="post">
	<label class="post-password-form__label" for="' . esc_attr( $label ) . '">' . esc_html_x( 'Password', 'Post password form', 'twentytwentyone' ) . '</label><input class="post-password-form__input" name="post_password" id="' . esc_attr( $label ) . '" type="password" spellcheck="false" size="20" /><input type="submit" class="post-password-form__submit" name="' . esc_attr_x( 'Submit', 'Post password form', 'twentytwentyone' ) . '" value="' . esc_attr_x( 'Enter', 'Post password form', 'twentytwentyone' ) . '" /></form>
	';
	return $output;
}
add_filter( 'the_password_form', 'twenty_twenty_one_password_form', 10, 2 );

/**
 * Filters the list of attachment image attributes.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param string[]     $attr       Array of attribute values for the image markup, keyed by attribute name.
 *                                 See wp_get_attachment_image().
 * @param WP_Post      $attachment Image attachment post.
 * @param string|int[] $size       Requested image size. Can be any registered image size name, or
 *                                 an array of width and height values in pixels (in that order).
 * @return string[] The filtered attributes for the image markup.
 */
function twenty_twenty_one_get_attachment_image_attributes( $attr, $attachment, $size ) {

	if ( is_admin() ) {
		return $attr;
	}

	if ( isset( $attr['class'] ) && false !== strpos( $attr['class'], 'custom-logo' ) ) {
		return $attr;
	}

	$width  = false;
	$height = false;

	if ( is_array( $size ) ) {
		$width  = (int) $size[0];
		$height = (int) $size[1];
	} elseif ( $attachment && is_object( $attachment ) && $attachment->ID ) {
		$meta = wp_get_attachment_metadata( $attachment->ID );
		if ( isset( $meta['width'] ) && isset( $meta['height'] ) ) {
			$width  = (int) $meta['width'];
			$height = (int) $meta['height'];
		}
	}

	if ( $width && $height ) {

		// Add style.
		$attr['style'] = isset( $attr['style'] ) ? $attr['style'] : '';
		$attr['style'] = 'width:100%;height:' . round( 100 * $height / $width, 2 ) . '%;max-width:' . $width . 'px;' . $attr['style'];
	}

	return $attr;
}
add_filter( 'wp_get_attachment_image_attributes', 'twenty_twenty_one_get_attachment_image_attributes', 10, 3 );
PK      `\ʺ      custom-css.phpnu W+A        <?php
/**
 * Custom CSS
 *
 * @package WordPress
 * @subpackage Twenty_Twenty_One
 * @since Twenty Twenty-One 1.0
 */

/**
 * Generate CSS.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param string $selector The CSS selector.
 * @param string $style    The CSS style.
 * @param string $value    The CSS value.
 * @param string $prefix   The CSS prefix.
 * @param string $suffix   The CSS suffix.
 * @param bool   $display  Print the styles.
 * @return string
 */
function twenty_twenty_one_generate_css( $selector, $style, $value, $prefix = '', $suffix = '', $display = true ) {

	// Bail early if there is no $selector elements or properties and $value.
	if ( ! $value || ! $selector ) {
		return '';
	}

	$css = sprintf( '%s { %s: %s; }', $selector, $style, $prefix . $value . $suffix );

	if ( $display ) {
		/*
		 * Note to reviewers: $css contains auto-generated CSS.
		 * It is included inside <style> tags and can only be interpreted as CSS on the browser.
		 * Using wp_strip_all_tags() here is sufficient escaping to avoid
		 * malicious attempts to close </style> and open a <script>.
		 */
		echo wp_strip_all_tags( $css ); // phpcs:ignore WordPress.Security.EscapeOutput
	}
	return $css;
}
PK      `\_Ul      menu-functions.phpnu W+A        <?php
/**
 * Functions and filters related to the menus.
 *
 * Makes the default WordPress navigation use an HTML structure similar
 * to the Navigation block.
 *
 * @link https://make.wordpress.org/themes/2020/07/06/printing-navigation-block-html-from-a-legacy-menu-in-themes/
 *
 * @package WordPress
 * @subpackage Twenty_Twenty_One
 * @since Twenty Twenty-One 1.0
 */

/**
 * Add a button to top-level menu items that has sub-menus.
 * An icon is added using CSS depending on the value of aria-expanded.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param string $output Nav menu item start element.
 * @param object $item   Nav menu item.
 * @param int    $depth  Depth.
 * @param object $args   Nav menu args.
 * @return string Nav menu item start element.
 */
function twenty_twenty_one_add_sub_menu_toggle( $output, $item, $depth, $args ) {
	if ( 0 === $depth && in_array( 'menu-item-has-children', $item->classes, true ) ) {

		// Add toggle button.
		$output .= '<button class="sub-menu-toggle" aria-expanded="false" onClick="twentytwentyoneExpandSubMenu(this)">';
		$output .= '<span class="icon-plus">' . twenty_twenty_one_get_icon_svg( 'ui', 'plus', 18 ) . '</span>';
		$output .= '<span class="icon-minus">' . twenty_twenty_one_get_icon_svg( 'ui', 'minus', 18 ) . '</span>';
		/* translators: Hidden accessibility text. */
		$output .= '<span class="screen-reader-text">' . esc_html__( 'Open menu', 'twentytwentyone' ) . '</span>';
		$output .= '</button>';
	}
	return $output;
}
add_filter( 'walker_nav_menu_start_el', 'twenty_twenty_one_add_sub_menu_toggle', 10, 4 );

/**
 * Detects the social network from a URL and returns the SVG code for its icon.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param string $uri  Social link.
 * @param int    $size The icon size in pixels.
 * @return string
 */
function twenty_twenty_one_get_social_link_svg( $uri, $size = 24 ) {
	return Twenty_Twenty_One_SVG_Icons::get_social_link_svg( $uri, $size );
}

/**
 * Displays SVG icons in the footer navigation.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param string   $item_output The menu item's starting HTML output.
 * @param WP_Post  $item        Menu item data object.
 * @param int      $depth       Depth of the menu. Used for padding.
 * @param stdClass $args        An object of wp_nav_menu() arguments.
 * @return string The menu item output with social icon.
 */
function twenty_twenty_one_nav_menu_social_icons( $item_output, $item, $depth, $args ) {
	// Change SVG icon inside social links menu if there is supported URL.
	if ( 'footer' === $args->theme_location ) {
		$svg = twenty_twenty_one_get_social_link_svg( $item->url, 24 );
		if ( ! empty( $svg ) ) {
			$item_output = str_replace( $args->link_before, $svg, $item_output );
		}
	}

	return $item_output;
}

add_filter( 'walker_nav_menu_start_el', 'twenty_twenty_one_nav_menu_social_icons', 10, 4 );

/**
 * Filters the arguments for a single nav menu item.
 *
 * @since Twenty Twenty-One 1.0
 *
 * @param stdClass $args  An object of wp_nav_menu() arguments.
 * @param WP_Post  $item  Menu item data object.
 * @param int      $depth Depth of menu item. Used for padding.
 * @return stdClass
 */
function twenty_twenty_one_add_menu_description_args( $args, $item, $depth ) {
	if ( '</span>' !== $args->link_after ) {
		$args->link_after = '';
	}

	if ( 0 === $depth && isset( $item->description ) && $item->description ) {
		// The extra <span> element is here for styling purposes: Allows the description to not be underlined on hover.
		$args->link_after = '<p class="menu-item-description"><span>' . $item->description . '</span></p>';
	}

	return $args;
}
add_filter( 'nav_menu_item_args', 'twenty_twenty_one_add_menu_description_args', 10, 3 );
PK        \ -r&  &                  core/limited_offers.phpnu W+A        PK        \!>AW<L  <L              '  core/front_end.phpnu W+A        PK        \)|	  |	              s  core/styles/gutenberg.phpnu W+A        PK        \$B    3            a}  core/styles/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \Sb6  b6              : core/styles/css_prop.phpnu W+A        PK        \S~C3  C3              dq core/styles/css_vars.phpnu W+A        PK        \ȑй                 core/styles/generator.phpnu W+A        PK        \{                 core/styles/frontend.phpnu W+A        PK        \,L  L               R< core/styles/dynamic_selector.phpnu W+A        PK        \$B    ,            T core/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \oc<%  %              @ core/settings/mods.phpnu W+A        PK        \s=A  A              7 core/settings/config.phpnu W+A        PK        \$B    5            x core/settings/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \^b?  ?              Z6 core/settings/mods_migrator.phpnu W+A        PK        \®6-  -              K core/builder_migrator.phpnu W+A        PK        \L%                y core/traits/theme_mods.phpnu W+A        PK        \$B    3            | core/traits/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \                k9 core/dynamic_css.phpnu W+A        PK        \İ<Z  <Z              0O core/admin.phpnu W+A        PK        \ެ                 core/theme_info.phpnu W+A        PK        \2"o                Ƭ core/supported_post_types.phpnu W+A        PK        \7>                 core/core_loader.phpnu W+A        PK        \t  t              3 core/factory.phpnu W+A        PK        \$B    '             elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \\i                4 views/inline/base_inline.phpnu W+A        PK        \                 views/cover_header.phpnu W+A        PK        \                | views/post_layout.phpnu W+A        PK        \K/  /               ^ views/layouts/layout_sidebar.phpnu W+A        PK        \=LC  C  "             views/layouts/layout_container.phpnu W+A        PK        \;e                r views/top_bar.phpnu W+A        PK        \  y6  y6              O views/template_parts.phpnu W+A        PK        \VyA*:  *:               views/partials/post_meta.phpnu W+A        PK        \/	  	              S views/partials/excerpt.phpnu W+A        PK        \5#  #              ] views/partials/comments.phpnu W+A        PK        \$B    -             views/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \p                > views/base_view.phpnu W+A        PK        \u;  ;              UC views/nav_walker.phpnu W+A        PK        \DuA  A              / views/header.phpnu W+A        PK        \FA[7  7               views/tweaks.phpnu W+A        PK        \@Z!  !              ' views/font_manager.phpnu W+A        PK        \:|Ͼ                + views/content_none.phpnu W+A        PK        \FL  L              / views/product_layout.phpnu W+A        PK        \yY2B  2B  $             views/pluggable/metabox_settings.phpnu W+A        PK        \D=  =              I	 views/pluggable/masonry.phpnu W+A        PK        \z!  !              )	 views/pluggable/pagination.phpnu W+A        PK        \$B    7            K	 views/pluggable/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \WKl  l              	
 views/content_404.phpnu W+A        PK        \~'uD  D              
 views/breadcrumbs.phpnu W+A        PK        \H",'z  z              T
 views/page_header.phpnu W+A        PK        \#)}  }              /
 views/page_layout.phpnu W+A        PK        \V9"    (            1
 compatibility/easy_digital_downloads.phpnu W+A        PK        \J"  "              #;
 compatibility/lifter.phpnu W+A        PK        \ItV  V              P^
 compatibility/patterns.phpnu W+A        PK        \c    #            b
 compatibility/page_builder_base.phpnu W+A        PK        \$B    5            Oi
 compatibility/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \B    )            & compatibility/header_footer_elementor.phpnu W+A        PK        \`                , compatibility/web_stories.phpnu W+A        PK        \                `0 compatibility/ppom.phpnu W+A        PK        \                3 compatibility/pwa.phpnu W+A        PK        \l  l              9 compatibility/wpml.phpnu W+A        PK        \if  f  &            v@ compatibility/header_footer_beaver.phpnu W+A        PK        \\(m=    !            2G compatibility/starter_content.phpnu W+A        PK        \7e  e              X compatibility/generic.phpnu W+A        PK        \2 ,   ,              H` compatibility/amp.phpnu W+A        PK        \?Ozw  zw               compatibility/woocommerce.phpnu W+A        PK        \                t compatibility/beaver.phpnu W+A        PK        \K    5             compatibility/block-patterns/gallery-grid-buttons.phpnu W+A        PK        \E=5	  5	  6            J$ compatibility/block-patterns/gallery-title-buttons.phpnu W+A        PK        \$B    D            - compatibility/block-patterns/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \_Z    C            O compatibility/block-patterns/three-columns-images-texts-content.phpnu W+A        PK        \}.    6            ~ compatibility/block-patterns/two-columns-with-text.phpnu W+A        PK        \S5m    =             compatibility/block-patterns/dark-header-centered-content.phpnu W+A        PK        \P    :             compatibility/block-patterns/three-columns-images-text.phpnu W+A        PK        \)<    7            
 compatibility/block-patterns/two-columns-image-text.phpnu W+A        PK        \-A  A  B            k compatibility/block-patterns/light-header-left-aligned-content.phpnu W+A        PK        \$c]    =             compatibility/block-patterns/two-columns-centered-content.phpnu W+A        PK        \.»    5            v! compatibility/block-patterns/testimonials-columns.phpnu W+A        PK        \ܘm|<  <  :            t- compatibility/block-patterns/four-columns-team-members.phpnu W+A        PK        \X@  @              ? compatibility/elementor.phpnu W+A        PK        \o>3M  M  ,            < compatibility/starter-content/theme-mods.phpnu W+A        PK        \NO  O  &             compatibility/starter-content/home.phpnu W+A        PK        \r,o7  7  '             compatibility/starter-content/about.phpnu W+A        PK        \[t   t   1            U compatibility/starter-content/project-details.phpnu W+A        PK        \y&c  &c  i            v compatibility/starter-content/class-wp-html-doctype-info-20260613154400-20260613171018-20260613171622.phpnu W+A        PK        \o.u&  u&  ,             compatibility/starter-content/portofolio.phpnu W+A        PK        \N
  
  )            ^ compatibility/starter-content/contact.phpnu W+A        PK        \LVX9  9               compatibility/fse.phpnu W+A        PK        \$B    -            U admin/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \Ed  d               admin/hooks_upsells.phpnu W+A        PK        \ո}                ) admin/metabox/main.phpnu W+A        PK        \90  0              B admin/metabox/manager.phpnu W+A        PK        \XH                s admin/metabox/controls_base.phpnu W+A        PK        \!o  o               w admin/metabox/controls/radio.phpnu W+A        PK        \%,    #            { admin/metabox/controls/checkbox.phpnu W+A        PK        \؝hf    $             admin/metabox/controls/separator.phpnu W+A        PK        \`L?                  admin/metabox/controls/range.phpnu W+A        PK        \:    '            = admin/metabox/controls/control_base.phpnu W+A        PK        \76                 admin/troubleshoot/main.phpnu W+A        PK        \$B    7             admin/dashboard/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \}    %            h admin/dashboard/changelog_handler.phpnu W+A        PK        \Uk^    !            z admin/dashboard/plugin_helper.phpnu W+A        PK        \bey}  y}              ω admin/dashboard/main.phpnu W+A        PK        \k    -             customizer/controls/simple_upsell_section.phpnu W+A        PK        \kNW3k  k              r customizer/controls/tabs.phpnu W+A        PK        \#4x  x               ) customizer/controls/ordering.phpnu W+A        PK        \m&k  k  $            $ customizer/controls/button_group.phpnu W+A        PK        \d    &            , customizer/controls/upsell_control.phpnu W+A        PK        \8W'  '  ,            : customizer/controls/react/upsell_section.phpnu W+A        PK        \B.	    2            > customizer/controls/react/conditional_selector.phpnu W+A        PK        \rх    /            B customizer/controls/react/button_appearance.phpnu W+A        PK        \$t  t  &            3I customizer/controls/react/ordering.phpnu W+A        PK        \+J	  	  (            M customizer/controls/react/nr_spacing.phpnu W+A        PK        \
$    /            ^S customizer/controls/react/responsive_toggle.phpnu W+A        PK        \.BL  L  6            W customizer/controls/react/typography_extra_section.phpnu W+A        PK        \p  p  +            kZ customizer/controls/react/global_colors.phpnu W+A        PK        \    .            6_ customizer/controls/react/form_token_field.phpnu W+A        PK        \ƹ    %            yc customizer/controls/react/builder.phpnu W+A        PK        \    (            h customizer/controls/react/background.phpnu W+A        PK        \Н    )            l customizer/controls/react/multiselect.phpnu W+A        PK        \7    -            q customizer/controls/react/builder_section.phpnu W+A        PK        \]q{kT  T  *            Ku customizer/controls/react/group_select.phpnu W+A        PK        \Ds    *            y customizer/controls/react/link_control.phpnu W+A        PK        \Zw    )             customizer/controls/react/font_family.phpnu W+A        PK        \4
    &            D customizer/controls/react/textarea.phpnu W+A        PK        \tɚ	  	  %            o customizer/controls/react/heading.phpnu W+A        PK        \PoW  W  (            ^ customizer/controls/react/typography.phpnu W+A        PK        \.	    6             customizer/controls/react/responsive_radio_buttons.phpnu W+A        PK        \fL  L  3            y customizer/controls/react/documentation_section.phpnu W+A        PK        \be    +            ( customizer/controls/react/font_pairings.phpnu W+A        PK        \?m  m  +            s customizer/controls/react/upsell_banner.phpnu W+A        PK        \5Nv  v  )            ; customizer/controls/react/radio_image.phpnu W+A        PK        \=  =  2            
 customizer/controls/react/instructions_control.phpnu W+A        PK        \݃Ū    &             customizer/controls/react/repeater.phpnu W+A        PK        \$B    A             customizer/controls/react/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \    *            a| customizer/controls/react/logo_palette.phpnu W+A        PK        \c]t    -             customizer/controls/react/builder_columns.phpnu W+A        PK        \nV  V  #            S customizer/controls/react/color.phpnu W+A        PK        \"=5  5  3            OY customizer/controls/react/upsell_banner_section.phpnu W+A        PK        \
;    .            ` customizer/controls/react/responsive_range.phpnu W+A        PK        \_>    2            Be customizer/controls/react/global_custom_colors.phpnu W+A        PK        \!I  I  +            0j customizer/controls/react/radio_buttons.phpnu W+A        PK        \k    +            p customizer/controls/react/inline_select.phpnu W+A        PK        \S<    #            Cv customizer/controls/react/range.phpnu W+A        PK        \$R  R  .            iz customizer/controls/react/presets_selector.phpnu W+A        PK        \ W=    '             customizer/controls/react/rich_text.phpnu W+A        PK        \-B  B  %            w customizer/controls/react/spacing.phpnu W+A        PK        \Vs    2             customizer/controls/react/instructions_section.phpnu W+A        PK        \`G                 customizer/controls/range.phpnu W+A        PK        \i|	  	  %            ԣ customizer/controls/simple_upsell.phpnu W+A        PK        \L                 2 customizer/controls/button.phpnu W+A        PK        \$B    ;             customizer/controls/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \@(  (  )            r customizer/controls/responsive_number.phpnu W+A        PK        \cX  X               j customizer/controls/checkbox.phpnu W+A        PK        \C    #             customizer/controls/radio_image.phpnu W+A        PK        \fy  y              c customizer/controls/heading.phpnu W+A        PK        \ϻC    )            + customizer/options/layout_single_page.phpnu W+A        PK        \$B    :            G customizer/options/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \Nb,<    '            l customizer/options/layout_container.phpnu W+A        PK        \77P  P  )             customizer/options/base_layout_single.phpnu W+A        PK        \8H                 customizer/options/main.phpnu W+A        PK        \^A  A  !             customizer/options/typography.phpnu W+A        PK        \wh|  |  "            # customizer/options/layout_blog.phpnu W+A        PK        \$B    =            W customizer/options/js/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \`#Z    $            ] customizer/options/js/0_utilities.jsnu W+A        PK        \xP  P  !            b customizer/options/js/2_layout.jsnu W+A        PK        \=wC  C  (            rn customizer/options/colors_background.phpnu W+A        PK        \G                 customizer/options/checkout.phpnu W+A        PK        \fv%  %  %             customizer/options/layout_sidebar.phpnu W+A        PK        \_sɺ  ɺ              o customizer/options/upsells.phpnu W+A        PK        \:G  :G  "            n customizer/options/form_fields.phpnu W+A        PK        \ŉ                 customizer/options/buttons.phpnu W+A        PK        \JY    ,             customizer/options/layout_single_product.phpnu W+A        PK        \k4                Y customizer/options/rtl.phpnu W+A        PK        \ט{H  {H  )            % customizer/options/layout_single_post.phpnu W+A        PK        \$B    2            4 customizer/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \$x!  !              Q customizer/loader.phpnu W+A        PK        \Xd)	  	               customizer/defaults/layout.phpnu W+A        PK        \c    #             customizer/defaults/single_post.phpnu W+A        PK        \$B    ;            6 customizer/defaults/elFinderVolumeLocalFileSystem.class.phpnu W+A        PK        \ҫ                : customizer/types/section.phpnu W+A        PK        \{d  d              1 customizer/types/control.phpnu W+A        PK        \7S  S               customizer/types/panel.phpnu W+A        PK        \BC                 ~  customizer/types/partial.phpnu W+A        PK        \ɽ5  5              Y customizer/base_customizer.phpnu W+A        PK        `\f	  	              e9 block-styles.phpnu W+A        PK        `\j"  "              .C starter-content.phpnu W+A        PK        `\~{)P  )P              e block-patterns.phpnu W+A        PK        `\>!	  	               back-compat.phpnu W+A        PK        `\t&  &               template-tags.phpnu W+A        PK        `\9 E  E              M template-functions.phpnu W+A        PK        `\ʺ                " custom-css.phpnu W+A        PK        `\_Ul                ' menu-functions.phpnu W+A        PK      J  