section.php (38958B)
1 <?php 2 namespace Elementor; 3 4 if ( ! defined( 'ABSPATH' ) ) { 5 exit; // Exit if accessed directly. 6 } 7 8 /** 9 * Elementor section element. 10 * 11 * Elementor section handler class is responsible for initializing the section 12 * element. 13 * 14 * @since 1.0.0 15 */ 16 class Element_Section extends Element_Base { 17 18 /** 19 * Section predefined columns presets. 20 * 21 * Holds the predefined columns width for each columns count available by 22 * default by Elementor. Default is an empty array. 23 * 24 * Note that when the user creates a section he can define custom sizes for 25 * the columns. But Elementor sets default values for predefined columns. 26 * 27 * For example two columns 50% width each one, or three columns 33.33% each 28 * one. This property hold the data for those preset values. 29 * 30 * @since 1.0.0 31 * @access private 32 * @static 33 * 34 * @var array Section presets. 35 */ 36 private static $presets = []; 37 38 /** 39 * Get element type. 40 * 41 * Retrieve the element type, in this case `section`. 42 * 43 * @since 2.1.0 44 * @access public 45 * @static 46 * 47 * @return string The type. 48 */ 49 public static function get_type() { 50 return 'section'; 51 } 52 53 /** 54 * Get section name. 55 * 56 * Retrieve the section name. 57 * 58 * @since 1.0.0 59 * @access public 60 * 61 * @return string Section name. 62 */ 63 public function get_name() { 64 return 'section'; 65 } 66 67 /** 68 * Get section title. 69 * 70 * Retrieve the section title. 71 * 72 * @since 1.0.0 73 * @access public 74 * 75 * @return string Section title. 76 */ 77 public function get_title() { 78 return esc_html__( 'Section', 'elementor' ); 79 } 80 81 /** 82 * Get section icon. 83 * 84 * Retrieve the section icon. 85 * 86 * @since 1.0.0 87 * @access public 88 * 89 * @return string Section icon. 90 */ 91 public function get_icon() { 92 return 'eicon-columns'; 93 } 94 95 /** 96 * Get presets. 97 * 98 * Retrieve a specific preset columns for a given columns count, or a list 99 * of all the preset if no parameters passed. 100 * 101 * @since 1.0.0 102 * @access public 103 * @static 104 * 105 * @param int $columns_count Optional. Columns count. Default is null. 106 * @param int $preset_index Optional. Preset index. Default is null. 107 * 108 * @return array Section presets. 109 */ 110 public static function get_presets( $columns_count = null, $preset_index = null ) { 111 if ( ! self::$presets ) { 112 self::init_presets(); 113 } 114 115 $presets = self::$presets; 116 117 if ( null !== $columns_count ) { 118 $presets = $presets[ $columns_count ]; 119 } 120 121 if ( null !== $preset_index ) { 122 $presets = $presets[ $preset_index ]; 123 } 124 125 return $presets; 126 } 127 128 /** 129 * Initialize presets. 130 * 131 * Initializing the section presets and set the number of columns the 132 * section can have by default. For example a column can have two columns 133 * 50% width each one, or three columns 33.33% each one. 134 * 135 * Note that Elementor sections have default section presets but the user 136 * can set custom number of columns and define custom sizes for each column. 137 138 * @since 1.0.0 139 * @access public 140 * @static 141 */ 142 public static function init_presets() { 143 $additional_presets = [ 144 2 => [ 145 [ 146 'preset' => [ 33, 66 ], 147 ], 148 [ 149 'preset' => [ 66, 33 ], 150 ], 151 ], 152 3 => [ 153 [ 154 'preset' => [ 25, 25, 50 ], 155 ], 156 [ 157 'preset' => [ 50, 25, 25 ], 158 ], 159 [ 160 'preset' => [ 25, 50, 25 ], 161 ], 162 [ 163 'preset' => [ 16, 66, 16 ], 164 ], 165 ], 166 ]; 167 168 foreach ( range( 1, 10 ) as $columns_count ) { 169 self::$presets[ $columns_count ] = [ 170 [ 171 'preset' => [], 172 ], 173 ]; 174 175 $preset_unit = floor( 1 / $columns_count * 100 ); 176 177 for ( $i = 0; $i < $columns_count; $i++ ) { 178 self::$presets[ $columns_count ][0]['preset'][] = $preset_unit; 179 } 180 181 if ( ! empty( $additional_presets[ $columns_count ] ) ) { 182 self::$presets[ $columns_count ] = array_merge( self::$presets[ $columns_count ], $additional_presets[ $columns_count ] ); 183 } 184 185 foreach ( self::$presets[ $columns_count ] as $preset_index => & $preset ) { 186 $preset['key'] = $columns_count . $preset_index; 187 } 188 } 189 } 190 191 /** 192 * Get initial config. 193 * 194 * Retrieve the current section initial configuration. 195 * 196 * Adds more configuration on top of the controls list, the tabs assigned to 197 * the control, element name, type, icon and more. This method also adds 198 * section presets. 199 * 200 * @since 2.9.0 201 * @access protected 202 * 203 * @return array The initial config. 204 */ 205 protected function get_initial_config() { 206 $config = parent::get_initial_config(); 207 208 $config['presets'] = self::get_presets(); 209 $config['controls'] = $this->get_controls(); 210 $config['tabs_controls'] = $this->get_tabs_controls(); 211 212 return $config; 213 } 214 215 /** 216 * Register section controls. 217 * 218 * Used to add new controls to the section element. 219 * 220 * @since 3.1.0 221 * @access protected 222 */ 223 protected function register_controls() { 224 $this->start_controls_section( 225 'section_layout', 226 [ 227 'label' => esc_html__( 'Layout', 'elementor' ), 228 'tab' => Controls_Manager::TAB_LAYOUT, 229 ] 230 ); 231 232 // Element Name for the Navigator 233 $this->add_control( 234 '_title', 235 [ 236 'label' => esc_html__( 'Title', 'elementor' ), 237 'type' => Controls_Manager::HIDDEN, 238 'render_type' => 'none', 239 ] 240 ); 241 242 $this->add_control( 243 'layout', 244 [ 245 'label' => esc_html__( 'Content Width', 'elementor' ), 246 'type' => Controls_Manager::SELECT, 247 'default' => 'boxed', 248 'options' => [ 249 'boxed' => esc_html__( 'Boxed', 'elementor' ), 250 'full_width' => esc_html__( 'Full Width', 'elementor' ), 251 ], 252 'prefix_class' => 'elementor-section-', 253 ] 254 ); 255 256 $this->add_responsive_control( 257 'content_width', 258 [ 259 'label' => esc_html__( 'Width', 'elementor' ), 260 'type' => Controls_Manager::SLIDER, 261 'range' => [ 262 'px' => [ 263 'min' => 500, 264 'max' => 1600, 265 ], 266 ], 267 'selectors' => [ 268 '{{WRAPPER}} > .elementor-container' => 'max-width: {{SIZE}}{{UNIT}};', 269 ], 270 'condition' => [ 271 'layout' => [ 'boxed' ], 272 ], 273 'separator' => 'none', 274 ] 275 ); 276 277 $this->add_control( 278 'gap', 279 [ 280 'label' => esc_html__( 'Columns Gap', 'elementor' ), 281 'type' => Controls_Manager::SELECT, 282 'default' => 'default', 283 'options' => [ 284 'default' => esc_html__( 'Default', 'elementor' ), 285 'no' => esc_html__( 'No Gap', 'elementor' ), 286 'narrow' => esc_html__( 'Narrow', 'elementor' ), 287 'extended' => esc_html__( 'Extended', 'elementor' ), 288 'wide' => esc_html__( 'Wide', 'elementor' ), 289 'wider' => esc_html__( 'Wider', 'elementor' ), 290 'custom' => esc_html__( 'Custom', 'elementor' ), 291 ], 292 ] 293 ); 294 295 $this->add_responsive_control( 296 'gap_columns_custom', 297 [ 298 'label' => esc_html__( 'Custom Columns Gap', 'elementor' ), 299 'type' => Controls_Manager::SLIDER, 300 'range' => [ 301 'px' => [ 302 'min' => 0, 303 'max' => 500, 304 ], 305 '%' => [ 306 'min' => 0, 307 'max' => 100, 308 ], 309 'vh' => [ 310 'min' => 0, 311 'max' => 100, 312 ], 313 'vw' => [ 314 'min' => 0, 315 'max' => 100, 316 ], 317 ], 318 'size_units' => [ 'px', '%', 'vh', 'vw' ], 319 'selectors' => [ 320 '{{WRAPPER}} .elementor-column-gap-custom .elementor-column > .elementor-element-populated' => 'padding: {{SIZE}}{{UNIT}};', 321 ], 322 'condition' => [ 323 'gap' => 'custom', 324 ], 325 ] 326 ); 327 328 $this->add_control( 329 'height', 330 [ 331 'label' => esc_html__( 'Height', 'elementor' ), 332 'type' => Controls_Manager::SELECT, 333 'default' => 'default', 334 'options' => [ 335 'default' => esc_html__( 'Default', 'elementor' ), 336 'full' => esc_html__( 'Fit To Screen', 'elementor' ), 337 'min-height' => esc_html__( 'Min Height', 'elementor' ), 338 ], 339 'prefix_class' => 'elementor-section-height-', 340 'hide_in_inner' => true, 341 ] 342 ); 343 344 $this->add_responsive_control( 345 'custom_height', 346 [ 347 'label' => esc_html__( 'Minimum Height', 'elementor' ), 348 'type' => Controls_Manager::SLIDER, 349 'default' => [ 350 'size' => 400, 351 ], 352 'range' => [ 353 'px' => [ 354 'min' => 0, 355 'max' => 1440, 356 ], 357 'vh' => [ 358 'min' => 0, 359 'max' => 100, 360 ], 361 'vw' => [ 362 'min' => 0, 363 'max' => 100, 364 ], 365 ], 366 'size_units' => [ 'px', 'vh', 'vw' ], 367 'selectors' => [ 368 '{{WRAPPER}} > .elementor-container' => 'min-height: {{SIZE}}{{UNIT}};', 369 ], 370 'condition' => [ 371 'height' => [ 'min-height' ], 372 ], 373 'hide_in_inner' => true, 374 ] 375 ); 376 377 $this->add_control( 378 'height_inner', 379 [ 380 'label' => esc_html__( 'Height', 'elementor' ), 381 'type' => Controls_Manager::SELECT, 382 'default' => 'default', 383 'options' => [ 384 'default' => esc_html__( 'Default', 'elementor' ), 385 'full' => esc_html__( 'Fit To Screen', 'elementor' ), 386 'min-height' => esc_html__( 'Min Height', 'elementor' ), 387 ], 388 'prefix_class' => 'elementor-section-height-', 389 'hide_in_top' => true, 390 ] 391 ); 392 393 $this->add_responsive_control( 394 'custom_height_inner', 395 [ 396 'label' => esc_html__( 'Minimum Height', 'elementor' ), 397 'type' => Controls_Manager::SLIDER, 398 'default' => [ 399 'size' => 400, 400 ], 401 'range' => [ 402 'px' => [ 403 'min' => 0, 404 'max' => 1440, 405 ], 406 ], 407 'selectors' => [ 408 '{{WRAPPER}} > .elementor-container' => 'min-height: {{SIZE}}{{UNIT}};', 409 ], 410 'condition' => [ 411 'height_inner' => [ 'min-height' ], 412 ], 413 'size_units' => [ 'px', 'vh', 'vw' ], 414 'hide_in_top' => true, 415 ] 416 ); 417 418 $this->add_control( 419 'column_position', 420 [ 421 'label' => esc_html__( 'Column Position', 'elementor' ), 422 'type' => Controls_Manager::SELECT, 423 'default' => 'middle', 424 'options' => [ 425 'stretch' => esc_html__( 'Stretch', 'elementor' ), 426 'top' => esc_html__( 'Top', 'elementor' ), 427 'middle' => esc_html__( 'Middle', 'elementor' ), 428 'bottom' => esc_html__( 'Bottom', 'elementor' ), 429 ], 430 'prefix_class' => 'elementor-section-items-', 431 'condition' => [ 432 'height' => [ 'full', 'min-height' ], 433 ], 434 ] 435 ); 436 437 $content_position_selector = Plugin::$instance->experiments->is_feature_active( 'e_dom_optimization' ) ? 438 '{{WRAPPER}} > .elementor-container > .elementor-column > .elementor-widget-wrap' : 439 '{{WRAPPER}} > .elementor-container > .elementor-row > .elementor-column > .elementor-column-wrap > .elementor-widget-wrap'; 440 441 $this->add_control( 442 'content_position', 443 [ 444 'label' => esc_html__( 'Vertical Align', 'elementor' ), 445 'type' => Controls_Manager::SELECT, 446 'default' => '', 447 'options' => [ 448 '' => esc_html__( 'Default', 'elementor' ), 449 'top' => esc_html__( 'Top', 'elementor' ), 450 'middle' => esc_html__( 'Middle', 'elementor' ), 451 'bottom' => esc_html__( 'Bottom', 'elementor' ), 452 'space-between' => esc_html__( 'Space Between', 'elementor' ), 453 'space-around' => esc_html__( 'Space Around', 'elementor' ), 454 'space-evenly' => esc_html__( 'Space Evenly', 'elementor' ), 455 ], 456 'selectors_dictionary' => [ 457 'top' => 'flex-start', 458 'middle' => 'center', 459 'bottom' => 'flex-end', 460 ], 461 'selectors' => [ 462 $content_position_selector => 'align-content: {{VALUE}}; align-items: {{VALUE}};', 463 ], 464 // TODO: The following line is for BC since 2.7.0 465 'prefix_class' => 'elementor-section-content-', 466 ] 467 ); 468 469 $this->add_control( 470 'overflow', 471 [ 472 'label' => esc_html__( 'Overflow', 'elementor' ), 473 'type' => Controls_Manager::SELECT, 474 'default' => '', 475 'options' => [ 476 '' => esc_html__( 'Default', 'elementor' ), 477 'hidden' => esc_html__( 'Hidden', 'elementor' ), 478 ], 479 'selectors' => [ 480 '{{WRAPPER}}' => 'overflow: {{VALUE}}', 481 ], 482 ] 483 ); 484 485 $this->add_control( 486 'stretch_section', 487 [ 488 'label' => esc_html__( 'Stretch Section', 'elementor' ), 489 'type' => Controls_Manager::SWITCHER, 490 'default' => '', 491 'return_value' => 'section-stretched', 492 'prefix_class' => 'elementor-', 493 'hide_in_inner' => true, 494 'description' => esc_html__( 'Stretch the section to the full width of the page using JS.', 'elementor' ) . sprintf( ' <a href="%1$s" target="_blank">%2$s</a>', 'https://go.elementor.com/stretch-section/', esc_html__( 'Learn more.', 'elementor' ) ), 495 'render_type' => 'none', 496 'frontend_available' => true, 497 ] 498 ); 499 500 $possible_tags = [ 501 'div', 502 'header', 503 'footer', 504 'main', 505 'article', 506 'section', 507 'aside', 508 'nav', 509 ]; 510 511 $options = [ 512 '' => esc_html__( 'Default', 'elementor' ), 513 ] + array_combine( $possible_tags, $possible_tags ); 514 515 $this->add_control( 516 'html_tag', 517 [ 518 'label' => esc_html__( 'HTML Tag', 'elementor' ), 519 'type' => Controls_Manager::SELECT, 520 'options' => $options, 521 'separator' => 'before', 522 ] 523 ); 524 525 $this->end_controls_section(); 526 527 // Section Structure 528 $this->start_controls_section( 529 'section_structure', 530 [ 531 'label' => esc_html__( 'Structure', 'elementor' ), 532 'tab' => Controls_Manager::TAB_LAYOUT, 533 ] 534 ); 535 536 $this->add_control( 537 'structure', 538 [ 539 'label' => esc_html__( 'Structure', 'elementor' ), 540 'type' => Controls_Manager::STRUCTURE, 541 'default' => '10', 542 'render_type' => 'none', 543 'style_transfer' => false, 544 ] 545 ); 546 547 $this->end_controls_section(); 548 549 // Section background 550 $this->start_controls_section( 551 'section_background', 552 [ 553 'label' => esc_html__( 'Background', 'elementor' ), 554 'tab' => Controls_Manager::TAB_STYLE, 555 ] 556 ); 557 558 $this->start_controls_tabs( 'tabs_background' ); 559 560 $this->start_controls_tab( 561 'tab_background_normal', 562 [ 563 'label' => esc_html__( 'Normal', 'elementor' ), 564 ] 565 ); 566 567 $this->add_group_control( 568 Group_Control_Background::get_type(), 569 [ 570 'name' => 'background', 571 'types' => [ 'classic', 'gradient', 'video', 'slideshow' ], 572 'fields_options' => [ 573 'background' => [ 574 'frontend_available' => true, 575 ], 576 ], 577 ] 578 ); 579 580 $this->end_controls_tab(); 581 582 $this->start_controls_tab( 583 'tab_background_hover', 584 [ 585 'label' => esc_html__( 'Hover', 'elementor' ), 586 ] 587 ); 588 589 $this->add_group_control( 590 Group_Control_Background::get_type(), 591 [ 592 'name' => 'background_hover', 593 'selector' => '{{WRAPPER}}:hover', 594 ] 595 ); 596 597 $this->add_control( 598 'background_hover_transition', 599 [ 600 'label' => esc_html__( 'Transition Duration', 'elementor' ), 601 'type' => Controls_Manager::SLIDER, 602 'default' => [ 603 'size' => 0.3, 604 ], 605 'range' => [ 606 'px' => [ 607 'max' => 3, 608 'step' => 0.1, 609 ], 610 ], 611 'render_type' => 'ui', 612 'separator' => 'before', 613 ] 614 ); 615 616 $this->end_controls_tab(); 617 618 $this->end_controls_tabs(); 619 620 $this->end_controls_section(); 621 622 // Background Overlay 623 $this->start_controls_section( 624 'section_background_overlay', 625 [ 626 'label' => esc_html__( 'Background Overlay', 'elementor' ), 627 'tab' => Controls_Manager::TAB_STYLE, 628 ] 629 ); 630 631 $this->start_controls_tabs( 'tabs_background_overlay' ); 632 633 $this->start_controls_tab( 634 'tab_background_overlay_normal', 635 [ 636 'label' => esc_html__( 'Normal', 'elementor' ), 637 ] 638 ); 639 640 $this->add_group_control( 641 Group_Control_Background::get_type(), 642 [ 643 'name' => 'background_overlay', 644 'selector' => '{{WRAPPER}} > .elementor-background-overlay', 645 ] 646 ); 647 648 $this->add_control( 649 'background_overlay_opacity', 650 [ 651 'label' => esc_html__( 'Opacity', 'elementor' ), 652 'type' => Controls_Manager::SLIDER, 653 'default' => [ 654 'size' => .5, 655 ], 656 'range' => [ 657 'px' => [ 658 'max' => 1, 659 'step' => 0.01, 660 ], 661 ], 662 'selectors' => [ 663 '{{WRAPPER}} > .elementor-background-overlay' => 'opacity: {{SIZE}};', 664 ], 665 'condition' => [ 666 'background_overlay_background' => [ 'classic', 'gradient' ], 667 ], 668 ] 669 ); 670 671 $this->add_group_control( 672 Group_Control_Css_Filter::get_type(), 673 [ 674 'name' => 'css_filters', 675 'selector' => '{{WRAPPER}} .elementor-background-overlay', 676 'conditions' => [ 677 'relation' => 'or', 678 'terms' => [ 679 [ 680 'name' => 'background_overlay_image[url]', 681 'operator' => '!==', 682 'value' => '', 683 ], 684 [ 685 'name' => 'background_overlay_color', 686 'operator' => '!==', 687 'value' => '', 688 ], 689 ], 690 ], 691 ] 692 ); 693 694 $this->add_control( 695 'overlay_blend_mode', 696 [ 697 'label' => esc_html__( 'Blend Mode', 'elementor' ), 698 'type' => Controls_Manager::SELECT, 699 'options' => [ 700 '' => esc_html__( 'Normal', 'elementor' ), 701 'multiply' => esc_html__( 'Multiply', 'elementor' ), 702 'screen' => esc_html__( 'Screen', 'elementor' ), 703 'overlay' => esc_html__( 'Overlay', 'elementor' ), 704 'darken' => esc_html__( 'Darken', 'elementor' ), 705 'lighten' => esc_html__( 'Lighten', 'elementor' ), 706 'color-dodge' => esc_html__( 'Color Dodge', 'elementor' ), 707 'saturation' => esc_html__( 'Saturation', 'elementor' ), 708 'color' => esc_html__( 'Color', 'elementor' ), 709 'luminosity' => esc_html__( 'Luminosity', 'elementor' ), 710 ], 711 'selectors' => [ 712 '{{WRAPPER}} > .elementor-background-overlay' => 'mix-blend-mode: {{VALUE}}', 713 ], 714 'conditions' => [ 715 'relation' => 'or', 716 'terms' => [ 717 [ 718 'name' => 'background_overlay_image[url]', 719 'operator' => '!==', 720 'value' => '', 721 ], 722 [ 723 'name' => 'background_overlay_color', 724 'operator' => '!==', 725 'value' => '', 726 ], 727 ], 728 ], 729 ] 730 ); 731 732 $this->end_controls_tab(); 733 734 $this->start_controls_tab( 735 'tab_background_overlay_hover', 736 [ 737 'label' => esc_html__( 'Hover', 'elementor' ), 738 ] 739 ); 740 741 $this->add_group_control( 742 Group_Control_Background::get_type(), 743 [ 744 'name' => 'background_overlay_hover', 745 'selector' => '{{WRAPPER}}:hover > .elementor-background-overlay', 746 ] 747 ); 748 749 $this->add_control( 750 'background_overlay_hover_opacity', 751 [ 752 'label' => esc_html__( 'Opacity', 'elementor' ), 753 'type' => Controls_Manager::SLIDER, 754 'default' => [ 755 'size' => .5, 756 ], 757 'range' => [ 758 'px' => [ 759 'max' => 1, 760 'step' => 0.01, 761 ], 762 ], 763 'selectors' => [ 764 '{{WRAPPER}}:hover > .elementor-background-overlay' => 'opacity: {{SIZE}};', 765 ], 766 'condition' => [ 767 'background_overlay_hover_background' => [ 'classic', 'gradient' ], 768 ], 769 ] 770 ); 771 772 $this->add_group_control( 773 Group_Control_Css_Filter::get_type(), 774 [ 775 'name' => 'css_filters_hover', 776 'selector' => '{{WRAPPER}}:hover > .elementor-background-overlay', 777 ] 778 ); 779 780 $this->add_control( 781 'background_overlay_hover_transition', 782 [ 783 'label' => esc_html__( 'Transition Duration', 'elementor' ), 784 'type' => Controls_Manager::SLIDER, 785 'default' => [ 786 'size' => 0.3, 787 ], 788 'range' => [ 789 'px' => [ 790 'max' => 3, 791 'step' => 0.1, 792 ], 793 ], 794 'render_type' => 'ui', 795 'separator' => 'before', 796 ] 797 ); 798 799 $this->end_controls_tab(); 800 801 $this->end_controls_tabs(); 802 803 $this->end_controls_section(); 804 805 // Section border 806 $this->start_controls_section( 807 'section_border', 808 [ 809 'label' => esc_html__( 'Border', 'elementor' ), 810 'tab' => Controls_Manager::TAB_STYLE, 811 ] 812 ); 813 814 $this->start_controls_tabs( 'tabs_border' ); 815 816 $this->start_controls_tab( 817 'tab_border_normal', 818 [ 819 'label' => esc_html__( 'Normal', 'elementor' ), 820 ] 821 ); 822 823 $this->add_group_control( 824 Group_Control_Border::get_type(), 825 [ 826 'name' => 'border', 827 ] 828 ); 829 830 $this->add_responsive_control( 831 'border_radius', 832 [ 833 'label' => esc_html__( 'Border Radius', 'elementor' ), 834 'type' => Controls_Manager::DIMENSIONS, 835 'size_units' => [ 'px', '%' ], 836 'selectors' => [ 837 '{{WRAPPER}}, {{WRAPPER}} > .elementor-background-overlay' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', 838 ], 839 ] 840 ); 841 842 $this->add_group_control( 843 Group_Control_Box_Shadow::get_type(), 844 [ 845 'name' => 'box_shadow', 846 ] 847 ); 848 849 $this->end_controls_tab(); 850 851 $this->start_controls_tab( 852 'tab_border_hover', 853 [ 854 'label' => esc_html__( 'Hover', 'elementor' ), 855 ] 856 ); 857 858 $this->add_group_control( 859 Group_Control_Border::get_type(), 860 [ 861 'name' => 'border_hover', 862 'selector' => '{{WRAPPER}}:hover', 863 ] 864 ); 865 866 $this->add_responsive_control( 867 'border_radius_hover', 868 [ 869 'label' => esc_html__( 'Border Radius', 'elementor' ), 870 'type' => Controls_Manager::DIMENSIONS, 871 'size_units' => [ 'px', '%' ], 872 'selectors' => [ 873 '{{WRAPPER}}:hover, {{WRAPPER}}:hover > .elementor-background-overlay' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', 874 ], 875 ] 876 ); 877 878 $this->add_group_control( 879 Group_Control_Box_Shadow::get_type(), 880 [ 881 'name' => 'box_shadow_hover', 882 'selector' => '{{WRAPPER}}:hover', 883 ] 884 ); 885 886 $this->add_control( 887 'border_hover_transition', 888 [ 889 'label' => esc_html__( 'Transition Duration', 'elementor' ), 890 'type' => Controls_Manager::SLIDER, 891 'separator' => 'before', 892 'default' => [ 893 'size' => 0.3, 894 ], 895 'range' => [ 896 'px' => [ 897 'max' => 3, 898 'step' => 0.1, 899 ], 900 ], 901 'conditions' => [ 902 'relation' => 'or', 903 'terms' => [ 904 [ 905 'name' => 'background_background', 906 'operator' => '!==', 907 'value' => '', 908 ], 909 [ 910 'name' => 'border_border', 911 'operator' => '!==', 912 'value' => '', 913 ], 914 ], 915 ], 916 'selectors' => [ 917 '{{WRAPPER}}' => 'transition: background {{background_hover_transition.SIZE}}s, border {{SIZE}}s, border-radius {{SIZE}}s, box-shadow {{SIZE}}s', 918 '{{WRAPPER}} > .elementor-background-overlay' => 'transition: background {{background_overlay_hover_transition.SIZE}}s, border-radius {{SIZE}}s, opacity {{background_overlay_hover_transition.SIZE}}s', 919 ], 920 ] 921 ); 922 923 $this->end_controls_tab(); 924 925 $this->end_controls_tabs(); 926 927 $this->end_controls_section(); 928 929 // Section Shape Divider 930 $this->start_controls_section( 931 'section_shape_divider', 932 [ 933 'label' => esc_html__( 'Shape Divider', 'elementor' ), 934 'tab' => Controls_Manager::TAB_STYLE, 935 ] 936 ); 937 938 $this->start_controls_tabs( 'tabs_shape_dividers' ); 939 940 $shapes_options = [ 941 '' => esc_html__( 'None', 'elementor' ), 942 ]; 943 944 foreach ( Shapes::get_shapes() as $shape_name => $shape_props ) { 945 $shapes_options[ $shape_name ] = $shape_props['title']; 946 } 947 948 foreach ( [ 949 'top' => esc_html__( 'Top', 'elementor' ), 950 'bottom' => esc_html__( 'Bottom', 'elementor' ), 951 ] as $side => $side_label ) { 952 $base_control_key = "shape_divider_$side"; 953 954 $this->start_controls_tab( 955 "tab_$base_control_key", 956 [ 957 'label' => $side_label, 958 ] 959 ); 960 961 $this->add_control( 962 $base_control_key, 963 [ 964 'label' => esc_html__( 'Type', 'elementor' ), 965 'type' => Controls_Manager::SELECT, 966 'options' => $shapes_options, 967 'render_type' => 'none', 968 'frontend_available' => true, 969 ] 970 ); 971 972 $this->add_control( 973 $base_control_key . '_color', 974 [ 975 'label' => esc_html__( 'Color', 'elementor' ), 976 'type' => Controls_Manager::COLOR, 977 'condition' => [ 978 "shape_divider_$side!" => '', 979 ], 980 'selectors' => [ 981 "{{WRAPPER}} > .elementor-shape-$side .elementor-shape-fill" => 'fill: {{UNIT}};', 982 ], 983 ] 984 ); 985 986 $this->add_responsive_control( 987 $base_control_key . '_width', 988 [ 989 'label' => esc_html__( 'Width', 'elementor' ), 990 'type' => Controls_Manager::SLIDER, 991 'default' => [ 992 'unit' => '%', 993 ], 994 'tablet_default' => [ 995 'unit' => '%', 996 ], 997 'mobile_default' => [ 998 'unit' => '%', 999 ], 1000 'range' => [ 1001 '%' => [ 1002 'min' => 100, 1003 'max' => 300, 1004 ], 1005 ], 1006 'condition' => [ 1007 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'height_only', Shapes::FILTER_EXCLUDE ) ), 1008 ], 1009 'selectors' => [ 1010 "{{WRAPPER}} > .elementor-shape-$side svg" => 'width: calc({{SIZE}}{{UNIT}} + 1.3px)', 1011 ], 1012 ] 1013 ); 1014 1015 $this->add_responsive_control( 1016 $base_control_key . '_height', 1017 [ 1018 'label' => esc_html__( 'Height', 'elementor' ), 1019 'type' => Controls_Manager::SLIDER, 1020 'range' => [ 1021 'px' => [ 1022 'max' => 500, 1023 ], 1024 ], 1025 'condition' => [ 1026 "shape_divider_$side!" => '', 1027 ], 1028 'selectors' => [ 1029 "{{WRAPPER}} > .elementor-shape-$side svg" => 'height: {{SIZE}}{{UNIT}};', 1030 ], 1031 ] 1032 ); 1033 1034 $this->add_control( 1035 $base_control_key . '_flip', 1036 [ 1037 'label' => esc_html__( 'Flip', 'elementor' ), 1038 'type' => Controls_Manager::SWITCHER, 1039 'condition' => [ 1040 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_flip' ) ), 1041 ], 1042 'selectors' => [ 1043 "{{WRAPPER}} > .elementor-shape-$side svg" => 'transform: translateX(-50%) rotateY(180deg)', 1044 ], 1045 ] 1046 ); 1047 1048 $this->add_control( 1049 $base_control_key . '_negative', 1050 [ 1051 'label' => esc_html__( 'Invert', 'elementor' ), 1052 'type' => Controls_Manager::SWITCHER, 1053 'frontend_available' => true, 1054 'condition' => [ 1055 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_negative' ) ), 1056 ], 1057 'render_type' => 'none', 1058 ] 1059 ); 1060 1061 $this->add_control( 1062 $base_control_key . '_above_content', 1063 [ 1064 'label' => esc_html__( 'Bring to Front', 'elementor' ), 1065 'type' => Controls_Manager::SWITCHER, 1066 'selectors' => [ 1067 "{{WRAPPER}} > .elementor-shape-$side" => 'z-index: 2; pointer-events: none', 1068 ], 1069 'condition' => [ 1070 "shape_divider_$side!" => '', 1071 ], 1072 ] 1073 ); 1074 1075 $this->end_controls_tab(); 1076 } 1077 1078 $this->end_controls_tabs(); 1079 1080 $this->end_controls_section(); 1081 1082 // Section Typography 1083 $this->start_controls_section( 1084 'section_typo', 1085 [ 1086 'label' => esc_html__( 'Typography', 'elementor' ), 1087 'tab' => Controls_Manager::TAB_STYLE, 1088 ] 1089 ); 1090 1091 $this->add_control( 1092 'heading_color', 1093 [ 1094 'label' => esc_html__( 'Heading Color', 'elementor' ), 1095 'type' => Controls_Manager::COLOR, 1096 'default' => '', 1097 'selectors' => [ 1098 '{{WRAPPER}} .elementor-heading-title' => 'color: {{VALUE}};', 1099 ], 1100 'separator' => 'none', 1101 ] 1102 ); 1103 1104 $this->add_control( 1105 'color_text', 1106 [ 1107 'label' => esc_html__( 'Text Color', 'elementor' ), 1108 'type' => Controls_Manager::COLOR, 1109 'default' => '', 1110 'selectors' => [ 1111 '{{WRAPPER}}' => 'color: {{VALUE}};', 1112 ], 1113 ] 1114 ); 1115 1116 $this->add_control( 1117 'color_link', 1118 [ 1119 'label' => esc_html__( 'Link Color', 'elementor' ), 1120 'type' => Controls_Manager::COLOR, 1121 'default' => '', 1122 'selectors' => [ 1123 '{{WRAPPER}} a' => 'color: {{VALUE}};', 1124 ], 1125 ] 1126 ); 1127 1128 $this->add_control( 1129 'color_link_hover', 1130 [ 1131 'label' => esc_html__( 'Link Hover Color', 'elementor' ), 1132 'type' => Controls_Manager::COLOR, 1133 'default' => '', 1134 'selectors' => [ 1135 '{{WRAPPER}} a:hover' => 'color: {{VALUE}};', 1136 ], 1137 ] 1138 ); 1139 1140 $this->add_control( 1141 'text_align', 1142 [ 1143 'label' => esc_html__( 'Text Align', 'elementor' ), 1144 'type' => Controls_Manager::CHOOSE, 1145 'options' => [ 1146 'left' => [ 1147 'title' => esc_html__( 'Left', 'elementor' ), 1148 'icon' => 'eicon-text-align-left', 1149 ], 1150 'center' => [ 1151 'title' => esc_html__( 'Center', 'elementor' ), 1152 'icon' => 'eicon-text-align-center', 1153 ], 1154 'right' => [ 1155 'title' => esc_html__( 'Right', 'elementor' ), 1156 'icon' => 'eicon-text-align-right', 1157 ], 1158 'justify' => [ 1159 'title' => __( 'Justified', 'elementor' ), 1160 'icon' => 'eicon-text-align-justify', 1161 ], 1162 ], 1163 'selectors' => [ 1164 '{{WRAPPER}} > .elementor-container' => 'text-align: {{VALUE}};', 1165 ], 1166 ] 1167 ); 1168 1169 $this->end_controls_section(); 1170 1171 // Section Advanced 1172 $this->start_controls_section( 1173 'section_advanced', 1174 [ 1175 'label' => esc_html__( 'Advanced', 'elementor' ), 1176 'tab' => Controls_Manager::TAB_ADVANCED, 1177 ] 1178 ); 1179 1180 $this->add_responsive_control( 1181 'margin', 1182 [ 1183 'label' => esc_html__( 'Margin', 'elementor' ), 1184 'type' => Controls_Manager::DIMENSIONS, 1185 'size_units' => [ 'px', 'em', '%', 'rem' ], 1186 'allowed_dimensions' => 'vertical', 1187 'placeholder' => [ 1188 'top' => '', 1189 'right' => 'auto', 1190 'bottom' => '', 1191 'left' => 'auto', 1192 ], 1193 'selectors' => [ 1194 '{{WRAPPER}}' => 'margin-top: {{TOP}}{{UNIT}}; margin-bottom: {{BOTTOM}}{{UNIT}};', 1195 ], 1196 ] 1197 ); 1198 1199 $this->add_responsive_control( 1200 'padding', 1201 [ 1202 'label' => esc_html__( 'Padding', 'elementor' ), 1203 'type' => Controls_Manager::DIMENSIONS, 1204 'size_units' => [ 'px', 'em', '%', 'rem' ], 1205 'selectors' => [ 1206 '{{WRAPPER}}' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', 1207 ], 1208 ] 1209 ); 1210 1211 $this->add_responsive_control( 1212 'z_index', 1213 [ 1214 'label' => esc_html__( 'Z-Index', 'elementor' ), 1215 'type' => Controls_Manager::NUMBER, 1216 'min' => 0, 1217 'selectors' => [ 1218 '{{WRAPPER}}' => 'z-index: {{VALUE}};', 1219 ], 1220 ] 1221 ); 1222 1223 $this->add_control( 1224 '_element_id', 1225 [ 1226 'label' => esc_html__( 'CSS ID', 'elementor' ), 1227 'type' => Controls_Manager::TEXT, 1228 'default' => '', 1229 'dynamic' => [ 1230 'active' => true, 1231 ], 1232 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ), 1233 'style_transfer' => false, 1234 'classes' => 'elementor-control-direction-ltr', 1235 ] 1236 ); 1237 1238 $this->add_control( 1239 'css_classes', 1240 [ 1241 'label' => esc_html__( 'CSS Classes', 'elementor' ), 1242 'type' => Controls_Manager::TEXT, 1243 'default' => '', 1244 'dynamic' => [ 1245 'active' => true, 1246 ], 1247 'prefix_class' => '', 1248 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ), 1249 'classes' => 'elementor-control-direction-ltr', 1250 ] 1251 ); 1252 1253 $this->end_controls_section(); 1254 1255 $this->start_controls_section( 1256 'section_effects', 1257 [ 1258 'label' => esc_html__( 'Motion Effects', 'elementor' ), 1259 'tab' => Controls_Manager::TAB_ADVANCED, 1260 ] 1261 ); 1262 1263 $this->add_responsive_control( 1264 'animation', 1265 [ 1266 'label' => esc_html__( 'Entrance Animation', 'elementor' ), 1267 'type' => Controls_Manager::ANIMATION, 1268 'frontend_available' => true, 1269 ] 1270 ); 1271 1272 $this->add_control( 1273 'animation_duration', 1274 [ 1275 'label' => esc_html__( 'Animation Duration', 'elementor' ), 1276 'type' => Controls_Manager::SELECT, 1277 'default' => '', 1278 'options' => [ 1279 'slow' => esc_html__( 'Slow', 'elementor' ), 1280 '' => esc_html__( 'Normal', 'elementor' ), 1281 'fast' => esc_html__( 'Fast', 'elementor' ), 1282 ], 1283 'prefix_class' => 'animated-', 1284 'condition' => [ 1285 'animation!' => '', 1286 ], 1287 ] 1288 ); 1289 1290 $this->add_control( 1291 'animation_delay', 1292 [ 1293 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)', 1294 'type' => Controls_Manager::NUMBER, 1295 'default' => '', 1296 'min' => 0, 1297 'step' => 100, 1298 'condition' => [ 1299 'animation!' => '', 1300 ], 1301 'render_type' => 'none', 1302 'frontend_available' => true, 1303 ] 1304 ); 1305 1306 $this->end_controls_section(); 1307 1308 // Section Responsive 1309 $this->start_controls_section( 1310 '_section_responsive', 1311 [ 1312 'label' => esc_html__( 'Responsive', 'elementor' ), 1313 'tab' => Controls_Manager::TAB_ADVANCED, 1314 ] 1315 ); 1316 1317 $this->add_control( 1318 'reverse_order_tablet', 1319 [ 1320 'label' => esc_html__( 'Reverse Columns', 'elementor' ) . ' (' . esc_html__( 'Tablet', 'elementor' ) . ')', 1321 'type' => Controls_Manager::SWITCHER, 1322 'default' => '', 1323 'prefix_class' => 'elementor-', 1324 'return_value' => 'reverse-tablet', 1325 ] 1326 ); 1327 1328 $this->add_control( 1329 'reverse_order_mobile', 1330 [ 1331 'label' => esc_html__( 'Reverse Columns', 'elementor' ) . ' (' . esc_html__( 'Mobile', 'elementor' ) . ')', 1332 'type' => Controls_Manager::SWITCHER, 1333 'default' => '', 1334 'prefix_class' => 'elementor-', 1335 'return_value' => 'reverse-mobile', 1336 ] 1337 ); 1338 1339 $this->add_control( 1340 'heading_visibility', 1341 [ 1342 'label' => esc_html__( 'Visibility', 'elementor' ), 1343 'type' => Controls_Manager::HEADING, 1344 'separator' => 'before', 1345 ] 1346 ); 1347 1348 $this->add_control( 1349 'responsive_description', 1350 [ 1351 'raw' => esc_html__( 'Responsive visibility will take effect only on preview or live page, and not while editing in Elementor.', 'elementor' ), 1352 'type' => Controls_Manager::RAW_HTML, 1353 'content_classes' => 'elementor-descriptor', 1354 ] 1355 ); 1356 1357 $this->add_hidden_device_controls(); 1358 1359 $this->end_controls_section(); 1360 1361 Plugin::$instance->controls_manager->add_custom_attributes_controls( $this ); 1362 1363 Plugin::$instance->controls_manager->add_custom_css_controls( $this ); 1364 } 1365 1366 /** 1367 * Render section output in the editor. 1368 * 1369 * Used to generate the live preview, using a Backbone JavaScript template. 1370 * 1371 * @since 2.9.0 1372 * @access protected 1373 */ 1374 protected function content_template() { 1375 ?> 1376 <# 1377 if ( settings.background_video_link ) { 1378 let videoAttributes = 'autoplay muted playsinline'; 1379 1380 if ( ! settings.background_play_once ) { 1381 videoAttributes += ' loop'; 1382 } 1383 1384 view.addRenderAttribute( 'background-video-container', 'class', 'elementor-background-video-container' ); 1385 1386 if ( ! settings.background_play_on_mobile ) { 1387 view.addRenderAttribute( 'background-video-container', 'class', 'elementor-hidden-mobile' ); 1388 } 1389 #> 1390 <div {{{ view.getRenderAttributeString( 'background-video-container' ) }}}> 1391 <div class="elementor-background-video-embed"></div> 1392 <video class="elementor-background-video-hosted elementor-html5-video" {{ videoAttributes }}></video> 1393 </div> 1394 <# } #> 1395 <div class="elementor-background-overlay"></div> 1396 <div class="elementor-shape elementor-shape-top"></div> 1397 <div class="elementor-shape elementor-shape-bottom"></div> 1398 <div class="elementor-container elementor-column-gap-{{ settings.gap }}"> 1399 <?php if ( ! Plugin::$instance->experiments->is_feature_active( 'e_dom_optimization' ) ) { ?> 1400 <div class="elementor-row"></div> 1401 <?php } ?> 1402 </div> 1403 <?php 1404 } 1405 1406 /** 1407 * Before section rendering. 1408 * 1409 * Used to add stuff before the section element. 1410 * 1411 * @since 1.0.0 1412 * @access public 1413 */ 1414 public function before_render() { 1415 $settings = $this->get_settings_for_display(); 1416 ?> 1417 <<?php 1418 // PHPCS - the method get_html_tag is safe. 1419 echo $this->get_html_tag(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 1420 ?> <?php $this->print_render_attribute_string( '_wrapper' ); ?>> 1421 <?php 1422 if ( 'video' === $settings['background_background'] ) : 1423 if ( $settings['background_video_link'] ) : 1424 $video_properties = Embed::get_video_properties( $settings['background_video_link'] ); 1425 1426 $this->add_render_attribute( 'background-video-container', 'class', 'elementor-background-video-container' ); 1427 1428 if ( ! $settings['background_play_on_mobile'] ) { 1429 $this->add_render_attribute( 'background-video-container', 'class', 'elementor-hidden-phone' ); 1430 } 1431 ?> 1432 <div <?php $this->print_render_attribute_string( 'background-video-container' ); ?>> 1433 <?php if ( $video_properties ) : ?> 1434 <div class="elementor-background-video-embed"></div> 1435 <?php 1436 else : 1437 $video_tag_attributes = 'autoplay muted playsinline'; 1438 if ( 'yes' !== $settings['background_play_once'] ) : 1439 $video_tag_attributes .= ' loop'; 1440 endif; 1441 ?> 1442 <video class="elementor-background-video-hosted elementor-html5-video" <?php 1443 // PHPCS - the variable $video_tag_attributes is a plain string. 1444 echo $video_tag_attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 1445 ?>></video> 1446 <?php endif; ?> 1447 </div> 1448 <?php 1449 endif; 1450 endif; 1451 1452 $has_background_overlay = in_array( $settings['background_overlay_background'], [ 'classic', 'gradient' ], true ) || 1453 in_array( $settings['background_overlay_hover_background'], [ 'classic', 'gradient' ], true ); 1454 1455 if ( $has_background_overlay ) : 1456 ?> 1457 <div class="elementor-background-overlay"></div> 1458 <?php 1459 endif; 1460 1461 if ( $settings['shape_divider_top'] ) { 1462 $this->print_shape_divider( 'top' ); 1463 } 1464 1465 if ( $settings['shape_divider_bottom'] ) { 1466 $this->print_shape_divider( 'bottom' ); 1467 } 1468 ?> 1469 <div class="elementor-container elementor-column-gap-<?php echo esc_attr( $settings['gap'] ); ?>"> 1470 <?php if ( ! Plugin::$instance->experiments->is_feature_active( 'e_dom_optimization' ) ) { ?> 1471 <div class="elementor-row"> 1472 <?php } 1473 } 1474 1475 /** 1476 * After section rendering. 1477 * 1478 * Used to add stuff after the section element. 1479 * 1480 * @since 1.0.0 1481 * @access public 1482 */ 1483 public function after_render() { 1484 ?> 1485 <?php if ( ! Plugin::$instance->experiments->is_feature_active( 'e_dom_optimization' ) ) { ?> 1486 </div> 1487 <?php } ?> 1488 </div> 1489 </<?php 1490 // PHPCS - the method get_html_tag is safe. 1491 echo $this->get_html_tag(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 1492 ?>> 1493 <?php 1494 } 1495 1496 /** 1497 * Add section render attributes. 1498 * 1499 * Used to add attributes to the current section wrapper HTML tag. 1500 * 1501 * @since 1.3.0 1502 * @access protected 1503 */ 1504 protected function add_render_attributes() { 1505 1506 $section_type = $this->get_data( 'isInner' ) ? 'inner' : 'top'; 1507 1508 $this->add_render_attribute( 1509 '_wrapper', 'class', [ 1510 'elementor-section', 1511 'elementor-' . $section_type . '-section', 1512 ] 1513 ); 1514 1515 parent::add_render_attributes(); 1516 } 1517 1518 /** 1519 * Get default child type. 1520 * 1521 * Retrieve the section child type based on element data. 1522 * 1523 * @since 1.0.0 1524 * @access protected 1525 * 1526 * @param array $element_data Element ID. 1527 * 1528 * @return Element_Base Section default child type. 1529 */ 1530 protected function _get_default_child_type( array $element_data ) { 1531 return Plugin::$instance->elements_manager->get_element_types( 'column' ); 1532 } 1533 1534 /** 1535 * Get HTML tag. 1536 * 1537 * Retrieve the section element HTML tag. 1538 * 1539 * @since 1.5.3 1540 * @access private 1541 * 1542 * @return string Section HTML tag. 1543 */ 1544 protected function get_html_tag() { 1545 $html_tag = $this->get_settings( 'html_tag' ); 1546 1547 if ( empty( $html_tag ) ) { 1548 $html_tag = 'section'; 1549 } 1550 1551 return Utils::validate_html_tag( $html_tag ); 1552 } 1553 1554 /** 1555 * Print section shape divider. 1556 * 1557 * Used to generate the shape dividers HTML. 1558 * 1559 * @since 1.3.0 1560 * @access private 1561 * 1562 * @param string $side Shape divider side, used to set the shape key. 1563 */ 1564 protected function print_shape_divider( $side ) { 1565 $settings = $this->get_active_settings(); 1566 $base_setting_key = "shape_divider_$side"; 1567 $negative = ! empty( $settings[ $base_setting_key . '_negative' ] ); 1568 $shape_path = Shapes::get_shape_path( $settings[ $base_setting_key ], $negative ); 1569 if ( ! is_file( $shape_path ) || ! is_readable( $shape_path ) ) { 1570 return; 1571 } 1572 ?> 1573 <div class="elementor-shape elementor-shape-<?php echo esc_attr( $side ); ?>" data-negative="<?php 1574 // PHPCS - the variable $negative is getting a setting value with a strict structure. 1575 echo var_export( $negative ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 1576 ?>"> 1577 <?php 1578 // PHPCS - The file content is being read from a strict file path structure. 1579 echo file_get_contents( $shape_path ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 1580 ?> 1581 </div> 1582 <?php 1583 } 1584 }