use Elementor\Core\Base\Base_Object;
use Elementor\Core\DynamicTags\Manager;
use Elementor\Core\Breakpoints\Manager as Breakpoints_Manager;
use Elementor\Core\Frontend\Performance;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
* Elementor controls stack.
* An abstract class that provides the needed properties and methods to
* manage and handle controls in the editor panel to inheriting classes.
abstract class Controls_Stack extends Base_Object {
* Responsive 'desktop' device name.
const RESPONSIVE_DESKTOP = 'desktop';
* Responsive 'tablet' device name.
const RESPONSIVE_TABLET = 'tablet';
* Responsive 'mobile' device name.
const RESPONSIVE_MOBILE = 'mobile';
private $active_settings;
private $parsed_active_settings;
* Parsed Dynamic Settings.
private $parsed_dynamic_settings;
* Holds all the raw data including the element type, the child elements,
* Holds the configuration used to generate the Elementor editor. It includes
* the element name, icon, categories, etc.
* The additional configuration.
* Holds additional configuration that has been set using `set_config` method.
* The `config` property is not modified directly while using the method because
* it's used to check whether the initial config already loaded (in `get_config`).
* After the initial config loaded, the additional config is merged into it.
private $additional_config = [];
* Holds the current section while inserting a set of controls sections.
private $current_section;
* Holds the current tab while inserting a set of controls tabs.
* Holds the current popover while inserting a set of controls.
private $current_popover;
* Holds the injection point in the stack where the control will be inserted.
private $injection_point;
private $settings_sanitized = false;
* Element render attributes.
* Holds all the render attributes of the element. Used to store data like
* the HTML class name and the class value, or HTML element ID name and value.
private $render_attributes = [];
* Retrieve the element name.
* @return string The name.
abstract public function get_name();
* Some classes need to use unique names, this method allows you to create
* them. By default it retrieves the regular name.
* @return string Unique name.
public function get_unique_name() {
return $this->get_name();
* Retrieve the element generic ID.
public function get_id() {
* Retrieve the element generic ID as integer.
* @return string The converted ID.
public function get_id_int() {
/** We ignore possible notices, in order to support elements created prior to v1.8.0 and might include
* non-base 16 characters as part of their ID.
return @hexdec( $this->id );
* Get the first three numbers of the element converted ID.
* @return string The widget number.
public function get_widget_number(): string {
return substr( $this->get_id_int(), 0, 3 );
* Retrieve the type, e.g. 'stack', 'section', 'widget' etc.
* @return string The type.
public static function get_type() {
public function is_editable() {
* When inserting new controls, this method will retrieve the current section.
* @return null|array Current section.
public function get_current_section() {
return $this->current_section;
* When inserting new controls, this method will retrieve the current tab.
* @return null|array Current tab.
public function get_current_tab() {
return $this->current_tab;
* Retrieve all the controls or, when requested, a specific control.
* @param string $control_id The ID of the requested control. Optional field,
* when set it will return a specific control.
* @return mixed Controls list.
public function get_controls( $control_id = null ) {
$stack = $this->get_stack();
if ( null !== $control_id ) {
$control_data = self::get_items( $stack['controls'], $control_id );
if ( null === $control_data && ! empty( $stack['style_controls'] ) ) {
$control_data = self::get_items( $stack['style_controls'], $control_id );
$controls = $stack['controls'];
if ( Performance::is_use_style_controls() && ! empty( $stack['style_controls'] ) ) {
$controls += $stack['style_controls'];
return self::get_items( $controls, $control_id );
* Retrieve an array of active controls that meet the condition field.
* If specific controls was given as a parameter, retrieve active controls
* from that list, otherwise check for all the controls available.
* @since 2.0.9 Added the `controls` and the `settings` parameters.
* @param array $controls Optional. An array of controls. Default is null.
* @param array $settings Optional. Controls settings. Default is null.
* @return array Active controls.
public function get_active_controls( array $controls = null, array $settings = null ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.0.0' );
$controls = $this->get_controls();
$settings = $this->get_controls_settings();
$active_controls = array_reduce(
array_keys( $controls ), function( $active_controls, $control_key ) use ( $controls, $settings ) {
$control = $controls[ $control_key ];
if ( $this->is_control_visible( $control, $settings, $controls ) ) {
$active_controls[ $control_key ] = $control;
* Retrieve the settings for all the controls that represent them.
* @return array Controls settings.
public function get_controls_settings() {
return array_intersect_key( $this->get_settings(), $this->get_controls() );
* Add new control to stack.
* Register a single control to allow the user to set/update data.
* This method should be used inside `register_controls()`.
* @param string $id Control ID.
* @param array $args Control arguments.
* @param array $options Optional. Control options. Default is an empty array.
* @return bool True if control added, False otherwise.
public function add_control( $id, array $args, $options = [] ) {
if ( isset( $args['scheme'] ) ) {
'default' => Plugin::$instance->kits_manager->convert_scheme_to_global( $args['scheme'] ),
unset( $args['scheme'] );
$options = array_merge( $default_options, $options );
if ( $options['position'] ) {
$this->start_injection( $options['position'] );
if ( $this->injection_point ) {
$options['index'] = $this->injection_point['index']++;
if ( empty( $args['type'] ) || ! in_array( $args['type'], [ Controls_Manager::SECTION, Controls_Manager::WP_WIDGET ], true ) ) {
$args = $this->handle_control_position( $args, $id, $options['overwrite'] );
if ( $options['position'] ) {
unset( $options['position'] );
if ( $this->current_popover ) {
if ( ! $this->current_popover['initialized'] ) {
$args['popover']['start'] = true;
$this->current_popover['initialized'] = true;
if ( Performance::should_optimize_controls() ) {
Controls_Manager::RAW_HTML,
Controls_Manager::DIVIDER,
Controls_Manager::HEADING,
Controls_Manager::BUTTON,
Controls_Manager::NOTICE,
Controls_Manager::DEPRECATED_NOTICE,
if ( ! empty( $args['type'] ) && ! empty( $args['section'] ) && in_array( $args['type'], $ui_controls ) ) {
'section' => $args['section'],
$args['editor_available'],
return Plugin::$instance->controls_manager->add_control_to_stack( $this, $id, $args, $options );
* Remove control from stack.