3 * Provide general element functions.
5 namespace Masterminds\HTML5;
8 * This class provides general information about HTML5 elements,
9 * including syntactic and semantic issues.
10 * Parsers and serializers can
11 * use this class as a reference point for information about the rules
12 * of various HTML5 elements.
14 * @todo consider using a bitmask table lookup. There is enough overlap in
15 * naming that this could significantly shrink the size and maybe make it
16 * faster. See the Go teams implementation at https://code.google.com/p/go/source/browse/html/atom.
22 * Indicates an element is described in the specification.
24 const KNOWN_ELEMENT = 1;
26 // From section 8.1.2: "script", "style"
27 // From 8.2.5.4.7 ("in body" insertion mode): "noembed"
28 // From 8.4 "style", "xmp", "iframe", "noembed", "noframes"
30 * Indicates the contained text should be processed as raw text.
34 // From section 8.1.2: "textarea", "title"
36 * Indicates the contained text should be processed as RCDATA.
38 const TEXT_RCDATA = 4;
41 * Indicates the tag cannot have content.
45 // "address", "article", "aside", "blockquote", "center", "details", "dialog", "dir", "div", "dl",
46 // "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "menu",
47 // "nav", "ol", "p", "section", "summary", "ul"
48 // "h1", "h2", "h3", "h4", "h5", "h6"
53 * Indicates that if a previous event is for a P tag, that element
54 * should be considered closed.
56 const AUTOCLOSE_P = 16;
59 * Indicates that the text inside is plaintext (pre).
61 const TEXT_PLAINTEXT = 32;
63 // See https://developer.mozilla.org/en-US/docs/HTML/Block-level_elements
65 * Indicates that the tag is a block.
70 * Indicates that the tag allows only inline elements as child nodes.
72 const BLOCK_ONLY_INLINE = 128;
75 * The HTML5 elements as defined in http://dev.w3.org/html5/markup/elements.html.
79 public static $html5 = array(
82 "address" => 65, // NORMAL | BLOCK_TAG
83 "area" => 9, // NORMAL | VOID_TAG
84 "article" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
85 "aside" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
86 "audio" => 65, // NORMAL | BLOCK_TAG
88 "base" => 9, // NORMAL | VOID_TAG
91 "blockquote" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
93 "br" => 9, // NORMAL | VOID_TAG
95 "canvas" => 65, // NORMAL | BLOCK_TAG
99 "col" => 9, // NORMAL | VOID_TAG
101 "command" => 9, // NORMAL | VOID_TAG
102 // "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
104 "dd" => 65, // NORMAL | BLOCK_TAG
106 "details" => 17, // NORMAL | AUTOCLOSE_P,
108 "dialog" => 17, // NORMAL | AUTOCLOSE_P,
109 "div" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
110 "dl" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
113 "embed" => 9, // NORMAL | VOID_TAG
114 "fieldset" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
115 "figcaption" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
116 "figure" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
117 "footer" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
118 "form" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
119 "h1" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
120 "h2" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
121 "h3" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
122 "h4" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
123 "h5" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
124 "h6" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
126 "header" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
127 "hgroup" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
128 "hr" => 73, // NORMAL | VOID_TAG
131 "iframe" => 3, // NORMAL | TEXT_RAW
132 "img" => 9, // NORMAL | VOID_TAG
133 "input" => 9, // NORMAL | VOID_TAG
136 "keygen" => 9, // NORMAL | VOID_TAG
140 "link" => 9, // NORMAL | VOID_TAG
143 "menu" => 17, // NORMAL | AUTOCLOSE_P,
144 "meta" => 9, // NORMAL | VOID_TAG
146 "nav" => 17, // NORMAL | AUTOCLOSE_P,
147 "noscript" => 65, // NORMAL | BLOCK_TAG
149 "ol" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
152 "output" => 65, // NORMAL | BLOCK_TAG
153 "p" => 209, // NORMAL | AUTOCLOSE_P | BLOCK_TAG | BLOCK_ONLY_INLINE
154 "param" => 9, // NORMAL | VOID_TAG
155 "pre" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
163 "script" => 3, // NORMAL | TEXT_RAW
164 "section" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
167 "source" => 9, // NORMAL | VOID_TAG
170 "style" => 3, // NORMAL | TEXT_RAW
172 "summary" => 17, // NORMAL | AUTOCLOSE_P,
174 "table" => 65, // NORMAL | BLOCK_TAG
177 "textarea" => 5, // NORMAL | TEXT_RCDATA
178 "tfoot" => 65, // NORMAL | BLOCK_TAG
182 "title" => 5, // NORMAL | TEXT_RCDATA
184 "track" => 9, // NORMAL | VOID_TAG
186 "ul" => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
188 "video" => 65, // NORMAL | BLOCK_TAG
189 "wbr" => 9, // NORMAL | VOID_TAG
192 'basefont' => 8, // VOID_TAG
193 'bgsound' => 8, // VOID_TAG
194 'noframes' => 2, // RAW_TEXT
195 'frame' => 9, // NORMAL | VOID_TAG
199 'listing' => 16, // AUTOCLOSE_P
200 'plaintext' => 48, // AUTOCLOSE_P | TEXT_PLAINTEXT
203 'isindex' => 8, // VOID_TAG
204 'xmp' => 20, // AUTOCLOSE_P | VOID_TAG | RAW_TEXT
205 'noembed' => 2 // RAW_TEXT
209 * The MathML elements.
210 * See http://www.w3.org/wiki/MathML/Elements.
212 * In our case we are only concerned with presentation MathML and not content
213 * MathML. There is a nice list of this subset at https://developer.mozilla.org/en-US/docs/MathML/Element.
217 public static $mathml = array(
230 "mmultiscripts" => 1,
262 * The Mozilla documentation has a good list at https://developer.mozilla.org/en-US/docs/SVG/Element.
263 * The w3c list appears to be lacking in some areas like filter effect elements.
264 * That list can be found at http://www.w3.org/wiki/SVG/Elements.
266 * Note, FireFox appears to do a better job rendering filter effects than chrome.
267 * While they are in the spec I'm not sure how widely implemented they are.
271 public static $svg = array(
278 "animateMotion" => 1,
279 "animateTransform" => 1,
282 "color-profile" => 1,
288 "feColorMatrix" => 1,
289 "feComponentTransfer" => 1,
291 "feConvolveMatrix" => 1,
292 "feDiffuseLighting" => 1,
293 "feDisplacementMap" => 1,
294 "feDistantLight" => 1,
300 "feGaussianBlur" => 1,
307 "feSpecularLighting" => 1,
314 "font-face-format" => 1,
315 "font-face-name" => 1,
316 "font-face-src" => 1,
317 "font-face-uri" => 1,
318 "foreignObject" => 1,
325 "linearGradient" => 1,
329 "missing-glyph" => 1,
335 "radialGradient" => 1,
337 "script" => 3, // NORMAL | RAW_TEXT
340 "style" => 3, // NORMAL | RAW_TEXT
355 * Some attributes in SVG are case sensetitive.
357 * This map contains key/value pairs with the key as the lowercase attribute
358 * name and the value with the correct casing.
360 public static $svgCaseSensitiveAttributeMap = array(
361 'attributename' => 'attributeName',
362 'attributetype' => 'attributeType',
363 'basefrequency' => 'baseFrequency',
364 'baseprofile' => 'baseProfile',
365 'calcmode' => 'calcMode',
366 'clippathunits' => 'clipPathUnits',
367 'contentscripttype' => 'contentScriptType',
368 'contentstyletype' => 'contentStyleType',
369 'diffuseconstant' => 'diffuseConstant',
370 'edgemode' => 'edgeMode',
371 'externalresourcesrequired' => 'externalResourcesRequired',
372 'filterres' => 'filterRes',
373 'filterunits' => 'filterUnits',
374 'glyphref' => 'glyphRef',
375 'gradienttransform' => 'gradientTransform',
376 'gradientunits' => 'gradientUnits',
377 'kernelmatrix' => 'kernelMatrix',
378 'kernelunitlength' => 'kernelUnitLength',
379 'keypoints' => 'keyPoints',
380 'keysplines' => 'keySplines',
381 'keytimes' => 'keyTimes',
382 'lengthadjust' => 'lengthAdjust',
383 'limitingconeangle' => 'limitingConeAngle',
384 'markerheight' => 'markerHeight',
385 'markerunits' => 'markerUnits',
386 'markerwidth' => 'markerWidth',
387 'maskcontentunits' => 'maskContentUnits',
388 'maskunits' => 'maskUnits',
389 'numoctaves' => 'numOctaves',
390 'pathlength' => 'pathLength',
391 'patterncontentunits' => 'patternContentUnits',
392 'patterntransform' => 'patternTransform',
393 'patternunits' => 'patternUnits',
394 'pointsatx' => 'pointsAtX',
395 'pointsaty' => 'pointsAtY',
396 'pointsatz' => 'pointsAtZ',
397 'preservealpha' => 'preserveAlpha',
398 'preserveaspectratio' => 'preserveAspectRatio',
399 'primitiveunits' => 'primitiveUnits',
402 'repeatcount' => 'repeatCount',
403 'repeatdur' => 'repeatDur',
404 'requiredextensions' => 'requiredExtensions',
405 'requiredfeatures' => 'requiredFeatures',
406 'specularconstant' => 'specularConstant',
407 'specularexponent' => 'specularExponent',
408 'spreadmethod' => 'spreadMethod',
409 'startoffset' => 'startOffset',
410 'stddeviation' => 'stdDeviation',
411 'stitchtiles' => 'stitchTiles',
412 'surfacescale' => 'surfaceScale',
413 'systemlanguage' => 'systemLanguage',
414 'tablevalues' => 'tableValues',
415 'targetx' => 'targetX',
416 'targety' => 'targetY',
417 'textlength' => 'textLength',
418 'viewbox' => 'viewBox',
419 'viewtarget' => 'viewTarget',
420 'xchannelselector' => 'xChannelSelector',
421 'ychannelselector' => 'yChannelSelector',
422 'zoomandpan' => 'zoomAndPan'
426 * Some SVG elements are case sensetitive.
427 * This map contains these.
429 * The map contains key/value store of the name is lowercase as the keys and
430 * the correct casing as the value.
432 public static $svgCaseSensitiveElementMap = array(
433 'altglyph' => 'altGlyph',
434 'altglyphdef' => 'altGlyphDef',
435 'altglyphitem' => 'altGlyphItem',
436 'animatecolor' => 'animateColor',
437 'animatemotion' => 'animateMotion',
438 'animatetransform' => 'animateTransform',
439 'clippath' => 'clipPath',
440 'feblend' => 'feBlend',
441 'fecolormatrix' => 'feColorMatrix',
442 'fecomponenttransfer' => 'feComponentTransfer',
443 'fecomposite' => 'feComposite',
444 'feconvolvematrix' => 'feConvolveMatrix',
445 'fediffuselighting' => 'feDiffuseLighting',
446 'fedisplacementmap' => 'feDisplacementMap',
447 'fedistantlight' => 'feDistantLight',
448 'feflood' => 'feFlood',
449 'fefunca' => 'feFuncA',
450 'fefuncb' => 'feFuncB',
451 'fefuncg' => 'feFuncG',
452 'fefuncr' => 'feFuncR',
453 'fegaussianblur' => 'feGaussianBlur',
454 'feimage' => 'feImage',
455 'femerge' => 'feMerge',
456 'femergenode' => 'feMergeNode',
457 'femorphology' => 'feMorphology',
458 'feoffset' => 'feOffset',
459 'fepointlight' => 'fePointLight',
460 'fespecularlighting' => 'feSpecularLighting',
461 'fespotlight' => 'feSpotLight',
462 'fetile' => 'feTile',
463 'feturbulence' => 'feTurbulence',
464 'foreignobject' => 'foreignObject',
465 'glyphref' => 'glyphRef',
466 'lineargradient' => 'linearGradient',
467 'radialgradient' => 'radialGradient',
468 'textpath' => 'textPath'
472 * Check whether the given element meets the given criterion.
476 * Elements::isA('script', Elements::TEXT_RAW); // Returns true.
478 * Elements::isA('script', Elements::TEXT_RCDATA); // Returns false.
480 * @param string $name
483 * One of the constants on this class.
484 * @return boolean true if the element matches the mask, false otherwise.
486 public static function isA($name, $mask)
488 if (! static::isElement($name)) {
492 return (static::element($name) & $mask) == $mask;
496 * Test if an element is a valid html5 element.
498 * @param string $name
499 * The name of the element.
501 * @return bool True if a html5 element and false otherwise.
503 public static function isHtml5Element($name)
505 // html5 element names are case insensetitive. Forcing lowercase for the check.
506 // Do we need this check or will all data passed here already be lowercase?
507 return isset(static::$html5[strtolower($name)]);
511 * Test if an element name is a valid MathML presentation element.
513 * @param string $name
514 * The name of the element.
516 * @return bool True if a MathML name and false otherwise.
518 public static function isMathMLElement($name)
520 // MathML is case-sensetitive unlike html5 elements.
521 return isset(static::$mathml[$name]);
525 * Test if an element is a valid SVG element.
527 * @param string $name
528 * The name of the element.
530 * @return boolean True if a SVG element and false otherise.
532 public static function isSvgElement($name)
534 // SVG is case-sensetitive unlike html5 elements.
535 return isset(static::$svg[$name]);
539 * Is an element name valid in an html5 document.
541 * This includes html5 elements along with other allowed embedded content
542 * such as svg and mathml.
544 * @param string $name
545 * The name of the element.
547 * @return bool True if valid and false otherwise.
549 public static function isElement($name)
551 return static::isHtml5Element($name) || static::isMathMLElement($name) || static::isSvgElement($name);
555 * Get the element mask for the given element name.
557 * @param string $name
558 * The name of the element.
560 * @return int|bool The element mask or false if element does not exist.
562 public static function element($name)
564 if (isset(static::$html5[$name])) {
565 return static::$html5[$name];
567 if (isset(static::$svg[$name])) {
568 return static::$svg[$name];
570 if (isset(static::$mathml[$name])) {
571 return static::$mathml[$name];
578 * Normalize a SVG element name to its proper case and form.
580 * @param string $name
581 * The name of the element.
583 * @return string The normalized form of the element name.
585 public static function normalizeSvgElement($name)
587 $name = strtolower($name);
588 if (isset(static::$svgCaseSensitiveElementMap[$name])) {
589 $name = static::$svgCaseSensitiveElementMap[$name];
596 * Normalize a SVG attribute name to its proper case and form.
598 * @param string $name
599 * The name of the attribute.
601 * @return string The normalized form of the attribute name.
603 public static function normalizeSvgAttribute($name)
605 $name = strtolower($name);
606 if (isset(static::$svgCaseSensitiveAttributeMap[$name])) {
607 $name = static::$svgCaseSensitiveAttributeMap[$name];
614 * Normalize a MathML attribute name to its proper case and form.
616 * Note, all MathML element names are lowercase.
618 * @param string $name
619 * The name of the attribute.
621 * @return string The normalized form of the attribute name.
623 public static function normalizeMathMlAttribute($name)
625 $name = strtolower($name);
627 // Only one attribute has a mixed case form for MathML.
628 if ($name == 'definitionurl') {
629 $name = 'definitionURL';