balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

frontend.php (36810B)


      1 <?php
      2 namespace Elementor;
      3 
      4 use Elementor\Core\Base\App;
      5 use Elementor\Core\Base\Document;
      6 use Elementor\Core\Frontend\Render_Mode_Manager;
      7 use Elementor\Core\Responsive\Files\Frontend as FrontendFile;
      8 use Elementor\Core\Files\CSS\Global_CSS;
      9 use Elementor\Core\Files\CSS\Post as Post_CSS;
     10 use Elementor\Core\Files\CSS\Post_Preview;
     11 use Elementor\Core\Responsive\Responsive;
     12 use Elementor\Core\Settings\Manager as SettingsManager;
     13 use Elementor\Core\Breakpoints\Manager as Breakpoints_Manager;
     14 
     15 if ( ! defined( 'ABSPATH' ) ) {
     16 	exit; // Exit if accessed directly.
     17 }
     18 
     19 /**
     20  * Elementor frontend.
     21  *
     22  * Elementor frontend handler class is responsible for initializing Elementor in
     23  * the frontend.
     24  *
     25  * @since 1.0.0
     26  */
     27 class Frontend extends App {
     28 
     29 	/**
     30 	 * The priority of the content filter.
     31 	 */
     32 	const THE_CONTENT_FILTER_PRIORITY = 9;
     33 
     34 	/**
     35 	 * Post ID.
     36 	 *
     37 	 * Holds the ID of the current post.
     38 	 *
     39 	 * @access private
     40 	 *
     41 	 * @var int Post ID.
     42 	 */
     43 	private $post_id;
     44 
     45 	/**
     46 	 * Fonts to enqueue
     47 	 *
     48 	 * Holds the list of fonts that are being used in the current page.
     49 	 *
     50 	 * @since 1.9.4
     51 	 * @access public
     52 	 *
     53 	 * @var array Used fonts. Default is an empty array.
     54 	 */
     55 	public $fonts_to_enqueue = [];
     56 
     57 	/**
     58 	 * Holds the class that respond to manage the render mode.
     59 	 *
     60 	 * @var Render_Mode_Manager
     61 	 */
     62 	public $render_mode_manager;
     63 
     64 	/**
     65 	 * Registered fonts.
     66 	 *
     67 	 * Holds the list of enqueued fonts in the current page.
     68 	 *
     69 	 * @since 1.0.0
     70 	 * @access private
     71 	 *
     72 	 * @var array Registered fonts. Default is an empty array.
     73 	 */
     74 	private $registered_fonts = [];
     75 
     76 	/**
     77 	 * Icon Fonts to enqueue
     78 	 *
     79 	 * Holds the list of Icon fonts that are being used in the current page.
     80 	 *
     81 	 * @since 2.4.0
     82 	 * @access private
     83 	 *
     84 	 * @var array Used icon fonts. Default is an empty array.
     85 	 */
     86 	private $icon_fonts_to_enqueue = [];
     87 
     88 	/**
     89 	 * Enqueue Icon Fonts
     90 	 *
     91 	 * Holds the list of Icon fonts already enqueued  in the current page.
     92 	 *
     93 	 * @since 2.4.0
     94 	 * @access private
     95 	 *
     96 	 * @var array enqueued icon fonts. Default is an empty array.
     97 	 */
     98 	private $enqueued_icon_fonts = [];
     99 
    100 	/**
    101 	 * Whether the page is using Elementor.
    102 	 *
    103 	 * Used to determine whether the current page is using Elementor.
    104 	 *
    105 	 * @since 1.0.0
    106 	 * @access private
    107 	 *
    108 	 * @var bool Whether Elementor is being used. Default is false.
    109 	 */
    110 	private $_has_elementor_in_page = false;
    111 
    112 	/**
    113 	 * Whether the excerpt is being called.
    114 	 *
    115 	 * Used to determine whether the call to `the_content()` came from `get_the_excerpt()`.
    116 	 *
    117 	 * @since 1.0.0
    118 	 * @access private
    119 	 *
    120 	 * @var bool Whether the excerpt is being used. Default is false.
    121 	 */
    122 	private $_is_excerpt = false;
    123 
    124 	/**
    125 	 * Filters removed from the content.
    126 	 *
    127 	 * Hold the list of filters removed from `the_content()`. Used to hold the filters that
    128 	 * conflicted with Elementor while Elementor process the content.
    129 	 *
    130 	 * @since 1.0.0
    131 	 * @access private
    132 	 *
    133 	 * @var array Filters removed from the content. Default is an empty array.
    134 	 */
    135 	private $content_removed_filters = [];
    136 
    137 	/**
    138 	 * @var string[]
    139 	 */
    140 	private $body_classes = [
    141 		'elementor-default',
    142 	];
    143 
    144 	/**
    145 	 * Front End constructor.
    146 	 *
    147 	 * Initializing Elementor front end. Make sure we are not in admin, not and
    148 	 * redirect from old URL structure of Elementor editor.
    149 	 *
    150 	 * @since 1.0.0
    151 	 * @access public
    152 	 */
    153 	public function __construct() {
    154 		// We don't need this class in admin side, but in AJAX requests.
    155 		if ( is_admin() && ! wp_doing_ajax() ) {
    156 			return;
    157 		}
    158 
    159 		add_action( 'template_redirect', [ $this, 'init_render_mode' ], -1 /* Before admin bar. */ );
    160 		add_action( 'template_redirect', [ $this, 'init' ] );
    161 		add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ], 5 );
    162 		add_action( 'wp_enqueue_scripts', [ $this, 'register_styles' ], 5 );
    163 
    164 		// TODO: a temporary solution to a scenario that the elementor-icons.css file was de-registered and the e-icons font fonts should not be loaded.
    165 		add_action( 'wp_enqueue_scripts', function() {
    166 			if ( ! wp_style_is( 'elementor-icons', 'registered' ) ) {
    167 				$elementor_icons_css_reset = '[class^="eicon"], [class*=" eicon-"] { font-family: "initial"; } [class^="eicon"]:before, [class*=" eicon-"]:before { content: ""; }';
    168 
    169 				wp_add_inline_style( 'elementor-frontend', $elementor_icons_css_reset );
    170 			}
    171 		}, 30 );
    172 
    173 		$this->add_content_filter();
    174 
    175 		// Hack to avoid enqueue post CSS while it's a `the_excerpt` call.
    176 		add_filter( 'get_the_excerpt', [ $this, 'start_excerpt_flag' ], 1 );
    177 		add_filter( 'get_the_excerpt', [ $this, 'end_excerpt_flag' ], 20 );
    178 	}
    179 
    180 	/**
    181 	 * Get module name.
    182 	 *
    183 	 * Retrieve the module name.
    184 	 *
    185 	 * @since 2.3.0
    186 	 * @access public
    187 	 *
    188 	 * @return string Module name.
    189 	 */
    190 	public function get_name() {
    191 		return 'frontend';
    192 	}
    193 
    194 	/**
    195 	 * Init render mode manager.
    196 	 */
    197 	public function init_render_mode() {
    198 		if ( Plugin::$instance->editor->is_edit_mode() ) {
    199 			return;
    200 		}
    201 
    202 		$this->render_mode_manager = new Render_Mode_Manager();
    203 	}
    204 
    205 	/**
    206 	 * Init.
    207 	 *
    208 	 * Initialize Elementor front end. Hooks the needed actions to run Elementor
    209 	 * in the front end, including script and style registration.
    210 	 *
    211 	 * Fired by `template_redirect` action.
    212 	 *
    213 	 * @since 1.0.0
    214 	 * @access public
    215 	 */
    216 	public function init() {
    217 		if ( Plugin::$instance->editor->is_edit_mode() ) {
    218 			return;
    219 		}
    220 
    221 		add_filter( 'body_class', [ $this, 'body_class' ] );
    222 
    223 		if ( Plugin::$instance->preview->is_preview_mode() ) {
    224 			return;
    225 		}
    226 
    227 		if ( current_user_can( 'manage_options' ) ) {
    228 			Plugin::$instance->init_common();
    229 		}
    230 
    231 		$this->post_id = get_the_ID();
    232 
    233 		$document = Plugin::$instance->documents->get( $this->post_id );
    234 
    235 		if ( is_singular() && $document && $document->is_built_with_elementor() ) {
    236 			add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ] );
    237 		}
    238 
    239 		// Priority 7 to allow google fonts in header template to load in <head> tag
    240 		add_action( 'wp_head', [ $this, 'print_fonts_links' ], 7 );
    241 		add_action( 'wp_head', [ $this, 'add_theme_color_meta_tag' ] );
    242 		add_action( 'wp_footer', [ $this, 'wp_footer' ] );
    243 	}
    244 
    245 	/**
    246 	 * @since 2.0.12
    247 	 * @access public
    248 	 * @param string|array $class
    249 	 */
    250 	public function add_body_class( $class ) {
    251 		if ( is_array( $class ) ) {
    252 			$this->body_classes = array_merge( $this->body_classes, $class );
    253 		} else {
    254 			$this->body_classes[] = $class;
    255 		}
    256 	}
    257 
    258 	/**
    259 	 * Add Theme Color Meta Tag
    260 	 *
    261 	 * @since 3.0.0
    262 	 * @access public
    263 	 */
    264 	public function add_theme_color_meta_tag() {
    265 		$kit = Plugin::$instance->kits_manager->get_active_kit_for_frontend();
    266 		$mobile_theme_color = $kit->get_settings( 'mobile_theme_color' );
    267 
    268 		if ( ! empty( $mobile_theme_color ) ) {
    269 			?>
    270 			<meta name="theme-color" content="<?php echo esc_html( $mobile_theme_color ); ?>">
    271 			<?php
    272 		}
    273 	}
    274 
    275 	/**
    276 	 * Body tag classes.
    277 	 *
    278 	 * Add new elementor classes to the body tag.
    279 	 *
    280 	 * Fired by `body_class` filter.
    281 	 *
    282 	 * @since 1.0.0
    283 	 * @access public
    284 	 *
    285 	 * @param array $classes Optional. One or more classes to add to the body tag class list.
    286 	 *                       Default is an empty array.
    287 	 *
    288 	 * @return array Body tag classes.
    289 	 */
    290 	public function body_class( $classes = [] ) {
    291 		$classes = array_merge( $classes, $this->body_classes );
    292 
    293 		$id = get_the_ID();
    294 
    295 		$document = Plugin::$instance->documents->get( $id );
    296 
    297 		if ( is_singular() && $document && $document->is_built_with_elementor() ) {
    298 			$classes[] = 'elementor-page elementor-page-' . $id;
    299 		}
    300 
    301 		if ( Plugin::$instance->preview->is_preview_mode() ) {
    302 			$editor_preferences = SettingsManager::get_settings_managers( 'editorPreferences' );
    303 
    304 			$show_hidden_elements = $editor_preferences->get_model()->get_settings( 'show_hidden_elements' );
    305 
    306 			if ( 'yes' === $show_hidden_elements ) {
    307 				$classes[] = 'e-preview--show-hidden-elements';
    308 			}
    309 		}
    310 
    311 		return $classes;
    312 	}
    313 
    314 	/**
    315 	 * Add content filter.
    316 	 *
    317 	 * Remove plain content and render the content generated by Elementor.
    318 	 *
    319 	 * @since 1.8.0
    320 	 * @access public
    321 	 */
    322 	public function add_content_filter() {
    323 		add_filter( 'the_content', [ $this, 'apply_builder_in_content' ], self::THE_CONTENT_FILTER_PRIORITY );
    324 	}
    325 
    326 	/**
    327 	 * Remove content filter.
    328 	 *
    329 	 * When the Elementor generated content rendered, we remove the filter to prevent multiple
    330 	 * accuracies. This way we make sure Elementor renders the content only once.
    331 	 *
    332 	 * @since 1.8.0
    333 	 * @access public
    334 	 */
    335 	public function remove_content_filter() {
    336 		remove_filter( 'the_content', [ $this, 'apply_builder_in_content' ], self::THE_CONTENT_FILTER_PRIORITY );
    337 	}
    338 
    339 	/**
    340 	 * Registers scripts.
    341 	 *
    342 	 * Registers all the frontend scripts.
    343 	 *
    344 	 * Fired by `wp_enqueue_scripts` action.
    345 	 *
    346 	 * @since 1.2.1
    347 	 * @access public
    348 	 */
    349 	public function register_scripts() {
    350 		/**
    351 		 * Before frontend register scripts.
    352 		 *
    353 		 * Fires before Elementor frontend scripts are registered.
    354 		 *
    355 		 * @since 1.2.1
    356 		 */
    357 		do_action( 'elementor/frontend/before_register_scripts' );
    358 
    359 		wp_register_script(
    360 			'elementor-webpack-runtime',
    361 			$this->get_js_assets_url( 'webpack.runtime', 'assets/js/' ),
    362 			[],
    363 			ELEMENTOR_VERSION,
    364 			true
    365 		);
    366 
    367 		wp_register_script(
    368 			'elementor-frontend-modules',
    369 			$this->get_js_assets_url( 'frontend-modules' ),
    370 			[
    371 				'elementor-webpack-runtime',
    372 				'jquery',
    373 			],
    374 			ELEMENTOR_VERSION,
    375 			true
    376 		);
    377 
    378 		wp_register_script(
    379 			'elementor-waypoints',
    380 			$this->get_js_assets_url( 'waypoints', 'assets/lib/waypoints/' ),
    381 			[
    382 				'jquery',
    383 			],
    384 			'4.0.2',
    385 			true
    386 		);
    387 
    388 		wp_register_script(
    389 			'flatpickr',
    390 			$this->get_js_assets_url( 'flatpickr', 'assets/lib/flatpickr/' ),
    391 			[
    392 				'jquery',
    393 			],
    394 			'4.1.4',
    395 			true
    396 		);
    397 
    398 		wp_register_script(
    399 			'imagesloaded',
    400 			$this->get_js_assets_url( 'imagesloaded', 'assets/lib/imagesloaded/' ),
    401 			[
    402 				'jquery',
    403 			],
    404 			'4.1.0',
    405 			true
    406 		);
    407 
    408 		wp_register_script(
    409 			'jquery-numerator',
    410 			$this->get_js_assets_url( 'jquery-numerator', 'assets/lib/jquery-numerator/' ),
    411 			[
    412 				'jquery',
    413 			],
    414 			'0.2.1',
    415 			true
    416 		);
    417 
    418 		/**
    419 		 * @deprecated since 2.7.0 Use Swiper instead
    420 		 */
    421 		wp_register_script(
    422 			'jquery-slick',
    423 			$this->get_js_assets_url( 'slick', 'assets/lib/slick/' ),
    424 			[
    425 				'jquery',
    426 			],
    427 			'1.8.1',
    428 			true
    429 		);
    430 
    431 		wp_register_script(
    432 			'elementor-dialog',
    433 			$this->get_js_assets_url( 'dialog', 'assets/lib/dialog/' ),
    434 			[
    435 				'jquery-ui-position',
    436 			],
    437 			'4.8.1',
    438 			true
    439 		);
    440 
    441 		wp_register_script(
    442 			'elementor-gallery',
    443 			$this->get_js_assets_url( 'e-gallery', 'assets/lib/e-gallery/js/' ),
    444 			[
    445 				'jquery',
    446 			],
    447 			'1.2.0',
    448 			true
    449 		);
    450 
    451 		wp_register_script(
    452 			'share-link',
    453 			$this->get_js_assets_url( 'share-link', 'assets/lib/share-link/' ),
    454 			[
    455 				'jquery',
    456 			],
    457 			ELEMENTOR_VERSION,
    458 			true
    459 		);
    460 
    461 		wp_register_script(
    462 			'elementor-frontend',
    463 			$this->get_js_assets_url( 'frontend' ),
    464 			$this->get_elementor_frontend_dependencies(),
    465 			ELEMENTOR_VERSION,
    466 			true
    467 		);
    468 
    469 		/**
    470 		 * After frontend register scripts.
    471 		 *
    472 		 * Fires after Elementor frontend scripts are registered.
    473 		 *
    474 		 * @since 1.2.1
    475 		 */
    476 		do_action( 'elementor/frontend/after_register_scripts' );
    477 	}
    478 
    479 	/**
    480 	 * Registers styles.
    481 	 *
    482 	 * Registers all the frontend styles.
    483 	 *
    484 	 * Fired by `wp_enqueue_scripts` action.
    485 	 *
    486 	 * @since 1.2.0
    487 	 * @access public
    488 	 */
    489 	public function register_styles() {
    490 		/**
    491 		 * Before frontend register styles.
    492 		 *
    493 		 * Fires before Elementor frontend styles are registered.
    494 		 *
    495 		 * @since 1.2.0
    496 		 */
    497 		do_action( 'elementor/frontend/before_register_styles' );
    498 
    499 		wp_register_style(
    500 			'font-awesome',
    501 			$this->get_css_assets_url( 'font-awesome', 'assets/lib/font-awesome/css/' ),
    502 			[],
    503 			'4.7.0'
    504 		);
    505 
    506 		wp_register_style(
    507 			'elementor-icons',
    508 			$this->get_css_assets_url( 'elementor-icons', 'assets/lib/eicons/css/' ),
    509 			[],
    510 			'5.12.0'
    511 		);
    512 
    513 		wp_register_style(
    514 			'flatpickr',
    515 			$this->get_css_assets_url( 'flatpickr', 'assets/lib/flatpickr/' ),
    516 			[],
    517 			'4.1.4'
    518 		);
    519 
    520 		wp_register_style(
    521 			'elementor-gallery',
    522 			$this->get_css_assets_url( 'e-gallery', 'assets/lib/e-gallery/css/' ),
    523 			[],
    524 			'1.2.0'
    525 		);
    526 
    527 		$min_suffix = Utils::is_script_debug() ? '' : '.min';
    528 
    529 		$direction_suffix = is_rtl() ? '-rtl' : '';
    530 
    531 		$frontend_base_file_name = $this->is_optimized_css_mode() ? 'frontend-lite' : 'frontend';
    532 
    533 		$frontend_file_name = $frontend_base_file_name . $direction_suffix . $min_suffix . '.css';
    534 
    535 		$has_custom_file = Plugin::$instance->breakpoints->has_custom_breakpoints();
    536 
    537 		if ( $has_custom_file ) {
    538 			$frontend_file = new FrontendFile( 'custom-' . $frontend_file_name, Breakpoints_Manager::get_stylesheet_templates_path() . $frontend_file_name );
    539 
    540 			$time = $frontend_file->get_meta( 'time' );
    541 
    542 			if ( ! $time ) {
    543 				$frontend_file->update();
    544 			}
    545 
    546 			$frontend_file_url = $frontend_file->get_url();
    547 		} else {
    548 			$frontend_file_url = ELEMENTOR_ASSETS_URL . 'css/' . $frontend_file_name;
    549 		}
    550 
    551 		$frontend_dependencies = [];
    552 
    553 		if ( ! Plugin::$instance->experiments->is_feature_active( 'e_dom_optimization' ) ) {
    554 			// If The Dom Optimization feature is disabled, register the legacy CSS
    555 			wp_register_style(
    556 				'elementor-frontend-legacy',
    557 				ELEMENTOR_ASSETS_URL . 'css/frontend-legacy' . $direction_suffix . $min_suffix . '.css',
    558 				[],
    559 				ELEMENTOR_VERSION
    560 			);
    561 
    562 			$frontend_dependencies[] = 'elementor-frontend-legacy';
    563 		}
    564 
    565 		wp_register_style(
    566 			'elementor-frontend',
    567 			$frontend_file_url,
    568 			$frontend_dependencies,
    569 			$has_custom_file ? null : ELEMENTOR_VERSION
    570 		);
    571 
    572 		/**
    573 		 * After frontend register styles.
    574 		 *
    575 		 * Fires after Elementor frontend styles are registered.
    576 		 *
    577 		 * @since 1.2.0
    578 		 */
    579 		do_action( 'elementor/frontend/after_register_styles' );
    580 	}
    581 
    582 	/**
    583 	 * Enqueue scripts.
    584 	 *
    585 	 * Enqueue all the frontend scripts.
    586 	 *
    587 	 * @since 1.0.0
    588 	 * @access public
    589 	 */
    590 	public function enqueue_scripts() {
    591 		/**
    592 		 * Before frontend enqueue scripts.
    593 		 *
    594 		 * Fires before Elementor frontend scripts are enqueued.
    595 		 *
    596 		 * @since 1.0.0
    597 		 */
    598 		do_action( 'elementor/frontend/before_enqueue_scripts' );
    599 
    600 		wp_enqueue_script( 'elementor-frontend' );
    601 
    602 		if ( ! $this->is_improved_assets_loading() ) {
    603 			wp_enqueue_script(
    604 				'preloaded-modules',
    605 				$this->get_js_assets_url( 'preloaded-modules', 'assets/js/' ),
    606 				[
    607 					'elementor-frontend',
    608 				],
    609 				ELEMENTOR_VERSION,
    610 				true
    611 			);
    612 		}
    613 
    614 		$this->print_config();
    615 
    616 		$this->enqueue_conditional_assets();
    617 
    618 		/**
    619 		 * After frontend enqueue scripts.
    620 		 *
    621 		 * Fires after Elementor frontend scripts are enqueued.
    622 		 *
    623 		 * @since 1.0.0
    624 		 */
    625 		do_action( 'elementor/frontend/after_enqueue_scripts' );
    626 	}
    627 
    628 	/**
    629 	 * Enqueue styles.
    630 	 *
    631 	 * Enqueue all the frontend styles.
    632 	 *
    633 	 * Fired by `wp_enqueue_scripts` action.
    634 	 *
    635 	 * @since 1.0.0
    636 	 * @access public
    637 	 */
    638 	public function enqueue_styles() {
    639 		static $is_enqueue_styles_already_triggered;
    640 
    641 		if ( ! $is_enqueue_styles_already_triggered ) {
    642 			$is_enqueue_styles_already_triggered = true;
    643 
    644 			/**
    645 			 * Before frontend styles enqueued.
    646 			 *
    647 			 * Fires before Elementor frontend styles are enqueued.
    648 			 *
    649 			 * @since 1.0.0
    650 			 */
    651 			do_action( 'elementor/frontend/before_enqueue_styles' );
    652 
    653 			$this->add_elementor_icons_inline_css();
    654 
    655 			// The e-icons are needed in preview mode for the editor icons (plus-icon for new section, folder-icon for the templates library etc.).
    656 			if ( ! $this->is_improved_assets_loading() || Plugin::$instance->preview->is_preview_mode() ) {
    657 				wp_enqueue_style( 'elementor-icons' );
    658 			}
    659 
    660 			wp_enqueue_style( 'elementor-frontend' );
    661 
    662 			/**
    663 			 * After frontend styles enqueued.
    664 			 *
    665 			 * Fires after Elementor frontend styles are enqueued.
    666 			 *
    667 			 * @since 1.0.0
    668 			 */
    669 			do_action( 'elementor/frontend/after_enqueue_styles' );
    670 
    671 			if ( ! Plugin::$instance->preview->is_preview_mode() ) {
    672 				$this->parse_global_css_code();
    673 
    674 				$post_id = get_the_ID();
    675 				// Check $post_id for virtual pages. check is singular because the $post_id is set to the first post on archive pages.
    676 				if ( $post_id && is_singular() ) {
    677 					$css_file = Post_CSS::create( get_the_ID() );
    678 					$css_file->enqueue();
    679 				}
    680 			}
    681 		}
    682 	}
    683 
    684 	/**
    685 	 * Enqueue assets conditionally.
    686 	 *
    687 	 * Enqueue all assets that were pre-enabled.
    688 	 *
    689 	 * @since 3.3.0
    690 	 * @access private
    691 	 */
    692 	private function enqueue_conditional_assets() {
    693 		Plugin::$instance->assets_loader->enqueue_assets();
    694 	}
    695 
    696 	/**
    697 	 * Elementor footer scripts and styles.
    698 	 *
    699 	 * Handle styles and scripts that are not printed in the header.
    700 	 *
    701 	 * Fired by `wp_footer` action.
    702 	 *
    703 	 * @since 1.0.11
    704 	 * @access public
    705 	 */
    706 	public function wp_footer() {
    707 		if ( ! $this->_has_elementor_in_page ) {
    708 			return;
    709 		}
    710 
    711 		$this->enqueue_styles();
    712 		$this->enqueue_scripts();
    713 
    714 		$this->print_fonts_links();
    715 	}
    716 
    717 	/**
    718 	 * Print fonts links.
    719 	 *
    720 	 * Enqueue all the frontend fonts by url.
    721 	 *
    722 	 * Fired by `wp_head` action.
    723 	 *
    724 	 * @since 1.9.4
    725 	 * @access public
    726 	 */
    727 	public function print_fonts_links() {
    728 		$google_fonts = [
    729 			'google' => [],
    730 			'early' => [],
    731 		];
    732 
    733 		foreach ( $this->fonts_to_enqueue as $key => $font ) {
    734 			$font_type = Fonts::get_font_type( $font );
    735 
    736 			switch ( $font_type ) {
    737 				case Fonts::GOOGLE:
    738 					$google_fonts['google'][] = $font;
    739 					break;
    740 
    741 				case Fonts::EARLYACCESS:
    742 					$google_fonts['early'][] = $font;
    743 					break;
    744 
    745 				case false:
    746 					$this->maybe_enqueue_icon_font( $font );
    747 					break;
    748 				default:
    749 					/**
    750 					 * Print font links.
    751 					 *
    752 					 * Fires when Elementor frontend fonts are printed on the HEAD tag.
    753 					 *
    754 					 * The dynamic portion of the hook name, `$font_type`, refers to the font type.
    755 					 *
    756 					 * @since 2.0.0
    757 					 *
    758 					 * @param string $font Font name.
    759 					 */
    760 					do_action( "elementor/fonts/print_font_links/{$font_type}", $font );
    761 			}
    762 		}
    763 		$this->fonts_to_enqueue = [];
    764 
    765 		$this->enqueue_google_fonts( $google_fonts );
    766 		$this->enqueue_icon_fonts();
    767 	}
    768 
    769 	private function maybe_enqueue_icon_font( $icon_font_type ) {
    770 		if ( ! Icons_Manager::is_migration_allowed() ) {
    771 			return;
    772 		}
    773 
    774 		$icons_types = Icons_Manager::get_icon_manager_tabs();
    775 		if ( ! isset( $icons_types[ $icon_font_type ] ) ) {
    776 			return;
    777 		}
    778 
    779 		$icon_type = $icons_types[ $icon_font_type ];
    780 		if ( isset( $icon_type['url'] ) ) {
    781 			$this->icon_fonts_to_enqueue[ $icon_font_type ] = [ $icon_type['url'] ];
    782 		}
    783 	}
    784 
    785 	private function enqueue_icon_fonts() {
    786 		if ( empty( $this->icon_fonts_to_enqueue ) || ! Icons_Manager::is_migration_allowed() ) {
    787 			return;
    788 		}
    789 
    790 		foreach ( $this->icon_fonts_to_enqueue as $icon_type => $css_url ) {
    791 			wp_enqueue_style( 'elementor-icons-' . $icon_type );
    792 			$this->enqueued_icon_fonts[] = $css_url;
    793 		}
    794 
    795 		//clear enqueued icons
    796 		$this->icon_fonts_to_enqueue = [];
    797 	}
    798 
    799 	/**
    800 	 * Print Google fonts.
    801 	 *
    802 	 * Enqueue all the frontend Google fonts.
    803 	 *
    804 	 * Fired by `wp_head` action.
    805 	 *
    806 	 * @since 1.0.0
    807 	 * @access private
    808 	 *
    809 	 * @param array $google_fonts Optional. Google fonts to print in the frontend.
    810 	 *                            Default is an empty array.
    811 	 */
    812 	private function enqueue_google_fonts( $google_fonts = [] ) {
    813 		static $google_fonts_index = 0;
    814 
    815 		$print_google_fonts = true;
    816 
    817 		/**
    818 		 * Print frontend google fonts.
    819 		 *
    820 		 * Filters whether to enqueue Google fonts in the frontend.
    821 		 *
    822 		 * @since 1.0.0
    823 		 *
    824 		 * @param bool $print_google_fonts Whether to enqueue Google fonts. Default is true.
    825 		 */
    826 		$print_google_fonts = apply_filters( 'elementor/frontend/print_google_fonts', $print_google_fonts );
    827 
    828 		if ( ! $print_google_fonts ) {
    829 			return;
    830 		}
    831 
    832 		// Print used fonts
    833 		if ( ! empty( $google_fonts['google'] ) ) {
    834 			$google_fonts_index++;
    835 
    836 			foreach ( $google_fonts['google'] as &$font ) {
    837 				$font = str_replace( ' ', '+', $font ) . ':100,100italic,200,200italic,300,300italic,400,400italic,500,500italic,600,600italic,700,700italic,800,800italic,900,900italic';
    838 			}
    839 
    840 			// Defining a font-display type to google fonts.
    841 			$font_display_url_str = '&display=' . Fonts::get_font_display_setting();
    842 
    843 			$fonts_url = sprintf( 'https://fonts.googleapis.com/css?family=%1$s%2$s', implode( rawurlencode( '|' ), $google_fonts['google'] ), $font_display_url_str );
    844 
    845 			$subsets = [
    846 				'ru_RU' => 'cyrillic',
    847 				'bg_BG' => 'cyrillic',
    848 				'he_IL' => 'hebrew',
    849 				'el' => 'greek',
    850 				'vi' => 'vietnamese',
    851 				'uk' => 'cyrillic',
    852 				'cs_CZ' => 'latin-ext',
    853 				'ro_RO' => 'latin-ext',
    854 				'pl_PL' => 'latin-ext',
    855 				'hr_HR' => 'latin-ext',
    856 				'hu_HU' => 'latin-ext',
    857 				'sk_SK' => 'latin-ext',
    858 				'tr_TR' => 'latin-ext',
    859 				'lt_LT' => 'latin-ext',
    860 			];
    861 
    862 			/**
    863 			 * Google font subsets.
    864 			 *
    865 			 * Filters the list of Google font subsets from which locale will be enqueued in frontend.
    866 			 *
    867 			 * @since 1.0.0
    868 			 *
    869 			 * @param array $subsets A list of font subsets.
    870 			 */
    871 			$subsets = apply_filters( 'elementor/frontend/google_font_subsets', $subsets );
    872 
    873 			$locale = get_locale();
    874 
    875 			if ( isset( $subsets[ $locale ] ) ) {
    876 				$fonts_url .= '&subset=' . $subsets[ $locale ];
    877 			}
    878 
    879 			wp_enqueue_style( 'google-fonts-' . $google_fonts_index, $fonts_url ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
    880 		}
    881 
    882 		if ( ! empty( $google_fonts['early'] ) ) {
    883 			foreach ( $google_fonts['early'] as $current_font ) {
    884 				$google_fonts_index++;
    885 
    886 				//printf( '<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/earlyaccess/%s.css">', strtolower( str_replace( ' ', '', $current_font ) ) );
    887 
    888 				$font_url = sprintf( 'https://fonts.googleapis.com/earlyaccess/%s.css', strtolower( str_replace( ' ', '', $current_font ) ) );
    889 
    890 				wp_enqueue_style( 'google-earlyaccess-' . $google_fonts_index, $font_url ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
    891 			}
    892 		}
    893 
    894 	}
    895 
    896 	/**
    897 	 * Enqueue fonts.
    898 	 *
    899 	 * Enqueue all the frontend fonts.
    900 	 *
    901 	 * @since 1.2.0
    902 	 * @access public
    903 	 *
    904 	 * @param array $font Fonts to enqueue in the frontend.
    905 	 */
    906 	public function enqueue_font( $font ) {
    907 		if ( in_array( $font, $this->registered_fonts ) ) {
    908 			return;
    909 		}
    910 
    911 		$this->fonts_to_enqueue[] = $font;
    912 		$this->registered_fonts[] = $font;
    913 	}
    914 
    915 	/**
    916 	 * Parse global CSS.
    917 	 *
    918 	 * Enqueue the global CSS file.
    919 	 *
    920 	 * @since 1.2.0
    921 	 * @access protected
    922 	 */
    923 	protected function parse_global_css_code() {
    924 		$scheme_css_file = Global_CSS::create( 'global.css' );
    925 
    926 		$scheme_css_file->enqueue();
    927 	}
    928 
    929 	/**
    930 	 * Apply builder in content.
    931 	 *
    932 	 * Used to apply the Elementor page editor on the post content.
    933 	 *
    934 	 * @since 1.0.0
    935 	 * @access public
    936 	 *
    937 	 * @param string $content The post content.
    938 	 *
    939 	 * @return string The post content.
    940 	 */
    941 	public function apply_builder_in_content( $content ) {
    942 		$this->restore_content_filters();
    943 
    944 		if ( Plugin::$instance->preview->is_preview_mode() || $this->_is_excerpt ) {
    945 			return $content;
    946 		}
    947 
    948 		// Remove the filter itself in order to allow other `the_content` in the elements
    949 		$this->remove_content_filter();
    950 
    951 		$post_id = get_the_ID();
    952 		$builder_content = $this->get_builder_content( $post_id );
    953 
    954 		if ( ! empty( $builder_content ) ) {
    955 			$content = $builder_content;
    956 			$this->remove_content_filters();
    957 		}
    958 
    959 		// Add the filter again for other `the_content` calls
    960 		$this->add_content_filter();
    961 
    962 		return $content;
    963 	}
    964 
    965 	/**
    966 	 * Retrieve builder content.
    967 	 *
    968 	 * Used to render and return the post content with all the Elementor elements.
    969 	 *
    970 	 * Note that this method is an internal method, please use `get_builder_content_for_display()`.
    971 	 *
    972 	 * @since 1.0.0
    973 	 * @access public
    974 	 *
    975 	 * @param int  $post_id  The post ID.
    976 	 * @param bool $with_css Optional. Whether to retrieve the content with CSS
    977 	 *                       or not. Default is false.
    978 	 *
    979 	 * @return string The post content.
    980 	 */
    981 	public function get_builder_content( $post_id, $with_css = false ) {
    982 		if ( post_password_required( $post_id ) ) {
    983 			return '';
    984 		}
    985 
    986 		$document = Plugin::$instance->documents->get_doc_for_frontend( $post_id );
    987 
    988 		if ( ! $document || ! $document->is_built_with_elementor() ) {
    989 			return '';
    990 		}
    991 
    992 		// Change the current post, so widgets can use `documents->get_current`.
    993 		Plugin::$instance->documents->switch_to_document( $document );
    994 
    995 		$data = $document->get_elements_data();
    996 
    997 		/**
    998 		 * Frontend builder content data.
    999 		 *
   1000 		 * Filters the builder content in the frontend.
   1001 		 *
   1002 		 * @since 1.0.0
   1003 		 *
   1004 		 * @param array $data    The builder content.
   1005 		 * @param int   $post_id The post ID.
   1006 		 */
   1007 		$data = apply_filters( 'elementor/frontend/builder_content_data', $data, $post_id );
   1008 
   1009 		do_action( 'elementor/frontend/before_get_builder_content', $document, $this->_is_excerpt );
   1010 
   1011 		if ( empty( $data ) ) {
   1012 			Plugin::$instance->documents->restore_document();
   1013 
   1014 			return '';
   1015 		}
   1016 
   1017 		if ( ! $this->_is_excerpt ) {
   1018 			if ( $document->is_autosave() ) {
   1019 				$css_file = Post_Preview::create( $document->get_post()->ID );
   1020 			} else {
   1021 				$css_file = Post_CSS::create( $post_id );
   1022 			}
   1023 
   1024 			$css_file->enqueue();
   1025 		}
   1026 
   1027 		ob_start();
   1028 
   1029 		// Handle JS and Customizer requests, with CSS inline.
   1030 		if ( is_customize_preview() || wp_doing_ajax() ) {
   1031 			$with_css = true;
   1032 		}
   1033 
   1034 		if ( ! empty( $css_file ) && $with_css ) {
   1035 			$css_file->print_css();
   1036 		}
   1037 
   1038 		$document->print_elements_with_wrapper( $data );
   1039 
   1040 		$content = ob_get_clean();
   1041 
   1042 		$content = $this->process_more_tag( $content );
   1043 
   1044 		/**
   1045 		 * Frontend content.
   1046 		 *
   1047 		 * Filters the content in the frontend.
   1048 		 *
   1049 		 * @since 1.0.0
   1050 		 *
   1051 		 * @param string $content The content.
   1052 		 */
   1053 		$content = apply_filters( 'elementor/frontend/the_content', $content );
   1054 
   1055 		if ( ! empty( $content ) ) {
   1056 			$this->_has_elementor_in_page = true;
   1057 		}
   1058 
   1059 		Plugin::$instance->documents->restore_document();
   1060 
   1061 		// BC
   1062 		// TODO: use Deprecation::do_deprecated_action() in 3.1.0
   1063 		do_action( 'elementor/frontend/get_builder_content', $document, $this->_is_excerpt, $with_css );
   1064 
   1065 		return $content;
   1066 	}
   1067 
   1068 	/**
   1069 	 * Retrieve builder content for display.
   1070 	 *
   1071 	 * Used to render and return the post content with all the Elementor elements.
   1072 	 *
   1073 	 * @since 1.0.0
   1074 	 * @access public
   1075 	 *
   1076 	 * @param int $post_id The post ID.
   1077 	 *
   1078 	 * @param bool $with_css Optional. Whether to retrieve the content with CSS
   1079 	 *                       or not. Default is false.
   1080 	 *
   1081 	 * @return string The post content.
   1082 	 */
   1083 	public function get_builder_content_for_display( $post_id, $with_css = false ) {
   1084 		if ( ! get_post( $post_id ) ) {
   1085 			return '';
   1086 		}
   1087 
   1088 		$editor = Plugin::$instance->editor;
   1089 
   1090 		// Avoid recursion
   1091 		if ( get_the_ID() === (int) $post_id ) {
   1092 			$content = '';
   1093 			if ( $editor->is_edit_mode() ) {
   1094 				$content = '<div class="elementor-alert elementor-alert-danger">' . esc_html__( 'Invalid Data: The Template ID cannot be the same as the currently edited template. Please choose a different one.', 'elementor' ) . '</div>';
   1095 			}
   1096 
   1097 			return $content;
   1098 		}
   1099 
   1100 		// Set edit mode as false, so don't render settings and etc. use the $is_edit_mode to indicate if we need the CSS inline
   1101 		$is_edit_mode = $editor->is_edit_mode();
   1102 		$editor->set_edit_mode( false );
   1103 
   1104 		$with_css = $with_css ? true : $is_edit_mode;
   1105 
   1106 		$content = $this->get_builder_content( $post_id, $with_css );
   1107 
   1108 		// Restore edit mode state
   1109 		Plugin::$instance->editor->set_edit_mode( $is_edit_mode );
   1110 
   1111 		return $content;
   1112 	}
   1113 
   1114 	/**
   1115 	 * Start excerpt flag.
   1116 	 *
   1117 	 * Flags when `the_excerpt` is called. Used to avoid enqueueing CSS in the excerpt.
   1118 	 *
   1119 	 * @since 1.4.3
   1120 	 * @access public
   1121 	 *
   1122 	 * @param string $excerpt The post excerpt.
   1123 	 *
   1124 	 * @return string The post excerpt.
   1125 	 */
   1126 	public function start_excerpt_flag( $excerpt ) {
   1127 		$this->_is_excerpt = true;
   1128 		return $excerpt;
   1129 	}
   1130 
   1131 	/**
   1132 	 * End excerpt flag.
   1133 	 *
   1134 	 * Flags when `the_excerpt` call ended.
   1135 	 *
   1136 	 * @since 1.4.3
   1137 	 * @access public
   1138 	 *
   1139 	 * @param string $excerpt The post excerpt.
   1140 	 *
   1141 	 * @return string The post excerpt.
   1142 	 */
   1143 	public function end_excerpt_flag( $excerpt ) {
   1144 		$this->_is_excerpt = false;
   1145 		return $excerpt;
   1146 	}
   1147 
   1148 	/**
   1149 	 * Remove content filters.
   1150 	 *
   1151 	 * Remove WordPress default filters that conflicted with Elementor.
   1152 	 *
   1153 	 * @since 1.5.0
   1154 	 * @access public
   1155 	 */
   1156 	public function remove_content_filters() {
   1157 		$filters = [
   1158 			'wpautop',
   1159 			'shortcode_unautop',
   1160 			'wptexturize',
   1161 		];
   1162 
   1163 		foreach ( $filters as $filter ) {
   1164 			// Check if another plugin/theme do not already removed the filter.
   1165 			if ( has_filter( 'the_content', $filter ) ) {
   1166 				remove_filter( 'the_content', $filter );
   1167 				$this->content_removed_filters[] = $filter;
   1168 			}
   1169 		}
   1170 	}
   1171 
   1172 	/**
   1173 	 * Has Elementor In Page
   1174 	 *
   1175 	 * Determine whether the current page is using Elementor.
   1176 	 *
   1177 	 * @since 2.0.9
   1178 	 *
   1179 	 * @access public
   1180 	 * @return bool
   1181 	 */
   1182 	public function has_elementor_in_page() {
   1183 		return $this->_has_elementor_in_page;
   1184 	}
   1185 
   1186 	public function create_action_hash( $action, array $settings = [] ) {
   1187 		return '#' . rawurlencode( sprintf( 'elementor-action:action=%1$s&settings=%2$s', $action, base64_encode( wp_json_encode( $settings ) ) ) );
   1188 	}
   1189 
   1190 	/**
   1191 	 * Is the current render mode is static.
   1192 	 *
   1193 	 * @return bool
   1194 	 */
   1195 	public function is_static_render_mode() {
   1196 		// The render mode manager is exists only in frontend,
   1197 		// so by default if it is not exist the method will return false.
   1198 		if ( ! $this->render_mode_manager ) {
   1199 			return false;
   1200 		}
   1201 
   1202 		return $this->render_mode_manager->get_current()->is_static();
   1203 	}
   1204 
   1205 	/**
   1206 	 * Get Init Settings
   1207 	 *
   1208 	 * Used to define the default/initial settings of the object. Inheriting classes may implement this method to define
   1209 	 * their own default/initial settings.
   1210 	 *
   1211 	 * @since 2.3.0
   1212 	 *
   1213 	 * @access protected
   1214 	 * @return array
   1215 	 */
   1216 	protected function get_init_settings() {
   1217 		$is_preview_mode = Plugin::$instance->preview->is_preview_mode( Plugin::$instance->preview->get_post_id() );
   1218 
   1219 		$active_experimental_features = Plugin::$instance->experiments->get_active_features();
   1220 
   1221 		$active_experimental_features = array_fill_keys( array_keys( $active_experimental_features ), true );
   1222 
   1223 		$assets_url = ELEMENTOR_ASSETS_URL;
   1224 
   1225 		/**
   1226 		 * Frontend assets URL
   1227 		 *
   1228 		 * Filters Elementor frontend assets URL.
   1229 		 *
   1230 		 * @since 2.3.0
   1231 		 *
   1232 		 * @param string $assets_url The frontend assets URL. Default is ELEMENTOR_ASSETS_URL.
   1233 		 */
   1234 		$assets_url = apply_filters( 'elementor/frontend/assets_url', $assets_url );
   1235 
   1236 		$settings = [
   1237 			'environmentMode' => [
   1238 				'edit' => $is_preview_mode,
   1239 				'wpPreview' => is_preview(),
   1240 				'isScriptDebug' => Utils::is_script_debug(),
   1241 			],
   1242 			'i18n' => [
   1243 				'shareOnFacebook' => esc_html__( 'Share on Facebook', 'elementor' ),
   1244 				'shareOnTwitter' => esc_html__( 'Share on Twitter', 'elementor' ),
   1245 				'pinIt' => esc_html__( 'Pin it', 'elementor' ),
   1246 				'download' => esc_html__( 'Download', 'elementor' ),
   1247 				'downloadImage' => esc_html__( 'Download image', 'elementor' ),
   1248 				'fullscreen' => esc_html__( 'Fullscreen', 'elementor' ),
   1249 				'zoom' => esc_html__( 'Zoom', 'elementor' ),
   1250 				'share' => esc_html__( 'Share', 'elementor' ),
   1251 				'playVideo' => esc_html__( 'Play Video', 'elementor' ),
   1252 				'previous' => esc_html__( 'Previous', 'elementor' ),
   1253 				'next' => esc_html__( 'Next', 'elementor' ),
   1254 				'close' => esc_html__( 'Close', 'elementor' ),
   1255 			],
   1256 			'is_rtl' => is_rtl(),
   1257 			// 'breakpoints' object is kept for BC.
   1258 			'breakpoints' => Responsive::get_breakpoints(),
   1259 			// 'responsive' contains the custom breakpoints config introduced in Elementor v3.2.0
   1260 			'responsive' => [
   1261 				'breakpoints' => Plugin::$instance->breakpoints->get_breakpoints_config(),
   1262 			],
   1263 			'version' => ELEMENTOR_VERSION,
   1264 			'is_static' => $this->is_static_render_mode(),
   1265 			'experimentalFeatures' => $active_experimental_features,
   1266 			'urls' => [
   1267 				'assets' => $assets_url,
   1268 			],
   1269 		];
   1270 
   1271 		$settings['settings'] = SettingsManager::get_settings_frontend_config();
   1272 
   1273 		$kit = Plugin::$instance->kits_manager->get_active_kit_for_frontend();
   1274 		$settings['kit'] = $kit->get_frontend_settings();
   1275 
   1276 		if ( is_singular() ) {
   1277 			$post = get_post();
   1278 
   1279 			$title = Utils::urlencode_html_entities( wp_get_document_title() );
   1280 
   1281 			// Try to use the 'large' WP image size because the Pinterest share API
   1282 			// has problems accepting shares with large images sometimes, and the WP 'large' thumbnail is
   1283 			// the largest default WP image size that will probably not be changed in most sites
   1284 			$featured_image_url = get_the_post_thumbnail_url( null, 'large' );
   1285 
   1286 			// If the large size was nullified, use the full size which cannot be nullified/deleted
   1287 			if ( ! $featured_image_url ) {
   1288 				$featured_image_url = get_the_post_thumbnail_url( null, 'full' );
   1289 			}
   1290 
   1291 			$settings['post'] = [
   1292 				'id' => $post->ID,
   1293 				'title' => $title,
   1294 				'excerpt' => $post->post_excerpt,
   1295 				'featuredImage' => $featured_image_url,
   1296 			];
   1297 		} else {
   1298 			$settings['post'] = [
   1299 				'id' => 0,
   1300 				'title' => wp_get_document_title(),
   1301 				'excerpt' => get_the_archive_description(),
   1302 			];
   1303 		}
   1304 
   1305 		$empty_object = (object) [];
   1306 
   1307 		if ( $is_preview_mode ) {
   1308 			$settings['elements'] = [
   1309 				'data' => $empty_object,
   1310 				'editSettings' => $empty_object,
   1311 				'keys' => $empty_object,
   1312 			];
   1313 		}
   1314 
   1315 		if ( is_user_logged_in() ) {
   1316 			$user = wp_get_current_user();
   1317 
   1318 			if ( ! empty( $user->roles ) ) {
   1319 				$settings['user'] = [
   1320 					'roles' => $user->roles,
   1321 				];
   1322 			}
   1323 		}
   1324 
   1325 		return $settings;
   1326 	}
   1327 
   1328 	/**
   1329 	 * Restore content filters.
   1330 	 *
   1331 	 * Restore removed WordPress filters that conflicted with Elementor.
   1332 	 *
   1333 	 * @since 1.5.0
   1334 	 * @access public
   1335 	 */
   1336 	public function restore_content_filters() {
   1337 		foreach ( $this->content_removed_filters as $filter ) {
   1338 			add_filter( 'the_content', $filter );
   1339 		}
   1340 
   1341 		$this->content_removed_filters = [];
   1342 	}
   1343 
   1344 	/**
   1345 	 * Process More Tag
   1346 	 *
   1347 	 * Respect the native WP (<!--more-->) tag
   1348 	 *
   1349 	 * @access private
   1350 	 * @since 2.0.4
   1351 	 *
   1352 	 * @param $content
   1353 	 *
   1354 	 * @return string
   1355 	 */
   1356 	private function process_more_tag( $content ) {
   1357 		$post = get_post();
   1358 		$content = str_replace( '&lt;!--more--&gt;', '<!--more-->', $content );
   1359 		$parts = get_extended( $content );
   1360 		if ( empty( $parts['extended'] ) ) {
   1361 			return $content;
   1362 		}
   1363 
   1364 		if ( is_singular() ) {
   1365 			return $parts['main'] . '<div id="more-' . $post->ID . '"></div>' . $parts['extended'];
   1366 		}
   1367 
   1368 		if ( empty( $parts['more_text'] ) ) {
   1369 			$parts['more_text'] = esc_html__( '(more&hellip;)', 'elementor' );
   1370 		}
   1371 
   1372 		$more_link_text = sprintf(
   1373 			'<span aria-label="%1$s">%2$s</span>',
   1374 			sprintf(
   1375 				/* translators: %s: Name of current post */
   1376 				__( 'Continue reading %s', 'elementor' ),
   1377 				the_title_attribute( [
   1378 					'echo' => false,
   1379 				] )
   1380 			),
   1381 			$parts['more_text']
   1382 		);
   1383 
   1384 		$more_link = sprintf( ' <a href="%s#more-%s" class="more-link elementor-more-link">%s</a>', get_permalink(), $post->ID, $more_link_text );
   1385 
   1386 		/**
   1387 		 * The content "more" link.
   1388 		 *
   1389 		 * Filters the "more" link displayed after the content.
   1390 		 *
   1391 		 * This hook can be used either to change the link syntax or to change the
   1392 		 * text inside the link.
   1393 		 *
   1394 		 * @since 2.0.4
   1395 		 *
   1396 		 * @param string $more_link      The more link.
   1397 		 * @param string $more_link_text The text inside the more link.
   1398 		 */
   1399 		$more_link = apply_filters( 'the_content_more_link', $more_link, $more_link_text );
   1400 
   1401 		return force_balance_tags( $parts['main'] ) . $more_link;
   1402 	}
   1403 
   1404 	private function is_improved_assets_loading() {
   1405 		return Plugin::$instance->experiments->is_feature_active( 'e_optimized_assets_loading' );
   1406 	}
   1407 
   1408 	private function get_elementor_frontend_dependencies() {
   1409 		$dependencies = [
   1410 			'elementor-frontend-modules',
   1411 			'elementor-waypoints',
   1412 			'jquery-ui-position',
   1413 		];
   1414 
   1415 		if ( ! $this->is_improved_assets_loading() ) {
   1416 			wp_register_script(
   1417 				'swiper',
   1418 				$this->get_js_assets_url( 'swiper', 'assets/lib/swiper/' ),
   1419 				[],
   1420 				'5.3.6',
   1421 				true
   1422 			);
   1423 
   1424 			$dependencies[] = 'swiper';
   1425 			$dependencies[] = 'share-link';
   1426 			$dependencies[] = 'elementor-dialog';
   1427 		}
   1428 
   1429 		return $dependencies;
   1430 	}
   1431 
   1432 	private function is_optimized_css_mode() {
   1433 		$is_optimized_css_loading = Plugin::$instance->experiments->is_feature_active( 'e_optimized_css_loading' );
   1434 
   1435 		return ! Utils::is_script_debug() && $is_optimized_css_loading && ! Plugin::$instance->preview->is_preview_mode();
   1436 	}
   1437 
   1438 	private function add_elementor_icons_inline_css() {
   1439 		$elementor_icons_library_version = '5.10.0';
   1440 
   1441 		/**
   1442 		 * The e-icons font-face must be printed inline due to custom breakpoints.
   1443 		 * When using custom breakpoints, the frontend CSS is loaded from the custom-frontend CSS file.
   1444 		 * The custom frontend file is located in a different path ('uploads' folder).
   1445 		 * Therefore, it cannot be called from a CSS file that its relative path can vary.
   1446 		 */
   1447 		$elementor_icons_inline_css = sprintf( '@font-face{font-family:eicons;src:url(%1$slib/eicons/fonts/eicons.eot?%2$s);src:url(%1$slib/eicons/fonts/eicons.eot?%2$s#iefix) format("embedded-opentype"),url(%1$slib/eicons/fonts/eicons.woff2?%2$s) format("woff2"),url(%1$slib/eicons/fonts/eicons.woff?%2$s) format("woff"),url(%1$slib/eicons/fonts/eicons.ttf?%2$s) format("truetype"),url(%1$slib/eicons/fonts/eicons.svg?%2$s#eicon) format("svg");font-weight:400;font-style:normal}', ELEMENTOR_ASSETS_URL, $elementor_icons_library_version );
   1448 
   1449 		wp_add_inline_style( 'elementor-frontend', $elementor_icons_inline_css );
   1450 	}
   1451 }