is_empty( $attr ) ) { return $attr; } else { return $default; } } /** * Get Google Fonts * * @param array $attr Attr values. * * @since 1.3.0 * @access public */ public function get_google_fonts( $attr ) { if ( isset( $attr['fontFamily'] ) ) { if ( ! array_key_exists( $attr['fontFamily'], self::$google_fonts ) ) { self::$google_fonts[ $attr['fontFamily'] ] = array( 'fontfamily' => $attr['fontFamily'], 'fontvariant' => ( isset( $attr['fontVariant'] ) && ! empty( $attr['fontVariant'] ) ? array( $attr['fontVariant'] ) : array() ), ); } else { if ( ! in_array( $attr['fontVariant'], self::$google_fonts[ $attr['fontFamily'] ]['fontvariant'], true ) ) { array_push( self::$google_fonts[ $attr['fontFamily'] ]['fontvariant'], ( isset( $attr['fontStyle'] ) && 'italic' === $attr['fontStyle'] ) ? $attr['fontVariant'] . ':i' : $attr['fontVariant'] ); } } } } /** * Convert HEX to RGBA. * * @param string $color Color data. * @param bool|int $opacity Opacity status. * * @return mixed * @since 1.3.0 * @access public */ public static function hex2rgba( $color, $opacity = false ) { $default = 'rgb(0,0,0)'; if ( empty( $color ) ) { return $default; } if ( '#' == $color[0] ) { $color = substr( $color, 1 ); } if ( strlen( $color ) == 6 ) { $hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] ); } elseif ( strlen( $color ) == 3 ) { $hex = array( $color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2] ); } else { return $default; } $rgb = array_map( 'hexdec', $hex ); if ( $opacity >= 0 ) { if ( abs( $opacity ) > 1 ) { $opacity = 1.0; } $output = 'rgba( ' . implode( ',', $rgb ) . ',' . $opacity . ' )'; } else { $output = 'rgb( ' . implode( ',', $rgb ) . ' )'; } return $output; } /** * Get Blocks CSS * * @param int $post_id Post id. * @return string|void * @since 1.3.0 * @access public */ public function get_blocks_css( $post_id ) { if ( ! function_exists( 'has_blocks' ) ) { return; } $content = get_post_field( 'post_content', $post_id ); $blocks = parse_blocks( $content ); if ( ! is_array( $blocks ) || empty( $blocks ) ) { return; } $animations = boolval( preg_match( '/\banimated\b/', $content ) ); return $this->cycle_through_static_blocks( $blocks, $animations ); } /** * Get Widgets CSS * * @return string|void * @since 1.7.0 * @access public */ public function get_widgets_css() { if ( ! function_exists( 'has_blocks' ) ) { return; } $content = Registration::get_active_widgets_content(); $blocks = parse_blocks( $content ); if ( ! is_array( $blocks ) || empty( $blocks ) ) { return ''; } $animations = boolval( preg_match( '/\banimated\b/', $content ) ); return $this->cycle_through_static_blocks( $blocks, $animations ); } /** * Get Reusable Blocks CSS * * @param int $post_id Post id. * @return string|void * @since 1.3.0 * @access public */ public function get_reusable_block_css( $post_id ) { $reusable_block = get_post( $post_id ); if ( ! $reusable_block || 'wp_block' !== $reusable_block->post_type ) { return; } if ( 'publish' !== $reusable_block->post_status || ! empty( $reusable_block->post_password ) ) { return; } $blocks = parse_blocks( $reusable_block->post_content ); $animations = boolval( preg_match( '/\banimated\b/', $reusable_block->post_content ) ); return $this->cycle_through_static_blocks( $blocks, $animations ); } /** * Cycle thorugh Static Blocks * * @param array $blocks List of blocks. * @param bool $animations To check for animations or not. * * @return string Style. * @since 1.3.0 * @access public */ public function cycle_through_static_blocks( $blocks, $animations = true ) { $style = ''; foreach ( $blocks as $block ) { foreach ( self::$blocks_classes as $classname ) { $path = new $classname(); if ( method_exists( $path, 'render_css' ) && isset( $path->block_prefix ) ) { if ( ( isset( $path->library_prefix ) ? $path->library_prefix : $this->library_prefix ) . '/' . $path->block_prefix === $block['blockName'] ) { $style .= $path->render_css( $block ); } } } $custom_css = apply_filters( 'otter_blocks_css', $block ); if ( is_string( $custom_css ) ) { $style .= $custom_css; } if ( isset( $block['innerBlocks'] ) && ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) { $style .= $this->cycle_through_static_blocks( $block['innerBlocks'], false ); } } if ( true === $animations && class_exists( '\ThemeIsle\GutenbergBlocks\Blocks_Animation' ) && get_option( 'themeisle_blocks_settings_blocks_animation', true ) && get_option( 'themeisle_blocks_settings_optimize_animations_css', true ) ) { $style .= $this->get_animation_css( $blocks ); } return $style; } /** * Get Animation CSS * * @param array $blocks List of blocks. * * @return string CSS. * @since 2.0.10 * @access public */ public function get_animation_css( $blocks ) { $style = ''; $classes = $this->get_animation_classes( $blocks ); if ( 0 === count( $classes ) ) { return $style; } $prepared_classes = array( ':root' ); foreach ( $classes as $class ) { array_push( $prepared_classes, '.' . $class, '.animated.' . $class ); } $classes = $prepared_classes; $content = get_transient( 'otter_animations_parsed' ); if ( false === $content ) { $parser = null; if ( function_exists( 'wpcom_vip_file_get_contents' ) ) { $parser = new Parser( wpcom_vip_file_get_contents( OTTER_BLOCKS_PATH . '/build/animation/index.css' ) ); } else { $parser = new Parser( file_get_contents( OTTER_BLOCKS_PATH . '/build/animation/index.css' ) ); // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown } $content = $parser->parse()->getContents(); set_transient( 'otter_animations_parsed', $content, MONTH_IN_SECONDS ); } $format = OutputFormat::createCompact(); foreach ( $content as $rule ) { if ( $rule instanceof DeclarationBlock ) { foreach ( $rule->getSelectors() as $selector ) { if ( in_array( $selector, $classes ) ) { $style .= $rule->render( $format ); } } continue; } /* * This is used to get the reduced-motion animation styles. */ if ( $rule instanceof AtRuleBlockList ) { if ( false === in_array( $rule->atRuleArgs(), array( '(prefers-reduced-motion:reduce),print', 'screen' ) ) ) { continue; } $style .= $rule->render( $format ); continue; } /* * This is used to get actual animation which is a @keyframe. */ if ( $rule instanceof KeyFrame ) { if ( in_array( '.' . $rule->getAnimationName(), $classes ) ) { $style .= $rule->render( $format ); } continue; } } return $style; } /** * Get Animation Classes * * @param array $blocks List of blocks. * * @return array CSS classes. * @since 2.0.10 * @access public */ public function get_animation_classes( $blocks ) { $classes = array(); $allowed_classes = array( 'animated', 'bounce', 'flash', 'pulse', 'rubberBand', 'shakeX', 'shakeY', 'headShake', 'swing', 'tada', 'wobble', 'jello', 'heartBeat', 'hinge', 'jackInTheBox', 'backInDown', 'backInLeft', 'backInRight', 'backInUp', 'backOutDown', 'backOutLeft', 'backOutRight', 'backOutUp', 'bounceIn', 'bounceInDown', 'bounceInLeft', 'bounceInRight', 'bounceInUp', 'bounceOut', 'bounceOutDown', 'bounceOutLeft', 'bounceOutRight', 'bounceOutUp', 'fadeIn', 'fadeInDown', 'fadeInDownBig', 'fadeInLeft', 'fadeInLeftBig', 'fadeInRight', 'fadeInRightBig', 'fadeInUp', 'fadeInTopLeft', 'fadeInTopRight', 'fadeInBottomLeft', 'fadeInBottomRight', 'fadeOut', 'fadeOutDown', 'fadeOutDownBig', 'fadeOutLeft', 'fadeOutLeftBig', 'fadeOutRight', 'fadeOutRightBig', 'fadeOutUp', 'fadeOutUpBig', 'fadeOutTopLeft', 'fadeOutTopRight', 'fadeOutBottomRight', 'fadeOutBottomLeft', 'flip', 'flipInX', 'flipInY', 'flipOutX', 'flipOutY', 'lightSpeedInRight', 'lightSpeedInLeft', 'lightSpeedOutRight', 'lightSpeedOutLeft', 'rotateIn', 'rotateInDownLeft', 'rotateInDownRight', 'rotateInUpLeft', 'rotateInUpRight', 'rotateOut', 'rotateOutDownLeft', 'rotateOutDownRight', 'rotateOutUpLeft', 'rotateOutUpRight', 'slideInDown', 'slideInLeft', 'slideInRight', 'slideInUp', 'slideOutDown', 'slideOutLeft', 'slideOutRight', 'slideOutUp', 'zoomIn', 'zoomInDown', 'zoomInLeft', 'zoomInRight', 'zoomInUp', 'zoomOut', 'zoomOutDown', 'zoomOutLeft', 'zoomOutRight', 'zoomOutUp', 'rollIn', 'rollOut', 'backOutDown', 'backOutLeft', 'backOutRight', 'backOutUp', 'bounceOut', 'bounceOutDown', 'bounceOutLeft', 'bounceOutRight', 'bounceOutUp', 'fadeOut', 'fadeOutDown', 'fadeOutDownBig', 'fadeOutLeft', 'fadeOutLeftBig', 'fadeOutRight', 'fadeOutRightBig', 'fadeOutUp', 'fadeOutUpBig', 'fadeOutTopLeft', 'fadeOutTopRight', 'fadeOutBottomRight', 'fadeOutBottomLeft', 'flipOutX', 'flipOutY', 'lightSpeedOutRight', 'lightSpeedOutLeft', 'rotateOut', 'rotateOutDownLeft', 'rotateOutDownRight', 'rotateOutUpLeft', 'rotateOutUpRight', 'slideOutDown', 'slideOutLeft', 'slideOutRight', 'slideOutUp', 'zoomOut', 'zoomOutDown', 'zoomOutLeft', 'zoomOutRight', 'zoomOutUp', 'rollOut', 'delay-100ms', 'delay-200ms', 'delay-500ms', 'delay-1s', 'delay-2s', 'delay-3s', 'delay-4s', 'delay-5s', 'slow', 'slower', 'fast', 'faster', ); foreach ( $blocks as $block ) { if ( isset( $block['attrs']['className'] ) && ! empty( $block['attrs']['className'] ) ) { if ( preg_match( '/\banimated\b/', $block['attrs']['className'] ) ) { $classes = array_merge( $classes, explode( ' ', trim( $block['attrs']['className'] ) ) ); } } if ( isset( $block['innerBlocks'] ) && ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) { $classes = array_merge( $classes, $this->get_animation_classes( $block['innerBlocks'] ) ); } } $classes = array_intersect( array_unique( $classes ), $allowed_classes ); return $classes; } /** * Cycle thorugh Global Styles * * @return string Style. * @since 2.0.0 * @access public */ public function cycle_through_global_styles() { $style = ''; foreach ( self::$blocks_classes as $classname ) { $path = new $classname(); if ( method_exists( $path, 'render_global_css' ) ) { $style .= $path->render_global_css(); } } return $style; } /** * Cycle thorugh Reusable Blocks * * @param array $blocks List of blocks. * * @return string Style. * @since 1.3.0 * @access public */ public function cycle_through_reusable_blocks( $blocks ) { $style = ''; foreach ( $blocks as $block ) { if ( 'core/block' === $block['blockName'] && ! empty( $block['attrs']['ref'] ) ) { $style .= $this->get_reusable_block_css( $block['attrs']['ref'] ); } if ( isset( $block['innerBlocks'] ) && ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) { $style .= $this->cycle_through_reusable_blocks( $block['innerBlocks'] ); } } return $style; } /** * Check if an url points to an image by checking if the an image extension exists. * * @param string $url The url. * * @return bool * @since 1.4.4 * @access public */ public static function is_image_url( $url ) { return is_string( $url ) && ( preg_match( '/\.(jpeg|jpg|png|gif|svg|bmp|ico|tiff|webp)$/i', $url ) || preg_match( '/\/dynamic\/?.[^"]*/i', $url ) ); } /** * Method to return path to child class in a Reflective Way. * * @return string * @since 1.3.0 * @access protected */ protected function get_dir() { return dirname( __FILE__ ); } /** * Throw error on object clone * * The whole idea of the singleton design pattern is that there is a single * object therefore, we don't want the object to be cloned. * * @access public * @return void * @since 1.3.0 */ public function __clone() { // Cloning instances of the class is forbidden. _doing_it_wrong( __FUNCTION__, 'Cheatin’ huh?', '1.0.0' ); } /** * Disable unserializing of the class * * @access public * @return void * @since 1.3.0 */ public function __wakeup() { // Unserializing instances of the class is forbidden. _doing_it_wrong( __FUNCTION__, 'Cheatin’ huh?', '1.0.0' ); } }