use Elementor\Core\Base\App;
use Elementor\Core\Base\Elements_Iteration_Actions\Assets;
use Elementor\Core\Files\Fonts\Google_Font;
use Elementor\Core\Frontend\Render_Mode_Manager;
use Elementor\Core\Responsive\Files\Frontend as FrontendFile;
use Elementor\Core\Files\CSS\Post as Post_CSS;
use Elementor\Core\Files\CSS\Post_Preview;
use Elementor\Core\Responsive\Responsive;
use Elementor\Core\Settings\Manager as SettingsManager;
use Elementor\Core\Breakpoints\Manager as Breakpoints_Manager;
use Elementor\Modules\FloatingButtons\Module;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
* Elementor frontend handler class is responsible for initializing Elementor in
class Frontend extends App {
* The priority of the content filter.
const THE_CONTENT_FILTER_PRIORITY = 9;
* The priority of the frontend enqueued styles.
const ENQUEUED_STYLES_PRIORITY = 20;
* Holds the ID of the current post.
* Holds the list of fonts that are being used in the current page.
* @var array Used fonts. Default is an empty array.
public $fonts_to_enqueue = [];
* Holds the class that respond to manage the render mode.
* @var Render_Mode_Manager
public $render_mode_manager;
* Holds the list of enqueued fonts in the current page.
* @var array Registered fonts. Default is an empty array.
private $registered_fonts = [];
* Holds the list of Icon fonts that are being used in the current page.
* @var array Used icon fonts. Default is an empty array.
private $icon_fonts_to_enqueue = [];
* Holds the list of Icon fonts already enqueued in the current page.
* @var array enqueued icon fonts. Default is an empty array.
private $enqueued_icon_fonts = [];
* Whether the page is using Elementor.
* Used to determine whether the current page is using Elementor.
* @var bool Whether Elementor is being used. Default is false.
private $_has_elementor_in_page = false;
* Whether the excerpt is being called.
* Used to determine whether the call to `the_content()` came from `get_the_excerpt()`.
* @var bool Whether the excerpt is being used. Default is false.
private $_is_excerpt = false;
* Filters removed from the content.
* Hold the list of filters removed from `the_content()`. Used to hold the filters that
* conflicted with Elementor while Elementor process the content.
* @var array Filters removed from the content. Default is an empty array.
private $content_removed_filters = [];
private $body_classes = [
* Initializing Elementor front end. Make sure we are not in admin, not and
* redirect from old URL structure of Elementor editor.
public function __construct() {
// We don't need this class in admin side, but in AJAX requests.
if ( is_admin() && ! wp_doing_ajax() ) {
add_action( 'template_redirect', [ $this, 'init_render_mode' ], -1 /* Before admin bar. */ );
add_action( 'template_redirect', [ $this, 'init' ] );
add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ], 5 );
add_action( 'wp_enqueue_scripts', [ $this, 'register_styles' ], 5 );
$this->add_content_filter();
// Hack to avoid enqueue post CSS while it's a `the_excerpt` call.
add_filter( 'get_the_excerpt', [ $this, 'start_excerpt_flag' ], 1 );
add_filter( 'get_the_excerpt', [ $this, 'end_excerpt_flag' ], 20 );
* Retrieve the module name.
* @return string Module name.
public function get_name() {
* Init render mode manager.
public function init_render_mode() {
if ( Plugin::$instance->editor->is_edit_mode() ) {
$this->render_mode_manager = new Render_Mode_Manager();
* Initialize Elementor front end. Hooks the needed actions to run Elementor
* in the front end, including script and style registration.
* Fired by `template_redirect` action.
if ( Plugin::$instance->editor->is_edit_mode() ) {
add_filter( 'body_class', [ $this, 'body_class' ] );
if ( Plugin::$instance->preview->is_preview_mode() ) {
if ( current_user_can( 'manage_options' ) ) {
Plugin::$instance->init_common();
$this->post_id = get_the_ID();
$document = Plugin::$instance->documents->get( $this->post_id );
if ( is_singular() && $document && $document->is_built_with_elementor() ) {
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ], self::ENQUEUED_STYLES_PRIORITY );
// Priority 7 to allow google fonts in header template to load in <head> tag
add_action( 'wp_head', [ $this, 'print_fonts_links' ], 7 );
add_action( 'wp_head', [ $this, 'add_theme_color_meta_tag' ] );
add_action( 'wp_footer', [ $this, 'wp_footer' ] );
* @param string|array $class
public function add_body_class( $class ) {
if ( is_array( $class ) ) {
$this->body_classes = array_merge( $this->body_classes, $class );
$this->body_classes[] = $class;
* Add Theme Color Meta Tag
public function add_theme_color_meta_tag() {
$kit = Plugin::$instance->kits_manager->get_active_kit_for_frontend();
$mobile_theme_color = $kit->get_settings( 'mobile_browser_background' );
if ( ! empty( $mobile_theme_color ) ) {
<meta name="theme-color" content="<?php echo esc_attr( $mobile_theme_color ); ?>">
* Add new elementor classes to the body tag.
* Fired by `body_class` filter.
* @param array $classes Optional. One or more classes to add to the body tag class list.
* Default is an empty array.
* @return array Body tag classes.
public function body_class( $classes = [] ) {
$classes = array_merge( $classes, $this->body_classes );
$document = Plugin::$instance->documents->get( $id );
if ( is_singular() && $document && $document->is_built_with_elementor() ) {
$classes[] = 'elementor-page elementor-page-' . $id;
if ( Plugin::$instance->preview->is_preview_mode() ) {
$editor_preferences = SettingsManager::get_settings_managers( 'editorPreferences' );
$show_hidden_elements = $editor_preferences->get_model()->get_settings( 'show_hidden_elements' );
if ( 'yes' === $show_hidden_elements ) {
$classes[] = 'e-preview--show-hidden-elements';
* Remove plain content and render the content generated by Elementor.
public function add_content_filter() {
add_filter( 'the_content', [ $this, 'apply_builder_in_content' ], self::THE_CONTENT_FILTER_PRIORITY );
* When the Elementor generated content rendered, we remove the filter to prevent multiple
* accuracies. This way we make sure Elementor renders the content only once.
public function remove_content_filter() {
remove_filter( 'the_content', [ $this, 'apply_builder_in_content' ], self::THE_CONTENT_FILTER_PRIORITY );
* Registers all the frontend scripts.
* Fired by `wp_enqueue_scripts` action.
public function register_scripts() {
* Before frontend register scripts.
* Fires before Elementor frontend scripts are registered.
do_action( 'elementor/frontend/before_register_scripts' );
'elementor-webpack-runtime',
$this->get_js_assets_url( 'webpack.runtime', 'assets/js/' ),
'elementor-frontend-modules',
$this->get_js_assets_url( 'frontend-modules' ),
'elementor-webpack-runtime',
$this->get_js_assets_url( 'swiper', 'assets/lib/swiper/v8/' ),
$this->get_js_assets_url( 'flatpickr', 'assets/lib/flatpickr/' ),
$this->get_js_assets_url( 'imagesloaded', 'assets/lib/imagesloaded/' ),
$this->get_js_assets_url( 'jquery-numerator', 'assets/lib/jquery-numerator/' ),
$this->get_js_assets_url( 'dialog', 'assets/lib/dialog/' ),
$this->get_js_assets_url( 'e-gallery', 'assets/lib/e-gallery/js/' ),
$this->get_js_assets_url( 'share-link', 'assets/lib/share-link/' ),
$this->get_js_assets_url( 'frontend' ),
'elementor-frontend-modules',
* After frontend register scripts.
* Fires after Elementor frontend scripts are registered.
do_action( 'elementor/frontend/after_register_scripts' );
* Registers all the frontend styles.
* Fired by `wp_enqueue_scripts` action.
public function register_styles() {
$min_suffix = Utils::is_script_debug() ? '' : '.min';
$direction_suffix = is_rtl() ? '-rtl' : '';
$has_custom_breakpoints = Plugin::$instance->breakpoints->has_custom_breakpoints();
* Before frontend register styles.
* Fires before Elementor frontend styles are registered.
do_action( 'elementor/frontend/before_register_styles' );
$this->get_css_assets_url( 'font-awesome', 'assets/lib/font-awesome/css/' ),
$this->get_css_assets_url( 'elementor-icons', 'assets/lib/eicons/css/' ),
Icons_Manager::ELEMENTOR_ICONS_VERSION