true, 'labelsVisibility' => 'always', 'hoverAnimation' => 'icgb__fade', 'labelVPosition' => 'icgbv__center', 'labelHPosition' => 'icgbh__center', 'beforeLabel' => 'Before', 'afterLabel' => 'After', ) ); $compare = wp_parse_args( isset( $attributes['comparisonOptions'] ) && is_array( $attributes['comparisonOptions'] ) ? $attributes['comparisonOptions'] : array(), array( 'disableSliding' => false, 'onlyHandleDraggable' => false, 'slideMode' => 'horizontal', 'swipeMode' => 'drag', 'initialPosition' => 50, ) ); $pick = static function ( $key, $allowed, $fallback ) use ( $attributes ) { return isset( $attributes[ $key ] ) && in_array( $attributes[ $key ], $allowed, true ) ? $attributes[ $key ] : $fallback; }; // More comparisons (carousel). $pairs = array(); if ( ! empty( $attributes['pairs'] ) && is_array( $attributes['pairs'] ) ) { foreach ( $attributes['pairs'] as $pair ) { $pair = array( 'before' => self::image( $pair['beforeImage'] ?? array() ), 'after' => self::image( $pair['afterImage'] ?? array() ), 'caption' => isset( $pair['caption'] ) ? (string) $pair['caption'] : '', ); if ( '' !== $pair['before']['url'] && '' !== $pair['after']['url'] ) { $pairs[] = $pair; } } } return array( 'uniqueId' => isset( $attributes['uniqueId'] ) ? (string) $attributes['uniqueId'] : '', 'before' => self::image( $attributes['beforeImage'] ?? array() ), 'after' => self::image( $attributes['afterImage'] ?? array() ), 'slider' => $slider, 'compare' => $compare, 'showCaption' => ! empty( $attributes['showCaption'] ), 'caption' => isset( $attributes['sliderCaption'] ) ? (string) $attributes['sliderCaption'] : '', 'captionTag' => $pick( 'captionTag', self::CAPTION_TAGS, 'span' ), 'design' => $pick( 'design', self::DESIGNS, '' ), 'ratio' => $pick( 'aspectRatio', self::RATIOS, '' ), 'imageSize' => $pick( 'imageSize', array( 'large', 'full' ), 'full' ), 'handleStyle' => $pick( 'handleStyle', self::HANDLE_STYLES, 'circle' ), 'handleText' => isset( $attributes['handleText'] ) ? (string) $attributes['handleText'] : '', 'handleIcon' => isset( $attributes['handleIcon'] ) ? (string) $attributes['handleIcon'] : '', 'mode' => $pick( 'compareMode', self::MODES, 'slide' ), 'introHint' => ! empty( $attributes['introHint'] ), 'autoPlay' => ! empty( $attributes['autoPlay'] ), 'fullscreen' => ! empty( $attributes['fullscreen'] ), 'ctaText' => isset( $attributes['ctaText'] ) ? (string) $attributes['ctaText'] : '', 'ctaUrl' => isset( $attributes['ctaUrl'] ) ? (string) $attributes['ctaUrl'] : '', 'ctaNewTab' => ! empty( $attributes['ctaNewTab'] ), 'ctaPosition' => $pick( 'ctaPosition', self::CTA_POSITIONS, 'bottom-center' ), 'video' => 'video' === ( $attributes['mediaType'] ?? 'image' ), 'beforeVideo' => self::image( $attributes['beforeVideo'] ?? array() ), 'afterVideo' => self::image( $attributes['afterVideo'] ?? array() ), 'pairs' => $pairs, 'pairThumbs' => ! isset( $attributes['pairThumbs'] ) || ! empty( $attributes['pairThumbs'] ), ); } /** * The parts of a saved image (or video) the page needs: id, url, alt. * * @param mixed $image Saved image. * @return array{id:int,url:string,alt:string} */ private static function image( $image ) { $image = is_array( $image ) ? $image : array(); return array( 'id' => isset( $image['id'] ) ? absint( $image['id'] ) : 0, 'url' => isset( $image['url'] ) ? (string) $image['url'] : '', 'alt' => isset( $image['alt'] ) ? (string) $image['alt'] : '', ); } /** * A label as shown: the untouched English default is translated. * * @param string $label Saved label. * @param string $which before|after. * @return string */ public static function label( $label, $which ) { if ( 'Before' === $label && 'before' === $which ) { return __( 'Before', 'slider-blocks' ); } if ( 'After' === $label && 'after' === $which ) { return __( 'After', 'slider-blocks' ); } return (string) $label; } /** * Block output. * * @param array $attributes Block attributes. * @param string $content Saved markup (only its HTML anchor is used). * @return string */ public static function render( $attributes, $content = '' ) { $atts = self::normalize( $attributes ); // The first comparison (images or videos) must be complete. $first = $atts['video'] ? array( 'before' => $atts['beforeVideo'], 'after' => $atts['afterVideo'], 'caption' => '' ) : array( 'before' => $atts['before'], 'after' => $atts['after'], 'caption' => '' ); if ( '' === $first['before']['url'] || '' === $first['after']['url'] ) { return ''; } // Several comparisons make a carousel (images only). $pairs = $atts['video'] ? array() : $atts['pairs']; $classes = array( $atts['uniqueId'] ); if ( $atts['design'] ) { $classes[] = 'gs-ba-design-' . $atts['design']; } if ( $pairs ) { $classes[] = 'has-pairs'; } $wrapper = array( 'class' => implode( ' ', array_filter( $classes ) ) ); // The HTML anchor is saved in the markup, not in the attributes. if ( preg_match( '/^\s*]*\bid="([^"]+)"/', (string) $content, $match ) ) { $wrapper['id'] = $match[1]; } $caption = $atts['showCaption'] ? $atts['caption'] : ''; if ( ! $pairs ) { return sprintf( '
%2$s
', get_block_wrapper_attributes( $wrapper ), self::figure( $first, $atts, $caption, true ) ); } // Carousel: Swiper only loads for blocks that need it. wp_enqueue_script( 'gutslider-swiper-script' ); wp_enqueue_style( 'gutslider-swiper-style' ); $all = array_merge( array( array_merge( $first, array( 'caption' => $caption ) ) ), $pairs ); $slides = ''; foreach ( $all as $index => $pair ) { $slides .= sprintf( '
%2$s
', /* translators: 1: comparison number, 2: number of comparisons. */ esc_attr( sprintf( __( '%1$d of %2$d', 'slider-blocks' ), $index + 1, count( $all ) ) ), self::figure( $pair, $atts, $pair['caption'], 0 === $index ) ); } $thumbs = ''; if ( $atts['pairThumbs'] ) { foreach ( $all as $index => $pair ) { $thumb = $pair['after']['id'] ? wp_get_attachment_image_url( $pair['after']['id'], 'thumbnail' ) : ''; $thumbs .= sprintf( '', 0 === $index ? ' is-active' : '', /* translators: %d: comparison number. */ esc_attr( sprintf( __( 'Show comparison %d', 'slider-blocks' ), $index + 1 ) ), 0 === $index ? ' aria-current="true"' : '', esc_url( $thumb ? $thumb : $pair['after']['url'] ) ); } $thumbs = '
' . $thumbs . '
'; } return sprintf( '
%6$s
', get_block_wrapper_attributes( $wrapper ), esc_attr__( 'carousel', 'slider-blocks' ), $slides, esc_attr__( 'Previous comparison', 'slider-blocks' ), esc_attr__( 'Next comparison', 'slider-blocks' ), $thumbs ); } /** * One comparison with its caption. * * @param array $pair before, after (id, url, alt). * @param array $atts Normalised settings. * @param string $caption Caption (HTML). * @param bool $lead First comparison of the block. * @return string HTML. */ private static function figure( $pair, $atts, $caption, $lead ) { $slider = $atts['slider']; $compare = $atts['compare']; $mode = $atts['mode']; $portrait = 'slide' === $mode && 'vertical' === $compare['slideMode']; $position = max( 0, min( 100, (float) $compare['initialPosition'] ) ); if ( 'toggle' === $mode ) { $position = 100; } // Only the page's first comparison may hold its main image. if ( $lead ) { ++self::$count; } $eager = $lead && 1 === self::$count; $root_classes = array( 'gutslider-compare-block', 'gs-ba', $portrait ? 'is-vertical' : 'is-horizontal', 'is-' . $mode, 'has-handle-' . $atts['handleStyle'], ); if ( $compare['disableSliding'] && 'toggle' !== $mode ) { $root_classes[] = 'is-disabled'; } if ( $compare['onlyHandleDraggable'] ) { $root_classes[] = 'is-handle-only'; } if ( $atts['ratio'] ) { $root_classes[] = 'has-ratio'; } if ( $atts['video'] ) { $root_classes[] = 'is-video'; } // Opacity of "after": fade follows the knob (right = after), toggle starts on "before". $style = sprintf( '--gs-ba-pos:%1$s%%;--gs-ba-o:%2$s;', $position, 'fade' === $mode ? $position / 100 : 1 - $position / 100 ); if ( $atts['ratio'] ) { $style .= 'aspect-ratio:' . $atts['ratio'] . ';'; } $before_label = self::label( $slider['beforeLabel'], 'before' ); $after_label = self::label( $slider['afterLabel'], 'after' ); ob_start(); ?>
%2$s', tag_escape( $atts['captionTag'] ), wp_kses_post( $caption ) ); ?>
' . esc_html( '' !== $atts['handleText'] ? $atts['handleText'] : __( 'Drag', 'slider-blocks' ) ) . '
'; } elseif ( 'icon' === $atts['handleStyle'] && '' !== $atts['handleIcon'] && function_exists( 'gutslider_display_icon' ) ) { $inner = '' . wp_kses( gutslider_display_icon( $atts['handleIcon'] ), gutslider_allowed_svg_tags() ) . ''; } else { $inner = '
'; } return sprintf( '
%6$s
', $disabled ? '-1' : '0', 'fade' === $atts['mode'] ? esc_attr__( 'Fade between the before and after images', 'slider-blocks' ) : esc_attr__( 'Drag to compare the before and after images', 'slider-blocks' ), $portrait ? 'vertical' : 'horizontal', esc_attr( (string) round( $position ) ), $disabled ? ' aria-disabled="true"' : '', $inner ); } /** * Buttons over the comparison: toggle, play/pause, fullscreen, call to action. * * @param array $atts Normalised settings. * @param string $before_label Before label. * @param string $after_label After label. * @return string HTML. */ private static function ui( $atts, $before_label, $after_label ) { $html = ''; if ( 'toggle' === $atts['mode'] ) { $html .= sprintf( '
', esc_attr__( 'Show', 'slider-blocks' ), esc_html( '' !== $before_label ? $before_label : __( 'Before', 'slider-blocks' ) ), esc_html( '' !== $after_label ? $after_label : __( 'After', 'slider-blocks' ) ) ); } if ( $atts['video'] ) { $html .= sprintf( '', esc_attr__( 'Pause videos', 'slider-blocks' ), esc_attr__( 'Play videos', 'slider-blocks' ) ); } if ( $atts['fullscreen'] ) { $html .= sprintf( '', esc_attr__( 'View fullscreen', 'slider-blocks' ), esc_attr__( 'Exit fullscreen', 'slider-blocks' ) ); } if ( '' !== $atts['ctaText'] && '' !== $atts['ctaUrl'] ) { $html .= sprintf( '%4$s', esc_attr( $atts['ctaPosition'] ), esc_url( $atts['ctaUrl'] ), $atts['ctaNewTab'] ? ' target="_blank" rel="noopener noreferrer"' : '', esc_html( $atts['ctaText'] ) ); } return $html; } /** * One side: its label and image (or video). * * @param array $media id, url, alt. * @param string $which before|after. * @param string $label Label text. * @param array $atts Normalised settings. * @param bool $eager Load right away. * @param bool $priority May be fetched with high priority. * @return string HTML. */ private static function item( $media, $which, $label, $atts, $eager, $priority ) { $slider = $atts['slider']; $visibility = 'hover' === $slider['labelsVisibility'] ? 'hover' : 'always'; $portrait = 'slide' === $atts['mode'] && 'vertical' === $atts['compare']['slideMode']; $label_classes = array( 'gutslider-label', $which . '-label', $portrait ? 'vertical' : 'horizontal' ); if ( 'hover' === $visibility && in_array( $slider['hoverAnimation'], self::ANIMATIONS, true ) ) { $label_classes[] = $slider['hoverAnimation']; } $place = $portrait ? $slider['labelHPosition'] : $slider['labelVPosition']; if ( in_array( $place, $portrait ? self::H_POSITIONS : self::V_POSITIONS, true ) ) { $label_classes[] = $place; } if ( $atts['video'] ) { $body = sprintf( '', esc_url( $media['url'] ), esc_attr( wp_strip_all_tags( $label ) ) ); } else { // The attachment's current alt text, else the saved one, else the label. $alt = $media['id'] ? (string) get_post_meta( $media['id'], '_wp_attachment_image_alt', true ) : ''; if ( '' === $alt ) { $alt = '' !== $media['alt'] ? $media['alt'] : wp_strip_all_tags( $label ); } $loading = $eager ? 'eager' : 'lazy'; $body = $media['id'] && wp_attachment_is_image( $media['id'] ) ? wp_get_attachment_image( $media['id'], $atts['imageSize'], false, array( 'class' => 'gs-ba__img wp-image-' . $media['id'], 'alt' => $alt, 'draggable' => 'false', 'decoding' => 'async', 'loading' => $loading, 'fetchpriority' => $priority ? 'high' : 'auto', ) ) : ''; if ( '' === $body ) { $body = sprintf( '%2$s', esc_url( $media['url'] ), esc_attr( $alt ), esc_attr( $loading ) ); } } ob_start(); ?>