PluginProbe
Elementor Website Builder – more than just a page builder / 3.22.1
Elementor Website Builder – more than just a page builder v3.22.1
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / includes / elements / section.php

section.php in Elementor Website Builder – more than just a page builder 3.22.1, at includes/elements/section.php

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