diff options
Diffstat (limited to 'library/HTML5/TreeBuilder.php')
-rw-r--r-- | library/HTML5/TreeBuilder.php | 3721 |
1 files changed, 0 insertions, 3721 deletions
diff --git a/library/HTML5/TreeBuilder.php b/library/HTML5/TreeBuilder.php deleted file mode 100644 index 578e73682..000000000 --- a/library/HTML5/TreeBuilder.php +++ /dev/null @@ -1,3721 +0,0 @@ -<?php - -/* - -Copyright 2007 Jeroen van der Meer <http://jero.net/> -Copyright 2009 Edward Z. Yang <edwardzyang@thewritingpot.com> - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -// Tags for FIX ME!!!: (in order of priority) -// XXX - should be fixed NAO! -// XERROR - with regards to parse errors -// XSCRIPT - with regards to scripting mode -// XENCODING - with regards to encoding (for reparsing tests) - -class HTML5_TreeBuilder { - public $stack = array(); - public $content_model; - - private $mode; - private $original_mode; - private $secondary_mode; - private $dom; - // Whether or not normal insertion of nodes should actually foster - // parent (used in one case in spec) - private $foster_parent = false; - private $a_formatting = array(); - - private $head_pointer = null; - private $form_pointer = null; - - private $flag_frameset_ok = true; - private $flag_force_quirks = false; - private $ignored = false; - private $quirks_mode = null; - // this gets to 2 when we want to ignore the next lf character, and - // is decrement at the beginning of each processed token (this way, - // code can check for (bool)$ignore_lf_token, but it phases out - // appropriately) - private $ignore_lf_token = 0; - private $fragment = false; - private $root; - - private $scoping = array('applet','button','caption','html','marquee','object','table','td','th', 'svg:foreignObject'); - private $formatting = array('a','b','big','code','em','font','i','nobr','s','small','strike','strong','tt','u'); - private $special = array('address','area','article','aside','base','basefont','bgsound', - 'blockquote','body','br','center','col','colgroup','command','dd','details','dialog','dir','div','dl', - 'dt','embed','fieldset','figure','footer','form','frame','frameset','h1','h2','h3','h4','h5', - 'h6','head','header','hgroup','hr','iframe','img','input','isindex','li','link', - 'listing','menu','meta','nav','noembed','noframes','noscript','ol', - 'p','param','plaintext','pre','script','select','spacer','style', - 'tbody','textarea','tfoot','thead','title','tr','ul','wbr'); - - // Tree construction modes - const INITIAL = 0; - const BEFORE_HTML = 1; - const BEFORE_HEAD = 2; - const IN_HEAD = 3; - const IN_HEAD_NOSCRIPT = 4; - const AFTER_HEAD = 5; - const IN_BODY = 6; - const IN_CDATA_RCDATA = 7; - const IN_TABLE = 8; - const IN_CAPTION = 9; - const IN_COLUMN_GROUP = 10; - const IN_TABLE_BODY = 11; - const IN_ROW = 12; - const IN_CELL = 13; - const IN_SELECT = 14; - const IN_SELECT_IN_TABLE= 15; - const IN_FOREIGN_CONTENT= 16; - const AFTER_BODY = 17; - const IN_FRAMESET = 18; - const AFTER_FRAMESET = 19; - const AFTER_AFTER_BODY = 20; - const AFTER_AFTER_FRAMESET = 21; - - /** - * Converts a magic number to a readable name. Use for debugging. - */ - private function strConst($number) { - static $lookup; - if (!$lookup) { - $r = new ReflectionClass('HTML5_TreeBuilder'); - $lookup = array_flip($r->getConstants()); - } - return $lookup[$number]; - } - - // The different types of elements. - const SPECIAL = 100; - const SCOPING = 101; - const FORMATTING = 102; - const PHRASING = 103; - - // Quirks modes in $quirks_mode - const NO_QUIRKS = 200; - const QUIRKS_MODE = 201; - const LIMITED_QUIRKS_MODE = 202; - - // Marker to be placed in $a_formatting - const MARKER = 300; - - // Namespaces for foreign content - const NS_HTML = null; // to prevent DOM from requiring NS on everything - const NS_MATHML = 'http://www.w3.org/1998/Math/MathML'; - const NS_SVG = 'http://www.w3.org/2000/svg'; - const NS_XLINK = 'http://www.w3.org/1999/xlink'; - const NS_XML = 'http://www.w3.org/XML/1998/namespace'; - const NS_XMLNS = 'http://www.w3.org/2000/xmlns/'; - - public function __construct() { - $this->mode = self::INITIAL; - $this->dom = new DOMDocument; - - $this->dom->encoding = 'UTF-8'; - $this->dom->preserveWhiteSpace = true; - $this->dom->substituteEntities = true; - $this->dom->strictErrorChecking = false; - } - - // Process tag tokens - public function emitToken($token, $mode = null) { - // XXX: ignore parse errors... why are we emitting them, again? - if ($token['type'] === HTML5_Tokenizer::PARSEERROR) return; - if ($mode === null) $mode = $this->mode; - - /* - $backtrace = debug_backtrace(); - if ($backtrace[1]['class'] !== 'HTML5_TreeBuilder') echo "--\n"; - echo $this->strConst($mode); - if ($this->original_mode) echo " (originally ".$this->strConst($this->original_mode).")"; - echo "\n "; - token_dump($token); - $this->printStack(); - $this->printActiveFormattingElements(); - if ($this->foster_parent) echo " -> this is a foster parent mode\n"; - */ - - if ($this->ignore_lf_token) $this->ignore_lf_token--; - $this->ignored = false; - // indenting is a little wonky, this can be changed later on - switch ($mode) { - - case self::INITIAL: - - /* A character token that is one of U+0009 CHARACTER TABULATION, - * U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020 SPACE */ - if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Ignore the token. */ - $this->ignored = true; - } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - if ( - $token['name'] !== 'html' || !empty($token['public']) || - !empty($token['system']) || $token !== 'about:legacy-compat' - ) { - /* If the DOCTYPE token's name is not a case-sensitive match - * for the string "html", or if the token's public identifier - * is not missing, or if the token's system identifier is - * neither missing nor a case-sensitive match for the string - * "about:legacy-compat", then there is a parse error (this - * is the DOCTYPE parse error). */ - // DOCTYPE parse error - } - /* Append a DocumentType node to the Document node, with the name - * attribute set to the name given in the DOCTYPE token, or the - * empty string if the name was missing; the publicId attribute - * set to the public identifier given in the DOCTYPE token, or - * the empty string if the public identifier was missing; the - * systemId attribute set to the system identifier given in the - * DOCTYPE token, or the empty string if the system identifier - * was missing; and the other attributes specific to - * DocumentType objects set to null and empty lists as - * appropriate. Associate the DocumentType node with the - * Document object so that it is returned as the value of the - * doctype attribute of the Document object. */ - if (!isset($token['public'])) $token['public'] = null; - if (!isset($token['system'])) $token['system'] = null; - // Yes this is hacky. I'm kind of annoyed that I can't appendChild - // a doctype to DOMDocument. Maybe I haven't chanted the right - // syllables. - $impl = new DOMImplementation(); - // This call can fail for particularly pathological cases (namely, - // the qualifiedName parameter ($token['name']) could be missing. - if ($token['name']) { - $doctype = $impl->createDocumentType($token['name'], $token['public'], $token['system']); - $this->dom->appendChild($doctype); - } else { - // It looks like libxml's not actually *able* to express this case. - // So... don't. - $this->dom->emptyDoctype = true; - } - $public = is_null($token['public']) ? false : strtolower($token['public']); - $system = is_null($token['system']) ? false : strtolower($token['system']); - $publicStartsWithForQuirks = array( - "+//silmaril//dtd html pro v0r11 19970101//", - "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", - "-//as//dtd html 3.0 aswedit + extensions//", - "-//ietf//dtd html 2.0 level 1//", - "-//ietf//dtd html 2.0 level 2//", - "-//ietf//dtd html 2.0 strict level 1//", - "-//ietf//dtd html 2.0 strict level 2//", - "-//ietf//dtd html 2.0 strict//", - "-//ietf//dtd html 2.0//", - "-//ietf//dtd html 2.1e//", - "-//ietf//dtd html 3.0//", - "-//ietf//dtd html 3.2 final//", - "-//ietf//dtd html 3.2//", - "-//ietf//dtd html 3//", - "-//ietf//dtd html level 0//", - "-//ietf//dtd html level 1//", - "-//ietf//dtd html level 2//", - "-//ietf//dtd html level 3//", - "-//ietf//dtd html strict level 0//", - "-//ietf//dtd html strict level 1//", - "-//ietf//dtd html strict level 2//", - "-//ietf//dtd html strict level 3//", - "-//ietf//dtd html strict//", - "-//ietf//dtd html//", - "-//metrius//dtd metrius presentational//", - "-//microsoft//dtd internet explorer 2.0 html strict//", - "-//microsoft//dtd internet explorer 2.0 html//", - "-//microsoft//dtd internet explorer 2.0 tables//", - "-//microsoft//dtd internet explorer 3.0 html strict//", - "-//microsoft//dtd internet explorer 3.0 html//", - "-//microsoft//dtd internet explorer 3.0 tables//", - "-//netscape comm. corp.//dtd html//", - "-//netscape comm. corp.//dtd strict html//", - "-//o'reilly and associates//dtd html 2.0//", - "-//o'reilly and associates//dtd html extended 1.0//", - "-//o'reilly and associates//dtd html extended relaxed 1.0//", - "-//spyglass//dtd html 2.0 extended//", - "-//sq//dtd html 2.0 hotmetal + extensions//", - "-//sun microsystems corp.//dtd hotjava html//", - "-//sun microsystems corp.//dtd hotjava strict html//", - "-//w3c//dtd html 3 1995-03-24//", - "-//w3c//dtd html 3.2 draft//", - "-//w3c//dtd html 3.2 final//", - "-//w3c//dtd html 3.2//", - "-//w3c//dtd html 3.2s draft//", - "-//w3c//dtd html 4.0 frameset//", - "-//w3c//dtd html 4.0 transitional//", - "-//w3c//dtd html experimental 19960712//", - "-//w3c//dtd html experimental 970421//", - "-//w3c//dtd w3 html//", - "-//w3o//dtd w3 html 3.0//", - "-//webtechs//dtd mozilla html 2.0//", - "-//webtechs//dtd mozilla html//", - ); - $publicSetToForQuirks = array( - "-//w3o//dtd w3 html strict 3.0//", - "-/w3c/dtd html 4.0 transitional/en", - "html", - ); - $publicStartsWithAndSystemForQuirks = array( - "-//w3c//dtd html 4.01 frameset//", - "-//w3c//dtd html 4.01 transitional//", - ); - $publicStartsWithForLimitedQuirks = array( - "-//w3c//dtd xhtml 1.0 frameset//", - "-//w3c//dtd xhtml 1.0 transitional//", - ); - $publicStartsWithAndSystemForLimitedQuirks = array( - "-//w3c//dtd html 4.01 frameset//", - "-//w3c//dtd html 4.01 transitional//", - ); - // first, do easy checks - if ( - !empty($token['force-quirks']) || - strtolower($token['name']) !== 'html' - ) { - $this->quirks_mode = self::QUIRKS_MODE; - } else { - do { - if ($system) { - foreach ($publicStartsWithAndSystemForQuirks as $x) { - if (strncmp($public, $x, strlen($x)) === 0) { - $this->quirks_mode = self::QUIRKS_MODE; - break; - } - } - if (!is_null($this->quirks_mode)) break; - foreach ($publicStartsWithAndSystemForLimitedQuirks as $x) { - if (strncmp($public, $x, strlen($x)) === 0) { - $this->quirks_mode = self::LIMITED_QUIRKS_MODE; - break; - } - } - if (!is_null($this->quirks_mode)) break; - } - foreach ($publicSetToForQuirks as $x) { - if ($public === $x) { - $this->quirks_mode = self::QUIRKS_MODE; - break; - } - } - if (!is_null($this->quirks_mode)) break; - foreach ($publicStartsWithForLimitedQuirks as $x) { - if (strncmp($public, $x, strlen($x)) === 0) { - $this->quirks_mode = self::LIMITED_QUIRKS_MODE; - } - } - if (!is_null($this->quirks_mode)) break; - if ($system === "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") { - $this->quirks_mode = self::QUIRKS_MODE; - break; - } - foreach ($publicStartsWithForQuirks as $x) { - if (strncmp($public, $x, strlen($x)) === 0) { - $this->quirks_mode = self::QUIRKS_MODE; - break; - } - } - if (is_null($this->quirks_mode)) { - $this->quirks_mode = self::NO_QUIRKS; - } - } while (false); - } - $this->mode = self::BEFORE_HTML; - } else { - // parse error - /* Switch the insertion mode to "before html", then reprocess the - * current token. */ - $this->mode = self::BEFORE_HTML; - $this->quirks_mode = self::QUIRKS_MODE; - $this->emitToken($token); - } - break; - - case self::BEFORE_HTML: - - /* A DOCTYPE token */ - if($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // Parse error. Ignore the token. - $this->ignored = true; - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Ignore the token. */ - $this->ignored = true; - - /* A start tag whose tag name is "html" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] == 'html') { - /* Create an element for the token in the HTML namespace. Append it - * to the Document object. Put this element in the stack of open - * elements. */ - $html = $this->insertElement($token, false); - $this->dom->appendChild($html); - $this->stack[] = $html; - - $this->mode = self::BEFORE_HEAD; - - } else { - /* Create an html element. Append it to the Document object. Put - * this element in the stack of open elements. */ - $html = $this->dom->createElementNS(self::NS_HTML, 'html'); - $this->dom->appendChild($html); - $this->stack[] = $html; - - /* Switch the insertion mode to "before head", then reprocess the - * current token. */ - $this->mode = self::BEFORE_HEAD; - $this->emitToken($token); - } - break; - - case self::BEFORE_HEAD: - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Ignore the token. */ - $this->ignored = true; - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A DOCTYPE token */ - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE) { - /* Parse error. Ignore the token */ - $this->ignored = true; - // parse error - - /* A start tag token with the tag name "html" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - /* Process the token using the rules for the "in body" - * insertion mode. */ - $this->processWithRulesFor($token, self::IN_BODY); - - /* A start tag token with the tag name "head" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') { - /* Insert an HTML element for the token. */ - $element = $this->insertElement($token); - - /* Set the head element pointer to this new element node. */ - $this->head_pointer = $element; - - /* Change the insertion mode to "in head". */ - $this->mode = self::IN_HEAD; - - /* An end tag whose tag name is one of: "head", "body", "html", "br" */ - } elseif( - $token['type'] === HTML5_Tokenizer::ENDTAG && ( - $token['name'] === 'head' || $token['name'] === 'body' || - $token['name'] === 'html' || $token['name'] === 'br' - )) { - /* Act as if a start tag token with the tag name "head" and no - * attributes had been seen, then reprocess the current token. */ - $this->emitToken(array( - 'name' => 'head', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - $this->emitToken($token); - - /* Any other end tag */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG) { - /* Parse error. Ignore the token. */ - $this->ignored = true; - - } else { - /* Act as if a start tag token with the tag name "head" and no - * attributes had been seen, then reprocess the current token. - * Note: This will result in an empty head element being - * generated, with the current token being reprocessed in the - * "after head" insertion mode. */ - $this->emitToken(array( - 'name' => 'head', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - $this->emitToken($token); - } - break; - - case self::IN_HEAD: - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. */ - if($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Insert the character into the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A DOCTYPE token */ - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE) { - /* Parse error. Ignore the token. */ - $this->ignored = true; - // parse error - - /* A start tag whose tag name is "html" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'html') { - $this->processWithRulesFor($token, self::IN_BODY); - - /* A start tag whose tag name is one of: "base", "command", "link" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - ($token['name'] === 'base' || $token['name'] === 'command' || - $token['name'] === 'link')) { - /* Insert an HTML element for the token. Immediately pop the - * current node off the stack of open elements. */ - $this->insertElement($token); - array_pop($this->stack); - - // YYY: Acknowledge the token's self-closing flag, if it is set. - - /* A start tag whose tag name is "meta" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'meta') { - /* Insert an HTML element for the token. Immediately pop the - * current node off the stack of open elements. */ - $this->insertElement($token); - array_pop($this->stack); - - // XERROR: Acknowledge the token's self-closing flag, if it is set. - - // XENCODING: If the element has a charset attribute, and its value is a - // supported encoding, and the confidence is currently tentative, - // then change the encoding to the encoding given by the value of - // the charset attribute. - // - // Otherwise, if the element has a content attribute, and applying - // the algorithm for extracting an encoding from a Content-Type to - // its value returns a supported encoding encoding, and the - // confidence is currently tentative, then change the encoding to - // the encoding encoding. - - /* A start tag with the tag name "title" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'title') { - $this->insertRCDATAElement($token); - - /* A start tag whose tag name is "noscript", if the scripting flag is enabled, or - * A start tag whose tag name is one of: "noframes", "style" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - ($token['name'] === 'noscript' || $token['name'] === 'noframes' || $token['name'] === 'style')) { - // XSCRIPT: Scripting flag not respected - $this->insertCDATAElement($token); - - // XSCRIPT: Scripting flag disable not implemented - - /* A start tag with the tag name "script" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') { - /* 1. Create an element for the token in the HTML namespace. */ - $node = $this->insertElement($token, false); - - /* 2. Mark the element as being "parser-inserted" */ - // Uhhh... XSCRIPT - - /* 3. If the parser was originally created for the HTML - * fragment parsing algorithm, then mark the script element as - * "already executed". (fragment case) */ - // ditto... XSCRIPT - - /* 4. Append the new element to the current node and push it onto - * the stack of open elements. */ - end($this->stack)->appendChild($node); - $this->stack[] = $node; - // I guess we could squash these together - - /* 6. Let the original insertion mode be the current insertion mode. */ - $this->original_mode = $this->mode; - /* 7. Switch the insertion mode to "in CDATA/RCDATA" */ - $this->mode = self::IN_CDATA_RCDATA; - /* 5. Switch the tokeniser's content model flag to the CDATA state. */ - $this->content_model = HTML5_Tokenizer::CDATA; - - /* An end tag with the tag name "head" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'head') { - /* Pop the current node (which will be the head element) off the stack of open elements. */ - array_pop($this->stack); - - /* Change the insertion mode to "after head". */ - $this->mode = self::AFTER_HEAD; - - // Slight logic inversion here to minimize duplication - /* A start tag with the tag name "head". */ - /* An end tag whose tag name is not one of: "body", "html", "br" */ - } elseif(($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') || - ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] !== 'html' && - $token['name'] !== 'body' && $token['name'] !== 'br')) { - // Parse error. Ignore the token. - $this->ignored = true; - - /* Anything else */ - } else { - /* Act as if an end tag token with the tag name "head" had been - * seen, and reprocess the current token. */ - $this->emitToken(array( - 'name' => 'head', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - /* Then, reprocess the current token. */ - $this->emitToken($token); - } - break; - - case self::IN_HEAD_NOSCRIPT: - if ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - $this->processWithRulesFor($token, self::IN_BODY); - } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'noscript') { - /* Pop the current node (which will be a noscript element) from the - * stack of open elements; the new current node will be a head - * element. */ - array_pop($this->stack); - $this->mode = self::IN_HEAD; - } elseif ( - ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) || - ($token['type'] === HTML5_Tokenizer::COMMENT) || - ($token['type'] === HTML5_Tokenizer::STARTTAG && ( - $token['name'] === 'link' || $token['name'] === 'meta' || - $token['name'] === 'noframes' || $token['name'] === 'style'))) { - $this->processWithRulesFor($token, self::IN_HEAD); - // inverted logic - } elseif ( - ($token['type'] === HTML5_Tokenizer::STARTTAG && ( - $token['name'] === 'head' || $token['name'] === 'noscript')) || - ($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] !== 'br')) { - // parse error - } else { - // parse error - $this->emitToken(array( - 'type' => HTML5_Tokenizer::ENDTAG, - 'name' => 'noscript', - )); - $this->emitToken($token); - } - break; - - case self::AFTER_HEAD: - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - $this->processWithRulesFor($token, self::IN_BODY); - - /* A start tag token with the tag name "body" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'body') { - $this->insertElement($token); - - /* Set the frameset-ok flag to "not ok". */ - $this->flag_frameset_ok = false; - - /* Change the insertion mode to "in body". */ - $this->mode = self::IN_BODY; - - /* A start tag token with the tag name "frameset" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'frameset') { - /* Insert a frameset element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in frameset". */ - $this->mode = self::IN_FRAMESET; - - /* A start tag token whose tag name is one of: "base", "link", "meta", - "script", "style", "title" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'], - array('base', 'link', 'meta', 'noframes', 'script', 'style', 'title'))) { - // parse error - /* Push the node pointed to by the head element pointer onto the - * stack of open elements. */ - $this->stack[] = $this->head_pointer; - $this->processWithRulesFor($token, self::IN_HEAD); - array_splice($this->stack, array_search($this->head_pointer, $this->stack, true), 1); - - // inversion of specification - } elseif( - ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') || - ($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] !== 'body' && $token['name'] !== 'html' && - $token['name'] !== 'br')) { - // parse error - - /* Anything else */ - } else { - $this->emitToken(array( - 'name' => 'body', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - $this->flag_frameset_ok = true; - $this->emitToken($token); - } - break; - - case self::IN_BODY: - /* Handle the token as follows: */ - - switch($token['type']) { - /* A character token */ - case HTML5_Tokenizer::CHARACTER: - case HTML5_Tokenizer::SPACECHARACTER: - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Append the token's character to the current node. */ - $this->insertText($token['data']); - - /* If the token is not one of U+0009 CHARACTER TABULATION, - * U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020 - * SPACE, then set the frameset-ok flag to "not ok". */ - // i.e., if any of the characters is not whitespace - if (strlen($token['data']) !== strspn($token['data'], HTML5_Tokenizer::WHITESPACE)) { - $this->flag_frameset_ok = false; - } - break; - - /* A comment token */ - case HTML5_Tokenizer::COMMENT: - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - break; - - case HTML5_Tokenizer::DOCTYPE: - // parse error - break; - - case HTML5_Tokenizer::STARTTAG: - switch($token['name']) { - case 'html': - // parse error - /* For each attribute on the token, check to see if the - * attribute is already present on the top element of the - * stack of open elements. If it is not, add the attribute - * and its corresponding value to that element. */ - foreach($token['attr'] as $attr) { - if(!$this->stack[0]->hasAttribute($attr['name'])) { - $this->stack[0]->setAttribute($attr['name'], $attr['value']); - } - } - break; - - case 'base': case 'command': case 'link': case 'meta': case 'noframes': - case 'script': case 'style': case 'title': - /* Process the token as if the insertion mode had been "in - head". */ - $this->processWithRulesFor($token, self::IN_HEAD); - break; - - /* A start tag token with the tag name "body" */ - case 'body': - /* Parse error. If the second element on the stack of open - elements is not a body element, or, if the stack of open - elements has only one node on it, then ignore the token. - (fragment case) */ - if(count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') { - $this->ignored = true; - // Ignore - - /* Otherwise, for each attribute on the token, check to see - if the attribute is already present on the body element (the - second element) on the stack of open elements. If it is not, - add the attribute and its corresponding value to that - element. */ - } else { - foreach($token['attr'] as $attr) { - if(!$this->stack[1]->hasAttribute($attr['name'])) { - $this->stack[1]->setAttribute($attr['name'], $attr['value']); - } - } - } - break; - - case 'frameset': - // parse error - /* If the second element on the stack of open elements is - * not a body element, or, if the stack of open elements - * has only one node on it, then ignore the token. - * (fragment case) */ - if(count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') { - $this->ignored = true; - // Ignore - } elseif (!$this->flag_frameset_ok) { - $this->ignored = true; - // Ignore - } else { - /* 1. Remove the second element on the stack of open - * elements from its parent node, if it has one. */ - if($this->stack[1]->parentNode) { - $this->stack[1]->parentNode->removeChild($this->stack[1]); - } - - /* 2. Pop all the nodes from the bottom of the stack of - * open elements, from the current node up to the root - * html element. */ - array_splice($this->stack, 1); - - $this->insertElement($token); - $this->mode = self::IN_FRAMESET; - } - break; - - // in spec, there is a diversion here - - case 'address': case 'article': case 'aside': case 'blockquote': - case 'center': case 'datagrid': case 'details': case 'dialog': case 'dir': - case 'div': case 'dl': case 'fieldset': case 'figure': case 'footer': - case 'header': case 'hgroup': case 'menu': case 'nav': - case 'ol': case 'p': case 'section': case 'ul': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4", - "h5", "h6" */ - case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* If the current node is an element whose tag name is one - * of "h1", "h2", "h3", "h4", "h5", or "h6", then this is a - * parse error; pop the current node off the stack of open - * elements. */ - $peek = array_pop($this->stack); - if (in_array($peek->tagName, array("h1", "h2", "h3", "h4", "h5", "h6"))) { - // parse error - } else { - $this->stack[] = $peek; - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - case 'pre': case 'listing': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - $this->insertElement($token); - /* If the next token is a U+000A LINE FEED (LF) character - * token, then ignore that token and move on to the next - * one. (Newlines at the start of pre blocks are ignored as - * an authoring convenience.) */ - $this->ignore_lf_token = 2; - $this->flag_frameset_ok = false; - break; - - /* A start tag whose tag name is "form" */ - case 'form': - /* If the form element pointer is not null, ignore the - token with a parse error. */ - if($this->form_pointer !== null) { - $this->ignored = true; - // Ignore. - - /* Otherwise: */ - } else { - /* If the stack of open elements has a p element in - scope, then act as if an end tag with the tag name p - had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token, and set the - form element pointer to point to the element created. */ - $element = $this->insertElement($token); - $this->form_pointer = $element; - } - break; - - // condensed specification - case 'li': case 'dd': case 'dt': - /* 1. Set the frameset-ok flag to "not ok". */ - $this->flag_frameset_ok = false; - - $stack_length = count($this->stack) - 1; - for($n = $stack_length; 0 <= $n; $n--) { - /* 2. Initialise node to be the current node (the - bottommost node of the stack). */ - $stop = false; - $node = $this->stack[$n]; - $cat = $this->getElementCategory($node); - - // for case 'li': - /* 3. If node is an li element, then act as if an end - * tag with the tag name "li" had been seen, then jump - * to the last step. */ - // for case 'dd': case 'dt': - /* If node is a dd or dt element, then act as if an end - * tag with the same tag name as node had been seen, then - * jump to the last step. */ - if(($token['name'] === 'li' && $node->tagName === 'li') || - ($token['name'] !== 'li' && ($node->tagName === 'dd' || $node->tagName === 'dt'))) { // limited conditional - $this->emitToken(array( - 'type' => HTML5_Tokenizer::ENDTAG, - 'name' => $node->tagName, - )); - break; - } - - /* 4. If node is not in the formatting category, and is - not in the phrasing category, and is not an address, - div or p element, then stop this algorithm. */ - if($cat !== self::FORMATTING && $cat !== self::PHRASING && - $node->tagName !== 'address' && $node->tagName !== 'div' && - $node->tagName !== 'p') { - break; - } - - /* 5. Otherwise, set node to the previous entry in the - * stack of open elements and return to step 2. */ - } - - /* 6. This is the last step. */ - - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Finally, insert an HTML element with the same tag - name as the token's. */ - $this->insertElement($token); - break; - - /* A start tag token whose tag name is "plaintext" */ - case 'plaintext': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - $this->content_model = HTML5_Tokenizer::PLAINTEXT; - break; - - // more diversions - - /* A start tag whose tag name is "a" */ - case 'a': - /* If the list of active formatting elements contains - an element whose tag name is "a" between the end of the - list and the last marker on the list (or the start of - the list if there is no marker on the list), then this - is a parse error; act as if an end tag with the tag name - "a" had been seen, then remove that element from the list - of active formatting elements and the stack of open - elements if the end tag didn't already remove it (it - might not have if the element is not in table scope). */ - $leng = count($this->a_formatting); - - for($n = $leng - 1; $n >= 0; $n--) { - if($this->a_formatting[$n] === self::MARKER) { - break; - - } elseif($this->a_formatting[$n]->tagName === 'a') { - $a = $this->a_formatting[$n]; - $this->emitToken(array( - 'name' => 'a', - 'type' => HTML5_Tokenizer::ENDTAG - )); - if (in_array($a, $this->a_formatting)) { - $a_i = array_search($a, $this->a_formatting, true); - if($a_i !== false) array_splice($this->a_formatting, $a_i, 1); - } - if (in_array($a, $this->stack)) { - $a_i = array_search($a, $this->stack, true); - if ($a_i !== false) array_splice($this->stack, $a_i, 1); - } - break; - } - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - case 'b': case 'big': case 'code': case 'em': case 'font': case 'i': - case 's': case 'small': case 'strike': - case 'strong': case 'tt': case 'u': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - case 'nobr': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* If the stack of open elements has a nobr element in - * scope, then this is a parse error; act as if an end tag - * with the tag name "nobr" had been seen, then once again - * reconstruct the active formatting elements, if any. */ - if ($this->elementInScope('nobr')) { - $this->emitToken(array( - 'name' => 'nobr', - 'type' => HTML5_Tokenizer::ENDTAG, - )); - $this->reconstructActiveFormattingElements(); - } - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - // another diversion - - /* A start tag token whose tag name is "button" */ - case 'button': - /* If the stack of open elements has a button element in scope, - then this is a parse error; act as if an end tag with the tag - name "button" had been seen, then reprocess the token. (We don't - do that. Unnecessary.) (I hope you're right! -- ezyang) */ - if($this->elementInScope('button')) { - $this->emitToken(array( - 'name' => 'button', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - - $this->flag_frameset_ok = false; - break; - - case 'applet': case 'marquee': case 'object': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - - $this->flag_frameset_ok = false; - break; - - // spec diversion - - /* A start tag whose tag name is "table" */ - case 'table': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->quirks_mode !== self::QUIRKS_MODE && - $this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - $this->flag_frameset_ok = false; - - /* Change the insertion mode to "in table". */ - $this->mode = self::IN_TABLE; - break; - - /* A start tag whose tag name is one of: "area", "basefont", - "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */ - case 'area': case 'basefont': case 'bgsound': case 'br': - case 'embed': case 'img': case 'input': case 'keygen': case 'spacer': - case 'wbr': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - // YYY: Acknowledge the token's self-closing flag, if it is set. - - $this->flag_frameset_ok = false; - break; - - case 'param': case 'source': - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - // YYY: Acknowledge the token's self-closing flag, if it is set. - break; - - /* A start tag whose tag name is "hr" */ - case 'hr': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - // YYY: Acknowledge the token's self-closing flag, if it is set. - - $this->flag_frameset_ok = false; - break; - - /* A start tag whose tag name is "image" */ - case 'image': - /* Parse error. Change the token's tag name to "img" and - reprocess it. (Don't ask.) */ - $token['name'] = 'img'; - $this->emitToken($token); - break; - - /* A start tag whose tag name is "isindex" */ - case 'isindex': - /* Parse error. */ - - /* If the form element pointer is not null, - then ignore the token. */ - if($this->form_pointer === null) { - /* Act as if a start tag token with the tag name "form" had - been seen. */ - /* If the token has an attribute called "action", set - * the action attribute on the resulting form - * element to the value of the "action" attribute of - * the token. */ - $attr = array(); - $action = $this->getAttr($token, 'action'); - if ($action !== false) { - $attr[] = array('name' => 'action', 'value' => $action); - } - $this->emitToken(array( - 'name' => 'form', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => $attr - )); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->emitToken(array( - 'name' => 'hr', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - - /* Act as if a start tag token with the tag name "p" had - been seen. */ - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - - /* Act as if a start tag token with the tag name "label" - had been seen. */ - $this->emitToken(array( - 'name' => 'label', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - - /* Act as if a stream of character tokens had been seen. */ - $prompt = $this->getAttr($token, 'prompt'); - if ($prompt === false) { - $prompt = 'This is a searchable index. '. - 'Insert your search keywords here: '; - } - $this->emitToken(array( - 'data' => $prompt, - 'type' => HTML5_Tokenizer::CHARACTER, - )); - - /* Act as if a start tag token with the tag name "input" - had been seen, with all the attributes from the "isindex" - token, except with the "name" attribute set to the value - "isindex" (ignoring any explicit "name" attribute). */ - $attr = array(); - foreach ($token['attr'] as $keypair) { - if ($keypair['name'] === 'name' || $keypair['name'] === 'action' || - $keypair['name'] === 'prompt') continue; - $attr[] = $keypair; - } - $attr[] = array('name' => 'name', 'value' => 'isindex'); - - $this->emitToken(array( - 'name' => 'input', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => $attr - )); - - /* Act as if an end tag token with the tag name "label" - had been seen. */ - $this->emitToken(array( - 'name' => 'label', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - /* Act as if an end tag token with the tag name "p" had - been seen. */ - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->emitToken(array( - 'name' => 'hr', - 'type' => HTML5_Tokenizer::STARTTAG - )); - - /* Act as if an end tag token with the tag name "form" had - been seen. */ - $this->emitToken(array( - 'name' => 'form', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } else { - $this->ignored = true; - } - break; - - /* A start tag whose tag name is "textarea" */ - case 'textarea': - $this->insertElement($token); - - /* If the next token is a U+000A LINE FEED (LF) - * character token, then ignore that token and move on to - * the next one. (Newlines at the start of textarea - * elements are ignored as an authoring convenience.) - * need flag, see also <pre> */ - $this->ignore_lf_token = 2; - - $this->original_mode = $this->mode; - $this->flag_frameset_ok = false; - $this->mode = self::IN_CDATA_RCDATA; - - /* Switch the tokeniser's content model flag to the - RCDATA state. */ - $this->content_model = HTML5_Tokenizer::RCDATA; - break; - - /* A start tag token whose tag name is "xmp" */ - case 'xmp': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - $this->flag_frameset_ok = false; - - $this->insertCDATAElement($token); - break; - - case 'iframe': - $this->flag_frameset_ok = false; - $this->insertCDATAElement($token); - break; - - case 'noembed': case 'noscript': - // XSCRIPT: should check scripting flag - $this->insertCDATAElement($token); - break; - - /* A start tag whose tag name is "select" */ - case 'select': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - $this->flag_frameset_ok = false; - - /* If the insertion mode is one of in table", "in caption", - * "in column group", "in table body", "in row", or "in - * cell", then switch the insertion mode to "in select in - * table". Otherwise, switch the insertion mode to "in - * select". */ - if ( - $this->mode === self::IN_TABLE || $this->mode === self::IN_CAPTION || - $this->mode === self::IN_COLUMN_GROUP || $this->mode ==+self::IN_TABLE_BODY || - $this->mode === self::IN_ROW || $this->mode === self::IN_CELL - ) { - $this->mode = self::IN_SELECT_IN_TABLE; - } else { - $this->mode = self::IN_SELECT; - } - break; - - case 'option': case 'optgroup': - if ($this->elementInScope('option')) { - $this->emitToken(array( - 'name' => 'option', - 'type' => HTML5_Tokenizer::ENDTAG, - )); - } - $this->reconstructActiveFormattingElements(); - $this->insertElement($token); - break; - - case 'rp': case 'rt': - /* If the stack of open elements has a ruby element in scope, then generate - * implied end tags. If the current node is not then a ruby element, this is - * a parse error; pop all the nodes from the current node up to the node - * immediately before the bottommost ruby element on the stack of open elements. - */ - if ($this->elementInScope('ruby')) { - $this->generateImpliedEndTags(); - } - $peek = false; - do { - if ($peek) { - // parse error - } - $peek = array_pop($this->stack); - } while ($peek->tagName !== 'ruby'); - $this->stack[] = $peek; // we popped one too many - $this->insertElement($token); - break; - - // spec diversion - - case 'math': - $this->reconstructActiveFormattingElements(); - $token = $this->adjustMathMLAttributes($token); - $token = $this->adjustForeignAttributes($token); - $this->insertForeignElement($token, self::NS_MATHML); - if (isset($token['self-closing'])) { - // XERROR: acknowledge the token's self-closing flag - array_pop($this->stack); - } - if ($this->mode !== self::IN_FOREIGN_CONTENT) { - $this->secondary_mode = $this->mode; - $this->mode = self::IN_FOREIGN_CONTENT; - } - break; - - case 'svg': - $this->reconstructActiveFormattingElements(); - $token = $this->adjustSVGAttributes($token); - $token = $this->adjustForeignAttributes($token); - $this->insertForeignElement($token, self::NS_SVG); - if (isset($token['self-closing'])) { - // XERROR: acknowledge the token's self-closing flag - array_pop($this->stack); - } - if ($this->mode !== self::IN_FOREIGN_CONTENT) { - $this->secondary_mode = $this->mode; - $this->mode = self::IN_FOREIGN_CONTENT; - } - break; - - case 'caption': case 'col': case 'colgroup': case 'frame': case 'head': - case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead': case 'tr': - // parse error - break; - - /* A start tag token not covered by the previous entries */ - default: - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - $this->insertElement($token); - /* This element will be a phrasing element. */ - break; - } - break; - - case HTML5_Tokenizer::ENDTAG: - switch($token['name']) { - /* An end tag with the tag name "body" */ - case 'body': - /* If the second element in the stack of open elements is - not a body element, this is a parse error. Ignore the token. - (innerHTML case) */ - if(count($this->stack) < 2 || $this->stack[1]->tagName !== 'body') { - $this->ignored = true; - - /* Otherwise, if there is a node in the stack of open - * elements that is not either a dd element, a dt - * element, an li element, an optgroup element, an - * option element, a p element, an rp element, an rt - * element, a tbody element, a td element, a tfoot - * element, a th element, a thead element, a tr element, - * the body element, or the html element, then this is a - * parse error. */ - } else { - // XERROR: implement this check for parse error - } - - /* Change the insertion mode to "after body". */ - $this->mode = self::AFTER_BODY; - break; - - /* An end tag with the tag name "html" */ - case 'html': - /* Act as if an end tag with tag name "body" had been seen, - then, if that token wasn't ignored, reprocess the current - token. */ - $this->emitToken(array( - 'name' => 'body', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - if (!$this->ignored) $this->emitToken($token); - break; - - case 'address': case 'article': case 'aside': case 'blockquote': - case 'center': case 'datagrid': case 'details': case 'dir': - case 'div': case 'dl': case 'fieldset': case 'figure': case 'footer': - case 'header': case 'hgroup': case 'listing': case 'menu': - case 'nav': case 'ol': case 'pre': case 'section': case 'ul': - /* If the stack of open elements has an element in scope - with the same tag name as that of the token, then generate - implied end tags. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with - the same tag name as that of the token, then this - is a parse error. */ - // XERROR: implement parse error logic - - /* If the stack of open elements has an element in - scope with the same tag name as that of the token, - then pop elements from this stack until an element - with that tag name has been popped from the stack. */ - do { - $node = array_pop($this->stack); - } while ($node->tagName !== $token['name']); - } else { - // parse error - } - break; - - /* An end tag whose tag name is "form" */ - case 'form': - /* Let node be the element that the form element pointer is set to. */ - $node = $this->form_pointer; - /* Set the form element pointer to null. */ - $this->form_pointer = null; - /* If node is null or the stack of open elements does not - * have node in scope, then this is a parse error; ignore the token. */ - if ($node === null || !in_array($node, $this->stack)) { - // parse error - $this->ignored = true; - } else { - /* 1. Generate implied end tags. */ - $this->generateImpliedEndTags(); - /* 2. If the current node is not node, then this is a parse error. */ - if (end($this->stack) !== $node) { - // parse error - } - /* 3. Remove node from the stack of open elements. */ - array_splice($this->stack, array_search($node, $this->stack, true), 1); - } - - break; - - /* An end tag whose tag name is "p" */ - case 'p': - /* If the stack of open elements has a p element in scope, - then generate implied end tags, except for p elements. */ - if($this->elementInScope('p')) { - /* Generate implied end tags, except for elements with - * the same tag name as the token. */ - $this->generateImpliedEndTags(array('p')); - - /* If the current node is not a p element, then this is - a parse error. */ - // XERROR: implement - - /* Pop elements from the stack of open elements until - * an element with the same tag name as the token has - * been popped from the stack. */ - do { - $node = array_pop($this->stack); - } while ($node->tagName !== 'p'); - - } else { - // parse error - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::STARTTAG, - )); - $this->emitToken($token); - } - break; - - /* An end tag whose tag name is "dd", "dt", or "li" */ - case 'dd': case 'dt': case 'li': - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(array($token['name'])); - - /* If the current node is not an element with the same - tag name as the token, then this is a parse error. */ - // XERROR: implement parse error - - /* Pop elements from the stack of open elements until - * an element with the same tag name as the token has - * been popped from the stack. */ - do { - $node = array_pop($this->stack); - } while ($node->tagName !== $token['name']); - - } else { - // parse error - } - break; - - /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4", - "h5", "h6" */ - case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': - $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'); - - /* If the stack of open elements has in scope an element whose - tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then - generate implied end tags. */ - if($this->elementInScope($elements)) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with the same - tag name as that of the token, then this is a parse error. */ - // XERROR: implement parse error - - /* If the stack of open elements has in scope an element - whose tag name is one of "h1", "h2", "h3", "h4", "h5", or - "h6", then pop elements from the stack until an element - with one of those tag names has been popped from the stack. */ - do { - $node = array_pop($this->stack); - } while (!in_array($node->tagName, $elements)); - } else { - // parse error - } - break; - - /* An end tag whose tag name is one of: "a", "b", "big", "em", - "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ - case 'a': case 'b': case 'big': case 'code': case 'em': case 'font': - case 'i': case 'nobr': case 's': case 'small': case 'strike': - case 'strong': case 'tt': case 'u': - // XERROR: generally speaking this needs parse error logic - /* 1. Let the formatting element be the last element in - the list of active formatting elements that: - * is between the end of the list and the last scope - marker in the list, if any, or the start of the list - otherwise, and - * has the same tag name as the token. - */ - while(true) { - for($a = count($this->a_formatting) - 1; $a >= 0; $a--) { - if($this->a_formatting[$a] === self::MARKER) { - break; - - } elseif($this->a_formatting[$a]->tagName === $token['name']) { - $formatting_element = $this->a_formatting[$a]; - $in_stack = in_array($formatting_element, $this->stack, true); - $fe_af_pos = $a; - break; - } - } - - /* If there is no such node, or, if that node is - also in the stack of open elements but the element - is not in scope, then this is a parse error. Abort - these steps. The token is ignored. */ - if(!isset($formatting_element) || ($in_stack && - !$this->elementInScope($token['name']))) { - $this->ignored = true; - break; - - /* Otherwise, if there is such a node, but that node - is not in the stack of open elements, then this is a - parse error; remove the element from the list, and - abort these steps. */ - } elseif(isset($formatting_element) && !$in_stack) { - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - break; - } - - /* Otherwise, there is a formatting element and that - * element is in the stack and is in scope. If the - * element is not the current node, this is a parse - * error. In any case, proceed with the algorithm as - * written in the following steps. */ - // XERROR: implement me - - /* 2. Let the furthest block be the topmost node in the - stack of open elements that is lower in the stack - than the formatting element, and is not an element in - the phrasing or formatting categories. There might - not be one. */ - $fe_s_pos = array_search($formatting_element, $this->stack, true); - $length = count($this->stack); - - for($s = $fe_s_pos + 1; $s < $length; $s++) { - $category = $this->getElementCategory($this->stack[$s]); - - if($category !== self::PHRASING && $category !== self::FORMATTING) { - $furthest_block = $this->stack[$s]; - break; - } - } - - /* 3. If there is no furthest block, then the UA must - skip the subsequent steps and instead just pop all - the nodes from the bottom of the stack of open - elements, from the current node up to the formatting - element, and remove the formatting element from the - list of active formatting elements. */ - if(!isset($furthest_block)) { - for($n = $length - 1; $n >= $fe_s_pos; $n--) { - array_pop($this->stack); - } - - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - break; - } - - /* 4. Let the common ancestor be the element - immediately above the formatting element in the stack - of open elements. */ - $common_ancestor = $this->stack[$fe_s_pos - 1]; - - /* 5. Let a bookmark note the position of the - formatting element in the list of active formatting - elements relative to the elements on either side - of it in the list. */ - $bookmark = $fe_af_pos; - - /* 6. Let node and last node be the furthest block. - Follow these steps: */ - $node = $furthest_block; - $last_node = $furthest_block; - - while(true) { - for($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) { - /* 6.1 Let node be the element immediately - prior to node in the stack of open elements. */ - $node = $this->stack[$n]; - - /* 6.2 If node is not in the list of active - formatting elements, then remove node from - the stack of open elements and then go back - to step 1. */ - if(!in_array($node, $this->a_formatting, true)) { - array_splice($this->stack, $n, 1); - - } else { - break; - } - } - - /* 6.3 Otherwise, if node is the formatting - element, then go to the next step in the overall - algorithm. */ - if($node === $formatting_element) { - break; - - /* 6.4 Otherwise, if last node is the furthest - block, then move the aforementioned bookmark to - be immediately after the node in the list of - active formatting elements. */ - } elseif($last_node === $furthest_block) { - $bookmark = array_search($node, $this->a_formatting, true) + 1; - } - - /* 6.5 Create an element for the token for which - * the element node was created, replace the entry - * for node in the list of active formatting - * elements with an entry for the new element, - * replace the entry for node in the stack of open - * elements with an entry for the new element, and - * let node be the new element. */ - // we don't know what the token is anymore - $clone = $node->cloneNode(); - $a_pos = array_search($node, $this->a_formatting, true); - $s_pos = array_search($node, $this->stack, true); - $this->a_formatting[$a_pos] = $clone; - $this->stack[$s_pos] = $clone; - $node = $clone; - - /* 6.6 Insert last node into node, first removing - it from its previous parent node if any. */ - if($last_node->parentNode !== null) { - $last_node->parentNode->removeChild($last_node); - } - - $node->appendChild($last_node); - - /* 6.7 Let last node be node. */ - $last_node = $node; - - /* 6.8 Return to step 1 of this inner set of steps. */ - } - - /* 7. If the common ancestor node is a table, tbody, - * tfoot, thead, or tr element, then, foster parent - * whatever last node ended up being in the previous - * step, first removing it from its previous parent - * node if any. */ - if ($last_node->parentNode) { // common step - $last_node->parentNode->removeChild($last_node); - } - if (in_array($common_ancestor->tagName, array('table', 'tbody', 'tfoot', 'thead', 'tr'))) { - $this->fosterParent($last_node); - /* Otherwise, append whatever last node ended up being - * in the previous step to the common ancestor node, - * first removing it from its previous parent node if - * any. */ - } else { - $common_ancestor->appendChild($last_node); - } - - /* 8. Create an element for the token for which the - * formatting element was created. */ - $clone = $formatting_element->cloneNode(); - - /* 9. Take all of the child nodes of the furthest - block and append them to the element created in the - last step. */ - while($furthest_block->hasChildNodes()) { - $child = $furthest_block->firstChild; - $furthest_block->removeChild($child); - $clone->appendChild($child); - } - - /* 10. Append that clone to the furthest block. */ - $furthest_block->appendChild($clone); - - /* 11. Remove the formatting element from the list - of active formatting elements, and insert the new element - into the list of active formatting elements at the - position of the aforementioned bookmark. */ - $fe_af_pos = array_search($formatting_element, $this->a_formatting, true); - array_splice($this->a_formatting, $fe_af_pos, 1); - - $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1); - $af_part2 = array_slice($this->a_formatting, $bookmark); - $this->a_formatting = array_merge($af_part1, array($clone), $af_part2); - - /* 12. Remove the formatting element from the stack - of open elements, and insert the new element into the stack - of open elements immediately below the position of the - furthest block in that stack. */ - $fe_s_pos = array_search($formatting_element, $this->stack, true); - array_splice($this->stack, $fe_s_pos, 1); - - $fb_s_pos = array_search($furthest_block, $this->stack, true); - $s_part1 = array_slice($this->stack, 0, $fb_s_pos + 1); - $s_part2 = array_slice($this->stack, $fb_s_pos + 1); - $this->stack = array_merge($s_part1, array($clone), $s_part2); - - /* 13. Jump back to step 1 in this series of steps. */ - unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block); - } - break; - - case 'applet': case 'button': case 'marquee': case 'object': - /* If the stack of open elements has an element in scope whose - tag name matches the tag name of the token, then generate implied - tags. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with the same - tag name as the token, then this is a parse error. */ - // XERROR: implement logic - - /* Pop elements from the stack of open elements until - * an element with the same tag name as the token has - * been popped from the stack. */ - do { - $node = array_pop($this->stack); - } while ($node->tagName !== $token['name']); - - /* Clear the list of active formatting elements up to the - * last marker. */ - $keys = array_keys($this->a_formatting, self::MARKER, true); - $marker = end($keys); - - for($n = count($this->a_formatting) - 1; $n > $marker; $n--) { - array_pop($this->a_formatting); - } - } else { - // parse error - } - break; - - case 'br': - // Parse error - $this->emitToken(array( - 'name' => 'br', - 'type' => HTML5_Tokenizer::STARTTAG, - )); - break; - - /* An end tag token not covered by the previous entries */ - default: - for($n = count($this->stack) - 1; $n >= 0; $n--) { - /* Initialise node to be the current node (the bottommost - node of the stack). */ - $node = $this->stack[$n]; - - /* If node has the same tag name as the end tag token, - then: */ - if($token['name'] === $node->tagName) { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* If the tag name of the end tag token does not - match the tag name of the current node, this is a - parse error. */ - // XERROR: implement this - - /* Pop all the nodes from the current node up to - node, including node, then stop these steps. */ - // XSKETCHY - do { - $pop = array_pop($this->stack); - } while ($pop !== $node); - break; - - } else { - $category = $this->getElementCategory($node); - - if($category !== self::FORMATTING && $category !== self::PHRASING) { - /* Otherwise, if node is in neither the formatting - category nor the phrasing category, then this is a - parse error. Stop this algorithm. The end tag token - is ignored. */ - $this->ignored = true; - break; - // parse error - } - } - /* Set node to the previous entry in the stack of open elements. Loop. */ - } - break; - } - break; - } - break; - - case self::IN_CDATA_RCDATA: - if ( - $token['type'] === HTML5_Tokenizer::CHARACTER || - $token['type'] === HTML5_Tokenizer::SPACECHARACTER - ) { - $this->insertText($token['data']); - } elseif ($token['type'] === HTML5_Tokenizer::EOF) { - // parse error - /* If the current node is a script element, mark the script - * element as "already executed". */ - // probably not necessary - array_pop($this->stack); - $this->mode = $this->original_mode; - $this->emitToken($token); - } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'script') { - array_pop($this->stack); - $this->mode = $this->original_mode; - // we're ignoring all of the execution stuff - } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG) { - array_pop($this->stack); - $this->mode = $this->original_mode; - } - break; - - case self::IN_TABLE: - $clear = array('html', 'table'); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5_Tokenizer::SPACECHARACTER && - /* If the current table is tainted, then act as described in - * the "anything else" entry below. */ - // Note: hsivonen has a test that fails due to this line - // because he wants to convince Hixie not to do taint - !$this->currentTableIsTainted()) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - - /* A start tag whose tag name is "caption" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'caption') { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - - /* Insert an HTML element for the token, then switch the - insertion mode to "in caption". */ - $this->insertElement($token); - $this->mode = self::IN_CAPTION; - - /* A start tag whose tag name is "colgroup" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'colgroup') { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the - insertion mode to "in column group". */ - $this->insertElement($token); - $this->mode = self::IN_COLUMN_GROUP; - - /* A start tag whose tag name is "col" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'col') { - $this->emitToken(array( - 'name' => 'colgroup', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - - $this->emitToken($token); - - /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'], - array('tbody', 'tfoot', 'thead'))) { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the insertion - mode to "in table body". */ - $this->insertElement($token); - $this->mode = self::IN_TABLE_BODY; - - /* A start tag whose tag name is one of: "td", "th", "tr" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - in_array($token['name'], array('td', 'th', 'tr'))) { - /* Act as if a start tag token with the tag name "tbody" had been - seen, then reprocess the current token. */ - $this->emitToken(array( - 'name' => 'tbody', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - - $this->emitToken($token); - - /* A start tag whose tag name is "table" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'table') { - /* Parse error. Act as if an end tag token with the tag name "table" - had been seen, then, if that token wasn't ignored, reprocess the - current token. */ - $this->emitToken(array( - 'name' => 'table', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - if (!$this->ignored) $this->emitToken($token); - - /* An end tag whose tag name is "table" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] === 'table') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (fragment case) */ - if(!$this->elementInScope($token['name'], true)) { - $this->ignored = true; - - /* Otherwise: */ - } else { - do { - $node = array_pop($this->stack); - } while ($node->tagName !== 'table'); - - /* Reset the insertion mode appropriately. */ - $this->resetInsertionMode(); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td', - 'tfoot', 'th', 'thead', 'tr'))) { - // Parse error. Ignore the token. - - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - ($token['name'] === 'style' || $token['name'] === 'script')) { - $this->processWithRulesFor($token, self::IN_HEAD); - - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'input' && - // assignment is intentional - /* If the token does not have an attribute with the name "type", or - * if it does, but that attribute's value is not an ASCII - * case-insensitive match for the string "hidden", then: act as - * described in the "anything else" entry below. */ - ($type = $this->getAttr($token, 'type')) && strtolower($type) === 'hidden') { - // I.e., if its an input with the type attribute == 'hidden' - /* Otherwise */ - // parse error - $this->insertElement($token); - array_pop($this->stack); - } elseif ($token['type'] === HTML5_Tokenizer::EOF) { - /* If the current node is not the root html element, then this is a parse error. */ - if (end($this->stack)->tagName !== 'html') { - // Note: It can only be the current node in the fragment case. - // parse error - } - /* Stop parsing. */ - /* Anything else */ - } else { - /* Parse error. Process the token as if the insertion mode was "in - body", with the following exception: */ - - $old = $this->foster_parent; - $this->foster_parent = true; - $this->processWithRulesFor($token, self::IN_BODY); - $this->foster_parent = $old; - } - break; - - case self::IN_CAPTION: - /* An end tag whose tag name is "caption" */ - if($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'caption') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (fragment case) */ - if(!$this->elementInScope($token['name'], true)) { - $this->ignored = true; - // Ignore - - /* Otherwise: */ - } else { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Now, if the current node is not a caption element, then this - is a parse error. */ - // XERROR: implement - - /* Pop elements from this stack until a caption element has - been popped from the stack. */ - do { - $node = array_pop($this->stack); - } while ($node->tagName !== 'caption'); - - /* Clear the list of active formatting elements up to the last - marker. */ - $this->clearTheActiveFormattingElementsUpToTheLastMarker(); - - /* Switch the insertion mode to "in table". */ - $this->mode = self::IN_TABLE; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag - name is "table" */ - } elseif(($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', - 'thead', 'tr'))) || ($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] === 'table')) { - /* Parse error. Act as if an end tag with the tag name "caption" - had been seen, then, if that token wasn't ignored, reprocess the - current token. */ - $this->emitToken(array( - 'name' => 'caption', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - if (!$this->ignored) $this->emitToken($token); - - /* An end tag whose tag name is one of: "body", "col", "colgroup", - "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'], - array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th', - 'thead', 'tr'))) { - // Parse error. Ignore the token. - $this->ignored = true; - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in body". */ - $this->processWithRulesFor($token, self::IN_BODY); - } - break; - - case self::IN_COLUMN_GROUP: - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertToken($token['data']); - - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - $this->processWithRulesFor($token, self::IN_BODY); - - /* A start tag whose tag name is "col" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'col') { - /* Insert a col element for the token. Immediately pop the current - node off the stack of open elements. */ - $this->insertElement($token); - array_pop($this->stack); - // XERROR: Acknowledge the token's self-closing flag, if it is set. - - /* An end tag whose tag name is "colgroup" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] === 'colgroup') { - /* If the current node is the root html element, then this is a - parse error, ignore the token. (fragment case) */ - if(end($this->stack)->tagName === 'html') { - $this->ignored = true; - - /* Otherwise, pop the current node (which will be a colgroup - element) from the stack of open elements. Switch the insertion - mode to "in table". */ - } else { - array_pop($this->stack); - $this->mode = self::IN_TABLE; - } - - /* An end tag whose tag name is "col" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'col') { - /* Parse error. Ignore the token. */ - $this->ignored = true; - - /* An end-of-file token */ - /* If the current node is the root html element */ - } elseif($token['type'] === HTML5_Tokenizer::EOF && end($this->stack)->tagName === 'html') { - /* Stop parsing */ - - /* Anything else */ - } else { - /* Act as if an end tag with the tag name "colgroup" had been seen, - and then, if that token wasn't ignored, reprocess the current token. */ - $this->emitToken(array( - 'name' => 'colgroup', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - if (!$this->ignored) $this->emitToken($token); - } - break; - - case self::IN_TABLE_BODY: - $clear = array('tbody', 'tfoot', 'thead', 'html'); - - /* A start tag whose tag name is "tr" */ - if($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'tr') { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Insert a tr element for the token, then switch the insertion - mode to "in row". */ - $this->insertElement($token); - $this->mode = self::IN_ROW; - - /* A start tag whose tag name is one of: "th", "td" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - ($token['name'] === 'th' || $token['name'] === 'td')) { - /* Parse error. Act as if a start tag with the tag name "tr" had - been seen, then reprocess the current token. */ - $this->emitToken(array( - 'name' => 'tr', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - - $this->emitToken($token); - - /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - in_array($token['name'], array('tbody', 'tfoot', 'thead'))) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. */ - if(!$this->elementInScope($token['name'], true)) { - // Parse error - $this->ignored = true; - - /* Otherwise: */ - } else { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Pop the current node from the stack of open elements. Switch - the insertion mode to "in table". */ - array_pop($this->stack); - $this->mode = self::IN_TABLE; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */ - } elseif(($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead'))) || - ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) { - /* If the stack of open elements does not have a tbody, thead, or - tfoot element in table scope, this is a parse error. Ignore the - token. (fragment case) */ - if(!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) { - // parse error - $this->ignored = true; - - /* Otherwise: */ - } else { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Act as if an end tag with the same tag name as the current - node ("tbody", "tfoot", or "thead") had been seen, then - reprocess the current token. */ - $this->emitToken(array( - 'name' => end($this->stack)->tagName, - 'type' => HTML5_Tokenizer::ENDTAG - )); - - $this->emitToken($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "td", "th", "tr" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) { - /* Parse error. Ignore the token. */ - $this->ignored = true; - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in table". */ - $this->processWithRulesFor($token, self::IN_TABLE); - } - break; - - case self::IN_ROW: - $clear = array('tr', 'html'); - - /* A start tag whose tag name is one of: "th", "td" */ - if($token['type'] === HTML5_Tokenizer::STARTTAG && - ($token['name'] === 'th' || $token['name'] === 'td')) { - /* Clear the stack back to a table row context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the insertion - mode to "in cell". */ - $this->insertElement($token); - $this->mode = self::IN_CELL; - - /* Insert a marker at the end of the list of active formatting - elements. */ - $this->a_formatting[] = self::MARKER; - - /* An end tag whose tag name is "tr" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'tr') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (fragment case) */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore. - $this->ignored = true; - - /* Otherwise: */ - } else { - /* Clear the stack back to a table row context. */ - $this->clearStackToTableContext($clear); - - /* Pop the current node (which will be a tr element) from the - stack of open elements. Switch the insertion mode to "in table - body". */ - array_pop($this->stack); - $this->mode = self::IN_TABLE_BODY; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */ - } elseif(($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) || - ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) { - /* Act as if an end tag with the tag name "tr" had been seen, then, - if that token wasn't ignored, reprocess the current token. */ - $this->emitToken(array( - 'name' => 'tr', - 'type' => HTML5_Tokenizer::ENDTAG - )); - if (!$this->ignored) $this->emitToken($token); - - /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - in_array($token['name'], array('tbody', 'tfoot', 'thead'))) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. */ - if(!$this->elementInScope($token['name'], true)) { - $this->ignored = true; - - /* Otherwise: */ - } else { - /* Otherwise, act as if an end tag with the tag name "tr" had - been seen, then reprocess the current token. */ - $this->emitToken(array( - 'name' => 'tr', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - $this->emitToken($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "td", "th" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th'))) { - /* Parse error. Ignore the token. */ - $this->ignored = true; - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in table". */ - $this->processWithRulesFor($token, self::IN_TABLE); - } - break; - - case self::IN_CELL: - /* An end tag whose tag name is one of: "td", "th" */ - if($token['type'] === HTML5_Tokenizer::ENDTAG && - ($token['name'] === 'td' || $token['name'] === 'th')) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as that of the token, then this is a - parse error and the token must be ignored. */ - if(!$this->elementInScope($token['name'], true)) { - $this->ignored = true; - - /* Otherwise: */ - } else { - /* Generate implied end tags, except for elements with the same - tag name as the token. */ - $this->generateImpliedEndTags(array($token['name'])); - - /* Now, if the current node is not an element with the same tag - name as the token, then this is a parse error. */ - // XERROR: Implement parse error code - - /* Pop elements from this stack until an element with the same - tag name as the token has been popped from the stack. */ - do { - $node = array_pop($this->stack); - } while ($node->tagName !== $token['name']); - - /* Clear the list of active formatting elements up to the last - marker. */ - $this->clearTheActiveFormattingElementsUpToTheLastMarker(); - - /* Switch the insertion mode to "in row". (The current node - will be a tr element at this point.) */ - $this->mode = self::IN_ROW; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', - 'thead', 'tr'))) { - /* If the stack of open elements does not have a td or th element - in table scope, then this is a parse error; ignore the token. - (fragment case) */ - if(!$this->elementInScope(array('td', 'th'), true)) { - // parse error - $this->ignored = true; - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - $this->emitToken($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html'))) { - /* Parse error. Ignore the token. */ - $this->ignored = true; - - /* An end tag whose tag name is one of: "table", "tbody", "tfoot", - "thead", "tr" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'], - array('table', 'tbody', 'tfoot', 'thead', 'tr'))) { - /* If the stack of open elements does not have a td or th element - in table scope, then this is a parse error; ignore the token. - (innerHTML case) */ - if(!$this->elementInScope(array('td', 'th'), true)) { - // Parse error - $this->ignored = true; - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - $this->emitToken($token); - } - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in body". */ - $this->processWithRulesFor($token, self::IN_BODY); - } - break; - - case self::IN_SELECT: - /* Handle the token as follows: */ - - /* A character token */ - if( - $token['type'] === HTML5_Tokenizer::CHARACTER || - $token['type'] === HTML5_Tokenizer::SPACECHARACTER - ) { - /* Append the token's character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - $this->processWithRulesFor($token, self::INBODY); - - /* A start tag token whose tag name is "option" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'option') { - /* If the current node is an option element, act as if an end tag - with the tag name "option" had been seen. */ - if(end($this->stack)->tagName === 'option') { - $this->emitToken(array( - 'name' => 'option', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* A start tag token whose tag name is "optgroup" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'optgroup') { - /* If the current node is an option element, act as if an end tag - with the tag name "option" had been seen. */ - if(end($this->stack)->tagName === 'option') { - $this->emitToken(array( - 'name' => 'option', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* If the current node is an optgroup element, act as if an end tag - with the tag name "optgroup" had been seen. */ - if(end($this->stack)->tagName === 'optgroup') { - $this->emitToken(array( - 'name' => 'optgroup', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* An end tag token whose tag name is "optgroup" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] === 'optgroup') { - /* First, if the current node is an option element, and the node - immediately before it in the stack of open elements is an optgroup - element, then act as if an end tag with the tag name "option" had - been seen. */ - $elements_in_stack = count($this->stack); - - if($this->stack[$elements_in_stack - 1]->tagName === 'option' && - $this->stack[$elements_in_stack - 2]->tagName === 'optgroup') { - $this->emitToken(array( - 'name' => 'option', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* If the current node is an optgroup element, then pop that node - from the stack of open elements. Otherwise, this is a parse error, - ignore the token. */ - if(end($this->stack)->tagName === 'optgroup') { - array_pop($this->stack); - } else { - // parse error - $this->ignored = true; - } - - /* An end tag token whose tag name is "option" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] === 'option') { - /* If the current node is an option element, then pop that node - from the stack of open elements. Otherwise, this is a parse error, - ignore the token. */ - if(end($this->stack)->tagName === 'option') { - array_pop($this->stack); - } else { - // parse error - $this->ignored = true; - } - - /* An end tag whose tag name is "select" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] === 'select') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (fragment case) */ - if(!$this->elementInScope($token['name'], true)) { - $this->ignored = true; - // parse error - - /* Otherwise: */ - } else { - /* Pop elements from the stack of open elements until a select - element has been popped from the stack. */ - do { - $node = array_pop($this->stack); - } while ($node->tagName !== 'select'); - - /* Reset the insertion mode appropriately. */ - $this->resetInsertionMode(); - } - - /* A start tag whose tag name is "select" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'select') { - /* Parse error. Act as if the token had been an end tag with the - tag name "select" instead. */ - $this->emitToken(array( - 'name' => 'select', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - ($token['name'] === 'input' || $token['name'] === 'textarea')) { - // parse error - $this->emitToken(array( - 'name' => 'select', - 'type' => HTML5_Tokenizer::ENDTAG - )); - $this->emitToken($token); - - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') { - $this->processWithRulesFor($token, self::IN_HEAD); - - } elseif($token['type'] === HTML5_Tokenizer::EOF) { - // XERROR: If the current node is not the root html element, then this is a parse error. - /* Stop parsing */ - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - $this->ignored = true; - } - break; - - case self::IN_SELECT_IN_TABLE: - - if($token['type'] === HTML5_Tokenizer::STARTTAG && - in_array($token['name'], array('caption', 'table', 'tbody', - 'tfoot', 'thead', 'tr', 'td', 'th'))) { - // parse error - $this->emitToken(array( - 'name' => 'select', - 'type' => HTML5_Tokenizer::ENDTAG, - )); - $this->emitToken($token); - - /* An end tag whose tag name is one of: "caption", "table", "tbody", - "tfoot", "thead", "tr", "td", "th" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - in_array($token['name'], array('caption', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'td', 'th'))) { - /* Parse error. */ - // parse error - - /* If the stack of open elements has an element in table scope with - the same tag name as that of the token, then act as if an end tag - with the tag name "select" had been seen, and reprocess the token. - Otherwise, ignore the token. */ - if($this->elementInScope($token['name'], true)) { - $this->emitToken(array( - 'name' => 'select', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - $this->emitToken($token); - } else { - $this->ignored = true; - } - } else { - $this->processWithRulesFor($token, self::IN_SELECT); - } - break; - - case self::IN_FOREIGN_CONTENT: - if ($token['type'] === HTML5_Tokenizer::CHARACTER || - $token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - $this->insertText($token['data']); - } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) { - $this->insertComment($token['data']); - } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // XERROR: parse error - } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] === 'script' && end($this->stack)->tagName === 'script' && - end($this->stack)->namespaceURI === self::NS_SVG) { - array_pop($this->stack); - // a bunch of script running mumbo jumbo - } elseif ( - ($token['type'] === HTML5_Tokenizer::STARTTAG && - (( - $token['name'] !== 'mglyph' && - $token['name'] !== 'malignmark' && - end($this->stack)->namespaceURI === self::NS_MATHML && - in_array(end($this->stack)->tagName, array('mi', 'mo', 'mn', 'ms', 'mtext')) - ) || - ( - $token['name'] === 'svg' && - end($this->stack)->namespaceURI === self::NS_MATHML && - end($this->stack)->tagName === 'annotation-xml' - ) || - ( - end($this->stack)->namespaceURI === self::NS_SVG && - in_array(end($this->stack)->tagName, array('foreignObject', 'desc', 'title')) - ) || - ( - // XSKETCHY - end($this->stack)->namespaceURI === self::NS_HTML - )) - ) || $token['type'] === HTML5_Tokenizer::ENDTAG - ) { - $this->processWithRulesFor($token, $this->secondary_mode); - /* If, after doing so, the insertion mode is still "in foreign - * content", but there is no element in scope that has a namespace - * other than the HTML namespace, switch the insertion mode to the - * secondary insertion mode. */ - if ($this->mode === self::IN_FOREIGN_CONTENT) { - $found = false; - // this basically duplicates elementInScope() - for ($i = count($this->stack) - 1; $i >= 0; $i--) { - $node = $this->stack[$i]; - if ($node->namespaceURI !== self::NS_HTML) { - $found = true; - break; - } elseif (in_array($node->tagName, array('table', 'html', - 'applet', 'caption', 'td', 'th', 'button', 'marquee', - 'object')) || ($node->tagName === 'foreignObject' && - $node->namespaceURI === self::NS_SVG)) { - break; - } - } - if (!$found) { - $this->mode = $this->secondary_mode; - } - } - } elseif ($token['type'] === HTML5_Tokenizer::EOF || ( - $token['type'] === HTML5_Tokenizer::STARTTAG && - (in_array($token['name'], array('b', "big", "blockquote", "body", "br", - "center", "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", - "h3", "h4", "h5", "h6", "head", "hr", "i", "img", "li", "listing", - "menu", "meta", "nobr", "ol", "p", "pre", "ruby", "s", "small", - "span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul", - "var")) || ($token['name'] === 'font' && ($this->getAttr($token, 'color') || - $this->getAttr($token, 'face') || $this->getAttr($token, 'size')))))) { - // XERROR: parse error - do { - $node = array_pop($this->stack); - } while ($node->namespaceURI !== self::NS_HTML); - $this->stack[] = $node; - $this->mode = $this->secondary_mode; - $this->emitToken($token); - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG) { - static $svg_lookup = 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', - ); - $current = end($this->stack); - if ($current->namespaceURI === self::NS_MATHML) { - $token = $this->adjustMathMLAttributes($token); - } - if ($current->namespaceURI === self::NS_SVG && - isset($svg_lookup[$token['name']])) { - $token['name'] = $svg_lookup[$token['name']]; - } - if ($current->namespaceURI === self::NS_SVG) { - $token = $this->adjustSVGAttributes($token); - } - $token = $this->adjustForeignAttributes($token); - $this->insertForeignElement($token, $current->namespaceURI); - if (isset($token['self-closing'])) { - array_pop($this->stack); - // XERROR: acknowledge self-closing flag - } - } - break; - - case self::AFTER_BODY: - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Process the token as it would be processed if the insertion mode - was "in body". */ - $this->processWithRulesFor($token, self::IN_BODY); - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the first element in the stack of open - elements (the html element), with the data attribute set to the - data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->stack[0]->appendChild($comment); - - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - $this->processWithRulesFor($token, self::IN_BODY); - - /* An end tag with the tag name "html" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'html') { - /* If the parser was originally created as part of the HTML - * fragment parsing algorithm, this is a parse error; ignore - * the token. (fragment case) */ - $this->ignored = true; - // XERROR: implement this - - $this->mode = self::AFTER_AFTER_BODY; - - } elseif($token['type'] === HTML5_Tokenizer::EOF) { - /* Stop parsing */ - - /* Anything else */ - } else { - /* Parse error. Set the insertion mode to "in body" and reprocess - the token. */ - $this->mode = self::IN_BODY; - $this->emitToken($token); - } - break; - - case self::IN_FRAMESET: - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ - if($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - - /* A start tag with the tag name "frameset" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'frameset') { - $this->insertElement($token); - - /* An end tag with the tag name "frameset" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] === 'frameset') { - /* If the current node is the root html element, then this is a - parse error; ignore the token. (fragment case) */ - if(end($this->stack)->tagName === 'html') { - $this->ignored = true; - // Parse error - - } else { - /* Otherwise, pop the current node from the stack of open - elements. */ - array_pop($this->stack); - - /* If the parser was not originally created as part of the HTML - * fragment parsing algorithm (fragment case), and the current - * node is no longer a frameset element, then switch the - * insertion mode to "after frameset". */ - $this->mode = self::AFTER_FRAMESET; - } - - /* A start tag with the tag name "frame" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'frame') { - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - // XERROR: Acknowledge the token's self-closing flag, if it is set. - - /* A start tag with the tag name "noframes" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'noframes') { - /* Process the token using the rules for the "in head" insertion mode. */ - $this->processwithRulesFor($token, self::IN_HEAD); - - } elseif($token['type'] === HTML5_Tokenizer::EOF) { - // XERROR: If the current node is not the root html element, then this is a parse error. - /* Stop parsing */ - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - $this->ignored = true; - } - break; - - case self::AFTER_FRAMESET: - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ - if($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - $this->processWithRulesFor($token, self::IN_BODY); - - /* An end tag with the tag name "html" */ - } elseif($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] === 'html') { - $this->mode = self::AFTER_AFTER_FRAMESET; - - /* A start tag with the tag name "noframes" */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'noframes') { - $this->processWithRulesFor($token, self::IN_HEAD); - - } elseif($token['type'] === HTML5_Tokenizer::EOF) { - /* Stop parsing */ - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - $this->ignored = true; - } - break; - - case self::AFTER_AFTER_BODY: - /* A comment token */ - if($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE || - $token['type'] === HTML5_Tokenizer::SPACECHARACTER || - ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) { - $this->processWithRulesFor($token, self::IN_BODY); - - /* An end-of-file token */ - } elseif($token['type'] === HTML5_Tokenizer::EOF) { - /* OMG DONE!! */ - } else { - // parse error - $this->mode = self::IN_BODY; - $this->emitToken($token); - } - break; - - case self::AFTER_AFTER_FRAMESET: - /* A comment token */ - if($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - } elseif($token['type'] === HTML5_Tokenizer::DOCTYPE || - $token['type'] === HTML5_Tokenizer::SPACECHARACTER || - ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) { - $this->processWithRulesFor($token, self::IN_BODY); - - /* An end-of-file token */ - } elseif($token['type'] === HTML5_Tokenizer::EOF) { - /* OMG DONE!! */ - } elseif($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'nofrmaes') { - $this->processWithRulesFor($token, self::IN_HEAD); - } else { - // parse error - } - break; - } - // end funky indenting - } - - private function insertElement($token, $append = true) { - $el = $this->dom->createElementNS(self::NS_HTML, $token['name']); - - if (!empty($token['attr'])) { - foreach($token['attr'] as $attr) { - - // mike@macgirvin.com 2011-11-17, check attribute name for - // validity (ignoring extenders and combiners) as illegal chars in names - // causes everything to abort - - $valid = preg_match('/^[a-zA-Z\_\:]([\-a-zA-Z0-9\_\:\.]+$)/',$attr['name'],$matches); - if($attr['name'] && (!$el->hasAttribute($attr['name'])) && ($valid)) { - $el->setAttribute($attr['name'], $attr['value']); - } - } - } - if ($append) { - $this->appendToRealParent($el); - $this->stack[] = $el; - } - - return $el; - } - - private function insertText($data) { - if ($data === '') return; - if ($this->ignore_lf_token) { - if ($data[0] === "\n") { - $data = substr($data, 1); - if ($data === false) return; - } - } - $text = $this->dom->createTextNode($data); - $this->appendToRealParent($text); - } - - private function insertComment($data) { - $comment = $this->dom->createComment($data); - $this->appendToRealParent($comment); - } - - private function appendToRealParent($node) { - // this is only for the foster_parent case - /* If the current node is a table, tbody, tfoot, thead, or tr - element, then, whenever a node would be inserted into the current - node, it must instead be inserted into the foster parent element. */ - if(!$this->foster_parent || !in_array(end($this->stack)->tagName, - array('table', 'tbody', 'tfoot', 'thead', 'tr'))) { - end($this->stack)->appendChild($node); - } else { - $this->fosterParent($node); - } - } - - private function elementInScope($el, $table = false) { - if(is_array($el)) { - foreach($el as $element) { - if($this->elementInScope($element, $table)) { - return true; - } - } - - return false; - } - - $leng = count($this->stack); - - for($n = 0; $n < $leng; $n++) { - /* 1. Initialise node to be the current node (the bottommost node of - the stack). */ - $node = $this->stack[$leng - 1 - $n]; - - if($node->tagName === $el) { - /* 2. If node is the target node, terminate in a match state. */ - return true; - - // these are the common states for "in scope" and "in table scope" - } elseif($node->tagName === 'table' || $node->tagName === 'html') { - return false; - - // these are only valid for "in scope" - } elseif(!$table && - (in_array($node->tagName, array('applet', 'caption', 'td', - 'th', 'button', 'marquee', 'object')) || - $node->tagName === 'foreignObject' && $node->namespaceURI === self::NS_SVG)) { - return false; - } - - /* Otherwise, set node to the previous entry in the stack of open - elements and return to step 2. (This will never fail, since the loop - will always terminate in the previous step if the top of the stack - is reached.) */ - } - } - - private function reconstructActiveFormattingElements() { - /* 1. If there are no entries in the list of active formatting elements, - then there is nothing to reconstruct; stop this algorithm. */ - $formatting_elements = count($this->a_formatting); - - if($formatting_elements === 0) { - return false; - } - - /* 3. Let entry be the last (most recently added) element in the list - of active formatting elements. */ - $entry = end($this->a_formatting); - - /* 2. If the last (most recently added) entry in the list of active - formatting elements is a marker, or if it is an element that is in the - stack of open elements, then there is nothing to reconstruct; stop this - algorithm. */ - if($entry === self::MARKER || in_array($entry, $this->stack, true)) { - return false; - } - - for($a = $formatting_elements - 1; $a >= 0; true) { - /* 4. If there are no entries before entry in the list of active - formatting elements, then jump to step 8. */ - if($a === 0) { - $step_seven = false; - break; - } - - /* 5. Let entry be the entry one earlier than entry in the list of - active formatting elements. */ - $a--; - $entry = $this->a_formatting[$a]; - - /* 6. If entry is neither a marker nor an element that is also in - thetack of open elements, go to step 4. */ - if($entry === self::MARKER || in_array($entry, $this->stack, true)) { - break; - } - } - - while(true) { - /* 7. Let entry be the element one later than entry in the list of - active formatting elements. */ - if(isset($step_seven) && $step_seven === true) { - $a++; - $entry = $this->a_formatting[$a]; - } - - /* 8. Perform a shallow clone of the element entry to obtain clone. */ - $clone = $entry->cloneNode(); - - /* 9. Append clone to the current node and push it onto the stack - of open elements so that it is the new current node. */ - $this->appendToRealParent($clone); - $this->stack[] = $clone; - - /* 10. Replace the entry for entry in the list with an entry for - clone. */ - $this->a_formatting[$a] = $clone; - - /* 11. If the entry for clone in the list of active formatting - elements is not the last entry in the list, return to step 7. */ - if(end($this->a_formatting) !== $clone) { - $step_seven = true; - } else { - break; - } - } - } - - private function clearTheActiveFormattingElementsUpToTheLastMarker() { - /* When the steps below require the UA to clear the list of active - formatting elements up to the last marker, the UA must perform the - following steps: */ - - while(true) { - /* 1. Let entry be the last (most recently added) entry in the list - of active formatting elements. */ - $entry = end($this->a_formatting); - - /* 2. Remove entry from the list of active formatting elements. */ - array_pop($this->a_formatting); - - /* 3. If entry was a marker, then stop the algorithm at this point. - The list has been cleared up to the last marker. */ - if($entry === self::MARKER) { - break; - } - } - } - - private function generateImpliedEndTags($exclude = array()) { - /* When the steps below require the UA to generate implied end tags, - then, if the current node is a dd element, a dt element, an li element, - a p element, a td element, a th element, or a tr element, the UA must - act as if an end tag with the respective tag name had been seen and - then generate implied end tags again. */ - $node = end($this->stack); - $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude); - - while(in_array(end($this->stack)->tagName, $elements)) { - array_pop($this->stack); - } - } - - private function getElementCategory($node) { - if (!is_object($node)) debug_print_backtrace(); - $name = $node->tagName; - if(in_array($name, $this->special)) - return self::SPECIAL; - - elseif(in_array($name, $this->scoping)) - return self::SCOPING; - - elseif(in_array($name, $this->formatting)) - return self::FORMATTING; - - else - return self::PHRASING; - } - - private function clearStackToTableContext($elements) { - /* When the steps above require the UA to clear the stack back to a - table context, it means that the UA must, while the current node is not - a table element or an html element, pop elements from the stack of open - elements. */ - while(true) { - $name = end($this->stack)->tagName; - - if(in_array($name, $elements)) { - break; - } else { - array_pop($this->stack); - } - } - } - - private function resetInsertionMode($context = null) { - /* 1. Let last be false. */ - $last = false; - $leng = count($this->stack); - - for($n = $leng - 1; $n >= 0; $n--) { - /* 2. Let node be the last node in the stack of open elements. */ - $node = $this->stack[$n]; - - /* 3. If node is the first node in the stack of open elements, then - * set last to true and set node to the context element. (fragment - * case) */ - if($this->stack[0]->isSameNode($node)) { - $last = true; - $node = $context; - } - - /* 4. If node is a select element, then switch the insertion mode to - "in select" and abort these steps. (fragment case) */ - if($node->tagName === 'select') { - $this->mode = self::IN_SELECT; - break; - - /* 5. If node is a td or th element, then switch the insertion mode - to "in cell" and abort these steps. */ - } elseif($node->tagName === 'td' || $node->nodeName === 'th') { - $this->mode = self::IN_CELL; - break; - - /* 6. If node is a tr element, then switch the insertion mode to - "in row" and abort these steps. */ - } elseif($node->tagName === 'tr') { - $this->mode = self::IN_ROW; - break; - - /* 7. If node is a tbody, thead, or tfoot element, then switch the - insertion mode to "in table body" and abort these steps. */ - } elseif(in_array($node->tagName, array('tbody', 'thead', 'tfoot'))) { - $this->mode = self::IN_TABLE_BODY; - break; - - /* 8. If node is a caption element, then switch the insertion mode - to "in caption" and abort these steps. */ - } elseif($node->tagName === 'caption') { - $this->mode = self::IN_CAPTION; - break; - - /* 9. If node is a colgroup element, then switch the insertion mode - to "in column group" and abort these steps. (innerHTML case) */ - } elseif($node->tagName === 'colgroup') { - $this->mode = self::IN_COLUMN_GROUP; - break; - - /* 10. If node is a table element, then switch the insertion mode - to "in table" and abort these steps. */ - } elseif($node->tagName === 'table') { - $this->mode = self::IN_TABLE; - break; - - /* 11. If node is an element from the MathML namespace or the SVG - * namespace, then switch the insertion mode to "in foreign - * content", let the secondary insertion mode be "in body", and - * abort these steps. */ - } elseif($node->namespaceURI === self::NS_SVG || - $node->namespaceURI === self::NS_MATHML) { - $this->mode = self::IN_FOREIGN_CONTENT; - $this->secondary_mode = self::IN_BODY; - break; - - /* 12. If node is a head element, then switch the insertion mode - to "in body" ("in body"! not "in head"!) and abort these steps. - (fragment case) */ - } elseif($node->tagName === 'head') { - $this->mode = self::IN_BODY; - break; - - /* 13. If node is a body element, then switch the insertion mode to - "in body" and abort these steps. */ - } elseif($node->tagName === 'body') { - $this->mode = self::IN_BODY; - break; - - /* 14. If node is a frameset element, then switch the insertion - mode to "in frameset" and abort these steps. (fragment case) */ - } elseif($node->tagName === 'frameset') { - $this->mode = self::IN_FRAMESET; - break; - - /* 15. If node is an html element, then: if the head element - pointer is null, switch the insertion mode to "before head", - otherwise, switch the insertion mode to "after head". In either - case, abort these steps. (fragment case) */ - } elseif($node->tagName === 'html') { - $this->mode = ($this->head_pointer === null) - ? self::BEFORE_HEAD - : self::AFTER_HEAD; - - break; - - /* 16. If last is true, then set the insertion mode to "in body" - and abort these steps. (fragment case) */ - } elseif($last) { - $this->mode = self::IN_BODY; - break; - } - } - } - - private function closeCell() { - /* If the stack of open elements has a td or th element in table scope, - then act as if an end tag token with that tag name had been seen. */ - foreach(array('td', 'th') as $cell) { - if($this->elementInScope($cell, true)) { - $this->emitToken(array( - 'name' => $cell, - 'type' => HTML5_Tokenizer::ENDTAG - )); - - break; - } - } - } - - private function processWithRulesFor($token, $mode) { - /* "using the rules for the m insertion mode", where m is one of these - * modes, the user agent must use the rules described under the m - * insertion mode's section, but must leave the insertion mode - * unchanged unless the rules in m themselves switch the insertion mode - * to a new value. */ - return $this->emitToken($token, $mode); - } - - private function insertCDATAElement($token) { - $this->insertElement($token); - $this->original_mode = $this->mode; - $this->mode = self::IN_CDATA_RCDATA; - $this->content_model = HTML5_Tokenizer::CDATA; - } - - private function insertRCDATAElement($token) { - $this->insertElement($token); - $this->original_mode = $this->mode; - $this->mode = self::IN_CDATA_RCDATA; - $this->content_model = HTML5_Tokenizer::RCDATA; - } - - private function getAttr($token, $key) { - if (!isset($token['attr'])) return false; - $ret = false; - foreach ($token['attr'] as $keypair) { - if ($keypair['name'] === $key) $ret = $keypair['value']; - } - return $ret; - } - - private function getCurrentTable() { - /* The current table is the last table element in the stack of open - * elements, if there is one. If there is no table element in the stack - * of open elements (fragment case), then the current table is the - * first element in the stack of open elements (the html element). */ - for ($i = count($this->stack) - 1; $i >= 0; $i--) { - if ($this->stack[$i]->tagName === 'table') { - return $this->stack[$i]; - } - } - return $this->stack[0]; - } - - private function getFosterParent() { - /* The foster parent element is the parent element of the last - table element in the stack of open elements, if there is a - table element and it has such a parent element. If there is no - table element in the stack of open elements (innerHTML case), - then the foster parent element is the first element in the - stack of open elements (the html element). Otherwise, if there - is a table element in the stack of open elements, but the last - table element in the stack of open elements has no parent, or - its parent node is not an element, then the foster parent - element is the element before the last table element in the - stack of open elements. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->tagName === 'table') { - $table = $this->stack[$n]; - break; - } - } - - if(isset($table) && $table->parentNode !== null) { - return $table->parentNode; - - } elseif(!isset($table)) { - return $this->stack[0]; - - } elseif(isset($table) && ($table->parentNode === null || - $table->parentNode->nodeType !== XML_ELEMENT_NODE)) { - return $this->stack[$n - 1]; - } - } - - public function fosterParent($node) { - $foster_parent = $this->getFosterParent(); - $table = $this->getCurrentTable(); // almost equivalent to last table element, except it can be html - /* When a node node is to be foster parented, the node node must be - * inserted into the foster parent element, and the current table must - * be marked as tainted. (Once the current table has been tainted, - * whitespace characters are inserted into the foster parent element - * instead of the current node.) */ - $table->tainted = true; - /* If the foster parent element is the parent element of the last table - * element in the stack of open elements, then node must be inserted - * immediately before the last table element in the stack of open - * elements in the foster parent element; otherwise, node must be - * appended to the foster parent element. */ - if ($table->tagName === 'table' && $table->parentNode->isSameNode($foster_parent)) { - $foster_parent->insertBefore($node, $table); - } else { - $foster_parent->appendChild($node); - } - } - - /** - * For debugging, prints the stack - */ - private function printStack() { - $names = array(); - foreach ($this->stack as $i => $element) { - $names[] = $element->tagName; - } - echo " -> stack [" . implode(', ', $names) . "]\n"; - } - - /** - * For debugging, prints active formatting elements - */ - private function printActiveFormattingElements() { - if (!$this->a_formatting) return; - $names = array(); - foreach ($this->a_formatting as $node) { - if ($node === self::MARKER) $names[] = 'MARKER'; - else $names[] = $node->tagName; - } - echo " -> active formatting [" . implode(', ', $names) . "]\n"; - } - - public function currentTableIsTainted() { - return !empty($this->getCurrentTable()->tainted); - } - - /** - * Sets up the tree constructor for building a fragment. - */ - public function setupContext($context = null) { - $this->fragment = true; - if ($context) { - $context = $this->dom->createElementNS(self::NS_HTML, $context); - /* 4.1. Set the HTML parser's tokenization stage's content model - * flag according to the context element, as follows: */ - switch ($context->tagName) { - case 'title': case 'textarea': - $this->content_model = HTML5_Tokenizer::RCDATA; - break; - case 'style': case 'script': case 'xmp': case 'iframe': - case 'noembed': case 'noframes': - $this->content_model = HTML5_Tokenizer::CDATA; - break; - case 'noscript': - // XSCRIPT: assuming scripting is enabled - $this->content_model = HTML5_Tokenizer::CDATA; - break; - case 'plaintext': - $this->content_model = HTML5_Tokenizer::PLAINTEXT; - break; - } - /* 4.2. Let root be a new html element with no attributes. */ - $root = $this->dom->createElementNS(self::NS_HTML, 'html'); - $this->root = $root; - /* 4.3 Append the element root to the Document node created above. */ - $this->dom->appendChild($root); - /* 4.4 Set up the parser's stack of open elements so that it - * contains just the single element root. */ - $this->stack = array($root); - /* 4.5 Reset the parser's insertion mode appropriately. */ - $this->resetInsertionMode($context); - /* 4.6 Set the parser's form element pointer to the nearest node - * to the context element that is a form element (going straight up - * the ancestor chain, and including the element itself, if it is a - * form element), or, if there is no such form element, to null. */ - $node = $context; - do { - if ($node->tagName === 'form') { - $this->form_pointer = $node; - break; - } - } while ($node = $node->parentNode); - } - } - - public function adjustMathMLAttributes($token) { - foreach ($token['attr'] as &$kp) { - if ($kp['name'] === 'definitionurl') { - $kp['name'] = 'definitionURL'; - } - } - return $token; - } - - public function adjustSVGAttributes($token) { - static $lookup = 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', - ); - foreach ($token['attr'] as &$kp) { - if (isset($lookup[$kp['name']])) { - $kp['name'] = $lookup[$kp['name']]; - } - } - return $token; - } - - public function adjustForeignAttributes($token) { - static $lookup = array( - 'xlink:actuate' => array('xlink', 'actuate', self::NS_XLINK), - 'xlink:arcrole' => array('xlink', 'arcrole', self::NS_XLINK), - 'xlink:href' => array('xlink', 'href', self::NS_XLINK), - 'xlink:role' => array('xlink', 'role', self::NS_XLINK), - 'xlink:show' => array('xlink', 'show', self::NS_XLINK), - 'xlink:title' => array('xlink', 'title', self::NS_XLINK), - 'xlink:type' => array('xlink', 'type', self::NS_XLINK), - 'xml:base' => array('xml', 'base', self::NS_XML), - 'xml:lang' => array('xml', 'lang', self::NS_XML), - 'xml:space' => array('xml', 'space', self::NS_XML), - 'xmlns' => array(null, 'xmlns', self::NS_XMLNS), - 'xmlns:xlink' => array('xmlns', 'xlink', self::NS_XMLNS), - ); - foreach ($token['attr'] as &$kp) { - if (isset($lookup[$kp['name']])) { - $kp['name'] = $lookup[$kp['name']]; - } - } - return $token; - } - - public function insertForeignElement($token, $namespaceURI) { - $el = $this->dom->createElementNS($namespaceURI, $token['name']); - if (!empty($token['attr'])) { - foreach ($token['attr'] as $kp) { - $attr = $kp['name']; - if (is_array($attr)) { - $ns = $attr[2]; - $attr = $attr[1]; - } else { - $ns = self::NS_HTML; - } - if (!$el->hasAttributeNS($ns, $attr)) { - // XSKETCHY: work around godawful libxml bug - if ($ns === self::NS_XLINK) { - $el->setAttribute('xlink:'.$attr, $kp['value']); - } elseif ($ns === self::NS_HTML) { - // Another godawful libxml bug - $el->setAttribute($attr, $kp['value']); - } else { - $el->setAttributeNS($ns, $attr, $kp['value']); - } - } - } - } - $this->appendToRealParent($el); - $this->stack[] = $el; - // XERROR: see below - /* If the newly created element has an xmlns attribute in the XMLNS - * namespace whose value is not exactly the same as the element's - * namespace, that is a parse error. Similarly, if the newly created - * element has an xmlns:xlink attribute in the XMLNS namespace whose - * value is not the XLink Namespace, that is a parse error. */ - } - - public function save() { - $this->dom->normalize(); - if (!$this->fragment) { - return $this->dom; - } else { - if ($this->root) { - return $this->root->childNodes; - } else { - return $this->dom->childNodes; - } - } - } -} - |