elements. * * @since 1.16 */ class Html { /** @var bool[] List of void elements from HTML5, section 8.1.2 as of 2016-09-19 */ private static $voidElements = [ 'area' => true, 'base' => true, 'br' => true, 'col' => true, 'embed' => true, 'hr' => true, 'img' => true, 'input' => true, 'keygen' => true, 'link' => true, 'meta' => true, 'param' => true, 'source' => true, 'track' => true, 'wbr' => true, ]; /** * Boolean attributes, which may have the value omitted entirely. Manually * collected from the HTML5 spec as of 2011-08-12. * @var bool[] */ private static $boolAttribs = [ 'async' => true, 'autofocus' => true, 'autoplay' => true, 'checked' => true, 'controls' => true, 'default' => true, 'defer' => true, 'disabled' => true, 'formnovalidate' => true, 'hidden' => true, 'ismap' => true, 'itemscope' => true, 'loop' => true, 'multiple' => true, 'muted' => true, 'novalidate' => true, 'open' => true, 'pubdate' => true, 'readonly' => true, 'required' => true, 'reversed' => true, 'scoped' => true, 'seamless' => true, 'selected' => true, 'truespeed' => true, 'typemustmatch' => true, ]; /** * Modifies a set of attributes meant for button elements * and apply a set of default attributes when $wgUseMediaWikiUIEverywhere enabled. * @param array $attrs HTML attributes in an associative array * @param string[] $modifiers classes to add to the button * @see https://tools.wmflabs.org/styleguide/desktop/index.html for guidance on available modifiers * @return array Modified attributes array */ public static function buttonAttributes( array $attrs, array $modifiers = [] ) { $useMediaWikiUIEverywhere = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::UseMediaWikiUIEverywhere ); if ( $useMediaWikiUIEverywhere ) { if ( isset( $attrs['class'] ) ) { if ( is_array( $attrs['class'] ) ) { $attrs['class'][] = 'mw-ui-button'; $attrs['class'] = array_merge( $attrs['class'], $modifiers ); // ensure compatibility with Xml $attrs['class'] = implode( ' ', $attrs['class'] ); } else { $attrs['class'] .= ' mw-ui-button ' . implode( ' ', $modifiers ); } } else { // ensure compatibility with Xml $attrs['class'] = 'mw-ui-button ' . implode( ' ', $modifiers ); } } return $attrs; } /** * Modifies a set of attributes meant for text input elements * and apply a set of default attributes. * * @param array $attrs An attribute array. * @return array Modified attributes array */ public static function getTextInputAttributes( array $attrs ) { $useMediaWikiUIEverywhere = MediaWikiServices::getInstance() ->getMainConfig()->get( MainConfigNames::UseMediaWikiUIEverywhere ); if ( $useMediaWikiUIEverywhere ) { $cdxInputClass = 'cdx-text-input__input'; // This will only apply if the input is not using official Codex classes. // In future this should trigger a deprecation warning. if ( isset( $attrs['class'] ) ) { // see expandAttributes() for supported attr formats if ( is_array( $attrs['class'] ) ) { if ( !in_array( $cdxInputClass, $attrs['class'], true ) && !( $attrs['class'][$cdxInputClass] ?? false ) ) { $attrs['class']['mw-ui-input'] = true; } } elseif ( is_string( $attrs['class'] ) ) { if ( !preg_match( "/(^| )$cdxInputClass($| )/", $attrs['class'] ) ) { $attrs['class'] .= ' mw-ui-input'; } } else { throw new InvalidArgumentException( 'Unexpected class attr of type ' . gettype( $attrs['class'] ) ); } } else { $attrs['class'] = 'mw-ui-input'; } } return $attrs; } /** * Returns an HTML link element in a string styled as a button * (when $wgUseMediaWikiUIEverywhere is enabled). * * @param string $text The text of the element. Will be escaped (not raw HTML) * @param array $attrs Associative array of attributes, e.g., [ * 'href' => 'https://www.mediawiki.org/' ]. See expandAttributes() for * further documentation. * @param string[] $modifiers classes to add to the button * @see https://tools.wmflabs.org/styleguide/desktop/index.html for guidance on available modifiers * @return string Raw HTML */ public static function linkButton( $text, array $attrs, array $modifiers = [] ) { return self::element( 'a', self::buttonAttributes( $attrs, $modifiers ), $text ); } /** * Returns an HTML link element in a string styled as a button * (when $wgUseMediaWikiUIEverywhere is enabled). * * @param string $contents The raw HTML contents of the element: *not* * escaped! * @param array $attrs Associative array of attributes, e.g., [ * 'href' => 'https://www.mediawiki.org/' ]. See expandAttributes() for * further documentation. * @param string[] $modifiers classes to add to the button * @see https://tools.wmflabs.org/styleguide/desktop/index.html for guidance on available modifiers * @return string Raw HTML */ public static function submitButton( $contents, array $attrs, array $modifiers = [] ) { $attrs['type'] = 'submit'; $attrs['value'] = $contents; return self::element( 'input', self::buttonAttributes( $attrs, $modifiers ) ); } /** * Returns an HTML element in a string. The major advantage here over * manually typing out the HTML is that it will escape all attribute * values. * * This is quite similar to Xml::tags(), but it implements some useful * HTML-specific logic. For instance, there is no $allowShortTag * parameter: the closing tag is magically omitted if $element has an empty * content model. * * @param string $element The element's name, e.g., 'a' * @param array $attribs Associative array of attributes, e.g., [ * 'href' => 'https://www.mediawiki.org/' ]. See expandAttributes() for * further documentation. * @param string $contents The raw HTML contents of the element: *not* * escaped! * @return string Raw HTML */ public static function rawElement( $element, $attribs = [], $contents = '' ) { $start = self::openElement( $element, $attribs ); if ( isset( self::$voidElements[$element] ) ) { return $start; } else { return $start . $contents . self::closeElement( $element ); } } /** * Identical to rawElement(), but HTML-escapes $contents (like * Xml::element()). * * @param string $element Name of the element, e.g., 'a' * @param array $attribs Associative array of attributes, e.g., [ * 'href' => 'https://www.mediawiki.org/' ]. See expandAttributes() for * further documentation. * @param string $contents * * @return string */ public static function element( $element, $attribs = [], $contents = '' ) { return self::rawElement( $element, $attribs, strtr( $contents ?? '', [ // There's no point in escaping quotes, >, etc. in the contents of // elements. '&' => '&', '<' => '<', ] ) ); } /** * Identical to rawElement(), but has no third parameter and omits the end * tag (and the self-closing '/' in XML mode for empty elements). * * @param string $element Name of the element, e.g., 'a' * @param array $attribs Associative array of attributes, e.g., [ * 'href' => 'https://www.mediawiki.org/' ]. See expandAttributes() for * further documentation. * * @return string */ public static function openElement( $element, $attribs = [] ) { $attribs = (array)$attribs; // This is not required in HTML5, but let's do it anyway, for // consistency and better compression. $element = strtolower( $element ); // Some people were abusing this by passing things like // 'h1 id="foo" to $element, which we don't want. if ( strpos( $element, ' ' ) !== false ) { wfWarn( __METHOD__ . " given element name with space '$element'" ); } // Remove invalid input types if ( $element == 'input' ) { $validTypes = [ 'hidden' => true, 'text' => true, 'password' => true, 'checkbox' => true, 'radio' => true, 'file' => true, 'submit' => true, 'image' => true, 'reset' => true, 'button' => true, // HTML input types 'datetime' => true, 'datetime-local' => true, 'date' => true, 'month' => true, 'time' => true, 'week' => true, 'number' => true, 'range' => true, 'email' => true, 'url' => true, 'search' => true, 'tel' => true, 'color' => true, ]; if ( isset( $attribs['type'] ) && !isset( $validTypes[$attribs['type']] ) ) { unset( $attribs['type'] ); } } // According to standard the default type for