3 namespace RemexHtml\TreeBuilder;
4 use RemexHtml\Tokenizer\Attributes;
5 use RemexHtml\Tokenizer\Tokenizer;
8 * The "in head" insertion mode
10 class InHead extends InsertionMode {
11 public function characters( $text, $start, $length, $sourceStart, $sourceLength ) {
12 // Split and insert whitespace
13 list( $part1, $part2 ) = $this->splitInitialMatch(
14 true, "\t\n\f\r ", $text, $start, $length, $sourceStart, $sourceLength );
16 list( $start, $length, $sourceStart, $sourceLength ) = $part1;
18 $this->builder->insertCharacters( $text, $start, $length, $sourceStart,
22 // Handle non-whitespace specially
23 list( $start, $length, $sourceStart, $sourceLength ) = $part2;
28 $elt = $this->builder->pop( $sourceStart, 0 );
29 if ( $elt->htmlName !== 'head' ) {
30 throw new \Exception( 'In head mode but current element is not <head>' );
32 $this->dispatcher->switchMode( Dispatcher::AFTER_HEAD )
33 ->characters( $text, $start, $length, $sourceStart, $sourceLength );
36 public function startTag( $name, Attributes $attrs, $selfClose, $sourceStart, $sourceLength ) {
38 $tokenizerState = null;
41 $builder = $this->builder;
42 $dispatcher = $this->dispatcher;
46 $this->dispatcher->inBody->startTag( $name, $attrs, $selfClose,
47 $sourceStart, $sourceLength );
55 $dispatcher->ack = true;
60 $dispatcher->ack = true;
61 // charset handling omitted
65 $tokenizerState = Tokenizer::STATE_RCDATA;
66 $textMode = Dispatcher::TEXT;
70 if ( !$this->builder->scriptingFlag ) {
71 $mode = Dispatcher::IN_HEAD_NOSCRIPT;
74 /*. missing_break; .*/
77 $tokenizerState = Tokenizer::STATE_RAWTEXT;
78 $textMode = Dispatcher::TEXT;
82 $tokenizerState = Tokenizer::STATE_SCRIPT_DATA;
83 $textMode = Dispatcher::TEXT;
87 $this->builder->afe->insertMarker();
88 $this->builder->framesetOK = false;
89 $mode = Dispatcher::IN_TEMPLATE;
90 $this->dispatcher->templateModeStack->push( Dispatcher::IN_TEMPLATE );
94 $this->builder->error( 'unexpected head tag in head, ignoring', $sourceStart );
98 $elt = $this->builder->pop( $sourceStart, 0 );
99 if ( $elt->htmlName !== 'head' ) {
100 throw new \Exception( "In head mode but current element is not <head>" );
102 $this->dispatcher->switchMode( Dispatcher::AFTER_HEAD )
103 ->startTag( $name, $attrs, $selfClose, $sourceStart, $sourceLength );
107 // Generic element insertion, for all cases that didn't return above
108 $this->builder->insertElement( $name, $attrs, $void,
109 $sourceStart, $sourceLength );
110 if ( $tokenizerState !== null ) {
111 $this->builder->tokenizer->switchState( $tokenizerState, $name );
113 if ( $textMode !== null ) {
114 $this->dispatcher->switchAndSave( $textMode );
115 } elseif ( $mode !== null ) {
116 $this->dispatcher->switchMode( $mode );
120 public function endTag( $name, $sourceStart, $sourceLength ) {
121 $builder = $this->builder;
122 $stack = $builder->stack;
126 $builder->pop( $sourceStart, $sourceLength );
127 $this->dispatcher->switchMode( Dispatcher::AFTER_HEAD );
133 $builder->pop( $sourceStart, 0 );
134 $this->dispatcher->switchMode( Dispatcher::AFTER_HEAD )
135 ->endTag( $name, $sourceStart, $sourceLength );
139 if ( !$stack->hasTemplate() ) {
140 $builder->error( 'found </template> but there is no open template, ignoring',
144 $builder->generateImpliedEndTagsThoroughly( $sourceStart );
145 if ( $stack->current->htmlName !== 'template' ) {
146 $builder->error( 'found </template> when other tags are still open', $sourceStart );
148 $builder->popAllUpToName( 'template', $sourceStart, $sourceLength );
149 $builder->afe->clearToMarker();
150 $this->dispatcher->templateModeStack->pop();
151 $this->dispatcher->reset();
155 $builder->error( "ignoring </$name> in head", $sourceStart );
159 public function endDocument( $pos ) {
160 $this->builder->pop( $pos, 0 );
161 $this->dispatcher->switchMode( Dispatcher::AFTER_HEAD )
162 ->endDocument( $pos );