Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/elementor/includes/widgets/common-base.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 + namespace Elementor;
3 +
4 + if ( ! defined( 'ABSPATH' ) ) {
5 + exit; // Exit if accessed directly.
6 + }
7 +
8 + /**
9 + * Elementor common widget.
10 + *
11 + * Elementor base widget that gives you all the advanced options of the basic
12 + * widget.
13 + *
14 + * @since 1.0.0
15 + */
16 + class Widget_Common_Base extends Widget_Base {
17 +
18 + const WRAPPER_SELECTOR = '{{WRAPPER}} .elementor-widget-container';
19 + const WRAPPER_SELECTOR_CHILD = '{{WRAPPER}} > .elementor-widget-container';
20 + const WRAPPER_SELECTOR_HOVER = '{{WRAPPER}}:hover .elementor-widget-container';
21 + const WRAPPER_SELECTOR_HOVER_CHILD = '{{WRAPPER}}:hover > .elementor-widget-container';
22 + const MASK_SELECTOR_DEFAULT = '{{WRAPPER}}:not( .elementor-widget-image ) .elementor-widget-container';
23 + const MASK_SELECTOR_IMG = '{{WRAPPER}}.elementor-widget-image .elementor-widget-container img';
24 + const TRANSFORM_SELECTOR_CLASS = ' > .elementor-widget-container';
25 + const MARGIN = 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};';
26 +
27 + /**
28 + * Get widget name.
29 + *
30 + * Retrieve common widget name.
31 + *
32 + * @since 1.0.0
33 + * @access public
34 + *
35 + * @return string Widget name.
36 + */
37 + public function get_name() {
38 + return 'common-base';
39 + }
40 +
41 + /**
42 + * Show in panel.
43 + *
44 + * Whether to show the common widget in the panel or not.
45 + *
46 + * @since 1.0.0
47 + * @access public
48 + *
49 + * @return bool Whether to show the widget in the panel.
50 + */
51 + public function show_in_panel() {
52 + return false;
53 + }
54 +
55 + /**
56 + * Get Responsive Device Args
57 + *
58 + * Receives an array of device args, and duplicates it for each active breakpoint.
59 + * Returns an array of device args.
60 + *
61 + * @since 3.4.7
62 + * @deprecated 3.7.0 Not needed anymore because responsive conditioning in the Editor was fixed in v3.7.0.
63 + * @access protected
64 + *
65 + * @param array $args arguments to duplicate per breakpoint.
66 + * @param array $devices_to_exclude
67 + *
68 + * @return array responsive device args
69 + */
70 + protected function get_responsive_device_args( array $args, array $devices_to_exclude = [] ) {
71 + Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.7.0' );
72 +
73 + $device_args = [];
74 + $breakpoints = Plugin::$instance->breakpoints->get_active_breakpoints();
75 +
76 + foreach ( $breakpoints as $breakpoint_key => $breakpoint ) {
77 + // If the device is not excluded, add it to the device args array.
78 + if ( ! in_array( $breakpoint_key, $devices_to_exclude, true ) ) {
79 + $parsed_device_args = $this->parse_device_args_placeholders( $args, $breakpoint_key );
80 +
81 + $device_args[ $breakpoint_key ] = $parsed_device_args;
82 + }
83 + }
84 +
85 + return $device_args;
86 + }
87 +
88 + /**
89 + * Parse Device Args Placeholders
90 + *
91 + * Receives an array of args. Iterates over the args, and replaces the {{DEVICE}} placeholder, if exists, with the
92 + * passed breakpoint key.
93 + *
94 + * @since 3.4.7
95 + * @access private
96 + *
97 + * @param array $args
98 + * @param string $breakpoint_key
99 + * @return array parsed device args
100 + */
101 + private function parse_device_args_placeholders( array $args, $breakpoint_key ) {
102 + $parsed_args = [];
103 +
104 + foreach ( $args as $arg_key => $arg_value ) {
105 + $arg_key = str_replace( '{{DEVICE}}', $breakpoint_key, $arg_key );
106 +
107 + if ( is_array( $arg_value ) ) {
108 + $arg_value = $this->parse_device_args_placeholders( $arg_value, $breakpoint_key );
109 + }
110 +
111 + $parsed_args[ $arg_key ] = $arg_value;
112 + }
113 +
114 + return $parsed_args;
115 + }
116 +
117 + /**
118 + * @param String $shape Shape name.
119 + *
120 + * @return string The shape path in the assets folder.
121 + */
122 + private function get_shape_url( $shape ) {
123 + return ELEMENTOR_ASSETS_URL . 'mask-shapes/' . $shape . '.svg';
124 + }
125 +
126 + /**
127 + * Get a list of the available mask shapes.
128 + *
129 + * @param bool $custom_button Determine if the output should contain `Custom Mask` button.
130 + *
131 + * @return array A list of mask shapes.
132 + */
133 + private function get_shapes( $custom_button = true ): array {
134 + $shapes = [
135 + 'circle' => [
136 + 'title' => esc_html__( 'Circle', 'elementor' ),
137 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/circle.svg',
138 + ],
139 + 'oval-vertical' => [
140 + 'title' => esc_html__( 'Oval vertical', 'elementor' ),
141 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/oval-vertical.svg',
142 + ],
143 + 'oval-horizontal' => [
144 + 'title' => esc_html__( 'Oval horizontal', 'elementor' ),
145 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/oval-horizontal.svg',
146 + ],
147 + 'pill-vertical' => [
148 + 'title' => esc_html__( 'Pill vertical', 'elementor' ),
149 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/pill-vertical.svg',
150 + ],
151 + 'pill-horizontal' => [
152 + 'title' => esc_html__( 'Pill horizontal', 'elementor' ),
153 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/pill-horizontal.svg',
154 + ],
155 + 'triangle' => [
156 + 'title' => esc_html__( 'Triangle', 'elementor' ),
157 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/triangle.svg',
158 + ],
159 + 'diamond' => [
160 + 'title' => esc_html__( 'Diamond', 'elementor' ),
161 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/diamond.svg',
162 + ],
163 + 'pentagon' => [
164 + 'title' => esc_html__( 'Pentagon', 'elementor' ),
165 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/pentagon.svg',
166 + ],
167 + 'hexagon-vertical' => [
168 + 'title' => esc_html__( 'Hexagon vertical', 'elementor' ),
169 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/hexagon-vertical.svg',
170 + ],
171 + 'hexagon-horizontal' => [
172 + 'title' => esc_html__( 'Hexagon horizontal', 'elementor' ),
173 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/hexagon-horizontal.svg',
174 + ],
175 + 'heptagon' => [
176 + 'title' => esc_html__( 'Heptagon', 'elementor' ),
177 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/heptagon.svg',
178 + ],
179 + 'octagon' => [
180 + 'title' => esc_html__( 'Octagon', 'elementor' ),
181 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/octagon.svg',
182 + ],
183 + 'parallelogram-right' => [
184 + 'title' => esc_html__( 'Parallelogram right', 'elementor' ),
185 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/parallelogram-right.svg',
186 + ],
187 + 'parallelogram-left' => [
188 + 'title' => esc_html__( 'Parallelogram left', 'elementor' ),
189 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/parallelogram-left.svg',
190 + ],
191 + 'trapezoid-up' => [
192 + 'title' => esc_html__( 'Trapezoid Up', 'elementor' ),
193 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/trapezoid-up.svg',
194 + ],
195 + 'trapezoid-down' => [
196 + 'title' => esc_html__( 'Trapezoid Down', 'elementor' ),
197 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/trapezoid-down.svg',
198 + ],
199 + 'flower' => [
200 + 'title' => esc_html__( 'Flower', 'elementor' ),
201 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/flower.svg',
202 + ],
203 + 'sketch' => [
204 + 'title' => esc_html__( 'Sketch', 'elementor' ),
205 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/sketch.svg',
206 + ],
207 + 'hexagon' => [
208 + 'title' => esc_html__( 'Hexagon Donut', 'elementor' ),
209 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/hexagon.svg',
210 + ],
211 + 'blob' => [
212 + 'title' => esc_html__( 'Blob', 'elementor' ),
213 + 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/blob.svg',
214 + ],
215 + ];
216 +
217 + $shapes = array_merge( $shapes, self::get_additional_mask_shapes() );
218 +
219 + if ( $custom_button ) {
220 + $shapes['custom'] = [
221 + 'type' => 'button',
222 + 'title' => esc_html__( 'Custom Mask', 'elementor' ),
223 + ];
224 + }
225 +
226 + return $shapes;
227 + }
228 +
229 + /**
230 + * Get additional mask shapes.
231 + *
232 + * Used to add custom mask shapes to elementor.
233 + *
234 + * @since 3.30.0
235 + *
236 + * @return array A list of additional mask shapes.
237 + */
238 + private static function get_additional_mask_shapes(): array {
239 + static $additional_mask_shapes = null;
240 +
241 + if ( null !== $additional_mask_shapes ) {
242 + return $additional_mask_shapes;
243 + }
244 +
245 + $additional_mask_shapes = [];
246 +
247 + /**
248 + * Additional mask shapes.
249 + *
250 + * Filters the mask shapes used by Elementor to add additional mask shapes.
251 + *
252 + * @since 3.30.0
253 + *
254 + * @param array $additional_mask_shapes Additional mask shapes.
255 + */
256 + $additional_mask_shapes = apply_filters( 'elementor/mask_shapes/additional_shapes', $additional_mask_shapes );
257 +
258 + return $additional_mask_shapes;
259 + }
260 +
261 + /**
262 + * Gets a string of CSS rules to apply, and returns an array of selectors with those rules.
263 + * This function has been created in order to deal with masking for image widget.
264 + * For most of the widgets the mask is being applied to the wrapper itself, but in the case of an image widget,
265 + * the `img` tag should be masked directly. So instead of writing a lot of selectors every time,
266 + * this function builds both of those selectors easily.
267 + *
268 + * @param string $rules The CSS rules to apply.
269 + *
270 + * @return array Selectors with the rules applied.
271 + */
272 + private function get_mask_selectors( $rules ) {
273 + $mask_selectors = [
274 + 'default' => static::MASK_SELECTOR_DEFAULT,
275 + 'image' => static::MASK_SELECTOR_IMG,
276 + ];
277 +
278 + return [
279 + $mask_selectors['default'] . ', ' . $mask_selectors['image'] => $rules,
280 + ];
281 + }
282 +
283 + /**
284 + * Register the Layout section.
285 + *
286 + * @return void
287 + */
288 + private function register_layout_section() {
289 + $this->start_controls_section(
290 + '_section_style',
291 + [
292 + 'label' => esc_html__( 'Layout', 'elementor' ),
293 + 'tab' => Controls_Manager::TAB_ADVANCED,
294 + ]
295 + );
296 +
297 + // Element Name for the Navigator
298 + $this->add_control(
299 + '_title',
300 + [
301 + 'label' => esc_html__( 'Title', 'elementor' ),
302 + 'type' => Controls_Manager::HIDDEN,
303 + 'render_type' => 'none',
304 + ]
305 + );
306 +
307 + $this->add_responsive_control(
308 + '_margin',
309 + [
310 + 'label' => esc_html__( 'Margin', 'elementor' ),
311 + 'type' => Controls_Manager::DIMENSIONS,
312 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
313 + 'selectors' => [
314 + static::WRAPPER_SELECTOR_CHILD => static::MARGIN,
315 + ],
316 + ]
317 + );
318 +
319 + $this->add_responsive_control(
320 + '_padding',
321 + [
322 + 'label' => esc_html__( 'Padding', 'elementor' ),
323 + 'type' => Controls_Manager::DIMENSIONS,
324 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
325 + 'selectors' => [
326 + static::WRAPPER_SELECTOR_CHILD => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
327 + ],
328 + ]
329 + );
330 +
331 + $experiments_manager = Plugin::$instance->experiments;
332 + $is_container_active = $experiments_manager->is_feature_active( 'container' );
333 +
334 + $this->add_responsive_control(
335 + '_element_width',
336 + [
337 + 'label' => esc_html__( 'Width', 'elementor' ),
338 + 'type' => Controls_Manager::SELECT,
339 + 'default' => '',
340 + 'options' => [
341 + '' => esc_html__( 'Default', 'elementor' ),
342 + 'inherit' => esc_html__( 'Full Width', 'elementor' ) . ' (100%)',
343 + 'auto' => esc_html__( 'Inline', 'elementor' ) . ' (auto)',
344 + 'initial' => esc_html__( 'Custom', 'elementor' ),
345 + ],
346 + 'selectors_dictionary' => [
347 + 'inherit' => '100%',
348 + ],
349 + 'prefix_class' => 'elementor-widget%s__width-',
350 + 'selectors' => [
351 + '{{WRAPPER}}' => 'width: {{VALUE}}; max-width: {{VALUE}}',
352 + ],
353 + ]
354 + );
355 +
356 + $this->add_responsive_control(
357 + '_element_custom_width',
358 + [
359 + 'label' => esc_html__( 'Custom Width', 'elementor' ),
360 + 'type' => Controls_Manager::SLIDER,
361 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
362 + 'default' => [
363 + 'unit' => '%',
364 + ],
365 + 'range' => [
366 + 'px' => [
367 + 'max' => 1000,
368 + ],
369 + ],
370 + 'selectors' => [
371 + '{{WRAPPER}}' => '--container-widget-width: {{SIZE}}{{UNIT}}; --container-widget-flex-grow: 0; width: var( --container-widget-width, {{SIZE}}{{UNIT}} ); max-width: {{SIZE}}{{UNIT}}',
372 + ],
373 + 'condition' => [ '_element_width' => 'initial' ],
374 + ]
375 + );
376 +
377 + $this->add_control(
378 + '_heading_grid_item',
379 + [
380 + 'type' => Controls_Manager::HEADING,
381 + 'label' => esc_html__( 'Grid Item', 'elementor' ),
382 + 'separator' => 'before',
383 + ]
384 + );
385 +
386 + $this->add_responsive_control(
387 + '_grid_column',
388 + [
389 + 'label' => esc_html__( 'Column Span', 'elementor' ),
390 + 'type' => Controls_Manager::SELECT,
391 + 'options' => [
392 + '' => ' Default',
393 + '1' => '1',
394 + '2' => '2',
395 + '3' => '3',
396 + '4' => '4',
397 + '5' => '5',
398 + '6' => '6',
399 + '7' => '7',
400 + '8' => '8',
401 + '9' => '9',
402 + '10' => '10',
403 + '11' => '11',
404 + '12' => '12',
405 + 'custom' => 'Custom',
406 + ],
407 + 'selectors' => [
408 + '{{WRAPPER}}' => 'grid-column: span {{VALUE}};',
409 + ],
410 + ]
411 + );
412 +
413 + $this->add_responsive_control(
414 + '_grid_column_custom',
415 + [
416 + 'label' => esc_html__( 'Custom', 'elementor' ),
417 + 'type' => Controls_Manager::TEXT,
418 + 'ai' => [
419 + 'active' => false,
420 + ],
421 + 'selectors' => [
422 + '{{WRAPPER}}' => 'grid-column: {{VALUE}}',
423 + ],
424 + 'condition' => [
425 + '_grid_column' => 'custom',
426 + ],
427 + ]
428 + );
429 +
430 + $this->add_responsive_control(
431 + '_grid_row',
432 + [
433 + 'label' => esc_html__( 'Row Span', 'elementor' ),
434 + 'type' => Controls_Manager::SELECT,
435 + 'options' => [
436 + '' => ' Default',
437 + '1' => '1',
438 + '2' => '2',
439 + '3' => '3',
440 + '4' => '4',
441 + '5' => '5',
442 + '6' => '6',
443 + '7' => '7',
444 + '8' => '8',
445 + '9' => '9',
446 + '10' => '10',
447 + '11' => '11',
448 + '12' => '12',
449 + 'custom' => 'Custom',
450 + ],
451 + 'selectors' => [
452 + '{{WRAPPER}}' => 'grid-row: span {{VALUE}};',
453 + ],
454 + ]
455 + );
456 +
457 + $this->add_responsive_control(
458 + '_grid_row_custom',
459 + [
460 + 'label' => esc_html__( 'Custom', 'elementor' ),
461 + 'type' => Controls_Manager::TEXT,
462 + 'separator' => 'after',
463 + 'ai' => [
464 + 'active' => false,
465 + ],
466 + 'selectors' => [
467 + '{{WRAPPER}}' => 'grid-row: {{VALUE}}',
468 + ],
469 + 'condition' => [
470 + '_grid_row' => 'custom',
471 + ],
472 + ]
473 + );
474 +
475 + // Register Flex controls only if the Container experiment is active.
476 + if ( $is_container_active ) {
477 + $this->add_group_control(
478 + Group_Control_Flex_Item::get_type(),
479 + [
480 + 'name' => '_flex',
481 + // Hack to increase specificity and make sure that the current widget overrides the
482 + // parent flex settings.
483 + 'selector' => '{{WRAPPER}}.elementor-element',
484 + 'include' => [
485 + 'align_self',
486 + 'order',
487 + 'order_custom',
488 + 'size',
489 + 'grow',
490 + 'shrink',
491 + ],
492 + 'fields_options' => [
493 + 'align_self' => [
494 + 'separator' => 'before',
495 + ],
496 + ],
497 + ]
498 + );
499 + }
500 +
501 + $vertical_align_conditions = [
502 + '_element_width!' => '',
503 + '_position' => '',
504 + ];
505 +
506 + if ( $is_container_active ) {
507 + $vertical_align_conditions['_element_vertical_align!'] = ''; // TODO: For BC.
508 + }
509 +
510 + // TODO: For BC - Remove in the future.
511 + $this->add_responsive_control(
512 + '_element_vertical_align',
513 + [
514 + 'label' => esc_html__( 'Vertical Align', 'elementor' ),
515 + 'type' => Controls_Manager::CHOOSE,
516 + 'options' => [
517 + 'flex-start' => [
518 + 'title' => esc_html__( 'Start', 'elementor' ),
519 + 'icon' => 'eicon-v-align-top',
520 + ],
521 + 'center' => [
522 + 'title' => esc_html__( 'Center', 'elementor' ),
523 + 'icon' => 'eicon-v-align-middle',
524 + ],
525 + 'flex-end' => [
526 + 'title' => esc_html__( 'End', 'elementor' ),
527 + 'icon' => 'eicon-v-align-bottom',
528 + ],
529 + ],
530 + 'condition' => $vertical_align_conditions,
531 + 'selectors' => [
532 + '{{WRAPPER}}' => 'align-self: {{VALUE}}',
533 + ],
534 + ]
535 + );
536 +
537 + $this->add_control(
538 + '_position_description',
539 + [
540 + 'type' => Controls_Manager::ALERT,
541 + 'alert_type' => 'warning',
542 + 'heading' => esc_html__( 'Please note!', 'elementor' ),
543 + 'content' => esc_html__( 'Custom positioning is not considered best practice for responsive web design and should not be used too frequently.', 'elementor' ),
544 + 'render_type' => 'ui',
545 + 'condition' => [
546 + '_position!' => '',
547 + ],
548 + ]
549 + );
550 +
551 + $this->add_control(
552 + '_position',
553 + [
554 + 'label' => esc_html__( 'Position', 'elementor' ),
555 + 'type' => Controls_Manager::SELECT,
556 + 'default' => '',
557 + 'options' => [
558 + '' => esc_html__( 'Default', 'elementor' ),
559 + 'absolute' => esc_html__( 'Absolute', 'elementor' ),
560 + 'fixed' => esc_html__( 'Fixed', 'elementor' ),
561 + ],
562 + 'prefix_class' => 'elementor-',
563 + 'frontend_available' => true,
564 + 'separator' => 'before',
565 + ]
566 + );
567 +
568 + $start = is_rtl() ? esc_html__( 'Right', 'elementor' ) : esc_html__( 'Left', 'elementor' );
569 + $end = ! is_rtl() ? esc_html__( 'Right', 'elementor' ) : esc_html__( 'Left', 'elementor' );
570 +
571 + $this->add_control(
572 + '_offset_orientation_h',
573 + [
574 + 'label' => esc_html__( 'Horizontal Orientation', 'elementor' ),
575 + 'type' => Controls_Manager::CHOOSE,
576 + 'toggle' => false,
577 + 'default' => 'start',
578 + 'options' => [
579 + 'start' => [
580 + 'title' => $start,
581 + 'icon' => 'eicon-h-align-left',
582 + ],
583 + 'end' => [
584 + 'title' => $end,
585 + 'icon' => 'eicon-h-align-right',
586 + ],
587 + ],
588 + 'classes' => 'elementor-control-start-end',
589 + 'render_type' => 'ui',
590 + 'condition' => [
591 + '_position!' => '',
592 + ],
593 + ]
594 + );
595 +
596 + $this->add_responsive_control(
597 + '_offset_x',
598 + [
599 + 'label' => esc_html__( 'Offset', 'elementor' ),
600 + 'type' => Controls_Manager::SLIDER,
601 + 'range' => [
602 + 'px' => [
603 + 'min' => -1000,
604 + 'max' => 1000,
605 + ],
606 + '%' => [
607 + 'min' => -200,
608 + 'max' => 200,
609 + ],
610 + 'vw' => [
611 + 'min' => -200,
612 + 'max' => 200,
613 + ],
614 + 'vh' => [
615 + 'min' => -200,
616 + 'max' => 200,
617 + ],
618 + ],
619 + 'default' => [
620 + 'size' => 0,
621 + ],
622 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
623 + 'selectors' => [
624 + 'body:not(.rtl) {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
625 + 'body.rtl {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
626 + ],
627 + 'condition' => [
628 + '_offset_orientation_h!' => 'end',
629 + '_position!' => '',
630 + ],
631 + ]
632 + );
633 +
634 + $this->add_responsive_control(
635 + '_offset_x_end',
636 + [
637 + 'label' => esc_html__( 'Offset', 'elementor' ),
638 + 'type' => Controls_Manager::SLIDER,
639 + 'range' => [
640 + 'px' => [
641 + 'min' => -1000,
642 + 'max' => 1000,
643 + ],
644 + '%' => [
645 + 'min' => -200,
646 + 'max' => 200,
647 + ],
648 + 'vw' => [
649 + 'min' => -200,
650 + 'max' => 200,
651 + ],
652 + 'vh' => [
653 + 'min' => -200,
654 + 'max' => 200,
655 + ],
656 + ],
657 + 'default' => [
658 + 'size' => 0,
659 + ],
660 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
661 + 'selectors' => [
662 + 'body:not(.rtl) {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
663 + 'body.rtl {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
664 + ],
665 + 'condition' => [
666 + '_offset_orientation_h' => 'end',
667 + '_position!' => '',
668 + ],
669 + ]
670 + );
671 +
672 + $this->add_control(
673 + '_offset_orientation_v',
674 + [
675 + 'label' => esc_html__( 'Vertical Orientation', 'elementor' ),
676 + 'type' => Controls_Manager::CHOOSE,
677 + 'toggle' => false,
678 + 'default' => 'start',
679 + 'options' => [
680 + 'start' => [
681 + 'title' => esc_html__( 'Top', 'elementor' ),
682 + 'icon' => 'eicon-v-align-top',
683 + ],
684 + 'end' => [
685 + 'title' => esc_html__( 'Bottom', 'elementor' ),
686 + 'icon' => 'eicon-v-align-bottom',
687 + ],
688 + ],
689 + 'render_type' => 'ui',
690 + 'condition' => [
691 + '_position!' => '',
692 + ],
693 + ]
694 + );
695 +
696 + $this->add_responsive_control(
697 + '_offset_y',
698 + [
699 + 'label' => esc_html__( 'Offset', 'elementor' ),
700 + 'type' => Controls_Manager::SLIDER,
701 + 'range' => [
702 + 'px' => [
703 + 'min' => -1000,
704 + 'max' => 1000,
705 + ],
706 + '%' => [
707 + 'min' => -200,
708 + 'max' => 200,
709 + ],
710 + 'vh' => [
711 + 'min' => -200,
712 + 'max' => 200,
713 + ],
714 + 'vw' => [
715 + 'min' => -200,
716 + 'max' => 200,
717 + ],
718 + ],
719 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
720 + 'default' => [
721 + 'size' => 0,
722 + ],
723 + 'selectors' => [
724 + '{{WRAPPER}}' => 'top: {{SIZE}}{{UNIT}}',
725 + ],
726 + 'condition' => [
727 + '_offset_orientation_v!' => 'end',
728 + '_position!' => '',
729 + ],
730 + ]
731 + );
732 +
733 + $this->add_responsive_control(
734 + '_offset_y_end',
735 + [
736 + 'label' => esc_html__( 'Offset', 'elementor' ),
737 + 'type' => Controls_Manager::SLIDER,
738 + 'range' => [
739 + 'px' => [
740 + 'min' => -1000,
741 + 'max' => 1000,
742 + ],
743 + '%' => [
744 + 'min' => -200,
745 + 'max' => 200,
746 + ],
747 + 'vh' => [
748 + 'min' => -200,
749 + 'max' => 200,
750 + ],
751 + 'vw' => [
752 + 'min' => -200,
753 + 'max' => 200,
754 + ],
755 + ],
756 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
757 + 'default' => [
758 + 'size' => 0,
759 + ],
760 + 'selectors' => [
761 + '{{WRAPPER}}' => 'bottom: {{SIZE}}{{UNIT}}',
762 + ],
763 + 'condition' => [
764 + '_offset_orientation_v' => 'end',
765 + '_position!' => '',
766 + ],
767 + ]
768 + );
769 +
770 + $this->add_responsive_control(
771 + '_z_index',
772 + [
773 + 'label' => esc_html__( 'Z-Index', 'elementor' ),
774 + 'type' => Controls_Manager::NUMBER,
775 + 'selectors' => [
776 + '{{WRAPPER}}' => 'z-index: {{VALUE}};',
777 + ],
778 + ]
779 + );
780 +
781 + $this->add_control(
782 + '_element_id',
783 + [
784 + 'label' => esc_html__( 'CSS ID', 'elementor' ),
785 + 'type' => Controls_Manager::TEXT,
786 + 'dynamic' => [
787 + 'active' => true,
788 + ],
789 + 'ai' => [
790 + 'active' => false,
791 + ],
792 + 'default' => '',
793 + 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ),
794 + 'style_transfer' => false,
795 + 'classes' => 'elementor-control-direction-ltr',
796 + ]
797 + );
798 +
799 + $this->add_control(
800 + '_css_classes',
801 + [
802 + 'label' => esc_html__( 'CSS Classes', 'elementor' ),
803 + 'type' => Controls_Manager::TEXT,
804 + 'ai' => [
805 + 'active' => false,
806 + ],
807 + 'dynamic' => [
808 + 'active' => true,
809 + ],
810 + 'prefix_class' => '',
811 + 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ),
812 + 'classes' => 'elementor-control-direction-ltr',
813 + ]
814 + );
815 +
816 + Plugin::$instance->controls_manager->add_display_conditions_controls( $this );
817 +
818 + $this->end_controls_section();
819 + }
820 +
821 + /**
822 + * Register the Motion Effects section.
823 + *
824 + * @return void
825 + */
826 + private function register_effects_section() {
827 + $this->start_controls_section(
828 + 'section_effects',
829 + [
830 + 'label' => esc_html__( 'Motion Effects', 'elementor' ),
831 + 'tab' => Controls_Manager::TAB_ADVANCED,
832 + ]
833 + );
834 +
835 + Plugin::$instance->controls_manager->add_motion_effects_promotion_control( $this );
836 +
837 + $this->add_responsive_control(
838 + '_animation',
839 + [
840 + 'label' => esc_html__( 'Entrance Animation', 'elementor' ),
841 + 'type' => Controls_Manager::ANIMATION,
842 + 'frontend_available' => true,
843 + ]
844 + );
845 +
846 + $this->add_control(
847 + 'animation_duration',
848 + [
849 + 'label' => esc_html__( 'Animation Duration', 'elementor' ),
850 + 'type' => Controls_Manager::SELECT,
851 + 'default' => '',
852 + 'options' => [
853 + 'slow' => esc_html__( 'Slow', 'elementor' ),
854 + '' => esc_html__( 'Normal', 'elementor' ),
855 + 'fast' => esc_html__( 'Fast', 'elementor' ),
856 + ],
857 + 'prefix_class' => 'animated-',
858 + 'condition' => [
859 + '_animation!' => '',
860 + ],
861 + ]
862 + );
863 +
864 + $this->add_control(
865 + '_animation_delay',
866 + [
867 + 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)',
868 + 'type' => Controls_Manager::NUMBER,
869 + 'default' => '',
870 + 'min' => 0,
871 + 'step' => 100,
872 + 'condition' => [
873 + '_animation!' => '',
874 + ],
875 + 'render_type' => 'none',
876 + 'frontend_available' => true,
877 + ]
878 + );
879 +
880 + $this->end_controls_section();
881 + }
882 +
883 + /** Register the Background section.
884 + *
885 + * @return void
886 + */
887 + private function register_background_section() {
888 + $this->start_controls_section(
889 + '_section_background',
890 + [
891 + 'label' => esc_html__( 'Background', 'elementor' ),
892 + 'tab' => Controls_Manager::TAB_ADVANCED,
893 + ]
894 + );
895 +
896 + $this->start_controls_tabs( '_tabs_background' );
897 +
898 + $this->start_controls_tab(
899 + '_tab_background_normal',
900 + [
901 + 'label' => esc_html__( 'Normal', 'elementor' ),
902 + ]
903 + );
904 +
905 + $this->add_group_control(
906 + Group_Control_Background::get_type(),
907 + [
908 + 'name' => '_background',
909 + 'selector' => static::WRAPPER_SELECTOR_CHILD,
910 + ]
911 + );
912 +
913 + $this->end_controls_tab();
914 +
915 + $this->start_controls_tab(
916 + '_tab_background_hover',
917 + [
918 + 'label' => esc_html__( 'Hover', 'elementor' ),
919 + ]
920 + );
921 +
922 + $this->add_group_control(
923 + Group_Control_Background::get_type(),
924 + [
925 + 'name' => '_background_hover',
926 + 'selector' => static::WRAPPER_SELECTOR_HOVER,
927 + ]
928 + );
929 +
930 + $this->add_control(
931 + '_background_hover_transition',
932 + [
933 + 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
934 + 'type' => Controls_Manager::SLIDER,
935 + 'range' => [
936 + 'px' => [
937 + 'min' => 0,
938 + 'max' => 3,
939 + 'step' => 0.1,
940 + ],
941 + ],
942 + 'render_type' => 'ui',
943 + 'separator' => 'before',
944 + 'selectors' => [
945 + static::WRAPPER_SELECTOR_CHILD => 'transition: background {{SIZE}}s',
946 + ],
947 + ]
948 + );
949 +
950 + $this->end_controls_tab();
951 +
952 + $this->end_controls_tabs();
953 +
954 + $this->end_controls_section();
955 + }
956 +
957 + /**
958 + * Register the Border section.
959 + *
960 + * @return void
961 + */
962 + private function register_border_section() {
963 + $this->start_controls_section(
964 + '_section_border',
965 + [
966 + 'label' => esc_html__( 'Border', 'elementor' ),
967 + 'tab' => Controls_Manager::TAB_ADVANCED,
968 + ]
969 + );
970 +
971 + $this->start_controls_tabs( '_tabs_border' );
972 +
973 + $this->start_controls_tab(
974 + '_tab_border_normal',
975 + [
976 + 'label' => esc_html__( 'Normal', 'elementor' ),
977 + ]
978 + );
979 +
980 + $this->add_group_control(
981 + Group_Control_Border::get_type(),
982 + [
983 + 'name' => '_border',
984 + 'selector' => static::WRAPPER_SELECTOR_CHILD,
985 + ]
986 + );
987 +
988 + $this->add_responsive_control(
989 + '_border_radius',
990 + [
991 + 'label' => esc_html__( 'Border Radius', 'elementor' ),
992 + 'type' => Controls_Manager::DIMENSIONS,
993 + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
994 + 'selectors' => [
995 + static::WRAPPER_SELECTOR_CHILD => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
996 + ],
997 + ]
998 + );
999 +
1000 + $this->add_group_control(
1001 + Group_Control_Box_Shadow::get_type(),
1002 + [
1003 + 'name' => '_box_shadow',
1004 + 'selector' => static::WRAPPER_SELECTOR_CHILD,
1005 + ]
1006 + );
1007 +
1008 + $this->end_controls_tab();
1009 +
1010 + $this->start_controls_tab(
1011 + '_tab_border_hover',
1012 + [
1013 + 'label' => esc_html__( 'Hover', 'elementor' ),
1014 + ]
1015 + );
1016 +
1017 + $this->add_group_control(
1018 + Group_Control_Border::get_type(),
1019 + [
1020 + 'name' => '_border_hover',
1021 + 'selector' => static::WRAPPER_SELECTOR_HOVER,
1022 + ]
1023 + );
1024 +
1025 + $this->add_responsive_control(
1026 + '_border_radius_hover',
1027 + [
1028 + 'label' => esc_html__( 'Border Radius', 'elementor' ),
1029 + 'type' => Controls_Manager::DIMENSIONS,
1030 + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
1031 + 'selectors' => [
1032 + static::WRAPPER_SELECTOR_HOVER_CHILD => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1033 + ],
1034 + ]
1035 + );
1036 +
1037 + $this->add_group_control(
1038 + Group_Control_Box_Shadow::get_type(),
1039 + [
1040 + 'name' => '_box_shadow_hover',
1041 + 'selector' => static::WRAPPER_SELECTOR_HOVER,
1042 + ]
1043 + );
1044 +
1045 + $this->add_control(
1046 + '_border_hover_transition',
1047 + [
1048 + 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
1049 + 'type' => Controls_Manager::SLIDER,
1050 + 'separator' => 'before',
1051 + 'range' => [
1052 + 'px' => [
1053 + 'min' => 0,
1054 + 'max' => 3,
1055 + 'step' => 0.1,
1056 + ],
1057 + ],
1058 + 'selectors' => [
1059 + static::WRAPPER_SELECTOR => 'transition: background {{_background_hover_transition.SIZE}}s, border {{SIZE}}s, border-radius {{SIZE}}s, box-shadow {{SIZE}}s',
1060 + ],
1061 + ]
1062 + );
1063 +
1064 + $this->end_controls_tab();
1065 +
1066 + $this->end_controls_tabs();
1067 +
1068 + $this->end_controls_section();
1069 + }
1070 +
1071 +
1072 + /**
1073 + * Register the Mask section.
1074 + *
1075 + * @return void
1076 + */
1077 + private function register_masking_section() {
1078 + $this->start_controls_section(
1079 + '_section_masking',
1080 + [
1081 + 'label' => esc_html__( 'Mask', 'elementor' ),
1082 + 'tab' => Controls_Manager::TAB_ADVANCED,
1083 + ]
1084 + );
1085 +
1086 + $this->add_control(
1087 + '_mask_switch',
1088 + [
1089 + 'label' => esc_html__( 'Mask', 'elementor' ),
1090 + 'type' => Controls_Manager::SWITCHER,
1091 + 'label_on' => esc_html__( 'On', 'elementor' ),
1092 + 'label_off' => esc_html__( 'Off', 'elementor' ),
1093 + 'default' => '',
1094 + ]
1095 + );
1096 +
1097 + $this->add_control(
1098 + '_mask_shape',
1099 + [
1100 + 'label' => esc_html__( 'Shape', 'elementor' ),
1101 + 'type' => Controls_Manager::VISUAL_CHOICE,
1102 + 'label_block' => true,
1103 + 'columns' => 4,
1104 + 'options' => $this->get_shapes(),
1105 + 'default' => 'circle',
1106 + 'selectors' => $this->get_mask_selectors( '-webkit-mask-image: url( ' . ELEMENTOR_ASSETS_URL . 'mask-shapes/{{VALUE}}.svg );' ),
1107 + 'condition' => [
1108 + '_mask_switch!' => '',
1109 + ],
1110 + ]
1111 + );
1112 +
1113 + $this->add_responsive_control(
1114 + '_mask_image',
1115 + [
1116 + 'label' => esc_html__( 'Image', 'elementor' ),
1117 + 'type' => Controls_Manager::MEDIA,
1118 + 'media_types' => [ 'image' ],
1119 + 'should_include_svg_inline_option' => true,
1120 + 'library_type' => 'image/svg+xml',
1121 + 'dynamic' => [
1122 + 'active' => true,
1123 + ],
1124 + 'selectors' => $this->get_mask_selectors( '-webkit-mask-image: url( {{URL}} );' ),
1125 + 'condition' => [
1126 + '_mask_switch!' => '',
1127 + '_mask_shape' => 'custom',
1128 + ],
1129 + ]
1130 + );
1131 +
1132 + $this->add_responsive_control(
1133 + '_mask_size',
1134 + [
1135 + 'label' => esc_html__( 'Size', 'elementor' ),
1136 + 'type' => Controls_Manager::SELECT,
1137 + 'options' => [
1138 + 'contain' => esc_html__( 'Fit', 'elementor' ),
1139 + 'cover' => esc_html__( 'Fill', 'elementor' ),
1140 + 'custom' => esc_html__( 'Custom', 'elementor' ),
1141 + ],
1142 + 'default' => 'contain',
1143 + 'selectors' => $this->get_mask_selectors( '-webkit-mask-size: {{VALUE}};' ),
1144 + 'condition' => [
1145 + '_mask_switch!' => '',
1146 + ],
1147 + ]
1148 + );
1149 +
1150 + $this->add_responsive_control(
1151 + '_mask_size_scale',
1152 + [
1153 + 'label' => esc_html__( 'Scale', 'elementor' ),
1154 + 'type' => Controls_Manager::SLIDER,
1155 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1156 + 'range' => [
1157 + 'px' => [
1158 + 'min' => 0,
1159 + 'max' => 500,
1160 + ],
1161 + 'em' => [
1162 + 'min' => 0,
1163 + 'max' => 100,
1164 + ],
1165 + '%' => [
1166 + 'min' => 0,
1167 + 'max' => 200,
1168 + ],
1169 + ],
1170 + 'default' => [
1171 + 'unit' => '%',
1172 + 'size' => 100,
1173 + ],
1174 + 'selectors' => $this->get_mask_selectors( '-webkit-mask-size: {{SIZE}}{{UNIT}};' ),
1175 + 'condition' => [
1176 + '_mask_switch!' => '',
1177 + '_mask_size' => 'custom',
1178 + ],
1179 + ]
1180 + );
1181 +
1182 + $this->add_responsive_control(
1183 + '_mask_position',
1184 + [
1185 + 'label' => esc_html__( 'Position', 'elementor' ),
1186 + 'type' => Controls_Manager::SELECT,
1187 + 'options' => [
1188 + 'center center' => esc_html__( 'Center Center', 'elementor' ),
1189 + 'center left' => esc_html__( 'Center Left', 'elementor' ),
1190 + 'center right' => esc_html__( 'Center Right', 'elementor' ),
1191 + 'top center' => esc_html__( 'Top Center', 'elementor' ),
1192 + 'top left' => esc_html__( 'Top Left', 'elementor' ),
1193 + 'top right' => esc_html__( 'Top Right', 'elementor' ),
1194 + 'bottom center' => esc_html__( 'Bottom Center', 'elementor' ),
1195 + 'bottom left' => esc_html__( 'Bottom Left', 'elementor' ),
1196 + 'bottom right' => esc_html__( 'Bottom Right', 'elementor' ),
1197 + 'custom' => esc_html__( 'Custom', 'elementor' ),
1198 + ],
1199 + 'default' => 'center center',
1200 + 'selectors' => $this->get_mask_selectors( '-webkit-mask-position: {{VALUE}};' ),
1201 + 'condition' => [
1202 + '_mask_switch!' => '',
1203 + ],
1204 + ]
1205 + );
1206 +
1207 + $this->add_responsive_control(
1208 + '_mask_position_x',
1209 + [
1210 + 'label' => esc_html__( 'X Position', 'elementor' ),
1211 + 'type' => Controls_Manager::SLIDER,
1212 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1213 + 'range' => [
1214 + 'px' => [
1215 + 'min' => -500,
1216 + 'max' => 500,
1217 + ],
1218 + 'em' => [
1219 + 'min' => -100,
1220 + 'max' => 100,
1221 + ],
1222 + '%' => [
1223 + 'min' => -100,
1224 + 'max' => 100,
1225 + ],
1226 + 'vw' => [
1227 + 'min' => -100,
1228 + 'max' => 100,
1229 + ],
1230 + ],
1231 + 'default' => [
1232 + 'unit' => '%',
1233 + 'size' => 0,
1234 + ],
1235 + 'selectors' => $this->get_mask_selectors( '-webkit-mask-position-x: {{SIZE}}{{UNIT}};' ),
1236 + 'condition' => [
1237 + '_mask_switch!' => '',
1238 + '_mask_position' => 'custom',
1239 + ],
1240 + ]
1241 + );
1242 +
1243 + $this->add_responsive_control(
1244 + '_mask_position_y',
1245 + [
1246 + 'label' => esc_html__( 'Y Position', 'elementor' ),
1247 + 'type' => Controls_Manager::SLIDER,
1248 + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1249 + 'range' => [
1250 + 'px' => [
1251 + 'min' => -500,
1252 + 'max' => 500,
1253 + ],
1254 + 'em' => [
1255 + 'min' => -100,
1256 + 'max' => 100,
1257 + ],
1258 + '%' => [
1259 + 'min' => -100,
1260 + 'max' => 100,
1261 + ],
1262 + 'vw' => [
1263 + 'min' => -100,
1264 + 'max' => 100,
1265 + ],
1266 + ],
1267 + 'default' => [
1268 + 'unit' => '%',
1269 + 'size' => 0,
1270 + ],
1271 + 'selectors' => $this->get_mask_selectors( '-webkit-mask-position-y: {{SIZE}}{{UNIT}};' ),
1272 + 'condition' => [
1273 + '_mask_switch!' => '',
1274 + '_mask_position' => 'custom',
1275 + ],
1276 + ]
1277 + );
1278 +
1279 + $this->add_responsive_control(
1280 + '_mask_repeat',
1281 + [
1282 + 'label' => esc_html__( 'Repeat', 'elementor' ),
1283 + 'type' => Controls_Manager::SELECT,
1284 + 'options' => [
1285 + 'no-repeat' => esc_html__( 'No-repeat', 'elementor' ),
1286 + 'repeat' => esc_html__( 'Repeat', 'elementor' ),
1287 + 'repeat-x' => esc_html__( 'Repeat-x', 'elementor' ),
1288 + 'repeat-Y' => esc_html__( 'Repeat-y', 'elementor' ),
1289 + 'round' => esc_html__( 'Round', 'elementor' ),
1290 + 'space' => esc_html__( 'Space', 'elementor' ),
1291 + ],
1292 + 'default' => 'no-repeat',
1293 + 'selectors' => $this->get_mask_selectors( '-webkit-mask-repeat: {{VALUE}};' ),
1294 + 'condition' => [
1295 + '_mask_switch!' => '',
1296 + '_mask_size!' => 'cover',
1297 + ],
1298 + ]
1299 + );
1300 +
1301 + $this->end_controls_section();
1302 + }
1303 +
1304 +
1305 + /**
1306 + * Register the Responsive section.
1307 + *
1308 + * @return void
1309 + */
1310 + private function register_responsive_section() {
1311 + $this->start_controls_section(
1312 + '_section_responsive',
1313 + [
1314 + 'label' => esc_html__( 'Responsive', 'elementor' ),
1315 + 'tab' => Controls_Manager::TAB_ADVANCED,
1316 + ]
1317 + );
1318 +
1319 + $this->add_control(
1320 + 'responsive_description',
1321 + [
1322 + 'raw' => sprintf(
1323 + /* translators: 1: Link open tag, 2: Link close tag. */
1324 + 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' ),
1325 + '<a href="javascript: $e.run( \'panel/close\' )">',
1326 + '</a>'
1327 + ),
1328 + 'type' => Controls_Manager::RAW_HTML,
1329 + 'content_classes' => 'elementor-descriptor',
1330 + ]
1331 + );
1332 +
1333 + $this->add_hidden_device_controls();
1334 +
1335 + $this->end_controls_section();
1336 + }
1337 +
1338 + /**
1339 + * Register common widget controls.
1340 + *
1341 + * Adds different input fields to allow the user to change and customize the widget settings.
1342 + *
1343 + * @since 3.1.0
1344 + * @access protected
1345 + */
1346 + protected function register_controls() {
1347 + $this->register_layout_section();
1348 +
1349 + $this->register_effects_section();
1350 +
1351 + $this->register_transform_section( '', static::TRANSFORM_SELECTOR_CLASS );
1352 +
1353 + $this->register_background_section();
1354 +
1355 + $this->register_border_section();
1356 +
1357 + $this->register_masking_section();
1358 +
1359 + $this->register_responsive_section();
1360 +
1361 + $register_common_controls = apply_filters(
1362 + 'elementor/widget/common/register_css_attributes_control',
1363 + true,
1364 + $this
1365 + );
1366 +
1367 + if ( $register_common_controls ) {
1368 + Plugin::$instance->controls_manager->add_custom_attributes_controls( $this );
1369 +
1370 + Plugin::$instance->controls_manager->add_custom_css_controls( $this );
1371 + }
1372 + }
1373 + }
1374 +