104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeau 404fd306cSNickeaunamespace ComboStrap\TagAttribute; 504fd306cSNickeau 604fd306cSNickeau 704fd306cSNickeauuse ComboStrap\ColorRgb; 804fd306cSNickeauuse ComboStrap\ExceptionBadArgument; 904fd306cSNickeauuse ComboStrap\ExceptionCompile; 1004fd306cSNickeauuse ComboStrap\ExceptionInternal; 1104fd306cSNickeauuse ComboStrap\ExceptionNotFound; 1204fd306cSNickeauuse ComboStrap\FetcherSvg; 1304fd306cSNickeauuse ComboStrap\LazyLoad; 1404fd306cSNickeauuse ComboStrap\LogUtility; 1504fd306cSNickeauuse ComboStrap\MediaMarkup; 1604fd306cSNickeauuse ComboStrap\Opacity; 1704fd306cSNickeauuse ComboStrap\PluginUtility; 1804fd306cSNickeauuse ComboStrap\StringUtility; 1904fd306cSNickeauuse ComboStrap\TagAttributes; 2004fd306cSNickeau 2104fd306cSNickeau/** 2204fd306cSNickeau * Process background attribute 2304fd306cSNickeau */ 2404fd306cSNickeauclass BackgroundAttribute 2504fd306cSNickeau{ 2604fd306cSNickeau /** 2704fd306cSNickeau * Background Logical attribute / Public 2804fd306cSNickeau */ 2904fd306cSNickeau const BACKGROUND_COLOR = 'background-color'; 3004fd306cSNickeau const BACKGROUND_FILL = "fill"; 3104fd306cSNickeau 3204fd306cSNickeau 3304fd306cSNickeau /** 3404fd306cSNickeau * CSS attribute / not public 3504fd306cSNickeau */ 3604fd306cSNickeau const BACKGROUND_IMAGE = 'background-image'; 3704fd306cSNickeau const BACKGROUND_SIZE = "background-size"; 3804fd306cSNickeau const BACKGROUND_REPEAT = "background-repeat"; 3904fd306cSNickeau const BACKGROUND_POSITION = "background-position"; 4004fd306cSNickeau 4104fd306cSNickeau 4204fd306cSNickeau /** 4304fd306cSNickeau * The page canonical of the documentation 4404fd306cSNickeau */ 4504fd306cSNickeau const CANONICAL = "background"; 4604fd306cSNickeau /** 4704fd306cSNickeau * A component attributes to store backgrounds 4804fd306cSNickeau */ 4904fd306cSNickeau const BACKGROUNDS = "backgrounds"; 5004fd306cSNickeau 5104fd306cSNickeau /** 5204fd306cSNickeau * Pattern css 5304fd306cSNickeau * https://bansal.io/pattern-css 5404fd306cSNickeau */ 5504fd306cSNickeau const PATTERN_ATTRIBUTE = "pattern"; 5604fd306cSNickeau const PATTERN_CSS_SNIPPET_ID = "pattern.css"; 5704fd306cSNickeau const PATTERN_CSS_SIZE = ["sm", "md", "lg", "xl"]; 5804fd306cSNickeau const PATTERN_NAMES = ['checks', 'grid', 'dots', 'cross-dots', 'diagonal-lines', 'horizontal-lines', 'vertical-lines', 'diagonal-stripes', 'horizontal-stripes', 'vertical-stripes', 'triangles', 'zigzag']; 5904fd306cSNickeau const PATTERN_CSS_CLASS_PREFIX = "pattern"; 6004fd306cSNickeau const PATTERN_COLOR_ATTRIBUTE = "pattern-color"; 6104fd306cSNickeau 6204fd306cSNickeau 6304fd306cSNickeau public static function processBackgroundAttributes(TagAttributes &$tagAttributes) 6404fd306cSNickeau { 6504fd306cSNickeau 6604fd306cSNickeau /** 6704fd306cSNickeau * Backgrounds set with the {@link \syntax_plugin_combo_background} component 6804fd306cSNickeau */ 6904fd306cSNickeau if ($tagAttributes->hasComponentAttribute(self::BACKGROUNDS)) { 7004fd306cSNickeau PluginUtility::getSnippetManager()->attachCssInternalStyleSheet(self::CANONICAL); 7104fd306cSNickeau $backgrounds = $tagAttributes->getValueAndRemove(self::BACKGROUNDS); 7204fd306cSNickeau switch (sizeof($backgrounds)) { 7304fd306cSNickeau case 1: 7404fd306cSNickeau // Only one background was specified 7504fd306cSNickeau $background = $backgrounds[0]; 7604fd306cSNickeau if ( 7704fd306cSNickeau /** 7804fd306cSNickeau * We need to create a background node 7904fd306cSNickeau * if we transform or 8004fd306cSNickeau * use a CSS pattern (because it use the text color as one of painting color) 8104fd306cSNickeau */ 8204fd306cSNickeau !isset($background[TagAttributes::TRANSFORM]) && 8304fd306cSNickeau !isset($background[self::PATTERN_ATTRIBUTE]) 8404fd306cSNickeau ) { 8504fd306cSNickeau /** 8604fd306cSNickeau * For readability, 8704fd306cSNickeau * we put the background on the parent node 8804fd306cSNickeau * because there is only one background 8904fd306cSNickeau */ 90*70bbd7f1Sgerardnico $backgroundImage = $background[self::BACKGROUND_IMAGE] ?? null; 91*70bbd7f1Sgerardnico if ($backgroundImage !== null) { 92*70bbd7f1Sgerardnico $tagAttributes->addComponentAttributeValueIfNotEmpty(self::BACKGROUND_IMAGE, $backgroundImage); 93*70bbd7f1Sgerardnico } 94*70bbd7f1Sgerardnico $backgroundColor = $background[self::BACKGROUND_COLOR] ?? null; 95*70bbd7f1Sgerardnico if ($backgroundColor !== null) { 96*70bbd7f1Sgerardnico $tagAttributes->addComponentAttributeValueIfNotEmpty(self::BACKGROUND_COLOR, $backgroundColor); 97*70bbd7f1Sgerardnico } 98*70bbd7f1Sgerardnico $opacityAttribute = $background[Opacity::OPACITY_ATTRIBUTE] ?? null; 99*70bbd7f1Sgerardnico if ($opacityAttribute !== null) { 100*70bbd7f1Sgerardnico $tagAttributes->addComponentAttributeValueIfNotEmpty(Opacity::OPACITY_ATTRIBUTE, $opacityAttribute); 101*70bbd7f1Sgerardnico } 102*70bbd7f1Sgerardnico $backgroundPosition = $background[self::BACKGROUND_POSITION] ?? null; 103*70bbd7f1Sgerardnico if ($backgroundPosition !== null) { 104*70bbd7f1Sgerardnico $tagAttributes->addComponentAttributeValueIfNotEmpty(self::BACKGROUND_POSITION, $backgroundPosition); 105*70bbd7f1Sgerardnico } 106*70bbd7f1Sgerardnico $backgroundFill = $background[self::BACKGROUND_FILL] ?? null; 107*70bbd7f1Sgerardnico if ($backgroundFill !== null) { 108*70bbd7f1Sgerardnico $tagAttributes->addComponentAttributeValueIfNotEmpty(self::BACKGROUND_FILL, $backgroundFill); 109*70bbd7f1Sgerardnico } 11004fd306cSNickeau } else { 11104fd306cSNickeau $backgroundTagAttribute = TagAttributes::createFromCallStackArray($background); 11204fd306cSNickeau $backgroundTagAttribute->addClassName(self::CANONICAL); 11304fd306cSNickeau $backgroundHTML = "<div class=\"backgrounds\">" . 11404fd306cSNickeau $backgroundTagAttribute->toHtmlEnterTag("div") . 11504fd306cSNickeau "</div>" . 11604fd306cSNickeau "</div>"; 11704fd306cSNickeau $tagAttributes->addHtmlAfterEnterTag($backgroundHTML); 11804fd306cSNickeau } 11904fd306cSNickeau break; 12004fd306cSNickeau default: 12104fd306cSNickeau /** 12204fd306cSNickeau * More than one background 12304fd306cSNickeau * This backgrounds should have been set on the backgrounds 12404fd306cSNickeau */ 12504fd306cSNickeau $backgroundHTML = ""; 12604fd306cSNickeau foreach ($backgrounds as $background) { 12704fd306cSNickeau $backgroundTagAttribute = TagAttributes::createFromCallStackArray($background); 12804fd306cSNickeau $backgroundTagAttribute->addClassName(self::CANONICAL); 12904fd306cSNickeau $backgroundHTMLEnter = $backgroundTagAttribute->toHtmlEnterTag("div"); 13004fd306cSNickeau $backgroundHTML .= $backgroundHTMLEnter . "</div>"; 13104fd306cSNickeau } 13204fd306cSNickeau $tagAttributes->addHtmlAfterEnterTag($backgroundHTML); 13304fd306cSNickeau break; 13404fd306cSNickeau } 13504fd306cSNickeau } 13604fd306cSNickeau 13704fd306cSNickeau /** 13804fd306cSNickeau * Background-image attribute 13904fd306cSNickeau */ 14004fd306cSNickeau $backgroundImageStyleValue = ""; 14104fd306cSNickeau if ($tagAttributes->hasComponentAttribute(self::BACKGROUND_IMAGE)) { 14204fd306cSNickeau $backgroundImageValue = $tagAttributes->getValueAndRemove(self::BACKGROUND_IMAGE); 14304fd306cSNickeau if (is_string($backgroundImageValue)) { 14404fd306cSNickeau /** 14504fd306cSNickeau * Image background is set by the user 14604fd306cSNickeau */ 14704fd306cSNickeau $backgroundImageStyleValue = $tagAttributes->getValueAndRemove(self::BACKGROUND_IMAGE); 14804fd306cSNickeau 14904fd306cSNickeau } else { 15004fd306cSNickeau 15104fd306cSNickeau if (is_array($backgroundImageValue)) { 15204fd306cSNickeau 15304fd306cSNickeau /** 15404fd306cSNickeau * Background-fill for background image 15504fd306cSNickeau */ 15604fd306cSNickeau $backgroundFill = $tagAttributes->getValueAndRemove(self::BACKGROUND_FILL, "cover"); 15704fd306cSNickeau switch ($backgroundFill) { 15804fd306cSNickeau case "cover": 15904fd306cSNickeau // it makes the background responsive 16004fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_SIZE, $backgroundFill); 16104fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_REPEAT, "no-repeat"); 16204fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_POSITION, "center center"); 16304fd306cSNickeau 16404fd306cSNickeau /** 16504fd306cSNickeau * The type of image is important for the processing of SVG 16604fd306cSNickeau */ 16704fd306cSNickeau $backgroundImageValue[TagAttributes::TYPE_KEY] = FetcherSvg::ILLUSTRATION_TYPE; 16804fd306cSNickeau break; 16904fd306cSNickeau case "tile": 17004fd306cSNickeau // background size is then "auto" (ie repeat), the default 17104fd306cSNickeau // background position is not needed (the tile start on the left top corner) 17204fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_REPEAT, "repeat"); 17304fd306cSNickeau 17404fd306cSNickeau /** 17504fd306cSNickeau * The type of image is important for the processing of SVG 17604fd306cSNickeau * A tile needs to have a width and a height 17704fd306cSNickeau */ 17804fd306cSNickeau $backgroundImageValue[TagAttributes::TYPE_KEY] = FetcherSvg::TILE_TYPE; 17904fd306cSNickeau break; 18004fd306cSNickeau case "css": 18104fd306cSNickeau // custom, set by the user in own css stylesheet, nothing to do 18204fd306cSNickeau break; 18304fd306cSNickeau default: 18404fd306cSNickeau LogUtility::msg("The background `fill` attribute ($backgroundFill) is unknown. If you want to take over the filling via css, set the `fill` value to `css`.", self::CANONICAL); 18504fd306cSNickeau break; 18604fd306cSNickeau } 18704fd306cSNickeau 18804fd306cSNickeau 18904fd306cSNickeau try { 19004fd306cSNickeau $mediaMarkup = MediaMarkup::createFromCallStackArray($backgroundImageValue) 19104fd306cSNickeau ->setLinking(MediaMarkup::LINKING_NOLINK_VALUE); 19204fd306cSNickeau } catch (ExceptionCompile $e) { 19304fd306cSNickeau LogUtility::error("We could not create a background image. Error: {$e->getMessage()}"); 19404fd306cSNickeau return; 19504fd306cSNickeau } 19604fd306cSNickeau 19704fd306cSNickeau try { 19804fd306cSNickeau $imageFetcher = $mediaMarkup->getFetcher(); 19904fd306cSNickeau } catch (ExceptionBadArgument|ExceptionInternal|ExceptionNotFound $e) { 20004fd306cSNickeau LogUtility::internalError("The fetcher for the background image ($mediaMarkup) returns an error", self::CANONICAL, $e); 20104fd306cSNickeau return; 20204fd306cSNickeau } 20304fd306cSNickeau $mime = $imageFetcher->getMime(); 20404fd306cSNickeau if (!$mime->isImage()) { 20504fd306cSNickeau LogUtility::error("The background image ($mediaMarkup) is not an image but a $mime", self::CANONICAL); 20604fd306cSNickeau return; 20704fd306cSNickeau } 20804fd306cSNickeau $backgroundImageStyleValue = "url(" . $imageFetcher->getFetchUrl()->toAbsoluteUrl()->toCssString() . ")"; 20904fd306cSNickeau 21004fd306cSNickeau } else { 21104fd306cSNickeau LogUtility::msg("Internal Error: The background image value ($backgroundImageValue) is not a string nor an array", LogUtility::LVL_MSG_ERROR, self::CANONICAL); 21204fd306cSNickeau } 21304fd306cSNickeau 21404fd306cSNickeau } 21504fd306cSNickeau } 21604fd306cSNickeau if (!empty($backgroundImageStyleValue)) { 21704fd306cSNickeau if ($tagAttributes->hasComponentAttribute(Opacity::OPACITY_ATTRIBUTE)) { 21804fd306cSNickeau $opacity = $tagAttributes->getValueAndRemove(Opacity::OPACITY_ATTRIBUTE); 21904fd306cSNickeau $finalOpacity = 1 - $opacity; 22004fd306cSNickeau /** 22104fd306cSNickeau * In the argument of linear-gradient, we don't use `0 100%` to apply the 22204fd306cSNickeau * same image everywhere 22304fd306cSNickeau * 22404fd306cSNickeau * because the validator https://validator.w3.org/ would complain with 22504fd306cSNickeau * ` 22604fd306cSNickeau * CSS: background-image: too few values for the property linear-gradient. 22704fd306cSNickeau * ` 22804fd306cSNickeau */ 22904fd306cSNickeau $backgroundImageStyleValue = "linear-gradient(to right, rgba(255,255,255, $finalOpacity) 0 50%, rgba(255,255,255, $finalOpacity) 50% 100%)," . $backgroundImageStyleValue; 23004fd306cSNickeau } 23104fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_IMAGE, $backgroundImageStyleValue); 23204fd306cSNickeau } 23304fd306cSNickeau 23404fd306cSNickeau 23504fd306cSNickeau /** 23604fd306cSNickeau * Process the pattern css 23704fd306cSNickeau * https://bansal.io/pattern-css 23804fd306cSNickeau * This call should be before the processing of the background color 23904fd306cSNickeau * because it will set one if it's not available 24004fd306cSNickeau */ 24104fd306cSNickeau self::processPatternAttribute($tagAttributes); 24204fd306cSNickeau 24304fd306cSNickeau /** 24404fd306cSNickeau * Background color 24504fd306cSNickeau */ 24604fd306cSNickeau if ($tagAttributes->hasComponentAttribute(self::BACKGROUND_COLOR)) { 24704fd306cSNickeau 24804fd306cSNickeau $colorValue = $tagAttributes->getValueAndRemove(self::BACKGROUND_COLOR); 24904fd306cSNickeau 25004fd306cSNickeau $gradientPrefix = 'gradient-'; 25104fd306cSNickeau if (strpos($colorValue, $gradientPrefix) === 0) { 25204fd306cSNickeau /** 25304fd306cSNickeau * A gradient is an image 25404fd306cSNickeau * Check that there is no image 25504fd306cSNickeau */ 25604fd306cSNickeau if (!empty($backgroundImageStyleValue)) { 25704fd306cSNickeau LogUtility::msg("An image and a linear gradient color are exclusive because a linear gradient color creates an image. You can't use the linear color (" . $colorValue . ") and the image (" . $backgroundImageStyleValue . ")", LogUtility::LVL_MSG_WARNING, self::CANONICAL); 25804fd306cSNickeau } else { 25904fd306cSNickeau $mainColorValue = substr($colorValue, strlen($gradientPrefix)); 26004fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_IMAGE, 'linear-gradient(to top,#fff 0,' . ColorRgb::createFromString($mainColorValue)->toCssValue() . ' 100%)'); 26104fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_COLOR, 'unset!important'); 26204fd306cSNickeau } 26304fd306cSNickeau } else { 26404fd306cSNickeau 26504fd306cSNickeau $colorValue = ColorRgb::createFromString($colorValue)->toCssValue(); 26604fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_COLOR, $colorValue); 26704fd306cSNickeau 26804fd306cSNickeau } 26904fd306cSNickeau } 27004fd306cSNickeau 27104fd306cSNickeau 27204fd306cSNickeau } 27304fd306cSNickeau 27404fd306cSNickeau /** 27504fd306cSNickeau * Return a background array with background properties 27604fd306cSNickeau * from a media {@link MediaLink::toCallStackArray()} 27704fd306cSNickeau * @param array $mediaCallStackArray 27804fd306cSNickeau * @return array 27904fd306cSNickeau */ 28004fd306cSNickeau public static function fromMediaToBackgroundImageStackArray(array $mediaCallStackArray): array 28104fd306cSNickeau { 28204fd306cSNickeau /** 28304fd306cSNickeau * This attributes should no be taken 28404fd306cSNickeau */ 28504fd306cSNickeau $mediaCallStackArray[MediaMarkup::LINKING_KEY] = LazyLoad::LAZY_LOAD_METHOD_NONE_VALUE; 28604fd306cSNickeau $mediaCallStackArray[Align::ALIGN_ATTRIBUTE] = null; 28704fd306cSNickeau $mediaCallStackArray[TagAttributes::TITLE_KEY] = null; // not sure why 28804fd306cSNickeau return $mediaCallStackArray; 28904fd306cSNickeau 29004fd306cSNickeau } 29104fd306cSNickeau 29204fd306cSNickeau /** 29304fd306cSNickeau * @param TagAttributes $tagAttributes 29404fd306cSNickeau * Process the `pattern` attribute 29504fd306cSNickeau * that implements 29604fd306cSNickeau * https://bansal.io/pattern-css 29704fd306cSNickeau */ 29804fd306cSNickeau private static function processPatternAttribute(TagAttributes &$tagAttributes) 29904fd306cSNickeau { 30004fd306cSNickeau /** 30104fd306cSNickeau * Css Pattern 30204fd306cSNickeau */ 30304fd306cSNickeau if ($tagAttributes->hasComponentAttribute(self::PATTERN_ATTRIBUTE)) { 30404fd306cSNickeau 30504fd306cSNickeau /** 30604fd306cSNickeau * Attach the stylesheet 30704fd306cSNickeau */ 30804fd306cSNickeau PluginUtility::getSnippetManager()->attachRemoteCssStyleSheet( 30904fd306cSNickeau self::PATTERN_CSS_SNIPPET_ID, 31004fd306cSNickeau "https://cdn.jsdelivr.net/npm/pattern.css@1.0.0/dist/pattern.min.css", 31104fd306cSNickeau "sha256-Vwich3JPJa27TO9g6q+TxJGE7DNEigBaHNPm+KkMR6o=") 31204fd306cSNickeau ->setCritical(false); // not blocking for rendering 31304fd306cSNickeau 31404fd306cSNickeau 31504fd306cSNickeau $patternValue = strtolower($tagAttributes->getValueAndRemove(self::PATTERN_ATTRIBUTE)); 31604fd306cSNickeau 31704fd306cSNickeau $lastIndexOfMinus = StringUtility::lastIndexOf($patternValue, "-"); 31804fd306cSNickeau $lastMinusPart = substr($patternValue, $lastIndexOfMinus); 31904fd306cSNickeau /** 32004fd306cSNickeau * Do we have the size as last part 32104fd306cSNickeau */ 32204fd306cSNickeau if (!in_array($lastMinusPart, self::PATTERN_CSS_SIZE)) { 32304fd306cSNickeau /** 32404fd306cSNickeau * We don't have a size 32504fd306cSNickeau */ 32604fd306cSNickeau $pattern = $patternValue; 32704fd306cSNickeau $size = "md"; 32804fd306cSNickeau } else { 32904fd306cSNickeau $pattern = substr($patternValue, 0, $lastIndexOfMinus); 33004fd306cSNickeau $size = $lastMinusPart; 33104fd306cSNickeau } 33204fd306cSNickeau /** 33304fd306cSNickeau * Does this pattern is a known pattern 33404fd306cSNickeau */ 33504fd306cSNickeau if (!in_array($pattern, self::PATTERN_NAMES)) { 33604fd306cSNickeau LogUtility::msg("The pattern (" . $pattern . ") is not a known CSS pattern and was ignored.", LogUtility::LVL_MSG_WARNING, self::CANONICAL); 33704fd306cSNickeau return; 33804fd306cSNickeau } else { 33904fd306cSNickeau $tagAttributes->addClassName(self::PATTERN_CSS_CLASS_PREFIX . "-" . $pattern . "-" . $size); 34004fd306cSNickeau } 34104fd306cSNickeau 34204fd306cSNickeau if (!$tagAttributes->hasComponentAttribute(self::BACKGROUND_COLOR)) { 34304fd306cSNickeau LogUtility::msg("The background color was not set for the background with the (" . $pattern . "). It was set to the default color.", LogUtility::LVL_MSG_INFO, self::CANONICAL); 34404fd306cSNickeau $tagAttributes->addComponentAttributeValue(self::BACKGROUND_COLOR, "steelblue"); 34504fd306cSNickeau } 34604fd306cSNickeau /** 34704fd306cSNickeau * Color 34804fd306cSNickeau */ 34904fd306cSNickeau if ($tagAttributes->hasComponentAttribute(self::PATTERN_COLOR_ATTRIBUTE)) { 35004fd306cSNickeau $patternColor = $tagAttributes->getValueAndRemove(self::PATTERN_COLOR_ATTRIBUTE); 35104fd306cSNickeau } else { 35204fd306cSNickeau LogUtility::msg("The pattern color was not set for the background with the (" . $pattern . "). It was set to the default color.", LogUtility::LVL_MSG_INFO, self::CANONICAL); 35304fd306cSNickeau $patternColor = "#FDE482"; 35404fd306cSNickeau } 35504fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet(ColorRgb::COLOR, $patternColor); 35604fd306cSNickeau 35704fd306cSNickeau } 35804fd306cSNickeau } 35904fd306cSNickeau 36004fd306cSNickeau 36104fd306cSNickeau} 362