137748cd8SNickeau<?php 237748cd8SNickeau 337748cd8SNickeau 437748cd8SNickeaunamespace ComboStrap; 537748cd8SNickeau 637748cd8SNickeau 737748cd8SNickeauuse dokuwiki\Extension\SyntaxPlugin; 837748cd8SNickeauuse syntax_plugin_combo_button; 937748cd8SNickeauuse syntax_plugin_combo_link; 1082a60d03SNickeauuse syntax_plugin_combo_pageimage; 1137748cd8SNickeau 1237748cd8SNickeauclass Dimension 1337748cd8SNickeau{ 1437748cd8SNickeau /** 1537748cd8SNickeau * The element that have an width and height 1637748cd8SNickeau */ 1737748cd8SNickeau const NATURAL_SIZING_ELEMENT = [SvgImageLink::CANONICAL, RasterImageLink::CANONICAL]; 1837748cd8SNickeau 1937748cd8SNickeau const DESIGN_LAYOUT_CONSTRAINED = "constrained"; // fix value 2037748cd8SNickeau const DESIGN_LAYOUT_FLUID = "fluid"; // adapt 2137748cd8SNickeau 2237748cd8SNickeau /** 2337748cd8SNickeau * On the width, if set, the design is fluid and will adapt to all screen 2437748cd8SNickeau * with a min-width 2537748cd8SNickeau */ 2637748cd8SNickeau const WIDTH_LAYOUT_DEFAULT = self::DESIGN_LAYOUT_FLUID; 2737748cd8SNickeau /** 2837748cd8SNickeau * On height, if set, the design is constrained and overflow 2937748cd8SNickeau */ 3037748cd8SNickeau const HEIGHT_LAYOUT_DEFAULT = self::DESIGN_LAYOUT_CONSTRAINED; 3137748cd8SNickeau const SCROLL = "scroll"; 3282a60d03SNickeau 3382a60d03SNickeau /** 3482a60d03SNickeau * Logical height and width 3582a60d03SNickeau * used by default to define the width and height of an image or a css box 3682a60d03SNickeau */ 3737748cd8SNickeau const HEIGHT_KEY = 'height'; 3837748cd8SNickeau const WIDTH_KEY = 'width'; 3937748cd8SNickeau 4082a60d03SNickeau /** 4182a60d03SNickeau * The ratio (16:9, ...) permits to change: 4282a60d03SNickeau * * the viewBox in svg 4382a60d03SNickeau * * the intrinsic dimension in raster 4482a60d03SNickeau * 4582a60d03SNickeau * It's then part of the request 4682a60d03SNickeau * because in svg it is the definition of the viewBox 4782a60d03SNickeau * 4882a60d03SNickeau * The rendering function takes care of it 4982a60d03SNickeau * and it's also passed in the fetch url 5082a60d03SNickeau */ 5182a60d03SNickeau public const RATIO_ATTRIBUTE = "ratio"; 52*4cadd4f8SNickeau const ZOOM_ATTRIBUTE = "zoom"; 5382a60d03SNickeau 5437748cd8SNickeau 5537748cd8SNickeau /** 5637748cd8SNickeau * @param TagAttributes $attributes 5737748cd8SNickeau */ 58*4cadd4f8SNickeau public static function processWidthAndHeight(TagAttributes &$attributes) 5937748cd8SNickeau { 6037748cd8SNickeau $widthName = self::WIDTH_KEY; 6137748cd8SNickeau if ($attributes->hasComponentAttribute($widthName)) { 6237748cd8SNickeau 6337748cd8SNickeau $widthValue = trim($attributes->getValueAndRemove($widthName)); 6437748cd8SNickeau 6537748cd8SNickeau if ($widthValue == "0") { 6637748cd8SNickeau 6737748cd8SNickeau /** 6837748cd8SNickeau * For an image, the dimension are restricted by height 6937748cd8SNickeau */ 7037748cd8SNickeau if ($attributes->hasComponentAttribute(self::HEIGHT_KEY)) { 7182a60d03SNickeau $attributes->addStyleDeclarationIfNotSet("width", "auto"); 7237748cd8SNickeau } 7337748cd8SNickeau 7437748cd8SNickeau } else { 7537748cd8SNickeau 7637748cd8SNickeau 7737748cd8SNickeau if ($widthValue == "fit") { 7837748cd8SNickeau $widthValue = "fit-content"; 7937748cd8SNickeau } else { 8037748cd8SNickeau /** Numeric value */ 8137748cd8SNickeau $widthValue = TagAttributes::toQualifiedCssValue($widthValue); 8237748cd8SNickeau } 8337748cd8SNickeau 8437748cd8SNickeau 8537748cd8SNickeau /** 8682a60d03SNickeau * For an image (png, svg) 8782a60d03SNickeau * They have width and height **element** attribute 8837748cd8SNickeau */ 8937748cd8SNickeau if (in_array($attributes->getLogicalTag(), self::NATURAL_SIZING_ELEMENT)) { 9037748cd8SNickeau 9137748cd8SNickeau /** 9237748cd8SNickeau * If the image is not ask as static resource (ie HTTP request) 9337748cd8SNickeau * but added in HTML 9437748cd8SNickeau * (ie {@link \action_plugin_combo_svg}) 9537748cd8SNickeau */ 9637748cd8SNickeau $requestedMime = $attributes->getMime(); 9737748cd8SNickeau if ($requestedMime == TagAttributes::TEXT_HTML_MIME) { 9882a60d03SNickeau $attributes->addStyleDeclarationIfNotSet('max-width', $widthValue); 9982a60d03SNickeau $attributes->addStyleDeclarationIfNotSet('width', "100%"); 10037748cd8SNickeau } 10137748cd8SNickeau 10237748cd8SNickeau } else { 10337748cd8SNickeau 10437748cd8SNickeau /** 10537748cd8SNickeau * For a block 10637748cd8SNickeau */ 10782a60d03SNickeau $attributes->addStyleDeclarationIfNotSet('max-width', $widthValue); 10837748cd8SNickeau 10937748cd8SNickeau } 11037748cd8SNickeau } 11137748cd8SNickeau 11237748cd8SNickeau } 11337748cd8SNickeau 11437748cd8SNickeau $heightName = self::HEIGHT_KEY; 11537748cd8SNickeau if ($attributes->hasComponentAttribute($heightName)) { 11637748cd8SNickeau $heightValue = trim($attributes->getValueAndRemove($heightName)); 11737748cd8SNickeau if ($heightValue !== "") { 11837748cd8SNickeau $heightValue = TagAttributes::toQualifiedCssValue($heightValue); 11937748cd8SNickeau 12037748cd8SNickeau if (in_array($attributes->getLogicalTag(), self::NATURAL_SIZING_ELEMENT)) { 12137748cd8SNickeau 12237748cd8SNickeau /** 12337748cd8SNickeau * A element with a natural height is responsive, we set only the max-height 12437748cd8SNickeau * 12537748cd8SNickeau * By default, the image has a `height: auto` due to the img-fluid class 12637748cd8SNickeau * Making its height responsive 12737748cd8SNickeau */ 12882a60d03SNickeau $attributes->addStyleDeclarationIfNotSet("max-height", $heightValue); 12937748cd8SNickeau 13037748cd8SNickeau } else { 13137748cd8SNickeau 13237748cd8SNickeau /** 13337748cd8SNickeau * HTML Block 13437748cd8SNickeau * 13537748cd8SNickeau * Without the height value, a block display will collapse 13637748cd8SNickeau */ 13737748cd8SNickeau if (self::HEIGHT_LAYOUT_DEFAULT == self::DESIGN_LAYOUT_CONSTRAINED) { 13837748cd8SNickeau 13937748cd8SNickeau /** 14037748cd8SNickeau * The box is constrained in height 14137748cd8SNickeau * By default, a box is not constrained 14237748cd8SNickeau */ 14382a60d03SNickeau $attributes->addStyleDeclarationIfNotSet("height", $heightValue); 14437748cd8SNickeau 14537748cd8SNickeau $scrollMechanism = $attributes->getValueAndRemoveIfPresent("scroll"); 14637748cd8SNickeau if ($scrollMechanism != null) { 14737748cd8SNickeau $scrollMechanism = trim(strtolower($scrollMechanism)); 14837748cd8SNickeau } 14937748cd8SNickeau switch ($scrollMechanism) { 15037748cd8SNickeau case "toggle": 15137748cd8SNickeau // https://jsfiddle.net/gerardnico/h0g6xw58/ 15282a60d03SNickeau $attributes->addStyleDeclarationIfNotSet("overflow-y", "hidden"); 15382a60d03SNickeau $attributes->addStyleDeclarationIfNotSet("position", "relative"); 15482a60d03SNickeau $attributes->addStyleDeclarationIfNotSet("display", "block"); 15537748cd8SNickeau // The block should collapse to this height 15682a60d03SNickeau $attributes->addStyleDeclarationIfNotSet("min-height", $heightValue); 15737748cd8SNickeau if ($attributes->hasComponentAttribute("id")) { 15837748cd8SNickeau $id = $attributes->getValue("id"); 15937748cd8SNickeau } else { 16037748cd8SNickeau $id = $attributes->generateAndSetId(); 16137748cd8SNickeau } 16237748cd8SNickeau /** 16337748cd8SNickeau * Css of the button and other standard attribute 16437748cd8SNickeau */ 165*4cadd4f8SNickeau PluginUtility::getSnippetManager()->attachCssInternalStyleSheetForSlot("height-toggle"); 16637748cd8SNickeau /** 16737748cd8SNickeau * Set the color dynamically to the color of the parent 16837748cd8SNickeau */ 169*4cadd4f8SNickeau PluginUtility::getSnippetManager()->attachInternalJavascriptForSlot("height-toggle"); 17037748cd8SNickeau /** 17137748cd8SNickeau * The height when there is not the show class 17237748cd8SNickeau * is the original height 17337748cd8SNickeau */ 17437748cd8SNickeau $css = <<<EOF 17537748cd8SNickeau#$id:not(.show){ 17637748cd8SNickeau height: $heightValue; 17737748cd8SNickeau transition: height .35s ease; 17837748cd8SNickeau} 17937748cd8SNickeauEOF; 180*4cadd4f8SNickeau PluginUtility::getSnippetManager()->attachCssInternalStyleSheetForSlot("height-toggle-show", $css); 18137748cd8SNickeau $bootstrapDataNameSpace = Bootstrap::getDataNamespace(); 18237748cd8SNickeau $button = <<<EOF 18337748cd8SNickeau<button class="height-toggle-combo" data$bootstrapDataNameSpace-toggle="collapse" data$bootstrapDataNameSpace-target="#$id" aria-expanded="false"></button> 18437748cd8SNickeauEOF; 18537748cd8SNickeau 18637748cd8SNickeau $attributes->addHtmlAfterEnterTag($button); 18737748cd8SNickeau 18837748cd8SNickeau break; 18937748cd8SNickeau case "lift"; 19037748cd8SNickeau default: 19182a60d03SNickeau $attributes->addStyleDeclarationIfNotSet("overflow", "auto"); 19237748cd8SNickeau break; 19337748cd8SNickeau 19437748cd8SNickeau } 19537748cd8SNickeau 19637748cd8SNickeau 19737748cd8SNickeau } else { 19837748cd8SNickeau 19937748cd8SNickeau /** 20037748cd8SNickeau * if fluid 20137748cd8SNickeau * min-height and not height to not constraint the box 20237748cd8SNickeau */ 20382a60d03SNickeau $attributes->addStyleDeclarationIfNotSet("min-height", $heightValue); 20437748cd8SNickeau 20537748cd8SNickeau } 20637748cd8SNickeau } 20737748cd8SNickeau } 20837748cd8SNickeau 20937748cd8SNickeau } 21037748cd8SNickeau } 21137748cd8SNickeau 21237748cd8SNickeau /** 21337748cd8SNickeau * 21482a60d03SNickeau * Toggle with a click on the collapsed element 21537748cd8SNickeau * if there is no control element such as button or link inside 21637748cd8SNickeau * 21737748cd8SNickeau * This function is used at the {@link DOKU_LEXER_EXIT} state of a {@link SyntaxPlugin::handle()} 21837748cd8SNickeau * 21937748cd8SNickeau * @param CallStack $callStack 22037748cd8SNickeau */ 22137748cd8SNickeau public static function addScrollToggleOnClickIfNoControl(CallStack $callStack) 22237748cd8SNickeau { 22337748cd8SNickeau $callStack->moveToEnd(); 22437748cd8SNickeau $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall(); 22537748cd8SNickeau $scrollAttribute = $openingCall->getAttribute(Dimension::SCROLL); 22637748cd8SNickeau if ($scrollAttribute != null && $scrollAttribute == "toggle") { 22737748cd8SNickeau 22837748cd8SNickeau $controlFound = false; 22937748cd8SNickeau while ($actualCall = $callStack->next()) { 23037748cd8SNickeau if (in_array($actualCall->getTagName(), 23137748cd8SNickeau [syntax_plugin_combo_button::TAG, syntax_plugin_combo_link::TAG, "internallink", "externallink"])) { 23237748cd8SNickeau $controlFound = true; 23337748cd8SNickeau break; 23437748cd8SNickeau } 23537748cd8SNickeau } 23637748cd8SNickeau if (!$controlFound) { 23737748cd8SNickeau $toggleOnClickId = "height-toggle-onclick"; 238*4cadd4f8SNickeau PluginUtility::getSnippetManager()->attachInternalJavascriptForSlot($toggleOnClickId); 23937748cd8SNickeau $openingCall->addClassName("{$toggleOnClickId}-combo"); 24037748cd8SNickeau $openingCall->addCssStyle("cursor", "pointer"); 24137748cd8SNickeau } 24237748cd8SNickeau 24337748cd8SNickeau } 24437748cd8SNickeau } 24582a60d03SNickeau 24682a60d03SNickeau /** 24782a60d03SNickeau * @param $value - a css value to a pixel 24882a60d03SNickeau * @throws ExceptionCombo 24982a60d03SNickeau */ 25082a60d03SNickeau public static function toPixelValue($value): int 25182a60d03SNickeau { 252*4cadd4f8SNickeau 253*4cadd4f8SNickeau preg_match("/[a-z]/i", $value, $matches, PREG_OFFSET_CAPTURE); 254*4cadd4f8SNickeau $unit = null; 255*4cadd4f8SNickeau if (sizeof($matches) > 0) { 256*4cadd4f8SNickeau $firstPosition = $matches[0][1]; 257*4cadd4f8SNickeau $unit = strtolower(substr($value, $firstPosition)); 258*4cadd4f8SNickeau $value = DataType::toFloat((substr($value, 0, $firstPosition))); 259*4cadd4f8SNickeau } 260*4cadd4f8SNickeau switch ($unit) { 261*4cadd4f8SNickeau case "rem": 262*4cadd4f8SNickeau $remValue = Site::getRem(); 263*4cadd4f8SNickeau $targetValue = $value * $remValue; 264*4cadd4f8SNickeau break; 265*4cadd4f8SNickeau case "px": 266*4cadd4f8SNickeau default: 267*4cadd4f8SNickeau $targetValue = $value; 268*4cadd4f8SNickeau } 26982a60d03SNickeau return DataType::toInteger($targetValue); 27082a60d03SNickeau } 27182a60d03SNickeau 27282a60d03SNickeau /** 27382a60d03SNickeau * Convert 16:9, ... to a float 27482a60d03SNickeau * @param string $stringRatio 27582a60d03SNickeau * @return float 27682a60d03SNickeau * @throws ExceptionCombo 27782a60d03SNickeau */ 27882a60d03SNickeau public static function convertTextualRatioToNumber(string $stringRatio): float 27982a60d03SNickeau { 28082a60d03SNickeau list($width, $height) = explode(":", $stringRatio, 2); 28182a60d03SNickeau try { 28282a60d03SNickeau $width = DataType::toInteger($width); 28382a60d03SNickeau } catch (ExceptionCombo $e) { 28482a60d03SNickeau throw new ExceptionCombo("The width value ($width) of the ratio `$stringRatio` is not numeric", syntax_plugin_combo_pageimage::CANONICAL); 28582a60d03SNickeau } 28682a60d03SNickeau try { 28782a60d03SNickeau $height = DataType::toInteger($height); 28882a60d03SNickeau } catch (ExceptionCombo $e) { 28982a60d03SNickeau throw new ExceptionCombo("The width value ($height) of the ratio `$stringRatio` is not numeric", syntax_plugin_combo_pageimage::CANONICAL); 29082a60d03SNickeau } 29182a60d03SNickeau if ($height == 0) { 29282a60d03SNickeau throw new ExceptionCombo("The height value of the ratio `$stringRatio` should not be zero", syntax_plugin_combo_pageimage::CANONICAL); 29382a60d03SNickeau } 29482a60d03SNickeau return floatval($width / $height); 29582a60d03SNickeau 29682a60d03SNickeau } 29782a60d03SNickeau 29882a60d03SNickeau 29937748cd8SNickeau} 300