<?php
-# Copyright (C) 2009 Aryeh Gregor
+# Copyright © 2009 Aryeh Gregor
# http://www.mediawiki.org/
#
# This program is free software; you can redistribute it and/or modify
/**
* This class is a collection of static functions that serve two purposes:
*
- * 1) Implement any algorithms specified by HTML 5, or other HTML
+ * 1) Implement any algorithms specified by HTML5, or other HTML
* specifications, in a convenient and self-contained way.
*
* 2) Allow HTML elements to be conveniently and safely generated, like the
* This class is meant to be confined to utility functions that are called from
* trusted code paths. It does not do enforcement of policy like not allowing
* <a> elements.
+ *
+ * @since 1.16
*/
class Html {
- # List of void elements from HTML 5, section 9.1.2 as of 2009-08-10
+ # List of void elements from HTML5, section 9.1.2 as of 2009-08-10
private static $voidElements = array(
'area',
'base',
);
# Boolean attributes, which may have the value omitted entirely. Manually
- # collected from the HTML 5 spec as of 2009-08-10.
+ # collected from the HTML5 spec as of 2009-08-10.
private static $boolAttribs = array(
'async',
'autobuffer',
* @return string Raw HTML
*/
public static function rawElement( $element, $attribs = array(), $contents = '' ) {
+ global $wgWellFormedXml;
+ $start = self::openElement( $element, $attribs );
+ if ( in_array( $element, self::$voidElements ) ) {
+ if ( $wgWellFormedXml ) {
+ # Silly XML.
+ return substr( $start, 0, -1 ) . ' />';
+ }
+ return $start;
+ } else {
+ return "$start$contents" . self::closeElement( $element );
+ }
+ }
+
+ /**
+ * Identical to rawElement(), but HTML-escapes $contents (like
+ * Xml::element()).
+ */
+ public static function element( $element, $attribs = array(), $contents = '' ) {
+ return self::rawElement( $element, $attribs, strtr( $contents, array(
+ # 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).
+ */
+ public static function openElement( $element, $attribs = array() ) {
global $wgHtml5, $wgWellFormedXml;
$attribs = (array)$attribs;
- # This is not required in HTML 5, but let's do it anyway, for
+ # This is not required in HTML5, but let's do it anyway, for
# consistency and better compression.
$element = strtolower( $element );
+ # In text/html, initial <html> and <head> tags can be omitted under
+ # pretty much any sane circumstances, if they have no attributes. See:
+ # <http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags>
+ if ( !$wgWellFormedXml && !$attribs
+ && in_array( $element, array( 'html', 'head' ) ) ) {
+ return '';
+ }
+
# Remove HTML5-only attributes if we aren't doing HTML5
if ( !$wgHtml5 ) {
if ( $element == 'input' ) {
}
}
- $start = "<$element" . self::expandAttributes(
- self::dropDefaults( $element, $attribs ) );
- if ( in_array( $element, self::$voidElements ) ) {
- if ( $wgWellFormedXml ) {
- return "$start />";
- }
- return "$start>";
- } else {
- return "$start>$contents</$element>";
- }
+ return "<$element" . self::expandAttributes(
+ self::dropDefaults( $element, $attribs ) ) . '>';
}
/**
- * Identical to rawElement(), but HTML-escapes $contents (like
- * Xml::element()).
+ * Returns "</$element>", except if $wgWellFormedXml is off, in which case
+ * it returns the empty string when that's guaranteed to be safe.
+ *
+ * @param $element string Name of the element, e.g., 'a'
+ * @return string A closing tag, if required
*/
- public static function element( $element, $attribs = array(), $contents = '' ) {
- return self::rawElement( $element, $attribs, strtr( $contents, array(
- # There's no point in escaping quotes, >, etc. in the contents of
- # elements.
- '&' => '&',
- '<' => '<'
- ) ) );
+ public static function closeElement( $element ) {
+ global $wgWellFormedXml;
+
+ $element = strtolower( $element );
+
+ # Reference:
+ # http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags
+ if ( !$wgWellFormedXml && in_array( $element, array(
+ 'html',
+ 'head',
+ 'body',
+ 'li',
+ 'dt',
+ 'dd',
+ 'tr',
+ 'td',
+ 'th',
+ ) ) ) {
+ return '';
+ }
+ return "</$element>";
}
/**
'link' => array( 'media' => 'all' ),
'menu' => array( 'type' => 'list' ),
# Note: the use of text/javascript here instead of other JavaScript
- # MIME types follows the HTML 5 spec.
+ # MIME types follows the HTML5 spec.
'script' => array( 'type' => 'text/javascript' ),
'style' => array(
'media' => 'all',
$key = $value;
}
- # Not technically required in HTML 5, but required in XHTML 1.0,
+ # Not technically required in HTML5, but required in XHTML 1.0,
# and we'd like consistency and better compression anyway.
$key = strtolower( $key );
- # See the "Attributes" section in the HTML syntax part of HTML 5,
+ # See the "Attributes" section in the HTML syntax part of HTML5,
# 9.1.2.3 as of 2009-08-10. Most attributes can have quotation
# marks omitted, but not all. (Although a literal " is not
# permitted, we don't check for that, since it will be escaped
if ( in_array( $key, self::$boolAttribs ) ) {
# In XHTML 1.0 Transitional, the value needs to be equal to the
- # key. In HTML 5, we can leave the value empty instead. If we
+ # key. In HTML5, we can leave the value empty instead. If we
# don't need well-formed XML, we can omit the = entirely.
if ( !$wgWellFormedXml ) {
$ret .= " $key";
"\t" => '	'
);
if ( $wgWellFormedXml ) {
- # '<' must be escaped in attributes for XML for some
- # reason, per spec: http://www.w3.org/TR/xml/#NT-AttValue
+ # This is allowed per spec: <http://www.w3.org/TR/xml/#NT-AttValue>
+ # But reportedly it breaks some XML tools? FIXME: is this
+ # really true?
$map['<'] = '<';
}
$ret .= " $key=$quote" . strtr( $value, $map ) . $quote;
/**
* Convenience function to produce an <input> element. This supports the
- * new HTML 5 input types and attributes, and will silently strip them if
+ * new HTML5 input types and attributes, and will silently strip them if
* $wgHtml5 is false.
*
* @param $name string name attribute
global $wgHtml5;
$attribs['name'] = $name;
if ( !$wgHtml5 ) {
- if ( !array_key_exists('cols', $attribs) )
+ if ( !isset( $attribs['cols'] ) )
$attribs['cols'] = "";
- if ( !array_key_exists('rows', $attribs) )
+ if ( !isset( $attribs['rows'] ) )
$attribs['rows'] = "";
}
return self::element( 'textarea', $attribs, $value );