balmet.com

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

controls.php (23122B)


      1 <?php
      2 namespace Elementor;
      3 
      4 if ( ! defined( 'ABSPATH' ) ) {
      5 	exit; // Exit if accessed directly.
      6 }
      7 
      8 /**
      9  * Elementor controls manager.
     10  *
     11  * Elementor controls manager handler class is responsible for registering and
     12  * initializing all the supported controls, both regular controls and the group
     13  * controls.
     14  *
     15  * @since 1.0.0
     16  */
     17 class Controls_Manager {
     18 
     19 	/**
     20 	 * Content tab.
     21 	 */
     22 	const TAB_CONTENT = 'content';
     23 
     24 	/**
     25 	 * Style tab.
     26 	 */
     27 	const TAB_STYLE = 'style';
     28 
     29 	/**
     30 	 * Advanced tab.
     31 	 */
     32 	const TAB_ADVANCED = 'advanced';
     33 
     34 	/**
     35 	 * Responsive tab.
     36 	 */
     37 	const TAB_RESPONSIVE = 'responsive';
     38 
     39 	/**
     40 	 * Layout tab.
     41 	 */
     42 	const TAB_LAYOUT = 'layout';
     43 
     44 	/**
     45 	 * Settings tab.
     46 	 */
     47 	const TAB_SETTINGS = 'settings';
     48 
     49 	/**
     50 	 * Text control.
     51 	 */
     52 	const TEXT = 'text';
     53 
     54 	/**
     55 	 * Number control.
     56 	 */
     57 	const NUMBER = 'number';
     58 
     59 	/**
     60 	 * Textarea control.
     61 	 */
     62 	const TEXTAREA = 'textarea';
     63 
     64 	/**
     65 	 * Select control.
     66 	 */
     67 	const SELECT = 'select';
     68 
     69 	/**
     70 	 * Switcher control.
     71 	 */
     72 	const SWITCHER = 'switcher';
     73 
     74 	/**
     75 	 * Button control.
     76 	 */
     77 	const BUTTON = 'button';
     78 
     79 	/**
     80 	 * Hidden control.
     81 	 */
     82 	const HIDDEN = 'hidden';
     83 
     84 	/**
     85 	 * Heading control.
     86 	 */
     87 	const HEADING = 'heading';
     88 
     89 	/**
     90 	 * Raw HTML control.
     91 	 */
     92 	const RAW_HTML = 'raw_html';
     93 
     94 	/**
     95 	 * Deprecated Notice control.
     96 	 */
     97 	const DEPRECATED_NOTICE = 'deprecated_notice';
     98 
     99 	/**
    100 	 * Popover Toggle control.
    101 	 */
    102 	const POPOVER_TOGGLE = 'popover_toggle';
    103 
    104 	/**
    105 	 * Section control.
    106 	 */
    107 	const SECTION = 'section';
    108 
    109 	/**
    110 	 * Tab control.
    111 	 */
    112 	const TAB = 'tab';
    113 
    114 	/**
    115 	 * Tabs control.
    116 	 */
    117 	const TABS = 'tabs';
    118 
    119 	/**
    120 	 * Divider control.
    121 	 */
    122 	const DIVIDER = 'divider';
    123 
    124 	/**
    125 	 * Color control.
    126 	 */
    127 	const COLOR = 'color';
    128 
    129 	/**
    130 	 * Media control.
    131 	 */
    132 	const MEDIA = 'media';
    133 
    134 	/**
    135 	 * Slider control.
    136 	 */
    137 	const SLIDER = 'slider';
    138 
    139 	/**
    140 	 * Dimensions control.
    141 	 */
    142 	const DIMENSIONS = 'dimensions';
    143 
    144 	/**
    145 	 * Choose control.
    146 	 */
    147 	const CHOOSE = 'choose';
    148 
    149 	/**
    150 	 * WYSIWYG control.
    151 	 */
    152 	const WYSIWYG = 'wysiwyg';
    153 
    154 	/**
    155 	 * Code control.
    156 	 */
    157 	const CODE = 'code';
    158 
    159 	/**
    160 	 * Font control.
    161 	 */
    162 	const FONT = 'font';
    163 
    164 	/**
    165 	 * Image dimensions control.
    166 	 */
    167 	const IMAGE_DIMENSIONS = 'image_dimensions';
    168 
    169 	/**
    170 	 * WordPress widget control.
    171 	 */
    172 	const WP_WIDGET = 'wp_widget';
    173 
    174 	/**
    175 	 * URL control.
    176 	 */
    177 	const URL = 'url';
    178 
    179 	/**
    180 	 * Repeater control.
    181 	 */
    182 	const REPEATER = 'repeater';
    183 
    184 	/**
    185 	 * Icon control.
    186 	 */
    187 	const ICON = 'icon';
    188 
    189 	/**
    190 	 * Icons control.
    191 	 */
    192 	const ICONS = 'icons';
    193 
    194 	/**
    195 	 * Gallery control.
    196 	 */
    197 	const GALLERY = 'gallery';
    198 
    199 	/**
    200 	 * Structure control.
    201 	 */
    202 	const STRUCTURE = 'structure';
    203 
    204 	/**
    205 	 * Select2 control.
    206 	 */
    207 	const SELECT2 = 'select2';
    208 
    209 	/**
    210 	 * Date/Time control.
    211 	 */
    212 	const DATE_TIME = 'date_time';
    213 
    214 	/**
    215 	 * Box shadow control.
    216 	 */
    217 	const BOX_SHADOW = 'box_shadow';
    218 
    219 	/**
    220 	 * Text shadow control.
    221 	 */
    222 	const TEXT_SHADOW = 'text_shadow';
    223 
    224 	/**
    225 	 * Entrance animation control.
    226 	 */
    227 	const ANIMATION = 'animation';
    228 
    229 	/**
    230 	 * Hover animation control.
    231 	 */
    232 	const HOVER_ANIMATION = 'hover_animation';
    233 
    234 	/**
    235 	 * Exit animation control.
    236 	 */
    237 	const EXIT_ANIMATION = 'exit_animation';
    238 
    239 	/**
    240 	 * Controls.
    241 	 *
    242 	 * Holds the list of all the controls. Default is `null`.
    243 	 *
    244 	 * @since 1.0.0
    245 	 * @access private
    246 	 *
    247 	 * @var Base_Control[]
    248 	 */
    249 	private $controls = null;
    250 
    251 	/**
    252 	 * Control groups.
    253 	 *
    254 	 * Holds the list of all the control groups. Default is an empty array.
    255 	 *
    256 	 * @since 1.0.0
    257 	 * @access private
    258 	 *
    259 	 * @var Group_Control_Base[]
    260 	 */
    261 	private $control_groups = [];
    262 
    263 	/**
    264 	 * Control stacks.
    265 	 *
    266 	 * Holds the list of all the control stacks. Default is an empty array.
    267 	 *
    268 	 * @since 1.0.0
    269 	 * @access private
    270 	 *
    271 	 * @var array
    272 	 */
    273 	private $stacks = [];
    274 
    275 	/**
    276 	 * Tabs.
    277 	 *
    278 	 * Holds the list of all the tabs.
    279 	 *
    280 	 * @since 1.0.0
    281 	 * @access private
    282 	 * @static
    283 	 *
    284 	 * @var array
    285 	 */
    286 	private static $tabs;
    287 
    288 	/**
    289 	 * Init tabs.
    290 	 *
    291 	 * Initialize control tabs.
    292 	 *
    293 	 * @since 1.6.0
    294 	 * @access private
    295 	 * @static
    296 	 */
    297 	private static function init_tabs() {
    298 		self::$tabs = [
    299 			self::TAB_CONTENT => esc_html__( 'Content', 'elementor' ),
    300 			self::TAB_STYLE => esc_html__( 'Style', 'elementor' ),
    301 			self::TAB_ADVANCED => esc_html__( 'Advanced', 'elementor' ),
    302 			self::TAB_RESPONSIVE => esc_html__( 'Responsive', 'elementor' ),
    303 			self::TAB_LAYOUT => esc_html__( 'Layout', 'elementor' ),
    304 			self::TAB_SETTINGS => esc_html__( 'Settings', 'elementor' ),
    305 		];
    306 	}
    307 
    308 	/**
    309 	 * Get tabs.
    310 	 *
    311 	 * Retrieve the tabs of the current control.
    312 	 *
    313 	 * @since 1.6.0
    314 	 * @access public
    315 	 * @static
    316 	 *
    317 	 * @return array Control tabs.
    318 	 */
    319 	public static function get_tabs() {
    320 		if ( ! self::$tabs ) {
    321 			self::init_tabs();
    322 		}
    323 
    324 		return self::$tabs;
    325 	}
    326 
    327 	/**
    328 	 * Add tab.
    329 	 *
    330 	 * This method adds a new tab to the current control.
    331 	 *
    332 	 * @since 1.6.0
    333 	 * @access public
    334 	 * @static
    335 	 *
    336 	 * @param string $tab_name  Tab name.
    337 	 * @param string $tab_label Tab label.
    338 	 */
    339 	public static function add_tab( $tab_name, $tab_label = '' ) {
    340 		if ( ! self::$tabs ) {
    341 			self::init_tabs();
    342 		}
    343 
    344 		if ( isset( self::$tabs[ $tab_name ] ) ) {
    345 			return;
    346 		}
    347 
    348 		self::$tabs[ $tab_name ] = $tab_label;
    349 	}
    350 
    351 	public static function get_groups_names() {
    352 		// Group name must use "-" instead of "_"
    353 		return [
    354 			'background',
    355 			'border',
    356 			'typography',
    357 			'image-size',
    358 			'box-shadow',
    359 			'css-filter',
    360 			'text-shadow',
    361 		];
    362 	}
    363 
    364 	public static function get_controls_names() {
    365 		return [
    366 			self::TEXT,
    367 			self::NUMBER,
    368 			self::TEXTAREA,
    369 			self::SELECT,
    370 			self::SWITCHER,
    371 
    372 			self::BUTTON,
    373 			self::HIDDEN,
    374 			self::HEADING,
    375 			self::RAW_HTML,
    376 			self::POPOVER_TOGGLE,
    377 			self::SECTION,
    378 			self::TAB,
    379 			self::TABS,
    380 			self::DIVIDER,
    381 			self::DEPRECATED_NOTICE,
    382 
    383 			self::COLOR,
    384 			self::MEDIA,
    385 			self::SLIDER,
    386 			self::DIMENSIONS,
    387 			self::CHOOSE,
    388 			self::WYSIWYG,
    389 			self::CODE,
    390 			self::FONT,
    391 			self::IMAGE_DIMENSIONS,
    392 
    393 			self::WP_WIDGET,
    394 
    395 			self::URL,
    396 			self::REPEATER,
    397 			self::ICON,
    398 			self::ICONS,
    399 			self::GALLERY,
    400 			self::STRUCTURE,
    401 			self::SELECT2,
    402 			self::DATE_TIME,
    403 			self::BOX_SHADOW,
    404 			self::TEXT_SHADOW,
    405 			self::ANIMATION,
    406 			self::HOVER_ANIMATION,
    407 			self::EXIT_ANIMATION,
    408 		];
    409 	}
    410 
    411 	/**
    412 	 * Register controls.
    413 	 *
    414 	 * This method creates a list of all the supported controls by requiring the
    415 	 * control files and initializing each one of them.
    416 	 *
    417 	 * The list of supported controls includes the regular controls and the group
    418 	 * controls.
    419 	 *
    420 	 * External developers can register new controls by hooking to the
    421 	 * `elementor/controls/controls_registered` action.
    422 	 *
    423 	 * @since 3.1.0
    424 	 * @access private
    425 	 */
    426 	private function register_controls() {
    427 		$this->controls = [];
    428 
    429 		foreach ( self::get_controls_names() as $control_id ) {
    430 			$control_class_id = str_replace( ' ', '_', ucwords( str_replace( '_', ' ', $control_id ) ) );
    431 			$class_name = __NAMESPACE__ . '\Control_' . $control_class_id;
    432 
    433 			$this->register_control( $control_id, new $class_name() );
    434 		}
    435 
    436 		// Group Controls
    437 		foreach ( self::get_groups_names() as $group_name ) {
    438 			$group_class_id = str_replace( ' ', '_', ucwords( str_replace( '-', ' ', $group_name ) ) );
    439 			$class_name = __NAMESPACE__ . '\Group_Control_' . $group_class_id;
    440 
    441 			$this->control_groups[ $group_name ] = new $class_name();
    442 		}
    443 
    444 		/**
    445 		 * After controls registered.
    446 		 *
    447 		 * Fires after Elementor controls are registered.
    448 		 *
    449 		 * @since 1.0.0
    450 		 *
    451 		 * @param Controls_Manager $this The controls manager.
    452 		 */
    453 		do_action( 'elementor/controls/controls_registered', $this );
    454 	}
    455 
    456 	/**
    457 	 * Register control.
    458 	 *
    459 	 * This method adds a new control to the controls list. It adds any given
    460 	 * control to any given control instance.
    461 	 *
    462 	 * @since 1.0.0
    463 	 * @access public
    464 	 *
    465 	 * @param string       $control_id       Control ID.
    466 	 * @param Base_Control $control_instance Control instance, usually the
    467 	 *                                       current instance.
    468 	 */
    469 	public function register_control( $control_id, Base_Control $control_instance ) {
    470 		$this->controls[ $control_id ] = $control_instance;
    471 	}
    472 
    473 	/**
    474 	 * Unregister control.
    475 	 *
    476 	 * This method removes control from the controls list.
    477 	 *
    478 	 * @since 1.0.0
    479 	 * @access public
    480 	 *
    481 	 * @param string $control_id Control ID.
    482 	 *
    483 	 * @return bool True if the control was removed, False otherwise.
    484 	 */
    485 	public function unregister_control( $control_id ) {
    486 		if ( ! isset( $this->controls[ $control_id ] ) ) {
    487 			return false;
    488 		}
    489 
    490 		unset( $this->controls[ $control_id ] );
    491 
    492 		return true;
    493 	}
    494 
    495 	/**
    496 	 * Get controls.
    497 	 *
    498 	 * Retrieve the controls list from the current instance.
    499 	 *
    500 	 * @since 1.0.0
    501 	 * @access public
    502 	 *
    503 	 * @return Base_Control[] Controls list.
    504 	 */
    505 	public function get_controls() {
    506 		if ( null === $this->controls ) {
    507 			$this->register_controls();
    508 		}
    509 
    510 		return $this->controls;
    511 	}
    512 
    513 	/**
    514 	 * Get control.
    515 	 *
    516 	 * Retrieve a specific control from the current controls instance.
    517 	 *
    518 	 * @since 1.0.0
    519 	 * @access public
    520 	 *
    521 	 * @param string $control_id Control ID.
    522 	 *
    523 	 * @return bool|Base_Control Control instance, or False otherwise.
    524 	 */
    525 	public function get_control( $control_id ) {
    526 		$controls = $this->get_controls();
    527 
    528 		return isset( $controls[ $control_id ] ) ? $controls[ $control_id ] : false;
    529 	}
    530 
    531 	/**
    532 	 * Get controls data.
    533 	 *
    534 	 * Retrieve all the registered controls and all the data for each control.
    535 	 *
    536 	 * @since 1.0.0
    537 	 * @access public
    538 	 *
    539 	 * @return array {
    540 	 *    Control data.
    541 	 *
    542 	 *    @type array $name Control data.
    543 	 * }
    544 	 */
    545 	public function get_controls_data() {
    546 		$controls_data = [];
    547 
    548 		foreach ( $this->get_controls() as $name => $control ) {
    549 			$controls_data[ $name ] = $control->get_settings();
    550 		}
    551 
    552 		return $controls_data;
    553 	}
    554 
    555 	/**
    556 	 * Render controls.
    557 	 *
    558 	 * Generate the final HTML for all the registered controls using the element
    559 	 * template.
    560 	 *
    561 	 * @since 1.0.0
    562 	 * @access public
    563 	 */
    564 	public function render_controls() {
    565 		foreach ( $this->get_controls() as $control ) {
    566 			$control->print_template();
    567 		}
    568 	}
    569 
    570 	/**
    571 	 * Get control groups.
    572 	 *
    573 	 * Retrieve a specific group for a given ID, or a list of all the control
    574 	 * groups.
    575 	 *
    576 	 * If the given group ID is wrong, it will return `null`. When the ID valid,
    577 	 * it will return the group control instance. When no ID was given, it will
    578 	 * return all the control groups.
    579 	 *
    580 	 * @since 1.0.10
    581 	 * @access public
    582 	 *
    583 	 * @param string $id Optional. Group ID. Default is null.
    584 	 *
    585 	 * @return null|Group_Control_Base|Group_Control_Base[]
    586 	 */
    587 	public function get_control_groups( $id = null ) {
    588 		if ( $id ) {
    589 			return isset( $this->control_groups[ $id ] ) ? $this->control_groups[ $id ] : null;
    590 		}
    591 
    592 		return $this->control_groups;
    593 	}
    594 
    595 	/**
    596 	 * Add group control.
    597 	 *
    598 	 * This method adds a new group control to the control groups list. It adds
    599 	 * any given group control to any given group control instance.
    600 	 *
    601 	 * @since 1.0.0
    602 	 * @access public
    603 	 *
    604 	 * @param string             $id       Group control ID.
    605 	 * @param Group_Control_Base $instance Group control instance, usually the
    606 	 *                                     current instance.
    607 	 *
    608 	 * @return Group_Control_Base Group control instance.
    609 	 */
    610 	public function add_group_control( $id, $instance ) {
    611 		$this->control_groups[ $id ] = $instance;
    612 
    613 		return $instance;
    614 	}
    615 
    616 	/**
    617 	 * Enqueue control scripts and styles.
    618 	 *
    619 	 * Used to register and enqueue custom scripts and styles used by the control.
    620 	 *
    621 	 * @since 1.0.0
    622 	 * @access public
    623 	 */
    624 	public function enqueue_control_scripts() {
    625 		foreach ( $this->get_controls() as $control ) {
    626 			$control->enqueue();
    627 		}
    628 	}
    629 
    630 	/**
    631 	 * Open new stack.
    632 	 *
    633 	 * This method adds a new stack to the control stacks list. It adds any
    634 	 * given stack to the current control instance.
    635 	 *
    636 	 * @since 1.0.0
    637 	 * @access public
    638 	 *
    639 	 * @param Controls_Stack $controls_stack Controls stack.
    640 	 */
    641 	public function open_stack( Controls_Stack $controls_stack ) {
    642 		$stack_id = $controls_stack->get_unique_name();
    643 
    644 		$this->stacks[ $stack_id ] = [
    645 			'tabs' => [],
    646 			'controls' => [],
    647 		];
    648 	}
    649 
    650 	/**
    651 	 * Add control to stack.
    652 	 *
    653 	 * This method adds a new control to the stack.
    654 	 *
    655 	 * @since 1.0.0
    656 	 * @access public
    657 	 *
    658 	 * @param Controls_Stack $element      Element stack.
    659 	 * @param string         $control_id   Control ID.
    660 	 * @param array          $control_data Control data.
    661 	 * @param array          $options      Optional. Control additional options.
    662 	 *                                     Default is an empty array.
    663 	 *
    664 	 * @return bool True if control added, False otherwise.
    665 	 */
    666 	public function add_control_to_stack( Controls_Stack $element, $control_id, $control_data, $options = [] ) {
    667 		$default_options = [
    668 			'overwrite' => false,
    669 			'index' => null,
    670 		];
    671 
    672 		$options = array_merge( $default_options, $options );
    673 
    674 		$default_args = [
    675 			'type' => self::TEXT,
    676 			'tab' => self::TAB_CONTENT,
    677 		];
    678 
    679 		$control_data['name'] = $control_id;
    680 
    681 		$control_data = array_merge( $default_args, $control_data );
    682 
    683 		$control_type_instance = $this->get_control( $control_data['type'] );
    684 
    685 		if ( ! $control_type_instance ) {
    686 			_doing_it_wrong( sprintf( '%1$s::%2$s', __CLASS__, __FUNCTION__ ), sprintf( 'Control type "%s" not found.', esc_html( $control_data['type'] ) ), '1.0.0' );
    687 			return false;
    688 		}
    689 
    690 		if ( $control_type_instance instanceof Base_Data_Control ) {
    691 			$control_default_value = $control_type_instance->get_default_value();
    692 
    693 			if ( is_array( $control_default_value ) ) {
    694 				$control_data['default'] = isset( $control_data['default'] ) ? array_merge( $control_default_value, $control_data['default'] ) : $control_default_value;
    695 			} else {
    696 				$control_data['default'] = isset( $control_data['default'] ) ? $control_data['default'] : $control_default_value;
    697 			}
    698 		}
    699 
    700 		$stack_id = $element->get_unique_name();
    701 
    702 		if ( ! $options['overwrite'] && isset( $this->stacks[ $stack_id ]['controls'][ $control_id ] ) ) {
    703 			_doing_it_wrong( sprintf( '%1$s::%2$s', __CLASS__, __FUNCTION__ ), sprintf( 'Cannot redeclare control with same name "%s".', esc_html( $control_id ) ), '1.0.0' );
    704 
    705 			return false;
    706 		}
    707 
    708 		$tabs = self::get_tabs();
    709 
    710 		if ( ! isset( $tabs[ $control_data['tab'] ] ) ) {
    711 			$control_data['tab'] = $default_args['tab'];
    712 		}
    713 
    714 		$this->stacks[ $stack_id ]['tabs'][ $control_data['tab'] ] = $tabs[ $control_data['tab'] ];
    715 
    716 		$this->stacks[ $stack_id ]['controls'][ $control_id ] = $control_data;
    717 
    718 		if ( null !== $options['index'] ) {
    719 			$controls = $this->stacks[ $stack_id ]['controls'];
    720 
    721 			$controls_keys = array_keys( $controls );
    722 
    723 			array_splice( $controls_keys, $options['index'], 0, $control_id );
    724 
    725 			$this->stacks[ $stack_id ]['controls'] = array_merge( array_flip( $controls_keys ), $controls );
    726 		}
    727 
    728 		return true;
    729 	}
    730 
    731 	/**
    732 	 * Remove control from stack.
    733 	 *
    734 	 * This method removes a control a the stack.
    735 	 *
    736 	 * @since 1.0.0
    737 	 * @access public
    738 	 *
    739 	 * @param string $stack_id   Stack ID.
    740 	 * @param array|string $control_id The ID of the control to remove.
    741 	 *
    742 	 * @return bool|\WP_Error True if the stack was removed, False otherwise.
    743 	 */
    744 	public function remove_control_from_stack( $stack_id, $control_id ) {
    745 		if ( is_array( $control_id ) ) {
    746 			foreach ( $control_id as $id ) {
    747 				$this->remove_control_from_stack( $stack_id, $id );
    748 			}
    749 
    750 			return true;
    751 		}
    752 
    753 		if ( empty( $this->stacks[ $stack_id ]['controls'][ $control_id ] ) ) {
    754 			return new \WP_Error( 'Cannot remove not-exists control.' );
    755 		}
    756 
    757 		unset( $this->stacks[ $stack_id ]['controls'][ $control_id ] );
    758 
    759 		return true;
    760 	}
    761 
    762 	/**
    763 	 * Get control from stack.
    764 	 *
    765 	 * Retrieve a specific control for a given a specific stack.
    766 	 *
    767 	 * If the given control does not exist in the stack, or the stack does not
    768 	 * exist, it will return `WP_Error`. Otherwise, it will retrieve the control
    769 	 * from the stack.
    770 	 *
    771 	 * @since 1.1.0
    772 	 * @access public
    773 	 *
    774 	 * @param string $stack_id   Stack ID.
    775 	 * @param string $control_id Control ID.
    776 	 *
    777 	 * @return array|\WP_Error The control, or an error.
    778 	 */
    779 	public function get_control_from_stack( $stack_id, $control_id ) {
    780 		if ( empty( $this->stacks[ $stack_id ]['controls'][ $control_id ] ) ) {
    781 			return new \WP_Error( 'Cannot get a not-exists control.' );
    782 		}
    783 
    784 		return $this->stacks[ $stack_id ]['controls'][ $control_id ];
    785 	}
    786 
    787 	/**
    788 	 * Update control in stack.
    789 	 *
    790 	 * This method updates the control data for a given stack.
    791 	 *
    792 	 * @since 1.1.0
    793 	 * @access public
    794 	 *
    795 	 * @param Controls_Stack $element      Element stack.
    796 	 * @param string         $control_id   Control ID.
    797 	 * @param array          $control_data Control data.
    798 	 * @param array          $options      Optional. Control additional options.
    799 	 *                                     Default is an empty array.
    800 	 *
    801 	 * @return bool True if control updated, False otherwise.
    802 	 */
    803 	public function update_control_in_stack( Controls_Stack $element, $control_id, $control_data, array $options = [] ) {
    804 		$old_control_data = $this->get_control_from_stack( $element->get_unique_name(), $control_id );
    805 
    806 		if ( is_wp_error( $old_control_data ) ) {
    807 			return false;
    808 		}
    809 
    810 		if ( ! empty( $options['recursive'] ) ) {
    811 			$control_data = array_replace_recursive( $old_control_data, $control_data );
    812 		} else {
    813 			$control_data = array_merge( $old_control_data, $control_data );
    814 		}
    815 
    816 		return $this->add_control_to_stack( $element, $control_id, $control_data, [
    817 			'overwrite' => true,
    818 		] );
    819 	}
    820 
    821 	/**
    822 	 * Get stacks.
    823 	 *
    824 	 * Retrieve a specific stack for the list of stacks.
    825 	 *
    826 	 * If the given stack is wrong, it will return `null`. When the stack valid,
    827 	 * it will return the the specific stack. When no stack was given, it will
    828 	 * return all the stacks.
    829 	 *
    830 	 * @since 1.7.1
    831 	 * @access public
    832 	 *
    833 	 * @param string $stack_id Optional. stack ID. Default is null.
    834 	 *
    835 	 * @return null|array A list of stacks.
    836 	 */
    837 	public function get_stacks( $stack_id = null ) {
    838 		if ( $stack_id ) {
    839 			if ( isset( $this->stacks[ $stack_id ] ) ) {
    840 				return $this->stacks[ $stack_id ];
    841 			}
    842 
    843 			return null;
    844 		}
    845 
    846 		return $this->stacks;
    847 	}
    848 
    849 	/**
    850 	 * Get element stack.
    851 	 *
    852 	 * Retrieve a specific stack for the list of stacks from the current instance.
    853 	 *
    854 	 * @since 1.0.0
    855 	 * @access public
    856 	 *
    857 	 * @param Controls_Stack $controls_stack  Controls stack.
    858 	 *
    859 	 * @return null|array Stack data if it exist, `null` otherwise.
    860 	 */
    861 	public function get_element_stack( Controls_Stack $controls_stack ) {
    862 		$stack_id = $controls_stack->get_unique_name();
    863 
    864 		if ( ! isset( $this->stacks[ $stack_id ] ) ) {
    865 			return null;
    866 		}
    867 
    868 		return $this->stacks[ $stack_id ];
    869 	}
    870 
    871 	/**
    872 	 * Add custom CSS controls.
    873 	 *
    874 	 * This method adds a new control for the "Custom CSS" feature. The free
    875 	 * version of elementor uses this method to display an upgrade message to
    876 	 * Elementor Pro.
    877 	 *
    878 	 * @since 1.0.0
    879 	 * @access public
    880 	 *
    881 	 * @param Controls_Stack $controls_stack .
    882 	 * @param string $tab
    883 	 * @param array $additional_messages
    884 	 *
    885 	 */
    886 	public function add_custom_css_controls( Controls_Stack $controls_stack, $tab = self::TAB_ADVANCED, $additional_messages = [] ) {
    887 		$controls_stack->start_controls_section(
    888 			'section_custom_css_pro',
    889 			[
    890 				'label' => esc_html__( 'Custom CSS', 'elementor' ),
    891 				'tab' => $tab,
    892 			]
    893 		);
    894 
    895 		$messages = [
    896 			__( 'Custom CSS lets you add CSS code to any widget, and see it render live right in the editor.', 'elementor' ),
    897 		];
    898 
    899 		if ( $additional_messages ) {
    900 			$messages = array_merge( $messages, $additional_messages );
    901 		}
    902 
    903 		$controls_stack->add_control(
    904 			'custom_css_pro',
    905 			[
    906 				'type' => self::RAW_HTML,
    907 				'raw' => $this->get_teaser_template( [
    908 					'title' => esc_html__( 'Meet Our Custom CSS', 'elementor' ),
    909 					'messages' => $messages,
    910 					'link' => 'https://elementor.com/pro/?utm_source=panel-custom-css&utm_campaign=gopro&utm_medium=wp-dash',
    911 				] ),
    912 			]
    913 		);
    914 
    915 		$controls_stack->end_controls_section();
    916 	}
    917 
    918 	/**
    919 	 * Add Page Transitions controls.
    920 	 *
    921 	 * This method adds a new control for the "Page Transitions" feature. The Core
    922 	 * version of elementor uses this method to display an upgrade message to
    923 	 * Elementor Pro.
    924 	 *
    925 	 * @param Controls_Stack $controls_stack .
    926 	 * @param string $tab
    927 	 * @param array $additional_messages
    928 	 *
    929 	 * @return void
    930 	 */
    931 	public function add_page_transitions_controls( Controls_Stack $controls_stack, $tab = self::TAB_ADVANCED, $additional_messages = [] ) {
    932 		$controls_stack->start_controls_section(
    933 			'section_page_transitions_teaser',
    934 			[
    935 				'label' => __( 'Page Transitions', 'elementor' ),
    936 				'tab' => $tab,
    937 			]
    938 		);
    939 
    940 		$messages = [
    941 			__( 'Page Transitions let you style entrance and exit animations between pages as well as display loader until your page assets load.', 'elementor' ),
    942 		];
    943 
    944 		if ( $additional_messages ) {
    945 			$messages = array_merge( $messages, $additional_messages );
    946 		}
    947 
    948 		$controls_stack->add_control(
    949 			'page_transitions_teaser',
    950 			[
    951 				'type' => self::RAW_HTML,
    952 				'raw' => $this->get_teaser_template( [
    953 					'title' => __( 'Meet Page Transitions', 'elementor' ),
    954 					'messages' => $messages,
    955 					'link' => 'https://elementor.com/pro/?utm_source=panel-page-transitions&utm_campaign=gopro&utm_medium=wp-dash',
    956 				] ),
    957 			]
    958 		);
    959 
    960 		$controls_stack->end_controls_section();
    961 	}
    962 
    963 	public function get_teaser_template( $texts ) {
    964 		ob_start();
    965 		?>
    966 		<div class="elementor-nerd-box">
    967 			<img class="elementor-nerd-box-icon" src="<?php echo esc_url( ELEMENTOR_ASSETS_URL . 'images/go-pro.svg' ); ?>" />
    968 			<div class="elementor-nerd-box-title"><?php Utils::print_unescaped_internal_string( $texts['title'] ); ?></div>
    969 			<?php foreach ( $texts['messages'] as $message ) { ?>
    970 				<div class="elementor-nerd-box-message"><?php Utils::print_unescaped_internal_string( $message ); ?></div>
    971 			<?php }
    972 
    973 			// Show a `Go Pro` button only if the user doesn't have Pro.
    974 			if ( $texts['link'] && ! Utils::has_pro() ) { ?>
    975 				<a class="elementor-nerd-box-link elementor-button elementor-button-default elementor-button-go-pro" href="<?php echo esc_url( Utils::get_pro_link( $texts['link'] ) ); ?>" target="_blank">
    976 					<?php echo esc_html__( 'Go Pro', 'elementor' ); ?>
    977 				</a>
    978 			<?php } ?>
    979 		</div>
    980 		<?php
    981 
    982 		return ob_get_clean();
    983 	}
    984 
    985 	/**
    986 	 * Add custom attributes controls.
    987 	 *
    988 	 * This method adds a new control for the "Custom Attributes" feature. The free
    989 	 * version of elementor uses this method to display an upgrade message to
    990 	 * Elementor Pro.
    991 	 *
    992 	 * @since 2.8.3
    993 	 * @access public
    994 	 *
    995 	 * @param Controls_Stack $controls_stack.
    996 	 */
    997 	public function add_custom_attributes_controls( Controls_Stack $controls_stack ) {
    998 		$controls_stack->start_controls_section(
    999 			'section_custom_attributes_pro',
   1000 			[
   1001 				'label' => esc_html__( 'Attributes', 'elementor' ),
   1002 				'tab' => self::TAB_ADVANCED,
   1003 			]
   1004 		);
   1005 
   1006 		$controls_stack->add_control(
   1007 			'custom_attributes_pro',
   1008 			[
   1009 				'type' => self::RAW_HTML,
   1010 				'raw' => $this->get_teaser_template( [
   1011 					'title' => esc_html__( 'Meet Our Attributes', 'elementor' ),
   1012 					'messages' => [
   1013 						__( 'Attributes lets you add custom HTML attributes to any element.', 'elementor' ),
   1014 					],
   1015 					'link' => 'https://elementor.com/pro/?utm_source=panel-custom-attributes&utm_campaign=gopro&utm_medium=wp-dash',
   1016 				] ),
   1017 			]
   1018 		);
   1019 
   1020 		$controls_stack->end_controls_section();
   1021 	}
   1022 }