From 747d741293e064a979d7bf6c143ef969ea6d7629 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 24 May 2026 20:49:44 +0000
Subject: [PATCH] =GMBReview block minor tweaks. Refactored ReferralManager.php and ReferralRoutes.php to utilize the manager for all logic, and CustomTable for table interactions.

---
 inc/blocks/CustomBlocks.php |  809 ++++++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 569 insertions(+), 240 deletions(-)

diff --git a/inc/blocks/CustomBlocks.php b/inc/blocks/CustomBlocks.php
index 0b5830e..0817591 100644
--- a/inc/blocks/CustomBlocks.php
+++ b/inc/blocks/CustomBlocks.php
@@ -6,7 +6,9 @@
 use JVBase\managers\Cache;
 use JVBase\managers\LoginManager;
 use JVBase\managers\SEO\BreadcrumbManager;
+use JVBase\managers\SEO\render\Thing\CreativeWork\MediaObject\VideoObject;
 use JVBase\utility\Image;
+use JVBase\utility\Video;
 use WP_Block;
 use WP_Query;
 
@@ -22,11 +24,13 @@
 	protected static ?int $currentQueryId = null;
 	protected static array $counters = [];
 	protected static ?WP_Query $originalQuery = null;
-	protected array $ignore = ['align','alt','area','backgroundColor','borderColor','buttonText','buttonPosition','buttonUseIcon','categories','className','columns','contentPosition','customOverlayColor','dimRatio','displayAsDropdown','displayAuthor','displayFeaturedImage','displayPostContent','displayPostContentRadio','displayPostDate','excerptLength','featuredImageAlign','fontSize','gradient','height','iconColor','iconColorValue','iconColorValue','iconBackgroundColor','iconBackgroundColorValue','id','imageFill','isDark','isLink','isSearchFieldHidden','isStackedOnMobile','isUserOverlayColor','kind','label','largestFontSize','layout','level','mediaId','mediaLink','mediaSizeSlug','mediaType','metadata','minHeight','minHeightUnit','opacity','opensInNewTab','order','orderBy','ordered','overlayMenu','placeholder','postLayout','postsToShow','query', 'queryId','ref','rel','shouldSyncIcon','showEmpty','showHierarchy','showLabel','showLabels','showOnlyTopLevel','showPostCounts','showTagCounts','size','sizeSlug','slug','smallestFontSize','tagName','taxonomy','term','textAlign','textColor','theme','title','type','url','useFeaturedImage','width','widthUnit',];
+	protected array $ignore = ['align','alt','area','aspectRatio','backgroundColor','borderColor','buttonText','buttonPosition','buttonUseIcon','categories','className','columns','contentPosition','customOverlayColor','dimRatio','displayAsDropdown','displayAuthor','displayFeaturedImage','displayPostContent','displayPostContentRadio','displayPostDate','excerptLength','featuredImageAlign','fontSize','gradient','hasFixedLayout','hasParallax','height','iconColor','iconColorValue','iconColorValue','iconBackgroundColor','iconBackgroundColorValue','id','imageFill','isDark','isLink','isObjectPosition','isRepeated','isSearchFieldHidden','isStackedOnMobile','isUserOverlayColor','kind','label','largestFontSize','layout','lightbox','linkDestination','linkTo','level','mediaId','mediaLink','mediaPosition','mediaSizeSlug','mediaType','mediaWidth','metadata','minHeight','minHeightUnit','opacity','opensInNewTab','order','orderBy','ordered','overlayColor','overlayMenu','placeholder','postLayout','postsToShow','query', 'queryId','ref','rel','scale','shouldSyncIcon','showContent','showEmpty','showHierarchy','showLabel','showLabels','showOnlyTopLevel','showPostCounts','showTagCounts','size','sizeSlug','slug','smallestFontSize','tagName','taxonomy','term','textAlign','textColor','theme','title','type','url','useFeaturedImage','verticalAlignment','width','widthUnit',];
 
 	//For custom style output for nested links, etc
 	protected static array $pendingStyles = [];
 	protected static array $pendingClass = [];
+	protected static bool $renderGallery = false;
+
     public function __construct()
     {
         $this->cache = Cache::for('blocks', WEEK_IN_SECONDS);
@@ -98,7 +102,7 @@
 		} else if (method_exists($this, $method)) {
 			$content = $this->$method($block, $content, $parent);
 			return $isPrerender ? $this->maybeOutputCustomStyles().$content : $content;
-		} elseif (!empty($blockName) && JVB_TESTING) {
+		} elseif (JVB_TESTING && !empty($blockName)) {
 			if (!in_array($block['blockName'], $this->getIgnore($isPrerender))) {
 				jvbDump('No method found for '.print_r($block['blockName'], true));
 			}
@@ -109,7 +113,9 @@
 		{
 			//Ignore for both
 			$base = [
-				'core/null'
+				'core/null',
+				'core/list-item',
+				'jvb/drawer-menu'
 			];
 			if ($isPrerender) {
 				$base = array_merge($base, [
@@ -203,8 +209,11 @@
     {
 //		jvbDump($block, 'buttons');
 //		jvbDump($parent, 'Parent');
-        return '<ul'.$this->getClassesAndStyles($block['attrs']??[], ['buttons','row']).'>'.
-               $this->innerBlocks($block).'</ul>';
+        return sprintf(
+			'<ul%s>%s</ul>',
+			$this->getClassesAndStyles($block['attrs']??[], ['buttons','row']),
+               $this->innerBlocks($block)
+		);
     }
 
     public function prerender_core_column(array $block, ?string $content, ?WP_Block $parent):?string
@@ -215,14 +224,16 @@
                    array_key_exists('width', $block['attrs'])) ?
             ['flex-basis:'.$block['attrs']['width']]
             : [];
-        return '<div'.
-               $this->getClassesAndStyles($block['attrs']??[], ['col'], $styles).'>'.
-               $this->innerBlocks($block).'</div>';
+        return sprintf(
+			'<div%s>%s</div>',
+               $this->getClassesAndStyles($block['attrs']??[], ['col'], $styles),
+               $this->innerBlocks($block)
+		);
     }
 
     public function prerender_core_columns(array $block, ?string $content, ?WP_Block $parent):?string
     {
-		jvbDump($block, 'columns');
+//		jvbDump($block, 'columns');
 		$attrs = $block['attrs']??[];
 		$tagName = array_key_exists('tagName', $attrs) ? $attrs['tagName'] : 'section';
 
@@ -295,8 +306,10 @@
 
 //		jvbDump($block, 'spsacer');
 //		jvbDump($parent, 'Parent');
-        return '<div'.$this->getClassesAndStyles($block['attrs']??[], ['spacer'], ['height:2rem']).
-               ' aria-hidden="true"></div>';
+        return sprintf(
+			'<div%s aria-hidden="true"></div>',
+			$this->getClassesAndStyles($block['attrs']??[], ['spacer'], ['height:2rem'])
+		);
     }
     //core_table_of_contents
     //core_text_columns
@@ -309,22 +322,31 @@
      * Media Blocks
      */
     //core_audio
+	public function prerender_core_audio(array $block, ?string $content, ?WP_Block $parent):?string
+	{
+//		jvbDump($block,'audio');
+		$attrs = $block['attrs']??[];
+		$inside = $this->inside($block);
+		return sprintf('<figure%s>%s</figure>',
+			$this->getClassesAndStyles($attrs),
+			$inside
+		);
+	}
     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']);
+			$attrs['isObjectPosition'] = true;
 		}
 
+
         // Check for background type
         $backgroundType = $attrs['backgroundType'] ?? 'image';
         $background = '';
@@ -337,33 +359,93 @@
 			$ID = (int)$attrs['id'];
 		}
 
-        if ($backgroundType === 'image' && $ID) {
+
+		$doImage = true;
+		if ($this->checkAttrs('hasParallax', $attrs) || $this->checkAttrs('isRepeated', $attrs)) {
+			$doImage = false;
+			$attrs['style']['background']['backgroundImage']['id'] = $ID;
+		}
+
+        if ($doImage && $backgroundType === 'image' && $ID) {
 			$background .= str_replace('<img', '<img style="'.$position.'"', $this->image($ID));
         } elseif ($backgroundType === 'video' && isset($attrs['url'])) {
             $background .= '<video style="'.$position.'"autoplay muted loop playsinline src="' . esc_url($attrs['url']) . '"></video>';
         }
 
+		$overlay = '';
+		if (!isset($attrs['style']['color']['duotone']) && (array_key_exists('overlayColor', $attrs) || array_key_exists('dimRatio', $attrs))) {
+			$tmp = [];
+			if (array_key_exists('overlayColor', $attrs)) {
+				$tmp['overlayColor'] = $attrs['overlayColor'];
+			}
+			if (array_key_exists('dimRatio', $attrs)) {
+				$tmp['dimRatio'] = $attrs['dimRatio'];
+			}
+			unset($attrs['overlayColor']);
+			unset($attrs['dimRatio']);
+			$overlay = sprintf(
+				'<div class="overlay"%s></div>',
+				$this->buildStylesString($tmp)
+			);
+		}
+
 		// Build classes and styles
 		unset($attrs['url']);
 		$classes = $this->getClassesAndStyles($attrs, ['cover row']);
 
 
-		return '<section' . $classes . '>' .
-               $background .
-               '<div class="content">' .
-               $innerContent .
-               '</div></section>';
+
+		return sprintf('<section%s>%s%s<div class="content">%s</div></section>',
+			$classes,
+			$overlay,
+			$background,
+			$innerContent
+		);
     }
 
     //core_file
+	public function prerender_core_file(array $block, ?string $content, ?WP_Block $parent):?string
+	{
+		$attrs = $block['attrs']??[];
+
+		$showButton = !array_key_exists('showDownloadButton', $attrs);
+		preg_match('/>([^<]*)<\/a>/', $block['innerHTML'], $label);
+		$label = $label[1]??'';
+		$button = $showButton ?
+			sprintf(
+				'&emsp;<a class="btn chip" href="%s">%s<span>Download</span></a>',
+				$attrs['href'],
+				jvbIcon('cloud-arrow-down')
+			) :
+			'';
+
+		$aOpen = $showButton ? '' :
+			sprintf(
+				'<a href="%s">',
+				$attrs['href']
+			);
+		$aClose = $showButton ? '' : '</a>';
+		return sprintf(
+			'<p%s>%s%s%s%s</p>',
+			$this->getClassesAndStyles($attrs, ['file']),
+			$aOpen,
+			$showButton ? $label : 'Download: '.$label,
+			$aClose,
+			$button
+		);
+	}
 
     public function prerender_core_gallery(array $block, ?string $content, ?WP_Block $parent):?string
     {
 //		jvbDump($block, 'gallery');
+		$attrs = $block['attrs']??[];
 //		jvbDump($parent, 'Parent');
-        return '<ul'.$this->getClassesAndStyles($block['attrs']??[], ['gallery']).'>'.
-               $this->innerBlocks($block,'<li>', '</li>').
-               '</ul>';
+		static::$renderGallery = true;
+        return sprintf(
+		'<ul%s>%s</ul>',
+			$this->getClassesAndStyles($attrs, ['gallery']),
+			$this->innerBlocks($block,'<li>', '</li>')
+	   );
     }
 
     public function prerender_core_image(array $block, ?string $content, ?WP_Block $parent):?string
@@ -375,6 +457,10 @@
             return '';
         }
 
+		$attrs = $block['attrs']??[];
+
+		static::$renderGallery = true;
+
         $title = (get_the_title($ID) !== '') ? '<b>'.get_the_title($ID).'</b>' : '';
         $caption = (wp_get_attachment_caption($ID)) ?
             '<figcaption>' .
@@ -382,11 +468,23 @@
                 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, 'tiny', $size) .
-               $caption.'</figure>';
+		$size = $attrs['sizeSlug'] ?? 'large';
+		$img = $this->imageLink(true, $ID, 'tiny', $size);
+
+		$aspectRatio = $attrs['aspectRatio']??false;
+		if ($aspectRatio) {
+			$img = str_replace('<img', sprintf(
+				'<img style="aspect-ratio:%s;"',
+				$aspectRatio
+			), $img);
+		}
+
+        return sprintf(
+			'<figure%s>%s%s</figure>',
+               $this->getClassesAndStyles($block['attrs']??[]),
+               $img,
+               $caption,
+		);
     }
 
     public function prerender_core_media_text(array $block, ?string $content, ?WP_Block $parent):?string
@@ -408,13 +506,35 @@
 			$classes[] = 'stack-small';
 		}
 
+		$figClasses = [];
+		if (isset($attrs['mediaWidth'])) {
+			$figClasses[] = 'width:'.$attrs['mediaWidth'].'%';
+		}
+		if (isset($attrs['imageFill']) && $attrs['imageFill'] === true) {
+			$figClasses[] = 'object-fit: cover';
+		}
+		if (array_key_exists('focalPoint', $attrs)) {
+			$attrs['isObjectPosition'] = true;
+			$style = $this->getFocalPointStyle($attrs['focalPoint'], $attrs);
+			$style .= ';object-fit:none;';
+			$imgLink = str_replace('<img', sprintf(
+				'<img style="%s"',
+				$style
+			), $imgLink);
+			unset($attrs['focalPoint']);
+		}
+
+
+		$figClasses = empty($figClasses) ? '' : ' style="'.implode(';',$figClasses).'"';
+
+
 		$inside = array_key_exists('mediaPosition', $attrs) && $attrs['mediaPosition'] === 'right'
 			? sprintf(
-				'<div>%s</div><figure>%s</figure>',
-				$inner, $imgLink
+				'<div>%s</div><figure%s>%s</figure>',
+				$inner,$figClasses, $imgLink
 			) : sprintf(
-				'<figure>%s</figure><div>%s</div>',
-				$imgLink, $inner
+				'<figure%s>%s</figure><div>%s</div>',
+				$figClasses,$imgLink, $inner
 			);
 
         return sprintf(
@@ -427,27 +547,31 @@
 
 	public function prerender_core_video(array $block, ?string $content, ?WP_Block $parent):?string
 	{
-		jvbDump($block, 'video');
+//		jvbDump($block, 'video');
 //		jvbDump($parent, 'Parent');
-		$ID = $this->imageID('', $block);
-		if (!$ID) {
-			return '';
-		}
 
-		jvbDump($ID);
+		$attrs = $block['attrs']??[];
+//
+//		$ID = $attrs['id']??false;
+//		if (!$ID) {
+//			return '';
+//		}
+//		$caption = wp_get_attachment_caption($ID);
+//		$title = get_the_title($ID);
+//
+//		$figCaption = sprintf(
+//			'<figcaption><b>%s</b>%s</figcaption>',
+//			$title,
+//			$caption
+//		);
+//
+//		$video = Video::get($ID);
 
-		$title = (get_the_title($ID) !== '') ? '<b>'.get_the_title($ID).'</b>' : '';
-		$caption = (wp_get_attachment_caption($ID)) ?
-			'<figcaption>' .
-			$title .
-			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, 'tiny', $size) .
-			$caption.'</figure>';
+		$inside = $this->inside($block);
+		return sprintf('<figure%s>%s</figure>',
+			$this->getClassesAndStyles($attrs),
+			$inside
+		);
 	}
 
     /**
@@ -458,18 +582,56 @@
     /**
      * Text Blocks
     */
-    //prerender_core_code
-    //prerender_core_details
+    public function prerender_core_code(array $block, ?string $content, ?WP_Block $parent):?string
+	{
+//		jvbDump($block, 'code');
+		$attrs = $block['attrs']??[];
+		$content = $this->inside($block);
+
+		return str_replace('<code', sprintf(
+			'<code%s',
+			$this->getClassesAndStyles($attrs),
+		), $content);
+	}
+    public function prerender_core_details(array $block, ?string $content, ?WP_Block $parent):?string
+	{
+//		jvbDump($block, 'details');
+		$attrs = $block['attrs']??[];
+		$isOpen = $this->checkAttrs('showContent', $attrs);
+		$summary = $this->extractElement($block['innerHTML'], 'summary');
+		$inside = $this->innerBlocks($block);
+
+		return sprintf(
+			'<details%s%s><summary>%s</summary>%s</details>',
+			$this->getClassesAndStyles($attrs),
+			$isOpen ? ' open' : '',
+			$summary,
+			$inside
+		);
+	}
     //prerender_core_footnotes
+//	public function prerender_core_footnotes(array $block, ?string $content, ?WP_Block $parent):?string
+//	{
+//		jvbDump($block, 'footnotes');
+//
+//		return null;
+//	}
     //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';
+//		jvbDump($block, 'heading');
+		$attrs = $block['attrs']??[];
+        $level = $attrs['level'] ?? '2';
 		$content = $this->inside($block);
         $id = sanitize_title(wp_strip_all_tags($this->stripTagContents('small', $content)));
-        return '<h'.$level.' id="'.$id.'"'.$this->getClassesAndStyles($block['attrs']??[]).'>'.
-               $content.
-               '</h'.$level.'>';
+        return sprintf(
+		'<h%s id="%s"%s>%s</h%s>',
+		   $level,
+		   $id,
+			$this->getClassesAndStyles($attrs),
+		   $content,
+		   $level,
+		);
     }
 
 	public function prerender_core_list(array $block, ?string $content, ?WP_Block $parent):?string
@@ -477,8 +639,13 @@
 //		jvbDump($block, 'list');
 //		jvbDump($parent, 'Parent');
 		$tag = (array_key_exists('ordered', $block['attrs']??[])) ? 'ol' : 'ul';
-		$output = '<'.$tag.$this->getClassesAndStyles($block['attrs']??[]).'>'.$this->innerBlocks($block).'</'.$tag.'>';
-		return $output;
+		return sprintf(
+			'<%s%s>%s</%s>',
+			$tag,
+			$this->getClassesAndStyles($block['attrs']??[]),
+			$this->innerBlocks($block),
+			$tag
+		);
 	}
 
 //	public function prerender_core_list_item(array $block):string
@@ -516,10 +683,13 @@
 			$content = $this->stripTagContents('cite', $content);
 		}
 
-		return '<blockquote'.$this->getClassesAndStyles($block['attrs']??[]).'>
-        <div class="content">'.$content.'</div>'.
-			$citeHtml.
-			'</blockquote>';
+		return sprintf(
+			'<blockquote%s>%s%s%s</blockquote>',
+			$this->getClassesAndStyles($block['attrs']??[]),
+			jvbIcon('quotes',['style' => 'fill']),
+        	$content,
+			$citeHtml
+		);
 	}
 	public function prerender_core_pullquote(array $block, ?string $content, ?WP_Block $parent):?string
 	{
@@ -540,13 +710,58 @@
 		}
 		$content = jvb_filter_content( $content);
 
-		return '<blockquote'.$this->getClassesAndStyles($block['attrs']??[], ['pull']).'>'.
-        	$content.
-			$citeHtml.
-			'</blockquote>';
+		return sprintf(
+			'<blockquote%s>%s%s</blockquote>',
+			$this->getClassesAndStyles($block['attrs']??[], ['pull']),
+        	$content,
+			$citeHtml
+		);
 	}
-    //prerender_core_table
-    //prerender_core_verse
+    public function prerender_core_table(array $block, ?string $content, ?WP_Block $parent):?string
+	{
+//		jvbDump($block, 'table');
+		$attrs = $block['attrs']??[];
+		$figAttrs = [
+			'align' => $attrs['align']??''
+		];
+		unset($attrs['align']);
+
+		$inside = $this->inside($block);					// inside the figure
+		$parts = explode('<figcaption', $inside);	// inside the table
+		$table = $parts[0];
+		$table = str_replace(strtok($table, '>'),sprintf(
+			'<table%s',
+			$this->getClassesAndStyles($attrs)
+		), $table);
+		$caption = str_replace(strtok($parts[1], '>'), '<figcaption', $parts[1]);
+
+		return sprintf(
+			'<figure%s>%s%s</figure>',
+			$this->buildClassesString($figAttrs),
+			$table,
+			$caption
+		);
+	}
+    public function prerender_core_preformatted(array $block, ?string $content, ?WP_Block $parent):?string
+	{
+//		jvbDump($block, 'verse');
+		$attrs = $block['attrs']??[];
+		return sprintf(
+			'<pre%s>%s</pre>',
+			$this->getClassesAndStyles($attrs),
+			$this->inside($block)
+		);
+	}
+    public function prerender_core_verse(array $block, ?string $content, ?WP_Block $parent):?string
+	{
+//		jvbDump($block, 'verse');
+		$attrs = $block['attrs']??[];
+		return sprintf(
+			'<pre%s>%s</pre>',
+			$this->getClassesAndStyles($attrs),
+			$this->inside($block)
+		);
+	}
 
     /**
      * Theme Blocks
@@ -754,7 +969,12 @@
         $linkOpen = $this->buildNavigationLink($attrs, $aria);
 
 
-        return '<li'.$classes.'>'.$linkOpen.$block['attrs']['label'].'</a></li>';
+        return sprintf(
+			'<li%s>%s%s</a></li>',
+			$classes,
+			$linkOpen,
+			$block['attrs']['label']
+		);
     }
 
     public function prerender_core_navigation_submenu(array $block, ?string $content, ?WP_Block $parent):?string
@@ -829,7 +1049,14 @@
 					break;
             }
         }
-        return '<a href="'.$url.'"'.$aria.$rel.$target.$title.'>';
+        return sprintf(
+			'<a href="%s"%s%s%s%s>',
+			$url,
+			$aria,
+			$rel,
+			$target,
+			$title
+		);
     }
 
     /**
@@ -952,6 +1179,10 @@
             $result = $this->innerBlocks($block);
         }
 
+		if(static::$renderGallery) {
+			add_action('wp_footer', 'jvbRenderGallery');
+		}
+
 		return apply_filters('jvb_post_content_output', $result, $block);
     }
     //core_post_date
@@ -1045,20 +1276,23 @@
 		global $post;
 		$attrs = $block['attrs']??[];
 		$ID = get_post_thumbnail_id($post->ID);
-		$aspectRatio = $aOpen = $aClose = '';
+		$aOpen = $aClose = '';
 		if(!is_single($post->ID) && $this->checkAttrs('isLink', $attrs)) {
 			$aOpen = '<a href="'.get_the_permalink($post->ID).'">';
 			$aClose = '</a>';
 		}
-		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('<img', '<img style="aspect-ratio:'.$aspectRatio.';"', $img);
+		}
+		$aspectRatio = $attrs['aspectRatio']??false;
+		if ($aspectRatio) {
+			$img = str_replace('<img', sprintf(
+				'<img style="aspect-ratio:%s;"',
+				$aspectRatio
+			), $img);
 		}
 
 
@@ -1127,7 +1361,10 @@
 		$inner = '';
 
 		if (!static::$currentLoop) {
-			jvbDump('No loop stored');
+
+			if (JVB_TESTING) {
+				jvbDump('No loop stored');
+			}
 			return $content;
 		}
 		if (static::$currentLoop->have_posts()) {
@@ -1567,7 +1804,7 @@
 
 	public function prerender_core_query_title(array $block, ?string $content, ?WP_Block $parent):?string
 	{
-		jvbDump($block, 'query title');
+//		jvbDump($block, 'query title');
 		$attr = $block['attrs'];
 		$name = '';
 		$showPrefix = $attr['showPrefix']??false;
@@ -1720,7 +1957,7 @@
     //core_archives
 	public function render_core_archives(array $block, string $content):string
 	{
-		jvbDump($block, 'archives');
+//		jvbDump($block, 'archives');
 		$attrs = $block['attrs']??[];
 		$isDropdown = $this->checkAttrs('displayAsDropdown', $attrs);
 
@@ -1982,7 +2219,6 @@
 		}
 		$inside = [];
 		foreach($pages->posts as $page) {
-			jvbDump($page);
 			$inside[] = sprintf(
 				'<li><a href="%s">%s</a>',
 				get_the_permalink($page->ID),
@@ -2210,7 +2446,7 @@
 	 * @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
+	public function extractElement(string $html, string $tag): string
 	{
 		if (empty($html)) {
 			return '';
@@ -2344,51 +2580,64 @@
         return $out;
     }
 
-    protected function getClassesAndStyles(
+    public 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 = trim($this->buildClassesString($attrs, $classes) . $this->buildStylesString($attrs,$styles) . $this->buildDataset($attrs));
         return ($return=='')? '' : ' '.$return;
     }
+		protected function buildStylesString(array $attrs, array $custom = []):string
+		{
+			$attr_styles = $this->getInlineStyles($attrs);
+			$styles = array_merge($attr_styles, $custom);
+
+			$styles = array_map(function ($property, $value) {
+				return sprintf('%s:%s', $property, $value);
+			}, array_keys($styles), $styles);
+
+			return !empty($styles) ? ' style="' . implode(';', $styles) . '"' : '';
+		}
+
+		protected function buildClassesString(array $attrs, array $custom = []):string
+		{
+			$attr_classes = $this->getClasses($attrs);
+			if(array_key_exists('slug', $attrs) && $attrs['slug'] === 'footer') {
+				$attr_classes[] = 'col';
+			}
+
+			// Merge with passed classes and styles
+			$classes = array_merge($attr_classes, $custom);
+
+			if (!empty(static::$pendingClass)) {
+				$classes = array_merge($classes, static::$pendingClass);
+				static::$pendingClass = [];
+			}
+			$classes = array_unique($classes);
+
+			// Build attribute strings
+			return !empty($classes) ? ' class="' . implode(' ', $classes) . '"' : '';
+		}
+
+		protected function buildDataset(array $attrs):string
+		{
+			$data = $this->getDataset($attrs);
+			$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 $data_string;
+		}
     /**
      * @param string $spacing
      *
@@ -2434,6 +2683,7 @@
                 return match ($value) {
                     'is-style-floating' => 'always mobile fixed',
 					'is-style-fixed' => 'fixed bottom',
+					'is-style-default' => '',
                     default => str_replace('is-style-', '', $value),
                 };
 			case 'contentPosition':
@@ -2465,7 +2715,7 @@
             case 'width':
 				return $this->getWidth($value);
             case 'dimRatio':
-                return $this->getDimRatio($value);
+                return $this->getDimRatio($value, $attrs);
 			case 'overlayColor':
 				return $value;
 				break;
@@ -2477,7 +2727,7 @@
 
             //Media
             case 'hasParallax':
-                return $value === true ? 'bg-parallax' : '';
+                return $value === true ? 'bg-fixed' : '';
             case 'isRepeated':
                 return $value === true ? 'bg-repeat' : '';
 
@@ -2615,7 +2865,17 @@
 
 		private function getWidth(string $value):string
 		{
-			$value = (int)str_replace('%', '', $value);
+
+			$value = str_replace('%', '', $value);
+
+			if (str_contains($value, 'px') ||
+				str_contains($value, 'em') ||
+				str_contains($value, 'rem') ||
+				str_contains($value, 'vw') ||
+				str_contains($value, 'vh')) {
+				return '';
+			}
+
 			return sprintf(
 				'width-%d',
 				match (true) {
@@ -2628,8 +2888,11 @@
 				}
 			);
 		}
-		private function getDimRatio(string $value):string
+		private function getDimRatio(string $value, array $attrs):string
 		{
+			if (array_key_exists('overlayColor', $attrs)) {
+				return '';
+			}
 			if (is_numeric($value)) {
 				return sprintf(
 					'op-%d',
@@ -2658,6 +2921,8 @@
 				if (array_key_exists('duotone', $value['color'])) {
 					$preset = explode('|', $value['color']['duotone']);
 					$preset = $preset[array_key_last($preset)];
+					$preset = $this->getColor($preset, false);
+
 					if (str_contains($preset, '-')) {
 						$preset = explode('-', $preset);
 					} else {
@@ -2771,10 +3036,9 @@
         $styles = [];
         foreach ($attrs as $key => $value) {
             $style = $this->getStyle($key, $value, $attrs);
-			if (is_string($style)) {
-				$style = [$style];
-			}
+
             $styles = array_merge($styles, $style);
+			$styles = array_unique($styles);
         }
         return $styles;
     }
@@ -2800,13 +3064,13 @@
             // Background URL (for cover, media blocks)
             case 'url':
                 if (!empty($value) && str_starts_with($value, 'http')) {
-                    return 'background-image: url('.$value.')';
+                    return ['background-image' => 'url('.$value.')'];
                 }
                 break;
 
             // Focal point for background images
             case 'focalPoint':
-				return $this->getFocalPointStyle($value);
+				return $this->getFocalPointStyle($value, $attrs);
 
             // Complex style object
             case 'style':
@@ -2820,16 +3084,26 @@
                 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'].')';
+                    return ['background-image' => 'url('.$attrs['backgroundUrl'].')'];
                 }
                 break;
 
+			case 'width':
+				if (str_contains($value, 'px') ||
+					str_contains($value, 'em') ||
+					str_contains($value, 'rem') ||
+					str_contains($value, 'vw') ||
+					str_contains($value, 'vh')) {
+					return ['width' => $value];
+				}
+				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') {
+				if ($key === 'borderColor' && isset($attrs['border']['width'])) {
 					break;
 				}
 				return $this->getColorStyle($value, $type);
@@ -2853,13 +3127,13 @@
 			foreach ($values as $v) {
 				switch ($value) {
 					case 'has-small-icon-size':
-						$styles[] = '--w:var(--txt-x-small)';
+						$styles['--w'] = 'var(--txt-x-small)';
 						break;
 					case 'has-large-icon-size':
-						$styles[] = '--w:var(--txt-large)';
+						$styles['--w'] = 'var(--txt-large)';
 						break;
 					case 'has-huge-icon-size':
-						$styles[] = '--w:var(--txt-xx-large)';
+						$styles['--w'] = 'var(--txt-xx-large)';
 						break;
 					default:
 						if (JVB_TESTING) {
@@ -2869,38 +3143,36 @@
 			}
 			return $styles;
 		}
-		private function getFontFamilyStyle(string $value):string
+		private function getFontFamilyStyle(string $value):array
 		{
 			return match($value) {
-				'body'		=> 'font-family: var(--body)',
-				'heading'	=> 'font-family: var(--heading)',
-				default		=> sprintf('font-family: %s', $value)
+				'body'		=> ['font-family' =>  'var(--body)'],
+				'heading'	=> ['font-family' =>  'var(--heading)'],
+				default		=> ['font-family' => $value]
 			};
 		}
-		private function getColorStyle(string $value, ?string $type = 'color'):string
+		private function getColorStyle(string $value, ?string $type = 'color'):array
 		{
 			if (!in_array($type, ['color', 'background','background-color','border-color'])) {
 				$type = null;
 			}
-			return sprintf(
-				'%s%s',
-				$type ? $type.': ' : '',
-				$this->getColor($value)
-			);
+			if (!$type) {
+				return [];
+			}
+			return [
+				$type => $this->getColor($value)
+			];
 		}
-		private function getMinHeightStyle(string $value, array $attrs):string
+		private function getMinHeightStyle(string $value, array $attrs):array
 		{
-			$out = '';
+			$out = [];
 			if (!empty($value)) {
 				if (isset($attrs['minHeightUnit'])) {
-					$out = sprintf(
-						'min-height: %s%s',
-						$value,
-						$attrs['minHeightUnit']
-					);
+					$out['min-height'] = sprintf('%s%s', $value, $attrs['minHeightUnit']);
+
 				} else {
-					$out = sprintf(
-						'min-height: %spx',
+					$out['min-height'] = sprintf(
+						'%spx',
 						$value
 					);
 				}
@@ -2908,18 +3180,20 @@
 			return $out;
 		}
 
-		private function getFocalPointStyle(array $value):string
+		private function getFocalPointStyle(array $value, array $attrs):array
 		{
-			jvbDump($value, 'Focal Point');
-			$x = array_key_exists('x', $value) ? $value['x'] * 100 : 'center';
-			$y = array_key_exists('y', $value) ? $value['y'] * 100 : 'center';
+			$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',
+
+			$key = array_key_exists('isObjectPosition', $attrs) ? 'object-position' : 'background-position';
+			return [
+				$key => sprintf(
+				'%s%s',
 				$x,
 				$y
-			);
+			)];
 		}
 
 		private function extractStyles(array $value, array $attrs):array
@@ -2933,13 +3207,16 @@
 
 					case 'color':
 						if (isset($v['background'])) {
-							$styles[] = $this->getColorStyle($v['background'], 'background-color');
+							$styles['background-color'] = $this->getColor($v['background']);
 						}
 						if (isset($v['text'])) {
-							$styles[] = $this->getColorStyle($v['text']);
+							$styles['color'] = $this->getColor($v['text']);
 						}
 						if (isset($v['gradient'])) {
-							jvbDump($v, 'Gradient');
+
+							if (JVB_TESTING) {
+								jvbDump($v, 'Gradient');
+							}
 						}
 						break;
 
@@ -2962,13 +3239,13 @@
 									);
 								}
 								if (!empty($inner)) {
-									$styles[] = sprintf(
-										'--gap: %s',
+									$styles['--gap'] = sprintf(
+										'%s',
 										implode(' ', $inner)
 									);
 								}
 							} else {
-								$styles[] = '--gap: var(--sp'.$this->getPresetSpacing($v['blockGap']).')';
+								$styles['--gap'] = 'var(--sp'.$this->getPresetSpacing($v['blockGap']).')';
 							}
 						}
 
@@ -2977,7 +3254,7 @@
 						if (isset($v['margin'])) {
 							foreach ($v['margin'] as $direction => $size) {
 								if (!str_contains($size, 'var:preset')) {
-									$styles[] = 'margin-'.$direction.': '.$size;
+									$styles['margin-'.$direction] = $size;
 								}
 							}
 						}
@@ -2985,7 +3262,7 @@
 						if (isset($v['padding'])) {
 							foreach($v['padding'] as $dir => $size) {
 								if (!str_contains($size, 'var:preset')) {
-									$styles[] = 'padding-'.$dir.': '.$size;
+									$styles['padding-'.$dir] = $size;
 								}
 							}
 						}
@@ -2995,8 +3272,8 @@
 						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)',
+								$styles['background-image'] = sprintf(
+									'url(%s)',
 									$data['tiny']
 								);
 							}
@@ -3007,12 +3284,11 @@
 					case 'dimensions':
 						foreach ($v as $sk => $sv) {
 							if ($sk === 'minHeight') {
-								$styles[] = sprintf(
-									'min-height: %s',
-									$sv
-								);
+								$styles['min-height'] = $sv;
 							} else {
-								jvbDump('No config set for dimension '.$sk.': '.print_r($sv, true));
+								if (JVB_TESTING) {
+									jvbDump('No config set for dimension '.$sk.': '.print_r($sv, true));
+								}
 							}
 						}
 
@@ -3030,7 +3306,10 @@
 						break;
 
 					default:
-						jvbDump('No config set for '.$k.': '.print_r($v, true));
+						if (JVB_TESTING) {
+							jvbDump($v,'No config set for '.$k.': ');
+						}
+
 				}
 			}
 
@@ -3041,21 +3320,37 @@
 				$styles = [];
 
 				if (isset($border['radius'])) {
-					$styles[] = 'border-radius: '.$border['radius'];
+					$styles['border-radius'] = $border['radius'];
 				}
 
-				if (isset($border['width']) && isset($attrs['borderColor'])) {
+				if (isset($border['width']) && (isset($attrs['borderColor']) || isset($border['color']))) {
 					$st = $border['style'] ?? 'solid';
-					$styles[] = sprintf(
-						'border: %s %s %s',
+					$color = $border['color']??$attrs['borderColor'];
+					$styles['border'] = sprintf(
+						'%s %s %s',
 						$border['width'],
 						$st,
-						$this->getColor($attrs['borderColor'])
+						$this->getColor($color)
 					);
-				} elseif (isset($border['width'])) {
-					$styles[] = 'border-width: '.$border['width'];
-				} elseif (isset($border['style'])) {
-					$styles[] = 'border-style: '.$border['style'];
+				} else {
+					if (isset($border['color'])) {
+						$styles['border-color'] = $border['color'];
+					}
+					if (isset($border['width'])) {
+						$styles['border-width'] = $border['width'];
+					}
+					if (isset($border['style'])) {
+						$styles['border-style'] = $border['style'];
+					}
+				}
+				if (JVB_TESTING) {
+					unset($border['radius']);
+					unset($border['width']);
+					unset($border['style']);
+					unset($border['color']);
+					if (!empty($border)) {
+						jvbDump($border, '[getBorderStyle] Leftover styles:');
+					}
 				}
 
 				return $styles;
@@ -3070,9 +3365,9 @@
 					switch ($l) {
 						case 'selfStretch':
 							if ($option === 'fixed' && isset($layout['selfStretchValue'])) {
-								$styles[] = 'width: '.$layout['selfStretchValue'];
+								$styles['width'] = $layout['selfStretchValue'];
 							} elseif ($option === 'fill') {
-								$styles[] = 'flex:1';
+								$styles['flex'] = 1;
 							}
 							break;
 						default:
@@ -3099,30 +3394,41 @@
 			private function getTypographyStyle(array $typography, array $attrs):array
 			{
 				$styles = [];
-
-				if (isset($typography['fontSize'])) {
-					$styles[] = 'font-size: '.$typography['fontSize'];
+				foreach ($typography as $property => $value) {
+					switch ($property) {
+						case 'fontSize':
+							$styles['font-size'] = $value;
+							break;
+						case 'fontWeight':
+							$styles['font-weight'] = $value;
+							break;
+						case 'textDecoration':
+							$styles['text-decoration'] = $value;
+							break;
+						case 'textTransform':
+							$styles['text-transform'] = $value;
+							break;
+						case 'letterSpacing':
+							$styles['letter-spacing'] = $value;
+							break;
+						case 'lineHeight':
+							$styles['line-height'] = $value;
+							break;
+						case 'fontStyle':
+							$styles['font-style'] = $value;
+							break;
+						case 'writingMode':
+							$styles['writing-mode'] = $value;
+							break;
+						case 'textAlign':
+							$styles['text-align'] = $value;
+						default:
+							if (JVB_TESTING) {
+								jvbDump($value,'[getTypographyStyle] No property set for '.$property.': ');
+							}
+					}
 				}
 
-				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
@@ -3135,18 +3441,36 @@
 						default   => $element,
 					};
 
+					$selectors = explode(',',$selector);
 					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']);
+						$fullSelector = array_map(function($sel) use ($uid, $state) {
+							return str_starts_with($state, ':')
+								? ".{$uid} {$sel}{$state}"
+								: ".{$uid} {$sel}";
+						}, $selectors);
+						$fullSelector = implode(',', $fullSelector);
+
+
+						if (JVB_TESTING) {
+							jvbDump($state, 'state');
+							jvbDump($rules, 'rules');
 						}
-						if (isset($rules['color']['background'])) {
-							$css[] = 'background-color: '.$this->getColor($rules['color']['background']);
+
+						if (isset($rules['color']['text']) || isset($rules['text'])) {
+							$css['color'] = $this->getColor($rules['color']['text'] ?? $rules['text']);
 						}
+						if (isset($rules['color']['background']) || isset($rules['background'])) {
+							$css['background-color'] = $this->getColor($rules['color']['background']??$rules['background']);
+						}
+						//clean out possible empty values
+						$css = array_filter($css);
+
+						$css = array_map(function ($property, $value) {
+							return $property.': '.$value;
+						}, array_keys($css), $css);
+
 						if (!empty($css)) {
 							static::$pendingStyles[] = $fullSelector.' { '.implode('; ', $css).' }';
 						}
@@ -3162,31 +3486,32 @@
 		return $out;
 	}
 
-		private function getDimRatioStyle(int $value, array $attrs):string
+		private function getDimRatioStyle(int $value, array $attrs):array
 		{
 			//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));';
-				}
+//			jvbDump($value, 'dimRatio');
+//			jvbDump($attrs, 'dimRatio attrs');
+			$ratio = [];
+
+			$s = array_key_exists('overlayColor', $attrs) ? 'var(--'.$attrs['overlayColor'].')' : 'var(--base)';
+			$s = 'rgba('.$s.', ';
+			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;
+
+			return ['background-color' => $s];
 		}
 
 		protected function getDataset(array $attrs):array
@@ -3224,14 +3549,18 @@
 		return array_key_exists($test, $attrs) && $attrs[$test]===true;
 	}
 
-	protected function getColor(string $value):string
+	protected function getColor(string $value, bool $prefix = true):string
 	{
 		$defaults = apply_filters('jvbColours', ['base', 'contrast', 'action', 'secondary']);
 		foreach ($defaults as $default) {
 			if (str_starts_with($value, $default)) {
-				return 'var(--'.$value.')';
+				return $prefix ? 'var(--'.$value.')' : $value;
 			}
 		}
+		//We removed the presets
+		if (str_contains($value, 'var:preset')) {
+			return '';
+		}
 		return $value;
 	}
 

--
Gitblit v1.10.0