1<?php 2 3 4namespace ComboStrap; 5 6 7use dokuwiki\Extension\SyntaxPlugin; 8use syntax_plugin_combo_button; 9use syntax_plugin_combo_link; 10use syntax_plugin_combo_pageimage; 11 12class Dimension 13{ 14 /** 15 * The element that have an width and height 16 */ 17 const NATURAL_SIZING_ELEMENT = [SvgImageLink::CANONICAL, RasterImageLink::CANONICAL]; 18 19 const DESIGN_LAYOUT_CONSTRAINED = "constrained"; // fix value 20 const DESIGN_LAYOUT_FLUID = "fluid"; // adapt 21 22 /** 23 * On the width, if set, the design is fluid and will adapt to all screen 24 * with a min-width 25 */ 26 const WIDTH_LAYOUT_DEFAULT = self::DESIGN_LAYOUT_FLUID; 27 /** 28 * On height, if set, the design is constrained and overflow 29 */ 30 const HEIGHT_LAYOUT_DEFAULT = self::DESIGN_LAYOUT_CONSTRAINED; 31 const SCROLL = "scroll"; 32 33 /** 34 * Logical height and width 35 * used by default to define the width and height of an image or a css box 36 */ 37 const HEIGHT_KEY = 'height'; 38 const WIDTH_KEY = 'width'; 39 40 /** 41 * The ratio (16:9, ...) permits to change: 42 * * the viewBox in svg 43 * * the intrinsic dimension in raster 44 * 45 * It's then part of the request 46 * because in svg it is the definition of the viewBox 47 * 48 * The rendering function takes care of it 49 * and it's also passed in the fetch url 50 */ 51 public const RATIO_ATTRIBUTE = "ratio"; 52 const ZOOM_ATTRIBUTE = "zoom"; 53 54 55 /** 56 * @param TagAttributes $attributes 57 */ 58 public static function processWidthAndHeight(TagAttributes &$attributes) 59 { 60 $widthName = self::WIDTH_KEY; 61 if ($attributes->hasComponentAttribute($widthName)) { 62 63 $widthValue = trim($attributes->getValueAndRemove($widthName)); 64 65 if ($widthValue == "0") { 66 67 /** 68 * For an image, the dimension are restricted by height 69 */ 70 if ($attributes->hasComponentAttribute(self::HEIGHT_KEY)) { 71 $attributes->addStyleDeclarationIfNotSet("width", "auto"); 72 } 73 74 } else { 75 76 77 if ($widthValue == "fit") { 78 $widthValue = "fit-content"; 79 } else { 80 /** Numeric value */ 81 $widthValue = TagAttributes::toQualifiedCssValue($widthValue); 82 } 83 84 85 /** 86 * For an image (png, svg) 87 * They have width and height **element** attribute 88 */ 89 if (in_array($attributes->getLogicalTag(), self::NATURAL_SIZING_ELEMENT)) { 90 91 /** 92 * If the image is not ask as static resource (ie HTTP request) 93 * but added in HTML 94 * (ie {@link \action_plugin_combo_svg}) 95 */ 96 $requestedMime = $attributes->getMime(); 97 if ($requestedMime == TagAttributes::TEXT_HTML_MIME) { 98 $attributes->addStyleDeclarationIfNotSet('max-width', $widthValue); 99 $attributes->addStyleDeclarationIfNotSet('width', "100%"); 100 } 101 102 } else { 103 104 /** 105 * For a block 106 */ 107 $attributes->addStyleDeclarationIfNotSet('max-width', $widthValue); 108 109 } 110 } 111 112 } 113 114 $heightName = self::HEIGHT_KEY; 115 if ($attributes->hasComponentAttribute($heightName)) { 116 $heightValue = trim($attributes->getValueAndRemove($heightName)); 117 if ($heightValue !== "") { 118 $heightValue = TagAttributes::toQualifiedCssValue($heightValue); 119 120 if (in_array($attributes->getLogicalTag(), self::NATURAL_SIZING_ELEMENT)) { 121 122 /** 123 * A element with a natural height is responsive, we set only the max-height 124 * 125 * By default, the image has a `height: auto` due to the img-fluid class 126 * Making its height responsive 127 */ 128 $attributes->addStyleDeclarationIfNotSet("max-height", $heightValue); 129 130 } else { 131 132 /** 133 * HTML Block 134 * 135 * Without the height value, a block display will collapse 136 */ 137 if (self::HEIGHT_LAYOUT_DEFAULT == self::DESIGN_LAYOUT_CONSTRAINED) { 138 139 /** 140 * The box is constrained in height 141 * By default, a box is not constrained 142 */ 143 $attributes->addStyleDeclarationIfNotSet("height", $heightValue); 144 145 $scrollMechanism = $attributes->getValueAndRemoveIfPresent("scroll"); 146 if ($scrollMechanism != null) { 147 $scrollMechanism = trim(strtolower($scrollMechanism)); 148 } 149 switch ($scrollMechanism) { 150 case "toggle": 151 // https://jsfiddle.net/gerardnico/h0g6xw58/ 152 $attributes->addStyleDeclarationIfNotSet("overflow-y", "hidden"); 153 $attributes->addStyleDeclarationIfNotSet("position", "relative"); 154 $attributes->addStyleDeclarationIfNotSet("display", "block"); 155 // The block should collapse to this height 156 $attributes->addStyleDeclarationIfNotSet("min-height", $heightValue); 157 if ($attributes->hasComponentAttribute("id")) { 158 $id = $attributes->getValue("id"); 159 } else { 160 $id = $attributes->generateAndSetId(); 161 } 162 /** 163 * Css of the button and other standard attribute 164 */ 165 PluginUtility::getSnippetManager()->attachCssInternalStyleSheetForSlot("height-toggle"); 166 /** 167 * Set the color dynamically to the color of the parent 168 */ 169 PluginUtility::getSnippetManager()->attachInternalJavascriptForSlot("height-toggle"); 170 /** 171 * The height when there is not the show class 172 * is the original height 173 */ 174 $css = <<<EOF 175#$id:not(.show){ 176 height: $heightValue; 177 transition: height .35s ease; 178} 179EOF; 180 PluginUtility::getSnippetManager()->attachCssInternalStyleSheetForSlot("height-toggle-show", $css); 181 $bootstrapDataNameSpace = Bootstrap::getDataNamespace(); 182 $button = <<<EOF 183<button class="height-toggle-combo" data$bootstrapDataNameSpace-toggle="collapse" data$bootstrapDataNameSpace-target="#$id" aria-expanded="false"></button> 184EOF; 185 186 $attributes->addHtmlAfterEnterTag($button); 187 188 break; 189 case "lift"; 190 default: 191 $attributes->addStyleDeclarationIfNotSet("overflow", "auto"); 192 break; 193 194 } 195 196 197 } else { 198 199 /** 200 * if fluid 201 * min-height and not height to not constraint the box 202 */ 203 $attributes->addStyleDeclarationIfNotSet("min-height", $heightValue); 204 205 } 206 } 207 } 208 209 } 210 } 211 212 /** 213 * 214 * Toggle with a click on the collapsed element 215 * if there is no control element such as button or link inside 216 * 217 * This function is used at the {@link DOKU_LEXER_EXIT} state of a {@link SyntaxPlugin::handle()} 218 * 219 * @param CallStack $callStack 220 */ 221 public static function addScrollToggleOnClickIfNoControl(CallStack $callStack) 222 { 223 $callStack->moveToEnd(); 224 $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall(); 225 $scrollAttribute = $openingCall->getAttribute(Dimension::SCROLL); 226 if ($scrollAttribute != null && $scrollAttribute == "toggle") { 227 228 $controlFound = false; 229 while ($actualCall = $callStack->next()) { 230 if (in_array($actualCall->getTagName(), 231 [syntax_plugin_combo_button::TAG, syntax_plugin_combo_link::TAG, "internallink", "externallink"])) { 232 $controlFound = true; 233 break; 234 } 235 } 236 if (!$controlFound) { 237 $toggleOnClickId = "height-toggle-onclick"; 238 PluginUtility::getSnippetManager()->attachInternalJavascriptForSlot($toggleOnClickId); 239 $openingCall->addClassName("{$toggleOnClickId}-combo"); 240 $openingCall->addCssStyle("cursor", "pointer"); 241 } 242 243 } 244 } 245 246 /** 247 * @param $value - a css value to a pixel 248 * @throws ExceptionCombo 249 */ 250 public static function toPixelValue($value): int 251 { 252 253 preg_match("/[a-z]/i", $value, $matches, PREG_OFFSET_CAPTURE); 254 $unit = null; 255 if (sizeof($matches) > 0) { 256 $firstPosition = $matches[0][1]; 257 $unit = strtolower(substr($value, $firstPosition)); 258 $value = DataType::toFloat((substr($value, 0, $firstPosition))); 259 } 260 switch ($unit) { 261 case "rem": 262 $remValue = Site::getRem(); 263 $targetValue = $value * $remValue; 264 break; 265 case "px": 266 default: 267 $targetValue = $value; 268 } 269 return DataType::toInteger($targetValue); 270 } 271 272 /** 273 * Convert 16:9, ... to a float 274 * @param string $stringRatio 275 * @return float 276 * @throws ExceptionCombo 277 */ 278 public static function convertTextualRatioToNumber(string $stringRatio): float 279 { 280 list($width, $height) = explode(":", $stringRatio, 2); 281 try { 282 $width = DataType::toInteger($width); 283 } catch (ExceptionCombo $e) { 284 throw new ExceptionCombo("The width value ($width) of the ratio `$stringRatio` is not numeric", syntax_plugin_combo_pageimage::CANONICAL); 285 } 286 try { 287 $height = DataType::toInteger($height); 288 } catch (ExceptionCombo $e) { 289 throw new ExceptionCombo("The width value ($height) of the ratio `$stringRatio` is not numeric", syntax_plugin_combo_pageimage::CANONICAL); 290 } 291 if ($height == 0) { 292 throw new ExceptionCombo("The height value of the ratio `$stringRatio` should not be zero", syntax_plugin_combo_pageimage::CANONICAL); 293 } 294 return floatval($width / $height); 295 296 } 297 298 299} 300