MediaWiki  master
Html.php
Go to the documentation of this file.
00001 <?php
00050 class Html {
00051         // List of void elements from HTML5, section 8.1.2 as of 2011-08-12
00052         private static $voidElements = array(
00053                 'area',
00054                 'base',
00055                 'br',
00056                 'col',
00057                 'command',
00058                 'embed',
00059                 'hr',
00060                 'img',
00061                 'input',
00062                 'keygen',
00063                 'link',
00064                 'meta',
00065                 'param',
00066                 'source',
00067                 'track',
00068                 'wbr',
00069         );
00070 
00071         // Boolean attributes, which may have the value omitted entirely.  Manually
00072         // collected from the HTML5 spec as of 2011-08-12.
00073         private static $boolAttribs = array(
00074                 'async',
00075                 'autofocus',
00076                 'autoplay',
00077                 'checked',
00078                 'controls',
00079                 'default',
00080                 'defer',
00081                 'disabled',
00082                 'formnovalidate',
00083                 'hidden',
00084                 'ismap',
00085                 'itemscope',
00086                 'loop',
00087                 'multiple',
00088                 'muted',
00089                 'novalidate',
00090                 'open',
00091                 'pubdate',
00092                 'readonly',
00093                 'required',
00094                 'reversed',
00095                 'scoped',
00096                 'seamless',
00097                 'selected',
00098                 'truespeed',
00099                 'typemustmatch',
00100                 // HTML5 Microdata
00101                 'itemscope',
00102         );
00103 
00104         private static $HTMLFiveOnlyAttribs = array(
00105                 'autocomplete',
00106                 'autofocus',
00107                 'max',
00108                 'min',
00109                 'multiple',
00110                 'pattern',
00111                 'placeholder',
00112                 'required',
00113                 'step',
00114                 'spellcheck',
00115         );
00116 
00137         public static function rawElement( $element, $attribs = array(), $contents = '' ) {
00138                 global $wgWellFormedXml;
00139                 $start = self::openElement( $element, $attribs );
00140                 if ( in_array( $element, self::$voidElements ) ) {
00141                         if ( $wgWellFormedXml ) {
00142                                 // Silly XML.
00143                                 return substr( $start, 0, -1 ) . ' />';
00144                         }
00145                         return $start;
00146                 } else {
00147                         return "$start$contents" . self::closeElement( $element );
00148                 }
00149         }
00150 
00161         public static function element( $element, $attribs = array(), $contents = '' ) {
00162                 return self::rawElement( $element, $attribs, strtr( $contents, array(
00163                         // There's no point in escaping quotes, >, etc. in the contents of
00164                         // elements.
00165                         '&' => '&amp;',
00166                         '<' => '&lt;'
00167                 ) ) );
00168         }
00169 
00179         public static function openElement( $element, $attribs = array() ) {
00180                 global $wgHtml5, $wgWellFormedXml;
00181                 $attribs = (array)$attribs;
00182                 // This is not required in HTML5, but let's do it anyway, for
00183                 // consistency and better compression.
00184                 $element = strtolower( $element );
00185 
00186                 // In text/html, initial <html> and <head> tags can be omitted under
00187                 // pretty much any sane circumstances, if they have no attributes.  See:
00188                 // <http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags>
00189                 if ( !$wgWellFormedXml && !$attribs
00190                 && in_array( $element, array( 'html', 'head' ) ) ) {
00191                         return '';
00192                 }
00193 
00194                 // Remove invalid input types
00195                 if ( $element == 'input' ) {
00196                         $validTypes = array(
00197                                 'hidden',
00198                                 'text',
00199                                 'password',
00200                                 'checkbox',
00201                                 'radio',
00202                                 'file',
00203                                 'submit',
00204                                 'image',
00205                                 'reset',
00206                                 'button',
00207                         );
00208 
00209                         // Allow more input types in HTML5 mode
00210                         if( $wgHtml5 ) {
00211                                 $validTypes = array_merge( $validTypes, array(
00212                                         'datetime',
00213                                         'datetime-local',
00214                                         'date',
00215                                         'month',
00216                                         'time',
00217                                         'week',
00218                                         'number',
00219                                         'range',
00220                                         'email',
00221                                         'url',
00222                                         'search',
00223                                         'tel',
00224                                         'color',
00225                                 ) );
00226                         }
00227                         if ( isset( $attribs['type'] )
00228                         && !in_array( $attribs['type'], $validTypes ) ) {
00229                                 unset( $attribs['type'] );
00230                         }
00231                 }
00232 
00233                 if ( !$wgHtml5 && $element == 'textarea' && isset( $attribs['maxlength'] ) ) {
00234                         unset( $attribs['maxlength'] );
00235                 }
00236 
00237                 return "<$element" . self::expandAttributes(
00238                         self::dropDefaults( $element, $attribs ) ) . '>';
00239         }
00240 
00249         public static function closeElement( $element ) {
00250                 global $wgWellFormedXml;
00251 
00252                 $element = strtolower( $element );
00253 
00254                 // Reference:
00255                 // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags
00256                 if ( !$wgWellFormedXml && in_array( $element, array(
00257                         'html',
00258                         'head',
00259                         'body',
00260                         'li',
00261                         'dt',
00262                         'dd',
00263                         'tr',
00264                         'td',
00265                         'th',
00266                 ) ) ) {
00267                         return '';
00268                 }
00269                 return "</$element>";
00270         }
00271 
00289         private static function dropDefaults( $element, $attribs ) {
00290                 // Don't bother doing anything if we aren't outputting HTML5; it's too
00291                 // much of a pain to maintain two sets of defaults.
00292                 global $wgHtml5;
00293                 if ( !$wgHtml5 ) {
00294                         return $attribs;
00295                 }
00296 
00297                 // Whenever altering this array, please provide a covering test case
00298                 // in HtmlTest::provideElementsWithAttributesHavingDefaultValues
00299                 static $attribDefaults = array(
00300                         'area' => array( 'shape' => 'rect' ),
00301                         'button' => array(
00302                                 'formaction' => 'GET',
00303                                 'formenctype' => 'application/x-www-form-urlencoded',
00304                                 'type' => 'submit',
00305                         ),
00306                         'canvas' => array(
00307                                 'height' => '150',
00308                                 'width' => '300',
00309                         ),
00310                         'command' => array( 'type' => 'command' ),
00311                         'form' => array(
00312                                 'action' => 'GET',
00313                                 'autocomplete' => 'on',
00314                                 'enctype' => 'application/x-www-form-urlencoded',
00315                         ),
00316                         'input' => array(
00317                                 'formaction' => 'GET',
00318                                 'type' => 'text',
00319                         ),
00320                         'keygen' => array( 'keytype' => 'rsa' ),
00321                         'link' => array( 'media' => 'all' ),
00322                         'menu' => array( 'type' => 'list' ),
00323                         // Note: the use of text/javascript here instead of other JavaScript
00324                         // MIME types follows the HTML5 spec.
00325                         'script' => array( 'type' => 'text/javascript' ),
00326                         'style' => array(
00327                                 'media' => 'all',
00328                                 'type' => 'text/css',
00329                         ),
00330                         'textarea' => array( 'wrap' => 'soft' ),
00331                 );
00332 
00333                 $element = strtolower( $element );
00334 
00335                 foreach ( $attribs as $attrib => $value ) {
00336                         $lcattrib = strtolower( $attrib );
00337                         if( is_array( $value ) ) {
00338                                 $value = implode( ' ', $value );
00339                         } else {
00340                                 $value = strval( $value );
00341                         }
00342 
00343                         // Simple checks using $attribDefaults
00344                         if ( isset( $attribDefaults[$element][$lcattrib] ) &&
00345                         $attribDefaults[$element][$lcattrib] == $value ) {
00346                                 unset( $attribs[$attrib] );
00347                         }
00348 
00349                         if ( $lcattrib == 'class' && $value == '' ) {
00350                                 unset( $attribs[$attrib] );
00351                         }
00352                 }
00353 
00354                 // More subtle checks
00355                 if ( $element === 'link' && isset( $attribs['type'] )
00356                 && strval( $attribs['type'] ) == 'text/css' ) {
00357                         unset( $attribs['type'] );
00358                 }
00359                 if ( $element === 'input' ) {
00360                         $type = isset( $attribs['type'] ) ? $attribs['type'] : null;
00361                         $value = isset( $attribs['value'] ) ? $attribs['value'] : null;
00362                         if ( $type === 'checkbox' || $type === 'radio' ) {
00363                                 // The default value for checkboxes and radio buttons is 'on'
00364                                 // not ''. By stripping value="" we break radio boxes that
00365                                 // actually wants empty values.
00366                                 if ( $value === 'on' ) {
00367                                         unset( $attribs['value'] );
00368                                 }
00369                         } elseif ( $type === 'submit' ) {
00370                                 // The default value for submit appears to be "Submit" but
00371                                 // let's not bother stripping out localized text that matches
00372                                 // that.
00373                         } else {
00374                                 // The default value for nearly every other field type is ''
00375                                 // The 'range' and 'color' types use different defaults but
00376                                 // stripping a value="" does not hurt them.
00377                                 if ( $value === '' ) {
00378                                         unset( $attribs['value'] );
00379                                 }
00380                         }
00381                 }
00382                 if ( $element === 'select' && isset( $attribs['size'] ) ) {
00383                         if ( in_array( 'multiple', $attribs )
00384                                 || ( isset( $attribs['multiple'] ) && $attribs['multiple'] !== false )
00385                         ) {
00386                                 // A multi-select
00387                                 if ( strval( $attribs['size'] ) == '4' ) {
00388                                         unset( $attribs['size'] );
00389                                 }
00390                         } else {
00391                                 // Single select
00392                                 if ( strval( $attribs['size'] ) == '1' ) {
00393                                         unset( $attribs['size'] );
00394                                 }
00395                         }
00396                 }
00397 
00398                 return $attribs;
00399         }
00400 
00440         public static function expandAttributes( $attribs ) {
00441                 global $wgHtml5, $wgWellFormedXml;
00442 
00443                 $ret = '';
00444                 $attribs = (array)$attribs;
00445                 foreach ( $attribs as $key => $value ) {
00446                         if ( $value === false || is_null( $value ) ) {
00447                                 continue;
00448                         }
00449 
00450                         // For boolean attributes, support array( 'foo' ) instead of
00451                         // requiring array( 'foo' => 'meaningless' ).
00452                         if ( is_int( $key )
00453                         && in_array( strtolower( $value ), self::$boolAttribs ) ) {
00454                                 $key = $value;
00455                         }
00456 
00457                         // Not technically required in HTML5, but required in XHTML 1.0,
00458                         // and we'd like consistency and better compression anyway.
00459                         $key = strtolower( $key );
00460 
00461                         // Here we're blacklisting some HTML5-only attributes...
00462                         if ( !$wgHtml5 && in_array( $key, self::$HTMLFiveOnlyAttribs )
00463                          ) {
00464                                 continue;
00465                         }
00466 
00467                         // Bug 23769: Blacklist all form validation attributes for now.  Current
00468                         // (June 2010) WebKit has no UI, so the form just refuses to submit
00469                         // without telling the user why, which is much worse than failing
00470                         // server-side validation.  Opera is the only other implementation at
00471                         // this time, and has ugly UI, so just kill the feature entirely until
00472                         // we have at least one good implementation.
00473                         if ( in_array( $key, array( 'max', 'min', 'pattern', 'required', 'step' ) ) ) {
00474                                 continue;
00475                         }
00476 
00477                         // http://www.w3.org/TR/html401/index/attributes.html ("space-separated")
00478                         // http://www.w3.org/TR/html5/index.html#attributes-1 ("space-separated")
00479                         $spaceSeparatedListAttributes = array(
00480                                 'class', // html4, html5
00481                                 'accesskey', // as of html5, multiple space-separated values allowed
00482                                 // html4-spec doesn't document rel= as space-separated
00483                                 // but has been used like that and is now documented as such
00484                                 // in the html5-spec.
00485                                 'rel',
00486                         );
00487 
00488                         // Specific features for attributes that allow a list of space-separated values
00489                         if ( in_array( $key, $spaceSeparatedListAttributes ) ) {
00490                                 // Apply some normalization and remove duplicates
00491 
00492                                 // Convert into correct array. Array can contain space-seperated
00493                                 // values. Implode/explode to get those into the main array as well.
00494                                 if ( is_array( $value ) ) {
00495                                         // If input wasn't an array, we can skip this step
00496                                         $newValue = array();
00497                                         foreach ( $value as $k => $v ) {
00498                                                 if ( is_string( $v ) ) {
00499                                                         // String values should be normal `array( 'foo' )`
00500                                                         // Just append them
00501                                                         if ( !isset( $value[$v] ) ) {
00502                                                                 // As a special case don't set 'foo' if a
00503                                                                 // separate 'foo' => true/false exists in the array
00504                                                                 // keys should be authoritive
00505                                                                 $newValue[] = $v;
00506                                                         }
00507                                                 } elseif ( $v ) {
00508                                                         // If the value is truthy but not a string this is likely
00509                                                         // an array( 'foo' => true ), falsy values don't add strings
00510                                                         $newValue[] = $k;
00511                                                 }
00512                                         }
00513                                         $value = implode( ' ', $newValue );
00514                                 }
00515                                 $value = explode( ' ', $value );
00516 
00517                                 // Normalize spacing by fixing up cases where people used
00518                                 // more than 1 space and/or a trailing/leading space
00519                                 $value = array_diff( $value, array( '', ' ' ) );
00520 
00521                                 // Remove duplicates and create the string
00522                                 $value = implode( ' ', array_unique( $value ) );
00523                         }
00524 
00525                         // See the "Attributes" section in the HTML syntax part of HTML5,
00526                         // 9.1.2.3 as of 2009-08-10.  Most attributes can have quotation
00527                         // marks omitted, but not all.  (Although a literal " is not
00528                         // permitted, we don't check for that, since it will be escaped
00529                         // anyway.)
00530                         #
00531                         // See also research done on further characters that need to be
00532                         // escaped: http://code.google.com/p/html5lib/issues/detail?id=93
00533                         $badChars = "\\x00- '=<>`/\x{00a0}\x{1680}\x{180e}\x{180F}\x{2000}\x{2001}"
00534                                 . "\x{2002}\x{2003}\x{2004}\x{2005}\x{2006}\x{2007}\x{2008}\x{2009}"
00535                                 . "\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}";
00536                         if ( $wgWellFormedXml || $value === ''
00537                         || preg_match( "![$badChars]!u", $value ) ) {
00538                                 $quote = '"';
00539                         } else {
00540                                 $quote = '';
00541                         }
00542 
00543                         if ( in_array( $key, self::$boolAttribs ) ) {
00544                                 // In XHTML 1.0 Transitional, the value needs to be equal to the
00545                                 // key.  In HTML5, we can leave the value empty instead.  If we
00546                                 // don't need well-formed XML, we can omit the = entirely.
00547                                 if ( !$wgWellFormedXml ) {
00548                                         $ret .= " $key";
00549                                 } elseif ( $wgHtml5 ) {
00550                                         $ret .= " $key=\"\"";
00551                                 } else {
00552                                         $ret .= " $key=\"$key\"";
00553                                 }
00554                         } else {
00555                                 // Apparently we need to entity-encode \n, \r, \t, although the
00556                                 // spec doesn't mention that.  Since we're doing strtr() anyway,
00557                                 // and we don't need <> escaped here, we may as well not call
00558                                 // htmlspecialchars().
00559                                 // @todo FIXME: Verify that we actually need to
00560                                 // escape \n\r\t here, and explain why, exactly.
00561                                 #
00562                                 // We could call Sanitizer::encodeAttribute() for this, but we
00563                                 // don't because we're stubborn and like our marginal savings on
00564                                 // byte size from not having to encode unnecessary quotes.
00565                                 $map = array(
00566                                         '&' => '&amp;',
00567                                         '"' => '&quot;',
00568                                         "\n" => '&#10;',
00569                                         "\r" => '&#13;',
00570                                         "\t" => '&#9;'
00571                                 );
00572                                 if ( $wgWellFormedXml ) {
00573                                         // This is allowed per spec: <http://www.w3.org/TR/xml/#NT-AttValue>
00574                                         // But reportedly it breaks some XML tools?
00575                                         // @todo FIXME: Is this really true?
00576                                         $map['<'] = '&lt;';
00577                                 }
00578                                 $ret .= " $key=$quote" . strtr( $value, $map ) . $quote;
00579                         }
00580                 }
00581                 return $ret;
00582         }
00583 
00593         public static function inlineScript( $contents ) {
00594                 global $wgHtml5, $wgJsMimeType, $wgWellFormedXml;
00595 
00596                 $attrs = array();
00597 
00598                 if ( !$wgHtml5 ) {
00599                         $attrs['type'] = $wgJsMimeType;
00600                 }
00601 
00602                 if ( $wgWellFormedXml && preg_match( '/[<&]/', $contents ) ) {
00603                         $contents = "/*<![CDATA[*/$contents/*]]>*/";
00604                 }
00605 
00606                 return self::rawElement( 'script', $attrs, $contents );
00607         }
00608 
00616         public static function linkedScript( $url ) {
00617                 global $wgHtml5, $wgJsMimeType;
00618 
00619                 $attrs = array( 'src' => $url );
00620 
00621                 if ( !$wgHtml5 ) {
00622                         $attrs['type'] = $wgJsMimeType;
00623                 }
00624 
00625                 return self::element( 'script', $attrs );
00626         }
00627 
00637         public static function inlineStyle( $contents, $media = 'all' ) {
00638                 global $wgWellFormedXml;
00639 
00640                 if ( $wgWellFormedXml && preg_match( '/[<&]/', $contents ) ) {
00641                         $contents = "/*<![CDATA[*/$contents/*]]>*/";
00642                 }
00643 
00644                 return self::rawElement( 'style', array(
00645                         'type' => 'text/css',
00646                         'media' => $media,
00647                 ), $contents );
00648         }
00649 
00658         public static function linkedStyle( $url, $media = 'all' ) {
00659                 return self::element( 'link', array(
00660                         'rel' => 'stylesheet',
00661                         'href' => $url,
00662                         'type' => 'text/css',
00663                         'media' => $media,
00664                 ) );
00665         }
00666 
00679         public static function input( $name, $value = '', $type = 'text', $attribs = array() ) {
00680                 $attribs['type'] = $type;
00681                 $attribs['value'] = $value;
00682                 $attribs['name'] = $name;
00683 
00684                 return self::element( 'input', $attribs );
00685         }
00686 
00696         public static function hidden( $name, $value, $attribs = array() ) {
00697                 return self::input( $name, $value, 'hidden', $attribs );
00698         }
00699 
00714         public static function textarea( $name, $value = '', $attribs = array() ) {
00715                 global $wgHtml5;
00716 
00717                 $attribs['name'] = $name;
00718 
00719                 if ( !$wgHtml5 ) {
00720                         if ( !isset( $attribs['cols'] ) ) {
00721                                 $attribs['cols'] = "";
00722                         }
00723 
00724                         if ( !isset( $attribs['rows'] ) ) {
00725                                 $attribs['rows'] = "";
00726                         }
00727                 }
00728 
00729                 if (substr($value, 0, 1) == "\n") {
00730                         // Workaround for bug 12130: browsers eat the initial newline
00731                         // assuming that it's just for show, but they do keep the later
00732                         // newlines, which we may want to preserve during editing.
00733                         // Prepending a single newline
00734                         $spacedValue = "\n" . $value;
00735                 } else {
00736                         $spacedValue = $value;
00737                 }
00738                 return self::element( 'textarea', $attribs, $spacedValue );
00739         }
00754         public static function namespaceSelector( array $params = array(), array $selectAttribs = array() ) {
00755                 global $wgContLang;
00756 
00757                 ksort( $selectAttribs );
00758 
00759                 // Is a namespace selected?
00760                 if ( isset( $params['selected'] ) ) {
00761                         // If string only contains digits, convert to clean int. Selected could also
00762                         // be "all" or "" etc. which needs to be left untouched.
00763                         // PHP is_numeric() has issues with large strings, PHP ctype_digit has other issues
00764                         // and returns false for already clean ints. Use regex instead..
00765                         if ( preg_match( '/^\d+$/', $params['selected'] ) ) {
00766                                 $params['selected'] = intval( $params['selected'] );
00767                         }
00768                         // else: leaves it untouched for later processing
00769                 } else {
00770                         $params['selected'] = '';
00771                 }
00772 
00773                 if ( !isset( $params['exclude'] ) || !is_array( $params['exclude'] ) ) {
00774                         $params['exclude'] = array();
00775                 }
00776                 if ( !isset( $params['disable'] ) || !is_array( $params['disable'] ) ) {
00777                         $params['disable'] = array();
00778                 }
00779 
00780                 // Associative array between option-values and option-labels
00781                 $options = array();
00782 
00783                 if ( isset( $params['all'] ) ) {
00784                         // add an option that would let the user select all namespaces.
00785                         // Value is provided by user, the name shown is localized for the user.
00786                         $options[$params['all']] = wfMessage( 'namespacesall' )->text();
00787                 }
00788                 // Add all namespaces as options (in the content langauge)
00789                 $options += $wgContLang->getFormattedNamespaces();
00790 
00791                 // Convert $options to HTML and filter out namespaces below 0
00792                 $optionsHtml = array();
00793                 foreach ( $options as $nsId => $nsName ) {
00794                         if ( $nsId < NS_MAIN || in_array( $nsId, $params['exclude'] ) ) {
00795                                 continue;
00796                         }
00797                         if ( $nsId === NS_MAIN ) {
00798                                 // For other namespaces use use the namespace prefix as label, but for
00799                                 // main we don't use "" but the user message descripting it (e.g. "(Main)" or "(Article)")
00800                                 $nsName = wfMessage( 'blanknamespace' )->text();
00801                         } elseif ( is_int( $nsId ) ) {
00802                                 $nsName = $wgContLang->convertNamespace( $nsId );
00803                         }
00804                         $optionsHtml[] = Html::element(
00805                                 'option', array(
00806                                         'disabled' => in_array( $nsId, $params['disable'] ),
00807                                         'value' => $nsId,
00808                                         'selected' => $nsId === $params['selected'],
00809                                 ), $nsName
00810                         );
00811                 }
00812 
00813                 if ( !array_key_exists( 'id', $selectAttribs ) ) {
00814                         $selectAttribs['id'] = 'namespace';
00815                 }
00816 
00817                 if ( !array_key_exists( 'name', $selectAttribs ) ) {
00818                         $selectAttribs['name'] = 'namespace';
00819                 }
00820 
00821                 $ret = '';
00822                 if ( isset( $params['label'] ) ) {
00823                         $ret .= Html::element(
00824                                 'label', array(
00825                                         'for' => isset( $selectAttribs['id'] ) ? $selectAttribs['id'] : null,
00826                                 ), $params['label']
00827                         ) . '&#160;';
00828                 }
00829 
00830                 // Wrap options in a <select>
00831                 $ret .= Html::openElement( 'select', $selectAttribs )
00832                         . "\n"
00833                         . implode( "\n", $optionsHtml )
00834                         . "\n"
00835                         . Html::closeElement( 'select' );
00836 
00837                 return $ret;
00838         }
00839 
00848         public static function htmlHeader( $attribs = array() ) {
00849                 $ret = '';
00850 
00851                 global $wgMimeType;
00852 
00853                 if ( self::isXmlMimeType( $wgMimeType ) ) {
00854                         $ret .= "<?xml version=\"1.0\" encoding=\"UTF-8\" ?" . ">\n";
00855                 }
00856 
00857                 global $wgHtml5, $wgHtml5Version, $wgDocType, $wgDTD;
00858                 global $wgXhtmlNamespaces, $wgXhtmlDefaultNamespace;
00859 
00860                 if ( $wgHtml5 ) {
00861                         $ret .= "<!DOCTYPE html>\n";
00862 
00863                         if ( $wgHtml5Version ) {
00864                                 $attribs['version'] = $wgHtml5Version;
00865                         }
00866                 } else {
00867                         $ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\" \"$wgDTD\">\n";
00868                         $attribs['xmlns'] = $wgXhtmlDefaultNamespace;
00869 
00870                         foreach ( $wgXhtmlNamespaces as $tag => $ns ) {
00871                                 $attribs["xmlns:$tag"] = $ns;
00872                         }
00873                 }
00874 
00875                 $html = Html::openElement( 'html', $attribs );
00876 
00877                 if ( $html ) {
00878                         $html .= "\n";
00879                 }
00880 
00881                 $ret .= $html;
00882 
00883                 return $ret;
00884         }
00885 
00892         public static function isXmlMimeType( $mimetype ) {
00893                 switch ( $mimetype ) {
00894                         case 'text/xml':
00895                         case 'application/xhtml+xml':
00896                         case 'application/xml':
00897                                 return true;
00898                         default:
00899                                 return false;
00900                 }
00901         }
00902 
00914         static function infoBox( $text, $icon, $alt, $class = false, $useStylePath = true ) {
00915                 global $wgStylePath;
00916 
00917                 if ( $useStylePath ) {
00918                         $icon = $wgStylePath.'/common/images/'.$icon;
00919                 }
00920 
00921                 $s  = Html::openElement( 'div', array( 'class' => "mw-infobox $class") );
00922 
00923                 $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-left' ) ).
00924                                 Html::element( 'img',
00925                                         array(
00926                                                 'src' => $icon,
00927                                                 'alt' => $alt,
00928                                         )
00929                                 ).
00930                                 Html::closeElement( 'div' );
00931 
00932                 $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-right' ) ).
00933                                 $text.
00934                                 Html::closeElement( 'div' );
00935                 $s .= Html::element( 'div', array( 'style' => 'clear: left;' ), ' ' );
00936 
00937                 $s .= Html::closeElement( 'div' );
00938 
00939                 $s .= Html::element( 'div', array( 'style' => 'clear: left;' ), ' ' );
00940 
00941                 return $s;
00942         }
00943 
00952         static function srcSet( $urls ) {
00953                 $candidates = array();
00954                 foreach( $urls as $density => $url ) {
00955                         // Image candidate syntax per current whatwg live spec, 2012-09-23:
00956                         // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#attr-img-srcset
00957                         $candidates[] = "{$url} {$density}x";
00958                 }
00959                 return implode( ", ", $candidates );
00960         }
00961 }