cache = Cache::for('blocks', WEEK_IN_SECONDS); $this->cache->connect('post')->connect('taxonomy'); $this->cache->flush(); add_filter('pre_render_block', [$this, 'prerender'], 10, 3); add_filter('render_block', [$this, 'render'], 10, 2); add_action('init', [$this, 'registerBlockStyles']); } public function registerBlockStyles():void { do_action('jvbBlockStyles'); //Register extra block styles register_block_style( 'core/navigation', [ 'name'=>'condensed', 'label' => __('Condensed', 'jvb') ] ); register_block_style( 'core/navigation', [ 'name'=>'floating', 'label' => __('Floating', 'jvb') ] ); register_block_style( 'core/navigation', [ 'name'=>'fixed', 'label' => __('Fixed', 'jvb') ] ); register_block_style( 'core/group', [ 'name' =>'callout', 'label' => __('Callout', 'jvb') ] ); register_block_style( 'core/group', [ 'name' =>'callalt', 'label' => __('Callout Alt', 'jvb') ] ); register_block_style( 'core/separator', [ 'name' =>'logo', 'label' => __('With Logo', 'jvb') ] ); } protected function checkMethods(?string $content, array $block, ?WP_Block $parent = null, bool $isPrerender = false):?string { $blockName = $this->sanitizeBlockName($block); $base = ($isPrerender) ? 'prerender_' : 'render_'; $method = $base.$blockName; $function = BASE.$method; if (function_exists($function)) { return $function($block, $content, $parent); } else if (method_exists($this, $method)) { $content = $this->$method($block, $content, $parent); return $isPrerender ? $this->maybeOutputCustomStyles().$content : $content; } elseif (!empty($blockName) && JVB_TESTING) { if (!in_array($block['blockName'], $this->getIgnore($isPrerender))) { jvbDump('No method found for '.print_r($block['blockName'], true)); } } return $content; } protected function getIgnore(bool $isPrerender):array { //Ignore for both $base = [ 'core/null' ]; if ($isPrerender) { $base = array_merge($base, [ 'core/query-pagination', 'core/query-pagination-previous', 'core/query-pagination-next', 'core/query-pagination-numbers', 'core/query', 'core/calendar', 'core/archives', ]); } else { $base = array_merge($base, [ ]); } return $base; } public function prerender(?string $content, array $block, ?WP_Block $parent = null):?string { $result = $this->checkMethods($content, $block, $parent, true); return $result; } public function render(string $content, array $block):string { 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 $this->checkMethods($content, $block); } /*********************************** * Blocks **********************************/ /** * Common Blocks */ //For Reference: //core_form //core_form_input //core_form_submission_notification //core_form_submit_button /** * Design blocks */ public function prerender_core_button(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'Button'); // jvbDump($parent, 'Parent'); preg_match('/href="([^"]*)"/', $block['innerHTML'], $url); preg_match('/>([^<]*)<\/a>/', $block['innerHTML'], $label); if (empty($url[1]) || empty($label[1])) { return ''; } $icon = ''; if (str_contains($url[1], 'google.com/maps')) { $icon = jvbIcon('google-logo'); } if (str_contains($url[1], 'maps.apple.com')) { $icon = jvbIcon('apple-logo'); } if ($icon !== '') { return sprintf( '%s Maps', $this->getClassesAndStyles($block['attrs']??[]), esc_url($url[1]), esc_html($label[1]), $icon ); } return sprintf( '%s', $this->getClassesAndStyles($block['attrs']??[]), esc_url($url[1]), esc_html($label[1]) ); } public function prerender_core_buttons(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'buttons'); // jvbDump($parent, 'Parent'); return 'getClassesAndStyles($block['attrs']??[], ['buttons','row']).'>'. $this->innerBlocks($block).''; } public function prerender_core_column(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'column'); // jvbDump($parent, 'Parent'); $styles = (array_key_exists('attrs', $block) && array_key_exists('width', $block['attrs'])) ? ['flex-basis:'.$block['attrs']['width']] : []; return 'getClassesAndStyles($block['attrs']??[], ['col'], $styles).'>'. $this->innerBlocks($block).''; } public function prerender_core_columns(array $block, ?string $content, ?WP_Block $parent):?string { jvbDump($block, 'columns'); $attrs = $block['attrs']??[]; $tagName = array_key_exists('tagName', $attrs) ? $attrs['tagName'] : 'section'; $classes = ['row', 'nowrap']; if (!array_key_exists('isStackedOnMobile', $attrs) || $attrs['isStackedOnMobile'] === true){ $classes[] = 'stack-small'; } return sprintf( '<%s%s>%s', $tagName, $this->getClassesAndStyles($attrs, $classes), $this->innerBlocks($block).'', $tagName ); } //core_comment_template public function prerender_core_group(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'group'); // jvbDump($parent, 'Parent'); $tag = (array_key_exists('tagName', $block['attrs']??[])) ? $block['attrs']['tagName'] : 'div'; return sprintf( '<%s%s>%s', $tag, $tag === 'main' ? '' : $this->getClassesAndStyles($block['attrs']??[], ['group']), $this->innerBlocks($block), $tag ); } //core_home_link //core_more //core_nextpage public function prerender_core_nextpage(array $block, ?string $content, ?WP_Block $parent):?string { return str_replace('', '',str_replace(' '', 'nextpagelink' => __('Next '.jvbIcon('caret-circle-right'), 'jvb'), 'previouspagelink' => __(jvbIcon('caret-circle-left').' Previous', 'jvb'), 'next_or_number'=> 'next', 'echo' => false ]))); } public function prerender_core_separator(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'separator'); // jvbDump($parent, 'Parent'); $attrs = $block['attrs']??[]; $logo = ''; if (array_key_exists('className', $attrs) && $attrs['className'] === 'is-style-logo'){ $logo = apply_filters('jvbSeparatorLogo', 'logo'); if (!empty($logo)) { $logo = jvbIcon($logo); } } return sprintf( '', $this->getClassesAndStyles($attrs), // $logo ); } public function prerender_core_spacer(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'spsacer'); // jvbDump($parent, 'Parent'); return 'getClassesAndStyles($block['attrs']??[], ['spacer'], ['height:2rem']). ' aria-hidden="true">'; } //core_table_of_contents //core_text_columns /** * Embed Block */ //core_embed /** * Media Blocks */ //core_audio public function prerender_core_cover(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'cover'); // jvbDump($parent, 'Parent'); // Extract block attributes $attrs = $block['attrs'] ?:[]; $innerContent = $this->innerBlocks($block); $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 = ''; $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('image($ID)); } elseif ($backgroundType === 'video' && isset($attrs['url'])) { $background .= ''; } // Build classes and styles unset($attrs['url']); $classes = $this->getClassesAndStyles($attrs, ['cover row']); return '' . $background . '
' . $innerContent . '
'; } //core_file public function prerender_core_gallery(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'gallery'); // jvbDump($parent, 'Parent'); return 'getClassesAndStyles($block['attrs']??[], ['gallery']).'>'. $this->innerBlocks($block,'
  • ', '
  • '). ''; } public function prerender_core_image(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'image'); // jvbDump($parent, 'Parent'); $ID = $this->imageID('', $block); if (!$ID) { return ''; } $title = (get_the_title($ID) !== '') ? ''.get_the_title($ID).'' : ''; $caption = (wp_get_attachment_caption($ID)) ? '
    ' . $title . wp_get_attachment_caption($ID) . '
    ' : '
    ' . $title . '
    '; $size = array_key_exists('sizeSlug', $block['attrs']??[]) ? $block['attrs']['sizeSlug'] : 'large'; return 'getClassesAndStyles($block['attrs']??[]).'>'. $this->imageLink(true, $ID, 'tiny', $size) . $caption.''; } public function prerender_core_media_text(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'media text'); // jvbDump($parent, 'Parent'); $ID = $this->imageID('', $block); $attrs = $block['attrs']??[]; $size = array_key_exists('mediaSizeSlug', $attrs) ? $attrs['mediaSizeSlug'] : 'large'; $imgLink = ($ID) ? $this->imageLink(true, $ID, 'tiny', $size) : ''; $inner = $this->innerBlocks($block); $classes = ['media-text', 'row', 'nowrap']; if (!array_key_exists('isStackedOnMobile', $attrs) || $attrs['isStackedOnMobile'] === true) { $classes[] = 'stack-small'; } $inside = array_key_exists('mediaPosition', $attrs) && $attrs['mediaPosition'] === 'right' ? sprintf( '
    %s
    %s
    ', $inner, $imgLink ) : sprintf( '
    %s
    %s
    ', $imgLink, $inner ); return sprintf( '%s', $this->getClassesAndStyles($attrs, $classes), $inside ); } //core_video public function prerender_core_video(array $block, ?string $content, ?WP_Block $parent):?string { jvbDump($block, 'video'); // jvbDump($parent, 'Parent'); $ID = $this->imageID('', $block); if (!$ID) { return ''; } jvbDump($ID); $title = (get_the_title($ID) !== '') ? ''.get_the_title($ID).'' : ''; $caption = (wp_get_attachment_caption($ID)) ? '
    ' . $title . wp_get_attachment_caption($ID) . '
    ' : '
    ' . $title . '
    '; $size = array_key_exists('sizeSlug', $block['attrs']??[]) ? $block['attrs']['sizeSlug'] : 'large'; return 'getClassesAndStyles($block['attrs']??[]).'>'. $this->imageLink(true, $ID, 'tiny', $size) . $caption.''; } /** * Reusable blocks */ //core_pattern /** * Text Blocks */ //prerender_core_code //prerender_core_details //prerender_core_footnotes //prerender_core_classic public function prerender_core_heading(array $block, ?string $content, ?WP_Block $parent):?string { $level = (array_key_exists('level', $block['attrs']??[])) ? $block['attrs']['level'] : '2'; $content = $this->inside($block); $id = sanitize_title(wp_strip_all_tags($this->stripTagContents('small', $content))); return 'getClassesAndStyles($block['attrs']??[]).'>'. $content. ''; } public function prerender_core_list(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'list'); // jvbDump($parent, 'Parent'); $tag = (array_key_exists('ordered', $block['attrs']??[])) ? 'ol' : 'ul'; $output = '<'.$tag.$this->getClassesAndStyles($block['attrs']??[]).'>'.$this->innerBlocks($block).''; return $output; } // public function prerender_core_list_item(array $block):string // { // return 'getClassesAndStyles($block['attrs']).'>'.$this->inside($block).''; // } //prerender_core_missing public function prerender_core_paragraph(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'paragraph'); // jvbDump($parent, 'Parent'); $inside = $this->inside($block); return empty($inside) ? '' : sprintf( '%s

    ', $this->getClassesAndStyles($block['attrs']??[]), $inside ); } public function prerender_core_quote(array $block, ?string $content, ?WP_Block $parent): ?string { // jvbDump($block, 'quote'); // jvbDump($parent, 'Parent'); $innerHTML = $block['innerHTML']; // Extract cite content first $cite = $this->extractElement($innerHTML, 'cite'); $citeHtml = ($cite === '') ? '' : '— '.$cite.''; // Get the blockquote content $content = $this->innerBlocks($block); // Remove the cite element from content if it exists if ($cite !== '') { $content = $this->stripTagContents('cite', $content); } return 'getClassesAndStyles($block['attrs']??[]).'>
    '.$content.'
    '. $citeHtml. ''; } public function prerender_core_pullquote(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'pullquote'); // jvbDump($parent, 'Parent'); $innerHTML = $block['innerHTML']; // Extract cite content first $cite = $this->extractElement($innerHTML, 'cite'); $citeHtml = ($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 'getClassesAndStyles($block['attrs']??[], ['pull']).'>'. $content. $citeHtml. ''; } //prerender_core_table //prerender_core_verse /** * Theme Blocks */ //core_avatar //core_loginout public function prerender_core_loginout(array $block, ?string $content, ?WP_Block $parent):?string { $action = is_user_logged_in() ? 'logout' : 'login'; $attrs = $block['attrs']; $redirect = ''; if (array_key_exists('redirectToCurrent', $attrs) && $attrs['redirectToCurrent']) { global $wp; $redirect = get_home_url(null, $wp->request); } if (array_key_exists('displayLoginAsForm', $attrs) && $attrs['displayLoginAsForm']) { LoginManager::getInstance()->setAction($action); return LoginManager::getInstance()->renderLoginForm($action, $redirect, '

    Login

    '); } return sprintf( '%s', wp_login_url($redirect), $this->getClassesAndStyles($attrs), $action === 'login' ? jvbIcon('sign-in').'Log in' : jvbIcon('sign-out').'Logout' ); } //core_pattern public function prerender_core_site_logo(array $block, ?string $content, ?WP_Block $parent = null):?string { // jvbDump($block, 'site logo'); // jvbDump($parent, 'Parent'); $attrs = $block['attrs']??[]; $open = $close = ''; if ((!is_home() && !is_front_page()) && (!array_key_exists('isLink', $attrs) || $attrs['isLink'] === true)) { $open = ''; } $img = get_theme_mod('custom_logo'); $img = sprintf( '%s', $this->getClassesAndStyles($attrs, ['logo']), $this->image($img, 'tiny', 'thumbnail') ); return $open.$img.$close; } public function prerender_core_site_tagline(array $block, ?string $content, ?WP_Block $parent):?string { $tagline = get_bloginfo('description'); return empty($tagline) ? '' : sprintf( '%s

    ', $this->getClassesAndStyles($block['attrs']??[], ['tagline']), $tagline ); } public function prerender_core_site_title(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'site title'); // jvbDump($parent, 'Parent'); $attrs = $block['attrs']??[]; $tag = (array_key_exists('level', $attrs)) ? $attrs['level'] : 1; $tag = ($tag == 0) ? 'p' : 'h'.$tag; $open = $close = ''; if (!is_front_page() && (!array_key_exists('isLink', $attrs) || $attrs['isLink'] === true)) { $open = sprintf( '', get_home_url() ); $close = ''; } $class = ($tag === 'p') ? $this->getClassesAndStyles($block['attrs']??[], ['title']) : $this->getClassesAndStyles($block['attrs']??[]); return sprintf( '<%s%s>%s%s%s', $tag, $class, $open, get_bloginfo('name'), $close, $tag ); } /** * Theme Comments Blocks */ //core_avatar //core_comment_author_name //core_comment_content //core_comment_date //core_comment_edit_link //core_comment_reply_link //core_comments //core_comments_pagination //core_comments_pagination_next //core_comments_pagination_number //core_comments_pagination_previous //core_comments_title //core_post_comments_form /** * Theme Navigation Blocks */ public function prerender_core_navigation(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'navigation'); // jvbDump($parent, 'Parent'); // jvbDump($block, 'navigation'); $ID = (array_key_exists('ref', $block['attrs']??[])) ? $block['attrs']['ref'] : false; if (empty($block['innerBlocks']) && $ID && get_post($ID)) { $block['innerBlocks'] = parse_blocks(get_post($ID)->post_content); } $attrs = $block['attrs']??[]; $toggle = ''; $classes = []; if (!array_key_exists('overlayMenu', $attrs) || $attrs['overlayMenu'] !== 'never') { $toggle = sprintf( '', $ID, jvbIcon('list'), jvbIcon('x') ); $classes[] = 'mobile'; if (array_key_exists('overlayMenu', $attrs) && $attrs['overlayMenu'] === 'always') { $classes[] = 'always'; } } if (!array_key_exists('layout', $attrs)) { $classes[] = 'left'; $classes[] = 'row'; } $class = $this->getClassesAndStyles($attrs, $classes); $helpmenu = ''; $title = get_the_title($ID); $isMain = false; if ($title === 'Main') { $isMain = true; $helpmenu = sprintf( '', jvbNotificationMenu(), jvbHelpMenu() ); } //Allows to add custom items to a menu, based on the menu name $helpmenu = apply_filters('jvbMenuExtraAfter', $helpmenu, $title, $ID); $main = trim(apply_filters('jvbMenuExtra', $this->innerBlocks($block), $title, $block)); $main = str_starts_with($main, '%s',$main); $skipToContent = $isMain ? ' Skip to Content ' : ''; return sprintf( ' %s%s%s%s', $class, $ID, $skipToContent, $toggle, $main, $helpmenu ); } public function prerender_core_navigation_link(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'navigation link'); // jvbDump($parent, 'Parent'); global $wp; if (!array_key_exists('attrs', $block)) { return ''; } $attrs = $block['attrs']??[]; $url = (str_starts_with($attrs['url'],'/')) ? home_url($attrs['url']) : $attrs['url']; $current = (home_url($wp->request.'/') == $url); $attrs['url'] = $url; $classes = ($current) ? $this->getClassesAndStyles($attrs, ['current']): $this->getClassesAndStyles($attrs); $aria = ''; if ($current) { $aria = ' aria-current="page"'; } $linkOpen = $this->buildNavigationLink($attrs, $aria); return ''.$linkOpen.$block['attrs']['label'].''; } public function prerender_core_navigation_submenu(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'navigation submenu'); // jvbDump($parent, 'Parent'); global $wp; $url = (str_starts_with($block['attrs']['url'],'/')) ? home_url($block['attrs']['url']) : $block['attrs']['url']; $current = (home_url($wp->request) == $url); $attrs = $block['attrs']??[]; $attrs['url'] = $url; $classes = ($current) ? $this->getClassesAndStyles($attrs, ['has-submenu', 'current']): $this->getClassesAndStyles($attrs, ['has-submenu']); $aria = ''; if ($current) { $aria = ' aria-current="page"'; } $id = sanitize_title($block['attrs']['label']); $linkOpen = $this->buildNavigationLink($attrs, $aria); $content = sprintf( '%s%s '; return $content; } protected function buildNavigationLink(array $attrs, string $aria):string { $url =(str_starts_with($attrs['url'],'/')) ? home_url($attrs['url']) : $attrs['url']; $target = $type = $id = $label = $desc = $rel = $title = $kind = ''; foreach ($attrs as $k => $v) { switch ($k) { case 'description': $desc = $v; break; case 'rel': $rel = ' rel="'.$v.'"'; break; case 'title': $title = ' title="'.$v.'"'; break; case 'kind': $kind = $v; break; case 'type': $type = $v; break; case 'opensInNewTab': $target = ' target="'.$v.'"'; break; } } return ''; } /** * Theme Query Blocks */ //core_post_author public function prerender_core_post_author(array $block, ?string $content, ?WP_Block $parent):?string { $attrs = $block['attrs'] ?? []; $size = 96; if (array_key_exists('avatarSize',$attrs) && is_int($attrs['avatarSize'])) { $size = $attrs['avatarSize']; } $byline = $aOpen = $aClose = $avatar = $bio = ''; global $post; $user = get_userdata($post->post_author); if (!array_key_exists('showAvatar', $attrs) || $this->checkAttrs('showAvatar', $attrs)){ $avatar = get_avatar($post->post_author, $size); } if (!array_key_exists('showBio', $attrs) || $this->checkAttrs('showBio', $attrs)) { $bio = wpautop($user->description); } $target = ''; if (array_key_exists('linkTarget', $attrs) && $attrs['linkTarget']=== '_blank') { $target = ' target="_blank"'; } if ($this->checkAttrs('isLink', $attrs)) { $aOpen = sprintf( '', get_author_posts_url($post->post_author), $target ); $aClose = ''; } if (array_key_exists('byline', $attrs)) { $byline = sprintf( '%s — ', $attrs['byline'] ); } $name = $user->display_name; return sprintf( '%s%s%s

    %s%s%s%s

    %s', $this->getClassesAndStyles($attrs, ['row','nowrap']), $aOpen, $avatar, $aClose, $aOpen, $byline, $name, $aClose, $bio ); } //core_post_author_biography public function prerender_core_post_author_name(array $block, ?string $content, ?WP_Block $parent):?string { $attrs = $block['attrs']??[]; global $post; $aOpen = $aClose = ''; if ($this->checkAttrs('isLink', $attrs)) { $aOpen = sprintf( ''; } $author = get_userdata($post->post_author); return sprintf( '%s%s%s

    ', $this->getClassesAndStyles($attrs, ['author']), $aOpen, $author->display_name, $aClose ); } public function prerender_core_post_content(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'post content'); // jvbDump($parent, 'Parent'); $tag = (array_key_exists('tagName', $block['attrs']??[])) ? $block['attrs']['tagName'] : 'main'; if ($content == '') { if(is_singular()) { global $post, $page; $pages = explode('', $post->post_content); $currentContent = $pages[max(0, $page - 1)] ?? $pages[0]; if ($page > 1 && !str_contains($currentContent, '')) { $currentContent = str_replace('','', $currentContent); $currentContent .= ' '; } $block['innerBlocks'] = parse_blocks($currentContent); $result = $this->innerBlocks($block); }else { $result = ''; } } else { $result = $this->innerBlocks($block); } return apply_filters('jvb_post_content_output', $result, $block); } //core_post_date public function prerender_core_post_date(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'post date'); // return null; $attrs = $block['attrs']??[]; $postDate = null; $itemProp = 'datePublished'; $format = array_key_exists('format', $attrs) ? $attrs['format'] : 'M d, Y'; $dateFormat = null; if (array_key_exists('displayType', $attrs)) { switch ($attrs['displayType']) { case 'displayType': $postDate = get_post_modified_time('c'); $dateFormat = get_post_modified_time($format); $itemProp = 'dateModified'; break; } } $postDate = is_null($postDate) ? get_the_date('c') : $postDate; $dateFormat = is_null($dateFormat) ? get_the_date($format) : $dateFormat; $aOpen = $aClose = ''; if ($this->checkAttrs('isLink', $attrs) && !is_singular()) { $aOpen = sprintf( '', get_the_permalink() ); $aClose = ''; } // jvbDump($parent, 'Parent'); return sprintf( '', $postDate, $itemProp, $this->getClassesAndStyles($attrs), $aOpen, $dateFormat, $aClose ); } //core_post_excerpt public function prerender_core_post_excerpt(array $block, ?string $content, ?WP_Block $parent):?string { $attrs = $block['attrs']??[]; $moreText = array_key_exists('moreText', $attrs) ? $attrs['moreText'] : 'Read more '.jvbIcon('arrow-circle-right'); $showMoreOnNewLine = !array_key_exists('showMoreOnNewLine', $attrs) || $this->checkAttrs('showMoreOnNewLine', $attrs); // jvbDump($block); // jvbDump($showMoreOnNewLine); $excerpt = array_filter(explode('

    ',wpautop(get_the_excerpt()))); $classes = $this->getClassesAndStyles($attrs); $excerpt = array_map(function ($line) use ($classes) { return sprintf( '%s', $classes, $line ); }, $excerpt); if (!empty($moreText)) { if ($showMoreOnNewLine) { $excerpt[] = sprintf( '%s

    ', $classes, get_the_permalink(), $moreText ); } else { $last = array_key_last($excerpt); $excerpt[$last] = str_replace('

    ', sprintf('%s', get_the_permalink(), $moreText), $excerpt[$last]); } } return implode('',$excerpt); } public function prerender_core_post_featured_image(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'featured image'); // jvbDump($parent, 'Parent'); global $post; $attrs = $block['attrs']??[]; $ID = get_post_thumbnail_id($post->ID); $aspectRatio = $aOpen = $aClose = ''; if(!is_single($post->ID) && $this->checkAttrs('isLink', $attrs)) { $aOpen = ''; $aClose = ''; } if (array_key_exists('aspectRatio', $attrs)) { $aspectRatio = $attrs['aspectRatio']; } $img = apply_filters('jvbCoreFeaturedImage', '', $post->post_type, $attrs); if (empty($img)) { $img = $this->image($ID); $img = empty($aspectRatio) ? $img : str_replace('%s%s%s', $this->getClassesAndStyles($attrs), $aOpen, $img, $aClose, ):''; } //core_post_navigation_link public function prerender_core_post_navigation_link(array $block, ?string $content, ?WP_Block $parent):?string { $attr = $block['attrs']; $isPrevious = $attr['type']==='previous'; $title = array_key_exists('showTitle', $attr)&&$attr['showTitle']; $linkLabel = array_key_exists('linkLabel', $attr)&&$attr['linkLabel']; $label = array_key_exists('label', $attr) ? $attr['label'] : ''; $arrow = ''; if (array_key_exists('arrow', $attr)) { $dir = $isPrevious ? 'left' : 'right'; $icon = match($attr['arrow']) { 'arrow' => 'arrow-square-', 'chevron' => 'caret-circle-' }; if ($icon) { $arrow = jvbIcon($icon.$dir); } } // return $content; $linkedLabel = $unlinkedLabel = ''; if (!empty($label)) { $linkedLabel = $linkLabel ? $label : ''; $unlinkedLabel = $linkLabel ? '' : $label; } if ($title) { $linkedLabel .=' %title'; } elseif (!empty($label)) { $linkedLabel = $label; $unlinkedLabel = ''; } else { $linkedLabel = $isPrevious ? 'Previous' : 'Next'; $unlinkedLabel = ''; } $result = $isPrevious ? get_previous_post_link( $arrow.$unlinkedLabel.' %link', $linkedLabel ) : get_next_post_link( '%link '.$unlinkedLabel.$arrow, $linkedLabel ); return sprintf('%s', $this->getClassesAndStyles($attr,['row', 'nowrap']), $result ); } //core_post_template public function prerender_core_post_template(array $block, ?string $content):?string { $inner = ''; if (!static::$currentLoop) { jvbDump('No loop stored'); return $content; } if (static::$currentLoop->have_posts()) { while (static::$currentLoop->have_posts()) { static::$currentLoop->the_post(); $inner .= sprintf( '
  • %s
  • ', $this->innerBlocks($block, '','',$block) ); } } return sprintf( '%s', $this->getClassesAndStyles($block['attrs']??[], ['loop']), $inner ); } //core_post_terms public function prerender_core_post_terms(array $block, ?string $content, ?WP_Block $parent):?string { if (!array_key_exists('attrs', $block)) { return ''; } $terms = get_the_terms(get_the_ID(), $block['attrs']['term']); $attrs = $block['attrs']??[]; $out = ''; if ($terms && !is_wp_error($terms)) { $out = sprintf( '', $this->getClassesAndStyles($attrs, ['term-list', 'row', 'left']) ); if (array_key_exists('prefix', $attrs)) { $out .= sprintf( '
  • %s
  • ', $attrs['prefix'] ); } foreach($terms as $term) { $out .= sprintf( '
  • ', get_term_link($term), html_entity_decode($term->name) ); } if (array_key_exists('suffix', $attrs)) { $out .= sprintf( '
  • %s
  • ', $attrs['suffix'] ); } $out .= ''; } return $out; } //core_post_time_to_read public function prerender_core_post_title(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($parent, 'Parent'); $open = $close = ''; $attrs = $block['attrs']??[]; if ($this->checkAttrs('isLink', $attrs)) { $rel = (array_key_exists('rel', $attrs)) ? ' rel="'.$block['attrs']['rel'].'"' : ''; $target = (array_key_exists('linkTarget', $attrs)) ? ' target="'.$block['attrs']['linkTarget'].'"' : ''; $open = ''; $close = ''; } $level = $attrs['level']??2; $title = (!static::$currentLoop && !is_singular()) ? get_the_title(get_queried_object_id()) : get_the_title(); return sprintf( '%s%s%s', $level, $this->getClassesAndStyles($attrs), $open, $title, $close, $level ); } public function prerender_core_query(array $block, ?string $content):?string { global $wp_query; $inherit = $block['attrs']['inherit'] ?? false; if ($inherit) { static::$currentLoop = $wp_query; } else { static::$currentLoop = new WP_Query($this->buildQueryArgs($block['attrs'])); } static::$currentQueryId = $block['attrs']['queryId'] ?? null; static::$originalQuery = $wp_query; $inside = $this->innerBlocks($block); if (str_contains($inside, 'loop')) { $classes = $this->getClassesAndStyles($block['attrs'] ?? [], ['loop']); $classes = str_replace(' class="', '', $classes); $classes = strtok($classes, '"'); $inside = str_replace('loop', $classes, $inside); } static::$currentQueryId = null; static::$currentLoop = null; wp_reset_postdata(); return $inside; } // public function render_core_query(array $block, string $content): string // { // $inside = $this->innerBlocks($block); // if (str_contains($inside, 'loop')) { // $classes = $this->getClassesAndStyles($block['attrs']??[], ['loop']); // $classes = str_replace(' class="', '', $classes); // $classes = strtok($classes, '"'); // // $inside = str_replace('loop', $classes, $inside); // } // return $inside; // } protected function buildQueryArgs(array $attrs): array { $queryID = $attrs['queryId'] ?? null; $args = []; foreach (($attrs['query'] ?? []) as $key => $value) { if (empty($value)) continue; switch ($key) { case 'postType': $args['post_type'] = $value; break; case 'perPage': $args['posts_per_page'] = $value; break; case 'orderBy': $args['orderby'] = $value; break; case 'sticky': match ($value) { 'ignore' => $args['ignore_sticky_posts'] = true, 'exclude' => $args['post__not_in'] = get_option('sticky_posts'), 'only' => $args['post__in'] = get_option('sticky_posts'), default => null }; break; case 'taxQuery': $taxQuery = array_map(fn($tax, $terms) => [ 'taxonomy' => $tax, 'terms' => $terms ], array_keys($value), $value); if (count($taxQuery) > 1) $taxQuery['relation'] = 'OR'; $args['tax_query'] = $taxQuery; break; case 'search': $args['s'] = $value; break; default: $args[$key] = $value; break; } } // Handle pagination from query string $search = 'q-' . $queryID.'-'; foreach ($_GET as $key => $value) { if (str_contains($key, $search) && str_replace($search, '', $key) === 'page') { $args['paged'] = (int)$value; } } return $args; } protected function buildPaginationUrl(int $page): string { $param = 'q-' . static::$currentQueryId . '-page'; $url = remove_query_arg($param); return $page > 1 ? add_query_arg($param, $page, $url) : $url; } protected function getCurrentPage(): int { $param = 'q-' . static::$currentQueryId . '-page'; return isset($_GET[$param]) ? (int)$_GET[$param] : 1; } // public function render_core_query(array $block, string $content): string // { // //// $queryID = $block['attrs']['queryId'] ?? null; //// $inherit = $block['attrs']['inherit'] ?? false; //// //// if ($inherit) { //// global $wp_query; //// $loop = $wp_query; //// } else { //// $args = []; //// 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; //// //// } //// } //// $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 .= '
    '; //// if ($loop->have_posts()) { //// while ($loop->have_posts()) { //// $loop->the_post(); //// $postType = get_post_type(); //// $inner .= '
    ' . $this->innerBlocks($innerBlock) . '
    '; //// } //// } //// $inner .= '
    '; //// break; //// } //// } //// //// // Reset only after a custom query, not the main query //// if (!$inherit) { //// wp_reset_postdata(); //// } // // $tagName = $block['attrs']['tagName'] ?? 'div'; //// return sprintf( //// '<%s class="loop">%s', //// $tagName, //// $this->innerBlocks($block), //// $tagName //// ); // return $this->innerBlocks($block); // } //core_query_no_results public function prerender_core_query_no_results(array $block, ?string $content):?string { if (!static::$currentLoop || static::$currentLoop->have_posts()) { return ''; } $inside = $this->innerBlocks($block); return empty($inside) ? '' : sprintf( '%s', $this->getClassesAndStyles($block['attrs']??[], ['no-results']), $inside ); } //core_query_pagination public function prerender_core_query_pagination(array $block, ?string $content):?string { return sprintf( '%s', $this->getClassesAndStyles($block['attrs']??[], ['pagination', 'condensed','btw']), $this->innerBlocks($block) ); } //core_query_pagination_next public function prerender_core_query_pagination_next(array $block, ?string $content, ?WP_Block $parent):?string { if (!static::$currentLoop) return ''; $currentPage = $this->getCurrentPage(); $maxPages = static::$currentLoop->max_num_pages; if ($currentPage >= $maxPages) return ''; $nextLabel = $rArrow = ''; $type = get_post_type_object(get_post_type())->label; if ($parent) { $attrs = $parent->attributes; if (array_key_exists('paginationArrow', $attrs)){ $rArrow = match($attrs['paginationArrow']) { 'chevron' => jvbIcon('caret-circle-right'), default => jvbIcon('arrow-circle-right') }; } if (!array_key_exists('showLabel', $attrs) || $attrs['showLabel'] === true) { $nextLabel = 'Next '.$type; } } else { $rArrow = jvbIcon('caret-circle-right'); } $aOpen = sprintf( ''; return sprintf( '%s%s%s%s', $aOpen, $nextLabel, $rArrow, $aClose ); } //core_query_pagination_numbers public function prerender_core_query_pagination_numbers(array $block, ?string $content):?string { if (!static::$currentLoop) return ''; $currentPage = $this->getCurrentPage(); $maxPages = (int)static::$currentLoop->max_num_pages; $attrs = $block['attrs']??[]; if ($maxPages <= 1) return ''; $midSize = $attrs['midSize'] ?? 2; $endSize = 1; $items = ''; $gap = false; for ($i = 1; $i <= $maxPages; $i++) { if (($i <= min($endSize + 1, $maxPages)) || ($i >= max(1, $currentPage - $midSize) && $i <= min($maxPages, $currentPage + $midSize)) || ($i >= max(1, $maxPages - $endSize) && $i <= $maxPages)) { $gap = true; $items .= ($i === $currentPage) ? sprintf('
  • %d
  • ', $i) : sprintf('
  • %d
  • ', $this->buildPaginationUrl($i), $i); } elseif ($gap) { $gap = false; $items .= sprintf( '
  • %s
  • ', jvbIcon('dots-three') ); } } return sprintf('%s', $this->getClassesAndStyles($attrs, ['row', 'nowrap']), $items ); } //core_query_pagination_previous public function prerender_core_query_pagination_previous(array $block, ?string $content, ?WP_Block $parent):?string { if (!static::$currentLoop) return ''; $currentPage = $this->getCurrentPage(); $maxPages = static::$currentLoop->max_num_pages; if ($currentPage <= 1) return ''; $nextLabel = $rArrow = ''; $type = get_post_type_object(get_post_type())->label; if ($parent) { $attrs = $parent->attributes; if (array_key_exists('paginationArrow', $attrs)){ $rArrow = match($attrs['paginationArrow']) { 'chevron' => jvbIcon('caret-circle-left'), default => jvbIcon('arrow-circle-left') }; } if (!array_key_exists('showLabel', $attrs) || $attrs['showLabel'] === true) { $nextLabel = 'Previous '.$type; } } else { $rArrow = jvbIcon('caret-circle-left'); } $aOpen = sprintf( ''; return sprintf( '%s%s%s%s', $aOpen, $nextLabel, $rArrow, $aClose ); } public function prerender_core_query_title(array $block, ?string $content, ?WP_Block $parent):?string { jvbDump($block, 'query title'); $attr = $block['attrs']; $name = ''; $showPrefix = $attr['showPrefix']??false; $obj = get_queried_object(); if (is_tax()) { $name = $showPrefix ? $obj->label.':' : ''; $name .= $obj->name; } else if (is_post_type_archive()) { $name = $showPrefix ? 'Archive: ' : ''; $name .= $obj->label; } elseif (is_search()) { $name = 'Search results for: '.get_search_query(); } elseif (is_singular()) { $name = $obj->post_title; } $level = array_key_exists('level', $attr) ? 'h'.$attr['level'] : 'h1'; return sprintf( '<%s id="%s">%s', $level, sanitize_title($name), $name, $level ); } //core_read_more public function prerender_core_template_part(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'template part'); // jvbDump($parent, 'Parent'); $slug = $block['attrs']['slug'] ?? null; $theme = $block['attrs']['theme'] ?? get_stylesheet(); $tag = $block['attrs']['tagName'] ?? 'div'; if (!$slug) { return $content; } // Try to get the template part post (customized via FSE) $template_part = get_block_template( "$theme//$slug", 'wp_template_part' ); if ( $template_part && ! empty( $template_part->content ) ) { $block['innerBlocks'] = parse_blocks( $template_part->content ); $before = $themeSwitch = $after = $beforeClose = ''; switch ($tag) { case 'header': $before = apply_filters('jvbAboveHeader', ''); if (!empty($before)) { $before = sprintf( '', $before ); } $themeSwitch = jvbDarkModeToggle(); $after = apply_filters('jvbBelowHeader', $after); if (!empty($after)) { $after = sprintf( '', $after ); } $after .= BreadcrumbManager::getInstance()->renderNavigation(); $beforeClose = '
    '; break; case 'footer': $before = apply_filters('jvbBeforeFooter', $before); if (!empty($before)) { $before = sprintf( '', $before ); } $beforeClose = jvbRandomFooterText(); break; } return sprintf( '%s<%s%s>%s%s%s%s', $before, $tag, $this->getClassesAndStyles($block['attrs']??[]), $themeSwitch, $this->innerBlocks($block), $beforeClose, $tag, $after ); } if (JVB_TESTING) { jvbDump('Could not create template part block for '.$block['blockName']); } return $content; // $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)) { // $innerContent = $content; // // $tag = $isHeaderTemplate ?: $isFooterTemplate ?: 'div'; // // $breadcrumbs = $themeSwitch = $afterHeader = $beforeHeader = $footerText= ''; // if ($isHeaderTemplate) { // // $beforeHeader = apply_filters('jvbAboveHeader', $beforeHeader); // if ($beforeHeader !== '') { // $beforeHeader = ''; // } // $themeSwitch = jvbDarkModeToggle(); // $breadcrumbs = BreadcrumbManager::getInstance()->renderNavigation(); // $afterHeader = apply_filters('jvbBelowHeader', $afterHeader); // // if ($afterHeader !== '') { // $afterHeader = ''; // } // $footerText = '
    //
    '; // } elseif ($isFooterTemplate) { // $beforeHeader = apply_filters('jvbBeforeFooter', ''); // if ($beforeHeader !== '') { // $beforeHeader = ''; // } // $footerText = jvbRandomFooterText(); // } // // $content = $beforeHeader.'<'.$tag.$this->getClassesAndStyles($block['attrs']??[]).'>'. // $themeSwitch . // $this->innerBlocks($block). //// $this->innerBlocks($block). //// $innerContent. // $footerText.''.$afterHeader.$breadcrumbs; // } // // return $content; } //core_term_description /** * Widgets Blocks */ //core_archives public function render_core_archives(array $block, string $content):string { jvbDump($block, 'archives'); $attrs = $block['attrs']??[]; $isDropdown = $this->checkAttrs('displayAsDropdown', $attrs); $replace = strtok($content,'>').'>'; $content = str_replace($replace, '', $content); if ($isDropdown) { $content = sprintf( '%s', $this->getClassesAndStyles($attrs, ['archive dropdown']), $content ); } else { $content = sprintf( '%s', $this->getClassesAndStyles($attrs, ['archive-list']), $content ); } return $content; } //core_calendar public function render_core_calendar(array $block, string $content):string { $content = $this->inside($block, false, $content); $replace = strtok($content, '>').'>'; $content = str_replace($replace, '', $content); return sprintf( '%s', $this->getClassesAndStyles($block['attrs']??[], ['calendar']), $content ); } //core_categories public function prerender_core_categories(array $block, ?string $content, ?WP_Block $parent):?string { $attrs = $block['attrs']??[]; $args = [ 'taxonomy' => 'category', 'hide_empty' => !$this->checkAttrs('showEmpty', $attrs) ]; $showHierarchy = $this->checkAttrs('showHierarchy', $attrs); if ($this->checkAttrs('showOnlyTopLevel', $attrs) || $showHierarchy){ $args['parent'] = 0; } $terms = $this->getTerms($args, $showHierarchy); if (!$terms){ return ''; } $showPostCounts = $this->checkAttrs('showPostCounts', $attrs); $isDropdown = $this->checkAttrs('displayAsDropdown', $attrs); if ($isDropdown) { $this->counter('core_categories'); } $tax = get_taxonomy($args['taxonomy']); $taxonomyName = $tax->label??'Categories'; $taxonomySingular = $tax->labels->singular_name??'Category'; $inner = $this->buildTermList($terms, $taxonomyName, $taxonomySingular, $isDropdown, $showPostCounts); if ($isDropdown) { return sprintf( '%s', $this->getClassesAndStyles($attrs, ['taxonomy-dropdown']), $inner ); } return sprintf( '%s', $this->getClassesAndStyles($attrs, ['taxonomy-list', jvbNoBase($args['taxonomy'])]), $inner ); } public function getTerms(array $args, bool $showHierarchy = false):array|false { $terms = get_terms($args); if (!$terms || is_wp_error($terms)) { return false; } $terms = array_map(function ($term) { return (array) $term; }, $terms); if ($showHierarchy) { $terms = array_map(function ($term) use ($args) { $args['parent'] = $term['term_id']; $children = $this->getTerms($args, true); $term['children'] = $children?:[]; return $term; }, $terms); } return $terms; } protected function buildTermList(array $terms, string $taxonomyName, string $taxonomySingular, bool $isDropdown, bool $showPostCounts, bool $isOpening = true, int $level = 0):string { $out = ''; if ($isOpening) { $out = $isDropdown ? sprintf( ' ' : ''; } else if (!$isDropdown) { $out .= ''; } return $out; } //core_html //core_latest_comments //core_latest_posts public function prerender_core_latest_posts(array $block, ?string $content, ?WP_Block $parent):?string { $attrs = $block['attrs']??[]; // jvbDump($block, 'latest posts'); $args = []; $title = 'Latest Posts'; $args['order'] = array_key_exists('order', $attrs) ? strtoupper($attrs['order']) : 'DESC'; $args['orderby'] = array_key_exists('orderBy', $attrs) ? $attrs['orderBy'] : 'date'; $args['posts_per_page'] = array_key_exists('postsToShow', $attrs) ? $attrs['postsToShow'] : 5; if (array_key_exists('categories', $attrs)) { $list = jvbCommaList(array_column($attrs['categories'], 'name')); $args['tax_query'] = []; $args['tax_query'][] = [ 'taxonomy' => 'category', 'terms' => array_column($attrs['categories'], 'id') ]; $title .= ' in '.$list; } $posts = new WP_Query($args); if (!$posts->have_posts()) { return ''; } $posts = array_map(function ($post) use ($attrs) { $img = $this->checkAttrs('displayFeaturedImage', $attrs) ? $this->image(get_post_thumbnail_id($post->ID), 'tiny', 'thumbnail') : ''; $author = $this->checkAttrs('displayAuthor', $attrs) ? sprintf( '%s', get_author_posts_url($post->post_author), get_userdata($post->post_author)->display_name ) : ''; $date = $this->checkAttrs('displayPostDate', $attrs) ? sprintf( '', date('Y-m-d', strtotime($post->post_date)), date_i18n('M j, Y', strtotime($post->post_date)) ) : ''; $authorDate = $author; if (!empty($authorDate) && !empty($date)) { $authorDate .= ' | '.$date; } else if (!empty($date)) { $authorDate = $date; } $excerpt = ''; if ($this->checkAttrs('displayPostContent', $attrs)) { if (array_key_exists('excerptLength', $attrs)) { $excerpt = wp_trim_words(get_the_content($post->ID), $attrs['excerptLength'], '...'); } else { $excerpt = get_the_excerpt($post->ID); } } if (!empty($excerpt)) { $excerpt = wpautop($excerpt); } return sprintf( '
  • %s

    %s%s

    %s
  • ', $img, get_the_permalink($post->ID), $post->post_title, !empty($authorDate) ? ' — '.$authorDate.'' : '', $excerpt ); }, $posts->posts); wp_reset_postdata(); return sprintf( '%s', // $title, $this->getClassesAndStyles($attrs, ['post-list']), implode('', $posts) ); } //core_page_list public function prerender_core_page_list(array $block, ?string $content, ?WP_Block $parent):?string{ $attrs = $block['attrs']??[]; $parent = array_key_exists('parentPageID', $attrs) ? $attrs['parentPageID'] : 0; $pages = new WP_Query([ 'post_type' => 'page', 'posts_per_page' => -1, 'parent' => $parent ]); if (!$pages->have_posts()) { return ''; } $inside = []; foreach($pages->posts as $page) { jvbDump($page); $inside[] = sprintf( '
  • %s', get_the_permalink($page->ID), $page->post_title ); } wp_reset_postdata(); return sprintf( '%s', $this->getClassesAndStyles($attrs, ['page-list']), implode('',$inside) ); } //core_page_list_item (doesn't seem to be a thing) // public function prerender_core_page_list_item(array $block, ?string $content, ?WP_Block $parent):?string{ // return $content; // } //core_ // public function prerender_core_rss(array $block, ?string $content, ?WP_Block $parent):?string // { // jvbDump($block, 'rss'); // return $content; // } //core_search public function prerender_core_search(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'search'); $attrs = $block['attrs']??[]; $label = array_key_exists('label', $attrs) && !empty($attrs['label']) ? $attrs['label'] : ''; if (array_key_exists('showLabel', $attrs) && $attrs['showLabel'] === false) { $label = ''; } $placeholder = array_key_exists('placeholder', $attrs) ? $attrs['placeholder'] : 'Search...'; $buttonText = array_key_exists('buttonText', $attrs) && !empty($attrs['buttonText']) ? $attrs['buttonText'] : ''; $isInside = array_key_exists('buttonPosition', $attrs) && $attrs['buttonPosition'] === 'button-inside'; $hideInput = $this->checkAttrs('isSearchFieldHidden', $attrs) || (array_key_exists('buttonPosition', $attrs) && $attrs['buttonPosition'] === 'button-only'); return str_replace('
    getClassesAndStyles($attrs, ['search-container', 'row', 'left', 'nowrap']) ), jvbSearch($placeholder, uniqid(), $label, $buttonText, $isInside, $hideInput)); } //core_shortcode public function prerender_core_social_link(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'social link'); // jvbDump($parent, 'Parent'); $parentAttrs = false; if ($parent) { $parentAttrs = $parent->attributes; } $attrs = $block['attrs']??[]; $url = $attrs['url']??''; $service = $attrs['service']?:''; $iconName = ($service === 'bluesky') ? 'butterfly' : $service.'-logo'; $icon = jvbIcon($iconName); if (!$icon) { $icon = jvbIcon('link'); } $serviceName = $this->getServiceName($service); $label = $parentAttrs && (!array_key_exists('className', $parentAttrs) || !str_contains($parentAttrs['className'], 'logos-only')) ? sprintf( '%s', $serviceName ) : sprintf( 'Find us on %s', $serviceName ); $pillShaped = $parentAttrs && (array_key_exists('className', $parentAttrs) && str_contains($parentAttrs['className'], 'pill-shape')) ? 'style="border-radius:var(--radius-outer);"' : ''; return sprintf( '
  • %s%s
  • ', $url, $serviceName, $pillShaped, $icon, $label ); } private function getServiceName(string $service) { return match($service){ 'wordpress' => 'WordPress', default => ucfirst($service) }; } public function prerender_core_social_links(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block['attrs']??[], 'social links'); // jvbDump($parent, 'Parent'); return sprintf( '%s', $this->getClassesAndStyles($block['attrs']??[], ['socials']), $this->innerBlocks($block, '','',$block) ); } //core_tag_cloud public function prerender_core_tag_cloud(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'tag cloud'); $attrs = $block['attrs']??[]; $taxonomy = (array_key_exists('taxonomy', $attrs) && !empty($attrs['taxonomy'])) ? $attrs['taxonomy'] : 'post_tag'; $showCounts = $this->checkAttrs('showTagCounts', $attrs); $terms = get_terms([ 'taxonomy' => $taxonomy, 'hide_empty' => true, ]); if (!$terms || is_wp_error($terms)) { return ''; } $inside = ''; foreach ($terms as $term) { $url = get_term_link($term->term_id, $taxonomy); $count = $showCounts ? sprintf( '%d', $term->count ) : ''; $size = match(true) { $term->count <= 2 => 'small', $term->count <= 5 => 'x-small', $term->count <= 10 => 'medium', $term->count <= 15 => 'x-medium', $term->count <= 20 => 'large', $term->count <= 25 => 'x-large', $term->count <= 30 => 'xx-large', $term->count > 30 => 'xxx-large', }; $fontSize = 'font-size: var(--txt-'.$size.');'; $inside .= sprintf( '
  • ', $size, // $fontSize, $url, $term->name, $count ); } return sprintf( '%s', $this->getClassesAndStyles($attrs, ['term-list','cloud', jvbNoBase($taxonomy)]), $inside ); } /** * Extra feed block localization */ protected function localize_feedblock():void { wp_localize_script('jvb-feed-view-script', 'feedSettings', [ 'currentUser' => is_user_logged_in() ? [ 'id' => get_current_user_id() ] : null, 'from' => get_the_ID(), ]); } /*********************************** * 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 = '', ?array $parent = null):string { if ($parent) { $parent = new WP_Block($parent); } $content = ''; foreach ($block['innerBlocks'] as $b) { $rendered = $parent ? $this->checkMethods(null, $b, $parent, true) : render_block($b); $content .= sprintf('%s%s%s', $before, $rendered, $after ); } return $content; } public function inside(array $block, mixed $tag = false, mixed $o = false): string { $html = $o ?: trim($block['innerHTML']); if (empty($html)) { return ''; } if (preg_match('/^<(\w+)[^>]*>(.*)<\/\1>$/s', $html, $matches)) { if ($tag && strtolower($matches[1]) !== strtolower($tag)) { return $html; } return trim($matches[2]); } return $html; } /** * 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('' . $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(); } else { if (array_key_exists('id', $block['attrs']??[])) { $ID = $block['attrs']['id']; } elseif (array_key_exists('mediaId', $block['attrs'])) { $ID = $block['attrs']['mediaId']; } } } if (!is_int($ID)) { return false; } return $ID; } public function imageLink( bool $close = false, int $ID = 0, string $start = 'tiny', string $replace = 'large', ?string $postSlug = null ):string { $image = jvbFormatImage($ID, $start, $replace, true, $postSlug); if ($close) { return $image; } $len = strlen(''); return substr_replace($image, '', -$len, $len); } public function image(string $ID = '', string $start = 'tiny', string $replace = 'large'):string { if ($ID == '') { $ID = $this->imageID($ID); } if ($ID === 0 || $ID === false) { return ''; } return jvbFormatImage($ID, $start, $replace, false); } public function sanitizeBlockName(array $block):string { if (!array_key_exists('blockName', $block) || is_null($block['blockName'])) { return ''; } return str_replace( '/', '_', str_replace( '-', '_', $block['blockName'] ) ); } /** * Replaces the outside tag of an HTML string * @param string $content * @param bool|string $tagName * @param string $classes * @param string $styles * * @return string */ public function replaceOutsideTag( string $content, bool|string $tagName = false, string $classes = '', string $styles = '' ):string { if ($tagName && !in_array($tagName, ['div', 'section', 'article', 'aside', 'main'])) { return $content; } $content = explode('>', $content); if ($classes !== '') { if (!str_contains($classes, 'class="')) { $classes = ' class="'.$classes.'"'; } } if ($styles !== '') { if (!str_contains($styles, 'style="')) { $styles = ' style="'.$styles.'"'; } } if ($tagName) { $content[0] = '<'.str_replace( '<', '', str_replace( '>', '', $tagName ) ).$classes.$styles; } else { unset($content[0]); } unset($content[array_key_last($content)]); $out = ''; foreach ($content as $c) { $out .= $c.'>'; } return $out; } protected function getClassesAndStyles( array $attrs, array $classes = [], array $styles = [] ):string { // Get styles and classes from attributes $attr_styles = $this->getInlineStyles($attrs); $attr_classes = $this->getClasses($attrs); if(array_key_exists('slug', $attrs) && $attrs['slug'] === 'footer') { $classes[] = 'col'; } // Merge with passed classes and styles $styles = array_merge($attr_styles, $styles); $classes = array_merge($attr_classes, $classes); if (!empty(static::$pendingClass)) { $classes = array_merge($classes, static::$pendingClass); static::$pendingClass = []; } $classes = array_unique($classes); $data = $this->getDataset($attrs); // Build attribute strings $class_string = !empty($classes) ? ' class="' . implode(' ', $classes) . '"' : ''; $style_string = !empty($styles) ? ' style="' . implode(';', $styles) . '"' : ''; $data_string = ''; if (!empty($data)) { foreach ($data as $d => $v) { if ($d === 'bg-small') { $data_string .= ' data-bg-img'; } $data_string .= sprintf( ' data-%s="%s"', $d, $v ); } } $return = trim($class_string . $style_string . $data_string); return ($return=='')? '' : ' '.$return; } /** * @param string $spacing * * @return string */ protected function getPresetSpacing(string $spacing):string { return match ($spacing) { '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, }; } protected function getClasses(array $attrs):array { if (empty($attrs)) { return []; } $classes = []; foreach ($attrs as $key => $value) { $class = $this->getClass($key, $value, $attrs); if (is_string($class)) { $class = explode(' ', $class); } $classes = array_merge($classes, $class); } return array_unique(array_filter($classes, function ($class) { return $class!=='' && !str_starts_with($class, 'wp'); })); } protected function getClass(string $key, string|bool|array|int $value, array $attrs):string|array { //TODO: gradient switch ($key) { //Any additional classes the user adds case 'className': return match ($value) { 'is-style-floating' => 'always mobile fixed', 'is-style-fixed' => 'fixed bottom', default => str_replace('is-style-', '', $value), }; case 'contentPosition': return $this->getContentPosition($value); case 'term': case 'taxonomy': return jvbNoBase($value); //Layout attributes case 'layout': return $this->getLayout($value, $attrs); case 'align': return !empty($value) ? 'align-'.$value : ''; case 'verticalAlignment': switch ($value) { case 'bottom': $value = 'btm'; break; case 'center': $value = 'y-mid'; default: } return !empty($value) ? $value : ''; case 'isStackedOnMobile': return ($value === true) ? 'stack-small' : ''; case 'justifyContent': return !empty($value) ? 'j-'.$value : ''; case 'orientation': return $value==='column' ? 'column' : ''; case 'width': return $this->getWidth($value); case 'dimRatio': return $this->getDimRatio($value); case 'overlayColor': return $value; break; //Typography case 'textAlign': return !empty($value) ? 'text-'.$value : ''; case 'dropCap': return $value === true ? 'drop-cap' : ''; //Media case 'hasParallax': return $value === true ? 'bg-parallax' : ''; case 'isRepeated': return $value === true ? 'bg-repeat' : ''; //Style base: case 'style': return $this->getPresetStyles($value); case 'fontSize': $classes[] = 'font-'.$value; return implode(' ', $classes); case 'postLayout': $classes[] = 'item-grid'; if (isset($attrs['columns']) && $attrs['columns']!== 3){ $classes[] = sprintf( 'split-%d', $attrs['columns'] ); } return $classes; default: if (JVB_TESTING && !is_admin() &&!in_array($key, $this->ignore)) { // TESTING jvbDump($attrs, '[getClass] '.$key); } return ''; } } /*** CLASS HELPERS ***/ private function getContentPosition(string $value):string { $classes = []; $pos = explode(' ', $value); foreach($pos as $p) { switch ($p) { case 'top': $classes[] = 'top'; break; case 'right': $classes[] = 'right'; break; case 'bottom': $classes[] = 'btm'; break; case 'left': $classes[] = 'left'; break; } } return implode(' ', $classes); } private function getLayout(array $value, array $attrs):array { // jvbDump($value, 'getLayout'); $classes = []; $type = 'row'; $isRow = true; //Determine type if ((array_key_exists('type', $value) && !in_array($value['type'], ['flex', 'grid'])) || (array_key_exists('orientation', $value) && $value['orientation'] === 'vertical')) { $type = 'col'; $isRow = false; } elseif (array_key_exists('type', $value) && $value['type'] === 'grid') { $type = 'item-grid'; $isRow = false; if (array_key_exists('columnCount', $value) && $value['columnCount']!== 3) { $classes[] = sprintf( 'split-%s', $value['columnCount'] ); } } if (array_key_exists('justifyContent', $value) && !array_key_exists('contentPosition', $attrs)) { switch ($value['justifyContent']) { case 'right': $classes[] = 'right'; break; case 'center': $classes[] = 'x-mid'; break; case 'space-between': $classes[] = 'x-btw'; break; case 'left': $classes[] = 'left'; break; case 'space-evenly': $classes[] = 'x-even'; break; case 'space-around': $classes[] = 'x-around'; break; case 'stretch': $classes[] = 'stretch'; } } else { $classes[] = 'left'; } if (array_key_exists('verticalAlignment', $value)) { switch ($value['verticalAlignment']) { case 'bottom': $classes[] = 'btm'; break; case 'top': $classes[] = 'top'; break; case 'center': $classes[] = 'y-mid'; break; case 'space-between': $classes[] = 'y-btw'; break; case 'space-around': $classes[] = 'y-around'; break; case 'space-even': $classes[] = 'y-even'; } } if (array_key_exists('flexWrap', $value)) { if ($value['flexWrap'] === 'nowrap') { $classes[] = 'nowrap'; } } $classes[] = $type; return $classes; } private function getWidth(string $value):string { $value = (int)str_replace('%', '', $value); return sprintf( 'width-%d', match (true) { $value <= 25 => '25', $value <= 33 => '33', $value <= 50 => '50', $value <= 66 => '66', $value <= 75 => '75', default => 'full', } ); } private function getDimRatio(string $value):string { if (is_numeric($value)) { return sprintf( 'op-%d', match (true) { $value <= 14 => '1', $value <= 28 => '2', $value <= 42 => '3', $value <= 56 => '45', $value <= 70 => '4', $value <= 84 => '5', default => '6', } ); } return ''; } private function getPresetStyles(array $value):string { $classes = []; //Margin and Padding if (array_key_exists('spacing', $value)) { $classes = array_merge($classes, $this->buildSpacingClasses($value)); } if (array_key_exists('color', $value)) { if (array_key_exists('duotone', $value['color'])) { $preset = explode('|', $value['color']['duotone']); $preset = $preset[array_key_last($preset)]; if (str_contains($preset, '-')) { $preset = explode('-', $preset); } else { $preset = [$preset]; } $classes[] = 'duotone'; foreach ($preset as $p) { $classes[] = $p; } } } if (array_key_exists('fontSize', $value)) { if (in_array($value['fontSize'], ['small', 'large', 'extra-large', 'huge'])) { $classes[] = 'font-'.$value['fontSize']; } if (in_array('fontWeight', $value)) { $classes[] = 'text-'.$value['fontWeight']; } if (in_array('textTransform', $value)) { if (in_array($value['textTransform'], ['uppercase', 'capitalize', 'lowercase'])) { $classes[] = $value['textTransform']; } } } return implode(' ', $classes); } private function buildSpacingClasses(array $value):array { $classes = []; 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; } } } } return $classes; } protected function getInlineStyles(array $attrs):array { if (empty($attrs)) { return []; } $styles = []; foreach ($attrs as $key => $value) { $style = $this->getStyle($key, $value, $attrs); if (is_string($style)) { $style = [$style]; } $styles = array_merge($styles, $style); } return $styles; } protected function getStyle(string $key, string|bool|array|int $value, array $attrs):array|string { $styles = []; switch ($key) { // Font family settings case 'size': return $this->getIconSizeStyle($value); case 'fontFamily': return $this->getFontFamilyStyle($value); // Icon color (for icon blocks) case 'iconColorValue': return $this->getColorStyle($value); // Minimum height settings case 'minHeight': return $this->getMinHeightStyle($value, $attrs); // Background URL (for cover, media blocks) case 'url': if (!empty($value) && str_starts_with($value, 'http')) { return 'background-image: url('.$value.')'; } break; // Focal point for background images case 'focalPoint': return $this->getFocalPointStyle($value); // Complex style object case 'style': return $this->extractStyles($value, $attrs); case 'dimRatio': return $this->getDimRatioStyle($value, $attrs); // Custom styles (any other attributes that need inline styling) case 'backgroundType': if ($value === 'video' && isset($attrs['backgroundUrl'])) { // Don't set a background image for videos - it will be handled by the video element } elseif (isset($attrs['backgroundUrl'])) { return 'background-image: url('.$attrs['backgroundUrl'].')'; } break; case 'backgroundColor': case 'borderColor': case 'textColor': $type = str_replace('Color', '-color', $key); $type = str_replace('text-', '', $type); if (isset($attrs['border']['width']) && $key === 'borderColor') { break; } return $this->getColorStyle($value, $type); // Any other attributes that need direct styling default: if (JVB_TESTING && !is_admin() && !in_array($key, $this->ignore)) { //TESTING jvbDump($attrs, '[getStyle] '.$key); } // No default inline styles break; } return $styles; } private function getIconSizeStyle(string $value):array { $values = explode(' ', $value); $styles = []; foreach ($values as $v) { switch ($value) { case 'has-small-icon-size': $styles[] = '--w:var(--txt-x-small)'; break; case 'has-large-icon-size': $styles[] = '--w:var(--txt-large)'; break; case 'has-huge-icon-size': $styles[] = '--w:var(--txt-xx-large)'; break; default: if (JVB_TESTING) { jvbDump($value, 'No preset found for size: '.print_r($value, true)); } } } return $styles; } private function getFontFamilyStyle(string $value):string { return match($value) { 'body' => 'font-family: var(--body)', 'heading' => 'font-family: var(--heading)', default => sprintf('font-family: %s', $value) }; } private function getColorStyle(string $value, ?string $type = 'color'):string { if (!in_array($type, ['color', 'background','background-color','border-color'])) { $type = null; } return sprintf( '%s%s', $type ? $type.': ' : '', $this->getColor($value) ); } private function getMinHeightStyle(string $value, array $attrs):string { $out = ''; if (!empty($value)) { if (isset($attrs['minHeightUnit'])) { $out = sprintf( 'min-height: %s%s', $value, $attrs['minHeightUnit'] ); } else { $out = sprintf( 'min-height: %spx', $value ); } } return $out; } private function getFocalPointStyle(array $value):string { jvbDump($value, 'Focal Point'); $x = array_key_exists('x', $value) ? $value['x'] * 100 : 'center'; $y = array_key_exists('y', $value) ? $value['y'] * 100 : 'center'; $y = $x === $y ? '' : ' '.$y; return sprintf( 'background-position:%s%s', $x, $y ); } private function extractStyles(array $value, array $attrs):array { $styles = []; foreach ($value as $k => $v) { switch ($k) { case 'border': $styles = array_merge($styles, $this->getBorderStyle($v, $attrs)); break; case 'color': if (isset($v['background'])) { $styles[] = $this->getColorStyle($v['background'], 'background-color'); } if (isset($v['text'])) { $styles[] = $this->getColorStyle($v['text']); } if (isset($v['gradient'])) { jvbDump($v, 'Gradient'); } break; case 'layout': $styles = array_merge($styles, $this->getLayoutStyle($v, $attrs)); break; case 'typography': $styles = array_merge($styles, $this->getTypographyStyle($v, $attrs)); break; case 'spacing': if (isset($v['blockGap'])) { if (is_array($v['blockGap'])) { $inner = []; foreach ($v['blockGap'] as $gap) { $inner[] = sprintf( 'var(--sp%s)', $this->getPresetSpacing($gap) ); } if (!empty($inner)) { $styles[] = sprintf( '--gap: %s', implode(' ', $inner) ); } } else { $styles[] = '--gap: var(--sp'.$this->getPresetSpacing($v['blockGap']).')'; } } // Don't duplicate margin/padding that's handled by classes // Only add specific CSS values here that wouldn't work well as classes if (isset($v['margin'])) { foreach ($v['margin'] as $direction => $size) { if (!str_contains($size, 'var:preset')) { $styles[] = 'margin-'.$direction.': '.$size; } } } if (isset($v['padding'])) { foreach($v['padding'] as $dir => $size) { if (!str_contains($size, 'var:preset')) { $styles[] = 'padding-'.$dir.': '.$size; } } } break; case 'background': if (array_key_exists('backgroundImage', $v)) { $data = Image::getData($v['backgroundImage']['id']); if (!empty($data) && array_key_exists('tiny', $data)) { $styles[] = sprintf( 'background-image: url(%s)', $data['tiny'] ); } } break; case 'dimensions': foreach ($v as $sk => $sv) { if ($sk === 'minHeight') { $styles[] = sprintf( 'min-height: %s', $sv ); } else { jvbDump('No config set for dimension '.$sk.': '.print_r($sv, true)); } } break; case 'elements': if (!empty($v)) { // Generate a unique class tied to this block instance $uid = 'b-'.substr(md5(serialize($attrs)), 0, 8); $this->extractElementStyles($v, $uid); // We need the uid added as a class — store it for getClassesAndStyles to pick up static::$pendingClass[] = $uid; } break; default: jvbDump('No config set for '.$k.': '.print_r($v, true)); } } return $styles; } private function getBorderStyle(array $border, array $attrs):array { $styles = []; if (isset($border['radius'])) { $styles[] = 'border-radius: '.$border['radius']; } if (isset($border['width']) && isset($attrs['borderColor'])) { $st = $border['style'] ?? 'solid'; $styles[] = sprintf( 'border: %s %s %s', $border['width'], $st, $this->getColor($attrs['borderColor']) ); } elseif (isset($border['width'])) { $styles[] = 'border-width: '.$border['width']; } elseif (isset($border['style'])) { $styles[] = 'border-style: '.$border['style']; } return $styles; } private function getLayoutStyle(array $layout, array $attrs):array { $styles = []; // jvbDump($layout); foreach ($layout as $l => $option) { switch ($l) { case 'selfStretch': if ($option === 'fixed' && isset($layout['selfStretchValue'])) { $styles[] = 'width: '.$layout['selfStretchValue']; } elseif ($option === 'fill') { $styles[] = 'flex:1'; } break; default: $ignore = [ 'selfStretchValue', 'flexSize', ]; if (JVB_TESTING && !in_array($l, $ignore)) { jvbDump($l, 'No layout style set for: '); } // case 'type': // if ($option === 'grid' && $value['layout']['columnCount'] !== 3) { // $styles[] = sprintf( // 'grid-template-columns: repeat(1fr, %s)', // $value['layout']['columnCount'] // ); // } // break; } } return $styles; } private function getTypographyStyle(array $typography, array $attrs):array { $styles = []; if (isset($typography['fontSize'])) { $styles[] = 'font-size: '.$typography['fontSize']; } if (isset($typography['fontWeight'])) { $styles[] = 'font-weight: '.$typography['fontWeight']; } if (isset($typography['textDecoration'])) { $styles[] = 'text-decoration: '.$typography['textDecoration']; } if (isset($typography['textTransform'])) { $styles[] = 'text-transform: '.$typography['textTransform']; } if (isset($typography['letterSpacing'])) { $styles[] = 'letter-spacing: '.$typography['letterSpacing']; } if (isset($typography['lineHeight'])) { $styles[] = 'line-height: '.$typography['lineHeight']; } return $styles; } private function extractElementStyles(array $elements, string $uid):void { foreach ($elements as $element => $states) { $selector = match($element) { 'link' => "a", 'heading' => "h1,h2,h3,h4,h5,h6", 'button' => "button,.button", default => $element, }; foreach ($states as $state => $rules) { $css = []; $fullSelector = str_starts_with($state, ':') ? ".{$uid} {$selector}{$state}" : ".{$uid} {$selector}"; if (isset($rules['color']['text'])) { $css[] = 'color: '.$this->getColor($rules['color']['text']); } if (isset($rules['color']['background'])) { $css[] = 'background-color: '.$this->getColor($rules['color']['background']); } if (!empty($css)) { static::$pendingStyles[] = $fullSelector.' { '.implode('; ', $css).' }'; } } } } public function maybeOutputCustomStyles(): string { if (empty(static::$pendingStyles)) return ''; $out = ''; static::$pendingStyles = []; return $out; } private function getDimRatioStyle(int $value, array $attrs):string { //TODO: This likely isn't working correctly jvbDump($value, 'dimRatio'); jvbDump($attrs, 'dimRatio attrs'); $s = ''; if (!array_key_exists('overlayColor', $attrs)) { $s = 'background-color: rgba(var(--base-rgb), '; if ($value <= 14) { $s .= 'var(--op-1));'; } elseif ($value <= 28) { $s .= 'var(--op-2));'; } elseif ($value <= 42) { $s .= 'var(--op-3));'; } elseif ($value <= 56) { $s .= 'var(--op-45));'; } elseif ($value <= 70) { $s .= 'var(--op-4));'; } elseif ($value <= 84) { $s .= 'var(--op-5));'; } else { $s .= 'var(--op-6));'; } } return $s; } protected function getDataset(array $attrs):array { $dataset = []; if (array_key_exists('style', $attrs)) { if (array_key_exists('background', $attrs['style'])){ if (array_key_exists('backgroundImage', $attrs['style']['background'])) { $id = $attrs['style']['background']['backgroundImage']['id']??false; if ($id) { $data = Image::getData($id); $dataset['bg-small'] = $data['small']; $dataset['bg-med'] = $data['medium']; $dataset['bg-large'] = $data['large']; } } } } return $dataset; } 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 jvbFormatImage($ID, $start, $replace); } protected function checkAttrs(string $test, array $attrs):bool { return array_key_exists($test, $attrs) && $attrs[$test]===true; } protected function getColor(string $value):string { $defaults = apply_filters('jvbColours', ['base', 'contrast', 'action', 'secondary']); foreach ($defaults as $default) { if (str_starts_with($value, $default)) { return 'var(--'.$value.')'; } } return $value; } protected function counter(string $key):void { if (!array_key_exists($key, static::$counters)) { static::$counters[$key] = 1; } else { static::$counters[$key]++; } } }