| | |
| | | <?php |
| | | namespace JVBase\blocks; |
| | | |
| | | use JVBase\managers\CacheManager; |
| | | use DateTime; |
| | | use DOMDocument; |
| | | use JVBase\managers\Cache; |
| | | use WP_Block; |
| | | use WP_Query; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; // Exit if accessed directly |
| | |
| | | |
| | | class CustomBlocks |
| | | { |
| | | protected CacheManager $cache; |
| | | protected CacheManager $imgCache; |
| | | protected Cache $cache; |
| | | public function __construct() |
| | | { |
| | | $this->cache = new CacheManager('blocks', DAY_IN_SECONDS); |
| | | $this->imgCache = new CacheManager('images', DAY_IN_SECONDS); |
| | | $this->cache = Cache::for('blocks', WEEK_IN_SECONDS); |
| | | $this->cache->connect('post')->connect('taxonomy'); |
| | | add_filter('render_block', [$this, 'render'], 999, 3); |
| | | |
| | | add_action('render_block', [$this, 'render'], 10, 3); |
| | | add_action('init', [$this, 'registerBlockStyles']); |
| | | } |
| | | |
| | | public function registerBlockStyles():void |
| | | { |
| | | do_action('jvbBlockStyles'); |
| | | //Register extra block styles |
| | | register_block_style( |
| | | 'core/navigation', |
| | |
| | | 'label' => __('Fixed', 'jvb') |
| | | ] |
| | | ); |
| | | register_block_style( |
| | | 'core/group', |
| | | [ |
| | | 'name' =>'callout', |
| | | 'label' => __('Callout', 'jvb') |
| | | ] |
| | | ); |
| | | register_block_style( |
| | | 'core/group', |
| | | [ |
| | | 'name' =>'callalt', |
| | | 'label' => __('Callout Alt', 'jvb') |
| | | ] |
| | | ); |
| | | } |
| | | |
| | | public function render(string $content, array $block, WP_Block $instance) |
| | | { |
| | | $method = 'render_'.$this->sanitizeBlockName($block); |
| | | if (method_exists($this, $method)) { |
| | | return $this->$method($block, $content); |
| | | //TODO: Recache it |
| | | $blockName = $this->sanitizeBlockName($block); |
| | | $method = 'render_'.$blockName; |
| | | $function = BASE.$method; |
| | | |
| | | if (function_exists($function)) { |
| | | return $function($block, $content); |
| | | // return $this->cache->remember( |
| | | // $block, |
| | | // get_the_ID(), |
| | | // function () use ($function, $block, $content) { |
| | | // return $function($block, $content); |
| | | // } |
| | | // ); |
| | | } else if (method_exists($this, $method)) { |
| | | return $this->$method($block, $content); |
| | | // |
| | | // return $this->cache->remember( |
| | | // get_the_ID(), |
| | | // function () use ($method, $block, $content) { |
| | | // return $this->$method($block, $content); |
| | | // } |
| | | // ); |
| | | } |
| | | } else if (!empty($block['blockName'])){ |
| | | //TESTING |
| | | $ignore = [ |
| | | 'core/null', |
| | | 'core/post-title', |
| | | 'core/list-item', |
| | | 'core/site-title', |
| | | 'jvb/forms' |
| | | ]; |
| | | // if (!in_array($block['blockName'], $ignore)) { |
| | | // jvbDump('No method found for '.print_r($block['blockName'], true)); |
| | | // } |
| | | } |
| | | if ($block['blockName'] === 'jvb/feed') { |
| | | // Enqueue the feed block script (it will automatically load dependencies) |
| | | $this->localize_feedblock(); |
| | | } |
| | | if ($block['blockName'] === 'jvb/forms') { |
| | | wp_enqueue_style('jvb-form'); |
| | | } |
| | | return $content; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Common Blocks |
| | | */ |
| | | //For Reference: |
| | | //core_form |
| | | //core_form_input |
| | | //core_form_submission_notification |
| | |
| | | */ |
| | | |
| | | |
| | | protected function render_core_button($block):string |
| | | public function render_core_button(array $block):string |
| | | { |
| | | $link = explode('href="', $block['innerHTML']); |
| | | $url = explode('">', $link[1]); |
| | | $label = explode('</a>', $url[1])[0]; |
| | | $url = $url[0]; |
| | | preg_match('/href="([^"]*)"/', $block['innerHTML'], $url); |
| | | preg_match('/>([^<]*)<\/a>/', $block['innerHTML'], $label); |
| | | |
| | | return '<li'.$this->getClassesAndStyles($block['attrs'],['row']).'> |
| | | <a href="'.$url.'">'.$label.'</a> |
| | | </li>'; |
| | | if (empty($url[1]) || empty($label[1])) { |
| | | return ''; |
| | | } |
| | | $icon = ''; |
| | | if (str_contains($url[1], 'google.com/maps')) { |
| | | $icon = 'google-logo'; |
| | | } |
| | | if (str_contains($url[1], 'maps.apple.com')) { |
| | | $icon = 'apple-logo'; |
| | | } |
| | | |
| | | if ($icon !== '') { |
| | | return sprintf( |
| | | '<li%s><a href="%s" title="Find Us On %s">%s Maps</a></li>', |
| | | $this->getClassesAndStyles($block['attrs']), |
| | | esc_url($url[1]), |
| | | esc_html($label[1]), |
| | | jvbIcon($icon) |
| | | ); |
| | | } |
| | | |
| | | return sprintf( |
| | | '<li%s><a href="%s">%s</a></li>', |
| | | $this->getClassesAndStyles($block['attrs']), |
| | | esc_url($url[1]), |
| | | esc_html($label[1]) |
| | | ); |
| | | } |
| | | |
| | | protected function render_core_buttons($block):string |
| | | public function render_core_buttons(array $block):string |
| | | { |
| | | return '<ul'.$this->getClassesAndStyles($block['attrs'], ['buttons row']).'">'. |
| | | return '<ul'.$this->getClassesAndStyles($block['attrs'], ['buttons','row']).'>'. |
| | | $this->innerBlocks($block).'</ul>'; |
| | | } |
| | | |
| | | protected function render_core_column($block):string |
| | | public function render_core_column(array $block):string |
| | | { |
| | | $styles = (array_key_exists('attrs', $block) && |
| | | array_key_exists('width', $block['attrs'])) ? |
| | |
| | | $this->innerBlocks($block).'</div>'; |
| | | } |
| | | |
| | | protected function render_core_columns($block):string |
| | | public function render_core_columns(array $block):string |
| | | { |
| | | return '<section'. |
| | | $this->getClassesAndStyles($block['attrs'], ['columns']).'>'. |
| | |
| | | } |
| | | //core_comment_template |
| | | |
| | | protected function render_core_group($block):string |
| | | public function render_core_group(array $block):string |
| | | { |
| | | $tag = (array_key_exists('tagName', $block['attrs'])) ? $block['attrs']['tagName'] : 'div'; |
| | | |
| | | $classes = ($tag === 'main') ? |
| | | $this->getClassesAndStyles($block['attrs']) : |
| | | $this->getClassesAndStyles($block['attrs'], ['group row']); |
| | | '' : |
| | | $this->getClassesAndStyles($block['attrs'], ['group']); |
| | | return '<'.$tag.$classes.'>'.$this->innerBlocks($block).'</'.$tag.'>'; |
| | | } |
| | | //core_home_link |
| | | //core_more |
| | | //core_nextpage |
| | | |
| | | protected function render_core_separator($block):string |
| | | public function render_core_separator(array $block):string |
| | | { |
| | | return '<hr'.$this->getClassesAndStyles($block['attrs']).'>'; |
| | | } |
| | | |
| | | protected function render_core_spacer($block):string |
| | | public function render_core_spacer(array $block):string |
| | | { |
| | | return '<div'.$this->getClassesAndStyles($block['attrs'], ['spacer'], ['height:2rem']). |
| | | ' aria-hidden="true"></div>'; |
| | |
| | | * Media Blocks |
| | | */ |
| | | //core_audio |
| | | protected function render_core_cover($block):string |
| | | public function render_core_cover(array $block):string |
| | | { |
| | | |
| | | // Extract block attributes |
| | | $attrs = $block['attrs'] ?? []; |
| | | $innerContent = $this->innerBlocks($block); |
| | | |
| | | // Handle overlay opacity |
| | | $dimRatio = $attrs['dimRatio'] ?? 50; |
| | | $overlayClass = 'overlay-' . (ceil($dimRatio / 25) * 25); |
| | | |
| | | // Build classes and styles |
| | | $classes = $this->getClassesAndStyles($attrs, ['cover', $overlayClass]); |
| | | $position = 'object-position: center;'; |
| | | if (array_key_exists('focalPoint', $attrs)) { |
| | | $x = (array_key_exists('x', $attrs['focalPoint'])) ? ($attrs['focalPoint']['x'] * 100).'%' : 'center'; |
| | | $y = (array_key_exists('y', $attrs['focalPoint'])) ? ($attrs['focalPoint']['y'] * 100).'%' : 'center'; |
| | | $position = 'object-position:'.$x.' '.$y.';'; |
| | | unset($attrs['focalPoint']); |
| | | } |
| | | |
| | | // Check for background type |
| | | $backgroundType = $attrs['backgroundType'] ?? 'image'; |
| | | $background = ''; |
| | | |
| | | if ($backgroundType === 'image' && isset($attrs['url'])) { |
| | | // Image background |
| | | $background = '<div class="cover-bg" aria-hidden="true"></div>'; |
| | | $ID = false; |
| | | if (array_key_exists('useFeaturedImage', $attrs)) { |
| | | global $post; |
| | | $ID = get_post_thumbnail_id($post->ID); |
| | | } else if (array_key_exists('id', $attrs)) { |
| | | $ID = (int)$attrs['id']; |
| | | } |
| | | |
| | | if ($backgroundType === 'image' && $ID) { |
| | | $background .= str_replace('<img', '<img style="'.$position.'"', $this->image($ID)); |
| | | } elseif ($backgroundType === 'video' && isset($attrs['url'])) { |
| | | // Video background |
| | | $background = '<div class="cover-bg" aria-hidden="true"></div>'; |
| | | $background .= '<video autoplay muted loop playsinline src="' . esc_url($attrs['url']) . '"></video>'; |
| | | $background .= '<video style="'.$position.'"autoplay muted loop playsinline src="' . esc_url($attrs['url']) . '"></video>'; |
| | | } |
| | | |
| | | return '<div' . $classes . '>' . |
| | | // Build classes and styles |
| | | unset($attrs['url']); |
| | | $classes = $this->getClassesAndStyles($attrs, ['cover row']); |
| | | |
| | | |
| | | return '<section' . $classes . '>' . |
| | | $background . |
| | | '<div class="content">' . |
| | | $innerContent . |
| | | '</div></div>'; |
| | | '</div></section>'; |
| | | } |
| | | |
| | | //core_file |
| | | |
| | | protected function render_core_gallery($block):string |
| | | public function render_core_gallery(array $block):string |
| | | { |
| | | return '<ul'.$this->getClassesAndStyles($block['attrs'], ['gallery']).'>'. |
| | | $this->innerBlocks($block,'<li>', '</li>'). |
| | | '</ul>'; |
| | | } |
| | | |
| | | protected function render_core_image($block):string |
| | | public function render_core_image(array $block):string |
| | | { |
| | | $ID = $this->imageID('', $block); |
| | | if (!$ID) { |
| | |
| | | wp_get_attachment_caption($ID) . |
| | | '</figcaption>' : |
| | | '<figcaption>' . $title . '</figcaption>'; |
| | | |
| | | $size = array_key_exists('sizeSlug', $block['attrs']) ? $block['attrs']['sizeSlug'] : 'large'; |
| | | return '<figure'. |
| | | $this->getClassesAndStyles($block['attrs']).'>'. |
| | | $this->imageLink(true, $ID) . |
| | | $this->imageLink(true, $ID, 'tiny', $size) . |
| | | $caption.'</figure>'; |
| | | } |
| | | |
| | | protected function render_core_media_text($block):string |
| | | public function render_core_media_text(array $block):string |
| | | { |
| | | |
| | | $ID = $this->imageID('', $block); |
| | | $img = ($ID) ? $this->image($ID, $block) : ''; |
| | | $imgLink = ($ID) ? $this->imageLink(true, $ID) : ''; |
| | | |
| | | $size = array_key_exists('mediaSizeSlug', $block['attrs']) ? $block['attrs']['mediaSizeSlug'] : 'large'; |
| | | $imgLink = ($ID) ? $this->imageLink(true, $ID, 'tiny', $size) : ''; |
| | | |
| | | $inner = $this->innerBlocks($block); |
| | | |
| | | $content = '<div'.$this->getClassesAndStyles($block['attrs'], ['media-text']).'>'; |
| | | |
| | | $classes = ['media-text', 'row']; |
| | | if (array_key_exists('isStackedOnMobile', $block['attrs'])) { |
| | | $classes[] = 'nowrap'; |
| | | } |
| | | $content = '<div'.$this->getClassesAndStyles($block['attrs'], $classes).'>'; |
| | | $content .= (array_key_exists( |
| | | 'mediaPosition', |
| | | $block['attrs'] |
| | |
| | | //render_core_details |
| | | //render_core_footnotes |
| | | //render_core_classic |
| | | protected function render_core_heading(array $block):string |
| | | public function render_core_heading(array $block):string |
| | | { |
| | | $level = (array_key_exists('level', $block['attrs'])) ? $block['attrs']['level'] : '2'; |
| | | $id = sanitize_title(wp_strip_all_tags($block['innerHTML'])); |
| | | $content = $this->inside($block); |
| | | $id = sanitize_title(wp_strip_all_tags($this->stripTagContents('small', $content))); |
| | | return '<h'.$level.' id="'.$id.'"'.$this->getClassesAndStyles($block['attrs']).'>'. |
| | | $this->inside($block). |
| | | $content. |
| | | '</h'.$level.'>'; |
| | | } |
| | | //render_core_list |
| | | //render_core_list_item |
| | | |
| | | public function render_core_list(array $block):string |
| | | { |
| | | $tag = (array_key_exists('ordered', $block['attrs'])) ? 'ol' : 'ul'; |
| | | return '<'.$tag.$this->getClassesAndStyles($block['attrs']).'>'.$this->innerBlocks($block).'</'.$tag.'>'; |
| | | } |
| | | |
| | | // public function render_core_list_item(array $block):string |
| | | // { |
| | | // return '<li'.$this->getClassesAndStyles($block['attrs']).'>'.$this->inside($block).'</li>'; |
| | | // } |
| | | //render_core_missing |
| | | |
| | | protected function render_core_paragraph(array $block):string |
| | | public function render_core_paragraph(array $block):string |
| | | { |
| | | return '<p'.$this->getClassesAndStyles($block['attrs'], ['paragraph']).'>'. |
| | | return '<p'.$this->getClassesAndStyles($block['attrs']).'>'. |
| | | $this->inside($block, 'p'). |
| | | '</p>'; |
| | | } |
| | | //render_core_quote |
| | | public function render_core_quote(array $block): string |
| | | { |
| | | $innerHTML = $block['innerHTML']; |
| | | |
| | | // Extract cite content first |
| | | $cite = $this->extractElement($innerHTML, 'cite'); |
| | | $citeHtml = ($cite === '') ? '' : '<cite>— '.$cite.'</cite>'; |
| | | |
| | | // Get the blockquote content |
| | | $content = $this->inside($block, 'blockquote'); |
| | | |
| | | // Remove the cite element from content if it exists |
| | | if ($cite !== '') { |
| | | $content = $this->stripTagContents('cite', $content); |
| | | } |
| | | |
| | | return '<blockquote'.$this->getClassesAndStyles($block['attrs']).'> |
| | | <div class="content">'.$content.'</div>'. |
| | | $citeHtml. |
| | | '</blockquote>'; |
| | | } |
| | | public function render_core_pullquote(array $block): string |
| | | { |
| | | $innerHTML = $block['innerHTML']; |
| | | |
| | | // Extract cite content first |
| | | $cite = $this->extractElement($innerHTML, 'cite'); |
| | | $citeHtml = ($cite === '') ? '' : '<cite>— '.$cite.'</cite>'; |
| | | |
| | | // Get the blockquote content |
| | | $content = $this->extractElement($innerHTML, 'blockquote'); |
| | | |
| | | // Remove the cite element from content if it exists |
| | | if ($cite !== '') { |
| | | $content = $this->stripTagContents('cite', $content); |
| | | } |
| | | $content = jvb_filter_content( $content); |
| | | |
| | | return '<blockquote'.$this->getClassesAndStyles($block['attrs'], ['pull']).'>'. |
| | | $content. |
| | | $citeHtml. |
| | | '</blockquote>'; |
| | | } |
| | | //render_core_table |
| | | //render_core_verse |
| | | |
| | |
| | | //core_loginout |
| | | //core_pattern |
| | | |
| | | protected function render_core_site_logo(array $block, string $content):string |
| | | public function render_core_site_logo(array $block, string $content):string |
| | | { |
| | | $open = $close = ''; |
| | | if ($block['attrs']['isLink']) { |
| | | |
| | | if (!is_home() && !is_front_page()) { |
| | | $open = '<a href="'.get_home_url().'" rel="home">'; |
| | | $close = '</a>'; |
| | | } |
| | | $img = get_theme_mod('custom_logo'); |
| | | $img = $this->image($img); |
| | | $img = $this->image($img, 'tiny', 'thumbnail'); |
| | | $img = str_replace('<img', '<img'.$this->getClassesAndStyles($block['attrs']), $img); |
| | | return $open.$img.$close; |
| | | } |
| | | //core_site_title_tagline |
| | | |
| | | protected function render_core_site_title(array $block, string $content):string |
| | | public function render_core_site_title(array $block, string $content):string |
| | | { |
| | | $tag = (array_key_exists('level', $block['attrs'])) ? $block['attrs']['level'] : 1; |
| | | $tag = ($tag == 0) ? 'p' : 'h'.$tag; |
| | |
| | | |
| | | return '<'.$tag.$class.'>'. |
| | | $open. |
| | | jvbIcon('logo-basic'). |
| | | '<span class="screen-reader-text">'. |
| | | get_bloginfo('name'). |
| | | '</span>'. |
| | | $close. |
| | | '</'.$tag.'>'; |
| | | } |
| | |
| | | /** |
| | | * Theme Navigation Blocks |
| | | */ |
| | | protected function render_core_navigation(array $block, string $content):string |
| | | public function render_core_navigation(array $block, string $content):string |
| | | { |
| | | $ID = $block['attrs']['ref']; |
| | | $ID = (array_key_exists('ref', $block['attrs'])) ? $block['attrs']['ref'] : false; |
| | | |
| | | if (empty($block['innerBlocks']) && get_post($ID)) { |
| | | if (empty($block['innerBlocks']) && $ID && get_post($ID)) { |
| | | $block['innerBlocks'] = parse_blocks(get_post($ID)->post_content); |
| | | } |
| | | |
| | |
| | | aria-label="Open Menu" |
| | | aria-controls="navigation-' .$ID. '" |
| | | aria-expanded="false">'. |
| | | jvbIcon('menu', ['title'=>'Toggle Menu']). |
| | | jvbIcon('close', ['title'=>'Toggle Menu']). |
| | | jvbIcon('list', ['title'=>'Toggle Menu']). |
| | | jvbIcon('x', ['title'=>'Toggle Menu']). |
| | | '</button>'; |
| | | $class = ($toggle === '') ? |
| | | $this->getClassesAndStyles($block['attrs'], ['mobile']) : |
| | |
| | | |
| | | //Allows to add custom items to a menu, based on the menu name |
| | | $helpmenu = apply_filters('jvbMenuExtraAfter', $helpmenu, get_the_title($ID)); |
| | | $main = trim(apply_filters('jvbMenuExtra', $this->innerBlocks($block), get_the_title($ID), $block)); |
| | | |
| | | $main = str_starts_with($main, '<ul') ? $main : '<ul>'.$main.'</ul>'; |
| | | |
| | | return '<nav'.$class.' id="navigation-' . $ID . '"aria-label="Navigation"> |
| | | <span class="screen-reader-text"> |
| | | <a href="#content">Skip to Content</a> |
| | | </span>' . |
| | | $toggle . |
| | | '<ul>'. |
| | | apply_filters('jvbMenuExtra', $this->innerBlocks($block), get_the_title($ID)). |
| | | '</ul></nav>'.$helpmenu; |
| | | $main. |
| | | '</nav>'.$helpmenu; |
| | | } |
| | | |
| | | protected function render_core_navigation_link($block):string |
| | | public function render_core_navigation_link(array $block):string |
| | | { |
| | | global $wp; |
| | | $url = (str_starts_with($block['attrs']['url'],'/')) ? |
| | | home_url($block['attrs']['url']) : |
| | | $block['attrs']['url']; |
| | | $current = (home_url($wp->request.'/') == $url); |
| | | |
| | | $temp = $block['attrs']; |
| | | unset($temp['url']); |
| | | $classes = ($current) ? |
| | | $this->getClassesAndStyles($block['attrs'], ['current']): |
| | | $this->getClassesAndStyles($block['attrs']); |
| | | $this->getClassesAndStyles($temp, ['current']): |
| | | $this->getClassesAndStyles($temp); |
| | | $aria = ''; |
| | | if ($current) { |
| | | $aria = ' aria-current="page"'; |
| | |
| | | return '<li'.$classes.'>'.$linkOpen.$block['attrs']['label'].'</a></li>'; |
| | | } |
| | | |
| | | protected function render_core_navigation_submenu(array $block):string |
| | | public function render_core_navigation_submenu(array $block):string |
| | | { |
| | | global $wp; |
| | | $url = (str_starts_with($block['attrs']['url'],'/')) ? |
| | |
| | | $block['attrs']['url']; |
| | | $current = (home_url($wp->request) == $url); |
| | | |
| | | $temp = $block['attrs']; |
| | | unset($temp['url']); |
| | | $classes = ($current) ? |
| | | $this->getClassesAndStyles($block['attrs'], ['has-submenu', 'current']): |
| | | $this->getClassesAndStyles($block['attrs'], ['has-submenu']); |
| | | $this->getClassesAndStyles($temp, ['has-submenu', 'current']): |
| | | $this->getClassesAndStyles($temp, ['has-submenu']); |
| | | |
| | | $aria = ''; |
| | | if ($current) { |
| | |
| | | $linkOpen = $this->build_navigation_link($block['attrs'], $aria); |
| | | $content = '<li'.$classes.'>'.$linkOpen.$block['attrs']['label']. |
| | | '</a><button class="toggle" data-action="toggle-submenu" title="Toggle Submenu" aria-label="Open '.$block['attrs']['label'].' Submenu" aria-expanded="false" aria-controls="'.$id.'-submenu">'. |
| | | jvbIcon('submenu', ['title'=>'Toggle Submenu']). |
| | | jvbIcon('caret-down', ['title'=>'Toggle Submenu']). |
| | | '</button><ul class="submenu" id='.$id.'-submenu">'; |
| | | |
| | | $content .= $this->innerBlocks($block); |
| | |
| | | home_url($attrs['url']) : |
| | | $attrs['url']; |
| | | |
| | | $type = $id = $label = $desc = $rel = $title = $kind = ''; |
| | | $target = $type = $id = $label = $desc = $rel = $title = $kind = ''; |
| | | foreach ($attrs as $k => $v) { |
| | | switch ($k) { |
| | | case 'description': |
| | |
| | | case 'type': |
| | | $type = $v; |
| | | break; |
| | | case 'opensInNewTab': |
| | | $target = ' target="'.$v.'"'; |
| | | break; |
| | | } |
| | | } |
| | | return '<a href="'.$url.'"'.$aria.$rel.$title.'>'; |
| | | return '<a href="'.$url.'"'.$aria.$rel.$target.$title.'>'; |
| | | } |
| | | |
| | | /** |
| | |
| | | //core_post_author |
| | | //core_post_author_biography |
| | | //core_post_author_name |
| | | protected function render_core_post_content(array $block, string $content = ''):string |
| | | public function render_core_post_content(array $block, string $content = ''):string |
| | | { |
| | | |
| | | $tag = (array_key_exists('tagName', $block['attrs'])) ? |
| | | $block['attrs']['tagName'] : |
| | | 'div'; |
| | | 'main'; |
| | | |
| | | if ($content == '') { |
| | | return do_blocks(get_the_content(get_the_ID())); |
| | | global $post; |
| | | |
| | | $block['innerBlocks'] = parse_blocks($post->post_content); |
| | | return $this->innerBlocks($block); |
| | | } else { |
| | | return $this->inside($block, $tag, $content); |
| | | } |
| | | } |
| | | //core_post_date |
| | | public function render_core_post_date(array $block):string |
| | | { |
| | | $postDate = get_the_date('c'); |
| | | return '<time datetime="'.$postDate.'" itemprop="datePublished"'.$this->getClassesAndStyles($block['attrs']).'>'.get_the_date().'</time>'; |
| | | } |
| | | //core_post_excerpt |
| | | protected function render_core_post_featured_image(array $block):string |
| | | public function render_core_post_featured_image(array $block):string |
| | | { |
| | | return '<figure'.$this->getClassesAndStyles($block['attrs']).'>'. |
| | | $this->imageLink(true). |
| | | '</figure>'; |
| | | global $post; |
| | | $ID = get_post_thumbnail_id($post->ID); |
| | | $aOpen = $aClose = ''; |
| | | if(!is_single($ID)) { |
| | | $aOpen = '<a href="'.get_the_permalink($post->ID).'">'; |
| | | $aClose = '</a>'; |
| | | } |
| | | |
| | | return $aOpen.'<figure'.$this->getClassesAndStyles($block['attrs']).'>'. |
| | | apply_filters('jvbCoreFeaturedImage', $this->image($ID), $post->post_type). |
| | | '</figure>'.$aClose; |
| | | } |
| | | //core_post_navigation_link |
| | | //core_post_template |
| | | //core_post_terms |
| | | public function render_core_post_terms(array $block):string |
| | | { |
| | | $terms = get_the_terms(get_the_ID(), $block['attrs']['term']); |
| | | $out = ''; |
| | | if ($terms && !is_wp_error($terms)) { |
| | | $out = '<ul class="term-list">'; |
| | | if (array_key_exists('prefix', $block['attrs'])) { |
| | | $out .= '<li>'.$block['attrs']['prefix'].'</li>'; |
| | | } |
| | | foreach($terms as $term) { |
| | | $out .= '<li><a href="'.get_term_link($term).'" rel="tag">'.html_entity_decode($term->name).'</a></li>'; |
| | | } |
| | | if (array_key_exists('suffix', $block['attrs'])) { |
| | | $out .= '<li>'.$block['attrs']['suffix'].'</li>'; |
| | | } |
| | | $out .= '</ul>'; |
| | | } |
| | | return $out; |
| | | } |
| | | //core_post_time_to_read |
| | | protected function render_core_post_title(array $block):string |
| | | public function render_core_post_title(array $block):string |
| | | { |
| | | $open = $close = ''; |
| | | if (array_key_exists('isLink', $block['attrs'])) { |
| | |
| | | $open.get_the_title().$close. |
| | | '</h'.$level.'>'; |
| | | } |
| | | //core_query |
| | | |
| | | public function render_core_query(array $block, string $content):string |
| | | { |
| | | $queryID = $block['attrs']['queryId']; |
| | | $args = []; |
| | | $inherit = $block['attrs']['inherit']??false; |
| | | if ($inherit) { |
| | | global $wp_query; |
| | | $loop = $wp_query; |
| | | } else { |
| | | foreach ($block['attrs']['query'] as $key => $value) { |
| | | if (empty($value)) { |
| | | continue; |
| | | } |
| | | switch ($key) { |
| | | case 'postType': |
| | | if ($value === BASE.'progress'){ |
| | | $args['post_parent'] = 0; |
| | | } |
| | | $args['post_type'] = $value; |
| | | break; |
| | | case 'perPage': |
| | | $args['posts_per_page'] = $value; |
| | | break; |
| | | case 'orderBy': |
| | | $args['orderby'] = $value; |
| | | break; |
| | | case 'taxQuery': |
| | | $taxQuery = []; |
| | | foreach ($value as $tax => $terms) { |
| | | $taxQuery[] = [ |
| | | 'taxonomy' => $tax, |
| | | 'terms' => $terms |
| | | ]; |
| | | } |
| | | if (!empty($taxQuery)) { |
| | | $args['tax_query'] = $taxQuery; |
| | | if (count($taxQuery) > 1) { |
| | | $args['tax_query']['relation'] = 'OR'; |
| | | } |
| | | } |
| | | break; |
| | | case 'sticky': |
| | | if ($value === 'ignore') { |
| | | $args['ignore_sticky_posts'] = true; |
| | | } else if ($value === 'exclude'){ |
| | | $args['post__not_in'] = get_option('sticky_posts'); |
| | | } else if ($value === 'only') { |
| | | $args['include'] = get_option('sticky_posts'); |
| | | } |
| | | break; |
| | | case 'search': |
| | | $args['s'] = $value; |
| | | break; |
| | | default: |
| | | $args[$key] = $value; |
| | | break; |
| | | |
| | | } |
| | | } |
| | | //Add in any args from the query string |
| | | $search = 'query-'.$queryID; |
| | | foreach ($_GET as $key => $value) { |
| | | if (str_contains($key, $search)) { |
| | | $key = str_replace($search, '', $key); |
| | | if ($key === 'page') { |
| | | $args['paged'] = (int)$value; |
| | | } |
| | | } |
| | | } |
| | | $loop = new WP_Query($args); |
| | | } |
| | | |
| | | $inner = ''; |
| | | |
| | | foreach ($block['innerBlocks'] as $innerBlock) { |
| | | switch ($innerBlock['blockName']) { |
| | | case 'core/post-template': |
| | | $inner .= '<section class="item-grid">'; |
| | | if ($loop->have_posts()) { |
| | | while($loop->have_posts()) { |
| | | $loop->the_post(); |
| | | $postType = get_post_type(); |
| | | $inner .= '<div class="item '.jvbNoBase($postType).'">'.$this->innerBlocks($innerBlock).'</div>'; |
| | | } |
| | | } |
| | | $inner .= '</section>'; |
| | | break; |
| | | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | $tagName = (array_key_exists('tagName', $block['attrs'])) ? $block['attrs']['tagName'] : 'div'; |
| | | $out = '<'.$tagName.' class="loop">'.$inner.'</'.$tagName.'>'; |
| | | if ($inherit) { |
| | | wp_reset_postdata(); |
| | | } |
| | | return $out; |
| | | } |
| | | |
| | | //core_query_no_results |
| | | //core_query_pagination |
| | | //core_query_pagination_next |
| | |
| | | //core_query_pagination_previous |
| | | //core_query_title |
| | | //core_read_more |
| | | protected function render_core_template_part(array $block, string $content):string |
| | | public function render_core_template_part(array $block, string $content):string |
| | | { |
| | | if (array_key_exists('attrs', $block) && array_key_exists('slug', $block['attrs']) && |
| | | in_array($block['attrs']['slug'], array('header', 'footer'))) { |
| | | $tag = (array_key_exists('slug', $block['attrs'])) ? $block['attrs']['slug'] : 'div'; |
| | | |
| | | $breadcrumbs = $themeSwitch = $afterHeader = $footerText= ''; |
| | | if ($block['attrs']['slug'] == 'header') { |
| | | $isHeaderTemplate = ( |
| | | (array_key_exists('slug', $block['attrs']) && str_contains(strtolower($block['attrs']['slug']), 'header')) || |
| | | (array_key_exists('tagName', $block['attrs']) && str_contains(strtolower($block['attrs']['tagName']), 'header')) |
| | | ) ? 'header' : false; |
| | | $isFooterTemplate = ( |
| | | (array_key_exists('slug', $block['attrs']) && str_contains(strtolower($block['attrs']['slug']), 'footer')) || |
| | | (array_key_exists('tagName', $block['attrs']) && str_contains(strtolower($block['attrs']['tagName']), 'footer')) |
| | | ) ? 'footer' : false; |
| | | |
| | | |
| | | if (($isHeaderTemplate || $isFooterTemplate)) { |
| | | |
| | | $tag = $isHeaderTemplate ?: $isFooterTemplate ?: 'div'; |
| | | |
| | | $breadcrumbs = $themeSwitch = $afterHeader = $beforeHeader = $footerText= ''; |
| | | if ($isHeaderTemplate) { |
| | | |
| | | $beforeHeader = apply_filters('jvbAboveHeader', $beforeHeader); |
| | | if ($beforeHeader !== '') { |
| | | $beforeHeader = '<aside class="pre-header">'.$beforeHeader.'</aside>'; |
| | | } |
| | | $checked = (is_user_logged_in() && current_user_can('prefers_dark_theme', true)) ? ' checked' : ''; |
| | | $title = ($checked == '') ? 'Toggle Dark Mode' : 'Toggle Light Mode'; |
| | | $themeSwitch = '<label title="'.$title.'" id="theme-switch" class="toggle-switch" for="theme-switcher"> |
| | | <input class="theme-switch row" id="theme-switcher" type="checkbox"'.$checked.' role="switch" name="dark-mode"><span class="slider">'. |
| | | jvbIcon('light', ['title'=> 'Light Mode']). |
| | | jvbIcon('dark', ['title'=>'Dark Mode']). |
| | | '</span></label>'; |
| | | $showThemeSwitch = (bool)apply_filters('jvb_show_theme_switch', true); |
| | | $themeSwitch = ($showThemeSwitch) ? '<label title="'.$title.'" id="theme-switch" class="toggle-switch" for="theme-switcher"> |
| | | <input class="theme-switch row" id="theme-switcher" name="theme-switcher" type="checkbox"'.$checked.' data-setting="theme" data-theme role="switch" name="dark-mode" aria-label="Toggle dark mode"><span class="slider">'. |
| | | jvbIcon('sun-dim', ['title'=> 'Light Mode']). |
| | | jvbIcon('moon', ['title'=>'Dark Mode']). |
| | | '</span></label>' : ''; |
| | | $breadcrumbs = jvbBuildBreadcrumbs(); |
| | | $afterHeader = apply_filters('jvbBelowHeader', $afterHeader); |
| | | } elseif ($block['attrs']['slug'] == 'footer') { |
| | | $footerText = jvbRandomFooterText(); |
| | | } |
| | | return '<'.$tag.$this->getClassesAndStyles($block['attrs']).'>'. |
| | | |
| | | if ($afterHeader !== '') { |
| | | $afterHeader = '<aside class="sub-header">'.$afterHeader.'</aside>'; |
| | | } |
| | | $footerText = '<div class="scroll-progress"><div class="bar"></div> |
| | | </div>'; |
| | | } elseif ($isFooterTemplate) { |
| | | $beforeHeader = apply_filters('jvbBeforeFooter', ''); |
| | | if ($beforeHeader !== '') { |
| | | $beforeHeader = '<section class="pre-footer">'.$beforeHeader.'</section>'; |
| | | } |
| | | $footerText = jvbRandomFooterText(); |
| | | } |
| | | // jvbDump($beforeHeader,'beforeHeader'); |
| | | // jvbDump('<'.$tag.$this->getClassesAndStyles($block['attrs']).'>','tag'); |
| | | // jvbDump($themeSwitch,'themeSwitch'); |
| | | // jvbDump($this->inside($block, $tag, $content),'inside'); |
| | | // jvbDump($footerText,'footerText'); |
| | | // jvbDump($afterHeader, 'afterheader'); |
| | | // jvbDump($breadcrumbs, 'breadcrumbs'); |
| | | |
| | | return $beforeHeader.'<'.$tag.$this->getClassesAndStyles($block['attrs']).'>'. |
| | | $themeSwitch . |
| | | $this->inside($block, $tag, $content). |
| | | $this->inside($block, $tag, $content) . |
| | | $footerText.'</'.$tag.'>'.$afterHeader.$breadcrumbs; |
| | | } |
| | | |
| | | return $content; |
| | | } |
| | | //core_term_description |
| | |
| | | //core_rss |
| | | //core_search |
| | | //core_shortcode |
| | | //core_social_link |
| | | //core_social_links |
| | | public function render_core_social_link(array $block, string $content):string |
| | | { |
| | | $url = $block['attrs']['url']; |
| | | $service = $block['attrs']['service']; |
| | | $iconName = ($service === 'bluesky') ? 'butterfly' : $service.'-logo'; |
| | | $icon = jvbIcon($iconName); |
| | | if (!$icon) { |
| | | $icon = jvbIcon('link'); |
| | | } |
| | | return '<li><a href="'.$url.'" target="_blank" rel="nofollow" title="Find us on '.ucfirst($service).'">'.$icon.'<span class="screen-reader-text">Find us on '.ucfirst($service).'</span></a></li>'; |
| | | } |
| | | public function render_core_social_links(array $block, string $content):string |
| | | { |
| | | return '<ul class="socials">'.$this->innerBlocks($block).'</ul>'; |
| | | } |
| | | //core_tag_cloud |
| | | |
| | | |
| | | /** |
| | | * Extra feed block localization |
| | | */ |
| | |
| | | protected function localize_feedblock():void |
| | | { |
| | | wp_localize_script('jvb-feed-view-script', 'feedSettings', [ |
| | | 'apiUrl' => rest_url('jvb/v1/'), |
| | | 'nonce' => wp_create_nonce('wp_rest'), |
| | | 'currentUser' => is_user_logged_in() ? [ |
| | | 'id' => get_current_user_id() |
| | | ] : null, |
| | | 'from' => get_the_ID(), |
| | | 'icons' => jvbFeedBlockIcons(), |
| | | |
| | | ]); |
| | | } |
| | | |
| | | /*********************************** |
| | | * Helpers |
| | | **********************************/ |
| | | public function stripTagContents(string $tag, string $content):string |
| | | { |
| | | $clean = preg_replace('/<'.$tag.'\b[^>]*>.*?<\/'.$tag.'>/is', '', $content); |
| | | $clean = preg_replace('/\s+/', ' ', $clean); |
| | | return trim($clean); |
| | | } |
| | | |
| | | public function innerBlocks(array $block, string $before = '', string $after = ''):string |
| | | { |
| | | $content = ''; |
| | | foreach ($block['innerBlocks'] as $b) { |
| | | $method = 'render_'.$this->sanitizeBlockName($b); |
| | | $content .= $before; |
| | | if (method_exists($this, $method)) { |
| | | $content .= $this->$method($b, ''); |
| | | } else { |
| | | $content .= render_block($b); |
| | | } |
| | | $content .= $after; |
| | | } |
| | | return $content; |
| | | $content = ''; |
| | | foreach ($block['innerBlocks'] as $b) { |
| | | $method = 'render_'.$this->sanitizeBlockName($b); |
| | | $function = BASE.$method; |
| | | |
| | | $content .= $before; |
| | | if (function_exists($function)) { |
| | | $content .= $function($b, ''); |
| | | } else if (method_exists($this, $method)) { |
| | | $content .= $this->$method($b, ''); |
| | | } else { |
| | | $content .= render_block($b); |
| | | } |
| | | $content .= $after; |
| | | } |
| | | return $content; |
| | | } |
| | | |
| | | public function inside(array $block, mixed $tag = false, mixed $o = false):string |
| | |
| | | $tag = (str_contains($tag, ' class')) ? strtok($tag, ' class') : $tag; |
| | | $tag = trim($tag); |
| | | } |
| | | |
| | | if (!str_starts_with($o, '<'.$tag)) { |
| | | return $o; |
| | | } |
| | | |
| | | $len = strlen('</'.$tag.'>'); |
| | | |
| | |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * Extract content from a specific nested element |
| | | * @param string $html The HTML to parse |
| | | * @param string $tag The tag name to extract |
| | | * @return string The content of the first matching element, or empty string |
| | | */ |
| | | protected function extractElement(string $html, string $tag): string |
| | | { |
| | | if (empty($html)) { |
| | | return ''; |
| | | } |
| | | |
| | | $dom = new DOMDocument(); |
| | | // Suppress errors for malformed HTML |
| | | libxml_use_internal_errors(true); |
| | | $dom->loadHTML('<?xml encoding="utf-8" ?>' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); |
| | | libxml_clear_errors(); |
| | | |
| | | $elements = $dom->getElementsByTagName($tag); |
| | | if ($elements->length === 0) { |
| | | return ''; |
| | | } |
| | | |
| | | return trim($elements->item(0)->textContent); |
| | | } |
| | | |
| | | public function imageID(int|string $ID, array $block = []):int|false |
| | | { |
| | | if ($ID === '' && !empty($block)) { |
| | | |
| | | if (($block['blockName'] === 'core/post-featured-image' || |
| | | (!array_key_exists('attrs', $block) && !array_key_exists('id', $block['attrs'])))) { |
| | | $ID = get_post_thumbnail_id(); |
| | |
| | | } |
| | | } |
| | | } |
| | | if ($ID == '' || is_null(get_post($ID))) { |
| | | if (!is_int($ID)) { |
| | | return false; |
| | | } |
| | | return $ID; |
| | |
| | | int $ID = 0, |
| | | string $start = 'tiny', |
| | | string $replace = 'large', |
| | | string $postSlug = '' |
| | | ?string $postSlug = null |
| | | ):string { |
| | | $image = $this->image($ID, $start, $replace); |
| | | if ($ID === 0) { |
| | | $ID = $this->imageID($ID); |
| | | } |
| | | if ($postSlug==='') { |
| | | global $post; |
| | | $postSlug = $post->post_name; |
| | | } |
| | | if (!$ID) { |
| | | return ''; |
| | | } |
| | | $imgSlug = get_post($ID)->post_name; |
| | | |
| | | $img = '<a class="open-gallery" target="_blank" rel="nofollow" data-opens="gallery-'.$postSlug.'" data-focus="'.$postSlug.'-'.$imgSlug.'">'.$image; |
| | | $image = jvbFormatImage($ID, $start, $replace, true, $postSlug); |
| | | |
| | | if ($close) { |
| | | $img .= '</a>'; |
| | | return $image; |
| | | } |
| | | return $img; |
| | | $len = strlen('</a>'); |
| | | return substr_replace($image, '', -$len, $len); |
| | | } |
| | | public function image($ID = '', $start = 'tiny', $replace = 'large'):string |
| | | public function image(string $ID = '', string $start = 'tiny', string $replace = 'large'):string |
| | | { |
| | | if ($ID == '') { |
| | | $ID = $this->imageID($ID); |
| | |
| | | if ($ID === 0 || $ID === false) { |
| | | return ''; |
| | | } |
| | | $img = wp_get_attachment_image_src($ID, $start)[0]; |
| | | |
| | | $data = $this->gallerySizes($ID, $replace); |
| | | |
| | | $alt = get_post_meta($ID, '_wp_attachment_image_alt', true); |
| | | $alt = ($alt=='')? '' : ' alt="'.$alt.'" '; |
| | | |
| | | return '<img width="100%" height="auto" src="' . |
| | | $img . '"' .$alt . $data . ' loading="lazy" decoding="async">'; |
| | | } |
| | | public function gallerySizes(int $ID, string $replace = 'medium'):string |
| | | { |
| | | if (!wp_get_attachment_image_src($ID)) { |
| | | return ''; |
| | | } |
| | | if ($replace == 'large') { |
| | | return 'data-small="'. |
| | | wp_get_attachment_image_src($ID, 'large')[0].'" data-medium="'. |
| | | wp_get_attachment_image_src($ID, 'full')[0].'" data-full="'. |
| | | wp_get_attachment_image_src($ID, 'full')[0].'"'; |
| | | } elseif ($replace == 'medium') { |
| | | return 'data-small="'. |
| | | wp_get_attachment_image_src($ID, 'large')[0].'" data-medium="'. |
| | | wp_get_attachment_image_src($ID, 'large')[0].'" data-full="'. |
| | | wp_get_attachment_image_src($ID, 'large')[0].'"'; |
| | | } elseif ($replace == 'thumbnail') { |
| | | return 'data-small="'. |
| | | wp_get_attachment_image_src($ID, 'medium')[0].'" data-medium="'. |
| | | wp_get_attachment_image_src($ID, 'medium')[0].'" data-full="'. |
| | | wp_get_attachment_image_src($ID, 'medium')[0].'"'; |
| | | } |
| | | return ''; |
| | | return jvbFormatImage($ID, $start, $replace, false); |
| | | } |
| | | public function sanitizeBlockName(array $block):string |
| | | { |
| | |
| | | protected function getPresetSpacing(string $spacing):string |
| | | { |
| | | return match ($spacing) { |
| | | 'var:preset|spacing|20' => '0.5rem', // 1 |
| | | 'var:preset|spacing|30' => '1rem', // 2 |
| | | 'var:preset|spacing|40' => '1.5rem', // 3 |
| | | 'var:preset|spacing|50' => '3rem', // 4 |
| | | 'var:preset|spacing|60' => '4rem', // 5 |
| | | 'var:preset|spacing|70' => '5rem', // 6 |
| | | 'var:preset|spacing|80' => '6rem', // 7 |
| | | 'var:preset|spacing|20' => 1, |
| | | 'var:preset|spacing|30' => 2, |
| | | 'var:preset|spacing|40' => 3, |
| | | 'var:preset|spacing|50' => 4, |
| | | 'var:preset|spacing|60' => 5, |
| | | 'var:preset|spacing|70' => 6, |
| | | 'var:preset|spacing|80' => 7, |
| | | default => $spacing, |
| | | }; |
| | | } |
| | |
| | | } |
| | | $classes = []; |
| | | foreach ($attrs as $key => $value) { |
| | | $class = $this->getClass($key, $value); |
| | | $class = $this->getClass($key, $value, $attrs); |
| | | if (is_array($class)) { |
| | | $classes = array_merge($classes, $class); |
| | | } else { |
| | | $classes[] = $class; |
| | | } |
| | | } |
| | | $classes = array_filter($classes, function ($class) { |
| | | return $class!=='' && !str_starts_with($class, 'wp'); |
| | | }); |
| | | |
| | | return $classes; |
| | | return array_filter($classes, function ($class) { |
| | | return $class!=='' && !str_starts_with($class, 'wp'); |
| | | }); |
| | | } |
| | | protected function getClass(string $key, string|bool|array|int $value):string|array |
| | | protected function getClass(string $key, string|bool|array|int $value, array $attrs):string|array |
| | | { |
| | | switch ($key) { |
| | | //Any additional classes the user adds |
| | |
| | | return match ($value) { |
| | | 'is-style-floating' => 'always mobile fixed', |
| | | 'is-style-fixed' => 'fixed bottom', |
| | | default => $value, |
| | | default => str_replace('is-style-', '', $value), |
| | | }; |
| | | case 'contentPosition': |
| | | |
| | | $classes = []; |
| | | $pos = explode(' ', $value); |
| | | foreach($pos as $p) { |
| | | switch ($p) { |
| | | case 'top': |
| | | $classes[] = 'a-start'; |
| | | break; |
| | | case 'right': |
| | | $classes[] = 'end'; |
| | | break; |
| | | case 'bottom': |
| | | $classes[] = 'a-end'; |
| | | break; |
| | | case 'left': |
| | | $classes[] = 'start'; |
| | | break; |
| | | } |
| | | } |
| | | return implode(' ', $classes); |
| | | //Layout attributes |
| | | case 'layout': |
| | | $classes = []; |
| | | $type = 'row'; |
| | | if (array_key_exists('type', $value)) { |
| | | if ($value['type'] === 'constrained') { |
| | | $classes[] = 'container'; |
| | | } |
| | | $type = 'col'; |
| | | // if ($value['type'] === 'constrained') { |
| | | // $classes[] = 'container col'; |
| | | // } |
| | | } |
| | | if (array_key_exists('justifyContent', $value)) { |
| | | if (in_array($value['justifyContent'], ['left', 'right','space-between'])) { |
| | | $classes[] = 'j-'.$value['justifyContent']; |
| | | } |
| | | } |
| | | if (array_key_exists('orientation', $value)) { |
| | | if (array_key_exists('orientation', $value)) { |
| | | $type = 'col'; |
| | | if ($value['orientation'] === 'vertical') { |
| | | $classes[] = 'col'; |
| | | $classes[] = 'col'; |
| | | if (in_array('row', $classes)) { |
| | | $index = array_search('row', $classes); |
| | | unset($classes[$index]); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }else if (array_key_exists('type', $value) && $value['type'] === 'flex') { |
| | | $classes[] = 'row'; |
| | | if (in_array('col', $classes)) { |
| | | $index = array_search('col', $classes); |
| | | unset($classes[$index]); |
| | | } |
| | | } |
| | | //jvbDump($type); |
| | | //jvbDump($value); |
| | | // $check = [$value, $attrs]; |
| | | // foreach ($check as $ch) { |
| | | // |
| | | // } |
| | | if (!array_key_exists('justifyContent', $value) && !array_key_exists('contentPosition', $attrs)) { |
| | | $classes[] = ($type === 'row') ? 'start' : 'a-start'; |
| | | } |
| | | if (array_key_exists('justifyContent', $value) && !array_key_exists('contentPosition', $attrs)) { |
| | | if (in_array($value['justifyContent'], ['left', 'right','space-between'])) { |
| | | // jvbDump($type); |
| | | switch ($value['justifyContent']) { |
| | | case 'right': |
| | | $classes[] = ($type === 'row') ? 'end' : 'a-end'; |
| | | break; |
| | | case 'space-between': |
| | | $classes[] = 'btw'; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | if (array_key_exists('flexWrap', $value)) { |
| | | if ($value['flexWrap'] === 'nowrap') { |
| | | $classes[] = 'nowrap'; |
| | |
| | | case 'dimRatio': |
| | | if (is_numeric($value)) { |
| | | $width = match (true) { |
| | | $value < 25 => 'one-fourth', |
| | | $value < 33 => 'one-third', |
| | | $value < 50 => 'half', |
| | | $value < 66 => 'two-third', |
| | | $value < 75 => 'three-fourth', |
| | | $value < 25 => '25', |
| | | $value < 33 => '33', |
| | | $value <= 50 => '50', |
| | | $value < 66 => '66', |
| | | $value < 75 => '75', |
| | | default => 'full', |
| | | }; |
| | | switch ($key) { |
| | |
| | | case 'style': |
| | | $classes = []; |
| | | //Margin and Padding |
| | | if (array_key_exists('spacing', $value)) { |
| | | foreach (['margin' => 'm', 'padding'=>'p'] as $search => $c) { |
| | | if (array_key_exists($search, $value['spacing'])) { |
| | | foreach ($value['spacing'][$search] as $direction => $size) { |
| | | $size = $this->getPresetSpacing($size); |
| | | if ($size) { |
| | | $classes[] = $c.'-'.$direction.'-'.$size; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | if (array_key_exists('spacing', $value)) { |
| | | foreach (['margin' => 'm', 'padding'=>'p'] as $search => $c) { |
| | | if (array_key_exists($search, $value['spacing'])) { |
| | | $directions = []; |
| | | |
| | | // Collect ONLY preset spacing values for classes |
| | | foreach ($value['spacing'][$search] as $direction => $size) { |
| | | $presetSize = $this->getPresetSpacing($size); |
| | | if ($presetSize) { |
| | | $directions[$direction] = $presetSize; |
| | | } |
| | | // Non-preset values are skipped here and handled by inline styles below |
| | | } |
| | | |
| | | if (empty($directions)) { |
| | | continue; |
| | | } |
| | | |
| | | // Check what directions we have |
| | | $hasTop = isset($directions['top']); |
| | | $hasBottom = isset($directions['bottom']); |
| | | $hasLeft = isset($directions['left']); |
| | | $hasRight = isset($directions['right']); |
| | | |
| | | // Check if axes match |
| | | $xMatch = $hasLeft && $hasRight && $directions['left'] === $directions['right']; |
| | | $yMatch = $hasTop && $hasBottom && $directions['top'] === $directions['bottom']; |
| | | |
| | | // All 4 directions exist and match → p-3 |
| | | if ($hasTop && $hasBottom && $hasLeft && $hasRight && |
| | | count(array_unique($directions)) === 1) { |
| | | $classes[] = $c . '-' . reset($directions); |
| | | } |
| | | // Both axes match → px-3 py-2 |
| | | elseif ($xMatch && $yMatch) { |
| | | $classes[] = $c . 'x-' . $directions['left']; |
| | | $classes[] = $c . 'y-' . $directions['top']; |
| | | } |
| | | // Only X axis matches → px-3 (+ individual for top/bottom) |
| | | elseif ($xMatch) { |
| | | $classes[] = $c . 'x-' . $directions['left']; |
| | | if ($hasTop) { |
| | | $classes[] = $c . 't-' . $directions['top']; |
| | | } |
| | | if ($hasBottom) { |
| | | $classes[] = $c . 'b-' . $directions['bottom']; |
| | | } |
| | | } |
| | | // Only Y axis matches → py-3 (+ individual for left/right) |
| | | elseif ($yMatch) { |
| | | $classes[] = $c . 'y-' . $directions['top']; |
| | | if ($hasLeft) { |
| | | $classes[] = $c . 'l-' . $directions['left']; |
| | | } |
| | | if ($hasRight) { |
| | | $classes[] = $c . 'r-' . $directions['right']; |
| | | } |
| | | } |
| | | // No matches - individual directions |
| | | else { |
| | | foreach ($directions as $direction => $size) { |
| | | $dir = match($direction) { |
| | | 'top' => 't', |
| | | 'bottom' => 'b', |
| | | 'left' => 'l', |
| | | 'right' => 'r', |
| | | default => $direction |
| | | }; |
| | | $classes[] = $c . $dir . '-' . $size; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (array_key_exists('fontSize', $value)) { |
| | | if (in_array($value['fontSize'], ['small', 'large', 'extra-large', 'huge'])) { |
| | | $classes[] = 'text-'.$value['fontSize']; |
| | | $classes[] = 'font-'.$value['fontSize']; |
| | | } |
| | | if (in_array('fontWeight', $value)) { |
| | | $classes[] = 'text-'.$value['fontWeight']; |
| | |
| | | } |
| | | } |
| | | return implode(' ', $classes); |
| | | |
| | | case 'fontSize': |
| | | $classes[] = 'font-'.$value; |
| | | return implode(' ', $classes); |
| | | case 'isStackedOnMobile': |
| | | return ($value === true) ? 'stack-small' : ''; |
| | | case 'width': |
| | | if (is_numeric($value)) { |
| | | $width = match (true) { |
| | | $value < 25 => '25', |
| | | $value < 33 => '33', |
| | | $value <= 50 => '50', |
| | | $value < 66 => '66', |
| | | $value < 75 => '75', |
| | | default => 'full', |
| | | }; |
| | | switch ($key) { |
| | | case 'width': |
| | | return 'width-'.$width; |
| | | case 'dimRatio': |
| | | return 'overlay-'.$width; |
| | | } |
| | | } |
| | | return ''; |
| | | default: |
| | | $ignore = [ |
| | | 'useFeaturedImage', |
| | | 'opacity', |
| | | 'borderColor', |
| | | 'backgroundColor', |
| | | 'textColor', |
| | | 'minHeight', |
| | | 'minHeightUnit', |
| | | 'isDark', |
| | | 'sizeSlug', |
| | | 'isUserOverlayColor', |
| | | 'customOverlayColor', |
| | | 'dimRatio', |
| | | 'placeholder', |
| | | 'alt', |
| | | 'imageFill', |
| | | 'mediaSizeSlug', |
| | | 'isLink', |
| | | 'kind', |
| | | 'label', |
| | | 'type', |
| | | 'id', |
| | | 'url', |
| | | 'label', |
| | | 'shouldSyncIcon', |
| | | 'rel', |
| | | 'opensInNewTab', |
| | | 'title', |
| | | 'ref', |
| | | 'overlayMenu', |
| | | 'slug', |
| | | 'theme', |
| | | 'tagName', |
| | | 'level', |
| | | 'ordered', |
| | | 'area', |
| | | 'mediaId', |
| | | 'mediaLink', |
| | | 'mediaType', |
| | | 'height', //maybe still need? |
| | | ]; |
| | | if (!is_admin() &&!in_array($key, $ignore)) { |
| | | // TESTING |
| | | // jvbDump($key, 'getClass'); |
| | | // jvbDump($attrs); |
| | | } |
| | | |
| | | return ''; |
| | | } |
| | | } |
| | |
| | | |
| | | // Focal point for background images |
| | | case 'focalPoint': |
| | | if (isset($value['x']) && isset($value['y'])) { |
| | | $styles[] = 'background-position: '.($value['x'] * 100).'% '.($value['y'] * 100).'%'; |
| | | } |
| | | $x = (array_key_exists('x', $attrs['focalPoint'])) ? $attrs['focalPoint']['x'] * 100 : 'center'; |
| | | $y = (array_key_exists('y', $attrs['focalPoint'])) ? $attrs['focalPoint']['y'] * 100 : 'center'; |
| | | $styles[] = 'background-position:'.$x.' '.$y.';'; |
| | | |
| | | break; |
| | | |
| | | // Complex style object |
| | |
| | | } |
| | | } |
| | | break; |
| | | case 'dimRatio': |
| | | $ratio = (ceil($value /25) *25); |
| | | $s = 'background-color: rgba(var(--base-rgb), '; |
| | | switch ($ratio) { |
| | | case 0: |
| | | $s .= 'var(--rgb-subtle-hover));'; |
| | | break; |
| | | case 25: |
| | | $s .= 'var(--rgb-light));'; |
| | | break; |
| | | case 50: |
| | | $s .= 'var(--rgb-medium));'; |
| | | break; |
| | | default: |
| | | $s .= 'var(--rgb-heavy));'; |
| | | break; |
| | | } |
| | | $styles[] = $s; |
| | | break; |
| | | |
| | | // Custom styles (any other attributes that need inline styling) |
| | | case 'backgroundType': |
| | |
| | | } |
| | | break; |
| | | |
| | | case 'backgroundColor': |
| | | case 'borderColor': |
| | | case 'textColor': |
| | | $type = ($key === 'backgroundColor') ? 'background-color:' : (($key === 'borderColor') ? 'border-color:' : 'color:'); |
| | | $defaults = apply_filters('jvbColours', ['base', 'contrast', 'action', 'secondary']); |
| | | $continue = true; |
| | | foreach ($defaults as $default) { |
| | | if (str_starts_with($value, $default)) { |
| | | $continue = false; |
| | | $styles[] = $type.'var(--'.$value.')'; |
| | | } |
| | | } |
| | | if ($continue) { |
| | | $styles[] = $type.$value; |
| | | } |
| | | break; |
| | | // Any other attributes that need direct styling |
| | | default: |
| | | $ignore = [ |
| | | 'useFeaturedImage', |
| | | 'opacity', |
| | | 'textAlign', |
| | | 'minHeightUnit', |
| | | 'isDark', |
| | | 'isUserOverlayColor', |
| | | 'contentPosition', |
| | | 'sizeSlug', |
| | | 'customOverlayColor', |
| | | 'alt', |
| | | 'placeholder', |
| | | 'imageFill', |
| | | 'mediaSizeSlug', |
| | | 'isLink', |
| | | 'kind', |
| | | 'label', |
| | | 'type', |
| | | 'id', |
| | | 'url', |
| | | 'label', |
| | | 'shouldSyncIcon', |
| | | 'rel', |
| | | 'opensInNewTab', |
| | | 'title', |
| | | 'ref', |
| | | 'overlayMenu', |
| | | 'slug', |
| | | 'theme', |
| | | 'tagName', |
| | | 'level', |
| | | 'ordered', |
| | | 'area', |
| | | 'className', |
| | | 'fontSize', |
| | | 'layout', |
| | | 'align', |
| | | 'mediaId', |
| | | 'mediaLink', |
| | | 'mediaType', |
| | | 'isStackedOnMobile', |
| | | 'width', |
| | | 'height', // maybe still need? |
| | | ]; |
| | | if (!is_admin() && !in_array($key, $ignore)) { |
| | | //TESTING |
| | | // jvbDump($key, 'getStyle'); |
| | | // jvbDump($attrs); |
| | | } |
| | | // No default inline styles |
| | | break; |
| | | } |
| | |
| | | return $styles; |
| | | } |
| | | |
| | | public function getGallerySizes(int $ID, string $replace):string |
| | | { |
| | | if (!wp_get_attachment_image_src($ID)) { |
| | | return ''; |
| | | } |
| | | if (!has_image_size($replace)) { |
| | | $replace = 'large'; |
| | | } |
| | | |
| | | if ($replace == 'large') { |
| | | return 'data-small="' . |
| | | wp_get_attachment_image_src($ID)[0] . '" data-medium="' . |
| | | wp_get_attachment_image_src($ID, 'large')[0] . '" data-full="' . |
| | | wp_get_attachment_image_src($ID, 'full')[0] . '"'; |
| | | } elseif ($replace == 'medium') { |
| | | return 'data-small="'. |
| | | wp_get_attachment_image_src($ID, 'large')[0].'" data-medium="'. |
| | | wp_get_attachment_image_src($ID, 'large')[0].'" data-full="'. |
| | | wp_get_attachment_image_src($ID, 'large')[0].'"'; |
| | | } elseif ($replace == 'thumbnail') { |
| | | return 'data-small="'. |
| | | wp_get_attachment_image_src($ID, 'medium')[0].'" data-medium="'. |
| | | wp_get_attachment_image_src($ID, 'medium')[0].'" data-full="'. |
| | | wp_get_attachment_image_src($ID, 'medium')[0].'"'; |
| | | } |
| | | return ''; |
| | | } |
| | | |
| | | public function formatImage(int $ID = 0, string $start = 'tiny', string $replace = 'large'):string |
| | | { |
| | | if ($ID === 0) { |
| | | $ID = $this->imageID($ID); |
| | | } |
| | | if ($ID === 0) { |
| | | return ''; |
| | | } |
| | | |
| | | return $this->imgCache->remember( |
| | | ['ID' => $ID, 'start' => $start, 'replace' => $replace], |
| | | function() use ($ID, $start, $replace) { |
| | | $img = wp_get_attachment_image_src($ID, $start); |
| | | if (!$img) { |
| | | return''; |
| | | } |
| | | $img = $img[0]; |
| | | |
| | | $data = $this->getGallerySizes($ID, $replace); |
| | | |
| | | |
| | | |
| | | $alt = get_post_meta($ID, '_wp_attachment_image_alt', true); |
| | | $alt = ($alt=='')? '' : ' alt="'.$alt.'" '; |
| | | return '<img width="300px" height="300px" src="'.$img.'"'.$alt.$data.' loading="lazy" decoding="async">'; |
| | | } |
| | | ); |
| | | if ($ID === 0) { |
| | | $ID = $this->imageID($ID); |
| | | } |
| | | if ($ID === 0) { |
| | | return ''; |
| | | } |
| | | return jvbFormatImage($ID, $start, $replace); |
| | | } |
| | | } |
| | | |
| | | new CustomBlocks(); |
| | | } |