get('id');
    $primary = isset($actions['optimizethumbs']) ? 'button-primary' : '';
    $output .= "
                    
                        ";
    $output .= "
";
    foreach($actions as $actionName => $actionData)
    {
        $link = ($actionData['type'] == 'js') ? 'javascript:' . $actionData['function'] : $actionData['function'];
        $output .= "
" . esc_html($actionData['text']) . "";
    }
    $output .= "
   ";
    return $output;
  }
  public static function renderSuccessText($imageObj)
  {
    $output = '';
    //$percent = $imageObj->getMeta('improvement');
    $percent = $imageObj->getImprovement();
    if($percent == 999) return ;
    if ($percent == 999 )
      $output .= __("Reduced by X%(unknown)", 'shortpixel-image-optimiser');
    if ($percent && $percent > 0)
    {
      $output .= __('Reduced by','shortpixel-image-optimiser') . ' ' . self::formatNumber($percent,2) . '% ';
    }
    if (intval($percent) < 5)
      $output .= __('Bonus processing','shortpixel-image-optimiser');
    $type = $imageObj->getMeta('compressionType');
    $output .= ' ('. self::compressionTypeToText($type) .')';
    $thumbs = $imageObj->get('thumbnails');
    $thumbsDone = $retinasDone = 0;
    $thumbsTotal = ($thumbs) ? count($thumbs) : 0;
    $retinas = $imageObj->get('retinas');
    $webpsTotal = $imageObj->count('webps');
    $avifsTotal = $imageObj->count('avifs');
    if($retinas)
    {
      foreach($retinas as $retinaObj)
      {
         if ($retinaObj->isOptimized())
         {
           $retinasDone++;
         }
      }
    }
    $improvements = $imageObj->getImprovements();
    $thumbTotal = $thumbsDone = 0;
    if ($imageObj->get('thumbnails'))
    {
      $thumbsTotal = count($imageObj->get('thumbnails'));  //
      //$thumbsDone =  (isset($improvements['thumbnails'])) ? count($improvements['thumbnails']) : 0;
      $thumbsDone = $imageObj->count('optimized', array('thumbs_only' => true));
      $excludedThumbs = $imageObj->count('user_excluded', array('thumbs_only' => true));
    }
    if (isset($improvements['thumbnails']))
    {
       $excluded = ($excludedThumbs > 0) ? sprintf(__('(%s excluded)', 'shortpixel-image-optimiser'), $excludedThumbs) : '';
       $output .= '';
       if ($thumbsTotal > $thumbsDone)
         $output .= '
' . sprintf(__('+%s of %s thumbnails optimized','shortpixel-image-optimiser'), self::formatNumber($thumbsDone,0), self::formatNumber($thumbsTotal,0)) . ' ' .   $excluded . '
';
       elseif ($thumbsDone > 0)
         $output .= '
' . sprintf(__('+%s thumbnails optimized','shortpixel-image-optimiser'), self::formatNumber($thumbsDone, 0)) . ' ' . $excluded . '
';
			 $improvs = array();
				 uasort($improvements['thumbnails'], function ($a, $b) {
					 	//return $b[0] <=> $a[0]; // @todo Efficient code to use once PHP 5 support is done.
						if ($a == $b) {
							return 0;
						}
						return ($b < $a) ? -1 : 1;
				 });
			 $cutoff = false;
			 $thumbCount = count($improvements['thumbnails']);
			 if ($thumbCount > 20)
			 {
				  $improvements['thumbnails'] =  array_slice($improvements['thumbnails'], 0, 15, true);
					$cutoff = true;
			 }
			 // Quality Check
			 foreach($improvements['thumbnails'] as $thumbName => $thumbStat)
			 {
				  $stat = $thumbStat[0];
				 	if (is_numeric($stat) && $stat >= 0)
					{
						 $improvs[$thumbName] = $stat; //self::formatNumber($stat,2);
					}
			 }
			 if (count($improvs) > 0)
			 {
		       $output .= "
";
					 $lowrating = 0;
		       foreach($improvs as $thumbName => $stat)
		       {
						   $statText = self::formatNumber($stat, 2);
		           $title =  sprintf(__('%s : %s', 'shortpixel-image-optimiser'), $thumbName, $statText . '%');
		           $rating = ceil( round($stat) / 10);
							 if (0 == $rating)
							 {
								 	$lowrating++;
									continue;
							 }
		           $blocks_on = str_repeat('
 ', $rating);
		           $blocks_off = str_repeat('
 ', (10- $rating));
		           $output .= "
"
		                       . "" .  $thumbName . '' .
		                        "" . $blocks_on . $blocks_off . "
		                      
";
		       }
					 if ($lowrating > 0)
					 {
						 $blocks_off = str_repeat('
 ', 10);
						 $output .= "
"
												 . "" . sprintf(__('+ %d thumbnails ', 'shortpixel-image-optimiser'), $lowrating) . '' .
													"" . $blocks_off . "
												
";
					 }
					 if (true === $cutoff)
					 {
						 $output .= '
' . sprintf(__('+ %d more', 'shortpixel-image-optimiser'), ($thumbCount - 15)) . '
';
					 }
		       $output .=  "
 ";
				}
				$output .= "
' . sprintf(__('+%s Retina images optimized','shortpixel-image-optimiser') , $retinasDone) . '
';
    }
    if ($webpsTotal > 0)
    {
      $output .=  '' . sprintf(__('+%s Webp images ','shortpixel-image-optimiser') , $webpsTotal) . '
';
    }
    if ($avifsTotal > 0)
    {
        $output .=  '' . sprintf(__('+%s Avif images ','shortpixel-image-optimiser') , $avifsTotal) . '
';
    }
    if ($imageObj->isSomethingOptimized() && $imageObj->isProcessable())
    {
        list($urls, $optimizable) = $imageObj->getCountOptimizeData('thumbnails');
				list($webpUrls, $webpCount)   =  $imageObj->getCountOptimizeData('webp');
				list($avifUrls, $avifCount)   =  $imageObj->getCountOptimizeData('avif');
				$maxList = 10;
			 if (count($urls) > $maxList)
			 {
				  $urls = array_slice($urls, 0, $maxList, true);
					$urls[] = '...';
			 }
			 if (count($webpUrls) > $maxList)
			 {
				  $webpUrls = array_slice($webpUrls, 0, $maxList, true);
					$webpUrls[] = '...';
			 }
			 if (count($avifUrls) > $maxList)
			 {
				  $avifUrls = array_slice($avifUrls, 0, $maxList, true);
					$avifUrls[] = '...';
			 }
        if ($optimizable > 0)
        {
           $output .= '' . sprintf(__('%d images to optimize', 'shortpixel-image-optimiser'), $optimizable) . '
';
             $output .= "";
               foreach($urls as $optObj)
               {
								 if ($optObj === '...')
									$output .= $optObj;
								 else
                  $output .= substr($optObj, strrpos($optObj, '/')+1) . '
';
               }
             $output .= "";
           $output .= '';
        }
        if ($webpCount > 0 )
        {
           $output .= '' . sprintf(__('%d Webp files to create', 'shortpixel-image-optimiser'), $webpCount) . '
';
             $output .= "";
               foreach($webpUrls as $optObj)
               {
								  if ($optObj === '...')
									 $output .= $optObj;
									else
                  	$output .= self::convertImageTypeName(substr($optObj, strrpos($optObj, '/')+1), 'webp') . '
';
               }
             $output .= "";
           $output .= '';
        }
        if ($avifCount > 0)
        {
            $output .= '' . sprintf(__('%d Avif files to create', 'shortpixel-image-optimiser'), $avifCount) . '
';
              $output .= "";
                foreach($avifUrls as $optObj)
                {
                   $output .= self::convertImageTypeName(substr($optObj, strrpos($optObj, '/')+1), 'avif') . '
';
                }
              $output .= "";
            $output .= '';
        }
    }
    return $output;
  }
  public static function compressionTypeToText($type)
  {
     if ($type == ImageModel::COMPRESSION_LOSSLESS )
       return __('Lossless', 'shortpixel-image-optimiser');
     if ($type == ImageModel::COMPRESSION_LOSSY )
         return __('Lossy', 'shortpixel-image-optimiser');
     if ($type == ImageModel::COMPRESSION_GLOSSY )
         return __('Glossy', 'shortpixel-image-optimiser');
      return $type;
  }
  public static function getListActions($mediaItem)
  {
      $list_actions = array();
      $id = $mediaItem->get('id');
		  $keyControl = ApiKeyController::getInstance();
			if (! $keyControl->keyIsVerified())
			{
				return array(); // nothing
			}
      $quotaControl = QuotaController::getInstance();
			$access = AccessModel::getInstance();
			if (! $access->imageIsEditable($mediaItem))
			{
				 return array();
			}
			if ($id === 0)
				return array();
      if ($mediaItem->isSomethingOptimized() )
      {
						list($u, $optimizable) = $mediaItem->getCountOptimizeData('thumbnails');
						list($u, $optimizableWebp)   =  $mediaItem->getCountOptimizeData('webp');
						list($u, $optimizableAvif)   =  $mediaItem->getCountOptimizeData('avif');
           if ($mediaItem->isProcessable() && ! $mediaItem->isOptimizePrevented())
           {
             $action = self::getAction('optimizethumbs', $id);
             if ($optimizable > 0)
             {
							 $total = $optimizable + $optimizableWebp + $optimizableAvif;
							 if ($optimizableWebp > 0 || $optimizableAvif > 0)
							 	   $itemText = __('items', 'shortpixel-image-optimiser');
								else {
									 $itemText = __('thumbnails', 'shortpixel-image-optimiser');
								}
               $action['text']  = sprintf(__('Optimize %s  %s','shortpixel-image-optimiser'),$total, $itemText);
             }
             else
             {
                 if ($optimizableWebp > 0 && $optimizableAvif > 0)
                   $text  = sprintf(__('Optimize %s webps and %s avif','shortpixel-image-optimiser'),$optimizableWebp, $optimizableAvif);
                 elseif ($optimizableWebp > 0)
                   $text  = sprintf(__('Optimize %s webps','shortpixel-image-optimiser'),$optimizableWebp);
                 else
                    $text  = sprintf(__('Optimize %s avifs','shortpixel-image-optimiser'),$optimizableAvif);
                 $action['text'] = $text;
             }
             $list_actions['optimizethumbs'] = $action;
          }
          if ($mediaItem->isRestorable())
          {
            if ($mediaItem->get('type') == 'custom')
            {
                if ($mediaItem->getExtension() !== 'pdf') // no support for this
                  $list_actions['comparer'] = self::getAction('compare-custom', $id);
            }
            else
            {
                // PDF without thumbnail can't be compared.
                $showCompare = true;
                if ($mediaItem->getExtension() == 'pdf')
                {
  				            if (! $mediaItem->getThumbnail('full'))
  					               $showCompare = false;
  				            elseif(! $mediaItem->getThumbnail('full')->hasBackup())
  					             $showCompare = false;
  			         }
  				       if ($showCompare)
                   $list_actions['comparer'] = self::getAction('compare', $id);
            }
			 			if ($mediaItem->isRestorable())
						{
							 $compressionType = $mediaItem->getMeta('compressionType');
		           switch($compressionType)
		           {
		               case ImageModel::COMPRESSION_LOSSLESS:
		                 $list_actions['reoptimize-lossy'] = self::getAction('reoptimize-lossy', $id);
		                 $list_actions['reoptimize-glossy'] = self::getAction('reoptimize-glossy', $id);
		               break;
		               case ImageModel::COMPRESSION_LOSSY:
		                 $list_actions['reoptimize-lossless'] = self::getAction('reoptimize-lossless', $id);
		                 $list_actions['reoptimize-glossy'] = self::getAction('reoptimize-glossy', $id);
		               break;
		               case ImageModel::COMPRESSION_GLOSSY:
		                 $list_actions['reoptimize-lossy'] = self::getAction('reoptimize-lossy', $id);
		                 $list_actions['reoptimize-lossless'] = self::getAction('reoptimize-lossless', $id);
		               break;
		           }
							 if ($mediaItem->get('type') === 'media')
							 {
							 		$list_actions['reoptimize-smartcrop'] = self::getAction('reoptimize-smartcrop', $id, array('compressionType' => $compressionType));
							 		$list_actions['reoptimize-smartcropless'] = self::getAction('reoptimize-smartcropless', $id, array('compressionType' => $compressionType));
								}
		          		$list_actions['restore'] = self::getAction('restore', $id);
							} // isRestorable
						else
						{
						}
        } // hasBackup
				if (\wpSPIO()->env()->is_debug && $mediaItem->get('type') == 'media')
				{
					 $list_actions['redo_legacy'] = self::getAction('redo_legacy', $id);
				}
      } //isOptimized
      if(! $quotaControl->hasQuota())
      {
         $remove = array('reoptimize-lossy' => '', 'reoptimize-glossy' => '', 'reoptimize-lossless' => '', 'optimizethumbs' => '');
         $list_actions = array_diff_key($list_actions, $remove);
      }
      return $list_actions;
  }
  public static function getActions($mediaItem)
  {
    $actions = array();
    $id = $mediaItem->get('id');
    $quotaControl = QuotaController::getInstance();
		$optimizeController = new OptimizeController();
		$keyControl = ApiKeyController::getInstance();
		if (! $keyControl->keyIsVerified())
		{
			return array(); // nothing
		}
		$access = AccessModel::getInstance();
		if (! $access->imageIsEditable($mediaItem))
		{
			 return array();
		}
		if ($id === 0)
			return array();
    if(! $quotaControl->hasQuota())
    {
       $actions['extendquota'] = self::getAction('extendquota', $id);
       $actions['checkquota'] = self::getAction('checkquota', $id);
    }
    elseif($mediaItem->isProcessable() && ! $mediaItem->isSomethingOptimized() && ! $mediaItem->isOptimizePrevented() && ! $optimizeController->isItemInQueue($mediaItem))
    {
       $actions['optimize'] = self::getAction('optimize', $id);
       $actions['markCompleted']  = self::getAction('markCompleted', $id);
    }
    elseif ($mediaItem->isUserExcluded() && false === $mediaItem->isSomethingOptimized())
    {
      $actions['optimize'] = self::getAction('forceOptimize', $id);
    }
    return $actions;
  }
  public static function getStatusText($mediaItem)
  {
    $keyControl = ApiKeyController::getInstance();
    $quotaControl = QuotaController::getInstance();
		$optimizeController = new OptimizeController();
    $settings = \wpSPIO()->settings();
    $text = '';
    if (! $keyControl->keyIsVerified())
    {
      $text = __('Invalid API Key. Check your Settings','shortpixel-image-optimiser');
    }
		// This basically happens when a NextGen gallery is not added to Custom Media.
		elseif ($mediaItem->get('id') === 0)
		{
			 if ($mediaItem->isProcessable(true) === false)
			 {
				 $text = __('Not Processable: ','shortpixel_image_optimiser');
				 $text  .= $mediaItem->getProcessableReason();
			 }
			 else {
         if (\wpSPIO()->env()->has_nextgen && false == $settings->includeNextGen)
         {
           $text = __('This image was not found in our database. Enable "Optimize nextgen galleries" in the settings, or add this folder manually. ', 'shortpixel-image-optimiser');
         }
         else {
           $text = __('This image was not found in our database. Refresh folders, or add this gallery', 'shortpixel-image-optimiser');
         }
			 }
		}
    elseif ($mediaItem->isSomethingOptimized())
    {
       $text = UiHelper::renderSuccessText($mediaItem);
    }
    elseif (false === $mediaItem->isProcessable()  )
    {
       $text = __('Not Processable: ','shortpixel_image_optimiser');
       $text  .= $mediaItem->getProcessableReason();
    }
    elseif (! $mediaItem->exists())
    {
       $text = __('File does not exist.','shortpixel-image-optimiser');
    }
    elseif ($mediaItem->getMeta('status') < 0)
    {
      $text = $mediaItem->getMeta('errorMessage');
    }
		elseif( $optimizeController->isItemInQueue($mediaItem) === true)
		{
			 $text = '' . __('This item is waiting to be processed', 'shortpixel-image-optimiser') . '
';
			 $action = self::getAction('cancelOptimize', $mediaItem->get('id'));
			 $text .= '' . $action['text'] . '
';
		}
    if ($mediaItem->isOptimizePrevented() !== false)
    {
          $retry = self::getAction('retry', $mediaItem->get('id'));
          $unmark = self::getAction('unMarkCompleted', $mediaItem->get('id'));
					$redo_legacy = false;
					if ($mediaItem->get('type') == 'media')
					{
	 					$was_converted = get_post_meta($mediaItem->get('id'), '_shortpixel_was_converted', true);
						$updateTs = 1656892800; // July 4th 2022 - 00:00 GMT
						if ($was_converted < $updateTs)
						{
							$meta = $mediaItem->getWPMetaData();
							if (is_array($meta) && isset($meta['ShortPixel']))
							{
								$redo_legacy = self::getAction('redo_legacy', $mediaItem->get('id'));
							}
						}
					}
          $status = $mediaItem->getMeta('status');
          $text = ''; // reset text
          if (ImageModel::FILE_STATUS_MARKED_DONE == $status)
          {
            $text .= "" . esc_html($mediaItem->getReason('processable'));
            $text .= "
" . sprintf(__('%s Click to unmark as completed %s', 'shortpixel-image-optimiser'), '', '') . '
';
            $text .= '
" . esc_html($mediaItem->getReason('processable'));
            $text .= "
" . sprintf(__('After you have fixed this issue, you can %s click here to retry %s', 'shortpixel-image-optimiser'), '', '') . '';
            $text .= '
";
						$text .= sprintf(esc_html__('It seems you have older converted legacy data, which might cause this issue. You can try to %s %s %s . If nothing changes, this is not the cause. ','shortpixel-image-optimiser'), '', $redo_legacy['text'], '');
						$text .= " ";
					}
      }
    return $text;
  }
  // Defines all possible actions in the Ui
  public static function getAction($name, $id, $args = array())
  {
     $action = array('function' => '', 'type' => '', 'text' => '', 'display' => '');
     $keyControl = ApiKeyController::getInstance();
		 $compressionType = isset($args['compressionType']) ? $args['compressionType'] : null;
    switch($name)
    {
      case 'optimize':
         $action['function'] = 'window.ShortPixelProcessor.screen.Optimize(' . $id . ')';
         $action['type']  = 'js';
         $action['text'] = __('Optimize Now', 'shortpixel-image-optimiser');
         $action['display'] = 'button';
      break;
      case 'forceOptimize':
        $action['function'] = 'window.ShortPixelProcessor.screen.Optimize(' . $id . ', true)';
        $action['type']  = 'js';
        $action['text'] = __('Override exclusions and optimize now', 'shortpixel-image-optimiser');
        $action['display'] = 'button';
      break;
			case 'cancelOptimize':
				 $action['function'] = 'window.ShortPixelProcessor.screen.CancelOptimizeItem(' . $id . ')';
				 $action['type']  = 'js';
				 $action['text'] = __('Cancel item optimization', 'shortpixel-image-optimiser');
				 $action['display'] = 'button';
			break;
      case 'markCompleted':
          $action['function'] = 'window.ShortPixelProcessor.screen.MarkCompleted(' . $id . ')';
          $action['type']  = 'js';
          $action['text'] = __('Mark as Completed', 'shortpixel-image-optimiser');
          $action['display'] = 'button-secondary';
          $action['layout'] = 'paragraph';
          $action['title'] = __('This will cause the plugin to skip this image for optimization', 'shortpixel-image-optimiser');
      break;
      case 'unMarkCompleted':
          $action['function'] = 'window.ShortPixelProcessor.screen.UnMarkCompleted(' . $id . ')';
          $action['type']  = 'js';
          $action['text'] = __('Click to unmark this item as done', 'shortpixel-image-optimiser');
          $action['display'] = 'js';
      break;
      case 'optimizethumbs':
          $action['function'] = 'window.ShortPixelProcessor.screen.Optimize(' . $id . ');';
          $action['type'] = 'js';
          $action['text']  = '';
          $action['display'] = 'inline';
      break;
      case 'retry':
         $action['function'] = 'window.ShortPixelProcessor.screen.Optimize(' . $id . ');';
         $action['type']  = 'js';
         $action['text'] = __('Retry', 'shortpixel-image-optimiser') ;
         $action['display'] = 'button';
     break;
		 case 'redo_legacy':
		 			$action['function'] = 'window.ShortPixelProcessor.screen.RedoLegacy(' . $id . ');';
		 			$action['type']  = 'js';
		 			$action['text'] = __('Redo Conversion', 'shortpixel-image-optimiser') ;
		 			$action['display'] = 'button';
		 break;
     case 'restore':
         $action['function'] = 'window.ShortPixelProcessor.screen.RestoreItem(' . $id . ');';
         $action['type'] = 'js';
         $action['text'] = __('Restore backup','shortpixel-image-optimiser');
         $action['display'] = 'inline';
     break;
     case 'compare':
        $action['function'] = 'ShortPixel.loadComparer(' . $id . ')';
        $action['type'] = 'js';
        $action['text'] = __('Compare', 'shortpixel-image-optimiser');
        $action['display'] = 'inline';
     break;
     case 'compare-custom':
        $action['function'] = 'ShortPixel.loadComparer(' . $id . ',"custom")';
        $action['type'] = 'js';
        $action['text'] = __('Compare', 'shortpixel-image-optimiser');
        $action['display'] = 'inline';
     break;
     case 'reoptimize-glossy':
        $action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . ImageModel::COMPRESSION_GLOSSY . ')';
        $action['type'] = 'js';
        $action['text'] = __('Re-optimize Glossy','shortpixel-image-optimiser') ;
        $action['display'] = 'inline';
     break;
     case 'reoptimize-lossy':
        $action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . ImageModel::COMPRESSION_LOSSY . ')';
        $action['type'] = 'js';
        $action['text'] = __('Re-optimize Lossy','shortpixel-image-optimiser');
        $action['display'] = 'inline';
     break;
     case 'reoptimize-lossless':
        $action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . ImageModel::COMPRESSION_LOSSLESS . ')';
        $action['type'] = 'js';
        $action['text'] = __('Re-optimize Lossless','shortpixel-image-optimiser');
        $action['display'] = 'inline';
     break;
		 case 'reoptimize-smartcrop':
        $action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . $compressionType . ',' . ImageModel::ACTION_SMARTCROP . ')';
        $action['type'] = 'js';
        $action['text'] = __('Re-optimize with SmartCrop','shortpixel-image-optimiser');
        $action['display'] = 'inline';
     break;
		 case 'reoptimize-smartcropless':
        $action['function'] = 'window.ShortPixelProcessor.screen.ReOptimize(' . $id . ',' . $compressionType . ',' . ImageModel::ACTION_SMARTCROPLESS . ')';
        $action['type'] = 'js';
        $action['text'] = __('Re-optimize without SmartCrop','shortpixel-image-optimiser');
        $action['display'] = 'inline';
     break;
     case 'extendquota':
        $action['function'] = 'https://shortpixel.com/login/'. $keyControl->getKeyForDisplay();
        $action['type'] = 'button';
        $action['text'] = __('Extend Quota','shortpixel-image-optimiser');
        $action['display'] = 'button';
     break;
     case 'checkquota':
        $action['function'] = 'ShortPixel.checkQuota()';
        $action['type'] = 'js';
        $action['display'] = 'button';
        $action['text'] = __('Check  Quota','shortpixel-image-optimiser');
     break;
   }
   return $action;
  }
	public static function getConvertErrorReason($error)
	{
		switch($error)
		{
			case -1: //ERROR_LIBRARY:
				$reason = __('PNG Library is not present or not working', 'shortpixel-image-optimiser');
			break;
			case -2: //ERROR_PATHFAIL:
				$reason = __('Could not create path', 'shortpixel-image-optimiser');
			break;
			case -3: //ERROR_RESULTLARGER:
				$reason  = __('Result file is larger','shortpixel-image-optimiser');
			break;
			case -4: // ERROR_WRITEERROR
				$reason = __('Could not write result file', 'shortpixel-image-optimiser');
			break;
			case -5: // ERROR_BACKUPERROR
				$reason = __('Could not create backup', 'shortpixel-image-optimiser');
			break;
			case -6:  // ERROR_TRANSPARENT
				$reason = __('Image is transparent', 'shortpixel-image-optimiser');
			break;
			default:
				$reason = sprintf(__('Unknown error %s', 'shortpixel-image-optimiser'), $error);
			break;
		}
		$message = sprintf(__('Not converted: %s ', 'shortpixel-image-optimiser'), $reason);
		return $message;
	}
	public static function getKBSearchLink($subject)
	{
			return esc_url(self::$knowledge_url . sanitize_text_field($subject));
	}
	// @param MediaLibraryModel Object $imageItem
	// @param String $size  Preferred size
	// @param String Preload The preloader tries to guess what the preview might be for a more smooth process. Ignore optimize / backup
	public static function findBestPreview($imageItem, $size = 800, $preload = false)
	{
		 	$closestObj = $imageItem;
			// set the standard.
			if ($imageItem->getExtension() == 'pdf') // try not to select non-showable extensions.
				$bestdiff = 0;
			else
				$bestdiff = abs($imageItem->get('width') - $size);
			$thumbnails = $imageItem->get('thumbnails');
			if (! is_array($thumbnails))
			{
				 return $closestObj; // nothing more to do.
			}
			foreach($thumbnails as $thumbnail)
			{
				 if (! $preload && (! $thumbnail->isOptimized() || ! $thumbnail->hasBackup()))
				 	continue;
					$diff = abs($thumbnail->get('width') - $size);
					if ($diff < $bestdiff)
					{
						 $closestObj = $thumbnail;
						 $bestdiff = $diff;
					}
			}
			return $closestObj;
	}
  public static function formatTS($ts)
  {
      //$format = get_option('date_format') .' @ ' . date_i18n(get_option('time_format');
			if (function_exists('wp_date'))
			{
      	$date = wp_date(get_option('date_format'), $ts);
				$date .= ' @ ' . wp_date(get_option('time_format'), $ts);
			}
			else
			{
      	$date = date_i18n(get_option('date_format'), $ts);
				$date .= ' @ ' . date_i18n(get_option('time_format'), $ts);
			}
      return $date;
  }
  public static function formatBytes($bytes, $precision = 2) {
      $units = array('B', 'KB', 'MB', 'GB', 'TB');
      $bytes = max($bytes, 0);
      $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
      $pow = min($pow, count($units) - 1);
      $bytes /= pow(1024, $pow);
      return number_format_i18n(round($bytes, $precision), $precision) . ' ' . $units[$pow];
  }
	public static function formatNumber($number, $precision = 2)
	{
			global $wp_locale;
			$decimalpoint = isset($wp_locale->number_format['decimal_point']) ? $wp_locale->number_format['decimal_point'] : false;
			$number =  number_format_i18n( (float) $number, $precision);
 			$hasDecimal = (strpos($number, $decimalpoint) === false) ? false : true;
			// Don't show trailing zeroes if number is a whole unbroken number. -> string comparison because number_format_i18n returns string.
			if ($decimalpoint !== false && $hasDecimal && substr($number, strpos($number, $decimalpoint) + 1) === '00')
			{
				 $number = substr($number, 0, strpos($number, $decimalpoint));
			}
			// Some locale's have no-breaking-space as thousands separator. This doesn't work well in JS / Cron Shell so replace with space.
			$number = str_replace(' ', ' ', $number);
		  return $number;
	}
	public static function formatDate( $date ) {
	if ( '0000-00-00 00:00:00' === $date->format('Y-m-d ') ) {
			$h_time = '';
	} else {
			$time   = $date->format('U'); //get_post_time( 'G', true, $post, false );
			if ( ( abs( $t_diff = time() - $time ) ) < DAY_IN_SECONDS ) {
					if ( $t_diff < 0 ) {
							$h_time = sprintf( __( '%s from now' ), human_time_diff( $time ) );
					} else {
							$h_time = sprintf( __( '%s ago' ), human_time_diff( $time ) );
					}
			} else {
					$h_time = $date->format( 'Y/m/d' );
			}
	}
	return $h_time;
}
	protected static function convertImageTypeName($name, $type)
	{
		if ($type == 'webp')
		{
			$is_double = \wpSPIO()->env()->useDoubleWebpExtension();
		}
		if ($type == 'avif')
		{
			$is_double = \wpSPIO()->env()->useDoubleAvifExtension();
		}
		if ($is_double)
		{
			 return $name . '.' . $type;
		}
		else
		{
			 return substr($name, 0, strrpos($name, '.')) . '.' . $type;
		}
	}
  /* Strings on settings page that need to be available for both JS and PHP  */
  public static function getSettingsStrings($name = false)
  {
      $strings = array(
      );
      $exclusion_types = array(
          'name' => __('Name', 'shortpixel-image-optimiser'),
          'path' => __('Path', 'shortpixel-image-optimiser'),
          'size' => __('Size', 'shortpixel-image-optimiser'),
      );
      $exclusion_apply = array(
           'all' => __('All', 'shortpixel-image-optimiser'),
           'only-thumbs' => __('Only Thumbnails', 'shortpixel-image-optimiser'),
           'only-custom' =>  __('Only Custom Media Images', 'shortpixel-image-optimiser'),
           'selected-thumbs' => __('Selected Images', 'shortpixel-image-optimiser'),
      );
      $strings['exclusion_types'] = $exclusion_types;
      $strings['exclusion_apply'] = $exclusion_apply;
      if ($name !== false && isset($strings[$name]))
      {
         return $strings[$name];
      }
      return $strings;
  }
} // class