1<?php 2 3namespace ComboStrap\Tag; 4 5use ComboStrap\CallStack; 6use ComboStrap\ColorRgb; 7use ComboStrap\Dimension; 8use ComboStrap\FetcherSvg; 9use ComboStrap\IFetcherAbs; 10use ComboStrap\LinkMarkup; 11use ComboStrap\MarkupRef; 12use ComboStrap\MediaMarkup; 13use ComboStrap\PluginUtility; 14use ComboStrap\Position; 15use ComboStrap\TagAttribute\BackgroundAttribute; 16use ComboStrap\TagAttributes; 17use Doku_Renderer_metadata; 18use syntax_plugin_combo_media; 19 20 21/** 22 * The {@link BackgroundTag background tag} does not render as HTML tag 23 * but collects data to create a {@link BackgroundAttribute} 24 * on the parent node 25 * 26 * Implementation of a background 27 * 28 * 29 * Cool calm example of moving square background 30 * https://codepen.io/Lewitje/pen/BNNJjo 31 * Particles.js 32 * https://codepen.io/akey96/pen/oNgeQYX 33 * Gradient positioning above a photo 34 * https://codepen.io/uzoawili/pen/GypGOy 35 * Fire flies 36 * https://codepen.io/mikegolus/pen/Jegvym 37 * 38 * z-index:100 could also be on the front 39 * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_without_z-index 40 * https://getbootstrap.com/docs/5.0/layout/z-index/ 41 */ 42class BackgroundTag 43{ 44 45 public const MARKUP_LONG = "background"; 46 public const MARKUP_SHORT = "bg"; 47 const LOGICAL_TAG = self::MARKUP_LONG; 48 49 /** 50 * Function used in the special and enter tag 51 * @param TagAttributes $attributes 52 */ 53 public static function handleEnterAndSpecial(TagAttributes $attributes) 54 { 55 56 $color = $attributes->getValueAndRemoveIfPresent(ColorRgb::COLOR); 57 if ($color !== null) { 58 $attributes->addComponentAttributeValue(BackgroundAttribute::BACKGROUND_COLOR, $color); 59 } 60 61 } 62 63 /** 64 * @param CallStack $callStack 65 * @param TagAttributes $backgroundAttributes 66 * @param $state 67 * @return array 68 */ 69 public static function setAttributesToParentAndReturnData(CallStack $callStack, TagAttributes $backgroundAttributes, $state): array 70 { 71 72 /** 73 * The data array 74 */ 75 $data = array(); 76 77 /** 78 * Set the backgrounds attributes 79 * to the parent 80 * There is two state (special and exit) 81 * Go to the opening call if in exit 82 */ 83 if ($state == DOKU_LEXER_EXIT) { 84 $callStack->moveToEnd(); 85 $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall(); 86 } 87 $parentCall = $callStack->moveToParent(); 88 89 /** @noinspection PhpPointlessBooleanExpressionInConditionInspection */ 90 if ($parentCall != false) { 91 if ($parentCall->getTagName() == BackgroundAttribute::BACKGROUNDS) { 92 /** 93 * The backgrounds node 94 * (is already relative) 95 */ 96 $parentCall = $callStack->moveToParent(); 97 } else { 98 /** 99 * Another parent node 100 * With a image background, the node should be relative 101 */ 102 if ($backgroundAttributes->hasComponentAttribute(BackgroundAttribute::BACKGROUND_IMAGE)) { 103 $parentCall->addAttribute(Position::POSITION_ATTRIBUTE, "relative"); 104 } 105 } 106 $backgrounds = $parentCall->getAttribute(BackgroundAttribute::BACKGROUNDS); 107 if ($backgrounds == null) { 108 $backgrounds = [$backgroundAttributes->toCallStackArray()]; 109 } else { 110 $backgrounds[] = $backgroundAttributes->toCallStackArray(); 111 } 112 $parentCall->addAttribute(BackgroundAttribute::BACKGROUNDS, $backgrounds); 113 114 } else { 115 $data[PluginUtility::EXIT_MESSAGE] = "A background should have a parent"; 116 } 117 118 /** 119 * Return the image data for the metadata 120 * (Metadat is taken only from enter/exit) 121 */ 122 if ($state === DOKU_LEXER_EXIT && isset($openingCall)) { 123 // exit state 124 $backgroundImage = $backgroundAttributes->getComponentAttributeValue(BackgroundAttribute::BACKGROUND_IMAGE); 125 $openingCall->setAttribute(BackgroundAttribute::BACKGROUND_IMAGE, $backgroundImage); 126 } else { 127 // special state 128 $data[PluginUtility::ATTRIBUTES] = $backgroundAttributes->toCallStackArray(); 129 } 130 return $data; 131 132 } 133 134 /** 135 * Print only any error 136 */ 137 public static function renderExitSpecialHtml($data): string 138 { 139 140 if (isset($data[PluginUtility::EXIT_MESSAGE])) { 141 $class = LinkMarkup::TEXT_ERROR_CLASS; 142 $error = $data[PluginUtility::EXIT_MESSAGE]; 143 return "<p class=\"$class\">$error</p>" . DOKU_LF; 144 } 145 146 return ""; 147 } 148 149 public static function handleExit($handler): array 150 { 151 $callStack = CallStack::createFromHandler($handler); 152 $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 153 $backgroundAttributes = TagAttributes::createFromCallStackArray($openingTag->getAttributes()) 154 ->setLogicalTag(BackgroundTag::LOGICAL_TAG); 155 156 /** 157 * if the media syntax of Combo is not used, try to retrieve the media of dokuwiki 158 */ 159 $imageTag = [syntax_plugin_combo_media::TAG, MediaMarkup::INTERNAL_MEDIA_CALL_NAME]; 160 161 /** 162 * Collect the image if any 163 */ 164 while ($actual = $callStack->next()) { 165 166 $tagName = $actual->getTagName(); 167 if (in_array($tagName, $imageTag)) { 168 $imageAttribute = $actual->getAttributes(); 169 if ($tagName == syntax_plugin_combo_media::TAG) { 170 $backgroundImageAttribute = BackgroundAttribute::fromMediaToBackgroundImageStackArray($imageAttribute); 171 172 /** 173 * Hack for tile svg 174 */ 175 $fill = $openingTag->getAttribute(BackgroundAttribute::BACKGROUND_FILL); 176 if ($fill === FetcherSvg::TILE_TYPE) { 177 $ref = $backgroundImageAttribute[MarkupRef::REF_ATTRIBUTE]; 178 if (!str_contains($ref, TagAttributes::TYPE_KEY) && str_contains($ref, "svg")) { 179 if (str_contains($ref, "?")) { 180 $ref = "$ref&type=$fill"; 181 } else { 182 $ref = "$ref?type=$fill"; 183 } 184 } 185 $backgroundImageAttribute[MarkupRef::REF_ATTRIBUTE] = $ref; 186 } 187 } else { 188 /** 189 * As seen in {@link Doku_Handler::media()} 190 */ 191 $backgroundImageAttribute = [ 192 MediaMarkup::MEDIA_DOKUWIKI_TYPE => MediaMarkup::INTERNAL_MEDIA_CALL_NAME, 193 MediaMarkup::DOKUWIKI_SRC => $imageAttribute[0], 194 Dimension::WIDTH_KEY => $imageAttribute[3], 195 Dimension::HEIGHT_KEY => $imageAttribute[4], 196 IFetcherAbs::CACHE_KEY => $imageAttribute[5] 197 ]; 198 } 199 $backgroundAttributes->addComponentAttributeValue(BackgroundAttribute::BACKGROUND_IMAGE, $backgroundImageAttribute); 200 $callStack->deleteActualCallAndPrevious(); 201 } 202 } 203 return BackgroundTag::setAttributesToParentAndReturnData($callStack, $backgroundAttributes, DOKU_LEXER_EXIT); 204 } 205 206 public static function renderEnterTag(): string 207 { 208 /** 209 * background is printed via the {@link BackgroundAttribute::processBackgroundAttributes()} 210 */ 211 return ""; 212 } 213 214 public static function renderMeta(array $data, Doku_Renderer_metadata $renderer) 215 { 216 217 $attributes = $data[PluginUtility::ATTRIBUTES]; 218 if (isset($attributes[BackgroundAttribute::BACKGROUND_IMAGE])) { 219 $image = $attributes[BackgroundAttribute::BACKGROUND_IMAGE]; 220 syntax_plugin_combo_media::registerImageMeta($image, $renderer); 221 } 222 223 } 224} 225