2014-09-15 22:24:06 +02:00
< ? php
/**
* Provide general element functions .
*/
2015-06-14 02:03:20 +02:00
namespace Masterminds\HTML5 ;
2014-09-15 22:24:06 +02:00
/**
2015-06-14 02:03:20 +02:00
* This class provides general information about HTML5 elements ,
* including syntactic and semantic issues .
* Parsers and serializers can
* use this class as a reference point for information about the rules
2014-09-15 22:24:06 +02:00
* of various HTML5 elements .
*
* @ todo consider using a bitmask table lookup . There is enough overlap in
2015-06-14 02:03:20 +02:00
* naming that this could significantly shrink the size and maybe make it
* faster . See the Go teams implementation at https :// code . google . com / p / go / source / browse / html / atom .
2014-09-15 22:24:06 +02:00
*/
2015-06-14 02:03:20 +02:00
class Elements
{
/**
* Indicates an element is described in the specification .
*/
const KNOWN_ELEMENT = 1 ;
// From section 8.1.2: "script", "style"
// From 8.2.5.4.7 ("in body" insertion mode): "noembed", "noscript"
// From 8.4 "style", "xmp", "iframe", "noembed", "noframes"
/**
* Indicates the contained text should be processed as raw text .
*/
const TEXT_RAW = 2 ;
// From section 8.1.2: "textarea", "title"
/**
* Indicates the contained text should be processed as RCDATA .
*/
const TEXT_RCDATA = 4 ;
/**
* Indicates the tag cannot have content .
*/
const VOID_TAG = 8 ;
// "address", "article", "aside", "blockquote", "center", "details", "dialog", "dir", "div", "dl",
// "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "menu",
// "nav", "ol", "p", "section", "summary", "ul"
// "h1", "h2", "h3", "h4", "h5", "h6"
// "pre", "listing"
// "form"
// "plaintext"
/**
* Indicates that if a previous event is for a P tag , that element
* should be considered closed .
*/
const AUTOCLOSE_P = 16 ;
/**
* Indicates that the text inside is plaintext ( pre ) .
*/
const TEXT_PLAINTEXT = 32 ;
// See https://developer.mozilla.org/en-US/docs/HTML/Block-level_elements
/**
* Indicates that the tag is a block .
*/
const BLOCK_TAG = 64 ;
2017-02-18 16:06:19 +01:00
/**
* Indicates that the tag allows only inline elements as child nodes .
*/
const BLOCK_ONLY_INLINE = 128 ;
2015-06-14 02:03:20 +02:00
/**
* The HTML5 elements as defined in http :// dev . w3 . org / html5 / markup / elements . html .
*
* @ var array
*/
public static $html5 = array (
" a " => 1 ,
" abbr " => 1 ,
" address " => 89 , // NORMAL | VOID_TAG | AUTOCLOSE_P | BLOCK_TAG
" area " => 9 , // NORMAL | VOID_TAG
" article " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" aside " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" audio " => 65 , // NORMAL | BLOCK_TAG
" b " => 1 ,
" base " => 9 , // NORMAL | VOID_TAG
" bdi " => 1 ,
" bdo " => 1 ,
" blockquote " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" body " => 1 ,
" br " => 9 , // NORMAL | VOID_TAG
" button " => 1 ,
" canvas " => 65 , // NORMAL | BLOCK_TAG
" caption " => 1 ,
" cite " => 1 ,
" code " => 1 ,
" col " => 9 , // NORMAL | VOID_TAG
" colgroup " => 1 ,
" command " => 9 , // NORMAL | VOID_TAG
// "data" => 1, // This is highly experimental and only part of the whatwg spec (not w3c). See https://developer.mozilla.org/en-US/docs/HTML/Element/data
" datalist " => 1 ,
" dd " => 65 , // NORMAL | BLOCK_TAG
" del " => 1 ,
" details " => 17 , // NORMAL | AUTOCLOSE_P,
" dfn " => 1 ,
" dialog " => 17 , // NORMAL | AUTOCLOSE_P,
" div " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" dl " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" dt " => 1 ,
" em " => 1 ,
" embed " => 9 , // NORMAL | VOID_TAG
" fieldset " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" figcaption " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" figure " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" footer " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" form " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" h1 " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" h2 " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" h3 " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" h4 " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" h5 " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" h6 " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" head " => 1 ,
" header " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" hgroup " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
2017-02-18 16:06:19 +01:00
" hr " => 73 , // NORMAL | VOID_TAG
2015-06-14 02:03:20 +02:00
" html " => 1 ,
" i " => 1 ,
" iframe " => 3 , // NORMAL | TEXT_RAW
" img " => 9 , // NORMAL | VOID_TAG
" input " => 9 , // NORMAL | VOID_TAG
" kbd " => 1 ,
" ins " => 1 ,
" keygen " => 9 , // NORMAL | VOID_TAG
" label " => 1 ,
" legend " => 1 ,
" li " => 1 ,
" link " => 9 , // NORMAL | VOID_TAG
" map " => 1 ,
" mark " => 1 ,
" menu " => 17 , // NORMAL | AUTOCLOSE_P,
" meta " => 9 , // NORMAL | VOID_TAG
" meter " => 1 ,
" nav " => 17 , // NORMAL | AUTOCLOSE_P,
" noscript " => 67 , // NORMAL | TEXT_RAW | BLOCK_TAG
" object " => 1 ,
" ol " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" optgroup " => 1 ,
" option " => 1 ,
" output " => 65 , // NORMAL | BLOCK_TAG
2017-02-18 16:06:19 +01:00
" p " => 209 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG | BLOCK_ONLY_INLINE
2015-06-14 02:03:20 +02:00
" param " => 9 , // NORMAL | VOID_TAG
" pre " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" progress " => 1 ,
" q " => 1 ,
" rp " => 1 ,
" rt " => 1 ,
" ruby " => 1 ,
" s " => 1 ,
" samp " => 1 ,
" script " => 3 , // NORMAL | TEXT_RAW
" section " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" select " => 1 ,
" small " => 1 ,
" source " => 9 , // NORMAL | VOID_TAG
" span " => 1 ,
" strong " => 1 ,
" style " => 3 , // NORMAL | TEXT_RAW
" sub " => 1 ,
" summary " => 17 , // NORMAL | AUTOCLOSE_P,
" sup " => 1 ,
" table " => 65 , // NORMAL | BLOCK_TAG
" tbody " => 1 ,
" td " => 1 ,
" textarea " => 5 , // NORMAL | TEXT_RCDATA
" tfoot " => 65 , // NORMAL | BLOCK_TAG
" th " => 1 ,
" thead " => 1 ,
" time " => 1 ,
" title " => 5 , // NORMAL | TEXT_RCDATA
" tr " => 1 ,
" track " => 9 , // NORMAL | VOID_TAG
" u " => 1 ,
" ul " => 81 , // NORMAL | AUTOCLOSE_P | BLOCK_TAG
" var " => 1 ,
" video " => 65 , // NORMAL | BLOCK_TAG
" wbr " => 9 , // NORMAL | VOID_TAG
// Legacy?
'basefont' => 8 , // VOID_TAG
'bgsound' => 8 , // VOID_TAG
'noframes' => 2 , // RAW_TEXT
'frame' => 9 , // NORMAL | VOID_TAG
'frameset' => 1 ,
'center' => 16 ,
'dir' => 16 ,
'listing' => 16 , // AUTOCLOSE_P
'plaintext' => 48 , // AUTOCLOSE_P | TEXT_PLAINTEXT
'applet' => 0 ,
'marquee' => 0 ,
'isindex' => 8 , // VOID_TAG
'xmp' => 20 , // AUTOCLOSE_P | VOID_TAG | RAW_TEXT
'noembed' => 2 // RAW_TEXT
);
/**
* The MathML elements .
* See http :// www . w3 . org / wiki / MathML / Elements .
*
* In our case we are only concerned with presentation MathML and not content
* MathML . There is a nice list of this subset at https :// developer . mozilla . org / en - US / docs / MathML / Element .
*
* @ var array
*/
public static $mathml = array (
" maction " => 1 ,
" maligngroup " => 1 ,
" malignmark " => 1 ,
" math " => 1 ,
" menclose " => 1 ,
" merror " => 1 ,
" mfenced " => 1 ,
" mfrac " => 1 ,
" mglyph " => 1 ,
" mi " => 1 ,
" mlabeledtr " => 1 ,
" mlongdiv " => 1 ,
" mmultiscripts " => 1 ,
" mn " => 1 ,
" mo " => 1 ,
" mover " => 1 ,
" mpadded " => 1 ,
" mphantom " => 1 ,
" mroot " => 1 ,
" mrow " => 1 ,
" ms " => 1 ,
" mscarries " => 1 ,
" mscarry " => 1 ,
" msgroup " => 1 ,
" msline " => 1 ,
" mspace " => 1 ,
" msqrt " => 1 ,
" msrow " => 1 ,
" mstack " => 1 ,
" mstyle " => 1 ,
" msub " => 1 ,
" msup " => 1 ,
" msubsup " => 1 ,
" mtable " => 1 ,
" mtd " => 1 ,
" mtext " => 1 ,
" mtr " => 1 ,
" munder " => 1 ,
" munderover " => 1
);
/**
* The svg elements .
*
* The Mozilla documentation has a good list at https :// developer . mozilla . org / en - US / docs / SVG / Element .
* The w3c list appears to be lacking in some areas like filter effect elements .
* That list can be found at http :// www . w3 . org / wiki / SVG / Elements .
*
* Note , FireFox appears to do a better job rendering filter effects than chrome .
* While they are in the spec I ' m not sure how widely implemented they are .
*
* @ var array
*/
public static $svg = array (
" a " => 1 ,
" altGlyph " => 1 ,
" altGlyphDef " => 1 ,
" altGlyphItem " => 1 ,
" animate " => 1 ,
" animateColor " => 1 ,
" animateMotion " => 1 ,
" animateTransform " => 1 ,
" circle " => 1 ,
" clipPath " => 1 ,
" color-profile " => 1 ,
" cursor " => 1 ,
" defs " => 1 ,
" desc " => 1 ,
" ellipse " => 1 ,
" feBlend " => 1 ,
" feColorMatrix " => 1 ,
" feComponentTransfer " => 1 ,
" feComposite " => 1 ,
" feConvolveMatrix " => 1 ,
" feDiffuseLighting " => 1 ,
" feDisplacementMap " => 1 ,
" feDistantLight " => 1 ,
" feFlood " => 1 ,
" feFuncA " => 1 ,
" feFuncB " => 1 ,
" feFuncG " => 1 ,
" feFuncR " => 1 ,
" feGaussianBlur " => 1 ,
" feImage " => 1 ,
" feMerge " => 1 ,
" feMergeNode " => 1 ,
" feMorphology " => 1 ,
" feOffset " => 1 ,
" fePointLight " => 1 ,
" feSpecularLighting " => 1 ,
" feSpotLight " => 1 ,
" feTile " => 1 ,
" feTurbulence " => 1 ,
" filter " => 1 ,
" font " => 1 ,
" font-face " => 1 ,
" font-face-format " => 1 ,
" font-face-name " => 1 ,
" font-face-src " => 1 ,
" font-face-uri " => 1 ,
" foreignObject " => 1 ,
" g " => 1 ,
" glyph " => 1 ,
" glyphRef " => 1 ,
" hkern " => 1 ,
" image " => 1 ,
" line " => 1 ,
" linearGradient " => 1 ,
" marker " => 1 ,
" mask " => 1 ,
" metadata " => 1 ,
" missing-glyph " => 1 ,
" mpath " => 1 ,
" path " => 1 ,
" pattern " => 1 ,
" polygon " => 1 ,
" polyline " => 1 ,
" radialGradient " => 1 ,
" rect " => 1 ,
" script " => 3 , // NORMAL | RAW_TEXT
" set " => 1 ,
" stop " => 1 ,
" style " => 3 , // NORMAL | RAW_TEXT
" svg " => 1 ,
" switch " => 1 ,
" symbol " => 1 ,
" text " => 1 ,
" textPath " => 1 ,
" title " => 1 ,
" tref " => 1 ,
" tspan " => 1 ,
" use " => 1 ,
" view " => 1 ,
" vkern " => 1
);
/**
* Some attributes in SVG are case sensetitive .
*
* This map contains key / value pairs with the key as the lowercase attribute
* name and the value with the correct casing .
*/
public static $svgCaseSensitiveAttributeMap = array (
'attributename' => 'attributeName' ,
'attributetype' => 'attributeType' ,
'basefrequency' => 'baseFrequency' ,
'baseprofile' => 'baseProfile' ,
'calcmode' => 'calcMode' ,
'clippathunits' => 'clipPathUnits' ,
'contentscripttype' => 'contentScriptType' ,
'contentstyletype' => 'contentStyleType' ,
'diffuseconstant' => 'diffuseConstant' ,
'edgemode' => 'edgeMode' ,
'externalresourcesrequired' => 'externalResourcesRequired' ,
'filterres' => 'filterRes' ,
'filterunits' => 'filterUnits' ,
'glyphref' => 'glyphRef' ,
'gradienttransform' => 'gradientTransform' ,
'gradientunits' => 'gradientUnits' ,
'kernelmatrix' => 'kernelMatrix' ,
'kernelunitlength' => 'kernelUnitLength' ,
'keypoints' => 'keyPoints' ,
'keysplines' => 'keySplines' ,
'keytimes' => 'keyTimes' ,
'lengthadjust' => 'lengthAdjust' ,
'limitingconeangle' => 'limitingConeAngle' ,
'markerheight' => 'markerHeight' ,
'markerunits' => 'markerUnits' ,
'markerwidth' => 'markerWidth' ,
'maskcontentunits' => 'maskContentUnits' ,
'maskunits' => 'maskUnits' ,
'numoctaves' => 'numOctaves' ,
'pathlength' => 'pathLength' ,
'patterncontentunits' => 'patternContentUnits' ,
'patterntransform' => 'patternTransform' ,
'patternunits' => 'patternUnits' ,
'pointsatx' => 'pointsAtX' ,
'pointsaty' => 'pointsAtY' ,
'pointsatz' => 'pointsAtZ' ,
'preservealpha' => 'preserveAlpha' ,
'preserveaspectratio' => 'preserveAspectRatio' ,
'primitiveunits' => 'primitiveUnits' ,
'refx' => 'refX' ,
'refy' => 'refY' ,
'repeatcount' => 'repeatCount' ,
'repeatdur' => 'repeatDur' ,
'requiredextensions' => 'requiredExtensions' ,
'requiredfeatures' => 'requiredFeatures' ,
'specularconstant' => 'specularConstant' ,
'specularexponent' => 'specularExponent' ,
'spreadmethod' => 'spreadMethod' ,
'startoffset' => 'startOffset' ,
'stddeviation' => 'stdDeviation' ,
'stitchtiles' => 'stitchTiles' ,
'surfacescale' => 'surfaceScale' ,
'systemlanguage' => 'systemLanguage' ,
'tablevalues' => 'tableValues' ,
'targetx' => 'targetX' ,
'targety' => 'targetY' ,
'textlength' => 'textLength' ,
'viewbox' => 'viewBox' ,
'viewtarget' => 'viewTarget' ,
'xchannelselector' => 'xChannelSelector' ,
'ychannelselector' => 'yChannelSelector' ,
'zoomandpan' => 'zoomAndPan'
);
/**
* Some SVG elements are case sensetitive .
* This map contains these .
*
* The map contains key / value store of the name is lowercase as the keys and
* the correct casing as the value .
*/
public static $svgCaseSensitiveElementMap = array (
'altglyph' => 'altGlyph' ,
'altglyphdef' => 'altGlyphDef' ,
'altglyphitem' => 'altGlyphItem' ,
'animatecolor' => 'animateColor' ,
'animatemotion' => 'animateMotion' ,
'animatetransform' => 'animateTransform' ,
'clippath' => 'clipPath' ,
'feblend' => 'feBlend' ,
'fecolormatrix' => 'feColorMatrix' ,
'fecomponenttransfer' => 'feComponentTransfer' ,
'fecomposite' => 'feComposite' ,
'feconvolvematrix' => 'feConvolveMatrix' ,
'fediffuselighting' => 'feDiffuseLighting' ,
'fedisplacementmap' => 'feDisplacementMap' ,
'fedistantlight' => 'feDistantLight' ,
'feflood' => 'feFlood' ,
'fefunca' => 'feFuncA' ,
'fefuncb' => 'feFuncB' ,
'fefuncg' => 'feFuncG' ,
'fefuncr' => 'feFuncR' ,
'fegaussianblur' => 'feGaussianBlur' ,
'feimage' => 'feImage' ,
'femerge' => 'feMerge' ,
'femergenode' => 'feMergeNode' ,
'femorphology' => 'feMorphology' ,
'feoffset' => 'feOffset' ,
'fepointlight' => 'fePointLight' ,
'fespecularlighting' => 'feSpecularLighting' ,
'fespotlight' => 'feSpotLight' ,
'fetile' => 'feTile' ,
'feturbulence' => 'feTurbulence' ,
'foreignobject' => 'foreignObject' ,
'glyphref' => 'glyphRef' ,
'lineargradient' => 'linearGradient' ,
'radialgradient' => 'radialGradient' ,
'textpath' => 'textPath'
);
/**
* Check whether the given element meets the given criterion .
*
* Example :
*
* Elements :: isA ( 'script' , Elements :: TEXT_RAW ); // Returns true.
*
* Elements :: isA ( 'script' , Elements :: TEXT_RCDATA ); // Returns false.
*
* @ param string $name
* The element name .
* @ param int $mask
* One of the constants on this class .
* @ return boolean true if the element matches the mask , false otherwise .
*/
public static function isA ( $name , $mask )
{
if ( ! static :: isElement ( $name )) {
return false ;
}
return ( static :: element ( $name ) & $mask ) == $mask ;
2014-09-15 22:24:06 +02:00
}
2015-06-14 02:03:20 +02:00
/**
* Test if an element is a valid html5 element .
*
* @ param string $name
* The name of the element .
*
* @ return bool True if a html5 element and false otherwise .
*/
public static function isHtml5Element ( $name )
{
// html5 element names are case insensetitive. Forcing lowercase for the check.
// Do we need this check or will all data passed here already be lowercase?
return isset ( static :: $html5 [ strtolower ( $name )]);
2014-09-15 22:24:06 +02:00
}
2015-06-14 02:03:20 +02:00
/**
* Test if an element name is a valid MathML presentation element .
*
* @ param string $name
* The name of the element .
*
* @ return bool True if a MathML name and false otherwise .
*/
public static function isMathMLElement ( $name )
{
// MathML is case-sensetitive unlike html5 elements.
return isset ( static :: $mathml [ $name ]);
2014-09-15 22:24:06 +02:00
}
2015-06-14 02:03:20 +02:00
/**
* Test if an element is a valid SVG element .
*
* @ param string $name
* The name of the element .
*
* @ return boolean True if a SVG element and false otherise .
*/
public static function isSvgElement ( $name )
{
// SVG is case-sensetitive unlike html5 elements.
return isset ( static :: $svg [ $name ]);
}
2014-09-15 22:24:06 +02:00
2015-06-14 02:03:20 +02:00
/**
* Is an element name valid in an html5 document .
*
* This includes html5 elements along with other allowed embedded content
* such as svg and mathml .
*
* @ param string $name
* The name of the element .
*
* @ return bool True if valid and false otherwise .
*/
public static function isElement ( $name )
{
return static :: isHtml5Element ( $name ) || static :: isMathMLElement ( $name ) || static :: isSvgElement ( $name );
2014-09-15 22:24:06 +02:00
}
2015-06-14 02:03:20 +02:00
/**
* Get the element mask for the given element name .
*
* @ param string $name
* The name of the element .
*
* @ return int The element mask .
*/
public static function element ( $name )
{
if ( isset ( static :: $html5 [ $name ])) {
return static :: $html5 [ $name ];
}
if ( isset ( static :: $svg [ $name ])) {
return static :: $svg [ $name ];
}
if ( isset ( static :: $mathml [ $name ])) {
return static :: $mathml [ $name ];
}
return false ;
}
2014-09-15 22:24:06 +02:00
2015-06-14 02:03:20 +02:00
/**
* Normalize a SVG element name to its proper case and form .
*
* @ param string $name
* The name of the element .
*
* @ return string The normalized form of the element name .
*/
public static function normalizeSvgElement ( $name )
{
$name = strtolower ( $name );
if ( isset ( static :: $svgCaseSensitiveElementMap [ $name ])) {
$name = static :: $svgCaseSensitiveElementMap [ $name ];
}
return $name ;
}
2014-09-15 22:24:06 +02:00
2015-06-14 02:03:20 +02:00
/**
* Normalize a SVG attribute name to its proper case and form .
*
* @ param string $name
* The name of the attribute .
*
* @ return string The normalized form of the attribute name .
*/
public static function normalizeSvgAttribute ( $name )
{
$name = strtolower ( $name );
if ( isset ( static :: $svgCaseSensitiveAttributeMap [ $name ])) {
$name = static :: $svgCaseSensitiveAttributeMap [ $name ];
}
return $name ;
2014-09-15 22:24:06 +02:00
}
2015-06-14 02:03:20 +02:00
/**
* Normalize a MathML attribute name to its proper case and form .
*
* Note , all MathML element names are lowercase .
*
* @ param string $name
* The name of the attribute .
*
* @ return string The normalized form of the attribute name .
*/
public static function normalizeMathMlAttribute ( $name )
{
$name = strtolower ( $name );
// Only one attribute has a mixed case form for MathML.
if ( $name == 'definitionurl' ) {
$name = 'definitionURL' ;
}
return $name ;
}
2014-09-15 22:24:06 +02:00
}