diff --git a/admin/help/index.html b/admin/help/index.html
index d467dacdd..e332c1aac 100644
--- a/admin/help/index.html
+++ b/admin/help/index.html
@@ -5,7 +5,7 @@
- , $tokens);
+ getElementsByTagName('body')->item(0) //
+ ,
+ $tokens
+ );
return $tokens;
}
-
}
/*
-Copyright 2007 Jeroen van der Meer
+Copyright 2007 Jeroen van der Meer
-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:
+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 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.
+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.
*/
-class HTML5 {
+class HTML5
+{
private $data;
private $char;
private $EOF;
@@ -69,89 +77,418 @@ class HTML5 {
private $token;
private $content_model;
private $escape = false;
- private $entities = array('AElig;','AElig','AMP;','AMP','Aacute;','Aacute',
- 'Acirc;','Acirc','Agrave;','Agrave','Alpha;','Aring;','Aring','Atilde;',
- 'Atilde','Auml;','Auml','Beta;','COPY;','COPY','Ccedil;','Ccedil','Chi;',
- 'Dagger;','Delta;','ETH;','ETH','Eacute;','Eacute','Ecirc;','Ecirc','Egrave;',
- 'Egrave','Epsilon;','Eta;','Euml;','Euml','GT;','GT','Gamma;','Iacute;',
- 'Iacute','Icirc;','Icirc','Igrave;','Igrave','Iota;','Iuml;','Iuml','Kappa;',
- 'LT;','LT','Lambda;','Mu;','Ntilde;','Ntilde','Nu;','OElig;','Oacute;',
- 'Oacute','Ocirc;','Ocirc','Ograve;','Ograve','Omega;','Omicron;','Oslash;',
- 'Oslash','Otilde;','Otilde','Ouml;','Ouml','Phi;','Pi;','Prime;','Psi;',
- 'QUOT;','QUOT','REG;','REG','Rho;','Scaron;','Sigma;','THORN;','THORN',
- 'TRADE;','Tau;','Theta;','Uacute;','Uacute','Ucirc;','Ucirc','Ugrave;',
- 'Ugrave','Upsilon;','Uuml;','Uuml','Xi;','Yacute;','Yacute','Yuml;','Zeta;',
- 'aacute;','aacute','acirc;','acirc','acute;','acute','aelig;','aelig',
- 'agrave;','agrave','alefsym;','alpha;','amp;','amp','and;','ang;','apos;',
- 'aring;','aring','asymp;','atilde;','atilde','auml;','auml','bdquo;','beta;',
- 'brvbar;','brvbar','bull;','cap;','ccedil;','ccedil','cedil;','cedil',
- 'cent;','cent','chi;','circ;','clubs;','cong;','copy;','copy','crarr;',
- 'cup;','curren;','curren','dArr;','dagger;','darr;','deg;','deg','delta;',
- 'diams;','divide;','divide','eacute;','eacute','ecirc;','ecirc','egrave;',
- 'egrave','empty;','emsp;','ensp;','epsilon;','equiv;','eta;','eth;','eth',
- 'euml;','euml','euro;','exist;','fnof;','forall;','frac12;','frac12',
- 'frac14;','frac14','frac34;','frac34','frasl;','gamma;','ge;','gt;','gt',
- 'hArr;','harr;','hearts;','hellip;','iacute;','iacute','icirc;','icirc',
- 'iexcl;','iexcl','igrave;','igrave','image;','infin;','int;','iota;',
- 'iquest;','iquest','isin;','iuml;','iuml','kappa;','lArr;','lambda;','lang;',
- 'laquo;','laquo','larr;','lceil;','ldquo;','le;','lfloor;','lowast;','loz;',
- 'lrm;','lsaquo;','lsquo;','lt;','lt','macr;','macr','mdash;','micro;','micro',
- 'middot;','middot','minus;','mu;','nabla;','nbsp;','nbsp','ndash;','ne;',
- 'ni;','not;','not','notin;','nsub;','ntilde;','ntilde','nu;','oacute;',
- 'oacute','ocirc;','ocirc','oelig;','ograve;','ograve','oline;','omega;',
- 'omicron;','oplus;','or;','ordf;','ordf','ordm;','ordm','oslash;','oslash',
- 'otilde;','otilde','otimes;','ouml;','ouml','para;','para','part;','permil;',
- 'perp;','phi;','pi;','piv;','plusmn;','plusmn','pound;','pound','prime;',
- 'prod;','prop;','psi;','quot;','quot','rArr;','radic;','rang;','raquo;',
- 'raquo','rarr;','rceil;','rdquo;','real;','reg;','reg','rfloor;','rho;',
- 'rlm;','rsaquo;','rsquo;','sbquo;','scaron;','sdot;','sect;','sect','shy;',
- 'shy','sigma;','sigmaf;','sim;','spades;','sub;','sube;','sum;','sup1;',
- 'sup1','sup2;','sup2','sup3;','sup3','sup;','supe;','szlig;','szlig','tau;',
- 'there4;','theta;','thetasym;','thinsp;','thorn;','thorn','tilde;','times;',
- 'times','trade;','uArr;','uacute;','uacute','uarr;','ucirc;','ucirc',
- 'ugrave;','ugrave','uml;','uml','upsih;','upsilon;','uuml;','uuml','weierp;',
- 'xi;','yacute;','yacute','yen;','yen','yuml;','yuml','zeta;','zwj;','zwnj;');
+ private $entities = array(
+ 'AElig;',
+ 'AElig',
+ 'AMP;',
+ 'AMP',
+ 'Aacute;',
+ 'Aacute',
+ 'Acirc;',
+ 'Acirc',
+ 'Agrave;',
+ 'Agrave',
+ 'Alpha;',
+ 'Aring;',
+ 'Aring',
+ 'Atilde;',
+ 'Atilde',
+ 'Auml;',
+ 'Auml',
+ 'Beta;',
+ 'COPY;',
+ 'COPY',
+ 'Ccedil;',
+ 'Ccedil',
+ 'Chi;',
+ 'Dagger;',
+ 'Delta;',
+ 'ETH;',
+ 'ETH',
+ 'Eacute;',
+ 'Eacute',
+ 'Ecirc;',
+ 'Ecirc',
+ 'Egrave;',
+ 'Egrave',
+ 'Epsilon;',
+ 'Eta;',
+ 'Euml;',
+ 'Euml',
+ 'GT;',
+ 'GT',
+ 'Gamma;',
+ 'Iacute;',
+ 'Iacute',
+ 'Icirc;',
+ 'Icirc',
+ 'Igrave;',
+ 'Igrave',
+ 'Iota;',
+ 'Iuml;',
+ 'Iuml',
+ 'Kappa;',
+ 'LT;',
+ 'LT',
+ 'Lambda;',
+ 'Mu;',
+ 'Ntilde;',
+ 'Ntilde',
+ 'Nu;',
+ 'OElig;',
+ 'Oacute;',
+ 'Oacute',
+ 'Ocirc;',
+ 'Ocirc',
+ 'Ograve;',
+ 'Ograve',
+ 'Omega;',
+ 'Omicron;',
+ 'Oslash;',
+ 'Oslash',
+ 'Otilde;',
+ 'Otilde',
+ 'Ouml;',
+ 'Ouml',
+ 'Phi;',
+ 'Pi;',
+ 'Prime;',
+ 'Psi;',
+ 'QUOT;',
+ 'QUOT',
+ 'REG;',
+ 'REG',
+ 'Rho;',
+ 'Scaron;',
+ 'Sigma;',
+ 'THORN;',
+ 'THORN',
+ 'TRADE;',
+ 'Tau;',
+ 'Theta;',
+ 'Uacute;',
+ 'Uacute',
+ 'Ucirc;',
+ 'Ucirc',
+ 'Ugrave;',
+ 'Ugrave',
+ 'Upsilon;',
+ 'Uuml;',
+ 'Uuml',
+ 'Xi;',
+ 'Yacute;',
+ 'Yacute',
+ 'Yuml;',
+ 'Zeta;',
+ 'aacute;',
+ 'aacute',
+ 'acirc;',
+ 'acirc',
+ 'acute;',
+ 'acute',
+ 'aelig;',
+ 'aelig',
+ 'agrave;',
+ 'agrave',
+ 'alefsym;',
+ 'alpha;',
+ 'amp;',
+ 'amp',
+ 'and;',
+ 'ang;',
+ 'apos;',
+ 'aring;',
+ 'aring',
+ 'asymp;',
+ 'atilde;',
+ 'atilde',
+ 'auml;',
+ 'auml',
+ 'bdquo;',
+ 'beta;',
+ 'brvbar;',
+ 'brvbar',
+ 'bull;',
+ 'cap;',
+ 'ccedil;',
+ 'ccedil',
+ 'cedil;',
+ 'cedil',
+ 'cent;',
+ 'cent',
+ 'chi;',
+ 'circ;',
+ 'clubs;',
+ 'cong;',
+ 'copy;',
+ 'copy',
+ 'crarr;',
+ 'cup;',
+ 'curren;',
+ 'curren',
+ 'dArr;',
+ 'dagger;',
+ 'darr;',
+ 'deg;',
+ 'deg',
+ 'delta;',
+ 'diams;',
+ 'divide;',
+ 'divide',
+ 'eacute;',
+ 'eacute',
+ 'ecirc;',
+ 'ecirc',
+ 'egrave;',
+ 'egrave',
+ 'empty;',
+ 'emsp;',
+ 'ensp;',
+ 'epsilon;',
+ 'equiv;',
+ 'eta;',
+ 'eth;',
+ 'eth',
+ 'euml;',
+ 'euml',
+ 'euro;',
+ 'exist;',
+ 'fnof;',
+ 'forall;',
+ 'frac12;',
+ 'frac12',
+ 'frac14;',
+ 'frac14',
+ 'frac34;',
+ 'frac34',
+ 'frasl;',
+ 'gamma;',
+ 'ge;',
+ 'gt;',
+ 'gt',
+ 'hArr;',
+ 'harr;',
+ 'hearts;',
+ 'hellip;',
+ 'iacute;',
+ 'iacute',
+ 'icirc;',
+ 'icirc',
+ 'iexcl;',
+ 'iexcl',
+ 'igrave;',
+ 'igrave',
+ 'image;',
+ 'infin;',
+ 'int;',
+ 'iota;',
+ 'iquest;',
+ 'iquest',
+ 'isin;',
+ 'iuml;',
+ 'iuml',
+ 'kappa;',
+ 'lArr;',
+ 'lambda;',
+ 'lang;',
+ 'laquo;',
+ 'laquo',
+ 'larr;',
+ 'lceil;',
+ 'ldquo;',
+ 'le;',
+ 'lfloor;',
+ 'lowast;',
+ 'loz;',
+ 'lrm;',
+ 'lsaquo;',
+ 'lsquo;',
+ 'lt;',
+ 'lt',
+ 'macr;',
+ 'macr',
+ 'mdash;',
+ 'micro;',
+ 'micro',
+ 'middot;',
+ 'middot',
+ 'minus;',
+ 'mu;',
+ 'nabla;',
+ 'nbsp;',
+ 'nbsp',
+ 'ndash;',
+ 'ne;',
+ 'ni;',
+ 'not;',
+ 'not',
+ 'notin;',
+ 'nsub;',
+ 'ntilde;',
+ 'ntilde',
+ 'nu;',
+ 'oacute;',
+ 'oacute',
+ 'ocirc;',
+ 'ocirc',
+ 'oelig;',
+ 'ograve;',
+ 'ograve',
+ 'oline;',
+ 'omega;',
+ 'omicron;',
+ 'oplus;',
+ 'or;',
+ 'ordf;',
+ 'ordf',
+ 'ordm;',
+ 'ordm',
+ 'oslash;',
+ 'oslash',
+ 'otilde;',
+ 'otilde',
+ 'otimes;',
+ 'ouml;',
+ 'ouml',
+ 'para;',
+ 'para',
+ 'part;',
+ 'permil;',
+ 'perp;',
+ 'phi;',
+ 'pi;',
+ 'piv;',
+ 'plusmn;',
+ 'plusmn',
+ 'pound;',
+ 'pound',
+ 'prime;',
+ 'prod;',
+ 'prop;',
+ 'psi;',
+ 'quot;',
+ 'quot',
+ 'rArr;',
+ 'radic;',
+ 'rang;',
+ 'raquo;',
+ 'raquo',
+ 'rarr;',
+ 'rceil;',
+ 'rdquo;',
+ 'real;',
+ 'reg;',
+ 'reg',
+ 'rfloor;',
+ 'rho;',
+ 'rlm;',
+ 'rsaquo;',
+ 'rsquo;',
+ 'sbquo;',
+ 'scaron;',
+ 'sdot;',
+ 'sect;',
+ 'sect',
+ 'shy;',
+ 'shy',
+ 'sigma;',
+ 'sigmaf;',
+ 'sim;',
+ 'spades;',
+ 'sub;',
+ 'sube;',
+ 'sum;',
+ 'sup1;',
+ 'sup1',
+ 'sup2;',
+ 'sup2',
+ 'sup3;',
+ 'sup3',
+ 'sup;',
+ 'supe;',
+ 'szlig;',
+ 'szlig',
+ 'tau;',
+ 'there4;',
+ 'theta;',
+ 'thetasym;',
+ 'thinsp;',
+ 'thorn;',
+ 'thorn',
+ 'tilde;',
+ 'times;',
+ 'times',
+ 'trade;',
+ 'uArr;',
+ 'uacute;',
+ 'uacute',
+ 'uarr;',
+ 'ucirc;',
+ 'ucirc',
+ 'ugrave;',
+ 'ugrave',
+ 'uml;',
+ 'uml',
+ 'upsih;',
+ 'upsilon;',
+ 'uuml;',
+ 'uuml',
+ 'weierp;',
+ 'xi;',
+ 'yacute;',
+ 'yacute',
+ 'yen;',
+ 'yen',
+ 'yuml;',
+ 'yuml',
+ 'zeta;',
+ 'zwj;',
+ 'zwnj;'
+ );
- const PCDATA = 0;
- const RCDATA = 1;
- const CDATA = 2;
+ const PCDATA = 0;
+ const RCDATA = 1;
+ const CDATA = 2;
const PLAINTEXT = 3;
- const DOCTYPE = 0;
+ const DOCTYPE = 0;
const STARTTAG = 1;
- const ENDTAG = 2;
- const COMMENT = 3;
+ const ENDTAG = 2;
+ const COMMENT = 3;
const CHARACTR = 4;
- const EOF = 5;
-
- public function __construct($data) {
+ const EOF = 5;
+ public function __construct($data)
+ {
$this->data = $data;
$this->char = -1;
- $this->EOF = strlen($data);
+ $this->EOF = strlen($data);
$this->tree = new HTML5TreeConstructer;
$this->content_model = self::PCDATA;
$this->state = 'data';
- while($this->state !== null) {
- $this->{$this->state.'State'}();
+ while ($this->state !== null) {
+ $this->{$this->state . 'State'}();
}
}
- public function save() {
+ public function save()
+ {
return $this->tree->save();
}
- private function char() {
+ private function char()
+ {
return ($this->char < $this->EOF)
? $this->data[$this->char]
: false;
}
- private function character($s, $l = 0) {
- if($s + $l < $this->EOF) {
- if($l === 0) {
+ private function character($s, $l = 0)
+ {
+ if ($s + $l < $this->EOF) {
+ if ($l === 0) {
return $this->data[$s];
} else {
return substr($this->data, $s, $l);
@@ -159,46 +496,52 @@ class HTML5 {
}
}
- private function characters($char_class, $start) {
- return preg_replace('#^(['.$char_class.']+).*#s', '\\1', substr($this->data, $start));
+ private function characters($char_class, $start)
+ {
+ return preg_replace('#^([' . $char_class . ']+).*#s', '\\1', substr($this->data, $start));
}
- private function dataState() {
+ private function dataState()
+ {
// Consume the next input character
$this->char++;
$char = $this->char();
- if($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) {
+ if ($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) {
/* U+0026 AMPERSAND (&)
When the content model flag is set to one of the PCDATA or RCDATA
states: switch to the entity data state. Otherwise: treat it as per
the "anything else" entry below. */
$this->state = 'entityData';
- } elseif($char === '-') {
+ } elseif ($char === '-') {
/* If the content model flag is set to either the RCDATA state or
the CDATA state, and the escape flag is false, and there are at
least three characters before this one in the input stream, and the
last four characters in the input stream, including this one, are
U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS,
and U+002D HYPHEN-MINUS (""),
set the escape flag to false. */
- if(($this->content_model === self::RCDATA ||
- $this->content_model === self::CDATA) && $this->escape === true &&
- $this->character($this->char, 3) === '-->') {
+ if (($this->content_model === self::RCDATA ||
+ $this->content_model === self::CDATA) && $this->escape === true &&
+ $this->character($this->char, 3) === '-->'
+ ) {
$this->escape = false;
}
/* In any case, emit the input character as a character token.
Stay in the data state. */
- $this->emitToken(array(
- 'type' => self::CHARACTR,
- 'data' => $char
- ));
+ $this->emitToken(
+ array(
+ 'type' => self::CHARACTR,
+ 'data' => $char
+ )
+ );
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
/* EOF
Emit an end-of-file token. */
$this->EOF();
- } elseif($this->content_model === self::PLAINTEXT) {
+ } elseif ($this->content_model === self::PLAINTEXT) {
/* When the content model flag is set to the PLAINTEXT state
THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of
the text and emit it as a character token. */
- $this->emitToken(array(
- 'type' => self::CHARACTR,
- 'data' => substr($this->data, $this->char)
- ));
+ $this->emitToken(
+ array(
+ 'type' => self::CHARACTR,
+ 'data' => substr($this->data, $this->char)
+ )
+ );
$this->EOF();
@@ -250,37 +598,43 @@ class HTML5 {
THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that
otherwise would also be treated as a character token and emit it
as a single character token. Stay in the data state. */
- $len = strcspn($this->data, '<&', $this->char);
+ $len = strcspn($this->data, '<&', $this->char);
$char = substr($this->data, $this->char, $len);
$this->char += $len - 1;
- $this->emitToken(array(
- 'type' => self::CHARACTR,
- 'data' => $char
- ));
+ $this->emitToken(
+ array(
+ 'type' => self::CHARACTR,
+ 'data' => $char
+ )
+ );
$this->state = 'data';
}
}
- private function entityDataState() {
+ private function entityDataState()
+ {
// Attempt to consume an entity.
$entity = $this->entity();
// If nothing is returned, emit a U+0026 AMPERSAND character token.
// Otherwise, emit the character token that was returned.
$char = (!$entity) ? '&' : $entity;
- $this->emitToken(array(
- 'type' => self::CHARACTR,
- 'data' => $char
- ));
+ $this->emitToken(
+ array(
+ 'type' => self::CHARACTR,
+ 'data' => $char
+ )
+ );
// Finally, switch to the data state.
$this->state = 'data';
}
- private function tagOpenState() {
- switch($this->content_model) {
+ private function tagOpenState()
+ {
+ switch ($this->content_model) {
case self::RCDATA:
case self::CDATA:
/* If the next input character is a U+002F SOLIDUS (/) character,
@@ -288,19 +642,21 @@ class HTML5 {
input character is not a U+002F SOLIDUS (/) character, emit a
U+003C LESS-THAN SIGN character token and switch to the data
state to process the next input character. */
- if($this->character($this->char + 1) === '/') {
+ if ($this->character($this->char + 1) === '/') {
$this->char++;
$this->state = 'closeTagOpen';
} else {
- $this->emitToken(array(
- 'type' => self::CHARACTR,
- 'data' => '<'
- ));
+ $this->emitToken(
+ array(
+ 'type' => self::CHARACTR,
+ 'data' => '<'
+ )
+ );
$this->state = 'data';
}
- break;
+ break;
case self::PCDATA:
// If the content model flag is set to the PCDATA state
@@ -308,42 +664,44 @@ class HTML5 {
$this->char++;
$char = $this->char();
- if($char === '!') {
+ if ($char === '!') {
/* U+0021 EXCLAMATION MARK (!)
Switch to the markup declaration open state. */
$this->state = 'markupDeclarationOpen';
- } elseif($char === '/') {
+ } elseif ($char === '/') {
/* U+002F SOLIDUS (/)
Switch to the close tag open state. */
$this->state = 'closeTagOpen';
- } elseif(preg_match('/^[A-Za-z]$/', $char)) {
+ } elseif (preg_match('/^[A-Za-z]$/', $char)) {
/* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
Create a new start tag token, set its tag name to the lowercase
version of the input character (add 0x0020 to the character's code
point), then switch to the tag name state. (Don't emit the token
yet; further details will be filled in before it is emitted.) */
$this->token = array(
- 'name' => strtolower($char),
- 'type' => self::STARTTAG,
- 'attr' => array()
+ 'name' => strtolower($char),
+ 'type' => self::STARTTAG,
+ 'attr' => array()
);
$this->state = 'tagName';
- } elseif($char === '>') {
+ } elseif ($char === '>') {
/* U+003E GREATER-THAN SIGN (>)
Parse error. Emit a U+003C LESS-THAN SIGN character token and a
U+003E GREATER-THAN SIGN character token. Switch to the data state. */
- $this->emitToken(array(
- 'type' => self::CHARACTR,
- 'data' => '<>'
- ));
+ $this->emitToken(
+ array(
+ 'type' => self::CHARACTR,
+ 'data' => '<>'
+ )
+ );
$this->state = 'data';
- } elseif($char === '?') {
+ } elseif ($char === '?') {
/* U+003F QUESTION MARK (?)
Parse error. Switch to the bogus comment state. */
$this->state = 'bogusComment';
@@ -352,25 +710,31 @@ class HTML5 {
/* Anything else
Parse error. Emit a U+003C LESS-THAN SIGN character token and
reconsume the current input character in the data state. */
- $this->emitToken(array(
- 'type' => self::CHARACTR,
- 'data' => '<'
- ));
+ $this->emitToken(
+ array(
+ 'type' => self::CHARACTR,
+ 'data' => '<'
+ )
+ );
$this->char--;
$this->state = 'data';
}
- break;
+ break;
}
}
- private function closeTagOpenState() {
+ private function closeTagOpenState()
+ {
$next_node = strtolower($this->characters('A-Za-z', $this->char + 1));
$the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName;
- if(($this->content_model === self::RCDATA || $this->content_model === self::CDATA) &&
- (!$the_same || ($the_same && (!preg_match('/[\t\n\x0b\x0c >\/]/',
- $this->character($this->char + 1 + strlen($next_node))) || $this->EOF === $this->char)))) {
+ if (($this->content_model === self::RCDATA || $this->content_model === self::CDATA) &&
+ (!$the_same || ($the_same && (!preg_match(
+ '/[\t\n\x0b\x0c >\/]/',
+ $this->character($this->char + 1 + strlen($next_node))
+ ) || $this->EOF === $this->char)))
+ ) {
/* If the content model flag is set to the RCDATA or CDATA states then
examine the next few characters. If they do not match the tag name of
the last start tag token emitted (case insensitively), or if they do but
@@ -386,10 +750,12 @@ class HTML5 {
...then there is a parse error. Emit a U+003C LESS-THAN SIGN character
token, a U+002F SOLIDUS character token, and switch to the data state
to process the next input character. */
- $this->emitToken(array(
- 'type' => self::CHARACTR,
- 'data' => ''
- ));
+ $this->emitToken(
+ array(
+ 'type' => self::CHARACTR,
+ 'data' => ''
+ )
+ );
$this->state = 'data';
@@ -400,32 +766,34 @@ class HTML5 {
$this->char++;
$char = $this->char();
- if(preg_match('/^[A-Za-z]$/', $char)) {
+ if (preg_match('/^[A-Za-z]$/', $char)) {
/* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
Create a new end tag token, set its tag name to the lowercase version
of the input character (add 0x0020 to the character's code point), then
switch to the tag name state. (Don't emit the token yet; further details
will be filled in before it is emitted.) */
$this->token = array(
- 'name' => strtolower($char),
- 'type' => self::ENDTAG
+ 'name' => strtolower($char),
+ 'type' => self::ENDTAG
);
$this->state = 'tagName';
- } elseif($char === '>') {
+ } elseif ($char === '>') {
/* U+003E GREATER-THAN SIGN (>)
Parse error. Switch to the data state. */
$this->state = 'data';
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
/* EOF
Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F
SOLIDUS character token. Reconsume the EOF character in the data state. */
- $this->emitToken(array(
- 'type' => self::CHARACTR,
- 'data' => ''
- ));
+ $this->emitToken(
+ array(
+ 'type' => self::CHARACTR,
+ 'data' => ''
+ )
+ );
$this->char--;
$this->state = 'data';
@@ -437,12 +805,13 @@ class HTML5 {
}
}
- private function tagNameState() {
+ private function tagNameState()
+ {
// Consume the next input character:
$this->char++;
$char = $this->character($this->char);
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
/* U+0009 CHARACTER TABULATION
U+000A LINE FEED (LF)
U+000B LINE TABULATION
@@ -451,13 +820,13 @@ class HTML5 {
Switch to the before attribute name state. */
$this->state = 'beforeAttributeName';
- } elseif($char === '>') {
+ } elseif ($char === '>') {
/* U+003E GREATER-THAN SIGN (>)
Emit the current tag token. Switch to the data state. */
$this->emitToken($this->token);
$this->state = 'data';
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
/* EOF
Parse error. Emit the current tag token. Reconsume the EOF
character in the data state. */
@@ -466,7 +835,7 @@ class HTML5 {
$this->char--;
$this->state = 'data';
- } elseif($char === '/') {
+ } elseif ($char === '/') {
/* U+002F SOLIDUS (/)
Parse error unless this is a permitted slash. Switch to the before
attribute name state. */
@@ -481,12 +850,13 @@ class HTML5 {
}
}
- private function beforeAttributeNameState() {
+ private function beforeAttributeNameState()
+ {
// Consume the next input character:
$this->char++;
$char = $this->character($this->char);
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
/* U+0009 CHARACTER TABULATION
U+000A LINE FEED (LF)
U+000B LINE TABULATION
@@ -495,19 +865,19 @@ class HTML5 {
Stay in the before attribute name state. */
$this->state = 'beforeAttributeName';
- } elseif($char === '>') {
+ } elseif ($char === '>') {
/* U+003E GREATER-THAN SIGN (>)
Emit the current tag token. Switch to the data state. */
$this->emitToken($this->token);
$this->state = 'data';
- } elseif($char === '/') {
+ } elseif ($char === '/') {
/* U+002F SOLIDUS (/)
Parse error unless this is a permitted slash. Stay in the before
attribute name state. */
$this->state = 'beforeAttributeName';
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
/* EOF
Parse error. Emit the current tag token. Reconsume the EOF
character in the data state. */
@@ -522,7 +892,7 @@ class HTML5 {
name to the current input character, and its value to the empty string.
Switch to the attribute name state. */
$this->token['attr'][] = array(
- 'name' => strtolower($char),
+ 'name' => strtolower($char),
'value' => null
);
@@ -530,12 +900,13 @@ class HTML5 {
}
}
- private function attributeNameState() {
+ private function attributeNameState()
+ {
// Consume the next input character:
$this->char++;
$char = $this->character($this->char);
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
/* U+0009 CHARACTER TABULATION
U+000A LINE FEED (LF)
U+000B LINE TABULATION
@@ -544,24 +915,24 @@ class HTML5 {
Stay in the before attribute name state. */
$this->state = 'afterAttributeName';
- } elseif($char === '=') {
+ } elseif ($char === '=') {
/* U+003D EQUALS SIGN (=)
Switch to the before attribute value state. */
$this->state = 'beforeAttributeValue';
- } elseif($char === '>') {
+ } elseif ($char === '>') {
/* U+003E GREATER-THAN SIGN (>)
Emit the current tag token. Switch to the data state. */
$this->emitToken($this->token);
$this->state = 'data';
- } elseif($char === '/' && $this->character($this->char + 1) !== '>') {
+ } elseif ($char === '/' && $this->character($this->char + 1) !== '>') {
/* U+002F SOLIDUS (/)
Parse error unless this is a permitted slash. Switch to the before
attribute name state. */
$this->state = 'beforeAttributeName';
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
/* EOF
Parse error. Emit the current tag token. Reconsume the EOF
character in the data state. */
@@ -581,12 +952,13 @@ class HTML5 {
}
}
- private function afterAttributeNameState() {
+ private function afterAttributeNameState()
+ {
// Consume the next input character:
$this->char++;
$char = $this->character($this->char);
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
/* U+0009 CHARACTER TABULATION
U+000A LINE FEED (LF)
U+000B LINE TABULATION
@@ -595,24 +967,24 @@ class HTML5 {
Stay in the after attribute name state. */
$this->state = 'afterAttributeName';
- } elseif($char === '=') {
+ } elseif ($char === '=') {
/* U+003D EQUALS SIGN (=)
Switch to the before attribute value state. */
$this->state = 'beforeAttributeValue';
- } elseif($char === '>') {
+ } elseif ($char === '>') {
/* U+003E GREATER-THAN SIGN (>)
Emit the current tag token. Switch to the data state. */
$this->emitToken($this->token);
$this->state = 'data';
- } elseif($char === '/' && $this->character($this->char + 1) !== '>') {
+ } elseif ($char === '/' && $this->character($this->char + 1) !== '>') {
/* U+002F SOLIDUS (/)
Parse error unless this is a permitted slash. Switch to the
before attribute name state. */
$this->state = 'beforeAttributeName';
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
/* EOF
Parse error. Emit the current tag token. Reconsume the EOF
character in the data state. */
@@ -627,7 +999,7 @@ class HTML5 {
name to the current input character, and its value to the empty string.
Switch to the attribute name state. */
$this->token['attr'][] = array(
- 'name' => strtolower($char),
+ 'name' => strtolower($char),
'value' => null
);
@@ -635,12 +1007,13 @@ class HTML5 {
}
}
- private function beforeAttributeValueState() {
+ private function beforeAttributeValueState()
+ {
// Consume the next input character:
$this->char++;
$char = $this->character($this->char);
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
/* U+0009 CHARACTER TABULATION
U+000A LINE FEED (LF)
U+000B LINE TABULATION
@@ -649,24 +1022,24 @@ class HTML5 {
Stay in the before attribute value state. */
$this->state = 'beforeAttributeValue';
- } elseif($char === '"') {
+ } elseif ($char === '"') {
/* U+0022 QUOTATION MARK (")
Switch to the attribute value (double-quoted) state. */
$this->state = 'attributeValueDoubleQuoted';
- } elseif($char === '&') {
+ } elseif ($char === '&') {
/* U+0026 AMPERSAND (&)
Switch to the attribute value (unquoted) state and reconsume
this input character. */
$this->char--;
$this->state = 'attributeValueUnquoted';
- } elseif($char === '\'') {
+ } elseif ($char === '\'') {
/* U+0027 APOSTROPHE (')
Switch to the attribute value (single-quoted) state. */
$this->state = 'attributeValueSingleQuoted';
- } elseif($char === '>') {
+ } elseif ($char === '>') {
/* U+003E GREATER-THAN SIGN (>)
Emit the current tag token. Switch to the data state. */
$this->emitToken($this->token);
@@ -683,22 +1056,23 @@ class HTML5 {
}
}
- private function attributeValueDoubleQuotedState() {
+ private function attributeValueDoubleQuotedState()
+ {
// Consume the next input character:
$this->char++;
$char = $this->character($this->char);
- if($char === '"') {
+ if ($char === '"') {
/* U+0022 QUOTATION MARK (")
Switch to the before attribute name state. */
$this->state = 'beforeAttributeName';
- } elseif($char === '&') {
+ } elseif ($char === '&') {
/* U+0026 AMPERSAND (&)
Switch to the entity in attribute value state. */
$this->entityInAttributeValueState('double');
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
/* EOF
Parse error. Emit the current tag token. Reconsume the character
in the data state. */
@@ -718,22 +1092,23 @@ class HTML5 {
}
}
- private function attributeValueSingleQuotedState() {
+ private function attributeValueSingleQuotedState()
+ {
// Consume the next input character:
$this->char++;
$char = $this->character($this->char);
- if($char === '\'') {
+ if ($char === '\'') {
/* U+0022 QUOTATION MARK (')
Switch to the before attribute name state. */
$this->state = 'beforeAttributeName';
- } elseif($char === '&') {
+ } elseif ($char === '&') {
/* U+0026 AMPERSAND (&)
Switch to the entity in attribute value state. */
$this->entityInAttributeValueState('single');
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
/* EOF
Parse error. Emit the current tag token. Reconsume the character
in the data state. */
@@ -753,12 +1128,13 @@ class HTML5 {
}
}
- private function attributeValueUnquotedState() {
+ private function attributeValueUnquotedState()
+ {
// Consume the next input character:
$this->char++;
$char = $this->character($this->char);
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
/* U+0009 CHARACTER TABULATION
U+000A LINE FEED (LF)
U+000B LINE TABULATION
@@ -767,12 +1143,12 @@ class HTML5 {
Switch to the before attribute name state. */
$this->state = 'beforeAttributeName';
- } elseif($char === '&') {
+ } elseif ($char === '&') {
/* U+0026 AMPERSAND (&)
Switch to the entity in attribute value state. */
$this->entityInAttributeValueState();
- } elseif($char === '>') {
+ } elseif ($char === '>') {
/* U+003E GREATER-THAN SIGN (>)
Emit the current tag token. Switch to the data state. */
$this->emitToken($this->token);
@@ -789,7 +1165,8 @@ class HTML5 {
}
}
- private function entityInAttributeValueState() {
+ private function entityInAttributeValueState()
+ {
// Attempt to consume an entity.
$entity = $this->entity();
@@ -804,7 +1181,8 @@ class HTML5 {
$this->token['attr'][$last]['value'] .= $char;
}
- private function bogusCommentState() {
+ private function bogusCommentState()
+ {
/* Consume every character up to the first U+003E GREATER-THAN SIGN
character (>) or the end of the file (EOF), whichever comes first. Emit
a comment token whose data is the concatenation of all the characters
@@ -814,10 +1192,12 @@ class HTML5 {
end of the file otherwise. (If the comment was started by the end of
the file (EOF), the token is empty.) */
$data = $this->characters('^>', $this->char);
- $this->emitToken(array(
- 'data' => $data,
- 'type' => self::COMMENT
- ));
+ $this->emitToken(
+ array(
+ 'data' => $data,
+ 'type' => self::COMMENT
+ )
+ );
$this->char += strlen($data);
@@ -825,16 +1205,17 @@ class HTML5 {
$this->state = 'data';
/* If the end of the file was reached, reconsume the EOF character. */
- if($this->char === $this->EOF) {
+ if ($this->char === $this->EOF) {
$this->char = $this->EOF - 1;
}
}
- private function markupDeclarationOpenState() {
+ private function markupDeclarationOpenState()
+ {
/* If the next two characters are both U+002D HYPHEN-MINUS (-)
characters, consume those two characters, create a comment token whose
data is the empty string, and switch to the comment state. */
- if($this->character($this->char + 1, 2) === '--') {
+ if ($this->character($this->char + 1, 2) === '--') {
$this->char += 2;
$this->state = 'comment';
$this->token = array(
@@ -842,41 +1223,42 @@ class HTML5 {
'type' => self::COMMENT
);
- /* Otherwise if the next seven chacacters are a case-insensitive match
- for the word "DOCTYPE", then consume those characters and switch to the
- DOCTYPE state. */
- } elseif(strtolower($this->character($this->char + 1, 7)) === 'doctype') {
+ /* Otherwise if the next seven chacacters are a case-insensitive match
+ for the word "DOCTYPE", then consume those characters and switch to the
+ DOCTYPE state. */
+ } elseif (strtolower($this->character($this->char + 1, 7)) === 'doctype') {
$this->char += 7;
$this->state = 'doctype';
- /* Otherwise, is is a parse error. Switch to the bogus comment state.
- The next character that is consumed, if any, is the first character
- that will be in the comment. */
+ /* Otherwise, is is a parse error. Switch to the bogus comment state.
+ The next character that is consumed, if any, is the first character
+ that will be in the comment. */
} else {
$this->char++;
$this->state = 'bogusComment';
}
}
- private function commentState() {
+ private function commentState()
+ {
/* Consume the next input character: */
$this->char++;
$char = $this->char();
/* U+002D HYPHEN-MINUS (-) */
- if($char === '-') {
+ if ($char === '-') {
/* Switch to the comment dash state */
$this->state = 'commentDash';
- /* EOF */
- } elseif($this->char === $this->EOF) {
+ /* EOF */
+ } elseif ($this->char === $this->EOF) {
/* Parse error. Emit the comment token. Reconsume the EOF character
in the data state. */
$this->emitToken($this->token);
$this->char--;
$this->state = 'data';
- /* Anything else */
+ /* Anything else */
} else {
/* Append the input character to the comment token's data. Stay in
the comment state. */
@@ -884,62 +1266,65 @@ class HTML5 {
}
}
- private function commentDashState() {
+ private function commentDashState()
+ {
/* Consume the next input character: */
$this->char++;
$char = $this->char();
/* U+002D HYPHEN-MINUS (-) */
- if($char === '-') {
+ if ($char === '-') {
/* Switch to the comment end state */
$this->state = 'commentEnd';
- /* EOF */
- } elseif($this->char === $this->EOF) {
+ /* EOF */
+ } elseif ($this->char === $this->EOF) {
/* Parse error. Emit the comment token. Reconsume the EOF character
in the data state. */
$this->emitToken($this->token);
$this->char--;
$this->state = 'data';
- /* Anything else */
+ /* Anything else */
} else {
/* Append a U+002D HYPHEN-MINUS (-) character and the input
character to the comment token's data. Switch to the comment state. */
- $this->token['data'] .= '-'.$char;
+ $this->token['data'] .= '-' . $char;
$this->state = 'comment';
}
}
- private function commentEndState() {
+ private function commentEndState()
+ {
/* Consume the next input character: */
$this->char++;
$char = $this->char();
- if($char === '>') {
+ if ($char === '>') {
$this->emitToken($this->token);
$this->state = 'data';
- } elseif($char === '-') {
+ } elseif ($char === '-') {
$this->token['data'] .= '-';
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
$this->emitToken($this->token);
$this->char--;
$this->state = 'data';
} else {
- $this->token['data'] .= '--'.$char;
+ $this->token['data'] .= '--' . $char;
$this->state = 'comment';
}
}
- private function doctypeState() {
+ private function doctypeState()
+ {
/* Consume the next input character: */
$this->char++;
$char = $this->char();
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
$this->state = 'beforeDoctypeName';
} else {
@@ -948,15 +1333,16 @@ class HTML5 {
}
}
- private function beforeDoctypeNameState() {
+ private function beforeDoctypeNameState()
+ {
/* Consume the next input character: */
$this->char++;
$char = $this->char();
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
// Stay in the before DOCTYPE name state.
- } elseif(preg_match('/^[a-z]$/', $char)) {
+ } elseif (preg_match('/^[a-z]$/', $char)) {
$this->token = array(
'name' => strtoupper($char),
'type' => self::DOCTYPE,
@@ -965,21 +1351,25 @@ class HTML5 {
$this->state = 'doctypeName';
- } elseif($char === '>') {
- $this->emitToken(array(
- 'name' => null,
- 'type' => self::DOCTYPE,
- 'error' => true
- ));
+ } elseif ($char === '>') {
+ $this->emitToken(
+ array(
+ 'name' => null,
+ 'type' => self::DOCTYPE,
+ 'error' => true
+ )
+ );
$this->state = 'data';
- } elseif($this->char === $this->EOF) {
- $this->emitToken(array(
- 'name' => null,
- 'type' => self::DOCTYPE,
- 'error' => true
- ));
+ } elseif ($this->char === $this->EOF) {
+ $this->emitToken(
+ array(
+ 'name' => null,
+ 'type' => self::DOCTYPE,
+ 'error' => true
+ )
+ );
$this->char--;
$this->state = 'data';
@@ -995,22 +1385,23 @@ class HTML5 {
}
}
- private function doctypeNameState() {
+ private function doctypeNameState()
+ {
/* Consume the next input character: */
$this->char++;
$char = $this->char();
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
$this->state = 'AfterDoctypeName';
- } elseif($char === '>') {
+ } elseif ($char === '>') {
$this->emitToken($this->token);
$this->state = 'data';
- } elseif(preg_match('/^[a-z]$/', $char)) {
+ } elseif (preg_match('/^[a-z]$/', $char)) {
$this->token['name'] .= strtoupper($char);
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
$this->emitToken($this->token);
$this->char--;
$this->state = 'data';
@@ -1024,19 +1415,20 @@ class HTML5 {
: true;
}
- private function afterDoctypeNameState() {
+ private function afterDoctypeNameState()
+ {
/* Consume the next input character: */
$this->char++;
$char = $this->char();
- if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+ if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
// Stay in the DOCTYPE name state.
- } elseif($char === '>') {
+ } elseif ($char === '>') {
$this->emitToken($this->token);
$this->state = 'data';
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
$this->emitToken($this->token);
$this->char--;
$this->state = 'data';
@@ -1047,16 +1439,17 @@ class HTML5 {
}
}
- private function bogusDoctypeState() {
+ private function bogusDoctypeState()
+ {
/* Consume the next input character: */
$this->char++;
$char = $this->char();
- if($char === '>') {
+ if ($char === '>') {
$this->emitToken($this->token);
$this->state = 'data';
- } elseif($this->char === $this->EOF) {
+ } elseif ($this->char === $this->EOF) {
$this->emitToken($this->token);
$this->char--;
$this->state = 'data';
@@ -1066,22 +1459,23 @@ class HTML5 {
}
}
- private function entity() {
+ private function entity()
+ {
$start = $this->char;
// This section defines how to consume an entity. This definition is
// used when parsing entities in text and in attributes.
// The behaviour depends on the identity of the next character (the
- // one immediately after the U+0026 AMPERSAND character):
+ // one immediately after the U+0026 AMPERSAND character):
- switch($this->character($this->char + 1)) {
+ switch ($this->character($this->char + 1)) {
// U+0023 NUMBER SIGN (#)
case '#':
// The behaviour further depends on the character after the
// U+0023 NUMBER SIGN:
- switch($this->character($this->char + 1)) {
+ switch ($this->character($this->char + 1)) {
// U+0078 LATIN SMALL LETTER X
// U+0058 LATIN CAPITAL LETTER X
case 'x':
@@ -1094,7 +1488,7 @@ class HTML5 {
// words, 0-9, A-F, a-f).
$char = 1;
$char_class = '0-9A-Fa-f';
- break;
+ break;
// Anything else
default:
@@ -1103,7 +1497,7 @@ class HTML5 {
// NINE (i.e. just 0-9).
$char = 0;
$char_class = '0-9';
- break;
+ break;
}
// Consume as many characters as match the range of characters
@@ -1114,7 +1508,7 @@ class HTML5 {
$cond = strlen($e_name) > 0;
// The rest of the parsing happens bellow.
- break;
+ break;
// Anything else
default:
@@ -1124,12 +1518,12 @@ class HTML5 {
$e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
$len = strlen($e_name);
- for($c = 1; $c <= $len; $c++) {
+ for ($c = 1; $c <= $len; $c++) {
$id = substr($e_name, 0, $c);
$this->char++;
- if(in_array($id, $this->entities)) {
- if ($e_name[$c-1] !== ';') {
+ if (in_array($id, $this->entities)) {
+ if ($e_name[$c - 1] !== ';') {
if ($c < $len && $e_name[$c] == ';') {
$this->char++; // consume extra semicolon
}
@@ -1141,10 +1535,10 @@ class HTML5 {
$cond = isset($entity);
// The rest of the parsing happens bellow.
- break;
+ break;
}
- if(!$cond) {
+ if (!$cond) {
// If no match can be made, then this is a parse error. No
// characters are consumed, and nothing is returned.
$this->char = $start;
@@ -1153,81 +1547,157 @@ class HTML5 {
// Return a character token for the character corresponding to the
// entity name (as given by the second column of the entities table).
- return html_entity_decode('&'.$entity.';', ENT_QUOTES, 'UTF-8');
+ return html_entity_decode('&' . $entity . ';', ENT_QUOTES, 'UTF-8');
}
- private function emitToken($token) {
+ private function emitToken($token)
+ {
$emit = $this->tree->emitToken($token);
- if(is_int($emit)) {
+ if (is_int($emit)) {
$this->content_model = $emit;
- } elseif($token['type'] === self::ENDTAG) {
+ } elseif ($token['type'] === self::ENDTAG) {
$this->content_model = self::PCDATA;
}
}
- private function EOF() {
+ private function EOF()
+ {
$this->state = null;
- $this->tree->emitToken(array(
- 'type' => self::EOF
- ));
+ $this->tree->emitToken(
+ array(
+ 'type' => self::EOF
+ )
+ );
}
}
-class HTML5TreeConstructer {
+class HTML5TreeConstructer
+{
public $stack = array();
private $phase;
private $mode;
private $dom;
private $foster_parent = null;
- private $a_formatting = array();
+ private $a_formatting = array();
private $head_pointer = null;
private $form_pointer = null;
- private $scoping = array('button','caption','html','marquee','object','table','td','th');
- private $formatting = array('a','b','big','em','font','i','nobr','s','small','strike','strong','tt','u');
- private $special = array('address','area','base','basefont','bgsound',
- 'blockquote','body','br','center','col','colgroup','dd','dir','div','dl',
- 'dt','embed','fieldset','form','frame','frameset','h1','h2','h3','h4','h5',
- 'h6','head','hr','iframe','image','img','input','isindex','li','link',
- 'listing','menu','meta','noembed','noframes','noscript','ol','optgroup',
- 'option','p','param','plaintext','pre','script','select','spacer','style',
- 'tbody','textarea','tfoot','thead','title','tr','ul','wbr');
+ private $scoping = array('button', 'caption', 'html', 'marquee', 'object', 'table', 'td', 'th');
+ private $formatting = array(
+ 'a',
+ 'b',
+ 'big',
+ 'em',
+ 'font',
+ 'i',
+ 'nobr',
+ 's',
+ 'small',
+ 'strike',
+ 'strong',
+ 'tt',
+ 'u'
+ );
+ private $special = array(
+ 'address',
+ 'area',
+ 'base',
+ 'basefont',
+ 'bgsound',
+ 'blockquote',
+ 'body',
+ 'br',
+ 'center',
+ 'col',
+ 'colgroup',
+ 'dd',
+ 'dir',
+ 'div',
+ 'dl',
+ 'dt',
+ 'embed',
+ 'fieldset',
+ 'form',
+ 'frame',
+ 'frameset',
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ 'head',
+ 'hr',
+ 'iframe',
+ 'image',
+ 'img',
+ 'input',
+ 'isindex',
+ 'li',
+ 'link',
+ 'listing',
+ 'menu',
+ 'meta',
+ 'noembed',
+ 'noframes',
+ 'noscript',
+ 'ol',
+ 'optgroup',
+ 'option',
+ 'p',
+ 'param',
+ 'plaintext',
+ 'pre',
+ 'script',
+ 'select',
+ 'spacer',
+ 'style',
+ 'tbody',
+ 'textarea',
+ 'tfoot',
+ 'thead',
+ 'title',
+ 'tr',
+ 'ul',
+ 'wbr'
+ );
// The different phases.
const INIT_PHASE = 0;
const ROOT_PHASE = 1;
const MAIN_PHASE = 2;
- const END_PHASE = 3;
+ const END_PHASE = 3;
// The different insertion modes for the main phase.
const BEFOR_HEAD = 0;
- const IN_HEAD = 1;
+ const IN_HEAD = 1;
const AFTER_HEAD = 2;
- const IN_BODY = 3;
- const IN_TABLE = 4;
+ const IN_BODY = 3;
+ const IN_TABLE = 4;
const IN_CAPTION = 5;
- const IN_CGROUP = 6;
- const IN_TBODY = 7;
- const IN_ROW = 8;
- const IN_CELL = 9;
- const IN_SELECT = 10;
+ const IN_CGROUP = 6;
+ const IN_TBODY = 7;
+ const IN_ROW = 8;
+ const IN_CELL = 9;
+ const IN_SELECT = 10;
const AFTER_BODY = 11;
- const IN_FRAME = 12;
+ const IN_FRAME = 12;
const AFTR_FRAME = 13;
// The different types of elements.
- const SPECIAL = 0;
- const SCOPING = 1;
+ const SPECIAL = 0;
+ const SCOPING = 1;
const FORMATTING = 2;
- const PHRASING = 3;
+ const PHRASING = 3;
- const MARKER = 0;
+ const MARKER = 0;
- public function __construct() {
+ public function __construct()
+ {
$this->phase = self::INIT_PHASE;
$this->mode = self::BEFOR_HEAD;
$this->dom = new DOMDocument;
@@ -1239,16 +1709,26 @@ class HTML5TreeConstructer {
}
// Process tag tokens
- public function emitToken($token) {
- switch($this->phase) {
- case self::INIT_PHASE: return $this->initPhase($token); break;
- case self::ROOT_PHASE: return $this->rootElementPhase($token); break;
- case self::MAIN_PHASE: return $this->mainPhase($token); break;
- case self::END_PHASE : return $this->trailingEndPhase($token); break;
+ public function emitToken($token)
+ {
+ switch ($this->phase) {
+ case self::INIT_PHASE:
+ return $this->initPhase($token);
+ break;
+ case self::ROOT_PHASE:
+ return $this->rootElementPhase($token);
+ break;
+ case self::MAIN_PHASE:
+ return $this->mainPhase($token);
+ break;
+ case self::END_PHASE :
+ return $this->trailingEndPhase($token);
+ break;
}
}
- private function initPhase($token) {
+ private function initPhase($token)
+ {
/* Initially, the tree construction stage must handle each token
emitted from the tokenisation stage as follows: */
@@ -1260,13 +1740,14 @@ class HTML5TreeConstructer {
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
or U+0020 SPACE
An end-of-file token */
- if((isset($token['error']) && $token['error']) ||
- $token['type'] === HTML5::COMMENT ||
- $token['type'] === HTML5::STARTTAG ||
- $token['type'] === HTML5::ENDTAG ||
- $token['type'] === HTML5::EOF ||
- ($token['type'] === HTML5::CHARACTR && isset($token['data']) &&
- !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']))) {
+ if ((isset($token['error']) && $token['error']) ||
+ $token['type'] === HTML5::COMMENT ||
+ $token['type'] === HTML5::STARTTAG ||
+ $token['type'] === HTML5::ENDTAG ||
+ $token['type'] === HTML5::EOF ||
+ ($token['type'] === HTML5::CHARACTR && isset($token['data']) &&
+ !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']))
+ ) {
/* This specification does not define how to handle this case. In
particular, user agents may ignore the entirety of this specification
altogether for such documents, and instead invoke special parse modes
@@ -1275,8 +1756,8 @@ class HTML5TreeConstructer {
$this->phase = self::ROOT_PHASE;
return $this->rootElementPhase($token);
- /* A DOCTYPE token marked as being correct */
- } elseif(isset($token['error']) && !$token['error']) {
+ /* A DOCTYPE token marked as being correct */
+ } elseif (isset($token['error']) && !$token['error']) {
/* Append a DocumentType node to the Document node, with the name
attribute set to the name given in the DOCTYPE token (which will be
"HTML"), and the other attributes specific to DocumentType objects
@@ -1287,52 +1768,58 @@ class HTML5TreeConstructer {
stage. */
$this->phase = self::ROOT_PHASE;
- /* 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(isset($token['data']) && preg_match('/^[\t\n\x0b\x0c ]+$/',
- $token['data'])) {
+ /* 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 (isset($token['data']) && preg_match(
+ '/^[\t\n\x0b\x0c ]+$/',
+ $token['data']
+ )
+ ) {
/* Append that character to the Document node. */
$text = $this->dom->createTextNode($token['data']);
$this->dom->appendChild($text);
}
}
- private function rootElementPhase($token) {
+ private function rootElementPhase($token)
+ {
/* After the initial phase, as each token is emitted from the tokenisation
stage, it must be processed as described in this section. */
/* A DOCTYPE token */
- if($token['type'] === HTML5::DOCTYPE) {
+ if ($token['type'] === HTML5::DOCTYPE) {
// Parse error. Ignore the token.
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::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::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+ /* 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::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+ ) {
/* Append that character to the Document node. */
$text = $this->dom->createTextNode($token['data']);
$this->dom->appendChild($text);
- /* A character token that is not one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED
- (FF), or U+0020 SPACE
- A start tag token
- An end tag token
- An end-of-file token */
- } elseif(($token['type'] === HTML5::CHARACTR &&
- !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
- $token['type'] === HTML5::STARTTAG ||
- $token['type'] === HTML5::ENDTAG ||
- $token['type'] === HTML5::EOF) {
+ /* A character token that is not one of U+0009 CHARACTER TABULATION,
+ U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED
+ (FF), or U+0020 SPACE
+ A start tag token
+ An end tag token
+ An end-of-file token */
+ } elseif (($token['type'] === HTML5::CHARACTR &&
+ !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
+ $token['type'] === HTML5::STARTTAG ||
+ $token['type'] === HTML5::ENDTAG ||
+ $token['type'] === HTML5::EOF
+ ) {
/* Create an HTMLElement node with the tag name html, in the HTML
namespace. Append it to the Document object. Switch to the main
phase and reprocess the current token. */
@@ -1345,15 +1832,16 @@ class HTML5TreeConstructer {
}
}
- private function mainPhase($token) {
+ private function mainPhase($token)
+ {
/* Tokens in the main phase must be handled as follows: */
/* A DOCTYPE token */
- if($token['type'] === HTML5::DOCTYPE) {
+ if ($token['type'] === HTML5::DOCTYPE) {
// Parse error. Ignore the token.
- /* A start tag token with the tag name "html" */
- } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') {
+ /* A start tag token with the tag name "html" */
+ } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') {
/* If this start tag token was not the first start tag token, then
it is a parse error. */
@@ -1361,59 +1849,91 @@ class HTML5TreeConstructer {
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'])) {
+ foreach ($token['attr'] as $attr) {
+ if (!$this->stack[0]->hasAttribute($attr['name'])) {
$this->stack[0]->setAttribute($attr['name'], $attr['value']);
}
}
- /* An end-of-file token */
- } elseif($token['type'] === HTML5::EOF) {
+ /* An end-of-file token */
+ } elseif ($token['type'] === HTML5::EOF) {
/* Generate implied end tags. */
$this->generateImpliedEndTags();
- /* Anything else. */
+ /* Anything else. */
} else {
/* Depends on the insertion mode: */
- switch($this->mode) {
- case self::BEFOR_HEAD: return $this->beforeHead($token); break;
- case self::IN_HEAD: return $this->inHead($token); break;
- case self::AFTER_HEAD: return $this->afterHead($token); break;
- case self::IN_BODY: return $this->inBody($token); break;
- case self::IN_TABLE: return $this->inTable($token); break;
- case self::IN_CAPTION: return $this->inCaption($token); break;
- case self::IN_CGROUP: return $this->inColumnGroup($token); break;
- case self::IN_TBODY: return $this->inTableBody($token); break;
- case self::IN_ROW: return $this->inRow($token); break;
- case self::IN_CELL: return $this->inCell($token); break;
- case self::IN_SELECT: return $this->inSelect($token); break;
- case self::AFTER_BODY: return $this->afterBody($token); break;
- case self::IN_FRAME: return $this->inFrameset($token); break;
- case self::AFTR_FRAME: return $this->afterFrameset($token); break;
- case self::END_PHASE: return $this->trailingEndPhase($token); break;
+ switch ($this->mode) {
+ case self::BEFOR_HEAD:
+ return $this->beforeHead($token);
+ break;
+ case self::IN_HEAD:
+ return $this->inHead($token);
+ break;
+ case self::AFTER_HEAD:
+ return $this->afterHead($token);
+ break;
+ case self::IN_BODY:
+ return $this->inBody($token);
+ break;
+ case self::IN_TABLE:
+ return $this->inTable($token);
+ break;
+ case self::IN_CAPTION:
+ return $this->inCaption($token);
+ break;
+ case self::IN_CGROUP:
+ return $this->inColumnGroup($token);
+ break;
+ case self::IN_TBODY:
+ return $this->inTableBody($token);
+ break;
+ case self::IN_ROW:
+ return $this->inRow($token);
+ break;
+ case self::IN_CELL:
+ return $this->inCell($token);
+ break;
+ case self::IN_SELECT:
+ return $this->inSelect($token);
+ break;
+ case self::AFTER_BODY:
+ return $this->afterBody($token);
+ break;
+ case self::IN_FRAME:
+ return $this->inFrameset($token);
+ break;
+ case self::AFTR_FRAME:
+ return $this->afterFrameset($token);
+ break;
+ case self::END_PHASE:
+ return $this->trailingEndPhase($token);
+ break;
}
}
}
- private function beforeHead($token) {
+ private function beforeHead($token)
+ {
/* 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::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+ if ($token['type'] === HTML5::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+ ) {
/* Append the character to the current node. */
$this->insertText($token['data']);
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::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 start tag token with the tag name "head" */
- } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') {
+ /* A start tag token with the tag name "head" */
+ } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') {
/* Create an element for the token, append the new element to the
current node and push it onto the stack of open elements. */
$element = $this->insertElement($token);
@@ -1424,32 +1944,38 @@ class HTML5TreeConstructer {
/* Change the insertion mode to "in head". */
$this->mode = self::IN_HEAD;
- /* A start tag token whose tag name is one of: "base", "link", "meta",
- "script", "style", "title". Or an end tag with the tag name "html".
- Or a character token that is not one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- or U+0020 SPACE. Or any other start tag token */
- } elseif($token['type'] === HTML5::STARTTAG ||
- ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') ||
- ($token['type'] === HTML5::CHARACTR && !preg_match('/^[\t\n\x0b\x0c ]$/',
- $token['data']))) {
+ /* A start tag token whose tag name is one of: "base", "link", "meta",
+ "script", "style", "title". Or an end tag with the tag name "html".
+ Or a character token that is not one of U+0009 CHARACTER TABULATION,
+ U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+ or U+0020 SPACE. Or any other start tag token */
+ } elseif ($token['type'] === HTML5::STARTTAG ||
+ ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') ||
+ ($token['type'] === HTML5::CHARACTR && !preg_match(
+ '/^[\t\n\x0b\x0c ]$/',
+ $token['data']
+ ))
+ ) {
/* Act as if a start tag token with the tag name "head" and no
attributes had been seen, then reprocess the current token. */
- $this->beforeHead(array(
- 'name' => 'head',
- 'type' => HTML5::STARTTAG,
- 'attr' => array()
- ));
+ $this->beforeHead(
+ array(
+ 'name' => 'head',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => array()
+ )
+ );
return $this->inHead($token);
- /* Any other end tag */
- } elseif($token['type'] === HTML5::ENDTAG) {
+ /* Any other end tag */
+ } elseif ($token['type'] === HTML5::ENDTAG) {
/* Parse error. Ignore the token. */
}
}
- private function inHead($token) {
+ private function inHead($token)
+ {
/* Handle the token as follows: */
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
@@ -1459,30 +1985,34 @@ class HTML5TreeConstructer {
THIS DIFFERS FROM THE SPEC: If the current node is either a title, style
or script element, append the character to the current node regardless
of its content. */
- if(($token['type'] === HTML5::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || (
- $token['type'] === HTML5::CHARACTR && in_array(end($this->stack)->nodeName,
- array('title', 'style', 'script')))) {
+ if (($token['type'] === HTML5::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || (
+ $token['type'] === HTML5::CHARACTR && in_array(
+ end($this->stack)->nodeName,
+ array('title', 'style', 'script')
+ ))
+ ) {
/* Append the character to the current node. */
$this->insertText($token['data']);
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::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::ENDTAG &&
- in_array($token['name'], array('title', 'style', 'script'))) {
+ } elseif ($token['type'] === HTML5::ENDTAG &&
+ in_array($token['name'], array('title', 'style', 'script'))
+ ) {
array_pop($this->stack);
return HTML5::PCDATA;
- /* A start tag with the tag name "title" */
- } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') {
+ /* A start tag with the tag name "title" */
+ } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') {
/* Create an element for the token and append the new element to the
node pointed to by the head element pointer, or, if that is null
(innerHTML case), to the current node. */
- if($this->head_pointer !== null) {
+ if ($this->head_pointer !== null) {
$element = $this->insertElement($token, false);
$this->head_pointer->appendChild($element);
@@ -1493,12 +2023,12 @@ class HTML5TreeConstructer {
/* Switch the tokeniser's content model flag to the RCDATA state. */
return HTML5::RCDATA;
- /* A start tag with the tag name "style" */
- } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') {
+ /* A start tag with the tag name "style" */
+ } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') {
/* Create an element for the token and append the new element to the
node pointed to by the head element pointer, or, if that is null
(innerHTML case), to the current node. */
- if($this->head_pointer !== null) {
+ if ($this->head_pointer !== null) {
$element = $this->insertElement($token, false);
$this->head_pointer->appendChild($element);
@@ -1509,8 +2039,8 @@ class HTML5TreeConstructer {
/* Switch the tokeniser's content model flag to the CDATA state. */
return HTML5::CDATA;
- /* A start tag with the tag name "script" */
- } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') {
+ /* A start tag with the tag name "script" */
+ } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') {
/* Create an element for the token. */
$element = $this->insertElement($token, false);
$this->head_pointer->appendChild($element);
@@ -1518,13 +2048,16 @@ class HTML5TreeConstructer {
/* Switch the tokeniser's content model flag to the CDATA state. */
return HTML5::CDATA;
- /* A start tag with the tag name "base", "link", or "meta" */
- } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
- array('base', 'link', 'meta'))) {
+ /* A start tag with the tag name "base", "link", or "meta" */
+ } elseif ($token['type'] === HTML5::STARTTAG && in_array(
+ $token['name'],
+ array('base', 'link', 'meta')
+ )
+ ) {
/* Create an element for the token and append the new element to the
node pointed to by the head element pointer, or, if that is null
(innerHTML case), to the current node. */
- if($this->head_pointer !== null) {
+ if ($this->head_pointer !== null) {
$element = $this->insertElement($token, false);
$this->head_pointer->appendChild($element);
array_pop($this->stack);
@@ -1533,14 +2066,14 @@ class HTML5TreeConstructer {
$this->insertElement($token);
}
- /* An end tag with the tag name "head" */
- } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') {
+ /* An end tag with the tag name "head" */
+ } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') {
/* If the current node is a head element, pop the current node off
the stack of open elements. */
- if($this->head_pointer->isSameNode(end($this->stack))) {
+ if ($this->head_pointer->isSameNode(end($this->stack))) {
array_pop($this->stack);
- /* Otherwise, this is a parse error. */
+ /* Otherwise, this is a parse error. */
} else {
// k
}
@@ -1548,22 +2081,25 @@ class HTML5TreeConstructer {
/* Change the insertion mode to "after head". */
$this->mode = self::AFTER_HEAD;
- /* A start tag with the tag name "head" or an end tag except "html". */
- } elseif(($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') ||
- ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html')) {
+ /* A start tag with the tag name "head" or an end tag except "html". */
+ } elseif (($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') ||
+ ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html')
+ ) {
// Parse error. Ignore the token.
- /* Anything else */
+ /* Anything else */
} else {
/* If the current node is a head element, act as if an end tag
token with the tag name "head" had been seen. */
- if($this->head_pointer->isSameNode(end($this->stack))) {
- $this->inHead(array(
- 'name' => 'head',
- 'type' => HTML5::ENDTAG
- ));
+ if ($this->head_pointer->isSameNode(end($this->stack))) {
+ $this->inHead(
+ array(
+ 'name' => 'head',
+ 'type' => HTML5::ENDTAG
+ )
+ );
- /* Otherwise, change the insertion mode to "after head". */
+ /* Otherwise, change the insertion mode to "after head". */
} else {
$this->mode = self::AFTER_HEAD;
}
@@ -1573,66 +2109,74 @@ class HTML5TreeConstructer {
}
}
- private function afterHead($token) {
+ private function afterHead($token)
+ {
/* 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::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+ if ($token['type'] === HTML5::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+ ) {
/* Append the character to the current node. */
$this->insertText($token['data']);
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::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 start tag token with the tag name "body" */
- } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') {
+ /* A start tag token with the tag name "body" */
+ } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') {
/* Insert a body element for the token. */
$this->insertElement($token);
/* 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::STARTTAG && $token['name'] === 'frameset') {
+ /* A start tag token with the tag name "frameset" */
+ } elseif ($token['type'] === HTML5::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_FRAME;
- /* A start tag token whose tag name is one of: "base", "link", "meta",
- "script", "style", "title" */
- } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
- array('base', 'link', 'meta', 'script', 'style', 'title'))) {
+ /* A start tag token whose tag name is one of: "base", "link", "meta",
+ "script", "style", "title" */
+ } elseif ($token['type'] === HTML5::STARTTAG && in_array(
+ $token['name'],
+ array('base', 'link', 'meta', 'script', 'style', 'title')
+ )
+ ) {
/* Parse error. Switch the insertion mode back to "in head" and
reprocess the token. */
$this->mode = self::IN_HEAD;
return $this->inHead($token);
- /* Anything else */
+ /* Anything else */
} else {
/* Act as if a start tag token with the tag name "body" and no
attributes had been seen, and then reprocess the current token. */
- $this->afterHead(array(
- 'name' => 'body',
- 'type' => HTML5::STARTTAG,
- 'attr' => array()
- ));
+ $this->afterHead(
+ array(
+ 'name' => 'body',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => array()
+ )
+ );
return $this->inBody($token);
}
}
- private function inBody($token) {
+ private function inBody($token)
+ {
/* Handle the token as follows: */
- switch($token['type']) {
+ switch ($token['type']) {
/* A character token */
case HTML5::CHARACTR:
/* Reconstruct the active formatting elements, if any. */
@@ -1640,1015 +2184,1159 @@ class HTML5TreeConstructer {
/* Append the token's character to the current node. */
$this->insertText($token['data']);
- break;
+ break;
/* A comment token */
case HTML5::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;
+ break;
case HTML5::STARTTAG:
- switch($token['name']) {
- /* A start tag token whose tag name is one of: "script",
- "style" */
- case 'script': case 'style':
- /* Process the token as if the insertion mode had been "in
- head". */
- return $this->inHead($token);
- break;
+ switch ($token['name']) {
+ /* A start tag token whose tag name is one of: "script",
+ "style" */
+ case 'script':
+ case 'style':
+ /* Process the token as if the insertion mode had been "in
+ head". */
+ return $this->inHead($token);
+ break;
- /* A start tag token whose tag name is one of: "base", "link",
- "meta", "title" */
- case 'base': case 'link': case 'meta': case 'title':
- /* Parse error. Process the token as if the insertion mode
- had been "in head". */
- return $this->inHead($token);
- break;
+ /* A start tag token whose tag name is one of: "base", "link",
+ "meta", "title" */
+ case 'base':
+ case 'link':
+ case 'meta':
+ case 'title':
+ /* Parse error. Process the token as if the insertion mode
+ had been "in head". */
+ return $this->inHead($token);
+ 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.
- (innerHTML case) */
- if(count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') {
- // Ignore
+ /* 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.
+ (innerHTML case) */
+ if (count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') {
+ // 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']);
+ /* 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;
+ break;
- /* A start tag whose tag name is one of: "address",
- "blockquote", "center", "dir", "div", "dl", "fieldset",
- "listing", "menu", "ol", "p", "ul" */
- case 'address': case 'blockquote': case 'center': case 'dir':
- case 'div': case 'dl': case 'fieldset': case 'listing':
- case 'menu': case 'ol': case 'p': 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::ENDTAG
- ));
- }
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
- 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) {
- // 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::ENDTAG
- ));
+ /* A start tag whose tag name is one of: "address",
+ "blockquote", "center", "dir", "div", "dl", "fieldset",
+ "listing", "menu", "ol", "p", "ul" */
+ case 'address':
+ case 'blockquote':
+ case 'center':
+ case 'dir':
+ case 'div':
+ case 'dl':
+ case 'fieldset':
+ case 'listing':
+ case 'menu':
+ case 'ol':
+ case 'p':
+ 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::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;
+ /* Insert an HTML element for the token. */
+ $this->insertElement($token);
+ break;
- /* A start tag whose tag name is "li", "dd" or "dt" */
- case 'li': case 'dd': case 'dt':
- /* 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::ENDTAG
- ));
- }
+ /* 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) {
+ // Ignore.
- $stack_length = count($this->stack) - 1;
-
- for($n = $stack_length; 0 <= $n; $n--) {
- /* 1. Initialise node to be the current node (the
- bottommost node of the stack). */
- $stop = false;
- $node = $this->stack[$n];
- $cat = $this->getElementCategory($node->tagName);
-
- /* 2. If node is an li, dd or dt element, then pop all
- the nodes from the current node up to node, including
- node, then stop this algorithm. */
- if($token['name'] === $node->tagName || ($token['name'] !== 'li'
- && ($node->tagName === 'dd' || $node->tagName === 'dt'))) {
- for($x = $stack_length; $x >= $n ; $x--) {
- array_pop($this->stack);
+ /* 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::ENDTAG
+ )
+ );
}
- break;
+ /* 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;
+
+ /* A start tag whose tag name is "li", "dd" or "dt" */
+ case 'li':
+ case 'dd':
+ case 'dt':
+ /* 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::ENDTAG
+ )
+ );
}
- /* 3. If node is not in the formatting category, and is
- not in the phrasing category, and is not an address or
- div element, then stop this algorithm. */
- if($cat !== self::FORMATTING && $cat !== self::PHRASING &&
- $node->tagName !== 'address' && $node->tagName !== 'div') {
- break;
+ $stack_length = count($this->stack) - 1;
+
+ for ($n = $stack_length; 0 <= $n; $n--) {
+ /* 1. Initialise node to be the current node (the
+ bottommost node of the stack). */
+ $stop = false;
+ $node = $this->stack[$n];
+ $cat = $this->getElementCategory($node->tagName);
+
+ /* 2. If node is an li, dd or dt element, then pop all
+ the nodes from the current node up to node, including
+ node, then stop this algorithm. */
+ if ($token['name'] === $node->tagName || ($token['name'] !== 'li'
+ && ($node->tagName === 'dd' || $node->tagName === 'dt'))
+ ) {
+ for ($x = $stack_length; $x >= $n; $x--) {
+ array_pop($this->stack);
+ }
+
+ break;
+ }
+
+ /* 3. If node is not in the formatting category, and is
+ not in the phrasing category, and is not an address or
+ div element, then stop this algorithm. */
+ if ($cat !== self::FORMATTING && $cat !== self::PHRASING &&
+ $node->tagName !== 'address' && $node->tagName !== 'div'
+ ) {
+ break;
+ }
}
- }
- /* Finally, insert an HTML element with the same tag
- name as the token's. */
- $this->insertElement($token);
- break;
+ /* 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::ENDTAG
- ));
- }
+ /* 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::ENDTAG
+ )
+ );
+ }
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
+ /* Insert an HTML element for the token. */
+ $this->insertElement($token);
- return HTML5::PLAINTEXT;
- break;
+ return HTML5::PLAINTEXT;
+ 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::ENDTAG
- ));
- }
+ /* 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::ENDTAG
+ )
+ );
+ }
- /* 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
- this is a parse error; pop elements from the stack until an
- element with one of those tag names has been popped from the
- stack. */
- while($this->elementInScope(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
+ this is a parse error; pop elements from the stack until an
+ element with one of those tag names has been popped from the
+ stack. */
+ while ($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
+ array_pop($this->stack);
+ }
+
+ /* Insert an HTML element for the token. */
+ $this->insertElement($token);
+ break;
+
+ /* 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]->nodeName === 'a') {
+ $this->emitToken(
+ array(
+ 'name' => 'a',
+ 'type' => HTML5::ENDTAG
+ )
+ );
+ 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;
+
+ /* A start tag whose tag name is one of: "b", "big", "em", "font",
+ "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
+ case 'b':
+ case 'big':
+ case 'em':
+ case 'font':
+ case 'i':
+ case 'nobr':
+ 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;
+
+ /* 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.) */
+ if ($this->elementInScope('button')) {
+ $this->inBody(
+ array(
+ 'name' => 'button',
+ 'type' => HTML5::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;
+ break;
+
+ /* A start tag token whose tag name is one of: "marquee", "object" */
+ 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;
+ break;
+
+ /* A start tag token whose tag name is "xmp" */
+ case 'xmp':
+ /* Reconstruct the active formatting elements, if any. */
+ $this->reconstructActiveFormattingElements();
+
+ /* Insert an HTML element for the token. */
+ $this->insertElement($token);
+
+ /* Switch the content model flag to the CDATA state. */
+ return HTML5::CDATA;
+ break;
+
+ /* 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->elementInScope('p')) {
+ $this->emitToken(
+ array(
+ 'name' => 'p',
+ 'type' => HTML5::ENDTAG
+ )
+ );
+ }
+
+ /* Insert an HTML element for the token. */
+ $this->insertElement($token);
+
+ /* 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 'param':
+ 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);
- }
+ break;
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
- break;
-
- /* 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]->nodeName === 'a') {
- $this->emitToken(array(
- 'name' => 'a',
- 'type' => HTML5::ENDTAG
- ));
- 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::ENDTAG
+ )
+ );
}
- }
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
+ /* Insert an HTML element for the token. */
+ $this->insertElement($token);
- /* Insert an HTML element for the token. */
- $el = $this->insertElement($token);
+ /* Immediately pop the current node off the stack of open elements. */
+ array_pop($this->stack);
+ break;
- /* Add that element to the list of active formatting
- elements. */
- $this->a_formatting[] = $el;
+ /* 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';
+ return $this->inBody($token);
+ break;
+
+ /* A start tag whose tag name is "input" */
+ case 'input':
+ /* Reconstruct the active formatting elements, if any. */
+ $this->reconstructActiveFormattingElements();
+
+ /* Insert an input element for the token. */
+ $element = $this->insertElement($token, false);
+
+ /* If the form element pointer is not null, then associate the
+ input element with the form element pointed to by the form
+ element pointer. */
+ $this->form_pointer !== null
+ ? $this->form_pointer->appendChild($element)
+ : end($this->stack)->appendChild($element);
+
+ /* Pop that input element off the stack of open elements. */
+ array_pop($this->stack);
+ break;
+
+ /* A start tag whose tag name is "isindex" */
+ case 'isindex':
+ /* Parse error. */
+ // w/e
+
+ /* 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. */
+ $this->inBody(
+ array(
+ 'name' => 'body',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => array()
+ )
+ );
+
+ /* Act as if a start tag token with the tag name "hr" had
+ been seen. */
+ $this->inBody(
+ array(
+ 'name' => 'hr',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => array()
+ )
+ );
+
+ /* Act as if a start tag token with the tag name "p" had
+ been seen. */
+ $this->inBody(
+ array(
+ 'name' => 'p',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => array()
+ )
+ );
+
+ /* Act as if a start tag token with the tag name "label"
+ had been seen. */
+ $this->inBody(
+ array(
+ 'name' => 'label',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => array()
+ )
+ );
+
+ /* Act as if a stream of character tokens had been seen. */
+ $this->insertText(
+ 'This is a searchable index. ' .
+ 'Insert your search keywords here: '
+ );
+
+ /* 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 = $token['attr'];
+ $attr[] = array('name' => 'name', 'value' => 'isindex');
+
+ $this->inBody(
+ array(
+ 'name' => 'input',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => $attr
+ )
+ );
+
+ /* Act as if a stream of character tokens had been seen
+ (see below for what they should say). */
+ $this->insertText(
+ 'This is a searchable index. ' .
+ 'Insert your search keywords here: '
+ );
+
+ /* Act as if an end tag token with the tag name "label"
+ had been seen. */
+ $this->inBody(
+ array(
+ 'name' => 'label',
+ 'type' => HTML5::ENDTAG
+ )
+ );
+
+ /* Act as if an end tag token with the tag name "p" had
+ been seen. */
+ $this->inBody(
+ array(
+ 'name' => 'p',
+ 'type' => HTML5::ENDTAG
+ )
+ );
+
+ /* Act as if a start tag token with the tag name "hr" had
+ been seen. */
+ $this->inBody(
+ array(
+ 'name' => 'hr',
+ 'type' => HTML5::ENDTAG
+ )
+ );
+
+ /* Act as if an end tag token with the tag name "form" had
+ been seen. */
+ $this->inBody(
+ array(
+ 'name' => 'form',
+ 'type' => HTML5::ENDTAG
+ )
+ );
+ }
+ break;
+
+ /* A start tag whose tag name is "textarea" */
+ case 'textarea':
+ $this->insertElement($token);
+
+ /* Switch the tokeniser's content model flag to the
+ RCDATA state. */
+ return HTML5::RCDATA;
+ break;
+
+ /* A start tag whose tag name is one of: "iframe", "noembed",
+ "noframes" */
+ case 'iframe':
+ case 'noembed':
+ case 'noframes':
+ $this->insertElement($token);
+
+ /* Switch the tokeniser's content model flag to the CDATA state. */
+ return HTML5::CDATA;
+ 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);
+
+ /* Change the insertion mode to "in select". */
+ $this->mode = self::IN_SELECT;
+ break;
+
+ /* A start or end tag whose tag name is one of: "caption", "col",
+ "colgroup", "frame", "frameset", "head", "option", "optgroup",
+ "tbody", "td", "tfoot", "th", "thead", "tr". */
+ case 'caption':
+ case 'col':
+ case 'colgroup':
+ case 'frame':
+ case 'frameset':
+ case 'head':
+ case 'option':
+ case 'optgroup':
+ case 'tbody':
+ case 'td':
+ case 'tfoot':
+ case 'th':
+ case 'thead':
+ case 'tr':
+ // Parse error. Ignore the token.
+ break;
+
+ /* A start or end tag whose tag name is one of: "event-source",
+ "section", "nav", "article", "aside", "header", "footer",
+ "datagrid", "command" */
+ case 'event-source':
+ case 'section':
+ case 'nav':
+ case 'article':
+ case 'aside':
+ case 'header':
+ case 'footer':
+ case 'datagrid':
+ case 'command':
+ // Work in progress!
+ break;
+
+ /* A start tag token not covered by the previous entries */
+ default:
+ /* Reconstruct the active formatting elements, if any. */
+ $this->reconstructActiveFormattingElements();
+
+ $this->insertElement($token, true, true);
+ break;
+ }
break;
- /* A start tag whose tag name is one of: "b", "big", "em", "font",
- "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
- case 'b': case 'big': case 'em': case 'font': case 'i':
- case 'nobr': 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;
-
- /* 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.) */
- if($this->elementInScope('button')) {
- $this->inBody(array(
- 'name' => 'button',
- 'type' => HTML5::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;
- break;
-
- /* A start tag token whose tag name is one of: "marquee", "object" */
- 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;
- break;
-
- /* A start tag token whose tag name is "xmp" */
- case 'xmp':
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* Switch the content model flag to the CDATA state. */
- return HTML5::CDATA;
- break;
-
- /* 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->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5::ENDTAG
- ));
- }
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* 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 'param': 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);
- 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::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);
- 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';
- return $this->inBody($token);
- break;
-
- /* A start tag whose tag name is "input" */
- case 'input':
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* Insert an input element for the token. */
- $element = $this->insertElement($token, false);
-
- /* If the form element pointer is not null, then associate the
- input element with the form element pointed to by the form
- element pointer. */
- $this->form_pointer !== null
- ? $this->form_pointer->appendChild($element)
- : end($this->stack)->appendChild($element);
-
- /* Pop that input element off the stack of open elements. */
- array_pop($this->stack);
- break;
-
- /* A start tag whose tag name is "isindex" */
- case 'isindex':
- /* Parse error. */
- // w/e
-
- /* 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. */
- $this->inBody(array(
- 'name' => 'body',
- 'type' => HTML5::STARTTAG,
- 'attr' => array()
- ));
-
- /* Act as if a start tag token with the tag name "hr" had
- been seen. */
- $this->inBody(array(
- 'name' => 'hr',
- 'type' => HTML5::STARTTAG,
- 'attr' => array()
- ));
-
- /* Act as if a start tag token with the tag name "p" had
- been seen. */
- $this->inBody(array(
- 'name' => 'p',
- 'type' => HTML5::STARTTAG,
- 'attr' => array()
- ));
-
- /* Act as if a start tag token with the tag name "label"
- had been seen. */
- $this->inBody(array(
- 'name' => 'label',
- 'type' => HTML5::STARTTAG,
- 'attr' => array()
- ));
-
- /* Act as if a stream of character tokens had been seen. */
- $this->insertText('This is a searchable index. '.
- 'Insert your search keywords here: ');
-
- /* 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 = $token['attr'];
- $attr[] = array('name' => 'name', 'value' => 'isindex');
-
- $this->inBody(array(
- 'name' => 'input',
- 'type' => HTML5::STARTTAG,
- 'attr' => $attr
- ));
-
- /* Act as if a stream of character tokens had been seen
- (see below for what they should say). */
- $this->insertText('This is a searchable index. '.
- 'Insert your search keywords here: ');
-
- /* Act as if an end tag token with the tag name "label"
- had been seen. */
- $this->inBody(array(
- 'name' => 'label',
- 'type' => HTML5::ENDTAG
- ));
-
- /* Act as if an end tag token with the tag name "p" had
- been seen. */
- $this->inBody(array(
- 'name' => 'p',
- 'type' => HTML5::ENDTAG
- ));
-
- /* Act as if a start tag token with the tag name "hr" had
- been seen. */
- $this->inBody(array(
- 'name' => 'hr',
- 'type' => HTML5::ENDTAG
- ));
-
- /* Act as if an end tag token with the tag name "form" had
- been seen. */
- $this->inBody(array(
- 'name' => 'form',
- 'type' => HTML5::ENDTAG
- ));
- }
- break;
-
- /* A start tag whose tag name is "textarea" */
- case 'textarea':
- $this->insertElement($token);
-
- /* Switch the tokeniser's content model flag to the
- RCDATA state. */
- return HTML5::RCDATA;
- break;
-
- /* A start tag whose tag name is one of: "iframe", "noembed",
- "noframes" */
- case 'iframe': case 'noembed': case 'noframes':
- $this->insertElement($token);
-
- /* Switch the tokeniser's content model flag to the CDATA state. */
- return HTML5::CDATA;
- 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);
-
- /* Change the insertion mode to "in select". */
- $this->mode = self::IN_SELECT;
- break;
-
- /* A start or end tag whose tag name is one of: "caption", "col",
- "colgroup", "frame", "frameset", "head", "option", "optgroup",
- "tbody", "td", "tfoot", "th", "thead", "tr". */
- case 'caption': case 'col': case 'colgroup': case 'frame':
- case 'frameset': case 'head': case 'option': case 'optgroup':
- case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead':
- case 'tr':
- // Parse error. Ignore the token.
- break;
-
- /* A start or end tag whose tag name is one of: "event-source",
- "section", "nav", "article", "aside", "header", "footer",
- "datagrid", "command" */
- case 'event-source': case 'section': case 'nav': case 'article':
- case 'aside': case 'header': case 'footer': case 'datagrid':
- case 'command':
- // Work in progress!
- break;
-
- /* A start tag token not covered by the previous entries */
- default:
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- $this->insertElement($token, true, true);
- break;
- }
- break;
-
case HTML5::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]->nodeName !== 'body') {
- // Ignore.
+ 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]->nodeName !== 'body') {
+ // Ignore.
- /* If the current node is not the body element, then this
- is a parse error. */
- } elseif(end($this->stack)->nodeName !== 'body') {
- // 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->inBody(array(
- 'name' => 'body',
- 'type' => HTML5::ENDTAG
- ));
-
- return $this->afterBody($token);
- break;
-
- /* An end tag whose tag name is one of: "address", "blockquote",
- "center", "dir", "div", "dl", "fieldset", "listing", "menu",
- "ol", "pre", "ul" */
- case 'address': case 'blockquote': case 'center': case 'dir':
- case 'div': case 'dl': case 'fieldset': case 'listing':
- case 'menu': case 'ol': case 'pre': 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. */
- // w/e
-
- /* 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. */
- for($n = count($this->stack) - 1; $n >= 0; $n--) {
- if($this->stack[$n]->nodeName === $token['name']) {
- $n = -1;
- }
-
- array_pop($this->stack);
+ /* If the current node is not the body element, then this
+ is a parse error. */
+ } elseif (end($this->stack)->nodeName !== 'body') {
+ // Parse error.
}
- }
- break;
- /* An end tag whose tag name is "form" */
- case 'form':
- /* 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();
+ /* 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->inBody(
+ array(
+ 'name' => 'body',
+ 'type' => HTML5::ENDTAG
+ )
+ );
- if(end($this->stack)->nodeName !== $token['name']) {
- /* 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. */
- // w/e
-
- } else {
- /* Otherwise, if the current node is an element with
- the same tag name as that of the token pop that element
- from the stack. */
- array_pop($this->stack);
- }
-
- /* In any case, set the form element pointer to null. */
- $this->form_pointer = null;
- 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')) {
- $this->generateImpliedEndTags(array('p'));
-
- /* If the current node is not a p element, then this is
- a parse error. */
- // k
-
- /* If the stack of open elements has a p element in
- scope, then pop elements from this stack until the stack
- no longer has a p element in scope. */
- for($n = count($this->stack) - 1; $n >= 0; $n--) {
- if($this->elementInScope('p')) {
- array_pop($this->stack);
-
- } else {
- break;
- }
- }
- }
- break;
-
- /* An end tag whose tag name is "dd", "dt", or "li" */
- case 'dd': case 'dt': case 'li':
- /* If the stack of open elements has an element in scope
- whose tag name matches the tag name of the token, then
- generate implied end tags, except for elements with the
- same tag name as the token. */
- 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. */
- // w/e
+ return $this->afterBody($token);
+ break;
+ /* An end tag whose tag name is one of: "address", "blockquote",
+ "center", "dir", "div", "dl", "fieldset", "listing", "menu",
+ "ol", "pre", "ul" */
+ case 'address':
+ case 'blockquote':
+ case 'center':
+ case 'dir':
+ case 'div':
+ case 'dl':
+ case 'fieldset':
+ case 'listing':
+ case 'menu':
+ case 'ol':
+ case 'pre':
+ case 'ul':
/* If the stack of open elements has an element in scope
- whose tag name matches the tag name of the token, then
- pop elements from this stack until an element with that
- tag name has been popped from the stack. */
- for($n = count($this->stack) - 1; $n >= 0; $n--) {
- if($this->stack[$n]->nodeName === $token['name']) {
- $n = -1;
- }
+ with the same tag name as that of the token, then generate
+ implied end tags. */
+ if ($this->elementInScope($token['name'])) {
+ $this->generateImpliedEndTags();
- array_pop($this->stack);
- }
- }
- break;
+ /* 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. */
+ // w/e
- /* 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 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. */
+ for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+ if ($this->stack[$n]->nodeName === $token['name']) {
+ $n = -1;
+ }
- /* 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. */
- // w/e
-
- /* 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. */
- while($this->elementInScope($elements)) {
- array_pop($this->stack);
- }
- }
- 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 'em': case 'font':
- case 'i': case 'nobr': case 's': case 'small': case 'strike':
- case 'strong': case 'tt': case 'u':
- /* 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']))) {
- 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;
- }
-
- /* 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]->nodeName);
-
- if($category !== self::PHRASING && $category !== self::FORMATTING) {
- $furthest_block = $this->stack[$s];
- }
- }
-
- /* 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);
}
+ }
+ break;
+
+ /* An end tag whose tag name is "form" */
+ case 'form':
+ /* 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();
- 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];
+ if (end($this->stack)->nodeName !== $token['name']) {
+ /* 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. */
+ // w/e
- /* 5. If the furthest block has a parent node, then
- remove the furthest block from its parent node. */
- if($furthest_block->parentNode !== null) {
- $furthest_block->parentNode->removeChild($furthest_block);
+ } else {
+ /* Otherwise, if the current node is an element with
+ the same tag name as that of the token pop that element
+ from the stack. */
+ array_pop($this->stack);
}
- /* 6. 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;
+ /* In any case, set the form element pointer to null. */
+ $this->form_pointer = null;
+ break;
- /* 7. Let node and last node be the furthest block.
- Follow these steps: */
- $node = $furthest_block;
- $last_node = $furthest_block;
+ /* 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')) {
+ $this->generateImpliedEndTags(array('p'));
- while(true) {
- for($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) {
- /* 7.1 Let node be the element immediately
- prior to node in the stack of open elements. */
- $node = $this->stack[$n];
+ /* If the current node is not a p element, then this is
+ a parse error. */
+ // k
- /* 7.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)) {
- unset($this->stack[$n]);
- $this->stack = array_merge($this->stack);
+ /* If the stack of open elements has a p element in
+ scope, then pop elements from this stack until the stack
+ no longer has a p element in scope. */
+ for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+ if ($this->elementInScope('p')) {
+ array_pop($this->stack);
} else {
break;
}
}
+ }
+ break;
- /* 7.3 Otherwise, if node is the formatting
- element, then go to the next step in the overall
- algorithm. */
- if($node === $formatting_element) {
+ /* An end tag whose tag name is "dd", "dt", or "li" */
+ case 'dd':
+ case 'dt':
+ case 'li':
+ /* If the stack of open elements has an element in scope
+ whose tag name matches the tag name of the token, then
+ generate implied end tags, except for elements with the
+ same tag name as the token. */
+ 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. */
+ // w/e
+
+ /* If the stack of open elements has an element in scope
+ whose tag name matches the tag name of the token, then
+ pop elements from this stack until an element with that
+ tag name has been popped from the stack. */
+ for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+ if ($this->stack[$n]->nodeName === $token['name']) {
+ $n = -1;
+ }
+
+ array_pop($this->stack);
+ }
+ }
+ 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. */
+ // w/e
+
+ /* 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. */
+ while ($this->elementInScope($elements)) {
+ array_pop($this->stack);
+ }
+ }
+ 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 'em':
+ case 'font':
+ case 'i':
+ case 'nobr':
+ case 's':
+ case 'small':
+ case 'strike':
+ case 'strong':
+ case 'tt':
+ case 'u':
+ /* 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']))
+ ) {
break;
- /* 7.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;
+ /* 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;
}
- /* 7.5 If node has any children, perform a
- shallow clone of node, replace the entry for
- node in the list of active formatting elements
- with an entry for the clone, replace the entry
- for node in the stack of open elements with an
- entry for the clone, and let node be the clone. */
- if($node->hasChildNodes()) {
- $clone = $node->cloneNode();
- $s_pos = array_search($node, $this->stack, true);
- $a_pos = array_search($node, $this->a_formatting, true);
+ /* 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);
- $this->stack[$s_pos] = $clone;
- $this->a_formatting[$a_pos] = $clone;
- $node = $clone;
+ for ($s = $fe_s_pos + 1; $s < $length; $s++) {
+ $category = $this->getElementCategory($this->stack[$s]->nodeName);
+
+ if ($category !== self::PHRASING && $category !== self::FORMATTING) {
+ $furthest_block = $this->stack[$s];
+ }
}
- /* 7.6 Insert last node into node, first removing
- it from its previous parent node if any. */
- if($last_node->parentNode !== null) {
+ /* 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. If the furthest block has a parent node, then
+ remove the furthest block from its parent node. */
+ if ($furthest_block->parentNode !== null) {
+ $furthest_block->parentNode->removeChild($furthest_block);
+ }
+
+ /* 6. 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;
+
+ /* 7. 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--) {
+ /* 7.1 Let node be the element immediately
+ prior to node in the stack of open elements. */
+ $node = $this->stack[$n];
+
+ /* 7.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)) {
+ unset($this->stack[$n]);
+ $this->stack = array_merge($this->stack);
+
+ } else {
+ break;
+ }
+ }
+
+ /* 7.3 Otherwise, if node is the formatting
+ element, then go to the next step in the overall
+ algorithm. */
+ if ($node === $formatting_element) {
+ break;
+
+ /* 7.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;
+ }
+
+ /* 7.5 If node has any children, perform a
+ shallow clone of node, replace the entry for
+ node in the list of active formatting elements
+ with an entry for the clone, replace the entry
+ for node in the stack of open elements with an
+ entry for the clone, and let node be the clone. */
+ if ($node->hasChildNodes()) {
+ $clone = $node->cloneNode();
+ $s_pos = array_search($node, $this->stack, true);
+ $a_pos = array_search($node, $this->a_formatting, true);
+
+ $this->stack[$s_pos] = $clone;
+ $this->a_formatting[$a_pos] = $clone;
+ $node = $clone;
+ }
+
+ /* 7.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);
+
+ /* 7.7 Let last node be node. */
+ $last_node = $node;
+ }
+
+ /* 8. Insert whatever last node ended up being in
+ the previous step into the common ancestor 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);
+ $common_ancestor->appendChild($last_node);
- /* 7.7 Let last node be node. */
- $last_node = $node;
- }
+ /* 9. Perform a shallow clone of the formatting
+ element. */
+ $clone = $formatting_element->cloneNode();
- /* 8. Insert whatever last node ended up being in
- the previous step into the common ancestor node,
- first removing it from its previous parent node if
- any. */
- if($last_node->parentNode !== null) {
- $last_node->parentNode->removeChild($last_node);
- }
-
- $common_ancestor->appendChild($last_node);
-
- /* 9. Perform a shallow clone of the formatting
- element. */
- $clone = $formatting_element->cloneNode();
-
- /* 10. Take all of the child nodes of the furthest
- block and append them to the clone created in the
- last step. */
- while($furthest_block->hasChildNodes()) {
- $child = $furthest_block->firstChild;
- $furthest_block->removeChild($child);
- $clone->appendChild($child);
- }
-
- /* 11. Append that clone to the furthest block. */
- $furthest_block->appendChild($clone);
-
- /* 12. Remove the formatting element from the list
- of active formatting elements, and insert the clone
- 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);
- unset($this->a_formatting[$fe_af_pos]);
- $this->a_formatting = array_merge($this->a_formatting);
-
- $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
- $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting));
- $this->a_formatting = array_merge($af_part1, array($clone), $af_part2);
-
- /* 13. Remove the formatting element from the stack
- of open elements, and insert the clone into the stack
- of open elements immediately after (i.e. in a more
- deeply nested position than) the position of the
- furthest block in that stack. */
- $fe_s_pos = array_search($formatting_element, $this->stack, true);
- $fb_s_pos = array_search($furthest_block, $this->stack, true);
- unset($this->stack[$fe_s_pos]);
-
- $s_part1 = array_slice($this->stack, 0, $fb_s_pos);
- $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack));
- $this->stack = array_merge($s_part1, array($clone), $s_part2);
-
- /* 14. Jump back to step 1 in this series of steps. */
- unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
- }
- break;
-
- /* An end tag token whose tag name is one of: "button",
- "marquee", "object" */
- 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. */
- // k
-
- /* Now, if the stack of open elements has an element in scope
- whose tag name matches the tag name of the token, then pop
- elements from the stack until that element has been popped from
- the stack, and clear the list of active formatting elements up
- to the last marker. */
- for($n = count($this->stack) - 1; $n >= 0; $n--) {
- if($this->stack[$n]->nodeName === $token['name']) {
- $n = -1;
+ /* 10. Take all of the child nodes of the furthest
+ block and append them to the clone created in the
+ last step. */
+ while ($furthest_block->hasChildNodes()) {
+ $child = $furthest_block->firstChild;
+ $furthest_block->removeChild($child);
+ $clone->appendChild($child);
}
- array_pop($this->stack);
+ /* 11. Append that clone to the furthest block. */
+ $furthest_block->appendChild($clone);
+
+ /* 12. Remove the formatting element from the list
+ of active formatting elements, and insert the clone
+ 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);
+ unset($this->a_formatting[$fe_af_pos]);
+ $this->a_formatting = array_merge($this->a_formatting);
+
+ $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
+ $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting));
+ $this->a_formatting = array_merge($af_part1, array($clone), $af_part2);
+
+ /* 13. Remove the formatting element from the stack
+ of open elements, and insert the clone into the stack
+ of open elements immediately after (i.e. in a more
+ deeply nested position than) the position of the
+ furthest block in that stack. */
+ $fe_s_pos = array_search($formatting_element, $this->stack, true);
+ $fb_s_pos = array_search($furthest_block, $this->stack, true);
+ unset($this->stack[$fe_s_pos]);
+
+ $s_part1 = array_slice($this->stack, 0, $fb_s_pos);
+ $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack));
+ $this->stack = array_merge($s_part1, array($clone), $s_part2);
+
+ /* 14. Jump back to step 1 in this series of steps. */
+ unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
}
+ break;
- $marker = end(array_keys($this->a_formatting, self::MARKER, true));
-
- for($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
- array_pop($this->a_formatting);
- }
- }
- break;
-
- /* Or an end tag whose tag name is one of: "area", "basefont",
- "bgsound", "br", "embed", "hr", "iframe", "image", "img",
- "input", "isindex", "noembed", "noframes", "param", "select",
- "spacer", "table", "textarea", "wbr" */
- case 'area': case 'basefont': case 'bgsound': case 'br':
- case 'embed': case 'hr': case 'iframe': case 'image':
- case 'img': case 'input': case 'isindex': case 'noembed':
- case 'noframes': case 'param': case 'select': case 'spacer':
- case 'table': case 'textarea': case 'wbr':
- // Parse error. Ignore the token.
- 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 = end($this->stack);
-
- /* If node has the same tag name as the end tag token,
- then: */
- if($token['name'] === $node->nodeName) {
- /* Generate implied end tags. */
+ /* An end tag token whose tag name is one of: "button",
+ "marquee", "object" */
+ 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();
- /* If the tag name of the end tag token does not
- match the tag name of the current node, this is a
- parse error. */
+ /* Now, if the current node is not an element with the same
+ tag name as the token, then this is a parse error. */
// k
- /* Pop all the nodes from the current node up to
- node, including node, then stop this algorithm. */
- for($x = count($this->stack) - $n; $x >= $n; $x--) {
+ /* Now, if the stack of open elements has an element in scope
+ whose tag name matches the tag name of the token, then pop
+ elements from the stack until that element has been popped from
+ the stack, and clear the list of active formatting elements up
+ to the last marker. */
+ for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+ if ($this->stack[$n]->nodeName === $token['name']) {
+ $n = -1;
+ }
+
array_pop($this->stack);
}
-
- } else {
- $category = $this->getElementCategory($node);
- if($category !== self::SPECIAL && $category !== self::SCOPING) {
- /* 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. */
- return false;
+ $marker = end(array_keys($this->a_formatting, self::MARKER, true));
+
+ for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
+ array_pop($this->a_formatting);
}
}
- }
+ break;
+
+ /* Or an end tag whose tag name is one of: "area", "basefont",
+ "bgsound", "br", "embed", "hr", "iframe", "image", "img",
+ "input", "isindex", "noembed", "noframes", "param", "select",
+ "spacer", "table", "textarea", "wbr" */
+ case 'area':
+ case 'basefont':
+ case 'bgsound':
+ case 'br':
+ case 'embed':
+ case 'hr':
+ case 'iframe':
+ case 'image':
+ case 'img':
+ case 'input':
+ case 'isindex':
+ case 'noembed':
+ case 'noframes':
+ case 'param':
+ case 'select':
+ case 'spacer':
+ case 'table':
+ case 'textarea':
+ case 'wbr':
+ // Parse error. Ignore the token.
+ 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 = end($this->stack);
+
+ /* If node has the same tag name as the end tag token,
+ then: */
+ if ($token['name'] === $node->nodeName) {
+ /* 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. */
+ // k
+
+ /* Pop all the nodes from the current node up to
+ node, including node, then stop this algorithm. */
+ for ($x = count($this->stack) - $n; $x >= $n; $x--) {
+ array_pop($this->stack);
+ }
+
+ } else {
+ $category = $this->getElementCategory($node);
+
+ if ($category !== self::SPECIAL && $category !== self::SCOPING) {
+ /* 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. */
+ return false;
+ }
+ }
+ }
+ break;
+ }
break;
- }
- break;
}
}
- private function inTable($token) {
+ private function inTable($token)
+ {
$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::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+ if ($token['type'] === HTML5::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+ ) {
/* Append the character to the current node. */
$text = $this->dom->createTextNode($token['data']);
end($this->stack)->appendChild($text);
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::COMMENT) {
/* Append a Comment node to the current node with the data
attribute set to the data given in the comment token. */
$comment = $this->dom->createComment($token['data']);
end($this->stack)->appendChild($comment);
- /* A start tag whose tag name is "caption" */
- } elseif($token['type'] === HTML5::STARTTAG &&
- $token['name'] === 'caption') {
+ /* A start tag whose tag name is "caption" */
+ } elseif ($token['type'] === HTML5::STARTTAG &&
+ $token['name'] === 'caption'
+ ) {
/* Clear the stack back to a table context. */
$this->clearStackToTableContext($clear);
@@ -2661,9 +3349,10 @@ class HTML5TreeConstructer {
$this->insertElement($token);
$this->mode = self::IN_CAPTION;
- /* A start tag whose tag name is "colgroup" */
- } elseif($token['type'] === HTML5::STARTTAG &&
- $token['name'] === 'colgroup') {
+ /* A start tag whose tag name is "colgroup" */
+ } elseif ($token['type'] === HTML5::STARTTAG &&
+ $token['name'] === 'colgroup'
+ ) {
/* Clear the stack back to a table context. */
$this->clearStackToTableContext($clear);
@@ -2672,20 +3361,26 @@ class HTML5TreeConstructer {
$this->insertElement($token);
$this->mode = self::IN_CGROUP;
- /* A start tag whose tag name is "col" */
- } elseif($token['type'] === HTML5::STARTTAG &&
- $token['name'] === 'col') {
- $this->inTable(array(
- 'name' => 'colgroup',
- 'type' => HTML5::STARTTAG,
- 'attr' => array()
- ));
+ /* A start tag whose tag name is "col" */
+ } elseif ($token['type'] === HTML5::STARTTAG &&
+ $token['name'] === 'col'
+ ) {
+ $this->inTable(
+ array(
+ 'name' => 'colgroup',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => array()
+ )
+ );
$this->inColumnGroup($token);
- /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
- } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
- array('tbody', 'tfoot', 'thead'))) {
+ /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
+ } elseif ($token['type'] === HTML5::STARTTAG && in_array(
+ $token['name'],
+ array('tbody', 'tfoot', 'thead')
+ )
+ ) {
/* Clear the stack back to a table context. */
$this->clearStackToTableContext($clear);
@@ -2694,42 +3389,49 @@ class HTML5TreeConstructer {
$this->insertElement($token);
$this->mode = self::IN_TBODY;
- /* A start tag whose tag name is one of: "td", "th", "tr" */
- } elseif($token['type'] === HTML5::STARTTAG &&
- in_array($token['name'], array('td', 'th', 'tr'))) {
+ /* A start tag whose tag name is one of: "td", "th", "tr" */
+ } elseif ($token['type'] === HTML5::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->inTable(array(
- 'name' => 'tbody',
- 'type' => HTML5::STARTTAG,
- 'attr' => array()
- ));
+ $this->inTable(
+ array(
+ 'name' => 'tbody',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => array()
+ )
+ );
return $this->inTableBody($token);
- /* A start tag whose tag name is "table" */
- } elseif($token['type'] === HTML5::STARTTAG &&
- $token['name'] === 'table') {
+ /* A start tag whose tag name is "table" */
+ } elseif ($token['type'] === HTML5::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->inTable(array(
- 'name' => 'table',
- 'type' => HTML5::ENDTAG
- ));
+ $this->inTable(
+ array(
+ 'name' => 'table',
+ 'type' => HTML5::ENDTAG
+ )
+ );
return $this->mainPhase($token);
- /* An end tag whose tag name is "table" */
- } elseif($token['type'] === HTML5::ENDTAG &&
- $token['name'] === 'table') {
+ /* An end tag whose tag name is "table" */
+ } elseif ($token['type'] === HTML5::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. (innerHTML case) */
- if(!$this->elementInScope($token['name'], true)) {
+ if (!$this->elementInScope($token['name'], true)) {
return false;
- /* Otherwise: */
+ /* Otherwise: */
} else {
/* Generate implied end tags. */
$this->generateImpliedEndTags();
@@ -2740,11 +3442,11 @@ class HTML5TreeConstructer {
/* Pop elements from this stack until a table element has been
popped from the stack. */
- while(true) {
+ while (true) {
$current = end($this->stack)->nodeName;
array_pop($this->stack);
- if($current === 'table') {
+ if ($current === 'table') {
break;
}
}
@@ -2753,14 +3455,28 @@ class HTML5TreeConstructer {
$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::ENDTAG && in_array($token['name'],
- array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td',
- 'tfoot', 'th', 'thead', 'tr'))) {
+ /* An end tag whose tag name is one of: "body", "caption", "col",
+ "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
+ } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+ $token['name'],
+ array(
+ 'body',
+ 'caption',
+ 'col',
+ 'colgroup',
+ 'html',
+ 'tbody',
+ 'td',
+ 'tfoot',
+ 'th',
+ 'thead',
+ 'tr'
+ )
+ )
+ ) {
// Parse error. Ignore the token.
- /* Anything else */
+ /* Anything else */
} else {
/* Parse error. Process the token as if the insertion mode was "in
body", with the following exception: */
@@ -2768,8 +3484,11 @@ class HTML5TreeConstructer {
/* 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(in_array(end($this->stack)->nodeName,
- array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
+ if (in_array(
+ end($this->stack)->nodeName,
+ array('table', 'tbody', 'tfoot', 'thead', 'tr')
+ )
+ ) {
/* 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
@@ -2781,21 +3500,22 @@ class HTML5TreeConstructer {
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]->nodeName === 'table') {
+ for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+ if ($this->stack[$n]->nodeName === 'table') {
$table = $this->stack[$n];
break;
}
}
- if(isset($table) && $table->parentNode !== null) {
+ if (isset($table) && $table->parentNode !== null) {
$this->foster_parent = $table->parentNode;
- } elseif(!isset($table)) {
+ } elseif (!isset($table)) {
$this->foster_parent = $this->stack[0];
- } elseif(isset($table) && ($table->parentNode === null ||
- $table->parentNode->nodeType !== XML_ELEMENT_NODE)) {
+ } elseif (isset($table) && ($table->parentNode === null ||
+ $table->parentNode->nodeType !== XML_ELEMENT_NODE)
+ ) {
$this->foster_parent = $this->stack[$n - 1];
}
}
@@ -2804,16 +3524,17 @@ class HTML5TreeConstructer {
}
}
- private function inCaption($token) {
+ private function inCaption($token)
+ {
/* An end tag whose tag name is "caption" */
- if($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') {
+ if ($token['type'] === HTML5::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. (innerHTML case) */
- if(!$this->elementInScope($token['name'], true)) {
+ if (!$this->elementInScope($token['name'], true)) {
// Ignore
- /* Otherwise: */
+ /* Otherwise: */
} else {
/* Generate implied end tags. */
$this->generateImpliedEndTags();
@@ -2824,11 +3545,11 @@ class HTML5TreeConstructer {
/* Pop elements from this stack until a caption element has
been popped from the stack. */
- while(true) {
+ while (true) {
$node = end($this->stack)->nodeName;
array_pop($this->stack);
- if($node === 'caption') {
+ if ($node === 'caption') {
break;
}
}
@@ -2841,99 +3562,131 @@ class HTML5TreeConstructer {
$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::STARTTAG && in_array($token['name'],
- array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
- 'thead', 'tr'))) || ($token['type'] === HTML5::ENDTAG &&
- $token['name'] === '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::STARTTAG && in_array(
+ $token['name'],
+ array(
+ 'caption',
+ 'col',
+ 'colgroup',
+ 'tbody',
+ 'td',
+ 'tfoot',
+ 'th',
+ 'thead',
+ 'tr'
+ )
+ )) || ($token['type'] === HTML5::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->inCaption(array(
- 'name' => 'caption',
- 'type' => HTML5::ENDTAG
- ));
+ $this->inCaption(
+ array(
+ 'name' => 'caption',
+ 'type' => HTML5::ENDTAG
+ )
+ );
return $this->inTable($token);
- /* An end tag whose tag name is one of: "body", "col", "colgroup",
- "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
- } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
- array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th',
- 'thead', 'tr'))) {
+ /* An end tag whose tag name is one of: "body", "col", "colgroup",
+ "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
+ } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+ $token['name'],
+ array(
+ 'body',
+ 'col',
+ 'colgroup',
+ 'html',
+ 'tbody',
+ 'tfoot',
+ 'th',
+ 'thead',
+ 'tr'
+ )
+ )
+ ) {
// Parse error. Ignore the token.
- /* Anything else */
+ /* Anything else */
} else {
/* Process the token as if the insertion mode was "in body". */
$this->inBody($token);
}
}
- private function inColumnGroup($token) {
+ private function inColumnGroup($token)
+ {
/* 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::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+ if ($token['type'] === HTML5::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+ ) {
/* Append the character to the current node. */
$text = $this->dom->createTextNode($token['data']);
end($this->stack)->appendChild($text);
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::COMMENT) {
/* Append a Comment node to the current node with the data
attribute set to the data given in the comment token. */
$comment = $this->dom->createComment($token['data']);
end($this->stack)->appendChild($comment);
- /* A start tag whose tag name is "col" */
- } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') {
+ /* A start tag whose tag name is "col" */
+ } elseif ($token['type'] === HTML5::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);
- /* An end tag whose tag name is "colgroup" */
- } elseif($token['type'] === HTML5::ENDTAG &&
- $token['name'] === 'colgroup') {
+ /* An end tag whose tag name is "colgroup" */
+ } elseif ($token['type'] === HTML5::ENDTAG &&
+ $token['name'] === 'colgroup'
+ ) {
/* If the current node is the root html element, then this is a
parse error, ignore the token. (innerHTML case) */
- if(end($this->stack)->nodeName === 'html') {
+ if (end($this->stack)->nodeName === 'html') {
// Ignore
- /* Otherwise, pop the current node (which will be a colgroup
- element) from the stack of open elements. Switch the insertion
- mode to "in table". */
+ /* 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::ENDTAG && $token['name'] === 'col') {
+ /* An end tag whose tag name is "col" */
+ } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') {
/* Parse error. Ignore the token. */
- /* Anything else */
+ /* 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->inColumnGroup(array(
- 'name' => 'colgroup',
- 'type' => HTML5::ENDTAG
- ));
+ $this->inColumnGroup(
+ array(
+ 'name' => 'colgroup',
+ 'type' => HTML5::ENDTAG
+ )
+ );
return $this->inTable($token);
}
}
- private function inTableBody($token) {
+ private function inTableBody($token)
+ {
$clear = array('tbody', 'tfoot', 'thead', 'html');
/* A start tag whose tag name is "tr" */
- if($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') {
+ if ($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') {
/* Clear the stack back to a table body context. */
$this->clearStackToTableContext($clear);
@@ -2942,29 +3695,33 @@ class HTML5TreeConstructer {
$this->insertElement($token);
$this->mode = self::IN_ROW;
- /* A start tag whose tag name is one of: "th", "td" */
- } elseif($token['type'] === HTML5::STARTTAG &&
- ($token['name'] === 'th' || $token['name'] === 'td')) {
+ /* A start tag whose tag name is one of: "th", "td" */
+ } elseif ($token['type'] === HTML5::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->inTableBody(array(
- 'name' => 'tr',
- 'type' => HTML5::STARTTAG,
- 'attr' => array()
- ));
+ $this->inTableBody(
+ array(
+ 'name' => 'tr',
+ 'type' => HTML5::STARTTAG,
+ 'attr' => array()
+ )
+ );
return $this->inRow($token);
- /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
- } elseif($token['type'] === HTML5::ENDTAG &&
- in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
+ /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
+ } elseif ($token['type'] === HTML5::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)) {
+ if (!$this->elementInScope($token['name'], true)) {
// Ignore
- /* Otherwise: */
+ /* Otherwise: */
} else {
/* Clear the stack back to a table body context. */
$this->clearStackToTableContext($clear);
@@ -2975,18 +3732,21 @@ class HTML5TreeConstructer {
$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::STARTTAG && in_array($token['name'],
- array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead'))) ||
- ($token['type'] === HTML5::STARTTAG && $token['name'] === '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::STARTTAG && in_array(
+ $token['name'],
+ array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead')
+ )) ||
+ ($token['type'] === HTML5::STARTTAG && $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. (innerHTML case) */
- if(!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) {
+ if (!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) {
// Ignore.
- /* Otherwise: */
+ /* Otherwise: */
} else {
/* Clear the stack back to a table body context. */
$this->clearStackToTableContext($clear);
@@ -2994,33 +3754,40 @@ class HTML5TreeConstructer {
/* 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->inTableBody(array(
- 'name' => end($this->stack)->nodeName,
- 'type' => HTML5::ENDTAG
- ));
+ $this->inTableBody(
+ array(
+ 'name' => end($this->stack)->nodeName,
+ 'type' => HTML5::ENDTAG
+ )
+ );
return $this->mainPhase($token);
}
- /* An end tag whose tag name is one of: "body", "caption", "col",
- "colgroup", "html", "td", "th", "tr" */
- } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
- array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) {
+ /* An end tag whose tag name is one of: "body", "caption", "col",
+ "colgroup", "html", "td", "th", "tr" */
+ } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+ $token['name'],
+ array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr')
+ )
+ ) {
/* Parse error. Ignore the token. */
- /* Anything else */
+ /* Anything else */
} else {
/* Process the token as if the insertion mode was "in table". */
$this->inTable($token);
}
}
- private function inRow($token) {
+ private function inRow($token)
+ {
$clear = array('tr', 'html');
/* A start tag whose tag name is one of: "th", "td" */
- if($token['type'] === HTML5::STARTTAG &&
- ($token['name'] === 'th' || $token['name'] === 'td')) {
+ if ($token['type'] === HTML5::STARTTAG &&
+ ($token['name'] === 'th' || $token['name'] === 'td')
+ ) {
/* Clear the stack back to a table row context. */
$this->clearStackToTableContext($clear);
@@ -3033,15 +3800,15 @@ class HTML5TreeConstructer {
elements. */
$this->a_formatting[] = self::MARKER;
- /* An end tag whose tag name is "tr" */
- } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') {
+ /* An end tag whose tag name is "tr" */
+ } elseif ($token['type'] === HTML5::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. (innerHTML case) */
- if(!$this->elementInScope($token['name'], true)) {
+ if (!$this->elementInScope($token['name'], true)) {
// Ignore.
- /* Otherwise: */
+ /* Otherwise: */
} else {
/* Clear the stack back to a table row context. */
$this->clearStackToTableContext($clear);
@@ -3053,64 +3820,77 @@ class HTML5TreeConstructer {
$this->mode = self::IN_TBODY;
}
- /* 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::STARTTAG && in_array($token['name'],
- array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) {
+ /* 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::STARTTAG && in_array(
+ $token['name'],
+ array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr')
+ )
+ ) {
/* 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->inRow(array(
- 'name' => 'tr',
- 'type' => HTML5::ENDTAG
- ));
+ $this->inRow(
+ array(
+ 'name' => 'tr',
+ 'type' => HTML5::ENDTAG
+ )
+ );
return $this->inCell($token);
- /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
- } elseif($token['type'] === HTML5::ENDTAG &&
- in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
+ /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
+ } elseif ($token['type'] === HTML5::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)) {
+ if (!$this->elementInScope($token['name'], true)) {
// Ignore.
- /* Otherwise: */
+ /* Otherwise: */
} else {
/* Otherwise, act as if an end tag with the tag name "tr" had
been seen, then reprocess the current token. */
- $this->inRow(array(
- 'name' => 'tr',
- 'type' => HTML5::ENDTAG
- ));
+ $this->inRow(
+ array(
+ 'name' => 'tr',
+ 'type' => HTML5::ENDTAG
+ )
+ );
return $this->inCell($token);
}
- /* An end tag whose tag name is one of: "body", "caption", "col",
- "colgroup", "html", "td", "th" */
- } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
- array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) {
+ /* An end tag whose tag name is one of: "body", "caption", "col",
+ "colgroup", "html", "td", "th" */
+ } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+ $token['name'],
+ array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr')
+ )
+ ) {
/* Parse error. Ignore the token. */
- /* Anything else */
+ /* Anything else */
} else {
/* Process the token as if the insertion mode was "in table". */
$this->inTable($token);
}
}
- private function inCell($token) {
+ private function inCell($token)
+ {
/* An end tag whose tag name is one of: "td", "th" */
- if($token['type'] === HTML5::ENDTAG &&
- ($token['name'] === 'td' || $token['name'] === 'th')) {
+ if ($token['type'] === HTML5::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)) {
+ if (!$this->elementInScope($token['name'], true)) {
// Ignore.
- /* Otherwise: */
+ /* Otherwise: */
} else {
/* Generate implied end tags, except for elements with the same
tag name as the token. */
@@ -3122,11 +3902,11 @@ class HTML5TreeConstructer {
/* Pop elements from this stack until an element with the same
tag name as the token has been popped from the stack. */
- while(true) {
+ while (true) {
$node = end($this->stack)->nodeName;
array_pop($this->stack);
- if($node === $token['name']) {
+ if ($node === $token['name']) {
break;
}
}
@@ -3140,178 +3920,223 @@ class HTML5TreeConstructer {
$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::STARTTAG && in_array($token['name'],
- array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
- 'thead', 'tr'))) {
+ /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+ "tbody", "td", "tfoot", "th", "thead", "tr" */
+ } elseif ($token['type'] === HTML5::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.
(innerHTML case) */
- if(!$this->elementInScope(array('td', 'th'), true)) {
+ if (!$this->elementInScope(array('td', 'th'), true)) {
// Ignore.
- /* Otherwise, close the cell (see below) and reprocess the current
- token. */
+ /* Otherwise, close the cell (see below) and reprocess the current
+ token. */
} else {
$this->closeCell();
return $this->inRow($token);
}
- /* A start tag whose tag name is one of: "caption", "col", "colgroup",
- "tbody", "td", "tfoot", "th", "thead", "tr" */
- } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
- array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
- 'thead', 'tr'))) {
+ /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+ "tbody", "td", "tfoot", "th", "thead", "tr" */
+ } elseif ($token['type'] === HTML5::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.
(innerHTML case) */
- if(!$this->elementInScope(array('td', 'th'), true)) {
+ if (!$this->elementInScope(array('td', 'th'), true)) {
// Ignore.
- /* Otherwise, close the cell (see below) and reprocess the current
- token. */
+ /* Otherwise, close the cell (see below) and reprocess the current
+ token. */
} else {
$this->closeCell();
return $this->inRow($token);
}
- /* An end tag whose tag name is one of: "body", "caption", "col",
- "colgroup", "html" */
- } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
- array('body', 'caption', 'col', 'colgroup', 'html'))) {
+ /* An end tag whose tag name is one of: "body", "caption", "col",
+ "colgroup", "html" */
+ } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+ $token['name'],
+ array('body', 'caption', 'col', 'colgroup', 'html')
+ )
+ ) {
/* Parse error. Ignore the token. */
- /* An end tag whose tag name is one of: "table", "tbody", "tfoot",
- "thead", "tr" */
- } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
- array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
+ /* An end tag whose tag name is one of: "table", "tbody", "tfoot",
+ "thead", "tr" */
+ } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+ $token['name'],
+ array('table', 'tbody', 'tfoot', 'thead', 'tr')
+ )
+ ) {
/* If the stack of open elements does not have an element in table
scope with the same tag name as that of the token (which can only
happen for "tbody", "tfoot" and "thead", or, in the innerHTML case),
then this is a parse error and the token must be ignored. */
- if(!$this->elementInScope($token['name'], true)) {
+ if (!$this->elementInScope($token['name'], true)) {
// Ignore.
- /* Otherwise, close the cell (see below) and reprocess the current
- token. */
+ /* Otherwise, close the cell (see below) and reprocess the current
+ token. */
} else {
$this->closeCell();
return $this->inRow($token);
}
- /* Anything else */
+ /* Anything else */
} else {
/* Process the token as if the insertion mode was "in body". */
$this->inBody($token);
}
}
- private function inSelect($token) {
+ private function inSelect($token)
+ {
/* Handle the token as follows: */
/* A character token */
- if($token['type'] === HTML5::CHARACTR) {
+ if ($token['type'] === HTML5::CHARACTR) {
/* Append the token's character to the current node. */
$this->insertText($token['data']);
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::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 start tag token whose tag name is "option" */
- } elseif($token['type'] === HTML5::STARTTAG &&
- $token['name'] === 'option') {
+ /* A start tag token whose tag name is "option" */
+ } elseif ($token['type'] === HTML5::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)->nodeName === 'option') {
- $this->inSelect(array(
- 'name' => 'option',
- 'type' => HTML5::ENDTAG
- ));
+ if (end($this->stack)->nodeName === 'option') {
+ $this->inSelect(
+ array(
+ 'name' => 'option',
+ 'type' => HTML5::ENDTAG
+ )
+ );
}
/* Insert an HTML element for the token. */
$this->insertElement($token);
- /* A start tag token whose tag name is "optgroup" */
- } elseif($token['type'] === HTML5::STARTTAG &&
- $token['name'] === 'optgroup') {
+ /* A start tag token whose tag name is "optgroup" */
+ } elseif ($token['type'] === HTML5::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)->nodeName === 'option') {
- $this->inSelect(array(
- 'name' => 'option',
- 'type' => HTML5::ENDTAG
- ));
+ if (end($this->stack)->nodeName === 'option') {
+ $this->inSelect(
+ array(
+ 'name' => 'option',
+ 'type' => HTML5::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)->nodeName === 'optgroup') {
- $this->inSelect(array(
- 'name' => 'optgroup',
- 'type' => HTML5::ENDTAG
- ));
+ if (end($this->stack)->nodeName === 'optgroup') {
+ $this->inSelect(
+ array(
+ 'name' => 'optgroup',
+ 'type' => HTML5::ENDTAG
+ )
+ );
}
/* Insert an HTML element for the token. */
$this->insertElement($token);
- /* An end tag token whose tag name is "optgroup" */
- } elseif($token['type'] === HTML5::ENDTAG &&
- $token['name'] === 'optgroup') {
+ /* An end tag token whose tag name is "optgroup" */
+ } elseif ($token['type'] === HTML5::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]->nodeName === 'option' &&
- $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup') {
- $this->inSelect(array(
- 'name' => 'option',
- 'type' => HTML5::ENDTAG
- ));
+ if ($this->stack[$elements_in_stack - 1]->nodeName === 'option' &&
+ $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup'
+ ) {
+ $this->inSelect(
+ array(
+ 'name' => 'option',
+ 'type' => HTML5::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($this->stack[$elements_in_stack - 1] === 'optgroup') {
+ if ($this->stack[$elements_in_stack - 1] === 'optgroup') {
array_pop($this->stack);
}
- /* An end tag token whose tag name is "option" */
- } elseif($token['type'] === HTML5::ENDTAG &&
- $token['name'] === 'option') {
+ /* An end tag token whose tag name is "option" */
+ } elseif ($token['type'] === HTML5::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)->nodeName === 'option') {
+ if (end($this->stack)->nodeName === 'option') {
array_pop($this->stack);
}
- /* An end tag whose tag name is "select" */
- } elseif($token['type'] === HTML5::ENDTAG &&
- $token['name'] === 'select') {
+ /* An end tag whose tag name is "select" */
+ } elseif ($token['type'] === HTML5::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. (innerHTML case) */
- if(!$this->elementInScope($token['name'], true)) {
+ if (!$this->elementInScope($token['name'], true)) {
// w/e
- /* Otherwise: */
+ /* Otherwise: */
} else {
/* Pop elements from the stack of open elements until a select
element has been popped from the stack. */
- while(true) {
+ while (true) {
$current = end($this->stack)->nodeName;
array_pop($this->stack);
- if($current === 'select') {
+ if ($current === 'select') {
break;
}
}
@@ -3320,20 +4145,35 @@ class HTML5TreeConstructer {
$this->resetInsertionMode();
}
- /* A start tag whose tag name is "select" */
- } elseif($token['name'] === 'select' &&
- $token['type'] === HTML5::STARTTAG) {
+ /* A start tag whose tag name is "select" */
+ } elseif ($token['name'] === 'select' &&
+ $token['type'] === HTML5::STARTTAG
+ ) {
/* Parse error. Act as if the token had been an end tag with the
tag name "select" instead. */
- $this->inSelect(array(
- 'name' => 'select',
- 'type' => HTML5::ENDTAG
- ));
+ $this->inSelect(
+ array(
+ 'name' => 'select',
+ 'type' => HTML5::ENDTAG
+ )
+ );
- /* An end tag whose tag name is one of: "caption", "table", "tbody",
- "tfoot", "thead", "tr", "td", "th" */
- } elseif(in_array($token['name'], array('caption', 'table', 'tbody',
- 'tfoot', 'thead', 'tr', 'td', 'th')) && $token['type'] === HTML5::ENDTAG) {
+ /* An end tag whose tag name is one of: "caption", "table", "tbody",
+ "tfoot", "thead", "tr", "td", "th" */
+ } elseif (in_array(
+ $token['name'],
+ array(
+ 'caption',
+ 'table',
+ 'tbody',
+ 'tfoot',
+ 'thead',
+ 'tr',
+ 'td',
+ 'th'
+ )
+ ) && $token['type'] === HTML5::ENDTAG
+ ) {
/* Parse error. */
// w/e
@@ -3341,43 +4181,47 @@ class HTML5TreeConstructer {
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->inSelect(array(
- 'name' => 'select',
- 'type' => HTML5::ENDTAG
- ));
+ if ($this->elementInScope($token['name'], true)) {
+ $this->inSelect(
+ array(
+ 'name' => 'select',
+ 'type' => HTML5::ENDTAG
+ )
+ );
$this->mainPhase($token);
}
- /* Anything else */
+ /* Anything else */
} else {
/* Parse error. Ignore the token. */
}
}
- private function afterBody($token) {
+ private function afterBody($token)
+ {
/* 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::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+ if ($token['type'] === HTML5::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+ ) {
/* Process the token as it would be processed if the insertion mode
was "in body". */
$this->inBody($token);
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::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);
- /* An end tag with the tag name "html" */
- } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') {
+ /* An end tag with the tag name "html" */
+ } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') {
/* If the parser was originally created in order to handle the
setting of an element's innerHTML attribute, this is a parse error;
ignore the token. (The element will be an html element in this
@@ -3386,7 +4230,7 @@ class HTML5TreeConstructer {
/* Otherwise, switch to the trailing end phase. */
$this->phase = self::END_PHASE;
- /* Anything else */
+ /* Anything else */
} else {
/* Parse error. Set the insertion mode to "in body" and reprocess
the token. */
@@ -3395,34 +4239,38 @@ class HTML5TreeConstructer {
}
}
- private function inFrameset($token) {
+ private function inFrameset($token)
+ {
/* 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::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+ if ($token['type'] === HTML5::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+ ) {
/* Append the character to the current node. */
$this->insertText($token['data']);
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::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 start tag with the tag name "frameset" */
- } elseif($token['name'] === 'frameset' &&
- $token['type'] === HTML5::STARTTAG) {
+ /* A start tag with the tag name "frameset" */
+ } elseif ($token['name'] === 'frameset' &&
+ $token['type'] === HTML5::STARTTAG
+ ) {
$this->insertElement($token);
- /* An end tag with the tag name "frameset" */
- } elseif($token['name'] === 'frameset' &&
- $token['type'] === HTML5::ENDTAG) {
+ /* An end tag with the tag name "frameset" */
+ } elseif ($token['name'] === 'frameset' &&
+ $token['type'] === HTML5::ENDTAG
+ ) {
/* If the current node is the root html element, then this is a
parse error; ignore the token. (innerHTML case) */
- if(end($this->stack)->nodeName === 'html') {
+ if (end($this->stack)->nodeName === 'html') {
// Ignore
} else {
@@ -3437,103 +4285,113 @@ class HTML5TreeConstructer {
$this->mode = self::AFTR_FRAME;
}
- /* A start tag with the tag name "frame" */
- } elseif($token['name'] === 'frame' &&
- $token['type'] === HTML5::STARTTAG) {
+ /* A start tag with the tag name "frame" */
+ } elseif ($token['name'] === 'frame' &&
+ $token['type'] === HTML5::STARTTAG
+ ) {
/* 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);
- /* A start tag with the tag name "noframes" */
- } elseif($token['name'] === 'noframes' &&
- $token['type'] === HTML5::STARTTAG) {
+ /* A start tag with the tag name "noframes" */
+ } elseif ($token['name'] === 'noframes' &&
+ $token['type'] === HTML5::STARTTAG
+ ) {
/* Process the token as if the insertion mode had been "in body". */
$this->inBody($token);
- /* Anything else */
+ /* Anything else */
} else {
/* Parse error. Ignore the token. */
}
}
- private function afterFrameset($token) {
+ private function afterFrameset($token)
+ {
/* 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::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+ if ($token['type'] === HTML5::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+ ) {
/* Append the character to the current node. */
$this->insertText($token['data']);
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::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']);
- /* An end tag with the tag name "html" */
- } elseif($token['name'] === 'html' &&
- $token['type'] === HTML5::ENDTAG) {
+ /* An end tag with the tag name "html" */
+ } elseif ($token['name'] === 'html' &&
+ $token['type'] === HTML5::ENDTAG
+ ) {
/* Switch to the trailing end phase. */
$this->phase = self::END_PHASE;
- /* A start tag with the tag name "noframes" */
- } elseif($token['name'] === 'noframes' &&
- $token['type'] === HTML5::STARTTAG) {
+ /* A start tag with the tag name "noframes" */
+ } elseif ($token['name'] === 'noframes' &&
+ $token['type'] === HTML5::STARTTAG
+ ) {
/* Process the token as if the insertion mode had been "in body". */
$this->inBody($token);
- /* Anything else */
+ /* Anything else */
} else {
/* Parse error. Ignore the token. */
}
}
- private function trailingEndPhase($token) {
+ private function trailingEndPhase($token)
+ {
/* After the main phase, as each token is emitted from the tokenisation
stage, it must be processed as described in this section. */
/* A DOCTYPE token */
- if($token['type'] === HTML5::DOCTYPE) {
+ if ($token['type'] === HTML5::DOCTYPE) {
// Parse error. Ignore the token.
- /* A comment token */
- } elseif($token['type'] === HTML5::COMMENT) {
+ /* A comment token */
+ } elseif ($token['type'] === HTML5::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::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
+ /* 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::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+ ) {
/* Process the token as it would be processed in the main phase. */
$this->mainPhase($token);
- /* A character token that is not one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- or U+0020 SPACE. Or a start tag token. Or an end tag token. */
- } elseif(($token['type'] === HTML5::CHARACTR &&
- preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
- $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG) {
+ /* A character token that is not one of U+0009 CHARACTER TABULATION,
+ U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+ or U+0020 SPACE. Or a start tag token. Or an end tag token. */
+ } elseif (($token['type'] === HTML5::CHARACTR &&
+ preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
+ $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG
+ ) {
/* Parse error. Switch back to the main phase and reprocess the
token. */
$this->phase = self::MAIN_PHASE;
return $this->mainPhase($token);
- /* An end-of-file token */
- } elseif($token['type'] === HTML5::EOF) {
+ /* An end-of-file token */
+ } elseif ($token['type'] === HTML5::EOF) {
/* OMG DONE!! */
}
}
- private function insertElement($token, $append = true, $check = false) {
+ private function insertElement($token, $append = true, $check = false)
+ {
// Proprietary workaround for libxml2's limitations with tag names
if ($check) {
// Slightly modified HTML5 tag-name modification,
@@ -3542,13 +4400,15 @@ class HTML5TreeConstructer {
// Remove leading hyphens and numbers
$token['name'] = ltrim($token['name'], '-0..9');
// In theory, this should ever be needed, but just in case
- if ($token['name'] === '') $token['name'] = 'span'; // arbitrary generic choice
+ if ($token['name'] === '') {
+ $token['name'] = 'span';
+ } // arbitrary generic choice
}
-
+
$el = $this->dom->createElement($token['name']);
- foreach($token['attr'] as $attr) {
- if(!$el->hasAttribute($attr['name'])) {
+ foreach ($token['attr'] as $attr) {
+ if (!$el->hasAttribute($attr['name'])) {
$el->setAttribute($attr['name'], $attr['value']);
}
}
@@ -3559,48 +4419,54 @@ class HTML5TreeConstructer {
return $el;
}
- private function insertText($data) {
+ private function insertText($data)
+ {
$text = $this->dom->createTextNode($data);
$this->appendToRealParent($text);
}
- private function insertComment($data) {
+ private function insertComment($data)
+ {
$comment = $this->dom->createComment($data);
$this->appendToRealParent($comment);
}
- private function appendToRealParent($node) {
- if($this->foster_parent === null) {
+ private function appendToRealParent($node)
+ {
+ if ($this->foster_parent === null) {
end($this->stack)->appendChild($node);
- } elseif($this->foster_parent !== null) {
+ } elseif ($this->foster_parent !== null) {
/* If the foster parent element is the parent element of the
last table element in the stack of open elements, then the new
node must be inserted immediately before the last table element
in the stack of open elements in the foster parent element;
otherwise, the new node must be appended to the foster parent
element. */
- for($n = count($this->stack) - 1; $n >= 0; $n--) {
- if($this->stack[$n]->nodeName === 'table' &&
- $this->stack[$n]->parentNode !== null) {
+ for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+ if ($this->stack[$n]->nodeName === 'table' &&
+ $this->stack[$n]->parentNode !== null
+ ) {
$table = $this->stack[$n];
break;
}
}
- if(isset($table) && $this->foster_parent->isSameNode($table->parentNode))
+ if (isset($table) && $this->foster_parent->isSameNode($table->parentNode)) {
$this->foster_parent->insertBefore($node, $table);
- else
+ } else {
$this->foster_parent->appendChild($node);
+ }
$this->foster_parent = null;
}
}
- private function elementInScope($el, $table = false) {
- if(is_array($el)) {
- foreach($el as $element) {
- if($this->elementInScope($element, $table)) {
+ private function elementInScope($el, $table = false)
+ {
+ if (is_array($el)) {
+ foreach ($el as $element) {
+ if ($this->elementInScope($element, $table)) {
return true;
}
}
@@ -3610,28 +4476,38 @@ class HTML5TreeConstructer {
$leng = count($this->stack);
- for($n = 0; $n < $leng; $n++) {
+ 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) {
+ if ($node->tagName === $el) {
/* 2. If node is the target node, terminate in a match state. */
return true;
- } elseif($node->tagName === 'table') {
+ } elseif ($node->tagName === 'table') {
/* 3. Otherwise, if node is a table element, terminate in a failure
state. */
return false;
- } elseif($table === true && in_array($node->tagName, array('caption', 'td',
- 'th', 'button', 'marquee', 'object'))) {
+ } elseif ($table === true && in_array(
+ $node->tagName,
+ array(
+ 'caption',
+ 'td',
+ 'th',
+ 'button',
+ 'marquee',
+ 'object'
+ )
+ )
+ ) {
/* 4. Otherwise, if the algorithm is the "has an element in scope"
variant (rather than the "has an element in table scope" variant),
and node is one of the following, terminate in a failure state. */
return false;
- } elseif($node === $node->ownerDocument->documentElement) {
+ } elseif ($node === $node->ownerDocument->documentElement) {
/* 5. Otherwise, if node is an html element (root element), terminate
in a failure state. (This can only happen if the node is the topmost
node of the stack of open elements, and prevents the next step from
@@ -3646,12 +4522,13 @@ class HTML5TreeConstructer {
}
}
- private function reconstructActiveFormattingElements() {
+ 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) {
+ if ($formatting_elements === 0) {
return false;
}
@@ -3663,14 +4540,14 @@ class HTML5TreeConstructer {
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)) {
+ if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
return false;
}
- for($a = $formatting_elements - 1; $a >= 0; true) {
+ 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) {
+ if ($a === 0) {
$step_seven = false;
break;
}
@@ -3682,15 +4559,15 @@ class HTML5TreeConstructer {
/* 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)) {
+ if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
break;
}
}
- while(true) {
+ 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) {
+ if (isset($step_seven) && $step_seven === true) {
$a++;
$entry = $this->a_formatting[$a];
}
@@ -3709,7 +4586,7 @@ class HTML5TreeConstructer {
/* 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) {
+ if (end($this->a_formatting) !== $clone) {
$step_seven = true;
} else {
break;
@@ -3717,12 +4594,13 @@ class HTML5TreeConstructer {
}
}
- private function clearTheActiveFormattingElementsUpToTheLastMarker() {
+ 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) {
+ while (true) {
/* 1. Let entry be the last (most recently added) entry in the list
of active formatting elements. */
$entry = end($this->a_formatting);
@@ -3732,13 +4610,14 @@ class HTML5TreeConstructer {
/* 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) {
+ if ($entry === self::MARKER) {
break;
}
}
}
- private function generateImpliedEndTags($exclude = array()) {
+ 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
@@ -3747,36 +4626,36 @@ class HTML5TreeConstructer {
$node = end($this->stack);
$elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude);
- while(in_array(end($this->stack)->nodeName, $elements)) {
+ while (in_array(end($this->stack)->nodeName, $elements)) {
array_pop($this->stack);
}
}
- private function getElementCategory($node) {
+ private function getElementCategory($node)
+ {
$name = $node->tagName;
- if(in_array($name, $this->special))
+ if (in_array($name, $this->special)) {
return self::SPECIAL;
-
- elseif(in_array($name, $this->scoping))
+ } elseif (in_array($name, $this->scoping)) {
return self::SCOPING;
-
- elseif(in_array($name, $this->formatting))
+ } elseif (in_array($name, $this->formatting)) {
return self::FORMATTING;
-
- else
+ } else {
return self::PHRASING;
+ }
}
- private function clearStackToTableContext($elements) {
+ 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. If this causes any elements to be popped from the stack, then
this is a parse error. */
- while(true) {
+ while (true) {
$node = end($this->stack)->nodeName;
- if(in_array($node, $elements)) {
+ if (in_array($node, $elements)) {
break;
} else {
array_pop($this->stack);
@@ -3784,12 +4663,13 @@ class HTML5TreeConstructer {
}
}
- private function resetInsertionMode() {
+ private function resetInsertionMode()
+ {
/* 1. Let last be false. */
$last = false;
$leng = count($this->stack);
- for($n = $leng - 1; $n >= 0; $n--) {
+ for ($n = $leng - 1; $n >= 0; $n--) {
/* 2. Let node be the last node in the stack of open elements. */
$node = $this->stack[$n];
@@ -3797,108 +4677,111 @@ class HTML5TreeConstructer {
set last to true. If the element whose innerHTML attribute is being
set is neither a td element nor a th element, then set node to the
element whose innerHTML attribute is being set. (innerHTML case) */
- if($this->stack[0]->isSameNode($node)) {
+ if ($this->stack[0]->isSameNode($node)) {
$last = true;
}
/* 4. If node is a select element, then switch the insertion mode to
"in select" and abort these steps. (innerHTML case) */
- if($node->nodeName === 'select') {
+ if ($node->nodeName === '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->nodeName === 'td' || $node->nodeName === 'th') {
+ /* 5. If node is a td or th element, then switch the insertion mode
+ to "in cell" and abort these steps. */
+ } elseif ($node->nodeName === '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->nodeName === 'tr') {
+ /* 6. If node is a tr element, then switch the insertion mode to
+ "in row" and abort these steps. */
+ } elseif ($node->nodeName === '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->nodeName, array('tbody', 'thead', 'tfoot'))) {
+ /* 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->nodeName, array('tbody', 'thead', 'tfoot'))) {
$this->mode = self::IN_TBODY;
break;
- /* 8. If node is a caption element, then switch the insertion mode
- to "in caption" and abort these steps. */
- } elseif($node->nodeName === 'caption') {
+ /* 8. If node is a caption element, then switch the insertion mode
+ to "in caption" and abort these steps. */
+ } elseif ($node->nodeName === '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->nodeName === 'colgroup') {
+ /* 9. If node is a colgroup element, then switch the insertion mode
+ to "in column group" and abort these steps. (innerHTML case) */
+ } elseif ($node->nodeName === 'colgroup') {
$this->mode = self::IN_CGROUP;
break;
- /* 10. If node is a table element, then switch the insertion mode
- to "in table" and abort these steps. */
- } elseif($node->nodeName === 'table') {
+ /* 10. If node is a table element, then switch the insertion mode
+ to "in table" and abort these steps. */
+ } elseif ($node->nodeName === 'table') {
$this->mode = self::IN_TABLE;
break;
- /* 11. If node is a head element, then switch the insertion mode
- to "in body" ("in body"! not "in head"!) and abort these steps.
- (innerHTML case) */
- } elseif($node->nodeName === 'head') {
+ /* 11. If node is a head element, then switch the insertion mode
+ to "in body" ("in body"! not "in head"!) and abort these steps.
+ (innerHTML case) */
+ } elseif ($node->nodeName === 'head') {
$this->mode = self::IN_BODY;
break;
- /* 12. If node is a body element, then switch the insertion mode to
- "in body" and abort these steps. */
- } elseif($node->nodeName === 'body') {
+ /* 12. If node is a body element, then switch the insertion mode to
+ "in body" and abort these steps. */
+ } elseif ($node->nodeName === 'body') {
$this->mode = self::IN_BODY;
break;
- /* 13. If node is a frameset element, then switch the insertion
- mode to "in frameset" and abort these steps. (innerHTML case) */
- } elseif($node->nodeName === 'frameset') {
+ /* 13. If node is a frameset element, then switch the insertion
+ mode to "in frameset" and abort these steps. (innerHTML case) */
+ } elseif ($node->nodeName === 'frameset') {
$this->mode = self::IN_FRAME;
break;
- /* 14. 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. (innerHTML case) */
- } elseif($node->nodeName === 'html') {
+ /* 14. 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. (innerHTML case) */
+ } elseif ($node->nodeName === 'html') {
$this->mode = ($this->head_pointer === null)
? self::BEFOR_HEAD
: self::AFTER_HEAD;
break;
- /* 15. If last is true, then set the insertion mode to "in body"
- and abort these steps. (innerHTML case) */
- } elseif($last) {
+ /* 15. If last is true, then set the insertion mode to "in body"
+ and abort these steps. (innerHTML case) */
+ } elseif ($last) {
$this->mode = self::IN_BODY;
break;
}
}
}
- private function closeCell() {
+ 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->inCell(array(
- 'name' => $cell,
- 'type' => HTML5::ENDTAG
- ));
+ foreach (array('td', 'th') as $cell) {
+ if ($this->elementInScope($cell, true)) {
+ $this->inCell(
+ array(
+ 'name' => $cell,
+ 'type' => HTML5::ENDTAG
+ )
+ );
break;
}
}
}
- public function save() {
+ public function save()
+ {
return $this->dom;
}
}
-?>
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Node.php b/classes/security/htmlpurifier/library/HTMLPurifier/Node.php
new file mode 100644
index 000000000..3995fec9f
--- /dev/null
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Node.php
@@ -0,0 +1,49 @@
+data = $data;
+ $this->line = $line;
+ $this->col = $col;
+ }
+
+ public function toTokenPair() {
+ return array(new HTMLPurifier_Token_Comment($this->data, $this->line, $this->col), null);
+ }
+}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Node/Element.php b/classes/security/htmlpurifier/library/HTMLPurifier/Node/Element.php
new file mode 100644
index 000000000..6cbf56dad
--- /dev/null
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Node/Element.php
@@ -0,0 +1,59 @@
+ form or the form, i.e.
+ * is it a pair of start/end tokens or an empty token.
+ * @bool
+ */
+ public $empty = false;
+
+ public $endCol = null, $endLine = null, $endArmor = array();
+
+ public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) {
+ $this->name = $name;
+ $this->attr = $attr;
+ $this->line = $line;
+ $this->col = $col;
+ $this->armor = $armor;
+ }
+
+ public function toTokenPair() {
+ // XXX inefficiency here, normalization is not necessary
+ if ($this->empty) {
+ return array(new HTMLPurifier_Token_Empty($this->name, $this->attr, $this->line, $this->col, $this->armor), null);
+ } else {
+ $start = new HTMLPurifier_Token_Start($this->name, $this->attr, $this->line, $this->col, $this->armor);
+ $end = new HTMLPurifier_Token_End($this->name, array(), $this->endLine, $this->endCol, $this->endArmor);
+ //$end->start = $start;
+ return array($start, $end);
+ }
+ }
+}
+
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Node/Text.php b/classes/security/htmlpurifier/library/HTMLPurifier/Node/Text.php
new file mode 100644
index 000000000..aec916647
--- /dev/null
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Node/Text.php
@@ -0,0 +1,54 @@
+data = $data;
+ $this->is_whitespace = $is_whitespace;
+ $this->line = $line;
+ $this->col = $col;
+ }
+
+ public function toTokenPair() {
+ return array(new HTMLPurifier_Token_Text($this->data, $this->line, $this->col), null);
+ }
+}
+
+// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/PercentEncoder.php b/classes/security/htmlpurifier/library/HTMLPurifier/PercentEncoder.php
index a43c44f4c..18c8bbb00 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/PercentEncoder.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/PercentEncoder.php
@@ -13,17 +13,26 @@ class HTMLPurifier_PercentEncoder
/**
* Reserved characters to preserve when using encode().
+ * @type array
*/
protected $preserve = array();
/**
* String of characters that should be preserved while using encode().
+ * @param bool $preserve
*/
- public function __construct($preserve = false) {
+ public function __construct($preserve = false)
+ {
// unreserved letters, ought to const-ify
- for ($i = 48; $i <= 57; $i++) $this->preserve[$i] = true; // digits
- for ($i = 65; $i <= 90; $i++) $this->preserve[$i] = true; // upper-case
- for ($i = 97; $i <= 122; $i++) $this->preserve[$i] = true; // lower-case
+ for ($i = 48; $i <= 57; $i++) { // digits
+ $this->preserve[$i] = true;
+ }
+ for ($i = 65; $i <= 90; $i++) { // upper-case
+ $this->preserve[$i] = true;
+ }
+ for ($i = 97; $i <= 122; $i++) { // lower-case
+ $this->preserve[$i] = true;
+ }
$this->preserve[45] = true; // Dash -
$this->preserve[46] = true; // Period .
$this->preserve[95] = true; // Underscore _
@@ -44,13 +53,14 @@ class HTMLPurifier_PercentEncoder
* Assumes that the string has already been normalized, making any
* and all percent escape sequences valid. Percents will not be
* re-escaped, regardless of their status in $preserve
- * @param $string String to be encoded
- * @return Encoded string.
+ * @param string $string String to be encoded
+ * @return string Encoded string.
*/
- public function encode($string) {
+ public function encode($string)
+ {
$ret = '';
for ($i = 0, $c = strlen($string); $i < $c; $i++) {
- if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])]) ) {
+ if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])])) {
$ret .= '%' . sprintf('%02X', $int);
} else {
$ret .= $string[$i];
@@ -64,10 +74,14 @@ class HTMLPurifier_PercentEncoder
* @warning This function is affected by $preserve, even though the
* usual desired behavior is for this not to preserve those
* characters. Be careful when reusing instances of PercentEncoder!
- * @param $string String to normalize
+ * @param string $string String to normalize
+ * @return string
*/
- public function normalize($string) {
- if ($string == '') return '';
+ public function normalize($string)
+ {
+ if ($string == '') {
+ return '';
+ }
$parts = explode('%', $string);
$ret = array_shift($parts);
foreach ($parts as $part) {
@@ -92,7 +106,6 @@ class HTMLPurifier_PercentEncoder
}
return $ret;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Printer.php b/classes/security/htmlpurifier/library/HTMLPurifier/Printer.php
index e81b32e39..549e4cea1 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Printer.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Printer.php
@@ -7,25 +7,30 @@ class HTMLPurifier_Printer
{
/**
- * Instance of HTMLPurifier_Generator for HTML generation convenience funcs
+ * For HTML generation convenience funcs.
+ * @type HTMLPurifier_Generator
*/
protected $generator;
/**
- * Instance of HTMLPurifier_Config, for easy access
+ * For easy access.
+ * @type HTMLPurifier_Config
*/
protected $config;
/**
* Initialize $generator.
*/
- public function __construct() {
+ public function __construct()
+ {
}
/**
* Give generator necessary configuration if possible
+ * @param HTMLPurifier_Config $config
*/
- public function prepareGenerator($config) {
+ public function prepareGenerator($config)
+ {
$all = $config->getAll();
$context = new HTMLPurifier_Context();
$this->generator = new HTMLPurifier_Generator($config, $context);
@@ -39,45 +44,62 @@ class HTMLPurifier_Printer
/**
* Returns a start tag
- * @param $tag Tag name
- * @param $attr Attribute array
+ * @param string $tag Tag name
+ * @param array $attr Attribute array
+ * @return string
*/
- protected function start($tag, $attr = array()) {
+ protected function start($tag, $attr = array())
+ {
return $this->generator->generateFromToken(
- new HTMLPurifier_Token_Start($tag, $attr ? $attr : array())
- );
+ new HTMLPurifier_Token_Start($tag, $attr ? $attr : array())
+ );
}
/**
- * Returns an end teg
- * @param $tag Tag name
+ * Returns an end tag
+ * @param string $tag Tag name
+ * @return string
*/
- protected function end($tag) {
+ protected function end($tag)
+ {
return $this->generator->generateFromToken(
- new HTMLPurifier_Token_End($tag)
- );
+ new HTMLPurifier_Token_End($tag)
+ );
}
/**
* Prints a complete element with content inside
- * @param $tag Tag name
- * @param $contents Element contents
- * @param $attr Tag attributes
- * @param $escape Bool whether or not to escape contents
+ * @param string $tag Tag name
+ * @param string $contents Element contents
+ * @param array $attr Tag attributes
+ * @param bool $escape whether or not to escape contents
+ * @return string
*/
- protected function element($tag, $contents, $attr = array(), $escape = true) {
+ protected function element($tag, $contents, $attr = array(), $escape = true)
+ {
return $this->start($tag, $attr) .
- ($escape ? $this->escape($contents) : $contents) .
- $this->end($tag);
+ ($escape ? $this->escape($contents) : $contents) .
+ $this->end($tag);
}
- protected function elementEmpty($tag, $attr = array()) {
+ /**
+ * @param string $tag
+ * @param array $attr
+ * @return string
+ */
+ protected function elementEmpty($tag, $attr = array())
+ {
return $this->generator->generateFromToken(
new HTMLPurifier_Token_Empty($tag, $attr)
);
}
- protected function text($text) {
+ /**
+ * @param string $text
+ * @return string
+ */
+ protected function text($text)
+ {
return $this->generator->generateFromToken(
new HTMLPurifier_Token_Text($text)
);
@@ -85,57 +107,76 @@ class HTMLPurifier_Printer
/**
* Prints a simple key/value row in a table.
- * @param $name Key
- * @param $value Value
+ * @param string $name Key
+ * @param mixed $value Value
+ * @return string
*/
- protected function row($name, $value) {
- if (is_bool($value)) $value = $value ? 'On' : 'Off';
+ protected function row($name, $value)
+ {
+ if (is_bool($value)) {
+ $value = $value ? 'On' : 'Off';
+ }
return
$this->start('tr') . "\n" .
- $this->element('th', $name) . "\n" .
- $this->element('td', $value) . "\n" .
- $this->end('tr')
- ;
+ $this->element('th', $name) . "\n" .
+ $this->element('td', $value) . "\n" .
+ $this->end('tr');
}
/**
* Escapes a string for HTML output.
- * @param $string String to escape
+ * @param string $string String to escape
+ * @return string
*/
- protected function escape($string) {
+ protected function escape($string)
+ {
$string = HTMLPurifier_Encoder::cleanUTF8($string);
- $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
+ $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
return $string;
}
/**
* Takes a list of strings and turns them into a single list
- * @param $array List of strings
- * @param $polite Bool whether or not to add an end before the last
+ * @param string[] $array List of strings
+ * @param bool $polite Bool whether or not to add an end before the last
+ * @return string
*/
- protected function listify($array, $polite = false) {
- if (empty($array)) return 'None';
+ protected function listify($array, $polite = false)
+ {
+ if (empty($array)) {
+ return 'None';
+ }
$ret = '';
$i = count($array);
foreach ($array as $value) {
$i--;
$ret .= $value;
- if ($i > 0 && !($polite && $i == 1)) $ret .= ', ';
- if ($polite && $i == 1) $ret .= 'and ';
+ if ($i > 0 && !($polite && $i == 1)) {
+ $ret .= ', ';
+ }
+ if ($polite && $i == 1) {
+ $ret .= 'and ';
+ }
}
return $ret;
}
/**
* Retrieves the class of an object without prefixes, as well as metadata
- * @param $obj Object to determine class of
- * @param $prefix Further prefix to remove
+ * @param object $obj Object to determine class of
+ * @param string $sec_prefix Further prefix to remove
+ * @return string
*/
- protected function getClass($obj, $sec_prefix = '') {
+ protected function getClass($obj, $sec_prefix = '')
+ {
static $five = null;
- if ($five === null) $five = version_compare(PHP_VERSION, '5', '>=');
+ if ($five === null) {
+ $five = version_compare(PHP_VERSION, '5', '>=');
+ }
$prefix = 'HTMLPurifier_' . $sec_prefix;
- if (!$five) $prefix = strtolower($prefix);
+ if (!$five) {
+ $prefix = strtolower($prefix);
+ }
$class = str_replace($prefix, '', get_class($obj));
$lclass = strtolower($class);
$class .= '(';
@@ -164,13 +205,14 @@ class HTMLPurifier_Printer
break;
case 'css_importantdecorator':
$class .= $this->getClass($obj->def, $sec_prefix);
- if ($obj->allow) $class .= ', !important';
+ if ($obj->allow) {
+ $class .= ', !important';
+ }
break;
}
$class .= ')';
return $class;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php b/classes/security/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php
index 81f986590..29505fe12 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php
@@ -2,10 +2,17 @@
class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer
{
-
+ /**
+ * @type HTMLPurifier_CSSDefinition
+ */
protected $def;
- public function render($config) {
+ /**
+ * @param HTMLPurifier_Config $config
+ * @return string
+ */
+ public function render($config)
+ {
$this->def = $config->getCSSDefinition();
$ret = '';
@@ -32,7 +39,6 @@ class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer
return $ret;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php b/classes/security/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php
index 02aa65689..36100ce73 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php
@@ -7,17 +7,20 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
{
/**
- * Printers for specific fields
+ * Printers for specific fields.
+ * @type HTMLPurifier_Printer[]
*/
protected $fields = array();
/**
- * Documentation URL, can have fragment tagged on end
+ * Documentation URL, can have fragment tagged on end.
+ * @type string
*/
protected $docURL;
/**
- * Name of form element to stuff config in
+ * Name of form element to stuff config in.
+ * @type string
*/
protected $name;
@@ -25,24 +28,27 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
* Whether or not to compress directive names, clipping them off
* after a certain amount of letters. False to disable or integer letters
* before clipping.
+ * @type bool
*/
protected $compress = false;
/**
- * @param $name Form element name for directives to be stuffed into
- * @param $doc_url String documentation URL, will have fragment tagged on
- * @param $compress Integer max length before compressing a directive name, set to false to turn off
+ * @param string $name Form element name for directives to be stuffed into
+ * @param string $doc_url String documentation URL, will have fragment tagged on
+ * @param bool $compress Integer max length before compressing a directive name, set to false to turn off
*/
public function __construct(
- $name, $doc_url = null, $compress = false
+ $name,
+ $doc_url = null,
+ $compress = false
) {
parent::__construct();
$this->docURL = $doc_url;
- $this->name = $name;
+ $this->name = $name;
$this->compress = $compress;
// initialize sub-printers
- $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default();
- $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool();
+ $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default();
+ $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool();
}
/**
@@ -50,32 +56,42 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
* @param $cols Integer columns of textarea, null to use default
* @param $rows Integer rows of textarea, null to use default
*/
- public function setTextareaDimensions($cols = null, $rows = null) {
- if ($cols) $this->fields['default']->cols = $cols;
- if ($rows) $this->fields['default']->rows = $rows;
+ public function setTextareaDimensions($cols = null, $rows = null)
+ {
+ if ($cols) {
+ $this->fields['default']->cols = $cols;
+ }
+ if ($rows) {
+ $this->fields['default']->rows = $rows;
+ }
}
/**
* Retrieves styling, in case it is not accessible by webserver
*/
- public static function getCSS() {
+ public static function getCSS()
+ {
return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css');
}
/**
* Retrieves JavaScript, in case it is not accessible by webserver
*/
- public static function getJavaScript() {
+ public static function getJavaScript()
+ {
return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js');
}
/**
* Returns HTML output for a configuration form
- * @param $config Configuration object of current form state, or an array
+ * @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array
* where [0] has an HTML namespace and [1] is being rendered.
- * @param $allowed Optional namespace(s) and directives to restrict form to.
+ * @param array|bool $allowed Optional namespace(s) and directives to restrict form to.
+ * @param bool $render_controls
+ * @return string
*/
- public function render($config, $allowed = true, $render_controls = true) {
+ public function render($config, $allowed = true, $render_controls = true)
+ {
if (is_array($config) && isset($config[0])) {
$gen_config = $config[0];
$config = $config[1];
@@ -91,29 +107,29 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
$all = array();
foreach ($allowed as $key) {
list($ns, $directive) = $key;
- $all[$ns][$directive] = $config->get($ns .'.'. $directive);
+ $all[$ns][$directive] = $config->get($ns . '.' . $directive);
}
$ret = '';
$ret .= $this->start('table', array('class' => 'hp-config'));
$ret .= $this->start('thead');
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
- $ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
+ $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
+ $ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
$ret .= $this->end('tr');
$ret .= $this->end('thead');
foreach ($all as $ns => $directives) {
$ret .= $this->renderNamespace($ns, $directives);
}
if ($render_controls) {
- $ret .= $this->start('tbody');
- $ret .= $this->start('tr');
- $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls'));
- $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit'));
- $ret .= '[
Reset]';
- $ret .= $this->end('td');
- $ret .= $this->end('tr');
- $ret .= $this->end('tbody');
+ $ret .= $this->start('tbody');
+ $ret .= $this->start('tr');
+ $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls'));
+ $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit'));
+ $ret .= '[
Reset]';
+ $ret .= $this->end('td');
+ $ret .= $this->end('tr');
+ $ret .= $this->end('tbody');
}
$ret .= $this->end('table');
return $ret;
@@ -122,13 +138,15 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
/**
* Renders a single namespace
* @param $ns String namespace name
- * @param $directive Associative array of directives to values
+ * @param array $directives array of directives to values
+ * @return string
*/
- protected function renderNamespace($ns, $directives) {
+ protected function renderNamespace($ns, $directives)
+ {
$ret = '';
$ret .= $this->start('tbody', array('class' => 'namespace'));
$ret .= $this->start('tr');
- $ret .= $this->element('th', $ns, array('colspan' => 2));
+ $ret .= $this->element('th', $ns, array('colspan' => 2));
$ret .= $this->end('tr');
$ret .= $this->end('tbody');
$ret .= $this->start('tbody');
@@ -139,40 +157,44 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
$url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL);
$ret .= $this->start('a', array('href' => $url));
}
- $attr = array('for' => "{$this->name}:$ns.$directive");
+ $attr = array('for' => "{$this->name}:$ns.$directive");
- // crop directive name if it's too long
- if (!$this->compress || (strlen($directive) < $this->compress)) {
- $directive_disp = $directive;
- } else {
- $directive_disp = substr($directive, 0, $this->compress - 2) . '...';
- $attr['title'] = $directive;
- }
+ // crop directive name if it's too long
+ if (!$this->compress || (strlen($directive) < $this->compress)) {
+ $directive_disp = $directive;
+ } else {
+ $directive_disp = substr($directive, 0, $this->compress - 2) . '...';
+ $attr['title'] = $directive;
+ }
- $ret .= $this->element(
- 'label',
- $directive_disp,
- // component printers must create an element with this id
- $attr
- );
- if ($this->docURL) $ret .= $this->end('a');
+ $ret .= $this->element(
+ 'label',
+ $directive_disp,
+ // component printers must create an element with this id
+ $attr
+ );
+ if ($this->docURL) {
+ $ret .= $this->end('a');
+ }
$ret .= $this->end('th');
$ret .= $this->start('td');
- $def = $this->config->def->info["$ns.$directive"];
- if (is_int($def)) {
- $allow_null = $def < 0;
- $type = abs($def);
- } else {
- $type = $def->type;
- $allow_null = isset($def->allow_null);
- }
- if (!isset($this->fields[$type])) $type = 0; // default
- $type_obj = $this->fields[$type];
- if ($allow_null) {
- $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
- }
- $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config));
+ $def = $this->config->def->info["$ns.$directive"];
+ if (is_int($def)) {
+ $allow_null = $def < 0;
+ $type = abs($def);
+ } else {
+ $type = $def->type;
+ $allow_null = isset($def->allow_null);
+ }
+ if (!isset($this->fields[$type])) {
+ $type = 0;
+ } // default
+ $type_obj = $this->fields[$type];
+ if ($allow_null) {
+ $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
+ }
+ $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config));
$ret .= $this->end('td');
$ret .= $this->end('tr');
}
@@ -185,19 +207,33 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
/**
* Printer decorator for directives that accept null
*/
-class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer {
+class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
+{
/**
* Printer being decorated
+ * @type HTMLPurifier_Printer
*/
protected $obj;
+
/**
- * @param $obj Printer to decorate
+ * @param HTMLPurifier_Printer $obj Printer to decorate
*/
- public function __construct($obj) {
+ public function __construct($obj)
+ {
parent::__construct();
$this->obj = $obj;
}
- public function render($ns, $directive, $value, $name, $config) {
+
+ /**
+ * @param string $ns
+ * @param string $directive
+ * @param string $value
+ * @param string $name
+ * @param HTMLPurifier_Config|array $config
+ * @return string
+ */
+ public function render($ns, $directive, $value, $name, $config)
+ {
if (is_array($config) && isset($config[0])) {
$gen_config = $config[0];
$config = $config[1];
@@ -215,15 +251,19 @@ class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
'type' => 'checkbox',
'value' => '1',
'class' => 'null-toggle',
- 'name' => "$name"."[Null_$ns.$directive]",
+ 'name' => "$name" . "[Null_$ns.$directive]",
'id' => "$name:Null_$ns.$directive",
'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!!
);
if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) {
// modify inline javascript slightly
- $attr['onclick'] = "toggleWriteability('$name:Yes_$ns.$directive',checked);toggleWriteability('$name:No_$ns.$directive',checked)";
+ $attr['onclick'] =
+ "toggleWriteability('$name:Yes_$ns.$directive',checked);" .
+ "toggleWriteability('$name:No_$ns.$directive',checked)";
+ }
+ if ($value === null) {
+ $attr['checked'] = 'checked';
}
- if ($value === null) $attr['checked'] = 'checked';
$ret .= $this->elementEmpty('input', $attr);
$ret .= $this->text(' or ');
$ret .= $this->elementEmpty('br');
@@ -235,10 +275,28 @@ class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
/**
* Swiss-army knife configuration form field printer
*/
-class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
+class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
+{
+ /**
+ * @type int
+ */
public $cols = 18;
+
+ /**
+ * @type int
+ */
public $rows = 5;
- public function render($ns, $directive, $value, $name, $config) {
+
+ /**
+ * @param string $ns
+ * @param string $directive
+ * @param string $value
+ * @param string $name
+ * @param HTMLPurifier_Config|array $config
+ * @return string
+ */
+ public function render($ns, $directive, $value, $name, $config)
+ {
if (is_array($config) && isset($config[0])) {
$gen_config = $config[0];
$config = $config[1];
@@ -262,6 +320,7 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
foreach ($array as $val => $b) {
$value[] = $val;
}
+ //TODO does this need a break?
case HTMLPurifier_VarParser::ALIST:
$value = implode(PHP_EOL, $value);
break;
@@ -281,25 +340,27 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
$value = serialize($value);
}
$attr = array(
- 'name' => "$name"."[$ns.$directive]",
+ 'name' => "$name" . "[$ns.$directive]",
'id' => "$name:$ns.$directive"
);
- if ($value === null) $attr['disabled'] = 'disabled';
+ if ($value === null) {
+ $attr['disabled'] = 'disabled';
+ }
if (isset($def->allowed)) {
$ret .= $this->start('select', $attr);
foreach ($def->allowed as $val => $b) {
$attr = array();
- if ($value == $val) $attr['selected'] = 'selected';
+ if ($value == $val) {
+ $attr['selected'] = 'selected';
+ }
$ret .= $this->element('option', $val, $attr);
}
$ret .= $this->end('select');
- } elseif (
- $type === HTMLPurifier_VarParser::TEXT ||
- $type === HTMLPurifier_VarParser::ITEXT ||
- $type === HTMLPurifier_VarParser::ALIST ||
- $type === HTMLPurifier_VarParser::HASH ||
- $type === HTMLPurifier_VarParser::LOOKUP
- ) {
+ } elseif ($type === HTMLPurifier_VarParser::TEXT ||
+ $type === HTMLPurifier_VarParser::ITEXT ||
+ $type === HTMLPurifier_VarParser::ALIST ||
+ $type === HTMLPurifier_VarParser::HASH ||
+ $type === HTMLPurifier_VarParser::LOOKUP) {
$attr['cols'] = $this->cols;
$attr['rows'] = $this->rows;
$ret .= $this->start('textarea', $attr);
@@ -317,8 +378,18 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
/**
* Bool form field printer
*/
-class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer {
- public function render($ns, $directive, $value, $name, $config) {
+class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer
+{
+ /**
+ * @param string $ns
+ * @param string $directive
+ * @param string $value
+ * @param string $name
+ * @param HTMLPurifier_Config|array $config
+ * @return string
+ */
+ public function render($ns, $directive, $value, $name, $config)
+ {
if (is_array($config) && isset($config[0])) {
$gen_config = $config[0];
$config = $config[1];
@@ -336,12 +407,16 @@ class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer {
$attr = array(
'type' => 'radio',
- 'name' => "$name"."[$ns.$directive]",
+ 'name' => "$name" . "[$ns.$directive]",
'id' => "$name:Yes_$ns.$directive",
'value' => '1'
);
- if ($value === true) $attr['checked'] = 'checked';
- if ($value === null) $attr['disabled'] = 'disabled';
+ if ($value === true) {
+ $attr['checked'] = 'checked';
+ }
+ if ($value === null) {
+ $attr['disabled'] = 'disabled';
+ }
$ret .= $this->elementEmpty('input', $attr);
$ret .= $this->start('label', array('for' => "$name:No_$ns.$directive"));
@@ -351,12 +426,16 @@ class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer {
$attr = array(
'type' => 'radio',
- 'name' => "$name"."[$ns.$directive]",
+ 'name' => "$name" . "[$ns.$directive]",
'id' => "$name:No_$ns.$directive",
'value' => '0'
);
- if ($value === false) $attr['checked'] = 'checked';
- if ($value === null) $attr['disabled'] = 'disabled';
+ if ($value === false) {
+ $attr['checked'] = 'checked';
+ }
+ if ($value === null) {
+ $attr['disabled'] = 'disabled';
+ }
$ret .= $this->elementEmpty('input', $attr);
$ret .= $this->end('div');
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php b/classes/security/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php
index 8a8f126b8..5f2f2f8a7 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php
@@ -4,11 +4,16 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
{
/**
- * Instance of HTMLPurifier_HTMLDefinition, for easy access
+ * @type HTMLPurifier_HTMLDefinition, for easy access
*/
protected $def;
- public function render($config) {
+ /**
+ * @param HTMLPurifier_Config $config
+ * @return string
+ */
+ public function render($config)
+ {
$ret = '';
$this->config =& $config;
@@ -28,8 +33,10 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
/**
* Renders the Doctype table
+ * @return string
*/
- protected function renderDoctype() {
+ protected function renderDoctype()
+ {
$doctype = $this->def->doctype;
$ret = '';
$ret .= $this->start('table');
@@ -45,8 +52,10 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
/**
* Renders environment table, which is miscellaneous info
+ * @return string
*/
- protected function renderEnvironment() {
+ protected function renderEnvironment()
+ {
$def = $this->def;
$ret = '';
@@ -59,28 +68,28 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
$ret .= $this->row('Block wrap name', $def->info_block_wrapper);
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Global attributes');
- $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr),0,0);
+ $ret .= $this->element('th', 'Global attributes');
+ $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr), null, 0);
$ret .= $this->end('tr');
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Tag transforms');
- $list = array();
- foreach ($def->info_tag_transform as $old => $new) {
- $new = $this->getClass($new, 'TagTransform_');
- $list[] = "<$old> with $new";
- }
- $ret .= $this->element('td', $this->listify($list));
+ $ret .= $this->element('th', 'Tag transforms');
+ $list = array();
+ foreach ($def->info_tag_transform as $old => $new) {
+ $new = $this->getClass($new, 'TagTransform_');
+ $list[] = "<$old> with $new";
+ }
+ $ret .= $this->element('td', $this->listify($list));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Pre-AttrTransform');
- $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre));
+ $ret .= $this->element('th', 'Pre-AttrTransform');
+ $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Post-AttrTransform');
- $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post));
+ $ret .= $this->element('th', 'Post-AttrTransform');
+ $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post));
$ret .= $this->end('tr');
$ret .= $this->end('table');
@@ -89,8 +98,10 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
/**
* Renders the Content Sets table
+ * @return string
*/
- protected function renderContentSets() {
+ protected function renderContentSets()
+ {
$ret = '';
$ret .= $this->start('table');
$ret .= $this->element('caption', 'Content Sets');
@@ -106,8 +117,10 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
/**
* Renders the Elements ($info) table
+ * @return string
*/
- protected function renderInfo() {
+ protected function renderInfo()
+ {
$ret = '';
$ret .= $this->start('table');
$ret .= $this->element('caption', 'Elements ($info)');
@@ -118,39 +131,39 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
$ret .= $this->end('tr');
foreach ($this->def->info as $name => $def) {
$ret .= $this->start('tr');
- $ret .= $this->element('th', "<$name>", array('class'=>'heavy', 'colspan' => 2));
+ $ret .= $this->element('th', "<$name>", array('class' => 'heavy', 'colspan' => 2));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Inline content');
- $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No');
+ $ret .= $this->element('th', 'Inline content');
+ $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No');
$ret .= $this->end('tr');
if (!empty($def->excludes)) {
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Excludes');
- $ret .= $this->element('td', $this->listifyTagLookup($def->excludes));
+ $ret .= $this->element('th', 'Excludes');
+ $ret .= $this->element('td', $this->listifyTagLookup($def->excludes));
$ret .= $this->end('tr');
}
if (!empty($def->attr_transform_pre)) {
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Pre-AttrTransform');
- $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre));
+ $ret .= $this->element('th', 'Pre-AttrTransform');
+ $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre));
$ret .= $this->end('tr');
}
if (!empty($def->attr_transform_post)) {
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Post-AttrTransform');
- $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post));
+ $ret .= $this->element('th', 'Post-AttrTransform');
+ $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post));
$ret .= $this->end('tr');
}
if (!empty($def->auto_close)) {
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Auto closed by');
- $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close));
+ $ret .= $this->element('th', 'Auto closed by');
+ $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close));
$ret .= $this->end('tr');
}
$ret .= $this->start('tr');
- $ret .= $this->element('th', 'Allowed attributes');
- $ret .= $this->element('td',$this->listifyAttr($def->attr), array(), 0);
+ $ret .= $this->element('th', 'Allowed attributes');
+ $ret .= $this->element('td', $this->listifyAttr($def->attr), array(), 0);
$ret .= $this->end('tr');
if (!empty($def->required_attr)) {
@@ -165,64 +178,94 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
/**
* Renders a row describing the allowed children of an element
- * @param $def HTMLPurifier_ChildDef of pertinent element
+ * @param HTMLPurifier_ChildDef $def HTMLPurifier_ChildDef of pertinent element
+ * @return string
*/
- protected function renderChildren($def) {
+ protected function renderChildren($def)
+ {
$context = new HTMLPurifier_Context();
$ret = '';
$ret .= $this->start('tr');
+ $elements = array();
+ $attr = array();
+ if (isset($def->elements)) {
+ if ($def->type == 'strictblockquote') {
+ $def->validateChildren(array(), $this->config, $context);
+ }
+ $elements = $def->elements;
+ }
+ if ($def->type == 'chameleon') {
+ $attr['rowspan'] = 2;
+ } elseif ($def->type == 'empty') {
$elements = array();
- $attr = array();
- if (isset($def->elements)) {
- if ($def->type == 'strictblockquote') {
- $def->validateChildren(array(), $this->config, $context);
- }
- $elements = $def->elements;
- }
- if ($def->type == 'chameleon') {
- $attr['rowspan'] = 2;
- } elseif ($def->type == 'empty') {
- $elements = array();
- } elseif ($def->type == 'table') {
- $elements = array_flip(array('col', 'caption', 'colgroup', 'thead',
- 'tfoot', 'tbody', 'tr'));
- }
- $ret .= $this->element('th', 'Allowed children', $attr);
+ } elseif ($def->type == 'table') {
+ $elements = array_flip(
+ array(
+ 'col',
+ 'caption',
+ 'colgroup',
+ 'thead',
+ 'tfoot',
+ 'tbody',
+ 'tr'
+ )
+ );
+ }
+ $ret .= $this->element('th', 'Allowed children', $attr);
- if ($def->type == 'chameleon') {
+ if ($def->type == 'chameleon') {
- $ret .= $this->element('td',
- '
Block: ' .
- $this->escape($this->listifyTagLookup($def->block->elements)),0,0);
- $ret .= $this->end('tr');
- $ret .= $this->start('tr');
- $ret .= $this->element('td',
- '
Inline: ' .
- $this->escape($this->listifyTagLookup($def->inline->elements)),0,0);
+ $ret .= $this->element(
+ 'td',
+ '
Block: ' .
+ $this->escape($this->listifyTagLookup($def->block->elements)),
+ null,
+ 0
+ );
+ $ret .= $this->end('tr');
+ $ret .= $this->start('tr');
+ $ret .= $this->element(
+ 'td',
+ '
Inline: ' .
+ $this->escape($this->listifyTagLookup($def->inline->elements)),
+ null,
+ 0
+ );
- } elseif ($def->type == 'custom') {
+ } elseif ($def->type == 'custom') {
- $ret .= $this->element('td', '
'.ucfirst($def->type).': ' .
- $def->dtd_regex);
+ $ret .= $this->element(
+ 'td',
+ '
' . ucfirst($def->type) . ': ' .
+ $def->dtd_regex
+ );
- } else {
- $ret .= $this->element('td',
- '
'.ucfirst($def->type).': ' .
- $this->escape($this->listifyTagLookup($elements)),0,0);
- }
+ } else {
+ $ret .= $this->element(
+ 'td',
+ '
' . ucfirst($def->type) . ': ' .
+ $this->escape($this->listifyTagLookup($elements)),
+ null,
+ 0
+ );
+ }
$ret .= $this->end('tr');
return $ret;
}
/**
* Listifies a tag lookup table.
- * @param $array Tag lookup array in form of array('tagname' => true)
+ * @param array $array Tag lookup array in form of array('tagname' => true)
+ * @return string
*/
- protected function listifyTagLookup($array) {
+ protected function listifyTagLookup($array)
+ {
ksort($array);
$list = array();
foreach ($array as $name => $discard) {
- if ($name !== '#PCDATA' && !isset($this->def->info[$name])) continue;
+ if ($name !== '#PCDATA' && !isset($this->def->info[$name])) {
+ continue;
+ }
$list[] = $name;
}
return $this->listify($list);
@@ -230,13 +273,15 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
/**
* Listifies a list of objects by retrieving class names and internal state
- * @param $array List of objects
+ * @param array $array List of objects
+ * @return string
* @todo Also add information about internal state
*/
- protected function listifyObjectList($array) {
+ protected function listifyObjectList($array)
+ {
ksort($array);
$list = array();
- foreach ($array as $discard => $obj) {
+ foreach ($array as $obj) {
$list[] = $this->getClass($obj, 'AttrTransform_');
}
return $this->listify($list);
@@ -244,13 +289,17 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
/**
* Listifies a hash of attributes to AttrDef classes
- * @param $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef)
+ * @param array $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef)
+ * @return string
*/
- protected function listifyAttr($array) {
+ protected function listifyAttr($array)
+ {
ksort($array);
$list = array();
foreach ($array as $name => $obj) {
- if ($obj === false) continue;
+ if ($obj === false) {
+ continue;
+ }
$list[] = "$name =
" . $this->getClass($obj, 'AttrDef_') . '';
}
return $this->listify($list);
@@ -258,15 +307,18 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
/**
* Creates a heavy header row
+ * @param string $text
+ * @param int $num
+ * @return string
*/
- protected function heavyHeader($text, $num = 1) {
+ protected function heavyHeader($text, $num = 1)
+ {
$ret = '';
$ret .= $this->start('tr');
$ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy'));
$ret .= $this->end('tr');
return $ret;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/PropertyList.php b/classes/security/htmlpurifier/library/HTMLPurifier/PropertyList.php
index 2b99fb7bc..189348fd9 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/PropertyList.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/PropertyList.php
@@ -6,61 +6,93 @@
class HTMLPurifier_PropertyList
{
/**
- * Internal data-structure for properties
+ * Internal data-structure for properties.
+ * @type array
*/
protected $data = array();
/**
- * Parent plist
+ * Parent plist.
+ * @type HTMLPurifier_PropertyList
*/
protected $parent;
+ /**
+ * Cache.
+ * @type array
+ */
protected $cache;
- public function __construct($parent = null) {
+ /**
+ * @param HTMLPurifier_PropertyList $parent Parent plist
+ */
+ public function __construct($parent = null)
+ {
$this->parent = $parent;
}
/**
* Recursively retrieves the value for a key
+ * @param string $name
+ * @throws HTMLPurifier_Exception
*/
- public function get($name) {
- if ($this->has($name)) return $this->data[$name];
+ public function get($name)
+ {
+ if ($this->has($name)) {
+ return $this->data[$name];
+ }
// possible performance bottleneck, convert to iterative if necessary
- if ($this->parent) return $this->parent->get($name);
+ if ($this->parent) {
+ return $this->parent->get($name);
+ }
throw new HTMLPurifier_Exception("Key '$name' not found");
}
/**
* Sets the value of a key, for this plist
+ * @param string $name
+ * @param mixed $value
*/
- public function set($name, $value) {
+ public function set($name, $value)
+ {
$this->data[$name] = $value;
}
/**
* Returns true if a given key exists
+ * @param string $name
+ * @return bool
*/
- public function has($name) {
+ public function has($name)
+ {
return array_key_exists($name, $this->data);
}
/**
* Resets a value to the value of it's parent, usually the default. If
* no value is specified, the entire plist is reset.
+ * @param string $name
*/
- public function reset($name = null) {
- if ($name == null) $this->data = array();
- else unset($this->data[$name]);
+ public function reset($name = null)
+ {
+ if ($name == null) {
+ $this->data = array();
+ } else {
+ unset($this->data[$name]);
+ }
}
/**
* Squashes this property list and all of its property lists into a single
* array, and returns the array. This value is cached by default.
- * @param $force If true, ignores the cache and regenerates the array.
+ * @param bool $force If true, ignores the cache and regenerates the array.
+ * @return array
*/
- public function squash($force = false) {
- if ($this->cache !== null && !$force) return $this->cache;
+ public function squash($force = false)
+ {
+ if ($this->cache !== null && !$force) {
+ return $this->cache;
+ }
if ($this->parent) {
return $this->cache = array_merge($this->parent->squash($force), $this->data);
} else {
@@ -70,15 +102,19 @@ class HTMLPurifier_PropertyList
/**
* Returns the parent plist.
+ * @return HTMLPurifier_PropertyList
*/
- public function getParent() {
+ public function getParent()
+ {
return $this->parent;
}
/**
* Sets the parent plist.
+ * @param HTMLPurifier_PropertyList $plist Parent plist
*/
- public function setParent($plist) {
+ public function setParent($plist)
+ {
$this->parent = $plist;
}
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php b/classes/security/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php
index 8f250443e..15b330ea3 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php
@@ -6,27 +6,37 @@
class HTMLPurifier_PropertyListIterator extends FilterIterator
{
+ /**
+ * @type int
+ */
protected $l;
+ /**
+ * @type string
+ */
protected $filter;
/**
- * @param $data Array of data to iterate over
- * @param $filter Optional prefix to only allow values of
+ * @param Iterator $iterator Array of data to iterate over
+ * @param string $filter Optional prefix to only allow values of
*/
- public function __construct(Iterator $iterator, $filter = null) {
+ public function __construct(Iterator $iterator, $filter = null)
+ {
parent::__construct($iterator);
$this->l = strlen($filter);
$this->filter = $filter;
}
- public function accept() {
+ /**
+ * @return bool
+ */
+ public function accept()
+ {
$key = $this->getInnerIterator()->key();
- if( strncmp($key, $this->filter, $this->l) !== 0 ) {
+ if (strncmp($key, $this->filter, $this->l) !== 0) {
return false;
}
return true;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Queue.php b/classes/security/htmlpurifier/library/HTMLPurifier/Queue.php
new file mode 100644
index 000000000..f58db9042
--- /dev/null
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Queue.php
@@ -0,0 +1,56 @@
+input = $input;
+ $this->output = array();
+ }
+
+ /**
+ * Shifts an element off the front of the queue.
+ */
+ public function shift() {
+ if (empty($this->output)) {
+ $this->output = array_reverse($this->input);
+ $this->input = array();
+ }
+ if (empty($this->output)) {
+ return NULL;
+ }
+ return array_pop($this->output);
+ }
+
+ /**
+ * Pushes an element onto the front of the queue.
+ */
+ public function push($x) {
+ array_push($this->input, $x);
+ }
+
+ /**
+ * Checks if it's empty.
+ */
+ public function isEmpty() {
+ return empty($this->input) && empty($this->output);
+ }
+}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy.php b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy.php
index 246286521..e1ff3b72d 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy.php
@@ -15,12 +15,12 @@ abstract class HTMLPurifier_Strategy
/**
* Executes the strategy on the tokens.
*
- * @param $tokens Array of HTMLPurifier_Token objects to be operated on.
- * @param $config Configuration options
- * @returns Processed array of token objects.
+ * @param HTMLPurifier_Token[] $tokens Array of HTMLPurifier_Token objects to be operated on.
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return HTMLPurifier_Token[] Processed array of token objects.
*/
abstract public function execute($tokens, $config, $context);
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php
index 92aefd33e..d7d35ce7d 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php
@@ -8,16 +8,23 @@ abstract class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy
/**
* List of strategies to run tokens through.
+ * @type HTMLPurifier_Strategy[]
*/
protected $strategies = array();
- public function execute($tokens, $config, $context) {
+ /**
+ * @param HTMLPurifier_Token[] $tokens
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return HTMLPurifier_Token[]
+ */
+ public function execute($tokens, $config, $context)
+ {
foreach ($this->strategies as $strategy) {
$tokens = $strategy->execute($tokens, $config, $context);
}
return $tokens;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/Core.php b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/Core.php
index d90e15860..4414c17d6 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/Core.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/Core.php
@@ -5,14 +5,13 @@
*/
class HTMLPurifier_Strategy_Core extends HTMLPurifier_Strategy_Composite
{
-
- public function __construct() {
+ public function __construct()
+ {
$this->strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements();
$this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed();
$this->strategies[] = new HTMLPurifier_Strategy_FixNesting();
$this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes();
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php
index f81802391..6fa673db9 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php
@@ -10,12 +10,12 @@
* document type definitions, such as the chameleon nature of ins/del
* tags and global child exclusions.
*
- * The first major objective of this strategy is to iterate through all the
- * nodes (not tokens) of the list of tokens and determine whether or not
- * their children conform to the element's definition. If they do not, the
- * child definition may optionally supply an amended list of elements that
- * is valid or require that the entire node be deleted (and the previous
- * node rescanned).
+ * The first major objective of this strategy is to iterate through all
+ * the nodes and determine whether or not their children conform to the
+ * element's definition. If they do not, the child definition may
+ * optionally supply an amended list of elements that is valid or
+ * require that the entire node be deleted (and the previous node
+ * rescanned).
*
* The second objective is to ensure that explicitly excluded elements of
* an element do not appear in its children. Code that accomplishes this
@@ -25,24 +25,33 @@
* @note Whether or not unrecognized children are silently dropped or
* translated into text depends on the child definitions.
*
- * @todo Enable nodes to be bubbled out of the structure.
+ * @todo Enable nodes to be bubbled out of the structure. This is
+ * easier with our new algorithm.
*/
class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
{
- public function execute($tokens, $config, $context) {
+ /**
+ * @param HTMLPurifier_Token[] $tokens
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return array|HTMLPurifier_Token[]
+ */
+ public function execute($tokens, $config, $context)
+ {
+
//####################################################################//
// Pre-processing
+ // O(n) pass to convert to a tree, so that we can efficiently
+ // refer to substrings
+ $top_node = HTMLPurifier_Arborize::arborize($tokens, $config, $context);
+
// get a copy of the HTML definition
$definition = $config->getHTMLDefinition();
- // insert implicit "parent" node, will be removed at end.
- // DEFINITION CALL
- $parent_name = $definition->info_parent;
- array_unshift($tokens, new HTMLPurifier_Token_Start($parent_name));
- $tokens[] = new HTMLPurifier_Token_End($parent_name);
+ $excludes_enabled = !$config->get('Core.DisableExcludes');
// setup the context variable 'IsInline', for chameleon processing
// is 'false' when we are not inline, 'true' when it must always
@@ -57,272 +66,116 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
//####################################################################//
// Loop initialization
- // stack that contains the indexes of all parents,
- // $stack[count($stack)-1] being the current parent
- $stack = array();
-
// stack that contains all elements that are excluded
// it is organized by parent elements, similar to $stack,
// but it is only populated when an element with exclusions is
// processed, i.e. there won't be empty exclusions.
- $exclude_stack = array();
+ $exclude_stack = array($definition->info_parent_def->excludes);
// variable that contains the start token while we are processing
// nodes. This enables error reporting to do its job
- $start_token = false;
- $context->register('CurrentToken', $start_token);
+ $node = $top_node;
+ // dummy token
+ list($token, $d) = $node->toTokenPair();
+ $context->register('CurrentNode', $node);
+ $context->register('CurrentToken', $token);
//####################################################################//
// Loop
- // iterate through all start nodes. Determining the start node
- // is complicated so it has been omitted from the loop construct
- for ($i = 0, $size = count($tokens) ; $i < $size; ) {
+ // We need to implement a post-order traversal iteratively, to
+ // avoid running into stack space limits. This is pretty tricky
+ // to reason about, so we just manually stack-ify the recursive
+ // variant:
+ //
+ // function f($node) {
+ // foreach ($node->children as $child) {
+ // f($child);
+ // }
+ // validate($node);
+ // }
+ //
+ // Thus, we will represent a stack frame as array($node,
+ // $is_inline, stack of children)
+ // e.g. array_reverse($node->children) - already processed
+ // children.
- //################################################################//
- // Gather information on children
+ $parent_def = $definition->info_parent_def;
+ $stack = array(
+ array($top_node,
+ $parent_def->descendants_are_inline,
+ $parent_def->excludes, // exclusions
+ 0)
+ );
- // child token accumulator
- $child_tokens = array();
-
- // scroll to the end of this node, report number, and collect
- // all children
- for ($j = $i, $depth = 0; ; $j++) {
- if ($tokens[$j] instanceof HTMLPurifier_Token_Start) {
- $depth++;
- // skip token assignment on first iteration, this is the
- // token we currently are on
- if ($depth == 1) continue;
- } elseif ($tokens[$j] instanceof HTMLPurifier_Token_End) {
- $depth--;
- // skip token assignment on last iteration, this is the
- // end token of the token we're currently on
- if ($depth == 0) break;
+ while (!empty($stack)) {
+ list($node, $is_inline, $excludes, $ix) = array_pop($stack);
+ // recursive call
+ $go = false;
+ $def = empty($stack) ? $definition->info_parent_def : $definition->info[$node->name];
+ while (isset($node->children[$ix])) {
+ $child = $node->children[$ix++];
+ if ($child instanceof HTMLPurifier_Node_Element) {
+ $go = true;
+ $stack[] = array($node, $is_inline, $excludes, $ix);
+ $stack[] = array($child,
+ // ToDo: I don't think it matters if it's def or
+ // child_def, but double check this...
+ $is_inline || $def->descendants_are_inline,
+ empty($def->excludes) ? $excludes
+ : array_merge($excludes, $def->excludes),
+ 0);
+ break;
}
- $child_tokens[] = $tokens[$j];
- }
-
- // $i is index of start token
- // $j is index of end token
-
- $start_token = $tokens[$i]; // to make token available via CurrentToken
-
- //################################################################//
- // Gather information on parent
-
- // calculate parent information
- if ($count = count($stack)) {
- $parent_index = $stack[$count-1];
- $parent_name = $tokens[$parent_index]->name;
- if ($parent_index == 0) {
- $parent_def = $definition->info_parent_def;
+ };
+ if ($go) continue;
+ list($token, $d) = $node->toTokenPair();
+ // base case
+ if ($excludes_enabled && isset($excludes[$node->name])) {
+ $node->dead = true;
+ if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded');
+ } else {
+ // XXX I suppose it would be slightly more efficient to
+ // avoid the allocation here and have children
+ // strategies handle it
+ $children = array();
+ foreach ($node->children as $child) {
+ if (!$child->dead) $children[] = $child;
+ }
+ $result = $def->child->validateChildren($children, $config, $context);
+ if ($result === true) {
+ // nop
+ $node->children = $children;
+ } elseif ($result === false) {
+ $node->dead = true;
+ if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node removed');
} else {
- $parent_def = $definition->info[$parent_name];
- }
- } else {
- // processing as if the parent were the "root" node
- // unknown info, it won't be used anyway, in the future,
- // we may want to enforce one element only (this is
- // necessary for HTML Purifier to clean entire documents
- $parent_index = $parent_name = $parent_def = null;
- }
-
- // calculate context
- if ($is_inline === false) {
- // check if conditions make it inline
- if (!empty($parent_def) && $parent_def->descendants_are_inline) {
- $is_inline = $count - 1;
- }
- } else {
- // check if we're out of inline
- if ($count === $is_inline) {
- $is_inline = false;
- }
- }
-
- //################################################################//
- // Determine whether element is explicitly excluded SGML-style
-
- // determine whether or not element is excluded by checking all
- // parent exclusions. The array should not be very large, two
- // elements at most.
- $excluded = false;
- if (!empty($exclude_stack)) {
- foreach ($exclude_stack as $lookup) {
- if (isset($lookup[$tokens[$i]->name])) {
- $excluded = true;
- // no need to continue processing
- break;
+ $node->children = $result;
+ if ($e) {
+ // XXX This will miss mutations of internal nodes. Perhaps defer to the child validators
+ if (empty($result) && !empty($children)) {
+ $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed');
+ } else if ($result != $children) {
+ $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized');
+ }
}
}
}
-
- //################################################################//
- // Perform child validation
-
- if ($excluded) {
- // there is an exclusion, remove the entire node
- $result = false;
- $excludes = array(); // not used, but good to initialize anyway
- } else {
- // DEFINITION CALL
- if ($i === 0) {
- // special processing for the first node
- $def = $definition->info_parent_def;
- } else {
- $def = $definition->info[$tokens[$i]->name];
-
- }
-
- if (!empty($def->child)) {
- // have DTD child def validate children
- $result = $def->child->validateChildren(
- $child_tokens, $config, $context);
- } else {
- // weird, no child definition, get rid of everything
- $result = false;
- }
-
- // determine whether or not this element has any exclusions
- $excludes = $def->excludes;
- }
-
- // $result is now a bool or array
-
- //################################################################//
- // Process result by interpreting $result
-
- if ($result === true || $child_tokens === $result) {
- // leave the node as is
-
- // register start token as a parental node start
- $stack[] = $i;
-
- // register exclusions if there are any
- if (!empty($excludes)) $exclude_stack[] = $excludes;
-
- // move cursor to next possible start node
- $i++;
-
- } elseif($result === false) {
- // remove entire node
-
- if ($e) {
- if ($excluded) {
- $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded');
- } else {
- $e->send(E_ERROR, 'Strategy_FixNesting: Node removed');
- }
- }
-
- // calculate length of inner tokens and current tokens
- $length = $j - $i + 1;
-
- // perform removal
- array_splice($tokens, $i, $length);
-
- // update size
- $size -= $length;
-
- // there is no start token to register,
- // current node is now the next possible start node
- // unless it turns out that we need to do a double-check
-
- // this is a rought heuristic that covers 100% of HTML's
- // cases and 99% of all other cases. A child definition
- // that would be tricked by this would be something like:
- // ( | a b c) where it's all or nothing. Fortunately,
- // our current implementation claims that that case would
- // not allow empty, even if it did
- if (!$parent_def->child->allow_empty) {
- // we need to do a double-check
- $i = $parent_index;
- array_pop($stack);
- }
-
- // PROJECTED OPTIMIZATION: Process all children elements before
- // reprocessing parent node.
-
- } else {
- // replace node with $result
-
- // calculate length of inner tokens
- $length = $j - $i - 1;
-
- if ($e) {
- if (empty($result) && $length) {
- $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed');
- } else {
- $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized');
- }
- }
-
- // perform replacement
- array_splice($tokens, $i + 1, $length, $result);
-
- // update size
- $size -= $length;
- $size += count($result);
-
- // register start token as a parental node start
- $stack[] = $i;
-
- // register exclusions if there are any
- if (!empty($excludes)) $exclude_stack[] = $excludes;
-
- // move cursor to next possible start node
- $i++;
-
- }
-
- //################################################################//
- // Scroll to next start node
-
- // We assume, at this point, that $i is the index of the token
- // that is the first possible new start point for a node.
-
- // Test if the token indeed is a start tag, if not, move forward
- // and test again.
- $size = count($tokens);
- while ($i < $size and !$tokens[$i] instanceof HTMLPurifier_Token_Start) {
- if ($tokens[$i] instanceof HTMLPurifier_Token_End) {
- // pop a token index off the stack if we ended a node
- array_pop($stack);
- // pop an exclusion lookup off exclusion stack if
- // we ended node and that node had exclusions
- if ($i == 0 || $i == $size - 1) {
- // use specialized var if it's the super-parent
- $s_excludes = $definition->info_parent_def->excludes;
- } else {
- $s_excludes = $definition->info[$tokens[$i]->name]->excludes;
- }
- if ($s_excludes) {
- array_pop($exclude_stack);
- }
- }
- $i++;
- }
-
}
//####################################################################//
// Post-processing
- // remove implicit parent tokens at the beginning and end
- array_shift($tokens);
- array_pop($tokens);
-
// remove context variables
$context->destroy('IsInline');
+ $context->destroy('CurrentNode');
$context->destroy('CurrentToken');
//####################################################################//
// Return
- return $tokens;
-
+ return HTMLPurifier_Arborize::flatten($node, $config, $context);
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php
index c7aa1bb86..e389e0011 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php
@@ -16,66 +16,83 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
/**
* Array stream of tokens being processed.
+ * @type HTMLPurifier_Token[]
*/
protected $tokens;
/**
- * Current index in $tokens.
+ * Current token.
+ * @type HTMLPurifier_Token
*/
- protected $t;
+ protected $token;
+
+ /**
+ * Zipper managing the true state.
+ * @type HTMLPurifier_Zipper
+ */
+ protected $zipper;
/**
* Current nesting of elements.
+ * @type array
*/
protected $stack;
/**
* Injectors active in this stream processing.
+ * @type HTMLPurifier_Injector[]
*/
protected $injectors;
/**
* Current instance of HTMLPurifier_Config.
+ * @type HTMLPurifier_Config
*/
protected $config;
/**
* Current instance of HTMLPurifier_Context.
+ * @type HTMLPurifier_Context
*/
protected $context;
- public function execute($tokens, $config, $context) {
-
+ /**
+ * @param HTMLPurifier_Token[] $tokens
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return HTMLPurifier_Token[]
+ * @throws HTMLPurifier_Exception
+ */
+ public function execute($tokens, $config, $context)
+ {
$definition = $config->getHTMLDefinition();
// local variables
$generator = new HTMLPurifier_Generator($config, $context);
$escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
// used for autoclose early abortion
- $global_parent_allowed_elements = array();
- if (isset($definition->info[$definition->info_parent])) {
- // may be unset under testing circumstances
- $global_parent_allowed_elements = $definition->info[$definition->info_parent]->child->getAllowedElements($config);
- }
+ $global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements($config);
$e = $context->get('ErrorCollector', true);
- $t = false; // token index
$i = false; // injector index
- $token = false; // the current token
- $reprocess = false; // whether or not to reprocess the same token
+ list($zipper, $token) = HTMLPurifier_Zipper::fromArray($tokens);
+ if ($token === NULL) {
+ return array();
+ }
+ $reprocess = false; // whether or not to reprocess the same token
$stack = array();
// member variables
- $this->stack =& $stack;
- $this->t =& $t;
- $this->tokens =& $tokens;
- $this->config = $config;
+ $this->stack =& $stack;
+ $this->tokens =& $tokens;
+ $this->token =& $token;
+ $this->zipper =& $zipper;
+ $this->config = $config;
$this->context = $context;
// context variables
$context->register('CurrentNesting', $stack);
- $context->register('InputIndex', $t);
- $context->register('InputTokens', $tokens);
- $context->register('CurrentToken', $token);
+ $context->register('InputZipper', $zipper);
+ $context->register('CurrentToken', $token);
// -- begin INJECTOR --
@@ -87,9 +104,13 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
unset($injectors['Custom']); // special case
foreach ($injectors as $injector => $b) {
// XXX: Fix with a legitimate lookup table of enabled filters
- if (strpos($injector, '.') !== false) continue;
+ if (strpos($injector, '.') !== false) {
+ continue;
+ }
$injector = "HTMLPurifier_Injector_$injector";
- if (!$b) continue;
+ if (!$b) {
+ continue;
+ }
$this->injectors[] = new $injector;
}
foreach ($def_injectors as $injector) {
@@ -97,7 +118,9 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$this->injectors[] = $injector;
}
foreach ($custom_injectors as $injector) {
- if (!$injector) continue;
+ if (!$injector) {
+ continue;
+ }
if (is_string($injector)) {
$injector = "HTMLPurifier_Injector_$injector";
$injector = new $injector;
@@ -109,7 +132,9 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
// variables for performance reasons
foreach ($this->injectors as $ix => $injector) {
$error = $injector->prepare($config, $context);
- if (!$error) continue;
+ if (!$error) {
+ continue;
+ }
array_splice($this->injectors, $ix, 1); // rm the injector
trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING);
}
@@ -125,39 +150,40 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
// punt ($reprocess = true; continue;) and it does that for us.
// isset is in loop because $tokens size changes during loop exec
- for (
- $t = 0;
- $t == 0 || isset($tokens[$t - 1]);
- // only increment if we don't need to reprocess
- $reprocess ? $reprocess = false : $t++
- ) {
+ for (;;
+ // only increment if we don't need to reprocess
+ $reprocess ? $reprocess = false : $token = $zipper->next($token)) {
// check for a rewind
- if (is_int($i) && $i >= 0) {
+ if (is_int($i)) {
// possibility: disable rewinding if the current token has a
// rewind set on it already. This would offer protection from
// infinite loop, but might hinder some advanced rewinding.
- $rewind_to = $this->injectors[$i]->getRewind();
- if (is_int($rewind_to) && $rewind_to < $t) {
- if ($rewind_to < 0) $rewind_to = 0;
- while ($t > $rewind_to) {
- $t--;
- $prev = $tokens[$t];
+ $rewind_offset = $this->injectors[$i]->getRewindOffset();
+ if (is_int($rewind_offset)) {
+ for ($j = 0; $j < $rewind_offset; $j++) {
+ if (empty($zipper->front)) break;
+ $token = $zipper->prev($token);
// indicate that other injectors should not process this token,
// but we need to reprocess it
- unset($prev->skip[$i]);
- $prev->rewind = $i;
- if ($prev instanceof HTMLPurifier_Token_Start) array_pop($this->stack);
- elseif ($prev instanceof HTMLPurifier_Token_End) $this->stack[] = $prev->start;
+ unset($token->skip[$i]);
+ $token->rewind = $i;
+ if ($token instanceof HTMLPurifier_Token_Start) {
+ array_pop($this->stack);
+ } elseif ($token instanceof HTMLPurifier_Token_End) {
+ $this->stack[] = $token->start;
+ }
}
}
$i = false;
}
// handle case of document end
- if (!isset($tokens[$t])) {
+ if ($token === NULL) {
// kill processing if stack is empty
- if (empty($this->stack)) break;
+ if (empty($this->stack)) {
+ break;
+ }
// peek
$top_nesting = array_pop($this->stack);
@@ -169,26 +195,30 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
}
// append, don't splice, since this is the end
- $tokens[] = new HTMLPurifier_Token_End($top_nesting->name);
+ $token = new HTMLPurifier_Token_End($top_nesting->name);
// punt!
$reprocess = true;
continue;
}
- $token = $tokens[$t];
-
- //echo '
'; printTokens($tokens, $t); printTokens($this->stack);
+ //echo '
'; printZipper($zipper, $token);//printTokens($this->stack);
//flush();
// quick-check: if it's not a tag, no need to process
if (empty($token->is_tag)) {
if ($token instanceof HTMLPurifier_Token_Text) {
foreach ($this->injectors as $i => $injector) {
- if (isset($token->skip[$i])) continue;
- if ($token->rewind !== null && $token->rewind !== $i) continue;
- $injector->handleText($token);
- $this->processToken($token, $i);
+ if (isset($token->skip[$i])) {
+ continue;
+ }
+ if ($token->rewind !== null && $token->rewind !== $i) {
+ continue;
+ }
+ // XXX fuckup
+ $r = $token;
+ $injector->handleText($r);
+ $token = $this->processToken($r, $i);
$reprocess = true;
break;
}
@@ -207,12 +237,22 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$ok = false;
if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) {
// claims to be a start tag but is empty
- $token = new HTMLPurifier_Token_Empty($token->name, $token->attr, $token->line, $token->col, $token->armor);
+ $token = new HTMLPurifier_Token_Empty(
+ $token->name,
+ $token->attr,
+ $token->line,
+ $token->col,
+ $token->armor
+ );
$ok = true;
} elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) {
// claims to be empty but really is a start tag
- $this->swap(new HTMLPurifier_Token_End($token->name));
- $this->insertBefore(new HTMLPurifier_Token_Start($token->name, $token->attr, $token->line, $token->col, $token->armor));
+ // NB: this assignment is required
+ $old_token = $token;
+ $token = new HTMLPurifier_Token_End($token->name);
+ $token = $this->insertBefore(
+ new HTMLPurifier_Token_Start($old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor)
+ );
// punt (since we had to modify the input stream in a non-trivial way)
$reprocess = true;
continue;
@@ -241,31 +281,32 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$parent = array_pop($this->stack);
$this->stack[] = $parent;
+ $parent_def = null;
+ $parent_elements = null;
+ $autoclose = false;
if (isset($definition->info[$parent->name])) {
- $elements = $definition->info[$parent->name]->child->getAllowedElements($config);
- $autoclose = !isset($elements[$token->name]);
- } else {
- $autoclose = false;
+ $parent_def = $definition->info[$parent->name];
+ $parent_elements = $parent_def->child->getAllowedElements($config);
+ $autoclose = !isset($parent_elements[$token->name]);
}
if ($autoclose && $definition->info[$token->name]->wrap) {
- // Check if an element can be wrapped by another
- // element to make it valid in a context (for
+ // Check if an element can be wrapped by another
+ // element to make it valid in a context (for
// example,
needs a - in between)
$wrapname = $definition->info[$token->name]->wrap;
$wrapdef = $definition->info[$wrapname];
$elements = $wrapdef->child->getAllowedElements($config);
- $parent_elements = $definition->info[$parent->name]->child->getAllowedElements($config);
if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) {
$newtoken = new HTMLPurifier_Token_Start($wrapname);
- $this->insertBefore($newtoken);
+ $token = $this->insertBefore($newtoken);
$reprocess = true;
continue;
}
}
$carryover = false;
- if ($autoclose && $definition->info[$parent->name]->formatting) {
+ if ($autoclose && $parent_def->formatting) {
$carryover = true;
}
@@ -295,15 +336,6 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
// errors need to be updated
$new_token = new HTMLPurifier_Token_End($parent->name);
$new_token->start = $parent;
- if ($carryover) {
- $element = clone $parent;
- // [TagClosedAuto]
- $element->armor['MakeWellFormed_TagClosedError'] = true;
- $element->carryover = true;
- $this->processToken(array($new_token, $token, $element));
- } else {
- $this->insertBefore($new_token);
- }
// [TagClosedSuppress]
if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) {
if (!$carryover) {
@@ -312,8 +344,17 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent);
}
}
+ if ($carryover) {
+ $element = clone $parent;
+ // [TagClosedAuto]
+ $element->armor['MakeWellFormed_TagClosedError'] = true;
+ $element->carryover = true;
+ $token = $this->processToken(array($new_token, $token, $element));
+ } else {
+ $token = $this->insertBefore($new_token);
+ }
} else {
- $this->remove();
+ $token = $this->remove();
}
$reprocess = true;
continue;
@@ -325,20 +366,26 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
if ($ok) {
foreach ($this->injectors as $i => $injector) {
- if (isset($token->skip[$i])) continue;
- if ($token->rewind !== null && $token->rewind !== $i) continue;
- $injector->handleElement($token);
- $this->processToken($token, $i);
+ if (isset($token->skip[$i])) {
+ continue;
+ }
+ if ($token->rewind !== null && $token->rewind !== $i) {
+ continue;
+ }
+ $r = $token;
+ $injector->handleElement($r);
+ $token = $this->processToken($r, $i);
$reprocess = true;
break;
}
if (!$reprocess) {
// ah, nothing interesting happened; do normal processing
- $this->swap($token);
if ($token instanceof HTMLPurifier_Token_Start) {
$this->stack[] = $token;
} elseif ($token instanceof HTMLPurifier_Token_End) {
- throw new HTMLPurifier_Exception('Improper handling of end tag in start code; possible error in MakeWellFormed');
+ throw new HTMLPurifier_Exception(
+ 'Improper handling of end tag in start code; possible error in MakeWellFormed'
+ );
}
}
continue;
@@ -352,13 +399,15 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
// make sure that we have something open
if (empty($this->stack)) {
if ($escape_invalid_tags) {
- if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
- $this->swap(new HTMLPurifier_Token_Text(
- $generator->generateFromToken($token)
- ));
+ if ($e) {
+ $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
+ }
+ $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token));
} else {
- $this->remove();
- if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
+ if ($e) {
+ $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
+ }
+ $token = $this->remove();
}
$reprocess = true;
continue;
@@ -372,10 +421,15 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
if ($current_parent->name == $token->name) {
$token->start = $current_parent;
foreach ($this->injectors as $i => $injector) {
- if (isset($token->skip[$i])) continue;
- if ($token->rewind !== null && $token->rewind !== $i) continue;
- $injector->handleEnd($token);
- $this->processToken($token, $i);
+ if (isset($token->skip[$i])) {
+ continue;
+ }
+ if ($token->rewind !== null && $token->rewind !== $i) {
+ continue;
+ }
+ $r = $token;
+ $injector->handleEnd($r);
+ $token = $this->processToken($r, $i);
$this->stack[] = $current_parent;
$reprocess = true;
break;
@@ -403,13 +457,15 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
// we didn't find the tag, so remove
if ($skipped_tags === false) {
if ($escape_invalid_tags) {
- $this->swap(new HTMLPurifier_Token_Text(
- $generator->generateFromToken($token)
- ));
- if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
+ if ($e) {
+ $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
+ }
+ $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token));
} else {
- $this->remove();
- if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');
+ if ($e) {
+ $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');
+ }
+ $token = $this->remove();
}
$reprocess = true;
continue;
@@ -442,18 +498,17 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$replace[] = $element;
}
}
- $this->processToken($replace);
+ $token = $this->processToken($replace);
$reprocess = true;
continue;
}
- $context->destroy('CurrentNesting');
- $context->destroy('InputTokens');
- $context->destroy('InputIndex');
$context->destroy('CurrentToken');
+ $context->destroy('CurrentNesting');
+ $context->destroy('InputZipper');
- unset($this->injectors, $this->stack, $this->tokens, $this->t);
- return $tokens;
+ unset($this->injectors, $this->stack, $this->tokens);
+ return $zipper->toArray($token);
}
/**
@@ -472,25 +527,38 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
* If $token is an integer, that number of tokens (with the first token
* being the current one) will be deleted.
*
- * @param $token Token substitution value
- * @param $injector Injector that performed the substitution; default is if
+ * @param HTMLPurifier_Token|array|int|bool $token Token substitution value
+ * @param HTMLPurifier_Injector|int $injector Injector that performed the substitution; default is if
* this is not an injector related operation.
+ * @throws HTMLPurifier_Exception
*/
- protected function processToken($token, $injector = -1) {
-
+ protected function processToken($token, $injector = -1)
+ {
// normalize forms of token
- if (is_object($token)) $token = array(1, $token);
- if (is_int($token)) $token = array($token);
- if ($token === false) $token = array(1);
- if (!is_array($token)) throw new HTMLPurifier_Exception('Invalid token type from injector');
- if (!is_int($token[0])) array_unshift($token, 1);
- if ($token[0] === 0) throw new HTMLPurifier_Exception('Deleting zero tokens is not valid');
+ if (is_object($token)) {
+ $token = array(1, $token);
+ }
+ if (is_int($token)) {
+ $token = array($token);
+ }
+ if ($token === false) {
+ $token = array(1);
+ }
+ if (!is_array($token)) {
+ throw new HTMLPurifier_Exception('Invalid token type from injector');
+ }
+ if (!is_int($token[0])) {
+ array_unshift($token, 1);
+ }
+ if ($token[0] === 0) {
+ throw new HTMLPurifier_Exception('Deleting zero tokens is not valid');
+ }
// $token is now an array with the following form:
// array(number nodes to delete, new node 1, new node 2, ...)
$delete = array_shift($token);
- $old = array_splice($this->tokens, $this->t, $delete, $token);
+ list($old, $r) = $this->zipper->splice($this->token, $delete, $token);
if ($injector > -1) {
// determine appropriate skips
@@ -501,32 +569,32 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
}
}
+ return $r;
+
}
/**
* Inserts a token before the current token. Cursor now points to
* this token. You must reprocess after this.
+ * @param HTMLPurifier_Token $token
*/
- private function insertBefore($token) {
- array_splice($this->tokens, $this->t, 0, array($token));
+ private function insertBefore($token)
+ {
+ // NB not $this->zipper->insertBefore(), due to positioning
+ // differences
+ $splice = $this->zipper->splice($this->token, 0, array($token));
+
+ return $splice[1];
}
/**
* Removes current token. Cursor now points to new token occupying previously
* occupied space. You must reprocess after this.
*/
- private function remove() {
- array_splice($this->tokens, $this->t, 1);
+ private function remove()
+ {
+ return $this->zipper->delete();
}
-
- /**
- * Swap current token with new token. Cursor points to new token (no
- * change). You must reprocess after this.
- */
- private function swap($token) {
- $this->tokens[$this->t] = $token;
- }
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php
index bccaf14d3..1a8149ecc 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php
@@ -11,13 +11,20 @@
class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
{
- public function execute($tokens, $config, $context) {
+ /**
+ * @param HTMLPurifier_Token[] $tokens
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return array|HTMLPurifier_Token[]
+ */
+ public function execute($tokens, $config, $context)
+ {
$definition = $config->getHTMLDefinition();
$generator = new HTMLPurifier_Generator($config, $context);
$result = array();
$escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
- $remove_invalid_img = $config->get('Core.RemoveInvalidImg');
+ $remove_invalid_img = $config->get('Core.RemoveInvalidImg');
// currently only used to determine if comments should be kept
$trusted = $config->get('HTML.Trusted');
@@ -26,7 +33,7 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
$check_comments = $comment_lookup !== array() || $comment_regexp !== null;
$remove_script_contents = $config->get('Core.RemoveScriptContents');
- $hidden_elements = $config->get('Core.HiddenElements');
+ $hidden_elements = $config->get('Core.HiddenElements');
// remove script contents compatibility
if ($remove_script_contents === true) {
@@ -51,34 +58,31 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
$e =& $context->get('ErrorCollector');
}
- foreach($tokens as $token) {
+ foreach ($tokens as $token) {
if ($remove_until) {
if (empty($token->is_tag) || $token->name !== $remove_until) {
continue;
}
}
- if (!empty( $token->is_tag )) {
+ if (!empty($token->is_tag)) {
// DEFINITION CALL
// before any processing, try to transform the element
- if (
- isset($definition->info_tag_transform[$token->name])
- ) {
+ if (isset($definition->info_tag_transform[$token->name])) {
$original_name = $token->name;
// there is a transformation for this tag
// DEFINITION CALL
$token = $definition->
- info_tag_transform[$token->name]->
- transform($token, $config, $context);
- if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name);
+ info_tag_transform[$token->name]->transform($token, $config, $context);
+ if ($e) {
+ $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name);
+ }
}
if (isset($definition->info[$token->name])) {
-
// mostly everything's good, but
// we need to make sure required attributes are in order
- if (
- ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) &&
+ if (($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) &&
$definition->info[$token->name]->required_attr &&
($token->name != 'img' || $remove_invalid_img) // ensure config option still works
) {
@@ -91,7 +95,13 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
}
}
if (!$ok) {
- if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', $name);
+ if ($e) {
+ $e->send(
+ E_ERROR,
+ 'Strategy_RemoveForeignElements: Missing required attribute',
+ $name
+ );
+ }
continue;
}
$token->armor['ValidateAttributes'] = true;
@@ -105,7 +115,9 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
} elseif ($escape_invalid_tags) {
// invalid tag, generate HTML representation and insert in
- if ($e) $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
+ if ($e) {
+ $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
+ }
$token = new HTMLPurifier_Token_Text(
$generator->generateFromToken($token)
);
@@ -120,9 +132,13 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
} else {
$remove_until = false;
}
- if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed');
+ if ($e) {
+ $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed');
+ }
} else {
- if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed');
+ if ($e) {
+ $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed');
+ }
}
continue;
}
@@ -146,11 +162,15 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
$found_double_hyphen = true;
$token->data = str_replace('--', '-', $token->data);
}
- if ($trusted || !empty($comment_lookup[trim($token->data)]) || ($comment_regexp !== NULL && preg_match($comment_regexp, trim($token->data)))) {
+ if ($trusted || !empty($comment_lookup[trim($token->data)]) ||
+ ($comment_regexp !== null && preg_match($comment_regexp, trim($token->data)))) {
// OK good
if ($e) {
if ($trailing_hyphen) {
- $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed');
+ $e->send(
+ E_NOTICE,
+ 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed'
+ );
}
if ($found_double_hyphen) {
$e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed');
@@ -164,7 +184,9 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
}
} else {
// strip comments
- if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
+ if ($e) {
+ $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
+ }
continue;
}
} elseif ($token instanceof HTMLPurifier_Token_Text) {
@@ -177,12 +199,9 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
// we removed tokens until the end, throw error
$e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until);
}
-
$context->destroy('CurrentToken');
-
return $result;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php
index c3328a9d4..fbb3d27c8 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php
@@ -7,8 +7,14 @@
class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
{
- public function execute($tokens, $config, $context) {
-
+ /**
+ * @param HTMLPurifier_Token[] $tokens
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return HTMLPurifier_Token[]
+ */
+ public function execute($tokens, $config, $context)
+ {
// setup validator
$validator = new HTMLPurifier_AttrValidator();
@@ -19,21 +25,21 @@ class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
// only process tokens that have attributes,
// namely start and empty tags
- if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) continue;
+ if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) {
+ continue;
+ }
// skip tokens that are armored
- if (!empty($token->armor['ValidateAttributes'])) continue;
+ if (!empty($token->armor['ValidateAttributes'])) {
+ continue;
+ }
// note that we have no facilities here for removing tokens
$validator->validateToken($token, $config, $context);
-
- $tokens[$key] = $token; // for PHP 4
}
$context->destroy('CurrentToken');
-
return $tokens;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/StringHash.php b/classes/security/htmlpurifier/library/HTMLPurifier/StringHash.php
index 62085c5c2..c07370197 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/StringHash.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/StringHash.php
@@ -10,28 +10,36 @@
*/
class HTMLPurifier_StringHash extends ArrayObject
{
+ /**
+ * @type array
+ */
protected $accessed = array();
/**
* Retrieves a value, and logs the access.
+ * @param mixed $index
+ * @return mixed
*/
- public function offsetGet($index) {
+ public function offsetGet($index)
+ {
$this->accessed[$index] = true;
return parent::offsetGet($index);
}
/**
* Returns a lookup array of all array indexes that have been accessed.
- * @return Array in form array($index => true).
+ * @return array in form array($index => true).
*/
- public function getAccessed() {
+ public function getAccessed()
+ {
return $this->accessed;
}
/**
* Resets the access array.
*/
- public function resetAccessed() {
+ public function resetAccessed()
+ {
$this->accessed = array();
}
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/StringHashParser.php b/classes/security/htmlpurifier/library/HTMLPurifier/StringHashParser.php
index f3e70c712..7c73f8083 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/StringHashParser.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/StringHashParser.php
@@ -28,15 +28,25 @@
class HTMLPurifier_StringHashParser
{
+ /**
+ * @type string
+ */
public $default = 'ID';
/**
* Parses a file that contains a single string-hash.
+ * @param string $file
+ * @return array
*/
- public function parseFile($file) {
- if (!file_exists($file)) return false;
+ public function parseFile($file)
+ {
+ if (!file_exists($file)) {
+ return false;
+ }
$fh = fopen($file, 'r');
- if (!$fh) return false;
+ if (!$fh) {
+ return false;
+ }
$ret = $this->parseHandle($fh);
fclose($fh);
return $ret;
@@ -44,12 +54,19 @@ class HTMLPurifier_StringHashParser
/**
* Parses a file that contains multiple string-hashes delimited by '----'
+ * @param string $file
+ * @return array
*/
- public function parseMultiFile($file) {
- if (!file_exists($file)) return false;
+ public function parseMultiFile($file)
+ {
+ if (!file_exists($file)) {
+ return false;
+ }
$ret = array();
$fh = fopen($file, 'r');
- if (!$fh) return false;
+ if (!$fh) {
+ return false;
+ }
while (!feof($fh)) {
$ret[] = $this->parseHandle($fh);
}
@@ -62,26 +79,36 @@ class HTMLPurifier_StringHashParser
* @note While it's possible to simulate in-memory parsing by using
* custom stream wrappers, if such a use-case arises we should
* factor out the file handle into its own class.
- * @param $fh File handle with pointer at start of valid string-hash
+ * @param resource $fh File handle with pointer at start of valid string-hash
* block.
+ * @return array
*/
- protected function parseHandle($fh) {
+ protected function parseHandle($fh)
+ {
$state = false;
$single = false;
$ret = array();
do {
$line = fgets($fh);
- if ($line === false) break;
+ if ($line === false) {
+ break;
+ }
$line = rtrim($line, "\n\r");
- if (!$state && $line === '') continue;
- if ($line === '----') break;
+ if (!$state && $line === '') {
+ continue;
+ }
+ if ($line === '----') {
+ break;
+ }
if (strncmp('--#', $line, 3) === 0) {
// Comment
continue;
} elseif (strncmp('--', $line, 2) === 0) {
// Multiline declaration
$state = trim($line, '- ');
- if (!isset($ret[$state])) $ret[$state] = '';
+ if (!isset($ret[$state])) {
+ $ret[$state] = '';
+ }
continue;
} elseif (!$state) {
$single = true;
@@ -104,7 +131,6 @@ class HTMLPurifier_StringHashParser
} while (!feof($fh));
return $ret;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform.php b/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform.php
index 210a44721..7b8d83343 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform.php
@@ -8,14 +8,15 @@ abstract class HTMLPurifier_TagTransform
/**
* Tag name to transform the tag to.
+ * @type string
*/
public $transform_to;
/**
* Transforms the obsolete tag into the valid tag.
- * @param $tag Tag to be transformed.
- * @param $config Mandatory HTMLPurifier_Config object
- * @param $context Mandatory HTMLPurifier_Context object
+ * @param HTMLPurifier_Token_Tag $tag Tag to be transformed.
+ * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object
+ * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object
*/
abstract public function transform($tag, $config, $context);
@@ -23,14 +24,14 @@ abstract class HTMLPurifier_TagTransform
* Prepends CSS properties to the style attribute, creating the
* attribute if it doesn't exist.
* @warning Copied over from AttrTransform, be sure to keep in sync
- * @param $attr Attribute array to process (passed by reference)
- * @param $css CSS to prepend
+ * @param array $attr Attribute array to process (passed by reference)
+ * @param string $css CSS to prepend
*/
- protected function prependCSS(&$attr, $css) {
+ protected function prependCSS(&$attr, $css)
+ {
$attr['style'] = isset($attr['style']) ? $attr['style'] : '';
$attr['style'] = $css . $attr['style'];
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php b/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php
index 9db2db795..7853d90bc 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php
@@ -17,9 +17,14 @@
*/
class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
{
-
+ /**
+ * @type string
+ */
public $transform_to = 'span';
+ /**
+ * @type array
+ */
protected $_size_lookup = array(
'0' => 'xx-small',
'1' => 'xx-small',
@@ -37,8 +42,14 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
'+4' => '300%'
);
- public function transform($tag, $config, $context) {
-
+ /**
+ * @param HTMLPurifier_Token_Tag $tag
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return HTMLPurifier_Token_End|string
+ */
+ public function transform($tag, $config, $context)
+ {
if ($tag instanceof HTMLPurifier_Token_End) {
$new_tag = clone $tag;
$new_tag->name = $this->transform_to;
@@ -65,17 +76,23 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
// normalize large numbers
if ($attr['size'] !== '') {
if ($attr['size']{0} == '+' || $attr['size']{0} == '-') {
- $size = (int) $attr['size'];
- if ($size < -2) $attr['size'] = '-2';
- if ($size > 4) $attr['size'] = '+4';
+ $size = (int)$attr['size'];
+ if ($size < -2) {
+ $attr['size'] = '-2';
+ }
+ if ($size > 4) {
+ $attr['size'] = '+4';
+ }
} else {
- $size = (int) $attr['size'];
- if ($size > 7) $attr['size'] = '7';
+ $size = (int)$attr['size'];
+ if ($size > 7) {
+ $attr['size'] = '7';
+ }
}
}
if (isset($this->_size_lookup[$attr['size']])) {
$prepend_style .= 'font-size:' .
- $this->_size_lookup[$attr['size']] . ';';
+ $this->_size_lookup[$attr['size']] . ';';
}
unset($attr['size']);
}
@@ -91,7 +108,6 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
$new_tag->attr = $attr;
return $new_tag;
-
}
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php b/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php
index 0e36130f2..71bf10b91 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php
@@ -7,19 +7,29 @@
*/
class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform
{
-
+ /**
+ * @type string
+ */
protected $style;
/**
- * @param $transform_to Tag name to transform to.
- * @param $style CSS style to add to the tag
+ * @param string $transform_to Tag name to transform to.
+ * @param string $style CSS style to add to the tag
*/
- public function __construct($transform_to, $style = null) {
+ public function __construct($transform_to, $style = null)
+ {
$this->transform_to = $transform_to;
$this->style = $style;
}
- public function transform($tag, $config, $context) {
+ /**
+ * @param HTMLPurifier_Token_Tag $tag
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return string
+ */
+ public function transform($tag, $config, $context)
+ {
$new_tag = clone $tag;
$new_tag->name = $this->transform_to;
if (!is_null($this->style) &&
@@ -29,7 +39,6 @@ class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform
}
return $new_tag;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Token.php b/classes/security/htmlpurifier/library/HTMLPurifier/Token.php
index 7900e6cb1..85b85e072 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Token.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Token.php
@@ -3,55 +3,98 @@
/**
* Abstract base token class that all others inherit from.
*/
-class HTMLPurifier_Token {
- public $line; /**< Line number node was on in source document. Null if unknown. */
- public $col; /**< Column of line node was on in source document. Null if unknown. */
+abstract class HTMLPurifier_Token
+{
+ /**
+ * Line number node was on in source document. Null if unknown.
+ * @type int
+ */
+ public $line;
+
+ /**
+ * Column of line node was on in source document. Null if unknown.
+ * @type int
+ */
+ public $col;
/**
* Lookup array of processing that this token is exempt from.
* Currently, valid values are "ValidateAttributes" and
* "MakeWellFormed_TagClosedError"
+ * @type array
*/
public $armor = array();
/**
* Used during MakeWellFormed.
+ * @type
*/
public $skip;
+
+ /**
+ * @type
+ */
public $rewind;
+
+ /**
+ * @type
+ */
public $carryover;
- public function __get($n) {
- if ($n === 'type') {
- trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
- switch (get_class($this)) {
- case 'HTMLPurifier_Token_Start': return 'start';
- case 'HTMLPurifier_Token_Empty': return 'empty';
- case 'HTMLPurifier_Token_End': return 'end';
- case 'HTMLPurifier_Token_Text': return 'text';
- case 'HTMLPurifier_Token_Comment': return 'comment';
- default: return null;
+ /**
+ * @param string $n
+ * @return null|string
+ */
+ public function __get($n)
+ {
+ if ($n === 'type') {
+ trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
+ switch (get_class($this)) {
+ case 'HTMLPurifier_Token_Start':
+ return 'start';
+ case 'HTMLPurifier_Token_Empty':
+ return 'empty';
+ case 'HTMLPurifier_Token_End':
+ return 'end';
+ case 'HTMLPurifier_Token_Text':
+ return 'text';
+ case 'HTMLPurifier_Token_Comment':
+ return 'comment';
+ default:
+ return null;
+ }
}
- }
}
/**
* Sets the position of the token in the source document.
+ * @param int $l
+ * @param int $c
*/
- public function position($l = null, $c = null) {
+ public function position($l = null, $c = null)
+ {
$this->line = $l;
- $this->col = $c;
+ $this->col = $c;
}
/**
* Convenience function for DirectLex settings line/col position.
+ * @param int $l
+ * @param int $c
*/
- public function rawPosition($l, $c) {
- if ($c === -1) $l++;
+ public function rawPosition($l, $c)
+ {
+ if ($c === -1) {
+ $l++;
+ }
$this->line = $l;
- $this->col = $c;
+ $this->col = $c;
}
+ /**
+ * Converts a token into its corresponding node.
+ */
+ abstract public function toNode();
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Comment.php b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Comment.php
index dc6bdcabb..23453c705 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Comment.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Comment.php
@@ -5,17 +5,33 @@
*/
class HTMLPurifier_Token_Comment extends HTMLPurifier_Token
{
- public $data; /**< Character data within comment. */
+ /**
+ * Character data within comment.
+ * @type string
+ */
+ public $data;
+
+ /**
+ * @type bool
+ */
public $is_whitespace = true;
+
/**
* Transparent constructor.
*
- * @param $data String comment data.
+ * @param string $data String comment data.
+ * @param int $line
+ * @param int $col
*/
- public function __construct($data, $line = null, $col = null) {
+ public function __construct($data, $line = null, $col = null)
+ {
$this->data = $data;
$this->line = $line;
- $this->col = $col;
+ $this->col = $col;
+ }
+
+ public function toNode() {
+ return new HTMLPurifier_Node_Comment($this->data, $this->line, $this->col);
}
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Empty.php b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Empty.php
index 2a82b47ad..78a95f555 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Empty.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Empty.php
@@ -5,7 +5,11 @@
*/
class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag
{
-
+ public function toNode() {
+ $n = parent::toNode();
+ $n->empty = true;
+ return $n;
+ }
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Token/End.php b/classes/security/htmlpurifier/library/HTMLPurifier/Token/End.php
index 353e79daf..59b38fdc5 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Token/End.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Token/End.php
@@ -10,10 +10,15 @@
class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag
{
/**
- * Token that started this node. Added by MakeWellFormed. Please
- * do not edit this!
+ * Token that started this node.
+ * Added by MakeWellFormed. Please do not edit this!
+ * @type HTMLPurifier_Token
*/
public $start;
+
+ public function toNode() {
+ throw new Exception("HTMLPurifier_Token_End->toNode not supported!");
+ }
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Start.php b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Start.php
index e0e14fc62..019f317ad 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Start.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Start.php
@@ -5,7 +5,6 @@
*/
class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag
{
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Tag.php b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Tag.php
index f4d8f640e..d643fa64e 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Tag.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Tag.php
@@ -3,13 +3,14 @@
/**
* Abstract class of a tag token (start, end or empty), and its behavior.
*/
-class HTMLPurifier_Token_Tag extends HTMLPurifier_Token
+abstract class HTMLPurifier_Token_Tag extends HTMLPurifier_Token
{
/**
* Static bool marker that indicates the class is a tag.
*
* This allows us to check objects with !empty($obj->is_tag)
* without having to use a function call is_a().
+ * @type bool
*/
public $is_tag = true;
@@ -19,21 +20,27 @@ class HTMLPurifier_Token_Tag extends HTMLPurifier_Token
* @note Strictly speaking, XML tags are case sensitive, so we shouldn't
* be lower-casing them, but these tokens cater to HTML tags, which are
* insensitive.
+ * @type string
*/
public $name;
/**
* Associative array of the tag's attributes.
+ * @type array
*/
public $attr = array();
/**
* Non-overloaded constructor, which lower-cases passed tag name.
*
- * @param $name String name.
- * @param $attr Associative array of attributes.
+ * @param string $name String name.
+ * @param array $attr Associative array of attributes.
+ * @param int $line
+ * @param int $col
+ * @param array $armor
*/
- public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) {
+ public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array())
+ {
$this->name = ctype_lower($name) ? $name : strtolower($name);
foreach ($attr as $key => $value) {
// normalization only necessary when key is not lowercase
@@ -49,9 +56,13 @@ class HTMLPurifier_Token_Tag extends HTMLPurifier_Token
}
$this->attr = $attr;
$this->line = $line;
- $this->col = $col;
+ $this->col = $col;
$this->armor = $armor;
}
+
+ public function toNode() {
+ return new HTMLPurifier_Node_Element($this->name, $this->attr, $this->line, $this->col, $this->armor);
+ }
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Text.php b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Text.php
index 82efd823d..f26a1c211 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/Token/Text.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Token/Text.php
@@ -12,22 +12,42 @@
class HTMLPurifier_Token_Text extends HTMLPurifier_Token
{
- public $name = '#PCDATA'; /**< PCDATA tag name compatible with DTD. */
- public $data; /**< Parsed character data of text. */
- public $is_whitespace; /**< Bool indicating if node is whitespace. */
+ /**
+ * @type string
+ */
+ public $name = '#PCDATA';
+ /**< PCDATA tag name compatible with DTD. */
+
+ /**
+ * @type string
+ */
+ public $data;
+ /**< Parsed character data of text. */
+
+ /**
+ * @type bool
+ */
+ public $is_whitespace;
+
+ /**< Bool indicating if node is whitespace. */
/**
* Constructor, accepts data and determines if it is whitespace.
- *
- * @param $data String parsed character data.
+ * @param string $data String parsed character data.
+ * @param int $line
+ * @param int $col
*/
- public function __construct($data, $line = null, $col = null) {
+ public function __construct($data, $line = null, $col = null)
+ {
$this->data = $data;
$this->is_whitespace = ctype_space($data);
$this->line = $line;
- $this->col = $col;
+ $this->col = $col;
}
+ public function toNode() {
+ return new HTMLPurifier_Node_Text($this->data, $this->is_whitespace, $this->line, $this->col);
+ }
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/TokenFactory.php b/classes/security/htmlpurifier/library/HTMLPurifier/TokenFactory.php
index 7cf48fb41..dea2446b9 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/TokenFactory.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/TokenFactory.php
@@ -13,32 +13,53 @@
*/
class HTMLPurifier_TokenFactory
{
+ // p stands for prototype
/**
- * Prototypes that will be cloned.
- * @private
+ * @type HTMLPurifier_Token_Start
*/
- // p stands for prototype
- private $p_start, $p_end, $p_empty, $p_text, $p_comment;
+ private $p_start;
+
+ /**
+ * @type HTMLPurifier_Token_End
+ */
+ private $p_end;
+
+ /**
+ * @type HTMLPurifier_Token_Empty
+ */
+ private $p_empty;
+
+ /**
+ * @type HTMLPurifier_Token_Text
+ */
+ private $p_text;
+
+ /**
+ * @type HTMLPurifier_Token_Comment
+ */
+ private $p_comment;
/**
* Generates blank prototypes for cloning.
*/
- public function __construct() {
- $this->p_start = new HTMLPurifier_Token_Start('', array());
- $this->p_end = new HTMLPurifier_Token_End('');
- $this->p_empty = new HTMLPurifier_Token_Empty('', array());
- $this->p_text = new HTMLPurifier_Token_Text('');
- $this->p_comment= new HTMLPurifier_Token_Comment('');
+ public function __construct()
+ {
+ $this->p_start = new HTMLPurifier_Token_Start('', array());
+ $this->p_end = new HTMLPurifier_Token_End('');
+ $this->p_empty = new HTMLPurifier_Token_Empty('', array());
+ $this->p_text = new HTMLPurifier_Token_Text('');
+ $this->p_comment = new HTMLPurifier_Token_Comment('');
}
/**
* Creates a HTMLPurifier_Token_Start.
- * @param $name Tag name
- * @param $attr Associative array of attributes
- * @return Generated HTMLPurifier_Token_Start
+ * @param string $name Tag name
+ * @param array $attr Associative array of attributes
+ * @return HTMLPurifier_Token_Start Generated HTMLPurifier_Token_Start
*/
- public function createStart($name, $attr = array()) {
+ public function createStart($name, $attr = array())
+ {
$p = clone $this->p_start;
$p->__construct($name, $attr);
return $p;
@@ -46,10 +67,11 @@ class HTMLPurifier_TokenFactory
/**
* Creates a HTMLPurifier_Token_End.
- * @param $name Tag name
- * @return Generated HTMLPurifier_Token_End
+ * @param string $name Tag name
+ * @return HTMLPurifier_Token_End Generated HTMLPurifier_Token_End
*/
- public function createEnd($name) {
+ public function createEnd($name)
+ {
$p = clone $this->p_end;
$p->__construct($name);
return $p;
@@ -57,11 +79,12 @@ class HTMLPurifier_TokenFactory
/**
* Creates a HTMLPurifier_Token_Empty.
- * @param $name Tag name
- * @param $attr Associative array of attributes
- * @return Generated HTMLPurifier_Token_Empty
+ * @param string $name Tag name
+ * @param array $attr Associative array of attributes
+ * @return HTMLPurifier_Token_Empty Generated HTMLPurifier_Token_Empty
*/
- public function createEmpty($name, $attr = array()) {
+ public function createEmpty($name, $attr = array())
+ {
$p = clone $this->p_empty;
$p->__construct($name, $attr);
return $p;
@@ -69,10 +92,11 @@ class HTMLPurifier_TokenFactory
/**
* Creates a HTMLPurifier_Token_Text.
- * @param $data Data of text token
- * @return Generated HTMLPurifier_Token_Text
+ * @param string $data Data of text token
+ * @return HTMLPurifier_Token_Text Generated HTMLPurifier_Token_Text
*/
- public function createText($data) {
+ public function createText($data)
+ {
$p = clone $this->p_text;
$p->__construct($data);
return $p;
@@ -80,15 +104,15 @@ class HTMLPurifier_TokenFactory
/**
* Creates a HTMLPurifier_Token_Comment.
- * @param $data Data of comment token
- * @return Generated HTMLPurifier_Token_Comment
+ * @param string $data Data of comment token
+ * @return HTMLPurifier_Token_Comment Generated HTMLPurifier_Token_Comment
*/
- public function createComment($data) {
+ public function createComment($data)
+ {
$p = clone $this->p_comment;
$p->__construct($data);
return $p;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URI.php b/classes/security/htmlpurifier/library/HTMLPurifier/URI.php
index f158ef5e3..a5e7ae298 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URI.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URI.php
@@ -10,17 +10,57 @@
*/
class HTMLPurifier_URI
{
-
- public $scheme, $userinfo, $host, $port, $path, $query, $fragment;
+ /**
+ * @type string
+ */
+ public $scheme;
/**
+ * @type string
+ */
+ public $userinfo;
+
+ /**
+ * @type string
+ */
+ public $host;
+
+ /**
+ * @type int
+ */
+ public $port;
+
+ /**
+ * @type string
+ */
+ public $path;
+
+ /**
+ * @type string
+ */
+ public $query;
+
+ /**
+ * @type string
+ */
+ public $fragment;
+
+ /**
+ * @param string $scheme
+ * @param string $userinfo
+ * @param string $host
+ * @param int $port
+ * @param string $path
+ * @param string $query
+ * @param string $fragment
* @note Automatically normalizes scheme and port
*/
- public function __construct($scheme, $userinfo, $host, $port, $path, $query, $fragment) {
+ public function __construct($scheme, $userinfo, $host, $port, $path, $query, $fragment)
+ {
$this->scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme);
$this->userinfo = $userinfo;
$this->host = $host;
- $this->port = is_null($port) ? $port : (int) $port;
+ $this->port = is_null($port) ? $port : (int)$port;
$this->path = $path;
$this->query = $query;
$this->fragment = $fragment;
@@ -28,15 +68,18 @@ class HTMLPurifier_URI
/**
* Retrieves a scheme object corresponding to the URI's scheme/default
- * @param $config Instance of HTMLPurifier_Config
- * @param $context Instance of HTMLPurifier_Context
- * @return Scheme object appropriate for validating this URI
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return HTMLPurifier_URIScheme Scheme object appropriate for validating this URI
*/
- public function getSchemeObj($config, $context) {
+ public function getSchemeObj($config, $context)
+ {
$registry = HTMLPurifier_URISchemeRegistry::instance();
if ($this->scheme !== null) {
$scheme_obj = $registry->getScheme($this->scheme, $config, $context);
- if (!$scheme_obj) return false; // invalid scheme, clean it out
+ if (!$scheme_obj) {
+ return false;
+ } // invalid scheme, clean it out
} else {
// no scheme: retrieve the default one
$def = $config->getDefinition('URI');
@@ -56,12 +99,12 @@ class HTMLPurifier_URI
/**
* Generic validation method applicable for all schemes. May modify
* this URI in order to get it into a compliant form.
- * @param $config Instance of HTMLPurifier_Config
- * @param $context Instance of HTMLPurifier_Context
- * @return True if validation/filtering succeeds, false if failure
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool True if validation/filtering succeeds, false if failure
*/
- public function validate($config, $context) {
-
+ public function validate($config, $context)
+ {
// ABNF definitions from RFC 3986
$chars_sub_delims = '!$&\'()*+,;=';
$chars_gen_delims = ':/?#[]@';
@@ -71,7 +114,9 @@ class HTMLPurifier_URI
if (!is_null($this->host)) {
$host_def = new HTMLPurifier_AttrDef_URI_Host();
$this->host = $host_def->validate($this->host, $config, $context);
- if ($this->host === false) $this->host = null;
+ if ($this->host === false) {
+ $this->host = null;
+ }
}
// validate scheme
@@ -97,11 +142,12 @@ class HTMLPurifier_URI
// validate port
if (!is_null($this->port)) {
- if ($this->port < 1 || $this->port > 65535) $this->port = null;
+ if ($this->port < 1 || $this->port > 65535) {
+ $this->port = null;
+ }
}
// validate path
- $path_parts = array();
$segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/');
if (!is_null($this->host)) { // this catches $this->host === ''
// path-abempty (hier and relative)
@@ -161,16 +207,15 @@ class HTMLPurifier_URI
if (!is_null($this->fragment)) {
$this->fragment = $qf_encoder->encode($this->fragment);
}
-
return true;
-
}
/**
* Convert URI back to string
- * @return String URI appropriate for output
+ * @return string URI appropriate for output
*/
- public function toString() {
+ public function toString()
+ {
// reconstruct authority
$authority = null;
// there is a rendering difference between a null authority
@@ -178,9 +223,13 @@ class HTMLPurifier_URI
// (http:///foo-bar).
if (!is_null($this->host)) {
$authority = '';
- if(!is_null($this->userinfo)) $authority .= $this->userinfo . '@';
+ if (!is_null($this->userinfo)) {
+ $authority .= $this->userinfo . '@';
+ }
$authority .= $this->host;
- if(!is_null($this->port)) $authority .= ':' . $this->port;
+ if (!is_null($this->port)) {
+ $authority .= ':' . $this->port;
+ }
}
// Reconstruct the result
@@ -190,11 +239,19 @@ class HTMLPurifier_URI
// differently than http:///foo), so unfortunately we have to
// defer to the schemes to do the right thing.
$result = '';
- if (!is_null($this->scheme)) $result .= $this->scheme . ':';
- if (!is_null($authority)) $result .= '//' . $authority;
+ if (!is_null($this->scheme)) {
+ $result .= $this->scheme . ':';
+ }
+ if (!is_null($authority)) {
+ $result .= '//' . $authority;
+ }
$result .= $this->path;
- if (!is_null($this->query)) $result .= '?' . $this->query;
- if (!is_null($this->fragment)) $result .= '#' . $this->fragment;
+ if (!is_null($this->query)) {
+ $result .= '?' . $this->query;
+ }
+ if (!is_null($this->fragment)) {
+ $result .= '#' . $this->fragment;
+ }
return $result;
}
@@ -207,11 +264,19 @@ class HTMLPurifier_URI
* Note that this does not do any scheme checking, so it is mostly
* only appropriate for metadata that doesn't care about protocol
* security. isBenign is probably what you actually want.
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
*/
- public function isLocal($config, $context) {
- if ($this->host === null) return true;
+ public function isLocal($config, $context)
+ {
+ if ($this->host === null) {
+ return true;
+ }
$uri_def = $config->getDefinition('URI');
- if ($uri_def->host === $this->host) return true;
+ if ($uri_def->host === $this->host) {
+ return true;
+ }
return false;
}
@@ -221,12 +286,20 @@ class HTMLPurifier_URI
*
* - It is a local URL (isLocal), and
* - It has a equal or better level of security
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
*/
- public function isBenign($config, $context) {
- if (!$this->isLocal($config, $context)) return false;
+ public function isBenign($config, $context)
+ {
+ if (!$this->isLocal($config, $context)) {
+ return false;
+ }
$scheme_obj = $this->getSchemeObj($config, $context);
- if (!$scheme_obj) return false; // conservative approach
+ if (!$scheme_obj) {
+ return false;
+ } // conservative approach
$current_scheme_obj = $config->getDefinition('URI')->getDefaultScheme($config, $context);
if ($current_scheme_obj->secure) {
@@ -236,7 +309,6 @@ class HTMLPurifier_URI
}
return true;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIDefinition.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIDefinition.php
index 40e57bb7d..e0bd8bcca 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIDefinition.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIDefinition.php
@@ -23,20 +23,24 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
*/
public $defaultScheme;
- public function __construct() {
+ public function __construct()
+ {
$this->registerFilter(new HTMLPurifier_URIFilter_DisableExternal());
$this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources());
+ $this->registerFilter(new HTMLPurifier_URIFilter_DisableResources());
$this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist());
$this->registerFilter(new HTMLPurifier_URIFilter_SafeIframe());
$this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute());
$this->registerFilter(new HTMLPurifier_URIFilter_Munge());
}
- public function registerFilter($filter) {
+ public function registerFilter($filter)
+ {
$this->registeredFilters[$filter->name] = $filter;
}
- public function addFilter($filter, $config) {
+ public function addFilter($filter, $config)
+ {
$r = $filter->prepare($config);
if ($r === false) return; // null is ok, for backwards compat
if ($filter->post) {
@@ -46,12 +50,14 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
}
}
- protected function doSetup($config) {
+ protected function doSetup($config)
+ {
$this->setupMemberVariables($config);
$this->setupFilters($config);
}
- protected function setupFilters($config) {
+ protected function setupFilters($config)
+ {
foreach ($this->registeredFilters as $name => $filter) {
if ($filter->always_load) {
$this->addFilter($filter, $config);
@@ -65,7 +71,8 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
unset($this->registeredFilters);
}
- protected function setupMemberVariables($config) {
+ protected function setupMemberVariables($config)
+ {
$this->host = $config->get('URI.Host');
$base_uri = $config->get('URI.Base');
if (!is_null($base_uri)) {
@@ -77,11 +84,13 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme');
}
- public function getDefaultScheme($config, $context) {
+ public function getDefaultScheme($config, $context)
+ {
return HTMLPurifier_URISchemeRegistry::instance()->getScheme($this->defaultScheme, $config, $context);
}
- public function filter(&$uri, $config, $context) {
+ public function filter(&$uri, $config, $context)
+ {
foreach ($this->filters as $name => $f) {
$result = $f->filter($uri, $config, $context);
if (!$result) return false;
@@ -89,7 +98,8 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
return true;
}
- public function postFilter(&$uri, $config, $context) {
+ public function postFilter(&$uri, $config, $context)
+ {
foreach ($this->postFilters as $name => $f) {
$result = $f->filter($uri, $config, $context);
if (!$result) return false;
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter.php
index 6a1b0b08e..09724e9f4 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter.php
@@ -29,39 +29,46 @@ abstract class HTMLPurifier_URIFilter
{
/**
- * Unique identifier of filter
+ * Unique identifier of filter.
+ * @type string
*/
public $name;
/**
* True if this filter should be run after scheme validation.
+ * @type bool
*/
public $post = false;
/**
- * True if this filter should always be loaded (this permits
- * a filter to be named Foo without the corresponding %URI.Foo
- * directive existing.)
+ * True if this filter should always be loaded.
+ * This permits a filter to be named Foo without the corresponding
+ * %URI.Foo directive existing.
+ * @type bool
*/
public $always_load = false;
/**
* Performs initialization for the filter. If the filter returns
* false, this means that it shouldn't be considered active.
+ * @param HTMLPurifier_Config $config
+ * @return bool
*/
- public function prepare($config) {return true;}
+ public function prepare($config)
+ {
+ return true;
+ }
/**
* Filter a URI object
- * @param $uri Reference to URI object variable
- * @param $config Instance of HTMLPurifier_Config
- * @param $context Instance of HTMLPurifier_Context
+ * @param HTMLPurifier_URI $uri Reference to URI object variable
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
* @return bool Whether or not to continue processing: false indicates
* URL is no good, true indicates continue processing. Note that
* all changes are committed directly on the URI object
*/
abstract public function filter(&$uri, $config, $context);
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php
index d8a39a501..ced1b1376 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php
@@ -2,19 +2,50 @@
class HTMLPurifier_URIFilter_DisableExternal extends HTMLPurifier_URIFilter
{
+ /**
+ * @type string
+ */
public $name = 'DisableExternal';
+
+ /**
+ * @type array
+ */
protected $ourHostParts = false;
- public function prepare($config) {
+
+ /**
+ * @param HTMLPurifier_Config $config
+ * @return void
+ */
+ public function prepare($config)
+ {
$our_host = $config->getDefinition('URI')->host;
- if ($our_host !== null) $this->ourHostParts = array_reverse(explode('.', $our_host));
+ if ($our_host !== null) {
+ $this->ourHostParts = array_reverse(explode('.', $our_host));
+ }
}
- public function filter(&$uri, $config, $context) {
- if (is_null($uri->host)) return true;
- if ($this->ourHostParts === false) return false;
+
+ /**
+ * @param HTMLPurifier_URI $uri Reference
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function filter(&$uri, $config, $context)
+ {
+ if (is_null($uri->host)) {
+ return true;
+ }
+ if ($this->ourHostParts === false) {
+ return false;
+ }
$host_parts = array_reverse(explode('.', $uri->host));
foreach ($this->ourHostParts as $i => $x) {
- if (!isset($host_parts[$i])) return false;
- if ($host_parts[$i] != $this->ourHostParts[$i]) return false;
+ if (!isset($host_parts[$i])) {
+ return false;
+ }
+ if ($host_parts[$i] != $this->ourHostParts[$i]) {
+ return false;
+ }
}
return true;
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php
index 881abc43c..c6562169e 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php
@@ -2,9 +2,22 @@
class HTMLPurifier_URIFilter_DisableExternalResources extends HTMLPurifier_URIFilter_DisableExternal
{
+ /**
+ * @type string
+ */
public $name = 'DisableExternalResources';
- public function filter(&$uri, $config, $context) {
- if (!$context->get('EmbeddedURI', true)) return true;
+
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function filter(&$uri, $config, $context)
+ {
+ if (!$context->get('EmbeddedURI', true)) {
+ return true;
+ }
return parent::filter($uri, $config, $context);
}
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php
index 67538c7bb..d5c412c44 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php
@@ -2,8 +2,19 @@
class HTMLPurifier_URIFilter_DisableResources extends HTMLPurifier_URIFilter
{
+ /**
+ * @type string
+ */
public $name = 'DisableResources';
- public function filter(&$uri, $config, $context) {
+
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function filter(&$uri, $config, $context)
+ {
return !$context->get('EmbeddedURI', true);
}
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php
index 55fde3bf4..a6645c17e 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php
@@ -6,14 +6,35 @@
// points are involved), but I'm not 100% sure
class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter
{
+ /**
+ * @type string
+ */
public $name = 'HostBlacklist';
+
+ /**
+ * @type array
+ */
protected $blacklist = array();
- public function prepare($config) {
+
+ /**
+ * @param HTMLPurifier_Config $config
+ * @return bool
+ */
+ public function prepare($config)
+ {
$this->blacklist = $config->get('URI.HostBlacklist');
return true;
}
- public function filter(&$uri, $config, $context) {
- foreach($this->blacklist as $blacklisted_host_fragment) {
+
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function filter(&$uri, $config, $context)
+ {
+ foreach ($this->blacklist as $blacklisted_host_fragment) {
if (strpos($uri->host, $blacklisted_host_fragment) !== false) {
return false;
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php
index f46ab2630..c507bbff8 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php
@@ -4,14 +4,35 @@
class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
{
+ /**
+ * @type string
+ */
public $name = 'MakeAbsolute';
+
+ /**
+ * @type
+ */
protected $base;
+
+ /**
+ * @type array
+ */
protected $basePathStack = array();
- public function prepare($config) {
+
+ /**
+ * @param HTMLPurifier_Config $config
+ * @return bool
+ */
+ public function prepare($config)
+ {
$def = $config->getDefinition('URI');
$this->base = $def->base;
if (is_null($this->base)) {
- trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_WARNING);
+ trigger_error(
+ 'URI.MakeAbsolute is being ignored due to lack of ' .
+ 'value for URI.Base configuration',
+ E_USER_WARNING
+ );
return false;
}
$this->base->fragment = null; // fragment is invalid for base URI
@@ -21,19 +42,29 @@ class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
$this->basePathStack = $stack;
return true;
}
- public function filter(&$uri, $config, $context) {
- if (is_null($this->base)) return true; // abort early
- if (
- $uri->path === '' && is_null($uri->scheme) &&
- is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)
- ) {
+
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function filter(&$uri, $config, $context)
+ {
+ if (is_null($this->base)) {
+ return true;
+ } // abort early
+ if ($uri->path === '' && is_null($uri->scheme) &&
+ is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) {
// reference to current document
$uri = clone $this->base;
return true;
}
if (!is_null($uri->scheme)) {
// absolute URI already: don't change
- if (!is_null($uri->host)) return true;
+ if (!is_null($uri->host)) {
+ return true;
+ }
$scheme_obj = $uri->getSchemeObj($config, $context);
if (!$scheme_obj) {
// scheme not recognized
@@ -66,22 +97,33 @@ class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
}
// re-combine
$uri->scheme = $this->base->scheme;
- if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo;
- if (is_null($uri->host)) $uri->host = $this->base->host;
- if (is_null($uri->port)) $uri->port = $this->base->port;
+ if (is_null($uri->userinfo)) {
+ $uri->userinfo = $this->base->userinfo;
+ }
+ if (is_null($uri->host)) {
+ $uri->host = $this->base->host;
+ }
+ if (is_null($uri->port)) {
+ $uri->port = $this->base->port;
+ }
return true;
}
/**
* Resolve dots and double-dots in a path stack
+ * @param array $stack
+ * @return array
*/
- private function _collapseStack($stack) {
+ private function _collapseStack($stack)
+ {
$result = array();
$is_folder = false;
for ($i = 0; isset($stack[$i]); $i++) {
$is_folder = false;
// absorb an internally duplicated slash
- if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue;
+ if ($stack[$i] == '' && $i && isset($stack[$i + 1])) {
+ continue;
+ }
if ($stack[$i] == '..') {
if (!empty($result)) {
$segment = array_pop($result);
@@ -106,7 +148,9 @@ class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
}
$result[] = $stack[$i];
}
- if ($is_folder) $result[] = '';
+ if ($is_folder) {
+ $result[] = '';
+ }
return $result;
}
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php
index de695df14..6e03315a1 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php
@@ -2,26 +2,79 @@
class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
{
+ /**
+ * @type string
+ */
public $name = 'Munge';
- public $post = true;
- private $target, $parser, $doEmbed, $secretKey;
+ /**
+ * @type bool
+ */
+ public $post = true;
+
+ /**
+ * @type string
+ */
+ private $target;
+
+ /**
+ * @type HTMLPurifier_URIParser
+ */
+ private $parser;
+
+ /**
+ * @type bool
+ */
+ private $doEmbed;
+
+ /**
+ * @type string
+ */
+ private $secretKey;
+
+ /**
+ * @type array
+ */
protected $replace = array();
- public function prepare($config) {
- $this->target = $config->get('URI.' . $this->name);
- $this->parser = new HTMLPurifier_URIParser();
- $this->doEmbed = $config->get('URI.MungeResources');
+ /**
+ * @param HTMLPurifier_Config $config
+ * @return bool
+ */
+ public function prepare($config)
+ {
+ $this->target = $config->get('URI.' . $this->name);
+ $this->parser = new HTMLPurifier_URIParser();
+ $this->doEmbed = $config->get('URI.MungeResources');
$this->secretKey = $config->get('URI.MungeSecretKey');
+ if ($this->secretKey && !function_exists('hash_hmac')) {
+ throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support.");
+ }
return true;
}
- public function filter(&$uri, $config, $context) {
- if ($context->get('EmbeddedURI', true) && !$this->doEmbed) return true;
+
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function filter(&$uri, $config, $context)
+ {
+ if ($context->get('EmbeddedURI', true) && !$this->doEmbed) {
+ return true;
+ }
$scheme_obj = $uri->getSchemeObj($config, $context);
- if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it
- if (!$scheme_obj->browsable) return true; // ignore non-browseable schemes, since we can't munge those in a reasonable way
- if ($uri->isBenign($config, $context)) return true; // don't redirect if a benign URL
+ if (!$scheme_obj) {
+ return true;
+ } // ignore unknown schemes, maybe another postfilter did it
+ if (!$scheme_obj->browsable) {
+ return true;
+ } // ignore non-browseable schemes, since we can't munge those in a reasonable way
+ if ($uri->isBenign($config, $context)) {
+ return true;
+ } // don't redirect if a benign URL
$this->makeReplace($uri, $config, $context);
$this->replace = array_map('rawurlencode', $this->replace);
@@ -30,12 +83,20 @@ class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
$new_uri = $this->parser->parse($new_uri);
// don't redirect if the target host is the same as the
// starting host
- if ($uri->host === $new_uri->host) return true;
+ if ($uri->host === $new_uri->host) {
+ return true;
+ }
$uri = $new_uri; // overwrite
return true;
}
- protected function makeReplace($uri, $config, $context) {
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ */
+ protected function makeReplace($uri, $config, $context)
+ {
$string = $uri->toString();
// always available
$this->replace['%s'] = $string;
@@ -45,9 +106,10 @@ class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
$this->replace['%m'] = $context->get('CurrentAttr', true);
$this->replace['%p'] = $context->get('CurrentCSSProperty', true);
// not always available
- if ($this->secretKey) $this->replace['%t'] = sha1($this->secretKey . ':' . $string);
+ if ($this->secretKey) {
+ $this->replace['%t'] = hash_hmac("sha256", $string, $this->secretKey);
+ }
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php
index 284bb13de..f609c47a3 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php
@@ -8,25 +8,58 @@
*/
class HTMLPurifier_URIFilter_SafeIframe extends HTMLPurifier_URIFilter
{
+ /**
+ * @type string
+ */
public $name = 'SafeIframe';
+
+ /**
+ * @type bool
+ */
public $always_load = true;
- protected $regexp = NULL;
- // XXX: The not so good bit about how this is all setup now is we
+
+ /**
+ * @type string
+ */
+ protected $regexp = null;
+
+ // XXX: The not so good bit about how this is all set up now is we
// can't check HTML.SafeIframe in the 'prepare' step: we have to
// defer till the actual filtering.
- public function prepare($config) {
+ /**
+ * @param HTMLPurifier_Config $config
+ * @return bool
+ */
+ public function prepare($config)
+ {
$this->regexp = $config->get('URI.SafeIframeRegexp');
return true;
}
- public function filter(&$uri, $config, $context) {
+
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function filter(&$uri, $config, $context)
+ {
// check if filter not applicable
- if (!$config->get('HTML.SafeIframe')) return true;
+ if (!$config->get('HTML.SafeIframe')) {
+ return true;
+ }
// check if the filter should actually trigger
- if (!$context->get('EmbeddedURI', true)) return true;
+ if (!$context->get('EmbeddedURI', true)) {
+ return true;
+ }
$token = $context->get('CurrentToken', true);
- if (!($token && $token->name == 'iframe')) return true;
+ if (!($token && $token->name == 'iframe')) {
+ return true;
+ }
// check if we actually have some whitelists enabled
- if ($this->regexp === null) return false;
+ if ($this->regexp === null) {
+ return false;
+ }
// actually check the whitelists
return preg_match($this->regexp, $uri->toString());
}
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIParser.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIParser.php
index 7179e4ab8..0e7381a07 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIParser.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIParser.php
@@ -12,7 +12,8 @@ class HTMLPurifier_URIParser
*/
protected $percentEncoder;
- public function __construct() {
+ public function __construct()
+ {
$this->percentEncoder = new HTMLPurifier_PercentEncoder();
}
@@ -22,15 +23,15 @@ class HTMLPurifier_URIParser
* @return HTMLPurifier_URI representation of URI. This representation has
* not been validated yet and may not conform to RFC.
*/
- public function parse($uri) {
-
+ public function parse($uri)
+ {
$uri = $this->percentEncoder->normalize($uri);
// Regexp is as per Appendix B.
// Note that ["<>] are an addition to the RFC's recommended
// characters, because they represent external delimeters.
$r_URI = '!'.
- '(([^:/?#"<>]+):)?'. // 2. Scheme
+ '(([a-zA-Z0-9\.\+\-]+):)?'. // 2. Scheme
'(//([^/?#"<>]*))?'. // 4. Authority
'([^?#"<>]*)'. // 5. Path
'(\?([^#"<>]*))?'. // 7. Query
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme.php
index 7be958143..fe9e82cf2 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme.php
@@ -7,27 +7,31 @@ abstract class HTMLPurifier_URIScheme
{
/**
- * Scheme's default port (integer). If an explicit port number is
+ * Scheme's default port (integer). If an explicit port number is
* specified that coincides with the default port, it will be
* elided.
+ * @type int
*/
public $default_port = null;
/**
- * Whether or not URIs of this schem are locatable by a browser
+ * Whether or not URIs of this scheme are locatable by a browser
* http and ftp are accessible, while mailto and news are not.
+ * @type bool
*/
public $browsable = false;
/**
* Whether or not data transmitted over this scheme is encrypted.
* https is secure, http is not.
+ * @type bool
*/
public $secure = false;
/**
* Whether or not the URI always uses , resolves edge cases
* with making relative URIs absolute
+ * @type bool
*/
public $hierarchical = false;
@@ -35,28 +39,32 @@ abstract class HTMLPurifier_URIScheme
* Whether or not the URI may omit a hostname when the scheme is
* explicitly specified, ala file:///path/to/file. As of writing,
* 'file' is the only scheme that browsers support his properly.
+ * @type bool
*/
public $may_omit_host = false;
/**
* Validates the components of a URI for a specific scheme.
- * @param $uri Reference to a HTMLPurifier_URI object
- * @param $config HTMLPurifier_Config object
- * @param $context HTMLPurifier_Context object
- * @return Bool success or failure
+ * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool success or failure
*/
- public abstract function doValidate(&$uri, $config, $context);
+ abstract public function doValidate(&$uri, $config, $context);
/**
* Public interface for validating components of a URI. Performs a
* bunch of default actions. Don't overload this method.
- * @param $uri Reference to a HTMLPurifier_URI object
- * @param $config HTMLPurifier_Config object
- * @param $context HTMLPurifier_Context object
- * @return Bool success or failure
+ * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool success or failure
*/
- public function validate(&$uri, $config, $context) {
- if ($this->default_port == $uri->port) $uri->port = null;
+ public function validate(&$uri, $config, $context)
+ {
+ if ($this->default_port == $uri->port) {
+ $uri->port = null;
+ }
// kludge: browsers do funny things when the scheme but not the
// authority is set
if (!$this->may_omit_host &&
@@ -65,7 +73,7 @@ abstract class HTMLPurifier_URIScheme
// if the scheme is not present, a *blank* host is in error,
// since this translates into '///path' which most browsers
// interpret as being 'http://path'.
- (is_null($uri->scheme) && $uri->host === '')
+ (is_null($uri->scheme) && $uri->host === '')
) {
do {
if (is_null($uri->scheme)) {
@@ -89,7 +97,6 @@ abstract class HTMLPurifier_URIScheme
}
return $this->doValidate($uri, $config, $context);
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/data.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/data.php
index a6a9bdcdd..7093a1127 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/data.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/data.php
@@ -3,21 +3,38 @@
/**
* Implements data: URI for base64 encoded images supported by GD.
*/
-class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme {
-
+class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme
+{
+ /**
+ * @type bool
+ */
public $browsable = true;
+
+ /**
+ * @type array
+ */
public $allowed_types = array(
// you better write validation code for other types if you
// decide to allow them
'image/jpeg' => true,
'image/gif' => true,
'image/png' => true,
- );
+ );
// this is actually irrelevant since we only write out the path
// component
+ /**
+ * @type bool
+ */
public $may_omit_host = true;
- public function doValidate(&$uri, $config, $context) {
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function doValidate(&$uri, $config, $context)
+ {
$result = explode(',', $uri->path, 2);
$is_base64 = false;
$charset = null;
@@ -26,7 +43,7 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme {
list($metadata, $data) = $result;
// do some legwork on the metadata
$metas = explode(';', $metadata);
- while(!empty($metas)) {
+ while (!empty($metas)) {
$cur = array_shift($metas);
if ($cur == 'base64') {
$is_base64 = true;
@@ -35,10 +52,14 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme {
if (substr($cur, 0, 8) == 'charset=') {
// doesn't match if there are arbitrary spaces, but
// whatever dude
- if ($charset !== null) continue; // garbage
+ if ($charset !== null) {
+ continue;
+ } // garbage
$charset = substr($cur, 8); // not used
} else {
- if ($content_type !== null) continue; // garbage
+ if ($content_type !== null) {
+ continue;
+ } // garbage
$content_type = $cur;
}
}
@@ -64,11 +85,15 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme {
file_put_contents($file, $raw_data, LOCK_EX);
if (function_exists('exif_imagetype')) {
$image_code = exif_imagetype($file);
+ unlink($file);
} elseif (function_exists('getimagesize')) {
set_error_handler(array($this, 'muteErrorHandler'));
$info = getimagesize($file);
restore_error_handler();
- if ($info == false) return false;
+ unlink($file);
+ if ($info == false) {
+ return false;
+ }
$image_code = $info[2];
} else {
trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR);
@@ -77,7 +102,9 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme {
if ($real_content_type != $content_type) {
// we're nice guys; if the content type is something else we
// support, change it over
- if (empty($this->allowed_types[$real_content_type])) return false;
+ if (empty($this->allowed_types[$real_content_type])) {
+ return false;
+ }
$content_type = $real_content_type;
}
// ok, it's kosher, rewrite what we need
@@ -90,7 +117,11 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme {
return true;
}
- public function muteErrorHandler($errno, $errstr) {}
-
+ /**
+ * @param int $errno
+ * @param string $errstr
+ */
+ public function muteErrorHandler($errno, $errstr)
+ {
+ }
}
-
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/file.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/file.php
index d74a3f198..215be4ba8 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/file.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/file.php
@@ -3,30 +3,42 @@
/**
* Validates file as defined by RFC 1630 and RFC 1738.
*/
-class HTMLPurifier_URIScheme_file extends HTMLPurifier_URIScheme {
-
- // Generally file:// URLs are not accessible from most
- // machines, so placing them as an img src is incorrect.
+class HTMLPurifier_URIScheme_file extends HTMLPurifier_URIScheme
+{
+ /**
+ * Generally file:// URLs are not accessible from most
+ * machines, so placing them as an img src is incorrect.
+ * @type bool
+ */
public $browsable = false;
- // Basically the *only* URI scheme for which this is true, since
- // accessing files on the local machine is very common. In fact,
- // browsers on some operating systems don't understand the
- // authority, though I hear it is used on Windows to refer to
- // network shares.
+ /**
+ * Basically the *only* URI scheme for which this is true, since
+ * accessing files on the local machine is very common. In fact,
+ * browsers on some operating systems don't understand the
+ * authority, though I hear it is used on Windows to refer to
+ * network shares.
+ * @type bool
+ */
public $may_omit_host = true;
- public function doValidate(&$uri, $config, $context) {
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function doValidate(&$uri, $config, $context)
+ {
// Authentication method is not supported
$uri->userinfo = null;
// file:// makes no provisions for accessing the resource
- $uri->port = null;
+ $uri->port = null;
// While it seems to work on Firefox, the querystring has
// no possible effect and is thus stripped.
- $uri->query = null;
+ $uri->query = null;
return true;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php
index 0fb2abf64..1eb43ee5c 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php
@@ -3,14 +3,32 @@
/**
* Validates ftp (File Transfer Protocol) URIs as defined by generic RFC 1738.
*/
-class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme {
-
+class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme
+{
+ /**
+ * @type int
+ */
public $default_port = 21;
+
+ /**
+ * @type bool
+ */
public $browsable = true; // usually
+
+ /**
+ * @type bool
+ */
public $hierarchical = true;
- public function doValidate(&$uri, $config, $context) {
- $uri->query = null;
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function doValidate(&$uri, $config, $context)
+ {
+ $uri->query = null;
// typecode check
$semicolon_pos = strrpos($uri->path, ';'); // reverse
@@ -33,10 +51,8 @@ class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme {
$uri->path = str_replace(';', '%3B', $uri->path);
$uri->path .= $type_ret;
}
-
return true;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/http.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/http.php
index 959b8daff..ce69ec438 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/http.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/http.php
@@ -3,17 +3,34 @@
/**
* Validates http (HyperText Transfer Protocol) as defined by RFC 2616
*/
-class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme {
-
+class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme
+{
+ /**
+ * @type int
+ */
public $default_port = 80;
+
+ /**
+ * @type bool
+ */
public $browsable = true;
+
+ /**
+ * @type bool
+ */
public $hierarchical = true;
- public function doValidate(&$uri, $config, $context) {
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function doValidate(&$uri, $config, $context)
+ {
$uri->userinfo = null;
return true;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/https.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/https.php
index 159c2874e..0e96882db 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/https.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/https.php
@@ -3,11 +3,16 @@
/**
* Validates https (Secure HTTP) according to http scheme.
*/
-class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http {
-
+class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http
+{
+ /**
+ * @type int
+ */
public $default_port = 443;
+ /**
+ * @type bool
+ */
public $secure = true;
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php
index 9db4cb23f..c3a6b602a 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php
@@ -9,19 +9,32 @@
* @todo Filter allowed query parameters
*/
-class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme {
-
+class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme
+{
+ /**
+ * @type bool
+ */
public $browsable = false;
+
+ /**
+ * @type bool
+ */
public $may_omit_host = true;
- public function doValidate(&$uri, $config, $context) {
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function doValidate(&$uri, $config, $context)
+ {
$uri->userinfo = null;
$uri->host = null;
$uri->port = null;
// we need to validate path against RFC 2368's addr-spec
return true;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/news.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/news.php
index 84a6748d8..7490927d6 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/news.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/news.php
@@ -3,20 +3,33 @@
/**
* Validates news (Usenet) as defined by generic RFC 1738
*/
-class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme {
-
+class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme
+{
+ /**
+ * @type bool
+ */
public $browsable = false;
+
+ /**
+ * @type bool
+ */
public $may_omit_host = true;
- public function doValidate(&$uri, $config, $context) {
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function doValidate(&$uri, $config, $context)
+ {
$uri->userinfo = null;
- $uri->host = null;
- $uri->port = null;
- $uri->query = null;
+ $uri->host = null;
+ $uri->port = null;
+ $uri->query = null;
// typecode check needed on path
return true;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php
index 4ccea0dfc..f211d715e 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php
@@ -3,17 +3,30 @@
/**
* Validates nntp (Network News Transfer Protocol) as defined by generic RFC 1738
*/
-class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme {
-
+class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme
+{
+ /**
+ * @type int
+ */
public $default_port = 119;
+
+ /**
+ * @type bool
+ */
public $browsable = false;
- public function doValidate(&$uri, $config, $context) {
+ /**
+ * @param HTMLPurifier_URI $uri
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return bool
+ */
+ public function doValidate(&$uri, $config, $context)
+ {
$uri->userinfo = null;
- $uri->query = null;
+ $uri->query = null;
return true;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php b/classes/security/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php
index 576bf7b6d..4ac8a0b76 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php
@@ -8,12 +8,14 @@ class HTMLPurifier_URISchemeRegistry
/**
* Retrieve sole instance of the registry.
- * @param $prototype Optional prototype to overload sole instance with,
+ * @param HTMLPurifier_URISchemeRegistry $prototype Optional prototype to overload sole instance with,
* or bool true to reset to default registry.
+ * @return HTMLPurifier_URISchemeRegistry
* @note Pass a registry object $prototype with a compatible interface and
* the function will copy it and return it all further times.
*/
- public static function instance($prototype = null) {
+ public static function instance($prototype = null)
+ {
static $instance = null;
if ($prototype !== null) {
$instance = $prototype;
@@ -25,17 +27,22 @@ class HTMLPurifier_URISchemeRegistry
/**
* Cache of retrieved schemes.
+ * @type HTMLPurifier_URIScheme[]
*/
protected $schemes = array();
/**
* Retrieves a scheme validator object
- * @param $scheme String scheme name like http or mailto
- * @param $config HTMLPurifier_Config object
- * @param $config HTMLPurifier_Context object
+ * @param string $scheme String scheme name like http or mailto
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return HTMLPurifier_URIScheme
*/
- public function getScheme($scheme, $config, $context) {
- if (!$config) $config = HTMLPurifier_Config::createDefault();
+ public function getScheme($scheme, $config, $context)
+ {
+ if (!$config) {
+ $config = HTMLPurifier_Config::createDefault();
+ }
// important, otherwise attacker could include arbitrary file
$allowed_schemes = $config->get('URI.AllowedSchemes');
@@ -45,24 +52,30 @@ class HTMLPurifier_URISchemeRegistry
return;
}
- if (isset($this->schemes[$scheme])) return $this->schemes[$scheme];
- if (!isset($allowed_schemes[$scheme])) return;
+ if (isset($this->schemes[$scheme])) {
+ return $this->schemes[$scheme];
+ }
+ if (!isset($allowed_schemes[$scheme])) {
+ return;
+ }
$class = 'HTMLPurifier_URIScheme_' . $scheme;
- if (!class_exists($class)) return;
+ if (!class_exists($class)) {
+ return;
+ }
$this->schemes[$scheme] = new $class();
return $this->schemes[$scheme];
}
/**
* Registers a custom scheme to the cache, bypassing reflection.
- * @param $scheme Scheme name
- * @param $scheme_obj HTMLPurifier_URIScheme object
+ * @param string $scheme Scheme name
+ * @param HTMLPurifier_URIScheme $scheme_obj
*/
- public function register($scheme, $scheme_obj) {
+ public function register($scheme, $scheme_obj)
+ {
$this->schemes[$scheme] = $scheme_obj;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/UnitConverter.php b/classes/security/htmlpurifier/library/HTMLPurifier/UnitConverter.php
index 545d42622..166f3bf30 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/UnitConverter.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/UnitConverter.php
@@ -37,20 +37,24 @@ class HTMLPurifier_UnitConverter
/**
* Minimum bcmath precision for output.
+ * @type int
*/
protected $outputPrecision;
/**
* Bcmath precision for internal calculations.
+ * @type int
*/
protected $internalPrecision;
/**
- * Whether or not BCMath is available
+ * Whether or not BCMath is available.
+ * @type bool
*/
private $bcmath;
- public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) {
+ public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false)
+ {
$this->outputPrecision = $output_precision;
$this->internalPrecision = $internal_precision;
$this->bcmath = !$force_no_bcmath && function_exists('bcmul');
@@ -63,6 +67,7 @@ class HTMLPurifier_UnitConverter
* it before passing it here!
* @param string $to_unit
* Unit to convert to.
+ * @return HTMLPurifier_Length|bool
* @note
* About precision: This conversion function pays very special
* attention to the incoming precision of values and attempts
@@ -74,11 +79,13 @@ class HTMLPurifier_UnitConverter
* and this causes some decimals to be excluded, those
* decimals will be added on.
*/
- public function convert($length, $to_unit) {
+ public function convert($length, $to_unit)
+ {
+ if (!$length->isValid()) {
+ return false;
+ }
- if (!$length->isValid()) return false;
-
- $n = $length->getN();
+ $n = $length->getN();
$unit = $length->getUnit();
if ($n === '0' || $unit === false) {
@@ -87,21 +94,29 @@ class HTMLPurifier_UnitConverter
$state = $dest_state = false;
foreach (self::$units as $k => $x) {
- if (isset($x[$unit])) $state = $k;
- if (isset($x[$to_unit])) $dest_state = $k;
+ if (isset($x[$unit])) {
+ $state = $k;
+ }
+ if (isset($x[$to_unit])) {
+ $dest_state = $k;
+ }
+ }
+ if (!$state || !$dest_state) {
+ return false;
}
- if (!$state || !$dest_state) return false;
// Some calculations about the initial precision of the number;
// this will be useful when we need to do final rounding.
$sigfigs = $this->getSigFigs($n);
- if ($sigfigs < $this->outputPrecision) $sigfigs = $this->outputPrecision;
+ if ($sigfigs < $this->outputPrecision) {
+ $sigfigs = $this->outputPrecision;
+ }
// BCMath's internal precision deals only with decimals. Use
// our default if the initial number has no decimals, or increase
// it by how ever many decimals, thus, the number of guard digits
// will always be greater than or equal to internalPrecision.
- $log = (int) floor(log(abs($n), 10));
+ $log = (int)floor(log(abs($n), 10));
$cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision
for ($i = 0; $i < 2; $i++) {
@@ -152,14 +167,18 @@ class HTMLPurifier_UnitConverter
}
// Post-condition: $unit == $to_unit
- if ($unit !== $to_unit) return false;
+ if ($unit !== $to_unit) {
+ return false;
+ }
// Useful for debugging:
//echo "
n";
//echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n\n";
$n = $this->round($n, $sigfigs);
- if (strpos($n, '.') !== false) $n = rtrim($n, '0');
+ if (strpos($n, '.') !== false) {
+ $n = rtrim($n, '0');
+ }
$n = rtrim($n, '.');
return new HTMLPurifier_Length($n, $unit);
@@ -170,53 +189,84 @@ class HTMLPurifier_UnitConverter
* @param string $n Decimal number
* @return int number of sigfigs
*/
- public function getSigFigs($n) {
+ public function getSigFigs($n)
+ {
$n = ltrim($n, '0+-');
$dp = strpos($n, '.'); // decimal position
if ($dp === false) {
$sigfigs = strlen(rtrim($n, '0'));
} else {
$sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character
- if ($dp !== 0) $sigfigs--;
+ if ($dp !== 0) {
+ $sigfigs--;
+ }
}
return $sigfigs;
}
/**
* Adds two numbers, using arbitrary precision when available.
+ * @param string $s1
+ * @param string $s2
+ * @param int $scale
+ * @return string
*/
- private function add($s1, $s2, $scale) {
- if ($this->bcmath) return bcadd($s1, $s2, $scale);
- else return $this->scale($s1 + $s2, $scale);
+ private function add($s1, $s2, $scale)
+ {
+ if ($this->bcmath) {
+ return bcadd($s1, $s2, $scale);
+ } else {
+ return $this->scale((float)$s1 + (float)$s2, $scale);
+ }
}
/**
* Multiples two numbers, using arbitrary precision when available.
+ * @param string $s1
+ * @param string $s2
+ * @param int $scale
+ * @return string
*/
- private function mul($s1, $s2, $scale) {
- if ($this->bcmath) return bcmul($s1, $s2, $scale);
- else return $this->scale($s1 * $s2, $scale);
+ private function mul($s1, $s2, $scale)
+ {
+ if ($this->bcmath) {
+ return bcmul($s1, $s2, $scale);
+ } else {
+ return $this->scale((float)$s1 * (float)$s2, $scale);
+ }
}
/**
* Divides two numbers, using arbitrary precision when available.
+ * @param string $s1
+ * @param string $s2
+ * @param int $scale
+ * @return string
*/
- private function div($s1, $s2, $scale) {
- if ($this->bcmath) return bcdiv($s1, $s2, $scale);
- else return $this->scale($s1 / $s2, $scale);
+ private function div($s1, $s2, $scale)
+ {
+ if ($this->bcmath) {
+ return bcdiv($s1, $s2, $scale);
+ } else {
+ return $this->scale((float)$s1 / (float)$s2, $scale);
+ }
}
/**
* Rounds a number according to the number of sigfigs it should have,
* using arbitrary precision when available.
+ * @param float $n
+ * @param int $sigfigs
+ * @return string
*/
- private function round($n, $sigfigs) {
- $new_log = (int) floor(log(abs($n), 10)); // Number of digits left of decimal - 1
+ private function round($n, $sigfigs)
+ {
+ $new_log = (int)floor(log(abs($n), 10)); // Number of digits left of decimal - 1
$rp = $sigfigs - $new_log - 1; // Number of decimal places needed
$neg = $n < 0 ? '-' : ''; // Negative sign
if ($this->bcmath) {
if ($rp >= 0) {
- $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1);
+ $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1);
$n = bcdiv($n, '1', $rp);
} else {
// This algorithm partially depends on the standardized
@@ -232,23 +282,26 @@ class HTMLPurifier_UnitConverter
/**
* Scales a float to $scale digits right of decimal point, like BCMath.
+ * @param float $r
+ * @param int $scale
+ * @return string
*/
- private function scale($r, $scale) {
+ private function scale($r, $scale)
+ {
if ($scale < 0) {
// The f sprintf type doesn't support negative numbers, so we
// need to cludge things manually. First get the string.
- $r = sprintf('%.0f', (float) $r);
+ $r = sprintf('%.0f', (float)$r);
// Due to floating point precision loss, $r will more than likely
// look something like 4652999999999.9234. We grab one more digit
// than we need to precise from $r and then use that to round
// appropriately.
- $precise = (string) round(substr($r, 0, strlen($r) + $scale), -1);
+ $precise = (string)round(substr($r, 0, strlen($r) + $scale), -1);
// Now we return it, truncating the zero that was rounded off.
return substr($precise, 0, -1) . str_repeat('0', -$scale + 1);
}
- return sprintf('%.' . $scale . 'f', (float) $r);
+ return sprintf('%.' . $scale . 'f', (float)$r);
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/VarParser.php b/classes/security/htmlpurifier/library/HTMLPurifier/VarParser.php
index 68e72ae86..50cba6910 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/VarParser.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/VarParser.php
@@ -7,58 +7,59 @@
class HTMLPurifier_VarParser
{
- const STRING = 1;
- const ISTRING = 2;
- const TEXT = 3;
- const ITEXT = 4;
- const INT = 5;
- const FLOAT = 6;
- const BOOL = 7;
- const LOOKUP = 8;
- const ALIST = 9;
- const HASH = 10;
- const MIXED = 11;
+ const STRING = 1;
+ const ISTRING = 2;
+ const TEXT = 3;
+ const ITEXT = 4;
+ const INT = 5;
+ const FLOAT = 6;
+ const BOOL = 7;
+ const LOOKUP = 8;
+ const ALIST = 9;
+ const HASH = 10;
+ const MIXED = 11;
/**
* Lookup table of allowed types. Mainly for backwards compatibility, but
* also convenient for transforming string type names to the integer constants.
*/
- static public $types = array(
- 'string' => self::STRING,
- 'istring' => self::ISTRING,
- 'text' => self::TEXT,
- 'itext' => self::ITEXT,
- 'int' => self::INT,
- 'float' => self::FLOAT,
- 'bool' => self::BOOL,
- 'lookup' => self::LOOKUP,
- 'list' => self::ALIST,
- 'hash' => self::HASH,
- 'mixed' => self::MIXED
+ public static $types = array(
+ 'string' => self::STRING,
+ 'istring' => self::ISTRING,
+ 'text' => self::TEXT,
+ 'itext' => self::ITEXT,
+ 'int' => self::INT,
+ 'float' => self::FLOAT,
+ 'bool' => self::BOOL,
+ 'lookup' => self::LOOKUP,
+ 'list' => self::ALIST,
+ 'hash' => self::HASH,
+ 'mixed' => self::MIXED
);
/**
* Lookup table of types that are string, and can have aliases or
* allowed value lists.
*/
- static public $stringTypes = array(
- self::STRING => true,
- self::ISTRING => true,
- self::TEXT => true,
- self::ITEXT => true,
+ public static $stringTypes = array(
+ self::STRING => true,
+ self::ISTRING => true,
+ self::TEXT => true,
+ self::ITEXT => true,
);
/**
- * Validate a variable according to type. Throws
- * HTMLPurifier_VarParserException if invalid.
+ * Validate a variable according to type.
* It may return NULL as a valid type if $allow_null is true.
*
- * @param $var Variable to validate
- * @param $type Type of variable, see HTMLPurifier_VarParser->types
- * @param $allow_null Whether or not to permit null as a value
- * @return Validated and type-coerced variable
+ * @param mixed $var Variable to validate
+ * @param int $type Type of variable, see HTMLPurifier_VarParser->types
+ * @param bool $allow_null Whether or not to permit null as a value
+ * @return string Validated and type-coerced variable
+ * @throws HTMLPurifier_VarParserException
*/
- final public function parse($var, $type, $allow_null = false) {
+ final public function parse($var, $type, $allow_null = false)
+ {
if (is_string($type)) {
if (!isset(HTMLPurifier_VarParser::$types[$type])) {
throw new HTMLPurifier_VarParserException("Invalid type '$type'");
@@ -67,7 +68,9 @@ class HTMLPurifier_VarParser
}
}
$var = $this->parseImplementation($var, $type, $allow_null);
- if ($allow_null && $var === null) return null;
+ if ($allow_null && $var === null) {
+ return null;
+ }
// These are basic checks, to make sure nothing horribly wrong
// happened in our implementations.
switch ($type) {
@@ -75,27 +78,45 @@ class HTMLPurifier_VarParser
case (self::ISTRING):
case (self::TEXT):
case (self::ITEXT):
- if (!is_string($var)) break;
- if ($type == self::ISTRING || $type == self::ITEXT) $var = strtolower($var);
+ if (!is_string($var)) {
+ break;
+ }
+ if ($type == self::ISTRING || $type == self::ITEXT) {
+ $var = strtolower($var);
+ }
return $var;
case (self::INT):
- if (!is_int($var)) break;
+ if (!is_int($var)) {
+ break;
+ }
return $var;
case (self::FLOAT):
- if (!is_float($var)) break;
+ if (!is_float($var)) {
+ break;
+ }
return $var;
case (self::BOOL):
- if (!is_bool($var)) break;
+ if (!is_bool($var)) {
+ break;
+ }
return $var;
case (self::LOOKUP):
case (self::ALIST):
case (self::HASH):
- if (!is_array($var)) break;
+ if (!is_array($var)) {
+ break;
+ }
if ($type === self::LOOKUP) {
- foreach ($var as $k) if ($k !== true) $this->error('Lookup table contains value other than true');
+ foreach ($var as $k) {
+ if ($k !== true) {
+ $this->error('Lookup table contains value other than true');
+ }
+ }
} elseif ($type === self::ALIST) {
$keys = array_keys($var);
- if (array_keys($keys) !== $keys) $this->error('Indices for list are not uniform');
+ if (array_keys($keys) !== $keys) {
+ $this->error('Indices for list are not uniform');
+ }
}
return $var;
case (self::MIXED):
@@ -107,17 +128,24 @@ class HTMLPurifier_VarParser
}
/**
- * Actually implements the parsing. Base implementation is to not
+ * Actually implements the parsing. Base implementation does not
* do anything to $var. Subclasses should overload this!
+ * @param mixed $var
+ * @param int $type
+ * @param bool $allow_null
+ * @return string
*/
- protected function parseImplementation($var, $type, $allow_null) {
+ protected function parseImplementation($var, $type, $allow_null)
+ {
return $var;
}
/**
* Throws an exception.
+ * @throws HTMLPurifier_VarParserException
*/
- protected function error($msg) {
+ protected function error($msg)
+ {
throw new HTMLPurifier_VarParserException($msg);
}
@@ -126,29 +154,45 @@ class HTMLPurifier_VarParser
* @note This should not ever be called. It would be called if we
* extend the allowed values of HTMLPurifier_VarParser without
* updating subclasses.
+ * @param string $class
+ * @param int $type
+ * @throws HTMLPurifier_Exception
*/
- protected function errorInconsistent($class, $type) {
- throw new HTMLPurifier_Exception("Inconsistency in $class: ".HTMLPurifier_VarParser::getTypeName($type)." not implemented");
+ protected function errorInconsistent($class, $type)
+ {
+ throw new HTMLPurifier_Exception(
+ "Inconsistency in $class: " . HTMLPurifier_VarParser::getTypeName($type) .
+ " not implemented"
+ );
}
/**
* Generic error for if a type didn't work.
+ * @param mixed $var
+ * @param int $type
*/
- protected function errorGeneric($var, $type) {
+ protected function errorGeneric($var, $type)
+ {
$vtype = gettype($var);
- $this->error("Expected type ".HTMLPurifier_VarParser::getTypeName($type).", got $vtype");
+ $this->error("Expected type " . HTMLPurifier_VarParser::getTypeName($type) . ", got $vtype");
}
- static public function getTypeName($type) {
+ /**
+ * @param int $type
+ * @return string
+ */
+ public static function getTypeName($type)
+ {
static $lookup;
if (!$lookup) {
// Lazy load the alternative lookup table
$lookup = array_flip(HTMLPurifier_VarParser::$types);
}
- if (!isset($lookup[$type])) return 'unknown';
+ if (!isset($lookup[$type])) {
+ return 'unknown';
+ }
return $lookup[$type];
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php b/classes/security/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php
index 21b87675a..b15016c5b 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php
@@ -7,28 +7,41 @@
*/
class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
{
-
- protected function parseImplementation($var, $type, $allow_null) {
- if ($allow_null && $var === null) return null;
+ /**
+ * @param mixed $var
+ * @param int $type
+ * @param bool $allow_null
+ * @return array|bool|float|int|mixed|null|string
+ * @throws HTMLPurifier_VarParserException
+ */
+ protected function parseImplementation($var, $type, $allow_null)
+ {
+ if ($allow_null && $var === null) {
+ return null;
+ }
switch ($type) {
// Note: if code "breaks" from the switch, it triggers a generic
// exception to be thrown. Specific errors can be specifically
// done here.
- case self::MIXED :
- case self::ISTRING :
- case self::STRING :
- case self::TEXT :
- case self::ITEXT :
+ case self::MIXED:
+ case self::ISTRING:
+ case self::STRING:
+ case self::TEXT:
+ case self::ITEXT:
return $var;
- case self::INT :
- if (is_string($var) && ctype_digit($var)) $var = (int) $var;
+ case self::INT:
+ if (is_string($var) && ctype_digit($var)) {
+ $var = (int)$var;
+ }
return $var;
- case self::FLOAT :
- if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var;
+ case self::FLOAT:
+ if ((is_string($var) && is_numeric($var)) || is_int($var)) {
+ $var = (float)$var;
+ }
return $var;
- case self::BOOL :
+ case self::BOOL:
if (is_int($var) && ($var === 0 || $var === 1)) {
- $var = (bool) $var;
+ $var = (bool)$var;
} elseif (is_string($var)) {
if ($var == 'on' || $var == 'true' || $var == '1') {
$var = true;
@@ -39,45 +52,56 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
}
}
return $var;
- case self::ALIST :
- case self::HASH :
- case self::LOOKUP :
+ case self::ALIST:
+ case self::HASH:
+ case self::LOOKUP:
if (is_string($var)) {
// special case: technically, this is an array with
// a single empty string item, but having an empty
// array is more intuitive
- if ($var == '') return array();
+ if ($var == '') {
+ return array();
+ }
if (strpos($var, "\n") === false && strpos($var, "\r") === false) {
// simplistic string to array method that only works
// for simple lists of tag names or alphanumeric characters
- $var = explode(',',$var);
+ $var = explode(',', $var);
} else {
$var = preg_split('/(,|[\n\r]+)/', $var);
}
// remove spaces
- foreach ($var as $i => $j) $var[$i] = trim($j);
+ foreach ($var as $i => $j) {
+ $var[$i] = trim($j);
+ }
if ($type === self::HASH) {
// key:value,key2:value2
$nvar = array();
foreach ($var as $keypair) {
$c = explode(':', $keypair, 2);
- if (!isset($c[1])) continue;
+ if (!isset($c[1])) {
+ continue;
+ }
$nvar[trim($c[0])] = trim($c[1]);
}
$var = $nvar;
}
}
- if (!is_array($var)) break;
+ if (!is_array($var)) {
+ break;
+ }
$keys = array_keys($var);
if ($keys === array_keys($keys)) {
- if ($type == self::ALIST) return $var;
- elseif ($type == self::LOOKUP) {
+ if ($type == self::ALIST) {
+ return $var;
+ } elseif ($type == self::LOOKUP) {
$new = array();
foreach ($var as $key) {
$new[$key] = true;
}
return $new;
- } else break;
+ } else {
+ break;
+ }
}
if ($type === self::ALIST) {
trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING);
@@ -86,7 +110,11 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
if ($type === self::LOOKUP) {
foreach ($var as $key => $value) {
if ($value !== true) {
- trigger_error("Lookup array has non-true value at key '$key'; maybe your input array was not indexed numerically", E_USER_WARNING);
+ trigger_error(
+ "Lookup array has non-true value at key '$key'; " .
+ "maybe your input array was not indexed numerically",
+ E_USER_WARNING
+ );
}
$var[$key] = true;
}
@@ -97,7 +125,6 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
}
$this->errorGeneric($var, $type);
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/VarParser/Native.php b/classes/security/htmlpurifier/library/HTMLPurifier/VarParser/Native.php
index b02a6de54..f11c318ef 100644
--- a/classes/security/htmlpurifier/library/HTMLPurifier/VarParser/Native.php
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/VarParser/Native.php
@@ -8,11 +8,24 @@
class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser
{
- protected function parseImplementation($var, $type, $allow_null) {
+ /**
+ * @param mixed $var
+ * @param int $type
+ * @param bool $allow_null
+ * @return null|string
+ */
+ protected function parseImplementation($var, $type, $allow_null)
+ {
return $this->evalExpression($var);
}
- protected function evalExpression($expr) {
+ /**
+ * @param string $expr
+ * @return mixed
+ * @throws HTMLPurifier_VarParserException
+ */
+ protected function evalExpression($expr)
+ {
$var = null;
$result = eval("\$var = $expr;");
if ($result === false) {
@@ -20,7 +33,6 @@ class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser
}
return $var;
}
-
}
// vim: et sw=4 sts=4
diff --git a/classes/security/htmlpurifier/library/HTMLPurifier/Zipper.php b/classes/security/htmlpurifier/library/HTMLPurifier/Zipper.php
new file mode 100644
index 000000000..6e21ea070
--- /dev/null
+++ b/classes/security/htmlpurifier/library/HTMLPurifier/Zipper.php
@@ -0,0 +1,157 @@
+front = $front;
+ $this->back = $back;
+ }
+
+ /**
+ * Creates a zipper from an array, with a hole in the
+ * 0-index position.
+ * @param Array to zipper-ify.
+ * @return Tuple of zipper and element of first position.
+ */
+ static public function fromArray($array) {
+ $z = new self(array(), array_reverse($array));
+ $t = $z->delete(); // delete the "dummy hole"
+ return array($z, $t);
+ }
+
+ /**
+ * Convert zipper back into a normal array, optionally filling in
+ * the hole with a value. (Usually you should supply a $t, unless you
+ * are at the end of the array.)
+ */
+ public function toArray($t = NULL) {
+ $a = $this->front;
+ if ($t !== NULL) $a[] = $t;
+ for ($i = count($this->back)-1; $i >= 0; $i--) {
+ $a[] = $this->back[$i];
+ }
+ return $a;
+ }
+
+ /**
+ * Move hole to the next element.
+ * @param $t Element to fill hole with
+ * @return Original contents of new hole.
+ */
+ public function next($t) {
+ if ($t !== NULL) array_push($this->front, $t);
+ return empty($this->back) ? NULL : array_pop($this->back);
+ }
+
+ /**
+ * Iterated hole advancement.
+ * @param $t Element to fill hole with
+ * @param $i How many forward to advance hole
+ * @return Original contents of new hole, i away
+ */
+ public function advance($t, $n) {
+ for ($i = 0; $i < $n; $i++) {
+ $t = $this->next($t);
+ }
+ return $t;
+ }
+
+ /**
+ * Move hole to the previous element
+ * @param $t Element to fill hole with
+ * @return Original contents of new hole.
+ */
+ public function prev($t) {
+ if ($t !== NULL) array_push($this->back, $t);
+ return empty($this->front) ? NULL : array_pop($this->front);
+ }
+
+ /**
+ * Delete contents of current hole, shifting hole to
+ * next element.
+ * @return Original contents of new hole.
+ */
+ public function delete() {
+ return empty($this->back) ? NULL : array_pop($this->back);
+ }
+
+ /**
+ * Returns true if we are at the end of the list.
+ * @return bool
+ */
+ public function done() {
+ return empty($this->back);
+ }
+
+ /**
+ * Insert element before hole.
+ * @param Element to insert
+ */
+ public function insertBefore($t) {
+ if ($t !== NULL) array_push($this->front, $t);
+ }
+
+ /**
+ * Insert element after hole.
+ * @param Element to insert
+ */
+ public function insertAfter($t) {
+ if ($t !== NULL) array_push($this->back, $t);
+ }
+
+ /**
+ * Splice in multiple elements at hole. Functional specification
+ * in terms of array_splice:
+ *
+ * $arr1 = $arr;
+ * $old1 = array_splice($arr1, $i, $delete, $replacement);
+ *
+ * list($z, $t) = HTMLPurifier_Zipper::fromArray($arr);
+ * $t = $z->advance($t, $i);
+ * list($old2, $t) = $z->splice($t, $delete, $replacement);
+ * $arr2 = $z->toArray($t);
+ *
+ * assert($old1 === $old2);
+ * assert($arr1 === $arr2);
+ *
+ * NB: the absolute index location after this operation is
+ * *unchanged!*
+ *
+ * @param Current contents of hole.
+ */
+ public function splice($t, $delete, $replacement) {
+ // delete
+ $old = array();
+ $r = $t;
+ for ($i = $delete; $i > 0; $i--) {
+ $old[] = $r;
+ $r = $this->delete();
+ }
+ // insert
+ for ($i = count($replacement)-1; $i >= 0; $i--) {
+ $this->insertAfter($r);
+ $r = $replacement[$i];
+ }
+ return array($old, $r);
+ }
+}
diff --git a/classes/template/TemplateHandler.class.php b/classes/template/TemplateHandler.class.php
index d6831a6fb..de3bedbe5 100644
--- a/classes/template/TemplateHandler.class.php
+++ b/classes/template/TemplateHandler.class.php
@@ -461,7 +461,8 @@ class TemplateHandler
*/
private function _parseInline($buff)
{
- if(preg_match_all('/<([a-zA-Z]+\d?)(?>(?!<[a-z]+\d?[\s>]).)*?(?:[ \|]cond| loop)="/s', $buff, $match) === false)
+ preg_match_all('/<([a-zA-Z]+\d?)(?>(?!<[a-z]+\d?[\s>]).)*?(?:[ \|]cond| loop)="/s', $buff, $match);
+ if(empty($match))
{
return $buff;
}
diff --git a/common/js/plugins/handlebars.runtime/handlebars.runtime.js b/common/js/plugins/handlebars.runtime/handlebars.runtime.js
index 932fb7aea..79e753f31 100644
--- a/common/js/plugins/handlebars.runtime/handlebars.runtime.js
+++ b/common/js/plugins/handlebars.runtime/handlebars.runtime.js
@@ -1,6 +1,6 @@
/*!
- handlebars v2.0.0
+ handlebars v3.0.3
Copyright (C) 2011-2014 by Yehuda Katz
@@ -24,637 +24,889 @@ THE SOFTWARE.
@license
*/
-/* exported Handlebars */
-(function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- define([], factory);
- } else if (typeof exports === 'object') {
- module.exports = factory();
- } else {
- root.Handlebars = root.Handlebars || factory();
- }
-}(this, function () {
-// handlebars/safe-string.js
-var __module3__ = (function() {
- "use strict";
- var __exports__;
- // Build out our basic SafeString type
- function SafeString(string) {
- this.string = string;
- }
-
- SafeString.prototype.toString = function() {
- return "" + this.string;
- };
-
- __exports__ = SafeString;
- return __exports__;
-})();
-
-// handlebars/utils.js
-var __module2__ = (function(__dependency1__) {
- "use strict";
- var __exports__ = {};
- /*jshint -W004 */
- var SafeString = __dependency1__;
-
- var escape = {
- "&": "&",
- "<": "<",
- ">": ">",
- '"': """,
- "'": "'",
- "`": "`"
- };
-
- var badChars = /[&<>"'`]/g;
- var possible = /[&<>"'`]/;
-
- function escapeChar(chr) {
- return escape[chr];
- }
-
- function extend(obj /* , ...source */) {
- for (var i = 1; i < arguments.length; i++) {
- for (var key in arguments[i]) {
- if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
- obj[key] = arguments[i][key];
- }
- }
- }
-
- return obj;
- }
-
- __exports__.extend = extend;var toString = Object.prototype.toString;
- __exports__.toString = toString;
- // Sourced from lodash
- // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
- var isFunction = function(value) {
- return typeof value === 'function';
- };
- // fallback for older versions of Chrome and Safari
- /* istanbul ignore next */
- if (isFunction(/x/)) {
- isFunction = function(value) {
- return typeof value === 'function' && toString.call(value) === '[object Function]';
- };
- }
- var isFunction;
- __exports__.isFunction = isFunction;
- /* istanbul ignore next */
- var isArray = Array.isArray || function(value) {
- return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
- };
- __exports__.isArray = isArray;
-
- function escapeExpression(string) {
- // don't escape SafeStrings, since they're already safe
- if (string instanceof SafeString) {
- return string.toString();
- } else if (string == null) {
- return "";
- } else if (!string) {
- return string + '';
- }
-
- // Force a string conversion as this will be done by the append regardless and
- // the regex test will do this transparently behind the scenes, causing issues if
- // an object's to string has escaped characters in it.
- string = "" + string;
-
- if(!possible.test(string)) { return string; }
- return string.replace(badChars, escapeChar);
- }
-
- __exports__.escapeExpression = escapeExpression;function isEmpty(value) {
- if (!value && value !== 0) {
- return true;
- } else if (isArray(value) && value.length === 0) {
- return true;
- } else {
- return false;
- }
- }
-
- __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) {
- return (contextPath ? contextPath + '.' : '') + id;
- }
-
- __exports__.appendContextPath = appendContextPath;
- return __exports__;
-})(__module3__);
-
-// handlebars/exception.js
-var __module4__ = (function() {
- "use strict";
- var __exports__;
-
- var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
-
- function Exception(message, node) {
- var line;
- if (node && node.firstLine) {
- line = node.firstLine;
-
- message += ' - ' + line + ':' + node.firstColumn;
- }
-
- var tmp = Error.prototype.constructor.call(this, message);
-
- // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
- for (var idx = 0; idx < errorProps.length; idx++) {
- this[errorProps[idx]] = tmp[errorProps[idx]];
- }
-
- if (line) {
- this.lineNumber = line;
- this.column = node.firstColumn;
- }
- }
-
- Exception.prototype = new Error();
-
- __exports__ = Exception;
- return __exports__;
-})();
-
-// handlebars/base.js
-var __module1__ = (function(__dependency1__, __dependency2__) {
- "use strict";
- var __exports__ = {};
- var Utils = __dependency1__;
- var Exception = __dependency2__;
-
- var VERSION = "2.0.0";
- __exports__.VERSION = VERSION;var COMPILER_REVISION = 6;
- __exports__.COMPILER_REVISION = COMPILER_REVISION;
- var REVISION_CHANGES = {
- 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
- 2: '== 1.0.0-rc.3',
- 3: '== 1.0.0-rc.4',
- 4: '== 1.x.x',
- 5: '== 2.0.0-alpha.x',
- 6: '>= 2.0.0-beta.1'
- };
- __exports__.REVISION_CHANGES = REVISION_CHANGES;
- var isArray = Utils.isArray,
- isFunction = Utils.isFunction,
- toString = Utils.toString,
- objectType = '[object Object]';
-
- function HandlebarsEnvironment(helpers, partials) {
- this.helpers = helpers || {};
- this.partials = partials || {};
-
- registerDefaultHelpers(this);
- }
-
- __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = {
- constructor: HandlebarsEnvironment,
-
- logger: logger,
- log: log,
-
- registerHelper: function(name, fn) {
- if (toString.call(name) === objectType) {
- if (fn) { throw new Exception('Arg not supported with multiple helpers'); }
- Utils.extend(this.helpers, name);
- } else {
- this.helpers[name] = fn;
- }
- },
- unregisterHelper: function(name) {
- delete this.helpers[name];
- },
-
- registerPartial: function(name, partial) {
- if (toString.call(name) === objectType) {
- Utils.extend(this.partials, name);
- } else {
- this.partials[name] = partial;
- }
- },
- unregisterPartial: function(name) {
- delete this.partials[name];
- }
- };
-
- function registerDefaultHelpers(instance) {
- instance.registerHelper('helperMissing', function(/* [args, ]options */) {
- if(arguments.length === 1) {
- // A missing field in a {{foo}} constuct.
- return undefined;
- } else {
- // Someone is actually trying to call something, blow up.
- throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'");
- }
- });
-
- instance.registerHelper('blockHelperMissing', function(context, options) {
- var inverse = options.inverse,
- fn = options.fn;
-
- if(context === true) {
- return fn(this);
- } else if(context === false || context == null) {
- return inverse(this);
- } else if (isArray(context)) {
- if(context.length > 0) {
- if (options.ids) {
- options.ids = [options.name];
- }
-
- return instance.helpers.each(context, options);
- } else {
- return inverse(this);
- }
- } else {
- if (options.data && options.ids) {
- var data = createFrame(options.data);
- data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name);
- options = {data: data};
- }
-
- return fn(context, options);
- }
- });
-
- instance.registerHelper('each', function(context, options) {
- if (!options) {
- throw new Exception('Must pass iterator to #each');
- }
-
- var fn = options.fn, inverse = options.inverse;
- var i = 0, ret = "", data;
-
- var contextPath;
- if (options.data && options.ids) {
- contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.';
- }
-
- if (isFunction(context)) { context = context.call(this); }
-
- if (options.data) {
- data = createFrame(options.data);
- }
-
- if(context && typeof context === 'object') {
- if (isArray(context)) {
- for(var j = context.length; i= 2.0.0-beta.1'
+ };
+
+ exports.REVISION_CHANGES = REVISION_CHANGES;
+ var isArray = Utils.isArray,
+ isFunction = Utils.isFunction,
+ toString = Utils.toString,
+ objectType = '[object Object]';
+
+ function HandlebarsEnvironment(helpers, partials) {
+ this.helpers = helpers || {};
+ this.partials = partials || {};
+
+ registerDefaultHelpers(this);
+ }
+
+ HandlebarsEnvironment.prototype = {
+ constructor: HandlebarsEnvironment,
+
+ logger: logger,
+ log: log,
+
+ registerHelper: function registerHelper(name, fn) {
+ if (toString.call(name) === objectType) {
+ if (fn) {
+ throw new _Exception2['default']('Arg not supported with multiple helpers');
+ }
+ Utils.extend(this.helpers, name);
+ } else {
+ this.helpers[name] = fn;
+ }
+ },
+ unregisterHelper: function unregisterHelper(name) {
+ delete this.helpers[name];
+ },
+
+ registerPartial: function registerPartial(name, partial) {
+ if (toString.call(name) === objectType) {
+ Utils.extend(this.partials, name);
+ } else {
+ if (typeof partial === 'undefined') {
+ throw new _Exception2['default']('Attempting to register a partial as undefined');
+ }
+ this.partials[name] = partial;
+ }
+ },
+ unregisterPartial: function unregisterPartial(name) {
+ delete this.partials[name];
+ }
+ };
+
+ function registerDefaultHelpers(instance) {
+ instance.registerHelper('helperMissing', function () {
+ if (arguments.length === 1) {
+ // A missing field in a {{foo}} constuct.
+ return undefined;
+ } else {
+ // Someone is actually trying to call something, blow up.
+ throw new _Exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"');
+ }
+ });
+
+ instance.registerHelper('blockHelperMissing', function (context, options) {
+ var inverse = options.inverse,
+ fn = options.fn;
+
+ if (context === true) {
+ return fn(this);
+ } else if (context === false || context == null) {
+ return inverse(this);
+ } else if (isArray(context)) {
+ if (context.length > 0) {
+ if (options.ids) {
+ options.ids = [options.name];
+ }
+
+ return instance.helpers.each(context, options);
+ } else {
+ return inverse(this);
+ }
+ } else {
+ if (options.data && options.ids) {
+ var data = createFrame(options.data);
+ data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name);
+ options = { data: data };
+ }
+
+ return fn(context, options);
+ }
+ });
+
+ instance.registerHelper('each', function (context, options) {
+ if (!options) {
+ throw new _Exception2['default']('Must pass iterator to #each');
+ }
+
+ var fn = options.fn,
+ inverse = options.inverse,
+ i = 0,
+ ret = '',
+ data = undefined,
+ contextPath = undefined;
+
+ if (options.data && options.ids) {
+ contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.';
+ }
+
+ if (isFunction(context)) {
+ context = context.call(this);
+ }
+
+ if (options.data) {
+ data = createFrame(options.data);
+ }
+
+ function execIteration(field, index, last) {
+ if (data) {
+ data.key = field;
+ data.index = index;
+ data.first = index === 0;
+ data.last = !!last;
+
+ if (contextPath) {
+ data.contextPath = contextPath + field;
+ }
+ }
+
+ ret = ret + fn(context[field], {
+ data: data,
+ blockParams: Utils.blockParams([context[field], field], [contextPath + field, null])
+ });
+ }
+
+ if (context && typeof context === 'object') {
+ if (isArray(context)) {
+ for (var j = context.length; i < j; i++) {
+ execIteration(i, i, i === context.length - 1);
+ }
+ } else {
+ var priorKey = undefined;
+
+ for (var key in context) {
+ if (context.hasOwnProperty(key)) {
+ // We're running the iterations one step out of sync so we can detect
+ // the last iteration without have to scan the object twice and create
+ // an itermediate keys array.
+ if (priorKey) {
+ execIteration(priorKey, i - 1);
+ }
+ priorKey = key;
+ i++;
+ }
+ }
+ if (priorKey) {
+ execIteration(priorKey, i - 1, true);
+ }
+ }
+ }
+
+ if (i === 0) {
+ ret = inverse(this);
+ }
+
+ return ret;
+ });
+
+ instance.registerHelper('if', function (conditional, options) {
+ if (isFunction(conditional)) {
+ conditional = conditional.call(this);
+ }
+
+ // Default behavior is to render the positive path if the value is truthy and not empty.
+ // The `includeZero` option may be set to treat the condtional as purely not empty based on the
+ // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
+ if (!options.hash.includeZero && !conditional || Utils.isEmpty(conditional)) {
+ return options.inverse(this);
+ } else {
+ return options.fn(this);
+ }
+ });
+
+ instance.registerHelper('unless', function (conditional, options) {
+ return instance.helpers['if'].call(this, conditional, { fn: options.inverse, inverse: options.fn, hash: options.hash });
+ });
+
+ instance.registerHelper('with', function (context, options) {
+ if (isFunction(context)) {
+ context = context.call(this);
+ }
+
+ var fn = options.fn;
+
+ if (!Utils.isEmpty(context)) {
+ if (options.data && options.ids) {
+ var data = createFrame(options.data);
+ data.contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]);
+ options = { data: data };
+ }
+
+ return fn(context, options);
+ } else {
+ return options.inverse(this);
+ }
+ });
+
+ instance.registerHelper('log', function (message, options) {
+ var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
+ instance.log(level, message);
+ });
+
+ instance.registerHelper('lookup', function (obj, field) {
+ return obj && obj[field];
+ });
+ }
+
+ var logger = {
+ methodMap: { 0: 'debug', 1: 'info', 2: 'warn', 3: 'error' },
+
+ // State enum
+ DEBUG: 0,
+ INFO: 1,
+ WARN: 2,
+ ERROR: 3,
+ level: 1,
+
+ // Can be overridden in the host environment
+ log: function log(level, message) {
+ if (typeof console !== 'undefined' && logger.level <= level) {
+ var method = logger.methodMap[level];
+ (console[method] || console.log).call(console, message); // eslint-disable-line no-console
+ }
+ }
+ };
+
+ exports.logger = logger;
+ var log = logger.log;
+
+ exports.log = log;
+
+ function createFrame(object) {
+ var frame = Utils.extend({}, object);
+ frame._parent = object;
+ return frame;
+ }
+
+ /* [args, ]options */
+
+/***/ },
+/* 2 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+ // Build out our basic SafeString type
+ function SafeString(string) {
+ this.string = string;
+ }
+
+ SafeString.prototype.toString = SafeString.prototype.toHTML = function () {
+ return '' + this.string;
+ };
+
+ exports['default'] = SafeString;
+ module.exports = exports['default'];
+
+/***/ },
+/* 3 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+
+ var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
+
+ function Exception(message, node) {
+ var loc = node && node.loc,
+ line = undefined,
+ column = undefined;
+ if (loc) {
+ line = loc.start.line;
+ column = loc.start.column;
+
+ message += ' - ' + line + ':' + column;
+ }
+
+ var tmp = Error.prototype.constructor.call(this, message);
+
+ // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
+ for (var idx = 0; idx < errorProps.length; idx++) {
+ this[errorProps[idx]] = tmp[errorProps[idx]];
+ }
+
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, Exception);
+ }
+
+ if (loc) {
+ this.lineNumber = line;
+ this.column = column;
+ }
+ }
+
+ Exception.prototype = new Error();
+
+ exports['default'] = Exception;
+ module.exports = exports['default'];
+
+/***/ },
+/* 4 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+ exports.extend = extend;
+
+ // Older IE versions do not directly support indexOf so we must implement our own, sadly.
+ exports.indexOf = indexOf;
+ exports.escapeExpression = escapeExpression;
+ exports.isEmpty = isEmpty;
+ exports.blockParams = blockParams;
+ exports.appendContextPath = appendContextPath;
+ var escape = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ '\'': ''',
+ '`': '`'
+ };
+
+ var badChars = /[&<>"'`]/g,
+ possible = /[&<>"'`]/;
+
+ function escapeChar(chr) {
+ return escape[chr];
+ }
+
+ function extend(obj /* , ...source */) {
+ for (var i = 1; i < arguments.length; i++) {
+ for (var key in arguments[i]) {
+ if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
+ obj[key] = arguments[i][key];
+ }
+ }
+ }
+
+ return obj;
+ }
+
+ var toString = Object.prototype.toString;
+
+ exports.toString = toString;
+ // Sourced from lodash
+ // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
+ /*eslint-disable func-style, no-var */
+ var isFunction = function isFunction(value) {
+ return typeof value === 'function';
+ };
+ // fallback for older versions of Chrome and Safari
+ /* istanbul ignore next */
+ if (isFunction(/x/)) {
+ exports.isFunction = isFunction = function (value) {
+ return typeof value === 'function' && toString.call(value) === '[object Function]';
+ };
+ }
+ var isFunction;
+ exports.isFunction = isFunction;
+ /*eslint-enable func-style, no-var */
+
+ /* istanbul ignore next */
+ var isArray = Array.isArray || function (value) {
+ return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false;
+ };exports.isArray = isArray;
+
+ function indexOf(array, value) {
+ for (var i = 0, len = array.length; i < len; i++) {
+ if (array[i] === value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ function escapeExpression(string) {
+ if (typeof string !== 'string') {
+ // don't escape SafeStrings, since they're already safe
+ if (string && string.toHTML) {
+ return string.toHTML();
+ } else if (string == null) {
+ return '';
+ } else if (!string) {
+ return string + '';
+ }
+
+ // Force a string conversion as this will be done by the append regardless and
+ // the regex test will do this transparently behind the scenes, causing issues if
+ // an object's to string has escaped characters in it.
+ string = '' + string;
+ }
+
+ if (!possible.test(string)) {
+ return string;
+ }
+ return string.replace(badChars, escapeChar);
+ }
+
+ function isEmpty(value) {
+ if (!value && value !== 0) {
+ return true;
+ } else if (isArray(value) && value.length === 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function blockParams(params, ids) {
+ params.path = ids;
+ return params;
+ }
+
+ function appendContextPath(contextPath, id) {
+ return (contextPath ? contextPath + '.' : '') + id;
+ }
+
+/***/ },
+/* 5 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ var _interopRequireWildcard = __webpack_require__(7)['default'];
+
+ var _interopRequireDefault = __webpack_require__(8)['default'];
+
+ exports.__esModule = true;
+ exports.checkRevision = checkRevision;
+
+ // TODO: Remove this line and break up compilePartial
+
+ exports.template = template;
+ exports.wrapProgram = wrapProgram;
+ exports.resolvePartial = resolvePartial;
+ exports.invokePartial = invokePartial;
+ exports.noop = noop;
+
+ var _import = __webpack_require__(4);
+
+ var Utils = _interopRequireWildcard(_import);
+
+ var _Exception = __webpack_require__(3);
+
+ var _Exception2 = _interopRequireDefault(_Exception);
+
+ var _COMPILER_REVISION$REVISION_CHANGES$createFrame = __webpack_require__(1);
+
+ function checkRevision(compilerInfo) {
+ var compilerRevision = compilerInfo && compilerInfo[0] || 1,
+ currentRevision = _COMPILER_REVISION$REVISION_CHANGES$createFrame.COMPILER_REVISION;
+
+ if (compilerRevision !== currentRevision) {
+ if (compilerRevision < currentRevision) {
+ var runtimeVersions = _COMPILER_REVISION$REVISION_CHANGES$createFrame.REVISION_CHANGES[currentRevision],
+ compilerVersions = _COMPILER_REVISION$REVISION_CHANGES$createFrame.REVISION_CHANGES[compilerRevision];
+ throw new _Exception2['default']('Template was precompiled with an older version of Handlebars than the current runtime. ' + 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').');
+ } else {
+ // Use the embedded version info since the runtime doesn't know about this revision yet
+ throw new _Exception2['default']('Template was precompiled with a newer version of Handlebars than the current runtime. ' + 'Please update your runtime to a newer version (' + compilerInfo[1] + ').');
+ }
+ }
+ }
+
+ function template(templateSpec, env) {
+ /* istanbul ignore next */
+ if (!env) {
+ throw new _Exception2['default']('No environment passed to template');
+ }
+ if (!templateSpec || !templateSpec.main) {
+ throw new _Exception2['default']('Unknown template object: ' + typeof templateSpec);
+ }
+
+ // Note: Using env.VM references rather than local var references throughout this section to allow
+ // for external users to override these as psuedo-supported APIs.
+ env.VM.checkRevision(templateSpec.compiler);
+
+ function invokePartialWrapper(partial, context, options) {
+ if (options.hash) {
+ context = Utils.extend({}, context, options.hash);
+ }
+
+ partial = env.VM.resolvePartial.call(this, partial, context, options);
+ var result = env.VM.invokePartial.call(this, partial, context, options);
+
+ if (result == null && env.compile) {
+ options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
+ result = options.partials[options.name](context, options);
+ }
+ if (result != null) {
+ if (options.indent) {
+ var lines = result.split('\n');
+ for (var i = 0, l = lines.length; i < l; i++) {
+ if (!lines[i] && i + 1 === l) {
+ break;
+ }
+
+ lines[i] = options.indent + lines[i];
+ }
+ result = lines.join('\n');
+ }
+ return result;
+ } else {
+ throw new _Exception2['default']('The partial ' + options.name + ' could not be compiled when running in runtime-only mode');
+ }
+ }
+
+ // Just add water
+ var container = {
+ strict: function strict(obj, name) {
+ if (!(name in obj)) {
+ throw new _Exception2['default']('"' + name + '" not defined in ' + obj);
+ }
+ return obj[name];
+ },
+ lookup: function lookup(depths, name) {
+ var len = depths.length;
+ for (var i = 0; i < len; i++) {
+ if (depths[i] && depths[i][name] != null) {
+ return depths[i][name];
+ }
+ }
+ },
+ lambda: function lambda(current, context) {
+ return typeof current === 'function' ? current.call(context) : current;
+ },
+
+ escapeExpression: Utils.escapeExpression,
+ invokePartial: invokePartialWrapper,
+
+ fn: function fn(i) {
+ return templateSpec[i];
+ },
+
+ programs: [],
+ program: function program(i, data, declaredBlockParams, blockParams, depths) {
+ var programWrapper = this.programs[i],
+ fn = this.fn(i);
+ if (data || depths || blockParams || declaredBlockParams) {
+ programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths);
+ } else if (!programWrapper) {
+ programWrapper = this.programs[i] = wrapProgram(this, i, fn);
+ }
+ return programWrapper;
+ },
+
+ data: function data(value, depth) {
+ while (value && depth--) {
+ value = value._parent;
+ }
+ return value;
+ },
+ merge: function merge(param, common) {
+ var obj = param || common;
+
+ if (param && common && param !== common) {
+ obj = Utils.extend({}, common, param);
+ }
+
+ return obj;
+ },
+
+ noop: env.VM.noop,
+ compilerInfo: templateSpec.compiler
+ };
+
+ function ret(context) {
+ var options = arguments[1] === undefined ? {} : arguments[1];
+
+ var data = options.data;
+
+ ret._setup(options);
+ if (!options.partial && templateSpec.useData) {
+ data = initData(context, data);
+ }
+ var depths = undefined,
+ blockParams = templateSpec.useBlockParams ? [] : undefined;
+ if (templateSpec.useDepths) {
+ depths = options.depths ? [context].concat(options.depths) : [context];
+ }
+
+ return templateSpec.main.call(container, context, container.helpers, container.partials, data, blockParams, depths);
+ }
+ ret.isTop = true;
+
+ ret._setup = function (options) {
+ if (!options.partial) {
+ container.helpers = container.merge(options.helpers, env.helpers);
+
+ if (templateSpec.usePartial) {
+ container.partials = container.merge(options.partials, env.partials);
+ }
+ } else {
+ container.helpers = options.helpers;
+ container.partials = options.partials;
+ }
+ };
+
+ ret._child = function (i, data, blockParams, depths) {
+ if (templateSpec.useBlockParams && !blockParams) {
+ throw new _Exception2['default']('must pass block params');
+ }
+ if (templateSpec.useDepths && !depths) {
+ throw new _Exception2['default']('must pass parent depths');
+ }
+
+ return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths);
+ };
+ return ret;
+ }
+
+ function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) {
+ function prog(context) {
+ var options = arguments[1] === undefined ? {} : arguments[1];
+
+ return fn.call(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), depths && [context].concat(depths));
+ }
+ prog.program = i;
+ prog.depth = depths ? depths.length : 0;
+ prog.blockParams = declaredBlockParams || 0;
+ return prog;
+ }
+
+ function resolvePartial(partial, context, options) {
+ if (!partial) {
+ partial = options.partials[options.name];
+ } else if (!partial.call && !options.name) {
+ // This is a dynamic partial that returned a string
+ options.name = partial;
+ partial = options.partials[partial];
+ }
+ return partial;
+ }
+
+ function invokePartial(partial, context, options) {
+ options.partial = true;
+
+ if (partial === undefined) {
+ throw new _Exception2['default']('The partial ' + options.name + ' could not be found');
+ } else if (partial instanceof Function) {
+ return partial(context, options);
+ }
+ }
+
+ function noop() {
+ return '';
+ }
+
+ function initData(context, data) {
+ if (!data || !('root' in data)) {
+ data = data ? _COMPILER_REVISION$REVISION_CHANGES$createFrame.createFrame(data) : {};
+ data.root = context;
+ }
+ return data;
+ }
+
+/***/ },
+/* 6 */
+/***/ function(module, exports, __webpack_require__) {
+
+ /* WEBPACK VAR INJECTION */(function(global) {'use strict';
+
+ exports.__esModule = true;
+ /*global window */
+
+ exports['default'] = function (Handlebars) {
+ /* istanbul ignore next */
+ var root = typeof global !== 'undefined' ? global : window,
+ $Handlebars = root.Handlebars;
+ /* istanbul ignore next */
+ Handlebars.noConflict = function () {
+ if (root.Handlebars === Handlebars) {
+ root.Handlebars = $Handlebars;
+ }
+ };
+ };
+
+ module.exports = exports['default'];
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
+
+/***/ },
+/* 7 */
+/***/ function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+ exports["default"] = function (obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ } else {
+ var newObj = {};
+
+ if (typeof obj === "object" && obj !== null) {
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
+ }
+ }
+
+ newObj["default"] = obj;
+ return newObj;
+ }
+ };
+
+ exports.__esModule = true;
+
+/***/ },
+/* 8 */
+/***/ function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+ exports["default"] = function (obj) {
+ return obj && obj.__esModule ? obj : {
+ "default": obj
+ };
+ };
+
+ exports.__esModule = true;
+
+/***/ }
+/******/ ])
+});
+;
\ No newline at end of file
diff --git a/common/js/plugins/handlebars.runtime/handlebars.runtime.min.js b/common/js/plugins/handlebars.runtime/handlebars.runtime.min.js
index 59b1774f3..9caea7c48 100644
--- a/common/js/plugins/handlebars.runtime/handlebars.runtime.min.js
+++ b/common/js/plugins/handlebars.runtime/handlebars.runtime.min.js
@@ -1 +1 @@
-!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.Handlebars=a.Handlebars||b()}(this,function(){var a=function(){"use strict";function a(a){this.string=a}var b;return a.prototype.toString=function(){return""+this.string},b=a}(),b=function(a){"use strict";function b(a){return i[a]}function c(a){for(var b=1;b":">",'"':""","'":"'","`":"`"},j=/[&<>"'`]/g,k=/[&<>"'`]/;g.extend=c;var l=Object.prototype.toString;g.toString=l;var m=function(a){return"function"==typeof a};m(/x/)&&(m=function(a){return"function"==typeof a&&"[object Function]"===l.call(a)});var m;g.isFunction=m;var n=Array.isArray||function(a){return a&&"object"==typeof a?"[object Array]"===l.call(a):!1};return g.isArray=n,g.escapeExpression=d,g.isEmpty=e,g.appendContextPath=f,g}(a),c=function(){"use strict";function a(a,b){var d;b&&b.firstLine&&(d=b.firstLine,a+=" - "+d+":"+b.firstColumn);for(var e=Error.prototype.constructor.call(this,a),f=0;f0?(c.ids&&(c.ids=[c.name]),a.helpers.each(b,c)):d(this);if(c.data&&c.ids){var g=q(c.data);g.contextPath=f.appendContextPath(c.data.contextPath,c.name),c={data:g}}return e(b,c)}),a.registerHelper("each",function(a,b){if(!b)throw new g("Must pass iterator to #each");var c,d,e=b.fn,h=b.inverse,i=0,j="";if(b.data&&b.ids&&(d=f.appendContextPath(b.data.contextPath,b.ids[0])+"."),l(a)&&(a=a.call(this)),b.data&&(c=q(b.data)),a&&"object"==typeof a)if(k(a))for(var m=a.length;m>i;i++)c&&(c.index=i,c.first=0===i,c.last=i===a.length-1,d&&(c.contextPath=d+i)),j+=e(a[i],{data:c});else for(var n in a)a.hasOwnProperty(n)&&(c&&(c.key=n,c.index=i,c.first=0===i,d&&(c.contextPath=d+n)),j+=e(a[n],{data:c}),i++);return 0===i&&(j=h(this)),j}),a.registerHelper("if",function(a,b){return l(a)&&(a=a.call(this)),!b.hash.includeZero&&!a||f.isEmpty(a)?b.inverse(this):b.fn(this)}),a.registerHelper("unless",function(b,c){return a.helpers["if"].call(this,b,{fn:c.inverse,inverse:c.fn,hash:c.hash})}),a.registerHelper("with",function(a,b){l(a)&&(a=a.call(this));var c=b.fn;if(f.isEmpty(a))return b.inverse(this);if(b.data&&b.ids){var d=q(b.data);d.contextPath=f.appendContextPath(b.data.contextPath,b.ids[0]),b={data:d}}return c(a,b)}),a.registerHelper("log",function(b,c){var d=c.data&&null!=c.data.level?parseInt(c.data.level,10):1;a.log(d,b)}),a.registerHelper("lookup",function(a,b){return a&&a[b]})}var e={},f=a,g=b,h="2.0.0";e.VERSION=h;var i=6;e.COMPILER_REVISION=i;var j={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1"};e.REVISION_CHANGES=j;var k=f.isArray,l=f.isFunction,m=f.toString,n="[object Object]";e.HandlebarsEnvironment=c,c.prototype={constructor:c,logger:o,log:p,registerHelper:function(a,b){if(m.call(a)===n){if(b)throw new g("Arg not supported with multiple helpers");f.extend(this.helpers,a)}else this.helpers[a]=b},unregisterHelper:function(a){delete this.helpers[a]},registerPartial:function(a,b){m.call(a)===n?f.extend(this.partials,a):this.partials[a]=b},unregisterPartial:function(a){delete this.partials[a]}};var o={methodMap:{0:"debug",1:"info",2:"warn",3:"error"},DEBUG:0,INFO:1,WARN:2,ERROR:3,level:3,log:function(a,b){if(o.level<=a){var c=o.methodMap[a];"undefined"!=typeof console&&console[c]&&console[c].call(console,b)}}};e.logger=o;var p=o.log;e.log=p;var q=function(a){var b=f.extend({},a);return b._parent=a,b};return e.createFrame=q,e}(b,c),e=function(a,b,c){"use strict";function d(a){var b=a&&a[0]||1,c=m;if(b!==c){if(c>b){var d=n[c],e=n[b];throw new l("Template was precompiled with an older version of Handlebars than the current runtime. Please update your precompiler to a newer version ("+d+") or downgrade your runtime to an older version ("+e+").")}throw new l("Template was precompiled with a newer version of Handlebars than the current runtime. Please update your runtime to a newer version ("+a[1]+").")}}function e(a,b){if(!b)throw new l("No environment passed to template");if(!a||!a.main)throw new l("Unknown template object: "+typeof a);b.VM.checkRevision(a.compiler);var c=function(c,d,e,f,g,h,i,j,m){g&&(f=k.extend({},f,g));var n=b.VM.invokePartial.call(this,c,e,f,h,i,j,m);if(null==n&&b.compile){var o={helpers:h,partials:i,data:j,depths:m};i[e]=b.compile(c,{data:void 0!==j,compat:a.compat},b),n=i[e](f,o)}if(null!=n){if(d){for(var p=n.split("\n"),q=0,r=p.length;r>q&&(p[q]||q+1!==r);q++)p[q]=d+p[q];n=p.join("\n")}return n}throw new l("The partial "+e+" could not be compiled when running in runtime-only mode")},d={lookup:function(a,b){for(var c=a.length,d=0;c>d;d++)if(a[d]&&null!=a[d][b])return a[d][b]},lambda:function(a,b){return"function"==typeof a?a.call(b):a},escapeExpression:k.escapeExpression,invokePartial:c,fn:function(b){return a[b]},programs:[],program:function(a,b,c){var d=this.programs[a],e=this.fn(a);return b||c?d=f(this,a,e,b,c):d||(d=this.programs[a]=f(this,a,e)),d},data:function(a,b){for(;a&&b--;)a=a._parent;return a},merge:function(a,b){var c=a||b;return a&&b&&a!==b&&(c=k.extend({},b,a)),c},noop:b.VM.noop,compilerInfo:a.compiler},e=function(b,c){c=c||{};var f=c.data;e._setup(c),!c.partial&&a.useData&&(f=i(b,f));var g;return a.useDepths&&(g=c.depths?[b].concat(c.depths):[b]),a.main.call(d,b,d.helpers,d.partials,f,g)};return e.isTop=!0,e._setup=function(c){c.partial?(d.helpers=c.helpers,d.partials=c.partials):(d.helpers=d.merge(c.helpers,b.helpers),a.usePartial&&(d.partials=d.merge(c.partials,b.partials)))},e._child=function(b,c,e){if(a.useDepths&&!e)throw new l("must pass parent depths");return f(d,b,a[b],c,e)},e}function f(a,b,c,d,e){var f=function(b,f){return f=f||{},c.call(a,b,a.helpers,a.partials,f.data||d,e&&[b].concat(e))};return f.program=b,f.depth=e?e.length:0,f}function g(a,b,c,d,e,f,g){var h={partial:!0,helpers:d,partials:e,data:f,depths:g};if(void 0===a)throw new l("The partial "+b+" could not be found");return a instanceof Function?a(c,h):void 0}function h(){return""}function i(a,b){return b&&"root"in b||(b=b?o(b):{},b.root=a),b}var j={},k=a,l=b,m=c.COMPILER_REVISION,n=c.REVISION_CHANGES,o=c.createFrame;return j.checkRevision=d,j.template=e,j.program=f,j.invokePartial=g,j.noop=h,j}(b,c,d),f=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c,j=d,k=e,l=function(){var a=new g.HandlebarsEnvironment;return j.extend(a,g),a.SafeString=h,a.Exception=i,a.Utils=j,a.escapeExpression=j.escapeExpression,a.VM=k,a.template=function(b){return k.template(b,a)},a},m=l();return m.create=l,m["default"]=m,f=m}(d,a,c,b,e);return f});
\ No newline at end of file
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):"object"==typeof exports?exports.Handlebars=t():e.Handlebars=t()}(this,function(){return function(e){function t(n){if(r[n])return r[n].exports;var a=r[n]={exports:{},id:n,loaded:!1};return e[n].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){"use strict";function n(){var e=new s.HandlebarsEnvironment;return d.extend(e,s),e.SafeString=u["default"],e.Exception=f["default"],e.Utils=d,e.escapeExpression=d.escapeExpression,e.VM=m,e.template=function(t){return m.template(t,e)},e}var a=r(7)["default"],o=r(8)["default"];t.__esModule=!0;var i=r(1),s=a(i),l=r(2),u=o(l),c=r(3),f=o(c),p=r(4),d=a(p),h=r(5),m=a(h),v=r(6),g=o(v),w=n();w.create=n,g["default"](w),w["default"]=w,t["default"]=w,e.exports=t["default"]},function(e,t,r){"use strict";function n(e,t){this.helpers=e||{},this.partials=t||{},a(this)}function a(e){e.registerHelper("helperMissing",function(){if(1===arguments.length)return void 0;throw new f["default"]('Missing helper: "'+arguments[arguments.length-1].name+'"')}),e.registerHelper("blockHelperMissing",function(t,r){var n=r.inverse,a=r.fn;if(t===!0)return a(this);if(t===!1||null==t)return n(this);if(m(t))return t.length>0?(r.ids&&(r.ids=[r.name]),e.helpers.each(t,r)):n(this);if(r.data&&r.ids){var i=o(r.data);i.contextPath=u.appendContextPath(r.data.contextPath,r.name),r={data:i}}return a(t,r)}),e.registerHelper("each",function(e,t){function r(t,r,a){l&&(l.key=t,l.index=r,l.first=0===r,l.last=!!a,c&&(l.contextPath=c+t)),s+=n(e[t],{data:l,blockParams:u.blockParams([e[t],t],[c+t,null])})}if(!t)throw new f["default"]("Must pass iterator to #each");var n=t.fn,a=t.inverse,i=0,s="",l=void 0,c=void 0;if(t.data&&t.ids&&(c=u.appendContextPath(t.data.contextPath,t.ids[0])+"."),v(e)&&(e=e.call(this)),t.data&&(l=o(t.data)),e&&"object"==typeof e)if(m(e))for(var p=e.length;p>i;i++)r(i,i,i===e.length-1);else{var d=void 0;for(var h in e)e.hasOwnProperty(h)&&(d&&r(d,i-1),d=h,i++);d&&r(d,i-1,!0)}return 0===i&&(s=a(this)),s}),e.registerHelper("if",function(e,t){return v(e)&&(e=e.call(this)),!t.hash.includeZero&&!e||u.isEmpty(e)?t.inverse(this):t.fn(this)}),e.registerHelper("unless",function(t,r){return e.helpers["if"].call(this,t,{fn:r.inverse,inverse:r.fn,hash:r.hash})}),e.registerHelper("with",function(e,t){v(e)&&(e=e.call(this));var r=t.fn;if(u.isEmpty(e))return t.inverse(this);if(t.data&&t.ids){var n=o(t.data);n.contextPath=u.appendContextPath(t.data.contextPath,t.ids[0]),t={data:n}}return r(e,t)}),e.registerHelper("log",function(t,r){var n=r.data&&null!=r.data.level?parseInt(r.data.level,10):1;e.log(n,t)}),e.registerHelper("lookup",function(e,t){return e&&e[t]})}function o(e){var t=u.extend({},e);return t._parent=e,t}var i=r(7)["default"],s=r(8)["default"];t.__esModule=!0,t.HandlebarsEnvironment=n,t.createFrame=o;var l=r(4),u=i(l),c=r(3),f=s(c),p="3.0.1";t.VERSION=p;var d=6;t.COMPILER_REVISION=d;var h={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1"};t.REVISION_CHANGES=h;var m=u.isArray,v=u.isFunction,g=u.toString,w="[object Object]";n.prototype={constructor:n,logger:x,log:y,registerHelper:function(e,t){if(g.call(e)===w){if(t)throw new f["default"]("Arg not supported with multiple helpers");u.extend(this.helpers,e)}else this.helpers[e]=t},unregisterHelper:function(e){delete this.helpers[e]},registerPartial:function(e,t){if(g.call(e)===w)u.extend(this.partials,e);else{if("undefined"==typeof t)throw new f["default"]("Attempting to register a partial as undefined");this.partials[e]=t}},unregisterPartial:function(e){delete this.partials[e]}};var x={methodMap:{0:"debug",1:"info",2:"warn",3:"error"},DEBUG:0,INFO:1,WARN:2,ERROR:3,level:1,log:function(e,t){if("undefined"!=typeof console&&x.level<=e){var r=x.methodMap[e];(console[r]||console.log).call(console,t)}}};t.logger=x;var y=x.log;t.log=y},function(e,t){"use strict";function r(e){this.string=e}t.__esModule=!0,r.prototype.toString=r.prototype.toHTML=function(){return""+this.string},t["default"]=r,e.exports=t["default"]},function(e,t){"use strict";function r(e,t){var a=t&&t.loc,o=void 0,i=void 0;a&&(o=a.start.line,i=a.start.column,e+=" - "+o+":"+i);for(var s=Error.prototype.constructor.call(this,e),l=0;lr;r++)if(e[r]===t)return r;return-1}function o(e){if("string"!=typeof e){if(e&&e.toHTML)return e.toHTML();if(null==e)return"";if(!e)return e+"";e=""+e}return f.test(e)?e.replace(c,r):e}function i(e){return e||0===e?h(e)&&0===e.length?!0:!1:!0}function s(e,t){return e.path=t,e}function l(e,t){return(e?e+".":"")+t}t.__esModule=!0,t.extend=n,t.indexOf=a,t.escapeExpression=o,t.isEmpty=i,t.blockParams=s,t.appendContextPath=l;var u={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},c=/[&<>"'`]/g,f=/[&<>"'`]/,p=Object.prototype.toString;t.toString=p;var d=function(e){return"function"==typeof e};d(/x/)&&(t.isFunction=d=function(e){return"function"==typeof e&&"[object Function]"===p.call(e)});var d;t.isFunction=d;var h=Array.isArray||function(e){return e&&"object"==typeof e?"[object Array]"===p.call(e):!1};t.isArray=h},function(e,t,r){"use strict";function n(e){var t=e&&e[0]||1,r=v.COMPILER_REVISION;if(t!==r){if(r>t){var n=v.REVISION_CHANGES[r],a=v.REVISION_CHANGES[t];throw new m["default"]("Template was precompiled with an older version of Handlebars than the current runtime. Please update your precompiler to a newer version ("+n+") or downgrade your runtime to an older version ("+a+").")}throw new m["default"]("Template was precompiled with a newer version of Handlebars than the current runtime. Please update your runtime to a newer version ("+e[1]+").")}}function a(e,t){function r(r,n,a){a.hash&&(n=d.extend({},n,a.hash)),r=t.VM.resolvePartial.call(this,r,n,a);var o=t.VM.invokePartial.call(this,r,n,a);if(null==o&&t.compile&&(a.partials[a.name]=t.compile(r,e.compilerOptions,t),o=a.partials[a.name](n,a)),null!=o){if(a.indent){for(var i=o.split("\n"),s=0,l=i.length;l>s&&(i[s]||s+1!==l);s++)i[s]=a.indent+i[s];o=i.join("\n")}return o}throw new m["default"]("The partial "+a.name+" could not be compiled when running in runtime-only mode")}function n(t){var r=void 0===arguments[1]?{}:arguments[1],o=r.data;n._setup(r),!r.partial&&e.useData&&(o=u(t,o));var i=void 0,s=e.useBlockParams?[]:void 0;return e.useDepths&&(i=r.depths?[t].concat(r.depths):[t]),e.main.call(a,t,a.helpers,a.partials,o,s,i)}if(!t)throw new m["default"]("No environment passed to template");if(!e||!e.main)throw new m["default"]("Unknown template object: "+typeof e);t.VM.checkRevision(e.compiler);var a={strict:function(e,t){if(!(t in e))throw new m["default"]('"'+t+'" not defined in '+e);return e[t]},lookup:function(e,t){for(var r=e.length,n=0;r>n;n++)if(e[n]&&null!=e[n][t])return e[n][t]},lambda:function(e,t){return"function"==typeof e?e.call(t):e},escapeExpression:d.escapeExpression,invokePartial:r,fn:function(t){return e[t]},programs:[],program:function(e,t,r,n,a){var i=this.programs[e],s=this.fn(e);return t||a||n||r?i=o(this,e,s,t,r,n,a):i||(i=this.programs[e]=o(this,e,s)),i},data:function(e,t){for(;e&&t--;)e=e._parent;return e},merge:function(e,t){var r=e||t;return e&&t&&e!==t&&(r=d.extend({},t,e)),r},noop:t.VM.noop,compilerInfo:e.compiler};return n.isTop=!0,n._setup=function(r){r.partial?(a.helpers=r.helpers,a.partials=r.partials):(a.helpers=a.merge(r.helpers,t.helpers),e.usePartial&&(a.partials=a.merge(r.partials,t.partials)))},n._child=function(t,r,n,i){if(e.useBlockParams&&!n)throw new m["default"]("must pass block params");if(e.useDepths&&!i)throw new m["default"]("must pass parent depths");return o(a,t,e[t],r,0,n,i)},n}function o(e,t,r,n,a,o,i){function s(t){var a=void 0===arguments[1]?{}:arguments[1];return r.call(e,t,e.helpers,e.partials,a.data||n,o&&[a.blockParams].concat(o),i&&[t].concat(i))}return s.program=t,s.depth=i?i.length:0,s.blockParams=a||0,s}function i(e,t,r){return e?e.call||r.name||(r.name=e,e=r.partials[e]):e=r.partials[r.name],e}function s(e,t,r){if(r.partial=!0,void 0===e)throw new m["default"]("The partial "+r.name+" could not be found");return e instanceof Function?e(t,r):void 0}function l(){return""}function u(e,t){return t&&"root"in t||(t=t?v.createFrame(t):{},t.root=e),t}var c=r(7)["default"],f=r(8)["default"];t.__esModule=!0,t.checkRevision=n,t.template=a,t.wrapProgram=o,t.resolvePartial=i,t.invokePartial=s,t.noop=l;var p=r(4),d=c(p),h=r(3),m=f(h),v=r(1)},function(e,t){(function(r){"use strict";t.__esModule=!0,t["default"]=function(e){var t="undefined"!=typeof r?r:window,n=t.Handlebars;e.noConflict=function(){t.Handlebars===e&&(t.Handlebars=n)}},e.exports=t["default"]}).call(t,function(){return this}())},function(e,t){"use strict";t["default"]=function(e){if(e&&e.__esModule)return e;var t={};if("object"==typeof e&&null!==e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t["default"]=e,t},t.__esModule=!0},function(e,t){"use strict";t["default"]=function(e){return e&&e.__esModule?e:{"default":e}},t.__esModule=!0}])});
\ No newline at end of file
diff --git a/common/js/plugins/handlebars/handlebars.js b/common/js/plugins/handlebars/handlebars.js
index f826bbfd3..78f8d78db 100644
--- a/common/js/plugins/handlebars/handlebars.js
+++ b/common/js/plugins/handlebars/handlebars.js
@@ -1,6 +1,6 @@
/*!
- handlebars v2.0.0
+ handlebars v3.0.3
Copyright (C) 2011-2014 by Yehuda Katz
@@ -24,3056 +24,4108 @@ THE SOFTWARE.
@license
*/
-/* exported Handlebars */
-(function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- define([], factory);
- } else if (typeof exports === 'object') {
- module.exports = factory();
- } else {
- root.Handlebars = root.Handlebars || factory();
- }
-}(this, function () {
-// handlebars/safe-string.js
-var __module4__ = (function() {
- "use strict";
- var __exports__;
- // Build out our basic SafeString type
- function SafeString(string) {
- this.string = string;
- }
-
- SafeString.prototype.toString = function() {
- return "" + this.string;
- };
-
- __exports__ = SafeString;
- return __exports__;
-})();
-
-// handlebars/utils.js
-var __module3__ = (function(__dependency1__) {
- "use strict";
- var __exports__ = {};
- /*jshint -W004 */
- var SafeString = __dependency1__;
-
- var escape = {
- "&": "&",
- "<": "<",
- ">": ">",
- '"': """,
- "'": "'",
- "`": "`"
- };
-
- var badChars = /[&<>"'`]/g;
- var possible = /[&<>"'`]/;
-
- function escapeChar(chr) {
- return escape[chr];
- }
-
- function extend(obj /* , ...source */) {
- for (var i = 1; i < arguments.length; i++) {
- for (var key in arguments[i]) {
- if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
- obj[key] = arguments[i][key];
- }
- }
- }
-
- return obj;
- }
-
- __exports__.extend = extend;var toString = Object.prototype.toString;
- __exports__.toString = toString;
- // Sourced from lodash
- // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
- var isFunction = function(value) {
- return typeof value === 'function';
- };
- // fallback for older versions of Chrome and Safari
- /* istanbul ignore next */
- if (isFunction(/x/)) {
- isFunction = function(value) {
- return typeof value === 'function' && toString.call(value) === '[object Function]';
- };
- }
- var isFunction;
- __exports__.isFunction = isFunction;
- /* istanbul ignore next */
- var isArray = Array.isArray || function(value) {
- return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
- };
- __exports__.isArray = isArray;
-
- function escapeExpression(string) {
- // don't escape SafeStrings, since they're already safe
- if (string instanceof SafeString) {
- return string.toString();
- } else if (string == null) {
- return "";
- } else if (!string) {
- return string + '';
- }
-
- // Force a string conversion as this will be done by the append regardless and
- // the regex test will do this transparently behind the scenes, causing issues if
- // an object's to string has escaped characters in it.
- string = "" + string;
-
- if(!possible.test(string)) { return string; }
- return string.replace(badChars, escapeChar);
- }
-
- __exports__.escapeExpression = escapeExpression;function isEmpty(value) {
- if (!value && value !== 0) {
- return true;
- } else if (isArray(value) && value.length === 0) {
- return true;
- } else {
- return false;
- }
- }
-
- __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) {
- return (contextPath ? contextPath + '.' : '') + id;
- }
-
- __exports__.appendContextPath = appendContextPath;
- return __exports__;
-})(__module4__);
-
-// handlebars/exception.js
-var __module5__ = (function() {
- "use strict";
- var __exports__;
-
- var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
-
- function Exception(message, node) {
- var line;
- if (node && node.firstLine) {
- line = node.firstLine;
-
- message += ' - ' + line + ':' + node.firstColumn;
- }
-
- var tmp = Error.prototype.constructor.call(this, message);
-
- // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
- for (var idx = 0; idx < errorProps.length; idx++) {
- this[errorProps[idx]] = tmp[errorProps[idx]];
- }
-
- if (line) {
- this.lineNumber = line;
- this.column = node.firstColumn;
- }
- }
-
- Exception.prototype = new Error();
-
- __exports__ = Exception;
- return __exports__;
-})();
-
-// handlebars/base.js
-var __module2__ = (function(__dependency1__, __dependency2__) {
- "use strict";
- var __exports__ = {};
- var Utils = __dependency1__;
- var Exception = __dependency2__;
-
- var VERSION = "2.0.0";
- __exports__.VERSION = VERSION;var COMPILER_REVISION = 6;
- __exports__.COMPILER_REVISION = COMPILER_REVISION;
- var REVISION_CHANGES = {
- 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
- 2: '== 1.0.0-rc.3',
- 3: '== 1.0.0-rc.4',
- 4: '== 1.x.x',
- 5: '== 2.0.0-alpha.x',
- 6: '>= 2.0.0-beta.1'
- };
- __exports__.REVISION_CHANGES = REVISION_CHANGES;
- var isArray = Utils.isArray,
- isFunction = Utils.isFunction,
- toString = Utils.toString,
- objectType = '[object Object]';
-
- function HandlebarsEnvironment(helpers, partials) {
- this.helpers = helpers || {};
- this.partials = partials || {};
-
- registerDefaultHelpers(this);
- }
-
- __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = {
- constructor: HandlebarsEnvironment,
-
- logger: logger,
- log: log,
-
- registerHelper: function(name, fn) {
- if (toString.call(name) === objectType) {
- if (fn) { throw new Exception('Arg not supported with multiple helpers'); }
- Utils.extend(this.helpers, name);
- } else {
- this.helpers[name] = fn;
- }
- },
- unregisterHelper: function(name) {
- delete this.helpers[name];
- },
-
- registerPartial: function(name, partial) {
- if (toString.call(name) === objectType) {
- Utils.extend(this.partials, name);
- } else {
- this.partials[name] = partial;
- }
- },
- unregisterPartial: function(name) {
- delete this.partials[name];
- }
- };
-
- function registerDefaultHelpers(instance) {
- instance.registerHelper('helperMissing', function(/* [args, ]options */) {
- if(arguments.length === 1) {
- // A missing field in a {{foo}} constuct.
- return undefined;
- } else {
- // Someone is actually trying to call something, blow up.
- throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'");
- }
- });
-
- instance.registerHelper('blockHelperMissing', function(context, options) {
- var inverse = options.inverse,
- fn = options.fn;
-
- if(context === true) {
- return fn(this);
- } else if(context === false || context == null) {
- return inverse(this);
- } else if (isArray(context)) {
- if(context.length > 0) {
- if (options.ids) {
- options.ids = [options.name];
- }
-
- return instance.helpers.each(context, options);
- } else {
- return inverse(this);
- }
- } else {
- if (options.data && options.ids) {
- var data = createFrame(options.data);
- data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name);
- options = {data: data};
- }
-
- return fn(context, options);
- }
- });
-
- instance.registerHelper('each', function(context, options) {
- if (!options) {
- throw new Exception('Must pass iterator to #each');
- }
-
- var fn = options.fn, inverse = options.inverse;
- var i = 0, ret = "", data;
-
- var contextPath;
- if (options.data && options.ids) {
- contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.';
- }
-
- if (isFunction(context)) { context = context.call(this); }
-
- if (options.data) {
- data = createFrame(options.data);
- }
-
- if(context && typeof context === 'object') {
- if (isArray(context)) {
- for(var j = context.length; i 0) {
- throw new Exception("Invalid path: " + original, this);
- } else if (part === "..") {
- depth++;
- depthString += '../';
- } else {
- this.isScoped = true;
- }
- } else {
- dig.push(part);
- }
- }
-
- this.original = original;
- this.parts = dig;
- this.string = dig.join('.');
- this.depth = depth;
- this.idName = depthString + this.string;
-
- // an ID is simple if it only has one part, and that part is not
- // `..` or `this`.
- this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
-
- this.stringModeValue = this.string;
- },
-
- PartialNameNode: function(name, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "PARTIAL_NAME";
- this.name = name.original;
- },
-
- DataNode: function(id, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "DATA";
- this.id = id;
- this.stringModeValue = id.stringModeValue;
- this.idName = '@' + id.stringModeValue;
- },
-
- StringNode: function(string, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "STRING";
- this.original =
- this.string =
- this.stringModeValue = string;
- },
-
- NumberNode: function(number, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "NUMBER";
- this.original =
- this.number = number;
- this.stringModeValue = Number(number);
- },
-
- BooleanNode: function(bool, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "BOOLEAN";
- this.bool = bool;
- this.stringModeValue = bool === "true";
- },
-
- CommentNode: function(comment, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "comment";
- this.comment = comment;
-
- this.strip = {
- inlineStandalone: true
- };
- }
- };
-
-
- // Must be exported as an object rather than the root of the module as the jison lexer
- // most modify the object to operate properly.
- __exports__ = AST;
- return __exports__;
-})(__module5__);
-
-// handlebars/compiler/parser.js
-var __module9__ = (function() {
- "use strict";
- var __exports__;
- /* jshint ignore:start */
- /* istanbul ignore next */
- /* Jison generated parser */
- var handlebars = (function(){
- var parser = {trace: function trace() { },
- yy: {},
- symbols_: {"error":2,"root":3,"program":4,"EOF":5,"program_repetition0":6,"statement":7,"mustache":8,"block":9,"rawBlock":10,"partial":11,"CONTENT":12,"COMMENT":13,"openRawBlock":14,"END_RAW_BLOCK":15,"OPEN_RAW_BLOCK":16,"sexpr":17,"CLOSE_RAW_BLOCK":18,"openBlock":19,"block_option0":20,"closeBlock":21,"openInverse":22,"block_option1":23,"OPEN_BLOCK":24,"CLOSE":25,"OPEN_INVERSE":26,"inverseAndProgram":27,"INVERSE":28,"OPEN_ENDBLOCK":29,"path":30,"OPEN":31,"OPEN_UNESCAPED":32,"CLOSE_UNESCAPED":33,"OPEN_PARTIAL":34,"partialName":35,"param":36,"partial_option0":37,"partial_option1":38,"sexpr_repetition0":39,"sexpr_option0":40,"dataName":41,"STRING":42,"NUMBER":43,"BOOLEAN":44,"OPEN_SEXPR":45,"CLOSE_SEXPR":46,"hash":47,"hash_repetition_plus0":48,"hashSegment":49,"ID":50,"EQUALS":51,"DATA":52,"pathSegments":53,"SEP":54,"$accept":0,"$end":1},
- terminals_: {2:"error",5:"EOF",12:"CONTENT",13:"COMMENT",15:"END_RAW_BLOCK",16:"OPEN_RAW_BLOCK",18:"CLOSE_RAW_BLOCK",24:"OPEN_BLOCK",25:"CLOSE",26:"OPEN_INVERSE",28:"INVERSE",29:"OPEN_ENDBLOCK",31:"OPEN",32:"OPEN_UNESCAPED",33:"CLOSE_UNESCAPED",34:"OPEN_PARTIAL",42:"STRING",43:"NUMBER",44:"BOOLEAN",45:"OPEN_SEXPR",46:"CLOSE_SEXPR",50:"ID",51:"EQUALS",52:"DATA",54:"SEP"},
- productions_: [0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[10,3],[14,3],[9,4],[9,4],[19,3],[22,3],[27,2],[21,3],[8,3],[8,3],[11,5],[11,4],[17,3],[17,1],[36,1],[36,1],[36,1],[36,1],[36,1],[36,3],[47,1],[49,3],[35,1],[35,1],[35,1],[41,2],[30,1],[53,3],[53,1],[6,0],[6,2],[20,0],[20,1],[23,0],[23,1],[37,0],[37,1],[38,0],[38,1],[39,0],[39,2],[40,0],[40,1],[48,1],[48,2]],
- performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
-
- var $0 = $$.length - 1;
- switch (yystate) {
- case 1: yy.prepareProgram($$[$0-1].statements, true); return $$[$0-1];
- break;
- case 2:this.$ = new yy.ProgramNode(yy.prepareProgram($$[$0]), {}, this._$);
- break;
- case 3:this.$ = $$[$0];
- break;
- case 4:this.$ = $$[$0];
- break;
- case 5:this.$ = $$[$0];
- break;
- case 6:this.$ = $$[$0];
- break;
- case 7:this.$ = new yy.ContentNode($$[$0], this._$);
- break;
- case 8:this.$ = new yy.CommentNode($$[$0], this._$);
- break;
- case 9:this.$ = new yy.RawBlockNode($$[$0-2], $$[$0-1], $$[$0], this._$);
- break;
- case 10:this.$ = new yy.MustacheNode($$[$0-1], null, '', '', this._$);
- break;
- case 11:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], false, this._$);
- break;
- case 12:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], true, this._$);
- break;
- case 13:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 14:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 15:this.$ = { strip: yy.stripFlags($$[$0-1], $$[$0-1]), program: $$[$0] };
- break;
- case 16:this.$ = {path: $$[$0-1], strip: yy.stripFlags($$[$0-2], $$[$0])};
- break;
- case 17:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 18:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 19:this.$ = new yy.PartialNode($$[$0-3], $$[$0-2], $$[$0-1], yy.stripFlags($$[$0-4], $$[$0]), this._$);
- break;
- case 20:this.$ = new yy.PartialNode($$[$0-2], undefined, $$[$0-1], yy.stripFlags($$[$0-3], $$[$0]), this._$);
- break;
- case 21:this.$ = new yy.SexprNode([$$[$0-2]].concat($$[$0-1]), $$[$0], this._$);
- break;
- case 22:this.$ = new yy.SexprNode([$$[$0]], null, this._$);
- break;
- case 23:this.$ = $$[$0];
- break;
- case 24:this.$ = new yy.StringNode($$[$0], this._$);
- break;
- case 25:this.$ = new yy.NumberNode($$[$0], this._$);
- break;
- case 26:this.$ = new yy.BooleanNode($$[$0], this._$);
- break;
- case 27:this.$ = $$[$0];
- break;
- case 28:$$[$0-1].isHelper = true; this.$ = $$[$0-1];
- break;
- case 29:this.$ = new yy.HashNode($$[$0], this._$);
- break;
- case 30:this.$ = [$$[$0-2], $$[$0]];
- break;
- case 31:this.$ = new yy.PartialNameNode($$[$0], this._$);
- break;
- case 32:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0], this._$), this._$);
- break;
- case 33:this.$ = new yy.PartialNameNode(new yy.NumberNode($$[$0], this._$));
- break;
- case 34:this.$ = new yy.DataNode($$[$0], this._$);
- break;
- case 35:this.$ = new yy.IdNode($$[$0], this._$);
- break;
- case 36: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
- break;
- case 37:this.$ = [{part: $$[$0]}];
- break;
- case 38:this.$ = [];
- break;
- case 39:$$[$0-1].push($$[$0]);
- break;
- case 48:this.$ = [];
- break;
- case 49:$$[$0-1].push($$[$0]);
- break;
- case 52:this.$ = [$$[$0]];
- break;
- case 53:$$[$0-1].push($$[$0]);
- break;
- }
- },
- table: [{3:1,4:2,5:[2,38],6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],31:[2,38],32:[2,38],34:[2,38]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:[1,10],13:[1,11],14:16,16:[1,20],19:14,22:15,24:[1,18],26:[1,19],28:[2,2],29:[2,2],31:[1,12],32:[1,13],34:[1,17]},{1:[2,1]},{5:[2,39],12:[2,39],13:[2,39],16:[2,39],24:[2,39],26:[2,39],28:[2,39],29:[2,39],31:[2,39],32:[2,39],34:[2,39]},{5:[2,3],12:[2,3],13:[2,3],16:[2,3],24:[2,3],26:[2,3],28:[2,3],29:[2,3],31:[2,3],32:[2,3],34:[2,3]},{5:[2,4],12:[2,4],13:[2,4],16:[2,4],24:[2,4],26:[2,4],28:[2,4],29:[2,4],31:[2,4],32:[2,4],34:[2,4]},{5:[2,5],12:[2,5],13:[2,5],16:[2,5],24:[2,5],26:[2,5],28:[2,5],29:[2,5],31:[2,5],32:[2,5],34:[2,5]},{5:[2,6],12:[2,6],13:[2,6],16:[2,6],24:[2,6],26:[2,6],28:[2,6],29:[2,6],31:[2,6],32:[2,6],34:[2,6]},{5:[2,7],12:[2,7],13:[2,7],16:[2,7],24:[2,7],26:[2,7],28:[2,7],29:[2,7],31:[2,7],32:[2,7],34:[2,7]},{5:[2,8],12:[2,8],13:[2,8],16:[2,8],24:[2,8],26:[2,8],28:[2,8],29:[2,8],31:[2,8],32:[2,8],34:[2,8]},{17:21,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:27,30:22,41:23,50:[1,26],52:[1,25],53:24},{4:28,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{4:29,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{12:[1,30]},{30:32,35:31,42:[1,33],43:[1,34],50:[1,26],53:24},{17:35,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:36,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:37,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[1,38]},{18:[2,48],25:[2,48],33:[2,48],39:39,42:[2,48],43:[2,48],44:[2,48],45:[2,48],46:[2,48],50:[2,48],52:[2,48]},{18:[2,22],25:[2,22],33:[2,22],46:[2,22]},{18:[2,35],25:[2,35],33:[2,35],42:[2,35],43:[2,35],44:[2,35],45:[2,35],46:[2,35],50:[2,35],52:[2,35],54:[1,40]},{30:41,50:[1,26],53:24},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],52:[2,37],54:[2,37]},{33:[1,42]},{20:43,27:44,28:[1,45],29:[2,40]},{23:46,27:47,28:[1,45],29:[2,42]},{15:[1,48]},{25:[2,46],30:51,36:49,38:50,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],47:57,48:58,49:60,50:[1,59],52:[1,25],53:24},{25:[2,31],42:[2,31],43:[2,31],44:[2,31],45:[2,31],50:[2,31],52:[2,31]},{25:[2,32],42:[2,32],43:[2,32],44:[2,32],45:[2,32],50:[2,32],52:[2,32]},{25:[2,33],42:[2,33],43:[2,33],44:[2,33],45:[2,33],50:[2,33],52:[2,33]},{25:[1,61]},{25:[1,62]},{18:[1,63]},{5:[2,17],12:[2,17],13:[2,17],16:[2,17],24:[2,17],26:[2,17],28:[2,17],29:[2,17],31:[2,17],32:[2,17],34:[2,17]},{18:[2,50],25:[2,50],30:51,33:[2,50],36:65,40:64,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],46:[2,50],47:66,48:58,49:60,50:[1,59],52:[1,25],53:24},{50:[1,67]},{18:[2,34],25:[2,34],33:[2,34],42:[2,34],43:[2,34],44:[2,34],45:[2,34],46:[2,34],50:[2,34],52:[2,34]},{5:[2,18],12:[2,18],13:[2,18],16:[2,18],24:[2,18],26:[2,18],28:[2,18],29:[2,18],31:[2,18],32:[2,18],34:[2,18]},{21:68,29:[1,69]},{29:[2,41]},{4:70,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{21:71,29:[1,69]},{29:[2,43]},{5:[2,9],12:[2,9],13:[2,9],16:[2,9],24:[2,9],26:[2,9],28:[2,9],29:[2,9],31:[2,9],32:[2,9],34:[2,9]},{25:[2,44],37:72,47:73,48:58,49:60,50:[1,74]},{25:[1,75]},{18:[2,23],25:[2,23],33:[2,23],42:[2,23],43:[2,23],44:[2,23],45:[2,23],46:[2,23],50:[2,23],52:[2,23]},{18:[2,24],25:[2,24],33:[2,24],42:[2,24],43:[2,24],44:[2,24],45:[2,24],46:[2,24],50:[2,24],52:[2,24]},{18:[2,25],25:[2,25],33:[2,25],42:[2,25],43:[2,25],44:[2,25],45:[2,25],46:[2,25],50:[2,25],52:[2,25]},{18:[2,26],25:[2,26],33:[2,26],42:[2,26],43:[2,26],44:[2,26],45:[2,26],46:[2,26],50:[2,26],52:[2,26]},{18:[2,27],25:[2,27],33:[2,27],42:[2,27],43:[2,27],44:[2,27],45:[2,27],46:[2,27],50:[2,27],52:[2,27]},{17:76,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[2,47]},{18:[2,29],25:[2,29],33:[2,29],46:[2,29],49:77,50:[1,74]},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],51:[1,78],52:[2,37],54:[2,37]},{18:[2,52],25:[2,52],33:[2,52],46:[2,52],50:[2,52]},{12:[2,13],13:[2,13],16:[2,13],24:[2,13],26:[2,13],28:[2,13],29:[2,13],31:[2,13],32:[2,13],34:[2,13]},{12:[2,14],13:[2,14],16:[2,14],24:[2,14],26:[2,14],28:[2,14],29:[2,14],31:[2,14],32:[2,14],34:[2,14]},{12:[2,10]},{18:[2,21],25:[2,21],33:[2,21],46:[2,21]},{18:[2,49],25:[2,49],33:[2,49],42:[2,49],43:[2,49],44:[2,49],45:[2,49],46:[2,49],50:[2,49],52:[2,49]},{18:[2,51],25:[2,51],33:[2,51],46:[2,51]},{18:[2,36],25:[2,36],33:[2,36],42:[2,36],43:[2,36],44:[2,36],45:[2,36],46:[2,36],50:[2,36],52:[2,36],54:[2,36]},{5:[2,11],12:[2,11],13:[2,11],16:[2,11],24:[2,11],26:[2,11],28:[2,11],29:[2,11],31:[2,11],32:[2,11],34:[2,11]},{30:79,50:[1,26],53:24},{29:[2,15]},{5:[2,12],12:[2,12],13:[2,12],16:[2,12],24:[2,12],26:[2,12],28:[2,12],29:[2,12],31:[2,12],32:[2,12],34:[2,12]},{25:[1,80]},{25:[2,45]},{51:[1,78]},{5:[2,20],12:[2,20],13:[2,20],16:[2,20],24:[2,20],26:[2,20],28:[2,20],29:[2,20],31:[2,20],32:[2,20],34:[2,20]},{46:[1,81]},{18:[2,53],25:[2,53],33:[2,53],46:[2,53],50:[2,53]},{30:51,36:82,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],50:[1,26],52:[1,25],53:24},{25:[1,83]},{5:[2,19],12:[2,19],13:[2,19],16:[2,19],24:[2,19],26:[2,19],28:[2,19],29:[2,19],31:[2,19],32:[2,19],34:[2,19]},{18:[2,28],25:[2,28],33:[2,28],42:[2,28],43:[2,28],44:[2,28],45:[2,28],46:[2,28],50:[2,28],52:[2,28]},{18:[2,30],25:[2,30],33:[2,30],46:[2,30],50:[2,30]},{5:[2,16],12:[2,16],13:[2,16],16:[2,16],24:[2,16],26:[2,16],28:[2,16],29:[2,16],31:[2,16],32:[2,16],34:[2,16]}],
- defaultActions: {4:[2,1],44:[2,41],47:[2,43],57:[2,47],63:[2,10],70:[2,15],73:[2,45]},
- parseError: function parseError(str, hash) {
- throw new Error(str);
- },
- parse: function parse(input) {
- var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
- this.lexer.setInput(input);
- this.lexer.yy = this.yy;
- this.yy.lexer = this.lexer;
- this.yy.parser = this;
- if (typeof this.lexer.yylloc == "undefined")
- this.lexer.yylloc = {};
- var yyloc = this.lexer.yylloc;
- lstack.push(yyloc);
- var ranges = this.lexer.options && this.lexer.options.ranges;
- if (typeof this.yy.parseError === "function")
- this.parseError = this.yy.parseError;
- function popStack(n) {
- stack.length = stack.length - 2 * n;
- vstack.length = vstack.length - n;
- lstack.length = lstack.length - n;
- }
- function lex() {
- var token;
- token = self.lexer.lex() || 1;
- if (typeof token !== "number") {
- token = self.symbols_[token] || token;
- }
- return token;
- }
- var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
- while (true) {
- state = stack[stack.length - 1];
- if (this.defaultActions[state]) {
- action = this.defaultActions[state];
- } else {
- if (symbol === null || typeof symbol == "undefined") {
- symbol = lex();
- }
- action = table[state] && table[state][symbol];
- }
- if (typeof action === "undefined" || !action.length || !action[0]) {
- var errStr = "";
- if (!recovering) {
- expected = [];
- for (p in table[state])
- if (this.terminals_[p] && p > 2) {
- expected.push("'" + this.terminals_[p] + "'");
- }
- if (this.lexer.showPosition) {
- errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
- } else {
- errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
- }
- this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
- }
- }
- if (action[0] instanceof Array && action.length > 1) {
- throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
- }
- switch (action[0]) {
- case 1:
- stack.push(symbol);
- vstack.push(this.lexer.yytext);
- lstack.push(this.lexer.yylloc);
- stack.push(action[1]);
- symbol = null;
- if (!preErrorSymbol) {
- yyleng = this.lexer.yyleng;
- yytext = this.lexer.yytext;
- yylineno = this.lexer.yylineno;
- yyloc = this.lexer.yylloc;
- if (recovering > 0)
- recovering--;
- } else {
- symbol = preErrorSymbol;
- preErrorSymbol = null;
- }
- break;
- case 2:
- len = this.productions_[action[1]][1];
- yyval.$ = vstack[vstack.length - len];
- yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
- if (ranges) {
- yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
- }
- r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
- if (typeof r !== "undefined") {
- return r;
- }
- if (len) {
- stack = stack.slice(0, -1 * len * 2);
- vstack = vstack.slice(0, -1 * len);
- lstack = lstack.slice(0, -1 * len);
- }
- stack.push(this.productions_[action[1]][0]);
- vstack.push(yyval.$);
- lstack.push(yyval._$);
- newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
- stack.push(newState);
- break;
- case 3:
- return true;
- }
- }
- return true;
- }
- };
- /* Jison generated lexer */
- var lexer = (function(){
- var lexer = ({EOF:1,
- parseError:function parseError(str, hash) {
- if (this.yy.parser) {
- this.yy.parser.parseError(str, hash);
- } else {
- throw new Error(str);
- }
- },
- setInput:function (input) {
- this._input = input;
- this._more = this._less = this.done = false;
- this.yylineno = this.yyleng = 0;
- this.yytext = this.matched = this.match = '';
- this.conditionStack = ['INITIAL'];
- this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
- if (this.options.ranges) this.yylloc.range = [0,0];
- this.offset = 0;
- return this;
- },
- input:function () {
- var ch = this._input[0];
- this.yytext += ch;
- this.yyleng++;
- this.offset++;
- this.match += ch;
- this.matched += ch;
- var lines = ch.match(/(?:\r\n?|\n).*/g);
- if (lines) {
- this.yylineno++;
- this.yylloc.last_line++;
- } else {
- this.yylloc.last_column++;
- }
- if (this.options.ranges) this.yylloc.range[1]++;
-
- this._input = this._input.slice(1);
- return ch;
- },
- unput:function (ch) {
- var len = ch.length;
- var lines = ch.split(/(?:\r\n?|\n)/g);
-
- this._input = ch + this._input;
- this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
- //this.yyleng -= len;
- this.offset -= len;
- var oldLines = this.match.split(/(?:\r\n?|\n)/g);
- this.match = this.match.substr(0, this.match.length-1);
- this.matched = this.matched.substr(0, this.matched.length-1);
-
- if (lines.length-1) this.yylineno -= lines.length-1;
- var r = this.yylloc.range;
-
- this.yylloc = {first_line: this.yylloc.first_line,
- last_line: this.yylineno+1,
- first_column: this.yylloc.first_column,
- last_column: lines ?
- (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
- this.yylloc.first_column - len
- };
-
- if (this.options.ranges) {
- this.yylloc.range = [r[0], r[0] + this.yyleng - len];
- }
- return this;
- },
- more:function () {
- this._more = true;
- return this;
- },
- less:function (n) {
- this.unput(this.match.slice(n));
- },
- pastInput:function () {
- var past = this.matched.substr(0, this.matched.length - this.match.length);
- return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
- },
- upcomingInput:function () {
- var next = this.match;
- if (next.length < 20) {
- next += this._input.substr(0, 20-next.length);
- }
- return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
- },
- showPosition:function () {
- var pre = this.pastInput();
- var c = new Array(pre.length + 1).join("-");
- return pre + this.upcomingInput() + "\n" + c+"^";
- },
- next:function () {
- if (this.done) {
- return this.EOF;
- }
- if (!this._input) this.done = true;
-
- var token,
- match,
- tempMatch,
- index,
- col,
- lines;
- if (!this._more) {
- this.yytext = '';
- this.match = '';
- }
- var rules = this._currentRules();
- for (var i=0;i < rules.length; i++) {
- tempMatch = this._input.match(this.rules[rules[i]]);
- if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
- match = tempMatch;
- index = i;
- if (!this.options.flex) break;
- }
- }
- if (match) {
- lines = match[0].match(/(?:\r\n?|\n).*/g);
- if (lines) this.yylineno += lines.length;
- this.yylloc = {first_line: this.yylloc.last_line,
- last_line: this.yylineno+1,
- first_column: this.yylloc.last_column,
- last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
- this.yytext += match[0];
- this.match += match[0];
- this.matches = match;
- this.yyleng = this.yytext.length;
- if (this.options.ranges) {
- this.yylloc.range = [this.offset, this.offset += this.yyleng];
- }
- this._more = false;
- this._input = this._input.slice(match[0].length);
- this.matched += match[0];
- token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
- if (this.done && this._input) this.done = false;
- if (token) return token;
- else return;
- }
- if (this._input === "") {
- return this.EOF;
- } else {
- return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
- {text: "", token: null, line: this.yylineno});
- }
- },
- lex:function lex() {
- var r = this.next();
- if (typeof r !== 'undefined') {
- return r;
- } else {
- return this.lex();
- }
- },
- begin:function begin(condition) {
- this.conditionStack.push(condition);
- },
- popState:function popState() {
- return this.conditionStack.pop();
- },
- _currentRules:function _currentRules() {
- return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
- },
- topState:function () {
- return this.conditionStack[this.conditionStack.length-2];
- },
- pushState:function begin(condition) {
- this.begin(condition);
- }});
- lexer.options = {};
- lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
-
-
- function strip(start, end) {
- return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end);
- }
-
-
- var YYSTATE=YY_START
- switch($avoiding_name_collisions) {
- case 0:
- if(yy_.yytext.slice(-2) === "\\\\") {
- strip(0,1);
- this.begin("mu");
- } else if(yy_.yytext.slice(-1) === "\\") {
- strip(0,1);
- this.begin("emu");
- } else {
- this.begin("mu");
- }
- if(yy_.yytext) return 12;
-
- break;
- case 1:return 12;
- break;
- case 2:
- this.popState();
- return 12;
-
- break;
- case 3:
- yy_.yytext = yy_.yytext.substr(5, yy_.yyleng-9);
- this.popState();
- return 15;
-
- break;
- case 4: return 12;
- break;
- case 5:strip(0,4); this.popState(); return 13;
- break;
- case 6:return 45;
- break;
- case 7:return 46;
- break;
- case 8: return 16;
- break;
- case 9:
- this.popState();
- this.begin('raw');
- return 18;
-
- break;
- case 10:return 34;
- break;
- case 11:return 24;
- break;
- case 12:return 29;
- break;
- case 13:this.popState(); return 28;
- break;
- case 14:this.popState(); return 28;
- break;
- case 15:return 26;
- break;
- case 16:return 26;
- break;
- case 17:return 32;
- break;
- case 18:return 31;
- break;
- case 19:this.popState(); this.begin('com');
- break;
- case 20:strip(3,5); this.popState(); return 13;
- break;
- case 21:return 31;
- break;
- case 22:return 51;
- break;
- case 23:return 50;
- break;
- case 24:return 50;
- break;
- case 25:return 54;
- break;
- case 26:// ignore whitespace
- break;
- case 27:this.popState(); return 33;
- break;
- case 28:this.popState(); return 25;
- break;
- case 29:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 42;
- break;
- case 30:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 42;
- break;
- case 31:return 52;
- break;
- case 32:return 44;
- break;
- case 33:return 44;
- break;
- case 34:return 43;
- break;
- case 35:return 50;
- break;
- case 36:yy_.yytext = strip(1,2); return 50;
- break;
- case 37:return 'INVALID';
- break;
- case 38:return 5;
- break;
- }
- };
- lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
- lexer.conditions = {"mu":{"rules":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[5],"inclusive":false},"raw":{"rules":[3,4],"inclusive":false},"INITIAL":{"rules":[0,1,38],"inclusive":true}};
- return lexer;})()
- parser.lexer = lexer;
- function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
- return new Parser;
- })();__exports__ = handlebars;
- /* jshint ignore:end */
- return __exports__;
-})();
-
-// handlebars/compiler/helpers.js
-var __module10__ = (function(__dependency1__) {
- "use strict";
- var __exports__ = {};
- var Exception = __dependency1__;
-
- function stripFlags(open, close) {
- return {
- left: open.charAt(2) === '~',
- right: close.charAt(close.length-3) === '~'
- };
- }
-
- __exports__.stripFlags = stripFlags;
- function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) {
- /*jshint -W040 */
- if (mustache.sexpr.id.original !== close.path.original) {
- throw new Exception(mustache.sexpr.id.original + ' doesn\'t match ' + close.path.original, mustache);
- }
-
- var inverse = inverseAndProgram && inverseAndProgram.program;
-
- var strip = {
- left: mustache.strip.left,
- right: close.strip.right,
-
- // Determine the standalone candiacy. Basically flag our content as being possibly standalone
- // so our parent can determine if we actually are standalone
- openStandalone: isNextWhitespace(program.statements),
- closeStandalone: isPrevWhitespace((inverse || program).statements)
- };
-
- if (mustache.strip.right) {
- omitRight(program.statements, null, true);
- }
-
- if (inverse) {
- var inverseStrip = inverseAndProgram.strip;
-
- if (inverseStrip.left) {
- omitLeft(program.statements, null, true);
- }
- if (inverseStrip.right) {
- omitRight(inverse.statements, null, true);
- }
- if (close.strip.left) {
- omitLeft(inverse.statements, null, true);
- }
-
- // Find standalone else statments
- if (isPrevWhitespace(program.statements)
- && isNextWhitespace(inverse.statements)) {
-
- omitLeft(program.statements);
- omitRight(inverse.statements);
- }
- } else {
- if (close.strip.left) {
- omitLeft(program.statements, null, true);
- }
- }
-
- if (inverted) {
- return new this.BlockNode(mustache, inverse, program, strip, locInfo);
- } else {
- return new this.BlockNode(mustache, program, inverse, strip, locInfo);
- }
- }
-
- __exports__.prepareBlock = prepareBlock;
- function prepareProgram(statements, isRoot) {
- for (var i = 0, l = statements.length; i < l; i++) {
- var current = statements[i],
- strip = current.strip;
-
- if (!strip) {
- continue;
- }
-
- var _isPrevWhitespace = isPrevWhitespace(statements, i, isRoot, current.type === 'partial'),
- _isNextWhitespace = isNextWhitespace(statements, i, isRoot),
-
- openStandalone = strip.openStandalone && _isPrevWhitespace,
- closeStandalone = strip.closeStandalone && _isNextWhitespace,
- inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
-
- if (strip.right) {
- omitRight(statements, i, true);
- }
- if (strip.left) {
- omitLeft(statements, i, true);
- }
-
- if (inlineStandalone) {
- omitRight(statements, i);
-
- if (omitLeft(statements, i)) {
- // If we are on a standalone node, save the indent info for partials
- if (current.type === 'partial') {
- current.indent = (/([ \t]+$)/).exec(statements[i-1].original) ? RegExp.$1 : '';
- }
- }
- }
- if (openStandalone) {
- omitRight((current.program || current.inverse).statements);
-
- // Strip out the previous content node if it's whitespace only
- omitLeft(statements, i);
- }
- if (closeStandalone) {
- // Always strip the next node
- omitRight(statements, i);
-
- omitLeft((current.inverse || current.program).statements);
- }
- }
-
- return statements;
- }
-
- __exports__.prepareProgram = prepareProgram;function isPrevWhitespace(statements, i, isRoot) {
- if (i === undefined) {
- i = statements.length;
- }
-
- // Nodes that end with newlines are considered whitespace (but are special
- // cased for strip operations)
- var prev = statements[i-1],
- sibling = statements[i-2];
- if (!prev) {
- return isRoot;
- }
-
- if (prev.type === 'content') {
- return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original);
- }
- }
- function isNextWhitespace(statements, i, isRoot) {
- if (i === undefined) {
- i = -1;
- }
-
- var next = statements[i+1],
- sibling = statements[i+2];
- if (!next) {
- return isRoot;
- }
-
- if (next.type === 'content') {
- return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original);
- }
- }
-
- // Marks the node to the right of the position as omitted.
- // I.e. {{foo}}' ' will mark the ' ' node as omitted.
- //
- // If i is undefined, then the first child will be marked as such.
- //
- // If mulitple is truthy then all whitespace will be stripped out until non-whitespace
- // content is met.
- function omitRight(statements, i, multiple) {
- var current = statements[i == null ? 0 : i + 1];
- if (!current || current.type !== 'content' || (!multiple && current.rightStripped)) {
- return;
- }
-
- var original = current.string;
- current.string = current.string.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), '');
- current.rightStripped = current.string !== original;
- }
-
- // Marks the node to the left of the position as omitted.
- // I.e. ' '{{foo}} will mark the ' ' node as omitted.
- //
- // If i is undefined then the last child will be marked as such.
- //
- // If mulitple is truthy then all whitespace will be stripped out until non-whitespace
- // content is met.
- function omitLeft(statements, i, multiple) {
- var current = statements[i == null ? statements.length - 1 : i - 1];
- if (!current || current.type !== 'content' || (!multiple && current.leftStripped)) {
- return;
- }
-
- // We omit the last node if it's whitespace only and not preceeded by a non-content node.
- var original = current.string;
- current.string = current.string.replace(multiple ? (/\s+$/) : (/[ \t]+$/), '');
- current.leftStripped = current.string !== original;
- return current.leftStripped;
- }
- return __exports__;
-})(__module5__);
-
-// handlebars/compiler/base.js
-var __module8__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) {
- "use strict";
- var __exports__ = {};
- var parser = __dependency1__;
- var AST = __dependency2__;
- var Helpers = __dependency3__;
- var extend = __dependency4__.extend;
-
- __exports__.parser = parser;
-
- var yy = {};
- extend(yy, Helpers, AST);
-
- function parse(input) {
- // Just return if an already-compile AST was passed in.
- if (input.constructor === AST.ProgramNode) { return input; }
-
- parser.yy = yy;
-
- return parser.parse(input);
- }
-
- __exports__.parse = parse;
- return __exports__;
-})(__module9__, __module7__, __module10__, __module3__);
-
-// handlebars/compiler/compiler.js
-var __module11__ = (function(__dependency1__, __dependency2__) {
- "use strict";
- var __exports__ = {};
- var Exception = __dependency1__;
- var isArray = __dependency2__.isArray;
-
- var slice = [].slice;
-
- function Compiler() {}
-
- __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a
- // function in a context. This is necessary for mustache compatibility, which
- // requires that context functions in blocks are evaluated by blockHelperMissing,
- // and then proceed as if the resulting value was provided to blockHelperMissing.
-
- Compiler.prototype = {
- compiler: Compiler,
-
- equals: function(other) {
- var len = this.opcodes.length;
- if (other.opcodes.length !== len) {
- return false;
- }
-
- for (var i = 0; i < len; i++) {
- var opcode = this.opcodes[i],
- otherOpcode = other.opcodes[i];
- if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) {
- return false;
- }
- }
-
- // We know that length is the same between the two arrays because they are directly tied
- // to the opcode behavior above.
- len = this.children.length;
- for (i = 0; i < len; i++) {
- if (!this.children[i].equals(other.children[i])) {
- return false;
- }
- }
-
- return true;
- },
-
- guid: 0,
-
- compile: function(program, options) {
- this.opcodes = [];
- this.children = [];
- this.depths = {list: []};
- this.options = options;
- this.stringParams = options.stringParams;
- this.trackIds = options.trackIds;
-
- // These changes will propagate to the other compiler components
- var knownHelpers = this.options.knownHelpers;
- this.options.knownHelpers = {
- 'helperMissing': true,
- 'blockHelperMissing': true,
- 'each': true,
- 'if': true,
- 'unless': true,
- 'with': true,
- 'log': true,
- 'lookup': true
- };
- if (knownHelpers) {
- for (var name in knownHelpers) {
- this.options.knownHelpers[name] = knownHelpers[name];
- }
- }
-
- return this.accept(program);
- },
-
- accept: function(node) {
- return this[node.type](node);
- },
-
- program: function(program) {
- var statements = program.statements;
-
- for(var i=0, l=statements.length; i 0) {
- varDeclarations += ", " + locals.join(", ");
- }
-
- // Generate minimizer alias mappings
- for (var alias in this.aliases) {
- if (this.aliases.hasOwnProperty(alias)) {
- varDeclarations += ', ' + alias + '=' + this.aliases[alias];
- }
- }
-
- var params = ["depth0", "helpers", "partials", "data"];
-
- if (this.useDepths) {
- params.push('depths');
- }
-
- // Perform a second pass over the output to merge content when possible
- var source = this.mergeSource(varDeclarations);
-
- if (asObject) {
- params.push(source);
-
- return Function.apply(this, params);
- } else {
- return 'function(' + params.join(',') + ') {\n ' + source + '}';
- }
- },
- mergeSource: function(varDeclarations) {
- var source = '',
- buffer,
- appendOnly = !this.forceBuffer,
- appendFirst;
-
- for (var i = 0, len = this.source.length; i < len; i++) {
- var line = this.source[i];
- if (line.appendToBuffer) {
- if (buffer) {
- buffer = buffer + '\n + ' + line.content;
- } else {
- buffer = line.content;
- }
- } else {
- if (buffer) {
- if (!source) {
- appendFirst = true;
- source = buffer + ';\n ';
- } else {
- source += 'buffer += ' + buffer + ';\n ';
- }
- buffer = undefined;
- }
- source += line + '\n ';
-
- if (!this.environment.isSimple) {
- appendOnly = false;
- }
- }
- }
-
- if (appendOnly) {
- if (buffer || !source) {
- source += 'return ' + (buffer || '""') + ';\n';
- }
- } else {
- varDeclarations += ", buffer = " + (appendFirst ? '' : this.initializeBuffer());
- if (buffer) {
- source += 'return buffer + ' + buffer + ';\n';
- } else {
- source += 'return buffer;\n';
- }
- }
-
- if (varDeclarations) {
- source = 'var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n ') + source;
- }
-
- return source;
- },
-
- // [blockValue]
- //
- // On stack, before: hash, inverse, program, value
- // On stack, after: return value of blockHelperMissing
- //
- // The purpose of this opcode is to take a block of the form
- // `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and
- // replace it on the stack with the result of properly
- // invoking blockHelperMissing.
- blockValue: function(name) {
- this.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
-
- var params = [this.contextName(0)];
- this.setupParams(name, 0, params);
-
- var blockName = this.popStack();
- params.splice(1, 0, blockName);
-
- this.push('blockHelperMissing.call(' + params.join(', ') + ')');
- },
-
- // [ambiguousBlockValue]
- //
- // On stack, before: hash, inverse, program, value
- // Compiler value, before: lastHelper=value of last found helper, if any
- // On stack, after, if no lastHelper: same as [blockValue]
- // On stack, after, if lastHelper: value
- ambiguousBlockValue: function() {
- this.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
-
- // We're being a bit cheeky and reusing the options value from the prior exec
- var params = [this.contextName(0)];
- this.setupParams('', 0, params, true);
-
- this.flushInline();
-
- var current = this.topStack();
- params.splice(1, 0, current);
-
- this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
- },
-
- // [appendContent]
- //
- // On stack, before: ...
- // On stack, after: ...
- //
- // Appends the string value of `content` to the current buffer
- appendContent: function(content) {
- if (this.pendingContent) {
- content = this.pendingContent + content;
- }
-
- this.pendingContent = content;
- },
-
- // [append]
- //
- // On stack, before: value, ...
- // On stack, after: ...
- //
- // Coerces `value` to a String and appends it to the current buffer.
- //
- // If `value` is truthy, or 0, it is coerced into a string and appended
- // Otherwise, the empty string is appended
- append: function() {
- // Force anything that is inlined onto the stack so we don't have duplication
- // when we examine local
- this.flushInline();
- var local = this.popStack();
- this.pushSource('if (' + local + ' != null) { ' + this.appendToBuffer(local) + ' }');
- if (this.environment.isSimple) {
- this.pushSource("else { " + this.appendToBuffer("''") + " }");
- }
- },
-
- // [appendEscaped]
- //
- // On stack, before: value, ...
- // On stack, after: ...
- //
- // Escape `value` and append it to the buffer
- appendEscaped: function() {
- this.aliases.escapeExpression = 'this.escapeExpression';
-
- this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
- },
-
- // [getContext]
- //
- // On stack, before: ...
- // On stack, after: ...
- // Compiler value, after: lastContext=depth
- //
- // Set the value of the `lastContext` compiler value to the depth
- getContext: function(depth) {
- this.lastContext = depth;
- },
-
- // [pushContext]
- //
- // On stack, before: ...
- // On stack, after: currentContext, ...
- //
- // Pushes the value of the current context onto the stack.
- pushContext: function() {
- this.pushStackLiteral(this.contextName(this.lastContext));
- },
-
- // [lookupOnContext]
- //
- // On stack, before: ...
- // On stack, after: currentContext[name], ...
- //
- // Looks up the value of `name` on the current context and pushes
- // it onto the stack.
- lookupOnContext: function(parts, falsy, scoped) {
- /*jshint -W083 */
- var i = 0,
- len = parts.length;
-
- if (!scoped && this.options.compat && !this.lastContext) {
- // The depthed query is expected to handle the undefined logic for the root level that
- // is implemented below, so we evaluate that directly in compat mode
- this.push(this.depthedLookup(parts[i++]));
- } else {
- this.pushContext();
- }
-
- for (; i < len; i++) {
- this.replaceStack(function(current) {
- var lookup = this.nameLookup(current, parts[i], 'context');
- // We want to ensure that zero and false are handled properly if the context (falsy flag)
- // needs to have the special handling for these values.
- if (!falsy) {
- return ' != null ? ' + lookup + ' : ' + current;
- } else {
- // Otherwise we can use generic falsy handling
- return ' && ' + lookup;
- }
- });
- }
- },
-
- // [lookupData]
- //
- // On stack, before: ...
- // On stack, after: data, ...
- //
- // Push the data lookup operator
- lookupData: function(depth, parts) {
- /*jshint -W083 */
- if (!depth) {
- this.pushStackLiteral('data');
- } else {
- this.pushStackLiteral('this.data(data, ' + depth + ')');
- }
-
- var len = parts.length;
- for (var i = 0; i < len; i++) {
- this.replaceStack(function(current) {
- return ' && ' + this.nameLookup(current, parts[i], 'data');
- });
- }
- },
-
- // [resolvePossibleLambda]
- //
- // On stack, before: value, ...
- // On stack, after: resolved value, ...
- //
- // If the `value` is a lambda, replace it on the stack by
- // the return value of the lambda
- resolvePossibleLambda: function() {
- this.aliases.lambda = 'this.lambda';
-
- this.push('lambda(' + this.popStack() + ', ' + this.contextName(0) + ')');
- },
-
- // [pushStringParam]
- //
- // On stack, before: ...
- // On stack, after: string, currentContext, ...
- //
- // This opcode is designed for use in string mode, which
- // provides the string value of a parameter along with its
- // depth rather than resolving it immediately.
- pushStringParam: function(string, type) {
- this.pushContext();
- this.pushString(type);
-
- // If it's a subexpression, the string result
- // will be pushed after this opcode.
- if (type !== 'sexpr') {
- if (typeof string === 'string') {
- this.pushString(string);
- } else {
- this.pushStackLiteral(string);
- }
- }
- },
-
- emptyHash: function() {
- this.pushStackLiteral('{}');
-
- if (this.trackIds) {
- this.push('{}'); // hashIds
- }
- if (this.stringParams) {
- this.push('{}'); // hashContexts
- this.push('{}'); // hashTypes
- }
- },
- pushHash: function() {
- if (this.hash) {
- this.hashes.push(this.hash);
- }
- this.hash = {values: [], types: [], contexts: [], ids: []};
- },
- popHash: function() {
- var hash = this.hash;
- this.hash = this.hashes.pop();
-
- if (this.trackIds) {
- this.push('{' + hash.ids.join(',') + '}');
- }
- if (this.stringParams) {
- this.push('{' + hash.contexts.join(',') + '}');
- this.push('{' + hash.types.join(',') + '}');
- }
-
- this.push('{\n ' + hash.values.join(',\n ') + '\n }');
- },
-
- // [pushString]
- //
- // On stack, before: ...
- // On stack, after: quotedString(string), ...
- //
- // Push a quoted version of `string` onto the stack
- pushString: function(string) {
- this.pushStackLiteral(this.quotedString(string));
- },
-
- // [push]
- //
- // On stack, before: ...
- // On stack, after: expr, ...
- //
- // Push an expression onto the stack
- push: function(expr) {
- this.inlineStack.push(expr);
- return expr;
- },
-
- // [pushLiteral]
- //
- // On stack, before: ...
- // On stack, after: value, ...
- //
- // Pushes a value onto the stack. This operation prevents
- // the compiler from creating a temporary variable to hold
- // it.
- pushLiteral: function(value) {
- this.pushStackLiteral(value);
- },
-
- // [pushProgram]
- //
- // On stack, before: ...
- // On stack, after: program(guid), ...
- //
- // Push a program expression onto the stack. This takes
- // a compile-time guid and converts it into a runtime-accessible
- // expression.
- pushProgram: function(guid) {
- if (guid != null) {
- this.pushStackLiteral(this.programExpression(guid));
- } else {
- this.pushStackLiteral(null);
- }
- },
-
- // [invokeHelper]
- //
- // On stack, before: hash, inverse, program, params..., ...
- // On stack, after: result of helper invocation
- //
- // Pops off the helper's parameters, invokes the helper,
- // and pushes the helper's return value onto the stack.
- //
- // If the helper is not found, `helperMissing` is called.
- invokeHelper: function(paramSize, name, isSimple) {
- this.aliases.helperMissing = 'helpers.helperMissing';
-
- var nonHelper = this.popStack();
- var helper = this.setupHelper(paramSize, name);
-
- var lookup = (isSimple ? helper.name + ' || ' : '') + nonHelper + ' || helperMissing';
- this.push('((' + lookup + ').call(' + helper.callParams + '))');
- },
-
- // [invokeKnownHelper]
- //
- // On stack, before: hash, inverse, program, params..., ...
- // On stack, after: result of helper invocation
- //
- // This operation is used when the helper is known to exist,
- // so a `helperMissing` fallback is not required.
- invokeKnownHelper: function(paramSize, name) {
- var helper = this.setupHelper(paramSize, name);
- this.push(helper.name + ".call(" + helper.callParams + ")");
- },
-
- // [invokeAmbiguous]
- //
- // On stack, before: hash, inverse, program, params..., ...
- // On stack, after: result of disambiguation
- //
- // This operation is used when an expression like `{{foo}}`
- // is provided, but we don't know at compile-time whether it
- // is a helper or a path.
- //
- // This operation emits more code than the other options,
- // and can be avoided by passing the `knownHelpers` and
- // `knownHelpersOnly` flags at compile-time.
- invokeAmbiguous: function(name, helperCall) {
- this.aliases.functionType = '"function"';
- this.aliases.helperMissing = 'helpers.helperMissing';
- this.useRegister('helper');
-
- var nonHelper = this.popStack();
-
- this.emptyHash();
- var helper = this.setupHelper(0, name, helperCall);
-
- var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
-
- this.push(
- '((helper = (helper = ' + helperName + ' || ' + nonHelper + ') != null ? helper : helperMissing'
- + (helper.paramsInit ? '),(' + helper.paramsInit : '') + '),'
- + '(typeof helper === functionType ? helper.call(' + helper.callParams + ') : helper))');
- },
-
- // [invokePartial]
- //
- // On stack, before: context, ...
- // On stack after: result of partial invocation
- //
- // This operation pops off a context, invokes a partial with that context,
- // and pushes the result of the invocation back.
- invokePartial: function(name, indent) {
- var params = [this.nameLookup('partials', name, 'partial'), "'" + indent + "'", "'" + name + "'", this.popStack(), this.popStack(), "helpers", "partials"];
-
- if (this.options.data) {
- params.push("data");
- } else if (this.options.compat) {
- params.push('undefined');
- }
- if (this.options.compat) {
- params.push('depths');
- }
-
- this.push("this.invokePartial(" + params.join(", ") + ")");
- },
-
- // [assignToHash]
- //
- // On stack, before: value, ..., hash, ...
- // On stack, after: ..., hash, ...
- //
- // Pops a value off the stack and assigns it to the current hash
- assignToHash: function(key) {
- var value = this.popStack(),
- context,
- type,
- id;
-
- if (this.trackIds) {
- id = this.popStack();
- }
- if (this.stringParams) {
- type = this.popStack();
- context = this.popStack();
- }
-
- var hash = this.hash;
- if (context) {
- hash.contexts.push("'" + key + "': " + context);
- }
- if (type) {
- hash.types.push("'" + key + "': " + type);
- }
- if (id) {
- hash.ids.push("'" + key + "': " + id);
- }
- hash.values.push("'" + key + "': (" + value + ")");
- },
-
- pushId: function(type, name) {
- if (type === 'ID' || type === 'DATA') {
- this.pushString(name);
- } else if (type === 'sexpr') {
- this.pushStackLiteral('true');
- } else {
- this.pushStackLiteral('null');
- }
- },
-
- // HELPERS
-
- compiler: JavaScriptCompiler,
-
- compileChildren: function(environment, options) {
- var children = environment.children, child, compiler;
-
- for(var i=0, l=children.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
- return this.topStackName();
- },
- topStackName: function() {
- return "stack" + this.stackSlot;
- },
- flushInline: function() {
- var inlineStack = this.inlineStack;
- if (inlineStack.length) {
- this.inlineStack = [];
- for (var i = 0, len = inlineStack.length; i < len; i++) {
- var entry = inlineStack[i];
- if (entry instanceof Literal) {
- this.compileStack.push(entry);
- } else {
- this.pushStack(entry);
- }
- }
- }
- },
- isInline: function() {
- return this.inlineStack.length;
- },
-
- popStack: function(wrapped) {
- var inline = this.isInline(),
- item = (inline ? this.inlineStack : this.compileStack).pop();
-
- if (!wrapped && (item instanceof Literal)) {
- return item.value;
- } else {
- if (!inline) {
- /* istanbul ignore next */
- if (!this.stackSlot) {
- throw new Exception('Invalid stack pop');
- }
- this.stackSlot--;
- }
- return item;
- }
- },
-
- topStack: function() {
- var stack = (this.isInline() ? this.inlineStack : this.compileStack),
- item = stack[stack.length - 1];
-
- if (item instanceof Literal) {
- return item.value;
- } else {
- return item;
- }
- },
-
- contextName: function(context) {
- if (this.useDepths && context) {
- return 'depths[' + context + ']';
- } else {
- return 'depth' + context;
- }
- },
-
- quotedString: function(str) {
- return '"' + str
- .replace(/\\/g, '\\\\')
- .replace(/"/g, '\\"')
- .replace(/\n/g, '\\n')
- .replace(/\r/g, '\\r')
- .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
- .replace(/\u2029/g, '\\u2029') + '"';
- },
-
- objectLiteral: function(obj) {
- var pairs = [];
-
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- pairs.push(this.quotedString(key) + ':' + obj[key]);
- }
- }
-
- return '{' + pairs.join(',') + '}';
- },
-
- setupHelper: function(paramSize, name, blockHelper) {
- var params = [],
- paramsInit = this.setupParams(name, paramSize, params, blockHelper);
- var foundHelper = this.nameLookup('helpers', name, 'helper');
-
- return {
- params: params,
- paramsInit: paramsInit,
- name: foundHelper,
- callParams: [this.contextName(0)].concat(params).join(", ")
- };
- },
-
- setupOptions: function(helper, paramSize, params) {
- var options = {}, contexts = [], types = [], ids = [], param, inverse, program;
-
- options.name = this.quotedString(helper);
- options.hash = this.popStack();
-
- if (this.trackIds) {
- options.hashIds = this.popStack();
- }
- if (this.stringParams) {
- options.hashTypes = this.popStack();
- options.hashContexts = this.popStack();
- }
-
- inverse = this.popStack();
- program = this.popStack();
-
- // Avoid setting fn and inverse if neither are set. This allows
- // helpers to do a check for `if (options.fn)`
- if (program || inverse) {
- if (!program) {
- program = 'this.noop';
- }
-
- if (!inverse) {
- inverse = 'this.noop';
- }
-
- options.fn = program;
- options.inverse = inverse;
- }
-
- // The parameters go on to the stack in order (making sure that they are evaluated in order)
- // so we need to pop them off the stack in reverse order
- var i = paramSize;
- while (i--) {
- param = this.popStack();
- params[i] = param;
-
- if (this.trackIds) {
- ids[i] = this.popStack();
- }
- if (this.stringParams) {
- types[i] = this.popStack();
- contexts[i] = this.popStack();
- }
- }
-
- if (this.trackIds) {
- options.ids = "[" + ids.join(",") + "]";
- }
- if (this.stringParams) {
- options.types = "[" + types.join(",") + "]";
- options.contexts = "[" + contexts.join(",") + "]";
- }
-
- if (this.options.data) {
- options.data = "data";
- }
-
- return options;
- },
-
- // the params and contexts arguments are passed in arrays
- // to fill in
- setupParams: function(helperName, paramSize, params, useRegister) {
- var options = this.objectLiteral(this.setupOptions(helperName, paramSize, params));
-
- if (useRegister) {
- this.useRegister('options');
- params.push('options');
- return 'options=' + options;
- } else {
- params.push(options);
- return '';
- }
- }
- };
-
- var reservedWords = (
- "break else new var" +
- " case finally return void" +
- " catch for switch while" +
- " continue function this with" +
- " default if throw" +
- " delete in try" +
- " do instanceof typeof" +
- " abstract enum int short" +
- " boolean export interface static" +
- " byte extends long super" +
- " char final native synchronized" +
- " class float package throws" +
- " const goto private transient" +
- " debugger implements protected volatile" +
- " double import public let yield"
- ).split(" ");
-
- var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
-
- for(var i=0, l=reservedWords.length; i 1) {
+ throw new _Exception2['default']('Unsupported number of partial arguments: ' + params.length, partial);
+ } else if (!params.length) {
+ params.push({ type: 'PathExpression', parts: [], depth: 0 });
+ }
+
+ var partialName = partial.name.original,
+ isDynamic = partial.name.type === 'SubExpression';
+ if (isDynamic) {
+ this.accept(partial.name);
+ }
+
+ this.setupFullMustacheParams(partial, undefined, undefined, true);
+
+ var indent = partial.indent || '';
+ if (this.options.preventIndent && indent) {
+ this.opcode('appendContent', indent);
+ indent = '';
+ }
+
+ this.opcode('invokePartial', isDynamic, partialName, indent);
+ this.opcode('append');
+ },
+
+ MustacheStatement: function MustacheStatement(mustache) {
+ this.SubExpression(mustache); // eslint-disable-line new-cap
+
+ if (mustache.escaped && !this.options.noEscape) {
+ this.opcode('appendEscaped');
+ } else {
+ this.opcode('append');
+ }
+ },
+
+ ContentStatement: function ContentStatement(content) {
+ if (content.value) {
+ this.opcode('appendContent', content.value);
+ }
+ },
+
+ CommentStatement: function CommentStatement() {},
+
+ SubExpression: function SubExpression(sexpr) {
+ transformLiteralToPath(sexpr);
+ var type = this.classifySexpr(sexpr);
+
+ if (type === 'simple') {
+ this.simpleSexpr(sexpr);
+ } else if (type === 'helper') {
+ this.helperSexpr(sexpr);
+ } else {
+ this.ambiguousSexpr(sexpr);
+ }
+ },
+ ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) {
+ var path = sexpr.path,
+ name = path.parts[0],
+ isBlock = program != null || inverse != null;
+
+ this.opcode('getContext', path.depth);
+
+ this.opcode('pushProgram', program);
+ this.opcode('pushProgram', inverse);
+
+ this.accept(path);
+
+ this.opcode('invokeAmbiguous', name, isBlock);
+ },
+
+ simpleSexpr: function simpleSexpr(sexpr) {
+ this.accept(sexpr.path);
+ this.opcode('resolvePossibleLambda');
+ },
+
+ helperSexpr: function helperSexpr(sexpr, program, inverse) {
+ var params = this.setupFullMustacheParams(sexpr, program, inverse),
+ path = sexpr.path,
+ name = path.parts[0];
+
+ if (this.options.knownHelpers[name]) {
+ this.opcode('invokeKnownHelper', params.length, name);
+ } else if (this.options.knownHelpersOnly) {
+ throw new _Exception2['default']('You specified knownHelpersOnly, but used the unknown helper ' + name, sexpr);
+ } else {
+ path.falsy = true;
+
+ this.accept(path);
+ this.opcode('invokeHelper', params.length, path.original, _AST2['default'].helpers.simpleId(path));
+ }
+ },
+
+ PathExpression: function PathExpression(path) {
+ this.addDepth(path.depth);
+ this.opcode('getContext', path.depth);
+
+ var name = path.parts[0],
+ scoped = _AST2['default'].helpers.scopedId(path),
+ blockParamId = !path.depth && !scoped && this.blockParamIndex(name);
+
+ if (blockParamId) {
+ this.opcode('lookupBlockParam', blockParamId, path.parts);
+ } else if (!name) {
+ // Context reference, i.e. `{{foo .}}` or `{{foo ..}}`
+ this.opcode('pushContext');
+ } else if (path.data) {
+ this.options.data = true;
+ this.opcode('lookupData', path.depth, path.parts);
+ } else {
+ this.opcode('lookupOnContext', path.parts, path.falsy, scoped);
+ }
+ },
+
+ StringLiteral: function StringLiteral(string) {
+ this.opcode('pushString', string.value);
+ },
+
+ NumberLiteral: function NumberLiteral(number) {
+ this.opcode('pushLiteral', number.value);
+ },
+
+ BooleanLiteral: function BooleanLiteral(bool) {
+ this.opcode('pushLiteral', bool.value);
+ },
+
+ UndefinedLiteral: function UndefinedLiteral() {
+ this.opcode('pushLiteral', 'undefined');
+ },
+
+ NullLiteral: function NullLiteral() {
+ this.opcode('pushLiteral', 'null');
+ },
+
+ Hash: function Hash(hash) {
+ var pairs = hash.pairs,
+ i = 0,
+ l = pairs.length;
+
+ this.opcode('pushHash');
+
+ for (; i < l; i++) {
+ this.pushParam(pairs[i].value);
+ }
+ while (i--) {
+ this.opcode('assignToHash', pairs[i].key);
+ }
+ this.opcode('popHash');
+ },
+
+ // HELPERS
+ opcode: function opcode(name) {
+ this.opcodes.push({ opcode: name, args: slice.call(arguments, 1), loc: this.sourceNode[0].loc });
+ },
+
+ addDepth: function addDepth(depth) {
+ if (!depth) {
+ return;
+ }
+
+ this.useDepths = true;
+ },
+
+ classifySexpr: function classifySexpr(sexpr) {
+ var isSimple = _AST2['default'].helpers.simpleId(sexpr.path);
+
+ var isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]);
+
+ // a mustache is an eligible helper if:
+ // * its id is simple (a single part, not `this` or `..`)
+ var isHelper = !isBlockParam && _AST2['default'].helpers.helperExpression(sexpr);
+
+ // if a mustache is an eligible helper but not a definite
+ // helper, it is ambiguous, and will be resolved in a later
+ // pass or at runtime.
+ var isEligible = !isBlockParam && (isHelper || isSimple);
+
+ // if ambiguous, we can possibly resolve the ambiguity now
+ // An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc.
+ if (isEligible && !isHelper) {
+ var _name2 = sexpr.path.parts[0],
+ options = this.options;
+
+ if (options.knownHelpers[_name2]) {
+ isHelper = true;
+ } else if (options.knownHelpersOnly) {
+ isEligible = false;
+ }
+ }
+
+ if (isHelper) {
+ return 'helper';
+ } else if (isEligible) {
+ return 'ambiguous';
+ } else {
+ return 'simple';
+ }
+ },
+
+ pushParams: function pushParams(params) {
+ for (var i = 0, l = params.length; i < l; i++) {
+ this.pushParam(params[i]);
+ }
+ },
+
+ pushParam: function pushParam(val) {
+ var value = val.value != null ? val.value : val.original || '';
+
+ if (this.stringParams) {
+ if (value.replace) {
+ value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.');
+ }
+
+ if (val.depth) {
+ this.addDepth(val.depth);
+ }
+ this.opcode('getContext', val.depth || 0);
+ this.opcode('pushStringParam', value, val.type);
+
+ if (val.type === 'SubExpression') {
+ // SubExpressions get evaluated and passed in
+ // in string params mode.
+ this.accept(val);
+ }
+ } else {
+ if (this.trackIds) {
+ var blockParamIndex = undefined;
+ if (val.parts && !_AST2['default'].helpers.scopedId(val) && !val.depth) {
+ blockParamIndex = this.blockParamIndex(val.parts[0]);
+ }
+ if (blockParamIndex) {
+ var blockParamChild = val.parts.slice(1).join('.');
+ this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild);
+ } else {
+ value = val.original || value;
+ if (value.replace) {
+ value = value.replace(/^\.\//g, '').replace(/^\.$/g, '');
+ }
+
+ this.opcode('pushId', val.type, value);
+ }
+ }
+ this.accept(val);
+ }
+ },
+
+ setupFullMustacheParams: function setupFullMustacheParams(sexpr, program, inverse, omitEmpty) {
+ var params = sexpr.params;
+ this.pushParams(params);
+
+ this.opcode('pushProgram', program);
+ this.opcode('pushProgram', inverse);
+
+ if (sexpr.hash) {
+ this.accept(sexpr.hash);
+ } else {
+ this.opcode('emptyHash', omitEmpty);
+ }
+
+ return params;
+ },
+
+ blockParamIndex: function blockParamIndex(name) {
+ for (var depth = 0, len = this.options.blockParams.length; depth < len; depth++) {
+ var blockParams = this.options.blockParams[depth],
+ param = blockParams && _isArray$indexOf.indexOf(blockParams, name);
+ if (blockParams && param >= 0) {
+ return [depth, param];
+ }
+ }
+ }
+ };
+
+ function precompile(input, options, env) {
+ if (input == null || typeof input !== 'string' && input.type !== 'Program') {
+ throw new _Exception2['default']('You must pass a string or Handlebars AST to Handlebars.precompile. You passed ' + input);
+ }
+
+ options = options || {};
+ if (!('data' in options)) {
+ options.data = true;
+ }
+ if (options.compat) {
+ options.useDepths = true;
+ }
+
+ var ast = env.parse(input, options),
+ environment = new env.Compiler().compile(ast, options);
+ return new env.JavaScriptCompiler().compile(environment, options);
+ }
+
+ function compile(input, _x, env) {
+ var options = arguments[1] === undefined ? {} : arguments[1];
+
+ if (input == null || typeof input !== 'string' && input.type !== 'Program') {
+ throw new _Exception2['default']('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input);
+ }
+
+ if (!('data' in options)) {
+ options.data = true;
+ }
+ if (options.compat) {
+ options.useDepths = true;
+ }
+
+ var compiled = undefined;
+
+ function compileInput() {
+ var ast = env.parse(input, options),
+ environment = new env.Compiler().compile(ast, options),
+ templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true);
+ return env.template(templateSpec);
+ }
+
+ // Template is only compiled on first use and cached after that point.
+ function ret(context, execOptions) {
+ if (!compiled) {
+ compiled = compileInput();
+ }
+ return compiled.call(this, context, execOptions);
+ }
+ ret._setup = function (setupOptions) {
+ if (!compiled) {
+ compiled = compileInput();
+ }
+ return compiled._setup(setupOptions);
+ };
+ ret._child = function (i, data, blockParams, depths) {
+ if (!compiled) {
+ compiled = compileInput();
+ }
+ return compiled._child(i, data, blockParams, depths);
+ };
+ return ret;
+ }
+
+ function argEquals(a, b) {
+ if (a === b) {
+ return true;
+ }
+
+ if (_isArray$indexOf.isArray(a) && _isArray$indexOf.isArray(b) && a.length === b.length) {
+ for (var i = 0; i < a.length; i++) {
+ if (!argEquals(a[i], b[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ function transformLiteralToPath(sexpr) {
+ if (!sexpr.path.parts) {
+ var literal = sexpr.path;
+ // Casting to string here to make false and 0 literal values play nicely with the rest
+ // of the system.
+ sexpr.path = new _AST2['default'].PathExpression(false, 0, [literal.original + ''], literal.original + '', literal.loc);
+ }
+ }
+
+/***/ },
+/* 5 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ var _interopRequireDefault = __webpack_require__(8)['default'];
+
+ exports.__esModule = true;
+
+ var _COMPILER_REVISION$REVISION_CHANGES = __webpack_require__(10);
+
+ var _Exception = __webpack_require__(12);
+
+ var _Exception2 = _interopRequireDefault(_Exception);
+
+ var _isArray = __webpack_require__(13);
+
+ var _CodeGen = __webpack_require__(18);
+
+ var _CodeGen2 = _interopRequireDefault(_CodeGen);
+
+ function Literal(value) {
+ this.value = value;
+ }
+
+ function JavaScriptCompiler() {}
+
+ JavaScriptCompiler.prototype = {
+ // PUBLIC API: You can override these methods in a subclass to provide
+ // alternative compiled forms for name lookup and buffering semantics
+ nameLookup: function nameLookup(parent, name /* , type*/) {
+ if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
+ return [parent, '.', name];
+ } else {
+ return [parent, '[\'', name, '\']'];
+ }
+ },
+ depthedLookup: function depthedLookup(name) {
+ return [this.aliasable('this.lookup'), '(depths, "', name, '")'];
+ },
+
+ compilerInfo: function compilerInfo() {
+ var revision = _COMPILER_REVISION$REVISION_CHANGES.COMPILER_REVISION,
+ versions = _COMPILER_REVISION$REVISION_CHANGES.REVISION_CHANGES[revision];
+ return [revision, versions];
+ },
+
+ appendToBuffer: function appendToBuffer(source, location, explicit) {
+ // Force a source as this simplifies the merge logic.
+ if (!_isArray.isArray(source)) {
+ source = [source];
+ }
+ source = this.source.wrap(source, location);
+
+ if (this.environment.isSimple) {
+ return ['return ', source, ';'];
+ } else if (explicit) {
+ // This is a case where the buffer operation occurs as a child of another
+ // construct, generally braces. We have to explicitly output these buffer
+ // operations to ensure that the emitted code goes in the correct location.
+ return ['buffer += ', source, ';'];
+ } else {
+ source.appendToBuffer = true;
+ return source;
+ }
+ },
+
+ initializeBuffer: function initializeBuffer() {
+ return this.quotedString('');
+ },
+ // END PUBLIC API
+
+ compile: function compile(environment, options, context, asObject) {
+ this.environment = environment;
+ this.options = options;
+ this.stringParams = this.options.stringParams;
+ this.trackIds = this.options.trackIds;
+ this.precompile = !asObject;
+
+ this.name = this.environment.name;
+ this.isChild = !!context;
+ this.context = context || {
+ programs: [],
+ environments: []
+ };
+
+ this.preamble();
+
+ this.stackSlot = 0;
+ this.stackVars = [];
+ this.aliases = {};
+ this.registers = { list: [] };
+ this.hashes = [];
+ this.compileStack = [];
+ this.inlineStack = [];
+ this.blockParams = [];
+
+ this.compileChildren(environment, options);
+
+ this.useDepths = this.useDepths || environment.useDepths || this.options.compat;
+ this.useBlockParams = this.useBlockParams || environment.useBlockParams;
+
+ var opcodes = environment.opcodes,
+ opcode = undefined,
+ firstLoc = undefined,
+ i = undefined,
+ l = undefined;
+
+ for (i = 0, l = opcodes.length; i < l; i++) {
+ opcode = opcodes[i];
+
+ this.source.currentLocation = opcode.loc;
+ firstLoc = firstLoc || opcode.loc;
+ this[opcode.opcode].apply(this, opcode.args);
+ }
+
+ // Flush any trailing content that might be pending.
+ this.source.currentLocation = firstLoc;
+ this.pushSource('');
+
+ /* istanbul ignore next */
+ if (this.stackSlot || this.inlineStack.length || this.compileStack.length) {
+ throw new _Exception2['default']('Compile completed with content left on stack');
+ }
+
+ var fn = this.createFunctionContext(asObject);
+ if (!this.isChild) {
+ var ret = {
+ compiler: this.compilerInfo(),
+ main: fn
+ };
+ var programs = this.context.programs;
+ for (i = 0, l = programs.length; i < l; i++) {
+ if (programs[i]) {
+ ret[i] = programs[i];
+ }
+ }
+
+ if (this.environment.usePartial) {
+ ret.usePartial = true;
+ }
+ if (this.options.data) {
+ ret.useData = true;
+ }
+ if (this.useDepths) {
+ ret.useDepths = true;
+ }
+ if (this.useBlockParams) {
+ ret.useBlockParams = true;
+ }
+ if (this.options.compat) {
+ ret.compat = true;
+ }
+
+ if (!asObject) {
+ ret.compiler = JSON.stringify(ret.compiler);
+
+ this.source.currentLocation = { start: { line: 1, column: 0 } };
+ ret = this.objectLiteral(ret);
+
+ if (options.srcName) {
+ ret = ret.toStringWithSourceMap({ file: options.destName });
+ ret.map = ret.map && ret.map.toString();
+ } else {
+ ret = ret.toString();
+ }
+ } else {
+ ret.compilerOptions = this.options;
+ }
+
+ return ret;
+ } else {
+ return fn;
+ }
+ },
+
+ preamble: function preamble() {
+ // track the last context pushed into place to allow skipping the
+ // getContext opcode when it would be a noop
+ this.lastContext = 0;
+ this.source = new _CodeGen2['default'](this.options.srcName);
+ },
+
+ createFunctionContext: function createFunctionContext(asObject) {
+ var varDeclarations = '';
+
+ var locals = this.stackVars.concat(this.registers.list);
+ if (locals.length > 0) {
+ varDeclarations += ', ' + locals.join(', ');
+ }
+
+ // Generate minimizer alias mappings
+ //
+ // When using true SourceNodes, this will update all references to the given alias
+ // as the source nodes are reused in situ. For the non-source node compilation mode,
+ // aliases will not be used, but this case is already being run on the client and
+ // we aren't concern about minimizing the template size.
+ var aliasCount = 0;
+ for (var alias in this.aliases) {
+ // eslint-disable-line guard-for-in
+ var node = this.aliases[alias];
+
+ if (this.aliases.hasOwnProperty(alias) && node.children && node.referenceCount > 1) {
+ varDeclarations += ', alias' + ++aliasCount + '=' + alias;
+ node.children[0] = 'alias' + aliasCount;
+ }
+ }
+
+ var params = ['depth0', 'helpers', 'partials', 'data'];
+
+ if (this.useBlockParams || this.useDepths) {
+ params.push('blockParams');
+ }
+ if (this.useDepths) {
+ params.push('depths');
+ }
+
+ // Perform a second pass over the output to merge content when possible
+ var source = this.mergeSource(varDeclarations);
+
+ if (asObject) {
+ params.push(source);
+
+ return Function.apply(this, params);
+ } else {
+ return this.source.wrap(['function(', params.join(','), ') {\n ', source, '}']);
+ }
+ },
+ mergeSource: function mergeSource(varDeclarations) {
+ var isSimple = this.environment.isSimple,
+ appendOnly = !this.forceBuffer,
+ appendFirst = undefined,
+ sourceSeen = undefined,
+ bufferStart = undefined,
+ bufferEnd = undefined;
+ this.source.each(function (line) {
+ if (line.appendToBuffer) {
+ if (bufferStart) {
+ line.prepend(' + ');
+ } else {
+ bufferStart = line;
+ }
+ bufferEnd = line;
+ } else {
+ if (bufferStart) {
+ if (!sourceSeen) {
+ appendFirst = true;
+ } else {
+ bufferStart.prepend('buffer += ');
+ }
+ bufferEnd.add(';');
+ bufferStart = bufferEnd = undefined;
+ }
+
+ sourceSeen = true;
+ if (!isSimple) {
+ appendOnly = false;
+ }
+ }
+ });
+
+ if (appendOnly) {
+ if (bufferStart) {
+ bufferStart.prepend('return ');
+ bufferEnd.add(';');
+ } else if (!sourceSeen) {
+ this.source.push('return "";');
+ }
+ } else {
+ varDeclarations += ', buffer = ' + (appendFirst ? '' : this.initializeBuffer());
+
+ if (bufferStart) {
+ bufferStart.prepend('return buffer + ');
+ bufferEnd.add(';');
+ } else {
+ this.source.push('return buffer;');
+ }
+ }
+
+ if (varDeclarations) {
+ this.source.prepend('var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n'));
+ }
+
+ return this.source.merge();
+ },
+
+ // [blockValue]
+ //
+ // On stack, before: hash, inverse, program, value
+ // On stack, after: return value of blockHelperMissing
+ //
+ // The purpose of this opcode is to take a block of the form
+ // `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and
+ // replace it on the stack with the result of properly
+ // invoking blockHelperMissing.
+ blockValue: function blockValue(name) {
+ var blockHelperMissing = this.aliasable('helpers.blockHelperMissing'),
+ params = [this.contextName(0)];
+ this.setupHelperArgs(name, 0, params);
+
+ var blockName = this.popStack();
+ params.splice(1, 0, blockName);
+
+ this.push(this.source.functionCall(blockHelperMissing, 'call', params));
+ },
+
+ // [ambiguousBlockValue]
+ //
+ // On stack, before: hash, inverse, program, value
+ // Compiler value, before: lastHelper=value of last found helper, if any
+ // On stack, after, if no lastHelper: same as [blockValue]
+ // On stack, after, if lastHelper: value
+ ambiguousBlockValue: function ambiguousBlockValue() {
+ // We're being a bit cheeky and reusing the options value from the prior exec
+ var blockHelperMissing = this.aliasable('helpers.blockHelperMissing'),
+ params = [this.contextName(0)];
+ this.setupHelperArgs('', 0, params, true);
+
+ this.flushInline();
+
+ var current = this.topStack();
+ params.splice(1, 0, current);
+
+ this.pushSource(['if (!', this.lastHelper, ') { ', current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params), '}']);
+ },
+
+ // [appendContent]
+ //
+ // On stack, before: ...
+ // On stack, after: ...
+ //
+ // Appends the string value of `content` to the current buffer
+ appendContent: function appendContent(content) {
+ if (this.pendingContent) {
+ content = this.pendingContent + content;
+ } else {
+ this.pendingLocation = this.source.currentLocation;
+ }
+
+ this.pendingContent = content;
+ },
+
+ // [append]
+ //
+ // On stack, before: value, ...
+ // On stack, after: ...
+ //
+ // Coerces `value` to a String and appends it to the current buffer.
+ //
+ // If `value` is truthy, or 0, it is coerced into a string and appended
+ // Otherwise, the empty string is appended
+ append: function append() {
+ if (this.isInline()) {
+ this.replaceStack(function (current) {
+ return [' != null ? ', current, ' : ""'];
+ });
+
+ this.pushSource(this.appendToBuffer(this.popStack()));
+ } else {
+ var local = this.popStack();
+ this.pushSource(['if (', local, ' != null) { ', this.appendToBuffer(local, undefined, true), ' }']);
+ if (this.environment.isSimple) {
+ this.pushSource(['else { ', this.appendToBuffer('\'\'', undefined, true), ' }']);
+ }
+ }
+ },
+
+ // [appendEscaped]
+ //
+ // On stack, before: value, ...
+ // On stack, after: ...
+ //
+ // Escape `value` and append it to the buffer
+ appendEscaped: function appendEscaped() {
+ this.pushSource(this.appendToBuffer([this.aliasable('this.escapeExpression'), '(', this.popStack(), ')']));
+ },
+
+ // [getContext]
+ //
+ // On stack, before: ...
+ // On stack, after: ...
+ // Compiler value, after: lastContext=depth
+ //
+ // Set the value of the `lastContext` compiler value to the depth
+ getContext: function getContext(depth) {
+ this.lastContext = depth;
+ },
+
+ // [pushContext]
+ //
+ // On stack, before: ...
+ // On stack, after: currentContext, ...
+ //
+ // Pushes the value of the current context onto the stack.
+ pushContext: function pushContext() {
+ this.pushStackLiteral(this.contextName(this.lastContext));
+ },
+
+ // [lookupOnContext]
+ //
+ // On stack, before: ...
+ // On stack, after: currentContext[name], ...
+ //
+ // Looks up the value of `name` on the current context and pushes
+ // it onto the stack.
+ lookupOnContext: function lookupOnContext(parts, falsy, scoped) {
+ var i = 0;
+
+ if (!scoped && this.options.compat && !this.lastContext) {
+ // The depthed query is expected to handle the undefined logic for the root level that
+ // is implemented below, so we evaluate that directly in compat mode
+ this.push(this.depthedLookup(parts[i++]));
+ } else {
+ this.pushContext();
+ }
+
+ this.resolvePath('context', parts, i, falsy);
+ },
+
+ // [lookupBlockParam]
+ //
+ // On stack, before: ...
+ // On stack, after: blockParam[name], ...
+ //
+ // Looks up the value of `parts` on the given block param and pushes
+ // it onto the stack.
+ lookupBlockParam: function lookupBlockParam(blockParamId, parts) {
+ this.useBlockParams = true;
+
+ this.push(['blockParams[', blockParamId[0], '][', blockParamId[1], ']']);
+ this.resolvePath('context', parts, 1);
+ },
+
+ // [lookupData]
+ //
+ // On stack, before: ...
+ // On stack, after: data, ...
+ //
+ // Push the data lookup operator
+ lookupData: function lookupData(depth, parts) {
+ if (!depth) {
+ this.pushStackLiteral('data');
+ } else {
+ this.pushStackLiteral('this.data(data, ' + depth + ')');
+ }
+
+ this.resolvePath('data', parts, 0, true);
+ },
+
+ resolvePath: function resolvePath(type, parts, i, falsy) {
+ var _this = this;
+
+ if (this.options.strict || this.options.assumeObjects) {
+ this.push(strictLookup(this.options.strict, this, parts, type));
+ return;
+ }
+
+ var len = parts.length;
+ for (; i < len; i++) {
+ /*eslint-disable no-loop-func */
+ this.replaceStack(function (current) {
+ var lookup = _this.nameLookup(current, parts[i], type);
+ // We want to ensure that zero and false are handled properly if the context (falsy flag)
+ // needs to have the special handling for these values.
+ if (!falsy) {
+ return [' != null ? ', lookup, ' : ', current];
+ } else {
+ // Otherwise we can use generic falsy handling
+ return [' && ', lookup];
+ }
+ });
+ /*eslint-enable no-loop-func */
+ }
+ },
+
+ // [resolvePossibleLambda]
+ //
+ // On stack, before: value, ...
+ // On stack, after: resolved value, ...
+ //
+ // If the `value` is a lambda, replace it on the stack by
+ // the return value of the lambda
+ resolvePossibleLambda: function resolvePossibleLambda() {
+ this.push([this.aliasable('this.lambda'), '(', this.popStack(), ', ', this.contextName(0), ')']);
+ },
+
+ // [pushStringParam]
+ //
+ // On stack, before: ...
+ // On stack, after: string, currentContext, ...
+ //
+ // This opcode is designed for use in string mode, which
+ // provides the string value of a parameter along with its
+ // depth rather than resolving it immediately.
+ pushStringParam: function pushStringParam(string, type) {
+ this.pushContext();
+ this.pushString(type);
+
+ // If it's a subexpression, the string result
+ // will be pushed after this opcode.
+ if (type !== 'SubExpression') {
+ if (typeof string === 'string') {
+ this.pushString(string);
+ } else {
+ this.pushStackLiteral(string);
+ }
+ }
+ },
+
+ emptyHash: function emptyHash(omitEmpty) {
+ if (this.trackIds) {
+ this.push('{}'); // hashIds
+ }
+ if (this.stringParams) {
+ this.push('{}'); // hashContexts
+ this.push('{}'); // hashTypes
+ }
+ this.pushStackLiteral(omitEmpty ? 'undefined' : '{}');
+ },
+ pushHash: function pushHash() {
+ if (this.hash) {
+ this.hashes.push(this.hash);
+ }
+ this.hash = { values: [], types: [], contexts: [], ids: [] };
+ },
+ popHash: function popHash() {
+ var hash = this.hash;
+ this.hash = this.hashes.pop();
+
+ if (this.trackIds) {
+ this.push(this.objectLiteral(hash.ids));
+ }
+ if (this.stringParams) {
+ this.push(this.objectLiteral(hash.contexts));
+ this.push(this.objectLiteral(hash.types));
+ }
+
+ this.push(this.objectLiteral(hash.values));
+ },
+
+ // [pushString]
+ //
+ // On stack, before: ...
+ // On stack, after: quotedString(string), ...
+ //
+ // Push a quoted version of `string` onto the stack
+ pushString: function pushString(string) {
+ this.pushStackLiteral(this.quotedString(string));
+ },
+
+ // [pushLiteral]
+ //
+ // On stack, before: ...
+ // On stack, after: value, ...
+ //
+ // Pushes a value onto the stack. This operation prevents
+ // the compiler from creating a temporary variable to hold
+ // it.
+ pushLiteral: function pushLiteral(value) {
+ this.pushStackLiteral(value);
+ },
+
+ // [pushProgram]
+ //
+ // On stack, before: ...
+ // On stack, after: program(guid), ...
+ //
+ // Push a program expression onto the stack. This takes
+ // a compile-time guid and converts it into a runtime-accessible
+ // expression.
+ pushProgram: function pushProgram(guid) {
+ if (guid != null) {
+ this.pushStackLiteral(this.programExpression(guid));
+ } else {
+ this.pushStackLiteral(null);
+ }
+ },
+
+ // [invokeHelper]
+ //
+ // On stack, before: hash, inverse, program, params..., ...
+ // On stack, after: result of helper invocation
+ //
+ // Pops off the helper's parameters, invokes the helper,
+ // and pushes the helper's return value onto the stack.
+ //
+ // If the helper is not found, `helperMissing` is called.
+ invokeHelper: function invokeHelper(paramSize, name, isSimple) {
+ var nonHelper = this.popStack(),
+ helper = this.setupHelper(paramSize, name),
+ simple = isSimple ? [helper.name, ' || '] : '';
+
+ var lookup = ['('].concat(simple, nonHelper);
+ if (!this.options.strict) {
+ lookup.push(' || ', this.aliasable('helpers.helperMissing'));
+ }
+ lookup.push(')');
+
+ this.push(this.source.functionCall(lookup, 'call', helper.callParams));
+ },
+
+ // [invokeKnownHelper]
+ //
+ // On stack, before: hash, inverse, program, params..., ...
+ // On stack, after: result of helper invocation
+ //
+ // This operation is used when the helper is known to exist,
+ // so a `helperMissing` fallback is not required.
+ invokeKnownHelper: function invokeKnownHelper(paramSize, name) {
+ var helper = this.setupHelper(paramSize, name);
+ this.push(this.source.functionCall(helper.name, 'call', helper.callParams));
+ },
+
+ // [invokeAmbiguous]
+ //
+ // On stack, before: hash, inverse, program, params..., ...
+ // On stack, after: result of disambiguation
+ //
+ // This operation is used when an expression like `{{foo}}`
+ // is provided, but we don't know at compile-time whether it
+ // is a helper or a path.
+ //
+ // This operation emits more code than the other options,
+ // and can be avoided by passing the `knownHelpers` and
+ // `knownHelpersOnly` flags at compile-time.
+ invokeAmbiguous: function invokeAmbiguous(name, helperCall) {
+ this.useRegister('helper');
+
+ var nonHelper = this.popStack();
+
+ this.emptyHash();
+ var helper = this.setupHelper(0, name, helperCall);
+
+ var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
+
+ var lookup = ['(', '(helper = ', helperName, ' || ', nonHelper, ')'];
+ if (!this.options.strict) {
+ lookup[0] = '(helper = ';
+ lookup.push(' != null ? helper : ', this.aliasable('helpers.helperMissing'));
+ }
+
+ this.push(['(', lookup, helper.paramsInit ? ['),(', helper.paramsInit] : [], '),', '(typeof helper === ', this.aliasable('"function"'), ' ? ', this.source.functionCall('helper', 'call', helper.callParams), ' : helper))']);
+ },
+
+ // [invokePartial]
+ //
+ // On stack, before: context, ...
+ // On stack after: result of partial invocation
+ //
+ // This operation pops off a context, invokes a partial with that context,
+ // and pushes the result of the invocation back.
+ invokePartial: function invokePartial(isDynamic, name, indent) {
+ var params = [],
+ options = this.setupParams(name, 1, params, false);
+
+ if (isDynamic) {
+ name = this.popStack();
+ delete options.name;
+ }
+
+ if (indent) {
+ options.indent = JSON.stringify(indent);
+ }
+ options.helpers = 'helpers';
+ options.partials = 'partials';
+
+ if (!isDynamic) {
+ params.unshift(this.nameLookup('partials', name, 'partial'));
+ } else {
+ params.unshift(name);
+ }
+
+ if (this.options.compat) {
+ options.depths = 'depths';
+ }
+ options = this.objectLiteral(options);
+ params.push(options);
+
+ this.push(this.source.functionCall('this.invokePartial', '', params));
+ },
+
+ // [assignToHash]
+ //
+ // On stack, before: value, ..., hash, ...
+ // On stack, after: ..., hash, ...
+ //
+ // Pops a value off the stack and assigns it to the current hash
+ assignToHash: function assignToHash(key) {
+ var value = this.popStack(),
+ context = undefined,
+ type = undefined,
+ id = undefined;
+
+ if (this.trackIds) {
+ id = this.popStack();
+ }
+ if (this.stringParams) {
+ type = this.popStack();
+ context = this.popStack();
+ }
+
+ var hash = this.hash;
+ if (context) {
+ hash.contexts[key] = context;
+ }
+ if (type) {
+ hash.types[key] = type;
+ }
+ if (id) {
+ hash.ids[key] = id;
+ }
+ hash.values[key] = value;
+ },
+
+ pushId: function pushId(type, name, child) {
+ if (type === 'BlockParam') {
+ this.pushStackLiteral('blockParams[' + name[0] + '].path[' + name[1] + ']' + (child ? ' + ' + JSON.stringify('.' + child) : ''));
+ } else if (type === 'PathExpression') {
+ this.pushString(name);
+ } else if (type === 'SubExpression') {
+ this.pushStackLiteral('true');
+ } else {
+ this.pushStackLiteral('null');
+ }
+ },
+
+ // HELPERS
+
+ compiler: JavaScriptCompiler,
+
+ compileChildren: function compileChildren(environment, options) {
+ var children = environment.children,
+ child = undefined,
+ compiler = undefined;
+
+ for (var i = 0, l = children.length; i < l; i++) {
+ child = children[i];
+ compiler = new this.compiler(); // eslint-disable-line new-cap
+
+ var index = this.matchExistingProgram(child);
+
+ if (index == null) {
+ this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
+ index = this.context.programs.length;
+ child.index = index;
+ child.name = 'program' + index;
+ this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile);
+ this.context.environments[index] = child;
+
+ this.useDepths = this.useDepths || compiler.useDepths;
+ this.useBlockParams = this.useBlockParams || compiler.useBlockParams;
+ } else {
+ child.index = index;
+ child.name = 'program' + index;
+
+ this.useDepths = this.useDepths || child.useDepths;
+ this.useBlockParams = this.useBlockParams || child.useBlockParams;
+ }
+ }
+ },
+ matchExistingProgram: function matchExistingProgram(child) {
+ for (var i = 0, len = this.context.environments.length; i < len; i++) {
+ var environment = this.context.environments[i];
+ if (environment && environment.equals(child)) {
+ return i;
+ }
+ }
+ },
+
+ programExpression: function programExpression(guid) {
+ var child = this.environment.children[guid],
+ programParams = [child.index, 'data', child.blockParams];
+
+ if (this.useBlockParams || this.useDepths) {
+ programParams.push('blockParams');
+ }
+ if (this.useDepths) {
+ programParams.push('depths');
+ }
+
+ return 'this.program(' + programParams.join(', ') + ')';
+ },
+
+ useRegister: function useRegister(name) {
+ if (!this.registers[name]) {
+ this.registers[name] = true;
+ this.registers.list.push(name);
+ }
+ },
+
+ push: function push(expr) {
+ if (!(expr instanceof Literal)) {
+ expr = this.source.wrap(expr);
+ }
+
+ this.inlineStack.push(expr);
+ return expr;
+ },
+
+ pushStackLiteral: function pushStackLiteral(item) {
+ this.push(new Literal(item));
+ },
+
+ pushSource: function pushSource(source) {
+ if (this.pendingContent) {
+ this.source.push(this.appendToBuffer(this.source.quotedString(this.pendingContent), this.pendingLocation));
+ this.pendingContent = undefined;
+ }
+
+ if (source) {
+ this.source.push(source);
+ }
+ },
+
+ replaceStack: function replaceStack(callback) {
+ var prefix = ['('],
+ stack = undefined,
+ createdStack = undefined,
+ usedLiteral = undefined;
+
+ /* istanbul ignore next */
+ if (!this.isInline()) {
+ throw new _Exception2['default']('replaceStack on non-inline');
+ }
+
+ // We want to merge the inline statement into the replacement statement via ','
+ var top = this.popStack(true);
+
+ if (top instanceof Literal) {
+ // Literals do not need to be inlined
+ stack = [top.value];
+ prefix = ['(', stack];
+ usedLiteral = true;
+ } else {
+ // Get or create the current stack name for use by the inline
+ createdStack = true;
+ var _name = this.incrStack();
+
+ prefix = ['((', this.push(_name), ' = ', top, ')'];
+ stack = this.topStack();
+ }
+
+ var item = callback.call(this, stack);
+
+ if (!usedLiteral) {
+ this.popStack();
+ }
+ if (createdStack) {
+ this.stackSlot--;
+ }
+ this.push(prefix.concat(item, ')'));
+ },
+
+ incrStack: function incrStack() {
+ this.stackSlot++;
+ if (this.stackSlot > this.stackVars.length) {
+ this.stackVars.push('stack' + this.stackSlot);
+ }
+ return this.topStackName();
+ },
+ topStackName: function topStackName() {
+ return 'stack' + this.stackSlot;
+ },
+ flushInline: function flushInline() {
+ var inlineStack = this.inlineStack;
+ this.inlineStack = [];
+ for (var i = 0, len = inlineStack.length; i < len; i++) {
+ var entry = inlineStack[i];
+ /* istanbul ignore if */
+ if (entry instanceof Literal) {
+ this.compileStack.push(entry);
+ } else {
+ var stack = this.incrStack();
+ this.pushSource([stack, ' = ', entry, ';']);
+ this.compileStack.push(stack);
+ }
+ }
+ },
+ isInline: function isInline() {
+ return this.inlineStack.length;
+ },
+
+ popStack: function popStack(wrapped) {
+ var inline = this.isInline(),
+ item = (inline ? this.inlineStack : this.compileStack).pop();
+
+ if (!wrapped && item instanceof Literal) {
+ return item.value;
+ } else {
+ if (!inline) {
+ /* istanbul ignore next */
+ if (!this.stackSlot) {
+ throw new _Exception2['default']('Invalid stack pop');
+ }
+ this.stackSlot--;
+ }
+ return item;
+ }
+ },
+
+ topStack: function topStack() {
+ var stack = this.isInline() ? this.inlineStack : this.compileStack,
+ item = stack[stack.length - 1];
+
+ /* istanbul ignore if */
+ if (item instanceof Literal) {
+ return item.value;
+ } else {
+ return item;
+ }
+ },
+
+ contextName: function contextName(context) {
+ if (this.useDepths && context) {
+ return 'depths[' + context + ']';
+ } else {
+ return 'depth' + context;
+ }
+ },
+
+ quotedString: function quotedString(str) {
+ return this.source.quotedString(str);
+ },
+
+ objectLiteral: function objectLiteral(obj) {
+ return this.source.objectLiteral(obj);
+ },
+
+ aliasable: function aliasable(name) {
+ var ret = this.aliases[name];
+ if (ret) {
+ ret.referenceCount++;
+ return ret;
+ }
+
+ ret = this.aliases[name] = this.source.wrap(name);
+ ret.aliasable = true;
+ ret.referenceCount = 1;
+
+ return ret;
+ },
+
+ setupHelper: function setupHelper(paramSize, name, blockHelper) {
+ var params = [],
+ paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper);
+ var foundHelper = this.nameLookup('helpers', name, 'helper');
+
+ return {
+ params: params,
+ paramsInit: paramsInit,
+ name: foundHelper,
+ callParams: [this.contextName(0)].concat(params)
+ };
+ },
+
+ setupParams: function setupParams(helper, paramSize, params) {
+ var options = {},
+ contexts = [],
+ types = [],
+ ids = [],
+ param = undefined;
+
+ options.name = this.quotedString(helper);
+ options.hash = this.popStack();
+
+ if (this.trackIds) {
+ options.hashIds = this.popStack();
+ }
+ if (this.stringParams) {
+ options.hashTypes = this.popStack();
+ options.hashContexts = this.popStack();
+ }
+
+ var inverse = this.popStack(),
+ program = this.popStack();
+
+ // Avoid setting fn and inverse if neither are set. This allows
+ // helpers to do a check for `if (options.fn)`
+ if (program || inverse) {
+ options.fn = program || 'this.noop';
+ options.inverse = inverse || 'this.noop';
+ }
+
+ // The parameters go on to the stack in order (making sure that they are evaluated in order)
+ // so we need to pop them off the stack in reverse order
+ var i = paramSize;
+ while (i--) {
+ param = this.popStack();
+ params[i] = param;
+
+ if (this.trackIds) {
+ ids[i] = this.popStack();
+ }
+ if (this.stringParams) {
+ types[i] = this.popStack();
+ contexts[i] = this.popStack();
+ }
+ }
+
+ if (this.trackIds) {
+ options.ids = this.source.generateArray(ids);
+ }
+ if (this.stringParams) {
+ options.types = this.source.generateArray(types);
+ options.contexts = this.source.generateArray(contexts);
+ }
+
+ if (this.options.data) {
+ options.data = 'data';
+ }
+ if (this.useBlockParams) {
+ options.blockParams = 'blockParams';
+ }
+ return options;
+ },
+
+ setupHelperArgs: function setupHelperArgs(helper, paramSize, params, useRegister) {
+ var options = this.setupParams(helper, paramSize, params, true);
+ options = this.objectLiteral(options);
+ if (useRegister) {
+ this.useRegister('options');
+ params.push('options');
+ return ['options=', options];
+ } else {
+ params.push(options);
+ return '';
+ }
+ }
+ };
+
+ (function () {
+ var reservedWords = ('break else new var' + ' case finally return void' + ' catch for switch while' + ' continue function this with' + ' default if throw' + ' delete in try' + ' do instanceof typeof' + ' abstract enum int short' + ' boolean export interface static' + ' byte extends long super' + ' char final native synchronized' + ' class float package throws' + ' const goto private transient' + ' debugger implements protected volatile' + ' double import public let yield await' + ' null true false').split(' ');
+
+ var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
+
+ for (var i = 0, l = reservedWords.length; i < l; i++) {
+ compilerWords[reservedWords[i]] = true;
+ }
+ })();
+
+ JavaScriptCompiler.isValidJavaScriptVariableName = function (name) {
+ return !JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name);
+ };
+
+ function strictLookup(requireTerminal, compiler, parts, type) {
+ var stack = compiler.popStack(),
+ i = 0,
+ len = parts.length;
+ if (requireTerminal) {
+ len--;
+ }
+
+ for (; i < len; i++) {
+ stack = compiler.nameLookup(stack, parts[i], type);
+ }
+
+ if (requireTerminal) {
+ return [compiler.aliasable('this.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ')'];
+ } else {
+ return stack;
+ }
+ }
+
+ exports['default'] = JavaScriptCompiler;
+ module.exports = exports['default'];
+
+/***/ },
+/* 6 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ var _interopRequireDefault = __webpack_require__(8)['default'];
+
+ exports.__esModule = true;
+
+ var _Exception = __webpack_require__(12);
+
+ var _Exception2 = _interopRequireDefault(_Exception);
+
+ var _AST = __webpack_require__(2);
+
+ var _AST2 = _interopRequireDefault(_AST);
+
+ function Visitor() {
+ this.parents = [];
+ }
+
+ Visitor.prototype = {
+ constructor: Visitor,
+ mutating: false,
+
+ // Visits a given value. If mutating, will replace the value if necessary.
+ acceptKey: function acceptKey(node, name) {
+ var value = this.accept(node[name]);
+ if (this.mutating) {
+ // Hacky sanity check:
+ if (value && (!value.type || !_AST2['default'][value.type])) {
+ throw new _Exception2['default']('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type);
+ }
+ node[name] = value;
+ }
+ },
+
+ // Performs an accept operation with added sanity check to ensure
+ // required keys are not removed.
+ acceptRequired: function acceptRequired(node, name) {
+ this.acceptKey(node, name);
+
+ if (!node[name]) {
+ throw new _Exception2['default'](node.type + ' requires ' + name);
+ }
+ },
+
+ // Traverses a given array. If mutating, empty respnses will be removed
+ // for child elements.
+ acceptArray: function acceptArray(array) {
+ for (var i = 0, l = array.length; i < l; i++) {
+ this.acceptKey(array, i);
+
+ if (!array[i]) {
+ array.splice(i, 1);
+ i--;
+ l--;
+ }
+ }
+ },
+
+ accept: function accept(object) {
+ if (!object) {
+ return;
+ }
+
+ if (this.current) {
+ this.parents.unshift(this.current);
+ }
+ this.current = object;
+
+ var ret = this[object.type](object);
+
+ this.current = this.parents.shift();
+
+ if (!this.mutating || ret) {
+ return ret;
+ } else if (ret !== false) {
+ return object;
+ }
+ },
+
+ Program: function Program(program) {
+ this.acceptArray(program.body);
+ },
+
+ MustacheStatement: function MustacheStatement(mustache) {
+ this.acceptRequired(mustache, 'path');
+ this.acceptArray(mustache.params);
+ this.acceptKey(mustache, 'hash');
+ },
+
+ BlockStatement: function BlockStatement(block) {
+ this.acceptRequired(block, 'path');
+ this.acceptArray(block.params);
+ this.acceptKey(block, 'hash');
+
+ this.acceptKey(block, 'program');
+ this.acceptKey(block, 'inverse');
+ },
+
+ PartialStatement: function PartialStatement(partial) {
+ this.acceptRequired(partial, 'name');
+ this.acceptArray(partial.params);
+ this.acceptKey(partial, 'hash');
+ },
+
+ ContentStatement: function ContentStatement() {},
+ CommentStatement: function CommentStatement() {},
+
+ SubExpression: function SubExpression(sexpr) {
+ this.acceptRequired(sexpr, 'path');
+ this.acceptArray(sexpr.params);
+ this.acceptKey(sexpr, 'hash');
+ },
+
+ PathExpression: function PathExpression() {},
+
+ StringLiteral: function StringLiteral() {},
+ NumberLiteral: function NumberLiteral() {},
+ BooleanLiteral: function BooleanLiteral() {},
+ UndefinedLiteral: function UndefinedLiteral() {},
+ NullLiteral: function NullLiteral() {},
+
+ Hash: function Hash(hash) {
+ this.acceptArray(hash.pairs);
+ },
+ HashPair: function HashPair(pair) {
+ this.acceptRequired(pair, 'value');
+ }
+ };
+
+ exports['default'] = Visitor;
+ module.exports = exports['default'];
+ /* content */ /* comment */ /* path */ /* string */ /* number */ /* bool */ /* literal */ /* literal */
+
+/***/ },
+/* 7 */
+/***/ function(module, exports, __webpack_require__) {
+
+ /* WEBPACK VAR INJECTION */(function(global) {'use strict';
+
+ exports.__esModule = true;
+ /*global window */
+
+ exports['default'] = function (Handlebars) {
+ /* istanbul ignore next */
+ var root = typeof global !== 'undefined' ? global : window,
+ $Handlebars = root.Handlebars;
+ /* istanbul ignore next */
+ Handlebars.noConflict = function () {
+ if (root.Handlebars === Handlebars) {
+ root.Handlebars = $Handlebars;
+ }
+ };
+ };
+
+ module.exports = exports['default'];
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
+
+/***/ },
+/* 8 */
+/***/ function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+ exports["default"] = function (obj) {
+ return obj && obj.__esModule ? obj : {
+ "default": obj
+ };
+ };
+
+ exports.__esModule = true;
+
+/***/ },
+/* 9 */
+/***/ function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+ exports["default"] = function (obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ } else {
+ var newObj = {};
+
+ if (typeof obj === "object" && obj !== null) {
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
+ }
+ }
+
+ newObj["default"] = obj;
+ return newObj;
+ }
+ };
+
+ exports.__esModule = true;
+
+/***/ },
+/* 10 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ var _interopRequireWildcard = __webpack_require__(9)['default'];
+
+ var _interopRequireDefault = __webpack_require__(8)['default'];
+
+ exports.__esModule = true;
+ exports.HandlebarsEnvironment = HandlebarsEnvironment;
+ exports.createFrame = createFrame;
+
+ var _import = __webpack_require__(13);
+
+ var Utils = _interopRequireWildcard(_import);
+
+ var _Exception = __webpack_require__(12);
+
+ var _Exception2 = _interopRequireDefault(_Exception);
+
+ var VERSION = '3.0.1';
+ exports.VERSION = VERSION;
+ var COMPILER_REVISION = 6;
+
+ exports.COMPILER_REVISION = COMPILER_REVISION;
+ var REVISION_CHANGES = {
+ 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
+ 2: '== 1.0.0-rc.3',
+ 3: '== 1.0.0-rc.4',
+ 4: '== 1.x.x',
+ 5: '== 2.0.0-alpha.x',
+ 6: '>= 2.0.0-beta.1'
+ };
+
+ exports.REVISION_CHANGES = REVISION_CHANGES;
+ var isArray = Utils.isArray,
+ isFunction = Utils.isFunction,
+ toString = Utils.toString,
+ objectType = '[object Object]';
+
+ function HandlebarsEnvironment(helpers, partials) {
+ this.helpers = helpers || {};
+ this.partials = partials || {};
+
+ registerDefaultHelpers(this);
+ }
+
+ HandlebarsEnvironment.prototype = {
+ constructor: HandlebarsEnvironment,
+
+ logger: logger,
+ log: log,
+
+ registerHelper: function registerHelper(name, fn) {
+ if (toString.call(name) === objectType) {
+ if (fn) {
+ throw new _Exception2['default']('Arg not supported with multiple helpers');
+ }
+ Utils.extend(this.helpers, name);
+ } else {
+ this.helpers[name] = fn;
+ }
+ },
+ unregisterHelper: function unregisterHelper(name) {
+ delete this.helpers[name];
+ },
+
+ registerPartial: function registerPartial(name, partial) {
+ if (toString.call(name) === objectType) {
+ Utils.extend(this.partials, name);
+ } else {
+ if (typeof partial === 'undefined') {
+ throw new _Exception2['default']('Attempting to register a partial as undefined');
+ }
+ this.partials[name] = partial;
+ }
+ },
+ unregisterPartial: function unregisterPartial(name) {
+ delete this.partials[name];
+ }
+ };
+
+ function registerDefaultHelpers(instance) {
+ instance.registerHelper('helperMissing', function () {
+ if (arguments.length === 1) {
+ // A missing field in a {{foo}} constuct.
+ return undefined;
+ } else {
+ // Someone is actually trying to call something, blow up.
+ throw new _Exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"');
+ }
+ });
+
+ instance.registerHelper('blockHelperMissing', function (context, options) {
+ var inverse = options.inverse,
+ fn = options.fn;
+
+ if (context === true) {
+ return fn(this);
+ } else if (context === false || context == null) {
+ return inverse(this);
+ } else if (isArray(context)) {
+ if (context.length > 0) {
+ if (options.ids) {
+ options.ids = [options.name];
+ }
+
+ return instance.helpers.each(context, options);
+ } else {
+ return inverse(this);
+ }
+ } else {
+ if (options.data && options.ids) {
+ var data = createFrame(options.data);
+ data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name);
+ options = { data: data };
+ }
+
+ return fn(context, options);
+ }
+ });
+
+ instance.registerHelper('each', function (context, options) {
+ if (!options) {
+ throw new _Exception2['default']('Must pass iterator to #each');
+ }
+
+ var fn = options.fn,
+ inverse = options.inverse,
+ i = 0,
+ ret = '',
+ data = undefined,
+ contextPath = undefined;
+
+ if (options.data && options.ids) {
+ contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.';
+ }
+
+ if (isFunction(context)) {
+ context = context.call(this);
+ }
+
+ if (options.data) {
+ data = createFrame(options.data);
+ }
+
+ function execIteration(field, index, last) {
+ if (data) {
+ data.key = field;
+ data.index = index;
+ data.first = index === 0;
+ data.last = !!last;
+
+ if (contextPath) {
+ data.contextPath = contextPath + field;
+ }
+ }
+
+ ret = ret + fn(context[field], {
+ data: data,
+ blockParams: Utils.blockParams([context[field], field], [contextPath + field, null])
+ });
+ }
+
+ if (context && typeof context === 'object') {
+ if (isArray(context)) {
+ for (var j = context.length; i < j; i++) {
+ execIteration(i, i, i === context.length - 1);
+ }
+ } else {
+ var priorKey = undefined;
+
+ for (var key in context) {
+ if (context.hasOwnProperty(key)) {
+ // We're running the iterations one step out of sync so we can detect
+ // the last iteration without have to scan the object twice and create
+ // an itermediate keys array.
+ if (priorKey) {
+ execIteration(priorKey, i - 1);
+ }
+ priorKey = key;
+ i++;
+ }
+ }
+ if (priorKey) {
+ execIteration(priorKey, i - 1, true);
+ }
+ }
+ }
+
+ if (i === 0) {
+ ret = inverse(this);
+ }
+
+ return ret;
+ });
+
+ instance.registerHelper('if', function (conditional, options) {
+ if (isFunction(conditional)) {
+ conditional = conditional.call(this);
+ }
+
+ // Default behavior is to render the positive path if the value is truthy and not empty.
+ // The `includeZero` option may be set to treat the condtional as purely not empty based on the
+ // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
+ if (!options.hash.includeZero && !conditional || Utils.isEmpty(conditional)) {
+ return options.inverse(this);
+ } else {
+ return options.fn(this);
+ }
+ });
+
+ instance.registerHelper('unless', function (conditional, options) {
+ return instance.helpers['if'].call(this, conditional, { fn: options.inverse, inverse: options.fn, hash: options.hash });
+ });
+
+ instance.registerHelper('with', function (context, options) {
+ if (isFunction(context)) {
+ context = context.call(this);
+ }
+
+ var fn = options.fn;
+
+ if (!Utils.isEmpty(context)) {
+ if (options.data && options.ids) {
+ var data = createFrame(options.data);
+ data.contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]);
+ options = { data: data };
+ }
+
+ return fn(context, options);
+ } else {
+ return options.inverse(this);
+ }
+ });
+
+ instance.registerHelper('log', function (message, options) {
+ var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
+ instance.log(level, message);
+ });
+
+ instance.registerHelper('lookup', function (obj, field) {
+ return obj && obj[field];
+ });
+ }
+
+ var logger = {
+ methodMap: { 0: 'debug', 1: 'info', 2: 'warn', 3: 'error' },
+
+ // State enum
+ DEBUG: 0,
+ INFO: 1,
+ WARN: 2,
+ ERROR: 3,
+ level: 1,
+
+ // Can be overridden in the host environment
+ log: function log(level, message) {
+ if (typeof console !== 'undefined' && logger.level <= level) {
+ var method = logger.methodMap[level];
+ (console[method] || console.log).call(console, message); // eslint-disable-line no-console
+ }
+ }
+ };
+
+ exports.logger = logger;
+ var log = logger.log;
+
+ exports.log = log;
+
+ function createFrame(object) {
+ var frame = Utils.extend({}, object);
+ frame._parent = object;
+ return frame;
+ }
+
+ /* [args, ]options */
+
+/***/ },
+/* 11 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+ // Build out our basic SafeString type
+ function SafeString(string) {
+ this.string = string;
+ }
+
+ SafeString.prototype.toString = SafeString.prototype.toHTML = function () {
+ return '' + this.string;
+ };
+
+ exports['default'] = SafeString;
+ module.exports = exports['default'];
+
+/***/ },
+/* 12 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+
+ var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
+
+ function Exception(message, node) {
+ var loc = node && node.loc,
+ line = undefined,
+ column = undefined;
+ if (loc) {
+ line = loc.start.line;
+ column = loc.start.column;
+
+ message += ' - ' + line + ':' + column;
+ }
+
+ var tmp = Error.prototype.constructor.call(this, message);
+
+ // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
+ for (var idx = 0; idx < errorProps.length; idx++) {
+ this[errorProps[idx]] = tmp[errorProps[idx]];
+ }
+
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, Exception);
+ }
+
+ if (loc) {
+ this.lineNumber = line;
+ this.column = column;
+ }
+ }
+
+ Exception.prototype = new Error();
+
+ exports['default'] = Exception;
+ module.exports = exports['default'];
+
+/***/ },
+/* 13 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+ exports.extend = extend;
+
+ // Older IE versions do not directly support indexOf so we must implement our own, sadly.
+ exports.indexOf = indexOf;
+ exports.escapeExpression = escapeExpression;
+ exports.isEmpty = isEmpty;
+ exports.blockParams = blockParams;
+ exports.appendContextPath = appendContextPath;
+ var escape = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ '\'': ''',
+ '`': '`'
+ };
+
+ var badChars = /[&<>"'`]/g,
+ possible = /[&<>"'`]/;
+
+ function escapeChar(chr) {
+ return escape[chr];
+ }
+
+ function extend(obj /* , ...source */) {
+ for (var i = 1; i < arguments.length; i++) {
+ for (var key in arguments[i]) {
+ if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
+ obj[key] = arguments[i][key];
+ }
+ }
+ }
+
+ return obj;
+ }
+
+ var toString = Object.prototype.toString;
+
+ exports.toString = toString;
+ // Sourced from lodash
+ // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
+ /*eslint-disable func-style, no-var */
+ var isFunction = function isFunction(value) {
+ return typeof value === 'function';
+ };
+ // fallback for older versions of Chrome and Safari
+ /* istanbul ignore next */
+ if (isFunction(/x/)) {
+ exports.isFunction = isFunction = function (value) {
+ return typeof value === 'function' && toString.call(value) === '[object Function]';
+ };
+ }
+ var isFunction;
+ exports.isFunction = isFunction;
+ /*eslint-enable func-style, no-var */
+
+ /* istanbul ignore next */
+ var isArray = Array.isArray || function (value) {
+ return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false;
+ };exports.isArray = isArray;
+
+ function indexOf(array, value) {
+ for (var i = 0, len = array.length; i < len; i++) {
+ if (array[i] === value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ function escapeExpression(string) {
+ if (typeof string !== 'string') {
+ // don't escape SafeStrings, since they're already safe
+ if (string && string.toHTML) {
+ return string.toHTML();
+ } else if (string == null) {
+ return '';
+ } else if (!string) {
+ return string + '';
+ }
+
+ // Force a string conversion as this will be done by the append regardless and
+ // the regex test will do this transparently behind the scenes, causing issues if
+ // an object's to string has escaped characters in it.
+ string = '' + string;
+ }
+
+ if (!possible.test(string)) {
+ return string;
+ }
+ return string.replace(badChars, escapeChar);
+ }
+
+ function isEmpty(value) {
+ if (!value && value !== 0) {
+ return true;
+ } else if (isArray(value) && value.length === 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function blockParams(params, ids) {
+ params.path = ids;
+ return params;
+ }
+
+ function appendContextPath(contextPath, id) {
+ return (contextPath ? contextPath + '.' : '') + id;
+ }
+
+/***/ },
+/* 14 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ var _interopRequireWildcard = __webpack_require__(9)['default'];
+
+ var _interopRequireDefault = __webpack_require__(8)['default'];
+
+ exports.__esModule = true;
+ exports.checkRevision = checkRevision;
+
+ // TODO: Remove this line and break up compilePartial
+
+ exports.template = template;
+ exports.wrapProgram = wrapProgram;
+ exports.resolvePartial = resolvePartial;
+ exports.invokePartial = invokePartial;
+ exports.noop = noop;
+
+ var _import = __webpack_require__(13);
+
+ var Utils = _interopRequireWildcard(_import);
+
+ var _Exception = __webpack_require__(12);
+
+ var _Exception2 = _interopRequireDefault(_Exception);
+
+ var _COMPILER_REVISION$REVISION_CHANGES$createFrame = __webpack_require__(10);
+
+ function checkRevision(compilerInfo) {
+ var compilerRevision = compilerInfo && compilerInfo[0] || 1,
+ currentRevision = _COMPILER_REVISION$REVISION_CHANGES$createFrame.COMPILER_REVISION;
+
+ if (compilerRevision !== currentRevision) {
+ if (compilerRevision < currentRevision) {
+ var runtimeVersions = _COMPILER_REVISION$REVISION_CHANGES$createFrame.REVISION_CHANGES[currentRevision],
+ compilerVersions = _COMPILER_REVISION$REVISION_CHANGES$createFrame.REVISION_CHANGES[compilerRevision];
+ throw new _Exception2['default']('Template was precompiled with an older version of Handlebars than the current runtime. ' + 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').');
+ } else {
+ // Use the embedded version info since the runtime doesn't know about this revision yet
+ throw new _Exception2['default']('Template was precompiled with a newer version of Handlebars than the current runtime. ' + 'Please update your runtime to a newer version (' + compilerInfo[1] + ').');
+ }
+ }
+ }
+
+ function template(templateSpec, env) {
+ /* istanbul ignore next */
+ if (!env) {
+ throw new _Exception2['default']('No environment passed to template');
+ }
+ if (!templateSpec || !templateSpec.main) {
+ throw new _Exception2['default']('Unknown template object: ' + typeof templateSpec);
+ }
+
+ // Note: Using env.VM references rather than local var references throughout this section to allow
+ // for external users to override these as psuedo-supported APIs.
+ env.VM.checkRevision(templateSpec.compiler);
+
+ function invokePartialWrapper(partial, context, options) {
+ if (options.hash) {
+ context = Utils.extend({}, context, options.hash);
+ }
+
+ partial = env.VM.resolvePartial.call(this, partial, context, options);
+ var result = env.VM.invokePartial.call(this, partial, context, options);
+
+ if (result == null && env.compile) {
+ options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
+ result = options.partials[options.name](context, options);
+ }
+ if (result != null) {
+ if (options.indent) {
+ var lines = result.split('\n');
+ for (var i = 0, l = lines.length; i < l; i++) {
+ if (!lines[i] && i + 1 === l) {
+ break;
+ }
+
+ lines[i] = options.indent + lines[i];
+ }
+ result = lines.join('\n');
+ }
+ return result;
+ } else {
+ throw new _Exception2['default']('The partial ' + options.name + ' could not be compiled when running in runtime-only mode');
+ }
+ }
+
+ // Just add water
+ var container = {
+ strict: function strict(obj, name) {
+ if (!(name in obj)) {
+ throw new _Exception2['default']('"' + name + '" not defined in ' + obj);
+ }
+ return obj[name];
+ },
+ lookup: function lookup(depths, name) {
+ var len = depths.length;
+ for (var i = 0; i < len; i++) {
+ if (depths[i] && depths[i][name] != null) {
+ return depths[i][name];
+ }
+ }
+ },
+ lambda: function lambda(current, context) {
+ return typeof current === 'function' ? current.call(context) : current;
+ },
+
+ escapeExpression: Utils.escapeExpression,
+ invokePartial: invokePartialWrapper,
+
+ fn: function fn(i) {
+ return templateSpec[i];
+ },
+
+ programs: [],
+ program: function program(i, data, declaredBlockParams, blockParams, depths) {
+ var programWrapper = this.programs[i],
+ fn = this.fn(i);
+ if (data || depths || blockParams || declaredBlockParams) {
+ programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths);
+ } else if (!programWrapper) {
+ programWrapper = this.programs[i] = wrapProgram(this, i, fn);
+ }
+ return programWrapper;
+ },
+
+ data: function data(value, depth) {
+ while (value && depth--) {
+ value = value._parent;
+ }
+ return value;
+ },
+ merge: function merge(param, common) {
+ var obj = param || common;
+
+ if (param && common && param !== common) {
+ obj = Utils.extend({}, common, param);
+ }
+
+ return obj;
+ },
+
+ noop: env.VM.noop,
+ compilerInfo: templateSpec.compiler
+ };
+
+ function ret(context) {
+ var options = arguments[1] === undefined ? {} : arguments[1];
+
+ var data = options.data;
+
+ ret._setup(options);
+ if (!options.partial && templateSpec.useData) {
+ data = initData(context, data);
+ }
+ var depths = undefined,
+ blockParams = templateSpec.useBlockParams ? [] : undefined;
+ if (templateSpec.useDepths) {
+ depths = options.depths ? [context].concat(options.depths) : [context];
+ }
+
+ return templateSpec.main.call(container, context, container.helpers, container.partials, data, blockParams, depths);
+ }
+ ret.isTop = true;
+
+ ret._setup = function (options) {
+ if (!options.partial) {
+ container.helpers = container.merge(options.helpers, env.helpers);
+
+ if (templateSpec.usePartial) {
+ container.partials = container.merge(options.partials, env.partials);
+ }
+ } else {
+ container.helpers = options.helpers;
+ container.partials = options.partials;
+ }
+ };
+
+ ret._child = function (i, data, blockParams, depths) {
+ if (templateSpec.useBlockParams && !blockParams) {
+ throw new _Exception2['default']('must pass block params');
+ }
+ if (templateSpec.useDepths && !depths) {
+ throw new _Exception2['default']('must pass parent depths');
+ }
+
+ return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths);
+ };
+ return ret;
+ }
+
+ function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) {
+ function prog(context) {
+ var options = arguments[1] === undefined ? {} : arguments[1];
+
+ return fn.call(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), depths && [context].concat(depths));
+ }
+ prog.program = i;
+ prog.depth = depths ? depths.length : 0;
+ prog.blockParams = declaredBlockParams || 0;
+ return prog;
+ }
+
+ function resolvePartial(partial, context, options) {
+ if (!partial) {
+ partial = options.partials[options.name];
+ } else if (!partial.call && !options.name) {
+ // This is a dynamic partial that returned a string
+ options.name = partial;
+ partial = options.partials[partial];
+ }
+ return partial;
+ }
+
+ function invokePartial(partial, context, options) {
+ options.partial = true;
+
+ if (partial === undefined) {
+ throw new _Exception2['default']('The partial ' + options.name + ' could not be found');
+ } else if (partial instanceof Function) {
+ return partial(context, options);
+ }
+ }
+
+ function noop() {
+ return '';
+ }
+
+ function initData(context, data) {
+ if (!data || !('root' in data)) {
+ data = data ? _COMPILER_REVISION$REVISION_CHANGES$createFrame.createFrame(data) : {};
+ data.root = context;
+ }
+ return data;
+ }
+
+/***/ },
+/* 15 */
+/***/ function(module, exports, __webpack_require__) {
+
+ "use strict";
+
+ exports.__esModule = true;
+ /* istanbul ignore next */
+ /* Jison generated parser */
+ var handlebars = (function () {
+ var parser = { trace: function trace() {},
+ yy: {},
+ symbols_: { error: 2, root: 3, program: 4, EOF: 5, program_repetition0: 6, statement: 7, mustache: 8, block: 9, rawBlock: 10, partial: 11, content: 12, COMMENT: 13, CONTENT: 14, openRawBlock: 15, END_RAW_BLOCK: 16, OPEN_RAW_BLOCK: 17, helperName: 18, openRawBlock_repetition0: 19, openRawBlock_option0: 20, CLOSE_RAW_BLOCK: 21, openBlock: 22, block_option0: 23, closeBlock: 24, openInverse: 25, block_option1: 26, OPEN_BLOCK: 27, openBlock_repetition0: 28, openBlock_option0: 29, openBlock_option1: 30, CLOSE: 31, OPEN_INVERSE: 32, openInverse_repetition0: 33, openInverse_option0: 34, openInverse_option1: 35, openInverseChain: 36, OPEN_INVERSE_CHAIN: 37, openInverseChain_repetition0: 38, openInverseChain_option0: 39, openInverseChain_option1: 40, inverseAndProgram: 41, INVERSE: 42, inverseChain: 43, inverseChain_option0: 44, OPEN_ENDBLOCK: 45, OPEN: 46, mustache_repetition0: 47, mustache_option0: 48, OPEN_UNESCAPED: 49, mustache_repetition1: 50, mustache_option1: 51, CLOSE_UNESCAPED: 52, OPEN_PARTIAL: 53, partialName: 54, partial_repetition0: 55, partial_option0: 56, param: 57, sexpr: 58, OPEN_SEXPR: 59, sexpr_repetition0: 60, sexpr_option0: 61, CLOSE_SEXPR: 62, hash: 63, hash_repetition_plus0: 64, hashSegment: 65, ID: 66, EQUALS: 67, blockParams: 68, OPEN_BLOCK_PARAMS: 69, blockParams_repetition_plus0: 70, CLOSE_BLOCK_PARAMS: 71, path: 72, dataName: 73, STRING: 74, NUMBER: 75, BOOLEAN: 76, UNDEFINED: 77, NULL: 78, DATA: 79, pathSegments: 80, SEP: 81, $accept: 0, $end: 1 },
+ terminals_: { 2: "error", 5: "EOF", 13: "COMMENT", 14: "CONTENT", 16: "END_RAW_BLOCK", 17: "OPEN_RAW_BLOCK", 21: "CLOSE_RAW_BLOCK", 27: "OPEN_BLOCK", 31: "CLOSE", 32: "OPEN_INVERSE", 37: "OPEN_INVERSE_CHAIN", 42: "INVERSE", 45: "OPEN_ENDBLOCK", 46: "OPEN", 49: "OPEN_UNESCAPED", 52: "CLOSE_UNESCAPED", 53: "OPEN_PARTIAL", 59: "OPEN_SEXPR", 62: "CLOSE_SEXPR", 66: "ID", 67: "EQUALS", 69: "OPEN_BLOCK_PARAMS", 71: "CLOSE_BLOCK_PARAMS", 74: "STRING", 75: "NUMBER", 76: "BOOLEAN", 77: "UNDEFINED", 78: "NULL", 79: "DATA", 81: "SEP" },
+ productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [12, 1], [10, 3], [15, 5], [9, 4], [9, 4], [22, 6], [25, 6], [36, 6], [41, 2], [43, 3], [43, 1], [24, 3], [8, 5], [8, 5], [11, 5], [57, 1], [57, 1], [58, 5], [63, 1], [65, 3], [68, 3], [18, 1], [18, 1], [18, 1], [18, 1], [18, 1], [18, 1], [18, 1], [54, 1], [54, 1], [73, 2], [72, 1], [80, 3], [80, 1], [6, 0], [6, 2], [19, 0], [19, 2], [20, 0], [20, 1], [23, 0], [23, 1], [26, 0], [26, 1], [28, 0], [28, 2], [29, 0], [29, 1], [30, 0], [30, 1], [33, 0], [33, 2], [34, 0], [34, 1], [35, 0], [35, 1], [38, 0], [38, 2], [39, 0], [39, 1], [40, 0], [40, 1], [44, 0], [44, 1], [47, 0], [47, 2], [48, 0], [48, 1], [50, 0], [50, 2], [51, 0], [51, 1], [55, 0], [55, 2], [56, 0], [56, 1], [60, 0], [60, 2], [61, 0], [61, 1], [64, 1], [64, 2], [70, 1], [70, 2]],
+ performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) {
+
+ var $0 = $$.length - 1;
+ switch (yystate) {
+ case 1:
+ return $$[$0 - 1];
+ break;
+ case 2:
+ this.$ = new yy.Program($$[$0], null, {}, yy.locInfo(this._$));
+ break;
+ case 3:
+ this.$ = $$[$0];
+ break;
+ case 4:
+ this.$ = $$[$0];
+ break;
+ case 5:
+ this.$ = $$[$0];
+ break;
+ case 6:
+ this.$ = $$[$0];
+ break;
+ case 7:
+ this.$ = $$[$0];
+ break;
+ case 8:
+ this.$ = new yy.CommentStatement(yy.stripComment($$[$0]), yy.stripFlags($$[$0], $$[$0]), yy.locInfo(this._$));
+ break;
+ case 9:
+ this.$ = new yy.ContentStatement($$[$0], yy.locInfo(this._$));
+ break;
+ case 10:
+ this.$ = yy.prepareRawBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$);
+ break;
+ case 11:
+ this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1] };
+ break;
+ case 12:
+ this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], false, this._$);
+ break;
+ case 13:
+ this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], true, this._$);
+ break;
+ case 14:
+ this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) };
+ break;
+ case 15:
+ this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) };
+ break;
+ case 16:
+ this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) };
+ break;
+ case 17:
+ this.$ = { strip: yy.stripFlags($$[$0 - 1], $$[$0 - 1]), program: $$[$0] };
+ break;
+ case 18:
+ var inverse = yy.prepareBlock($$[$0 - 2], $$[$0 - 1], $$[$0], $$[$0], false, this._$),
+ program = new yy.Program([inverse], null, {}, yy.locInfo(this._$));
+ program.chained = true;
+
+ this.$ = { strip: $$[$0 - 2].strip, program: program, chain: true };
+
+ break;
+ case 19:
+ this.$ = $$[$0];
+ break;
+ case 20:
+ this.$ = { path: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 2], $$[$0]) };
+ break;
+ case 21:
+ this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$);
+ break;
+ case 22:
+ this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$);
+ break;
+ case 23:
+ this.$ = new yy.PartialStatement($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], yy.stripFlags($$[$0 - 4], $$[$0]), yy.locInfo(this._$));
+ break;
+ case 24:
+ this.$ = $$[$0];
+ break;
+ case 25:
+ this.$ = $$[$0];
+ break;
+ case 26:
+ this.$ = new yy.SubExpression($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], yy.locInfo(this._$));
+ break;
+ case 27:
+ this.$ = new yy.Hash($$[$0], yy.locInfo(this._$));
+ break;
+ case 28:
+ this.$ = new yy.HashPair(yy.id($$[$0 - 2]), $$[$0], yy.locInfo(this._$));
+ break;
+ case 29:
+ this.$ = yy.id($$[$0 - 1]);
+ break;
+ case 30:
+ this.$ = $$[$0];
+ break;
+ case 31:
+ this.$ = $$[$0];
+ break;
+ case 32:
+ this.$ = new yy.StringLiteral($$[$0], yy.locInfo(this._$));
+ break;
+ case 33:
+ this.$ = new yy.NumberLiteral($$[$0], yy.locInfo(this._$));
+ break;
+ case 34:
+ this.$ = new yy.BooleanLiteral($$[$0], yy.locInfo(this._$));
+ break;
+ case 35:
+ this.$ = new yy.UndefinedLiteral(yy.locInfo(this._$));
+ break;
+ case 36:
+ this.$ = new yy.NullLiteral(yy.locInfo(this._$));
+ break;
+ case 37:
+ this.$ = $$[$0];
+ break;
+ case 38:
+ this.$ = $$[$0];
+ break;
+ case 39:
+ this.$ = yy.preparePath(true, $$[$0], this._$);
+ break;
+ case 40:
+ this.$ = yy.preparePath(false, $$[$0], this._$);
+ break;
+ case 41:
+ $$[$0 - 2].push({ part: yy.id($$[$0]), original: $$[$0], separator: $$[$0 - 1] });this.$ = $$[$0 - 2];
+ break;
+ case 42:
+ this.$ = [{ part: yy.id($$[$0]), original: $$[$0] }];
+ break;
+ case 43:
+ this.$ = [];
+ break;
+ case 44:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 45:
+ this.$ = [];
+ break;
+ case 46:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 53:
+ this.$ = [];
+ break;
+ case 54:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 59:
+ this.$ = [];
+ break;
+ case 60:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 65:
+ this.$ = [];
+ break;
+ case 66:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 73:
+ this.$ = [];
+ break;
+ case 74:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 77:
+ this.$ = [];
+ break;
+ case 78:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 81:
+ this.$ = [];
+ break;
+ case 82:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 85:
+ this.$ = [];
+ break;
+ case 86:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 89:
+ this.$ = [$$[$0]];
+ break;
+ case 90:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ case 91:
+ this.$ = [$$[$0]];
+ break;
+ case 92:
+ $$[$0 - 1].push($$[$0]);
+ break;
+ }
+ },
+ table: [{ 3: 1, 4: 2, 5: [2, 43], 6: 3, 13: [2, 43], 14: [2, 43], 17: [2, 43], 27: [2, 43], 32: [2, 43], 46: [2, 43], 49: [2, 43], 53: [2, 43] }, { 1: [3] }, { 5: [1, 4] }, { 5: [2, 2], 7: 5, 8: 6, 9: 7, 10: 8, 11: 9, 12: 10, 13: [1, 11], 14: [1, 18], 15: 16, 17: [1, 21], 22: 14, 25: 15, 27: [1, 19], 32: [1, 20], 37: [2, 2], 42: [2, 2], 45: [2, 2], 46: [1, 12], 49: [1, 13], 53: [1, 17] }, { 1: [2, 1] }, { 5: [2, 44], 13: [2, 44], 14: [2, 44], 17: [2, 44], 27: [2, 44], 32: [2, 44], 37: [2, 44], 42: [2, 44], 45: [2, 44], 46: [2, 44], 49: [2, 44], 53: [2, 44] }, { 5: [2, 3], 13: [2, 3], 14: [2, 3], 17: [2, 3], 27: [2, 3], 32: [2, 3], 37: [2, 3], 42: [2, 3], 45: [2, 3], 46: [2, 3], 49: [2, 3], 53: [2, 3] }, { 5: [2, 4], 13: [2, 4], 14: [2, 4], 17: [2, 4], 27: [2, 4], 32: [2, 4], 37: [2, 4], 42: [2, 4], 45: [2, 4], 46: [2, 4], 49: [2, 4], 53: [2, 4] }, { 5: [2, 5], 13: [2, 5], 14: [2, 5], 17: [2, 5], 27: [2, 5], 32: [2, 5], 37: [2, 5], 42: [2, 5], 45: [2, 5], 46: [2, 5], 49: [2, 5], 53: [2, 5] }, { 5: [2, 6], 13: [2, 6], 14: [2, 6], 17: [2, 6], 27: [2, 6], 32: [2, 6], 37: [2, 6], 42: [2, 6], 45: [2, 6], 46: [2, 6], 49: [2, 6], 53: [2, 6] }, { 5: [2, 7], 13: [2, 7], 14: [2, 7], 17: [2, 7], 27: [2, 7], 32: [2, 7], 37: [2, 7], 42: [2, 7], 45: [2, 7], 46: [2, 7], 49: [2, 7], 53: [2, 7] }, { 5: [2, 8], 13: [2, 8], 14: [2, 8], 17: [2, 8], 27: [2, 8], 32: [2, 8], 37: [2, 8], 42: [2, 8], 45: [2, 8], 46: [2, 8], 49: [2, 8], 53: [2, 8] }, { 18: 22, 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 18: 33, 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 4: 34, 6: 3, 13: [2, 43], 14: [2, 43], 17: [2, 43], 27: [2, 43], 32: [2, 43], 37: [2, 43], 42: [2, 43], 45: [2, 43], 46: [2, 43], 49: [2, 43], 53: [2, 43] }, { 4: 35, 6: 3, 13: [2, 43], 14: [2, 43], 17: [2, 43], 27: [2, 43], 32: [2, 43], 42: [2, 43], 45: [2, 43], 46: [2, 43], 49: [2, 43], 53: [2, 43] }, { 12: 36, 14: [1, 18] }, { 18: 38, 54: 37, 58: 39, 59: [1, 40], 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 5: [2, 9], 13: [2, 9], 14: [2, 9], 16: [2, 9], 17: [2, 9], 27: [2, 9], 32: [2, 9], 37: [2, 9], 42: [2, 9], 45: [2, 9], 46: [2, 9], 49: [2, 9], 53: [2, 9] }, { 18: 41, 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 18: 42, 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 18: 43, 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 31: [2, 73], 47: 44, 59: [2, 73], 66: [2, 73], 74: [2, 73], 75: [2, 73], 76: [2, 73], 77: [2, 73], 78: [2, 73], 79: [2, 73] }, { 21: [2, 30], 31: [2, 30], 52: [2, 30], 59: [2, 30], 62: [2, 30], 66: [2, 30], 69: [2, 30], 74: [2, 30], 75: [2, 30], 76: [2, 30], 77: [2, 30], 78: [2, 30], 79: [2, 30] }, { 21: [2, 31], 31: [2, 31], 52: [2, 31], 59: [2, 31], 62: [2, 31], 66: [2, 31], 69: [2, 31], 74: [2, 31], 75: [2, 31], 76: [2, 31], 77: [2, 31], 78: [2, 31], 79: [2, 31] }, { 21: [2, 32], 31: [2, 32], 52: [2, 32], 59: [2, 32], 62: [2, 32], 66: [2, 32], 69: [2, 32], 74: [2, 32], 75: [2, 32], 76: [2, 32], 77: [2, 32], 78: [2, 32], 79: [2, 32] }, { 21: [2, 33], 31: [2, 33], 52: [2, 33], 59: [2, 33], 62: [2, 33], 66: [2, 33], 69: [2, 33], 74: [2, 33], 75: [2, 33], 76: [2, 33], 77: [2, 33], 78: [2, 33], 79: [2, 33] }, { 21: [2, 34], 31: [2, 34], 52: [2, 34], 59: [2, 34], 62: [2, 34], 66: [2, 34], 69: [2, 34], 74: [2, 34], 75: [2, 34], 76: [2, 34], 77: [2, 34], 78: [2, 34], 79: [2, 34] }, { 21: [2, 35], 31: [2, 35], 52: [2, 35], 59: [2, 35], 62: [2, 35], 66: [2, 35], 69: [2, 35], 74: [2, 35], 75: [2, 35], 76: [2, 35], 77: [2, 35], 78: [2, 35], 79: [2, 35] }, { 21: [2, 36], 31: [2, 36], 52: [2, 36], 59: [2, 36], 62: [2, 36], 66: [2, 36], 69: [2, 36], 74: [2, 36], 75: [2, 36], 76: [2, 36], 77: [2, 36], 78: [2, 36], 79: [2, 36] }, { 21: [2, 40], 31: [2, 40], 52: [2, 40], 59: [2, 40], 62: [2, 40], 66: [2, 40], 69: [2, 40], 74: [2, 40], 75: [2, 40], 76: [2, 40], 77: [2, 40], 78: [2, 40], 79: [2, 40], 81: [1, 45] }, { 66: [1, 32], 80: 46 }, { 21: [2, 42], 31: [2, 42], 52: [2, 42], 59: [2, 42], 62: [2, 42], 66: [2, 42], 69: [2, 42], 74: [2, 42], 75: [2, 42], 76: [2, 42], 77: [2, 42], 78: [2, 42], 79: [2, 42], 81: [2, 42] }, { 50: 47, 52: [2, 77], 59: [2, 77], 66: [2, 77], 74: [2, 77], 75: [2, 77], 76: [2, 77], 77: [2, 77], 78: [2, 77], 79: [2, 77] }, { 23: 48, 36: 50, 37: [1, 52], 41: 51, 42: [1, 53], 43: 49, 45: [2, 49] }, { 26: 54, 41: 55, 42: [1, 53], 45: [2, 51] }, { 16: [1, 56] }, { 31: [2, 81], 55: 57, 59: [2, 81], 66: [2, 81], 74: [2, 81], 75: [2, 81], 76: [2, 81], 77: [2, 81], 78: [2, 81], 79: [2, 81] }, { 31: [2, 37], 59: [2, 37], 66: [2, 37], 74: [2, 37], 75: [2, 37], 76: [2, 37], 77: [2, 37], 78: [2, 37], 79: [2, 37] }, { 31: [2, 38], 59: [2, 38], 66: [2, 38], 74: [2, 38], 75: [2, 38], 76: [2, 38], 77: [2, 38], 78: [2, 38], 79: [2, 38] }, { 18: 58, 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 28: 59, 31: [2, 53], 59: [2, 53], 66: [2, 53], 69: [2, 53], 74: [2, 53], 75: [2, 53], 76: [2, 53], 77: [2, 53], 78: [2, 53], 79: [2, 53] }, { 31: [2, 59], 33: 60, 59: [2, 59], 66: [2, 59], 69: [2, 59], 74: [2, 59], 75: [2, 59], 76: [2, 59], 77: [2, 59], 78: [2, 59], 79: [2, 59] }, { 19: 61, 21: [2, 45], 59: [2, 45], 66: [2, 45], 74: [2, 45], 75: [2, 45], 76: [2, 45], 77: [2, 45], 78: [2, 45], 79: [2, 45] }, { 18: 65, 31: [2, 75], 48: 62, 57: 63, 58: 66, 59: [1, 40], 63: 64, 64: 67, 65: 68, 66: [1, 69], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 66: [1, 70] }, { 21: [2, 39], 31: [2, 39], 52: [2, 39], 59: [2, 39], 62: [2, 39], 66: [2, 39], 69: [2, 39], 74: [2, 39], 75: [2, 39], 76: [2, 39], 77: [2, 39], 78: [2, 39], 79: [2, 39], 81: [1, 45] }, { 18: 65, 51: 71, 52: [2, 79], 57: 72, 58: 66, 59: [1, 40], 63: 73, 64: 67, 65: 68, 66: [1, 69], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 24: 74, 45: [1, 75] }, { 45: [2, 50] }, { 4: 76, 6: 3, 13: [2, 43], 14: [2, 43], 17: [2, 43], 27: [2, 43], 32: [2, 43], 37: [2, 43], 42: [2, 43], 45: [2, 43], 46: [2, 43], 49: [2, 43], 53: [2, 43] }, { 45: [2, 19] }, { 18: 77, 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 4: 78, 6: 3, 13: [2, 43], 14: [2, 43], 17: [2, 43], 27: [2, 43], 32: [2, 43], 45: [2, 43], 46: [2, 43], 49: [2, 43], 53: [2, 43] }, { 24: 79, 45: [1, 75] }, { 45: [2, 52] }, { 5: [2, 10], 13: [2, 10], 14: [2, 10], 17: [2, 10], 27: [2, 10], 32: [2, 10], 37: [2, 10], 42: [2, 10], 45: [2, 10], 46: [2, 10], 49: [2, 10], 53: [2, 10] }, { 18: 65, 31: [2, 83], 56: 80, 57: 81, 58: 66, 59: [1, 40], 63: 82, 64: 67, 65: 68, 66: [1, 69], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 59: [2, 85], 60: 83, 62: [2, 85], 66: [2, 85], 74: [2, 85], 75: [2, 85], 76: [2, 85], 77: [2, 85], 78: [2, 85], 79: [2, 85] }, { 18: 65, 29: 84, 31: [2, 55], 57: 85, 58: 66, 59: [1, 40], 63: 86, 64: 67, 65: 68, 66: [1, 69], 69: [2, 55], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 18: 65, 31: [2, 61], 34: 87, 57: 88, 58: 66, 59: [1, 40], 63: 89, 64: 67, 65: 68, 66: [1, 69], 69: [2, 61], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 18: 65, 20: 90, 21: [2, 47], 57: 91, 58: 66, 59: [1, 40], 63: 92, 64: 67, 65: 68, 66: [1, 69], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 31: [1, 93] }, { 31: [2, 74], 59: [2, 74], 66: [2, 74], 74: [2, 74], 75: [2, 74], 76: [2, 74], 77: [2, 74], 78: [2, 74], 79: [2, 74] }, { 31: [2, 76] }, { 21: [2, 24], 31: [2, 24], 52: [2, 24], 59: [2, 24], 62: [2, 24], 66: [2, 24], 69: [2, 24], 74: [2, 24], 75: [2, 24], 76: [2, 24], 77: [2, 24], 78: [2, 24], 79: [2, 24] }, { 21: [2, 25], 31: [2, 25], 52: [2, 25], 59: [2, 25], 62: [2, 25], 66: [2, 25], 69: [2, 25], 74: [2, 25], 75: [2, 25], 76: [2, 25], 77: [2, 25], 78: [2, 25], 79: [2, 25] }, { 21: [2, 27], 31: [2, 27], 52: [2, 27], 62: [2, 27], 65: 94, 66: [1, 95], 69: [2, 27] }, { 21: [2, 89], 31: [2, 89], 52: [2, 89], 62: [2, 89], 66: [2, 89], 69: [2, 89] }, { 21: [2, 42], 31: [2, 42], 52: [2, 42], 59: [2, 42], 62: [2, 42], 66: [2, 42], 67: [1, 96], 69: [2, 42], 74: [2, 42], 75: [2, 42], 76: [2, 42], 77: [2, 42], 78: [2, 42], 79: [2, 42], 81: [2, 42] }, { 21: [2, 41], 31: [2, 41], 52: [2, 41], 59: [2, 41], 62: [2, 41], 66: [2, 41], 69: [2, 41], 74: [2, 41], 75: [2, 41], 76: [2, 41], 77: [2, 41], 78: [2, 41], 79: [2, 41], 81: [2, 41] }, { 52: [1, 97] }, { 52: [2, 78], 59: [2, 78], 66: [2, 78], 74: [2, 78], 75: [2, 78], 76: [2, 78], 77: [2, 78], 78: [2, 78], 79: [2, 78] }, { 52: [2, 80] }, { 5: [2, 12], 13: [2, 12], 14: [2, 12], 17: [2, 12], 27: [2, 12], 32: [2, 12], 37: [2, 12], 42: [2, 12], 45: [2, 12], 46: [2, 12], 49: [2, 12], 53: [2, 12] }, { 18: 98, 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 36: 50, 37: [1, 52], 41: 51, 42: [1, 53], 43: 100, 44: 99, 45: [2, 71] }, { 31: [2, 65], 38: 101, 59: [2, 65], 66: [2, 65], 69: [2, 65], 74: [2, 65], 75: [2, 65], 76: [2, 65], 77: [2, 65], 78: [2, 65], 79: [2, 65] }, { 45: [2, 17] }, { 5: [2, 13], 13: [2, 13], 14: [2, 13], 17: [2, 13], 27: [2, 13], 32: [2, 13], 37: [2, 13], 42: [2, 13], 45: [2, 13], 46: [2, 13], 49: [2, 13], 53: [2, 13] }, { 31: [1, 102] }, { 31: [2, 82], 59: [2, 82], 66: [2, 82], 74: [2, 82], 75: [2, 82], 76: [2, 82], 77: [2, 82], 78: [2, 82], 79: [2, 82] }, { 31: [2, 84] }, { 18: 65, 57: 104, 58: 66, 59: [1, 40], 61: 103, 62: [2, 87], 63: 105, 64: 67, 65: 68, 66: [1, 69], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 30: 106, 31: [2, 57], 68: 107, 69: [1, 108] }, { 31: [2, 54], 59: [2, 54], 66: [2, 54], 69: [2, 54], 74: [2, 54], 75: [2, 54], 76: [2, 54], 77: [2, 54], 78: [2, 54], 79: [2, 54] }, { 31: [2, 56], 69: [2, 56] }, { 31: [2, 63], 35: 109, 68: 110, 69: [1, 108] }, { 31: [2, 60], 59: [2, 60], 66: [2, 60], 69: [2, 60], 74: [2, 60], 75: [2, 60], 76: [2, 60], 77: [2, 60], 78: [2, 60], 79: [2, 60] }, { 31: [2, 62], 69: [2, 62] }, { 21: [1, 111] }, { 21: [2, 46], 59: [2, 46], 66: [2, 46], 74: [2, 46], 75: [2, 46], 76: [2, 46], 77: [2, 46], 78: [2, 46], 79: [2, 46] }, { 21: [2, 48] }, { 5: [2, 21], 13: [2, 21], 14: [2, 21], 17: [2, 21], 27: [2, 21], 32: [2, 21], 37: [2, 21], 42: [2, 21], 45: [2, 21], 46: [2, 21], 49: [2, 21], 53: [2, 21] }, { 21: [2, 90], 31: [2, 90], 52: [2, 90], 62: [2, 90], 66: [2, 90], 69: [2, 90] }, { 67: [1, 96] }, { 18: 65, 57: 112, 58: 66, 59: [1, 40], 66: [1, 32], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 5: [2, 22], 13: [2, 22], 14: [2, 22], 17: [2, 22], 27: [2, 22], 32: [2, 22], 37: [2, 22], 42: [2, 22], 45: [2, 22], 46: [2, 22], 49: [2, 22], 53: [2, 22] }, { 31: [1, 113] }, { 45: [2, 18] }, { 45: [2, 72] }, { 18: 65, 31: [2, 67], 39: 114, 57: 115, 58: 66, 59: [1, 40], 63: 116, 64: 67, 65: 68, 66: [1, 69], 69: [2, 67], 72: 23, 73: 24, 74: [1, 25], 75: [1, 26], 76: [1, 27], 77: [1, 28], 78: [1, 29], 79: [1, 31], 80: 30 }, { 5: [2, 23], 13: [2, 23], 14: [2, 23], 17: [2, 23], 27: [2, 23], 32: [2, 23], 37: [2, 23], 42: [2, 23], 45: [2, 23], 46: [2, 23], 49: [2, 23], 53: [2, 23] }, { 62: [1, 117] }, { 59: [2, 86], 62: [2, 86], 66: [2, 86], 74: [2, 86], 75: [2, 86], 76: [2, 86], 77: [2, 86], 78: [2, 86], 79: [2, 86] }, { 62: [2, 88] }, { 31: [1, 118] }, { 31: [2, 58] }, { 66: [1, 120], 70: 119 }, { 31: [1, 121] }, { 31: [2, 64] }, { 14: [2, 11] }, { 21: [2, 28], 31: [2, 28], 52: [2, 28], 62: [2, 28], 66: [2, 28], 69: [2, 28] }, { 5: [2, 20], 13: [2, 20], 14: [2, 20], 17: [2, 20], 27: [2, 20], 32: [2, 20], 37: [2, 20], 42: [2, 20], 45: [2, 20], 46: [2, 20], 49: [2, 20], 53: [2, 20] }, { 31: [2, 69], 40: 122, 68: 123, 69: [1, 108] }, { 31: [2, 66], 59: [2, 66], 66: [2, 66], 69: [2, 66], 74: [2, 66], 75: [2, 66], 76: [2, 66], 77: [2, 66], 78: [2, 66], 79: [2, 66] }, { 31: [2, 68], 69: [2, 68] }, { 21: [2, 26], 31: [2, 26], 52: [2, 26], 59: [2, 26], 62: [2, 26], 66: [2, 26], 69: [2, 26], 74: [2, 26], 75: [2, 26], 76: [2, 26], 77: [2, 26], 78: [2, 26], 79: [2, 26] }, { 13: [2, 14], 14: [2, 14], 17: [2, 14], 27: [2, 14], 32: [2, 14], 37: [2, 14], 42: [2, 14], 45: [2, 14], 46: [2, 14], 49: [2, 14], 53: [2, 14] }, { 66: [1, 125], 71: [1, 124] }, { 66: [2, 91], 71: [2, 91] }, { 13: [2, 15], 14: [2, 15], 17: [2, 15], 27: [2, 15], 32: [2, 15], 42: [2, 15], 45: [2, 15], 46: [2, 15], 49: [2, 15], 53: [2, 15] }, { 31: [1, 126] }, { 31: [2, 70] }, { 31: [2, 29] }, { 66: [2, 92], 71: [2, 92] }, { 13: [2, 16], 14: [2, 16], 17: [2, 16], 27: [2, 16], 32: [2, 16], 37: [2, 16], 42: [2, 16], 45: [2, 16], 46: [2, 16], 49: [2, 16], 53: [2, 16] }],
+ defaultActions: { 4: [2, 1], 49: [2, 50], 51: [2, 19], 55: [2, 52], 64: [2, 76], 73: [2, 80], 78: [2, 17], 82: [2, 84], 92: [2, 48], 99: [2, 18], 100: [2, 72], 105: [2, 88], 107: [2, 58], 110: [2, 64], 111: [2, 11], 123: [2, 70], 124: [2, 29] },
+ parseError: function parseError(str, hash) {
+ throw new Error(str);
+ },
+ parse: function parse(input) {
+ var self = this,
+ stack = [0],
+ vstack = [null],
+ lstack = [],
+ table = this.table,
+ yytext = "",
+ yylineno = 0,
+ yyleng = 0,
+ recovering = 0,
+ TERROR = 2,
+ EOF = 1;
+ this.lexer.setInput(input);
+ this.lexer.yy = this.yy;
+ this.yy.lexer = this.lexer;
+ this.yy.parser = this;
+ if (typeof this.lexer.yylloc == "undefined") this.lexer.yylloc = {};
+ var yyloc = this.lexer.yylloc;
+ lstack.push(yyloc);
+ var ranges = this.lexer.options && this.lexer.options.ranges;
+ if (typeof this.yy.parseError === "function") this.parseError = this.yy.parseError;
+ function popStack(n) {
+ stack.length = stack.length - 2 * n;
+ vstack.length = vstack.length - n;
+ lstack.length = lstack.length - n;
+ }
+ function lex() {
+ var token;
+ token = self.lexer.lex() || 1;
+ if (typeof token !== "number") {
+ token = self.symbols_[token] || token;
+ }
+ return token;
+ }
+ var symbol,
+ preErrorSymbol,
+ state,
+ action,
+ a,
+ r,
+ yyval = {},
+ p,
+ len,
+ newState,
+ expected;
+ while (true) {
+ state = stack[stack.length - 1];
+ if (this.defaultActions[state]) {
+ action = this.defaultActions[state];
+ } else {
+ if (symbol === null || typeof symbol == "undefined") {
+ symbol = lex();
+ }
+ action = table[state] && table[state][symbol];
+ }
+ if (typeof action === "undefined" || !action.length || !action[0]) {
+ var errStr = "";
+ if (!recovering) {
+ expected = [];
+ for (p in table[state]) if (this.terminals_[p] && p > 2) {
+ expected.push("'" + this.terminals_[p] + "'");
+ }
+ if (this.lexer.showPosition) {
+ errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
+ } else {
+ errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1 ? "end of input" : "'" + (this.terminals_[symbol] || symbol) + "'");
+ }
+ this.parseError(errStr, { text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected });
+ }
+ }
+ if (action[0] instanceof Array && action.length > 1) {
+ throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
+ }
+ switch (action[0]) {
+ case 1:
+ stack.push(symbol);
+ vstack.push(this.lexer.yytext);
+ lstack.push(this.lexer.yylloc);
+ stack.push(action[1]);
+ symbol = null;
+ if (!preErrorSymbol) {
+ yyleng = this.lexer.yyleng;
+ yytext = this.lexer.yytext;
+ yylineno = this.lexer.yylineno;
+ yyloc = this.lexer.yylloc;
+ if (recovering > 0) recovering--;
+ } else {
+ symbol = preErrorSymbol;
+ preErrorSymbol = null;
+ }
+ break;
+ case 2:
+ len = this.productions_[action[1]][1];
+ yyval.$ = vstack[vstack.length - len];
+ yyval._$ = { first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column };
+ if (ranges) {
+ yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
+ }
+ r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
+ if (typeof r !== "undefined") {
+ return r;
+ }
+ if (len) {
+ stack = stack.slice(0, -1 * len * 2);
+ vstack = vstack.slice(0, -1 * len);
+ lstack = lstack.slice(0, -1 * len);
+ }
+ stack.push(this.productions_[action[1]][0]);
+ vstack.push(yyval.$);
+ lstack.push(yyval._$);
+ newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+ stack.push(newState);
+ break;
+ case 3:
+ return true;
+ }
+ }
+ return true;
+ }
+ };
+ /* Jison generated lexer */
+ var lexer = (function () {
+ var lexer = { EOF: 1,
+ parseError: function parseError(str, hash) {
+ if (this.yy.parser) {
+ this.yy.parser.parseError(str, hash);
+ } else {
+ throw new Error(str);
+ }
+ },
+ setInput: function setInput(input) {
+ this._input = input;
+ this._more = this._less = this.done = false;
+ this.yylineno = this.yyleng = 0;
+ this.yytext = this.matched = this.match = "";
+ this.conditionStack = ["INITIAL"];
+ this.yylloc = { first_line: 1, first_column: 0, last_line: 1, last_column: 0 };
+ if (this.options.ranges) this.yylloc.range = [0, 0];
+ this.offset = 0;
+ return this;
+ },
+ input: function input() {
+ var ch = this._input[0];
+ this.yytext += ch;
+ this.yyleng++;
+ this.offset++;
+ this.match += ch;
+ this.matched += ch;
+ var lines = ch.match(/(?:\r\n?|\n).*/g);
+ if (lines) {
+ this.yylineno++;
+ this.yylloc.last_line++;
+ } else {
+ this.yylloc.last_column++;
+ }
+ if (this.options.ranges) this.yylloc.range[1]++;
+
+ this._input = this._input.slice(1);
+ return ch;
+ },
+ unput: function unput(ch) {
+ var len = ch.length;
+ var lines = ch.split(/(?:\r\n?|\n)/g);
+
+ this._input = ch + this._input;
+ this.yytext = this.yytext.substr(0, this.yytext.length - len - 1);
+ //this.yyleng -= len;
+ this.offset -= len;
+ var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+ this.match = this.match.substr(0, this.match.length - 1);
+ this.matched = this.matched.substr(0, this.matched.length - 1);
+
+ if (lines.length - 1) this.yylineno -= lines.length - 1;
+ var r = this.yylloc.range;
+
+ this.yylloc = { first_line: this.yylloc.first_line,
+ last_line: this.yylineno + 1,
+ first_column: this.yylloc.first_column,
+ last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len
+ };
+
+ if (this.options.ranges) {
+ this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+ }
+ return this;
+ },
+ more: function more() {
+ this._more = true;
+ return this;
+ },
+ less: function less(n) {
+ this.unput(this.match.slice(n));
+ },
+ pastInput: function pastInput() {
+ var past = this.matched.substr(0, this.matched.length - this.match.length);
+ return (past.length > 20 ? "..." : "") + past.substr(-20).replace(/\n/g, "");
+ },
+ upcomingInput: function upcomingInput() {
+ var next = this.match;
+ if (next.length < 20) {
+ next += this._input.substr(0, 20 - next.length);
+ }
+ return (next.substr(0, 20) + (next.length > 20 ? "..." : "")).replace(/\n/g, "");
+ },
+ showPosition: function showPosition() {
+ var pre = this.pastInput();
+ var c = new Array(pre.length + 1).join("-");
+ return pre + this.upcomingInput() + "\n" + c + "^";
+ },
+ next: function next() {
+ if (this.done) {
+ return this.EOF;
+ }
+ if (!this._input) this.done = true;
+
+ var token, match, tempMatch, index, col, lines;
+ if (!this._more) {
+ this.yytext = "";
+ this.match = "";
+ }
+ var rules = this._currentRules();
+ for (var i = 0; i < rules.length; i++) {
+ tempMatch = this._input.match(this.rules[rules[i]]);
+ if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+ match = tempMatch;
+ index = i;
+ if (!this.options.flex) break;
+ }
+ }
+ if (match) {
+ lines = match[0].match(/(?:\r\n?|\n).*/g);
+ if (lines) this.yylineno += lines.length;
+ this.yylloc = { first_line: this.yylloc.last_line,
+ last_line: this.yylineno + 1,
+ first_column: this.yylloc.last_column,
+ last_column: lines ? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length };
+ this.yytext += match[0];
+ this.match += match[0];
+ this.matches = match;
+ this.yyleng = this.yytext.length;
+ if (this.options.ranges) {
+ this.yylloc.range = [this.offset, this.offset += this.yyleng];
+ }
+ this._more = false;
+ this._input = this._input.slice(match[0].length);
+ this.matched += match[0];
+ token = this.performAction.call(this, this.yy, this, rules[index], this.conditionStack[this.conditionStack.length - 1]);
+ if (this.done && this._input) this.done = false;
+ if (token) {
+ return token;
+ } else {
+ return;
+ }
+ }
+ if (this._input === "") {
+ return this.EOF;
+ } else {
+ return this.parseError("Lexical error on line " + (this.yylineno + 1) + ". Unrecognized text.\n" + this.showPosition(), { text: "", token: null, line: this.yylineno });
+ }
+ },
+ lex: function lex() {
+ var r = this.next();
+ if (typeof r !== "undefined") {
+ return r;
+ } else {
+ return this.lex();
+ }
+ },
+ begin: function begin(condition) {
+ this.conditionStack.push(condition);
+ },
+ popState: function popState() {
+ return this.conditionStack.pop();
+ },
+ _currentRules: function _currentRules() {
+ return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+ },
+ topState: function topState() {
+ return this.conditionStack[this.conditionStack.length - 2];
+ },
+ pushState: function begin(condition) {
+ this.begin(condition);
+ } };
+ lexer.options = {};
+ lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {
+
+ function strip(start, end) {
+ return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng - end);
+ }
+
+ var YYSTATE = YY_START;
+ switch ($avoiding_name_collisions) {
+ case 0:
+ if (yy_.yytext.slice(-2) === "\\\\") {
+ strip(0, 1);
+ this.begin("mu");
+ } else if (yy_.yytext.slice(-1) === "\\") {
+ strip(0, 1);
+ this.begin("emu");
+ } else {
+ this.begin("mu");
+ }
+ if (yy_.yytext) {
+ return 14;
+ }break;
+ case 1:
+ return 14;
+ break;
+ case 2:
+ this.popState();
+ return 14;
+
+ break;
+ case 3:
+ yy_.yytext = yy_.yytext.substr(5, yy_.yyleng - 9);
+ this.popState();
+ return 16;
+
+ break;
+ case 4:
+ return 14;
+ break;
+ case 5:
+ this.popState();
+ return 13;
+
+ break;
+ case 6:
+ return 59;
+ break;
+ case 7:
+ return 62;
+ break;
+ case 8:
+ return 17;
+ break;
+ case 9:
+ this.popState();
+ this.begin("raw");
+ return 21;
+
+ break;
+ case 10:
+ return 53;
+ break;
+ case 11:
+ return 27;
+ break;
+ case 12:
+ return 45;
+ break;
+ case 13:
+ this.popState();return 42;
+ break;
+ case 14:
+ this.popState();return 42;
+ break;
+ case 15:
+ return 32;
+ break;
+ case 16:
+ return 37;
+ break;
+ case 17:
+ return 49;
+ break;
+ case 18:
+ return 46;
+ break;
+ case 19:
+ this.unput(yy_.yytext);
+ this.popState();
+ this.begin("com");
+
+ break;
+ case 20:
+ this.popState();
+ return 13;
+
+ break;
+ case 21:
+ return 46;
+ break;
+ case 22:
+ return 67;
+ break;
+ case 23:
+ return 66;
+ break;
+ case 24:
+ return 66;
+ break;
+ case 25:
+ return 81;
+ break;
+ case 26:
+ // ignore whitespace
+ break;
+ case 27:
+ this.popState();return 52;
+ break;
+ case 28:
+ this.popState();return 31;
+ break;
+ case 29:
+ yy_.yytext = strip(1, 2).replace(/\\"/g, "\"");return 74;
+ break;
+ case 30:
+ yy_.yytext = strip(1, 2).replace(/\\'/g, "'");return 74;
+ break;
+ case 31:
+ return 79;
+ break;
+ case 32:
+ return 76;
+ break;
+ case 33:
+ return 76;
+ break;
+ case 34:
+ return 77;
+ break;
+ case 35:
+ return 78;
+ break;
+ case 36:
+ return 75;
+ break;
+ case 37:
+ return 69;
+ break;
+ case 38:
+ return 71;
+ break;
+ case 39:
+ return 66;
+ break;
+ case 40:
+ return 66;
+ break;
+ case 41:
+ return "INVALID";
+ break;
+ case 42:
+ return 5;
+ break;
+ }
+ };
+ lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/, /^(?:[^\x00]+)/, /^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/, /^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/, /^(?:[^\x00]*?(?=(\{\{\{\{\/)))/, /^(?:[\s\S]*?--(~)?\}\})/, /^(?:\()/, /^(?:\))/, /^(?:\{\{\{\{)/, /^(?:\}\}\}\})/, /^(?:\{\{(~)?>)/, /^(?:\{\{(~)?#)/, /^(?:\{\{(~)?\/)/, /^(?:\{\{(~)?\^\s*(~)?\}\})/, /^(?:\{\{(~)?\s*else\s*(~)?\}\})/, /^(?:\{\{(~)?\^)/, /^(?:\{\{(~)?\s*else\b)/, /^(?:\{\{(~)?\{)/, /^(?:\{\{(~)?&)/, /^(?:\{\{(~)?!--)/, /^(?:\{\{(~)?![\s\S]*?\}\})/, /^(?:\{\{(~)?)/, /^(?:=)/, /^(?:\.\.)/, /^(?:\.(?=([=~}\s\/.)|])))/, /^(?:[\/.])/, /^(?:\s+)/, /^(?:\}(~)?\}\})/, /^(?:(~)?\}\})/, /^(?:"(\\["]|[^"])*")/, /^(?:'(\\[']|[^'])*')/, /^(?:@)/, /^(?:true(?=([~}\s)])))/, /^(?:false(?=([~}\s)])))/, /^(?:undefined(?=([~}\s)])))/, /^(?:null(?=([~}\s)])))/, /^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/, /^(?:as\s+\|)/, /^(?:\|)/, /^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/, /^(?:\[[^\]]*\])/, /^(?:.)/, /^(?:$)/];
+ lexer.conditions = { mu: { rules: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42], inclusive: false }, emu: { rules: [2], inclusive: false }, com: { rules: [5], inclusive: false }, raw: { rules: [3, 4], inclusive: false }, INITIAL: { rules: [0, 1, 42], inclusive: true } };
+ return lexer;
+ })();
+ parser.lexer = lexer;
+ function Parser() {
+ this.yy = {};
+ }Parser.prototype = parser;parser.Parser = Parser;
+ return new Parser();
+ })();exports["default"] = handlebars;
+ module.exports = exports["default"];
+
+/***/ },
+/* 16 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ var _interopRequireDefault = __webpack_require__(8)['default'];
+
+ exports.__esModule = true;
+
+ var _Visitor = __webpack_require__(6);
+
+ var _Visitor2 = _interopRequireDefault(_Visitor);
+
+ function WhitespaceControl() {}
+ WhitespaceControl.prototype = new _Visitor2['default']();
+
+ WhitespaceControl.prototype.Program = function (program) {
+ var isRoot = !this.isRootSeen;
+ this.isRootSeen = true;
+
+ var body = program.body;
+ for (var i = 0, l = body.length; i < l; i++) {
+ var current = body[i],
+ strip = this.accept(current);
+
+ if (!strip) {
+ continue;
+ }
+
+ var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot),
+ _isNextWhitespace = isNextWhitespace(body, i, isRoot),
+ openStandalone = strip.openStandalone && _isPrevWhitespace,
+ closeStandalone = strip.closeStandalone && _isNextWhitespace,
+ inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
+
+ if (strip.close) {
+ omitRight(body, i, true);
+ }
+ if (strip.open) {
+ omitLeft(body, i, true);
+ }
+
+ if (inlineStandalone) {
+ omitRight(body, i);
+
+ if (omitLeft(body, i)) {
+ // If we are on a standalone node, save the indent info for partials
+ if (current.type === 'PartialStatement') {
+ // Pull out the whitespace from the final line
+ current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1];
+ }
+ }
+ }
+ if (openStandalone) {
+ omitRight((current.program || current.inverse).body);
+
+ // Strip out the previous content node if it's whitespace only
+ omitLeft(body, i);
+ }
+ if (closeStandalone) {
+ // Always strip the next node
+ omitRight(body, i);
+
+ omitLeft((current.inverse || current.program).body);
+ }
+ }
+
+ return program;
+ };
+ WhitespaceControl.prototype.BlockStatement = function (block) {
+ this.accept(block.program);
+ this.accept(block.inverse);
+
+ // Find the inverse program that is involed with whitespace stripping.
+ var program = block.program || block.inverse,
+ inverse = block.program && block.inverse,
+ firstInverse = inverse,
+ lastInverse = inverse;
+
+ if (inverse && inverse.chained) {
+ firstInverse = inverse.body[0].program;
+
+ // Walk the inverse chain to find the last inverse that is actually in the chain.
+ while (lastInverse.chained) {
+ lastInverse = lastInverse.body[lastInverse.body.length - 1].program;
+ }
+ }
+
+ var strip = {
+ open: block.openStrip.open,
+ close: block.closeStrip.close,
+
+ // Determine the standalone candiacy. Basically flag our content as being possibly standalone
+ // so our parent can determine if we actually are standalone
+ openStandalone: isNextWhitespace(program.body),
+ closeStandalone: isPrevWhitespace((firstInverse || program).body)
+ };
+
+ if (block.openStrip.close) {
+ omitRight(program.body, null, true);
+ }
+
+ if (inverse) {
+ var inverseStrip = block.inverseStrip;
+
+ if (inverseStrip.open) {
+ omitLeft(program.body, null, true);
+ }
+
+ if (inverseStrip.close) {
+ omitRight(firstInverse.body, null, true);
+ }
+ if (block.closeStrip.open) {
+ omitLeft(lastInverse.body, null, true);
+ }
+
+ // Find standalone else statments
+ if (isPrevWhitespace(program.body) && isNextWhitespace(firstInverse.body)) {
+ omitLeft(program.body);
+ omitRight(firstInverse.body);
+ }
+ } else if (block.closeStrip.open) {
+ omitLeft(program.body, null, true);
+ }
+
+ return strip;
+ };
+
+ WhitespaceControl.prototype.MustacheStatement = function (mustache) {
+ return mustache.strip;
+ };
+
+ WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function (node) {
+ /* istanbul ignore next */
+ var strip = node.strip || {};
+ return {
+ inlineStandalone: true,
+ open: strip.open,
+ close: strip.close
+ };
+ };
+
+ function isPrevWhitespace(body, i, isRoot) {
+ if (i === undefined) {
+ i = body.length;
+ }
+
+ // Nodes that end with newlines are considered whitespace (but are special
+ // cased for strip operations)
+ var prev = body[i - 1],
+ sibling = body[i - 2];
+ if (!prev) {
+ return isRoot;
+ }
+
+ if (prev.type === 'ContentStatement') {
+ return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original);
+ }
+ }
+ function isNextWhitespace(body, i, isRoot) {
+ if (i === undefined) {
+ i = -1;
+ }
+
+ var next = body[i + 1],
+ sibling = body[i + 2];
+ if (!next) {
+ return isRoot;
+ }
+
+ if (next.type === 'ContentStatement') {
+ return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test(next.original);
+ }
+ }
+
+ // Marks the node to the right of the position as omitted.
+ // I.e. {{foo}}' ' will mark the ' ' node as omitted.
+ //
+ // If i is undefined, then the first child will be marked as such.
+ //
+ // If mulitple is truthy then all whitespace will be stripped out until non-whitespace
+ // content is met.
+ function omitRight(body, i, multiple) {
+ var current = body[i == null ? 0 : i + 1];
+ if (!current || current.type !== 'ContentStatement' || !multiple && current.rightStripped) {
+ return;
+ }
+
+ var original = current.value;
+ current.value = current.value.replace(multiple ? /^\s+/ : /^[ \t]*\r?\n?/, '');
+ current.rightStripped = current.value !== original;
+ }
+
+ // Marks the node to the left of the position as omitted.
+ // I.e. ' '{{foo}} will mark the ' ' node as omitted.
+ //
+ // If i is undefined then the last child will be marked as such.
+ //
+ // If mulitple is truthy then all whitespace will be stripped out until non-whitespace
+ // content is met.
+ function omitLeft(body, i, multiple) {
+ var current = body[i == null ? body.length - 1 : i - 1];
+ if (!current || current.type !== 'ContentStatement' || !multiple && current.leftStripped) {
+ return;
+ }
+
+ // We omit the last node if it's whitespace only and not preceeded by a non-content node.
+ var original = current.value;
+ current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, '');
+ current.leftStripped = current.value !== original;
+ return current.leftStripped;
+ }
+
+ exports['default'] = WhitespaceControl;
+ module.exports = exports['default'];
+
+/***/ },
+/* 17 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ var _interopRequireDefault = __webpack_require__(8)['default'];
+
+ exports.__esModule = true;
+ exports.SourceLocation = SourceLocation;
+ exports.id = id;
+ exports.stripFlags = stripFlags;
+ exports.stripComment = stripComment;
+ exports.preparePath = preparePath;
+ exports.prepareMustache = prepareMustache;
+ exports.prepareRawBlock = prepareRawBlock;
+ exports.prepareBlock = prepareBlock;
+
+ var _Exception = __webpack_require__(12);
+
+ var _Exception2 = _interopRequireDefault(_Exception);
+
+ function SourceLocation(source, locInfo) {
+ this.source = source;
+ this.start = {
+ line: locInfo.first_line,
+ column: locInfo.first_column
+ };
+ this.end = {
+ line: locInfo.last_line,
+ column: locInfo.last_column
+ };
+ }
+
+ function id(token) {
+ if (/^\[.*\]$/.test(token)) {
+ return token.substr(1, token.length - 2);
+ } else {
+ return token;
+ }
+ }
+
+ function stripFlags(open, close) {
+ return {
+ open: open.charAt(2) === '~',
+ close: close.charAt(close.length - 3) === '~'
+ };
+ }
+
+ function stripComment(comment) {
+ return comment.replace(/^\{\{~?\!-?-?/, '').replace(/-?-?~?\}\}$/, '');
+ }
+
+ function preparePath(data, parts, locInfo) {
+ locInfo = this.locInfo(locInfo);
+
+ var original = data ? '@' : '',
+ dig = [],
+ depth = 0,
+ depthString = '';
+
+ for (var i = 0, l = parts.length; i < l; i++) {
+ var part = parts[i].part,
+
+ // If we have [] syntax then we do not treat path references as operators,
+ // i.e. foo.[this] resolves to approximately context.foo['this']
+ isLiteral = parts[i].original !== part;
+ original += (parts[i].separator || '') + part;
+
+ if (!isLiteral && (part === '..' || part === '.' || part === 'this')) {
+ if (dig.length > 0) {
+ throw new _Exception2['default']('Invalid path: ' + original, { loc: locInfo });
+ } else if (part === '..') {
+ depth++;
+ depthString += '../';
+ }
+ } else {
+ dig.push(part);
+ }
+ }
+
+ return new this.PathExpression(data, depth, dig, original, locInfo);
+ }
+
+ function prepareMustache(path, params, hash, open, strip, locInfo) {
+ // Must use charAt to support IE pre-10
+ var escapeFlag = open.charAt(3) || open.charAt(2),
+ escaped = escapeFlag !== '{' && escapeFlag !== '&';
+
+ return new this.MustacheStatement(path, params, hash, escaped, strip, this.locInfo(locInfo));
+ }
+
+ function prepareRawBlock(openRawBlock, content, close, locInfo) {
+ if (openRawBlock.path.original !== close) {
+ var errorNode = { loc: openRawBlock.path.loc };
+
+ throw new _Exception2['default'](openRawBlock.path.original + ' doesn\'t match ' + close, errorNode);
+ }
+
+ locInfo = this.locInfo(locInfo);
+ var program = new this.Program([content], null, {}, locInfo);
+
+ return new this.BlockStatement(openRawBlock.path, openRawBlock.params, openRawBlock.hash, program, undefined, {}, {}, {}, locInfo);
+ }
+
+ function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {
+ // When we are chaining inverse calls, we will not have a close path
+ if (close && close.path && openBlock.path.original !== close.path.original) {
+ var errorNode = { loc: openBlock.path.loc };
+
+ throw new _Exception2['default'](openBlock.path.original + ' doesn\'t match ' + close.path.original, errorNode);
+ }
+
+ program.blockParams = openBlock.blockParams;
+
+ var inverse = undefined,
+ inverseStrip = undefined;
+
+ if (inverseAndProgram) {
+ if (inverseAndProgram.chain) {
+ inverseAndProgram.program.body[0].closeStrip = close.strip;
+ }
+
+ inverseStrip = inverseAndProgram.strip;
+ inverse = inverseAndProgram.program;
+ }
+
+ if (inverted) {
+ inverted = inverse;
+ inverse = program;
+ program = inverted;
+ }
+
+ return new this.BlockStatement(openBlock.path, openBlock.params, openBlock.hash, program, inverse, openBlock.strip, inverseStrip, close && close.strip, this.locInfo(locInfo));
+ }
+
+/***/ },
+/* 18 */
+/***/ function(module, exports, __webpack_require__) {
+
+ 'use strict';
+
+ exports.__esModule = true;
+ /*global define */
+
+ var _isArray = __webpack_require__(13);
+
+ var SourceNode = undefined;
+
+ try {
+ /* istanbul ignore next */
+ if (false) {
+ // We don't support this in AMD environments. For these environments, we asusme that
+ // they are running on the browser and thus have no need for the source-map library.
+ var SourceMap = require('source-map');
+ SourceNode = SourceMap.SourceNode;
+ }
+ } catch (err) {}
+
+ /* istanbul ignore if: tested but not covered in istanbul due to dist build */
+ if (!SourceNode) {
+ SourceNode = function (line, column, srcFile, chunks) {
+ this.src = '';
+ if (chunks) {
+ this.add(chunks);
+ }
+ };
+ /* istanbul ignore next */
+ SourceNode.prototype = {
+ add: function add(chunks) {
+ if (_isArray.isArray(chunks)) {
+ chunks = chunks.join('');
+ }
+ this.src += chunks;
+ },
+ prepend: function prepend(chunks) {
+ if (_isArray.isArray(chunks)) {
+ chunks = chunks.join('');
+ }
+ this.src = chunks + this.src;
+ },
+ toStringWithSourceMap: function toStringWithSourceMap() {
+ return { code: this.toString() };
+ },
+ toString: function toString() {
+ return this.src;
+ }
+ };
+ }
+
+ function castChunk(chunk, codeGen, loc) {
+ if (_isArray.isArray(chunk)) {
+ var ret = [];
+
+ for (var i = 0, len = chunk.length; i < len; i++) {
+ ret.push(codeGen.wrap(chunk[i], loc));
+ }
+ return ret;
+ } else if (typeof chunk === 'boolean' || typeof chunk === 'number') {
+ // Handle primitives that the SourceNode will throw up on
+ return chunk + '';
+ }
+ return chunk;
+ }
+
+ function CodeGen(srcFile) {
+ this.srcFile = srcFile;
+ this.source = [];
+ }
+
+ CodeGen.prototype = {
+ prepend: function prepend(source, loc) {
+ this.source.unshift(this.wrap(source, loc));
+ },
+ push: function push(source, loc) {
+ this.source.push(this.wrap(source, loc));
+ },
+
+ merge: function merge() {
+ var source = this.empty();
+ this.each(function (line) {
+ source.add([' ', line, '\n']);
+ });
+ return source;
+ },
+
+ each: function each(iter) {
+ for (var i = 0, len = this.source.length; i < len; i++) {
+ iter(this.source[i]);
+ }
+ },
+
+ empty: function empty() {
+ var loc = arguments[0] === undefined ? this.currentLocation || { start: {} } : arguments[0];
+
+ return new SourceNode(loc.start.line, loc.start.column, this.srcFile);
+ },
+ wrap: function wrap(chunk) {
+ var loc = arguments[1] === undefined ? this.currentLocation || { start: {} } : arguments[1];
+
+ if (chunk instanceof SourceNode) {
+ return chunk;
+ }
+
+ chunk = castChunk(chunk, this, loc);
+
+ return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk);
+ },
+
+ functionCall: function functionCall(fn, type, params) {
+ params = this.generateList(params);
+ return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']);
+ },
+
+ quotedString: function quotedString(str) {
+ return '"' + (str + '').replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
+ .replace(/\u2029/g, '\\u2029') + '"';
+ },
+
+ objectLiteral: function objectLiteral(obj) {
+ var pairs = [];
+
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ var value = castChunk(obj[key], this);
+ if (value !== 'undefined') {
+ pairs.push([this.quotedString(key), ':', value]);
+ }
+ }
+ }
+
+ var ret = this.generateList(pairs);
+ ret.prepend('{');
+ ret.add('}');
+ return ret;
+ },
+
+ generateList: function generateList(entries, loc) {
+ var ret = this.empty(loc);
+
+ for (var i = 0, len = entries.length; i < len; i++) {
+ if (i) {
+ ret.add(',');
+ }
+
+ ret.add(castChunk(entries[i], this, loc));
+ }
+
+ return ret;
+ },
+
+ generateArray: function generateArray(entries, loc) {
+ var ret = this.generateList(entries, loc);
+ ret.prepend('[');
+ ret.add(']');
+
+ return ret;
+ }
+ };
+
+ exports['default'] = CodeGen;
+ module.exports = exports['default'];
+
+ /* NOP */
+
+/***/ }
+/******/ ])
+});
+;
\ No newline at end of file
diff --git a/common/js/plugins/handlebars/handlebars.min.js b/common/js/plugins/handlebars/handlebars.min.js
index fb1c0d051..a0b7db6e3 100644
--- a/common/js/plugins/handlebars/handlebars.min.js
+++ b/common/js/plugins/handlebars/handlebars.min.js
@@ -1,2 +1,4 @@
-!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.Handlebars=a.Handlebars||b()}(this,function(){var a=function(){"use strict";function a(a){this.string=a}var b;return a.prototype.toString=function(){return""+this.string},b=a}(),b=function(a){"use strict";function b(a){return i[a]}function c(a){for(var b=1;b":">",'"':""","'":"'","`":"`"},j=/[&<>"'`]/g,k=/[&<>"'`]/;g.extend=c;var l=Object.prototype.toString;g.toString=l;var m=function(a){return"function"==typeof a};m(/x/)&&(m=function(a){return"function"==typeof a&&"[object Function]"===l.call(a)});var m;g.isFunction=m;var n=Array.isArray||function(a){return a&&"object"==typeof a?"[object Array]"===l.call(a):!1};return g.isArray=n,g.escapeExpression=d,g.isEmpty=e,g.appendContextPath=f,g}(a),c=function(){"use strict";function a(a,b){var d;b&&b.firstLine&&(d=b.firstLine,a+=" - "+d+":"+b.firstColumn);for(var e=Error.prototype.constructor.call(this,a),f=0;f0?(c.ids&&(c.ids=[c.name]),a.helpers.each(b,c)):d(this);if(c.data&&c.ids){var g=q(c.data);g.contextPath=f.appendContextPath(c.data.contextPath,c.name),c={data:g}}return e(b,c)}),a.registerHelper("each",function(a,b){if(!b)throw new g("Must pass iterator to #each");var c,d,e=b.fn,h=b.inverse,i=0,j="";if(b.data&&b.ids&&(d=f.appendContextPath(b.data.contextPath,b.ids[0])+"."),l(a)&&(a=a.call(this)),b.data&&(c=q(b.data)),a&&"object"==typeof a)if(k(a))for(var m=a.length;m>i;i++)c&&(c.index=i,c.first=0===i,c.last=i===a.length-1,d&&(c.contextPath=d+i)),j+=e(a[i],{data:c});else for(var n in a)a.hasOwnProperty(n)&&(c&&(c.key=n,c.index=i,c.first=0===i,d&&(c.contextPath=d+n)),j+=e(a[n],{data:c}),i++);return 0===i&&(j=h(this)),j}),a.registerHelper("if",function(a,b){return l(a)&&(a=a.call(this)),!b.hash.includeZero&&!a||f.isEmpty(a)?b.inverse(this):b.fn(this)}),a.registerHelper("unless",function(b,c){return a.helpers["if"].call(this,b,{fn:c.inverse,inverse:c.fn,hash:c.hash})}),a.registerHelper("with",function(a,b){l(a)&&(a=a.call(this));var c=b.fn;if(f.isEmpty(a))return b.inverse(this);if(b.data&&b.ids){var d=q(b.data);d.contextPath=f.appendContextPath(b.data.contextPath,b.ids[0]),b={data:d}}return c(a,b)}),a.registerHelper("log",function(b,c){var d=c.data&&null!=c.data.level?parseInt(c.data.level,10):1;a.log(d,b)}),a.registerHelper("lookup",function(a,b){return a&&a[b]})}var e={},f=a,g=b,h="2.0.0";e.VERSION=h;var i=6;e.COMPILER_REVISION=i;var j={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1"};e.REVISION_CHANGES=j;var k=f.isArray,l=f.isFunction,m=f.toString,n="[object Object]";e.HandlebarsEnvironment=c,c.prototype={constructor:c,logger:o,log:p,registerHelper:function(a,b){if(m.call(a)===n){if(b)throw new g("Arg not supported with multiple helpers");f.extend(this.helpers,a)}else this.helpers[a]=b},unregisterHelper:function(a){delete this.helpers[a]},registerPartial:function(a,b){m.call(a)===n?f.extend(this.partials,a):this.partials[a]=b},unregisterPartial:function(a){delete this.partials[a]}};var o={methodMap:{0:"debug",1:"info",2:"warn",3:"error"},DEBUG:0,INFO:1,WARN:2,ERROR:3,level:3,log:function(a,b){if(o.level<=a){var c=o.methodMap[a];"undefined"!=typeof console&&console[c]&&console[c].call(console,b)}}};e.logger=o;var p=o.log;e.log=p;var q=function(a){var b=f.extend({},a);return b._parent=a,b};return e.createFrame=q,e}(b,c),e=function(a,b,c){"use strict";function d(a){var b=a&&a[0]||1,c=m;if(b!==c){if(c>b){var d=n[c],e=n[b];throw new l("Template was precompiled with an older version of Handlebars than the current runtime. Please update your precompiler to a newer version ("+d+") or downgrade your runtime to an older version ("+e+").")}throw new l("Template was precompiled with a newer version of Handlebars than the current runtime. Please update your runtime to a newer version ("+a[1]+").")}}function e(a,b){if(!b)throw new l("No environment passed to template");if(!a||!a.main)throw new l("Unknown template object: "+typeof a);b.VM.checkRevision(a.compiler);var c=function(c,d,e,f,g,h,i,j,m){g&&(f=k.extend({},f,g));var n=b.VM.invokePartial.call(this,c,e,f,h,i,j,m);if(null==n&&b.compile){var o={helpers:h,partials:i,data:j,depths:m};i[e]=b.compile(c,{data:void 0!==j,compat:a.compat},b),n=i[e](f,o)}if(null!=n){if(d){for(var p=n.split("\n"),q=0,r=p.length;r>q&&(p[q]||q+1!==r);q++)p[q]=d+p[q];n=p.join("\n")}return n}throw new l("The partial "+e+" could not be compiled when running in runtime-only mode")},d={lookup:function(a,b){for(var c=a.length,d=0;c>d;d++)if(a[d]&&null!=a[d][b])return a[d][b]},lambda:function(a,b){return"function"==typeof a?a.call(b):a},escapeExpression:k.escapeExpression,invokePartial:c,fn:function(b){return a[b]},programs:[],program:function(a,b,c){var d=this.programs[a],e=this.fn(a);return b||c?d=f(this,a,e,b,c):d||(d=this.programs[a]=f(this,a,e)),d},data:function(a,b){for(;a&&b--;)a=a._parent;return a},merge:function(a,b){var c=a||b;return a&&b&&a!==b&&(c=k.extend({},b,a)),c},noop:b.VM.noop,compilerInfo:a.compiler},e=function(b,c){c=c||{};var f=c.data;e._setup(c),!c.partial&&a.useData&&(f=i(b,f));var g;return a.useDepths&&(g=c.depths?[b].concat(c.depths):[b]),a.main.call(d,b,d.helpers,d.partials,f,g)};return e.isTop=!0,e._setup=function(c){c.partial?(d.helpers=c.helpers,d.partials=c.partials):(d.helpers=d.merge(c.helpers,b.helpers),a.usePartial&&(d.partials=d.merge(c.partials,b.partials)))},e._child=function(b,c,e){if(a.useDepths&&!e)throw new l("must pass parent depths");return f(d,b,a[b],c,e)},e}function f(a,b,c,d,e){var f=function(b,f){return f=f||{},c.call(a,b,a.helpers,a.partials,f.data||d,e&&[b].concat(e))};return f.program=b,f.depth=e?e.length:0,f}function g(a,b,c,d,e,f,g){var h={partial:!0,helpers:d,partials:e,data:f,depths:g};if(void 0===a)throw new l("The partial "+b+" could not be found");return a instanceof Function?a(c,h):void 0}function h(){return""}function i(a,b){return b&&"root"in b||(b=b?o(b):{},b.root=a),b}var j={},k=a,l=b,m=c.COMPILER_REVISION,n=c.REVISION_CHANGES,o=c.createFrame;return j.checkRevision=d,j.template=e,j.program=f,j.invokePartial=g,j.noop=h,j}(b,c,d),f=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c,j=d,k=e,l=function(){var a=new g.HandlebarsEnvironment;return j.extend(a,g),a.SafeString=h,a.Exception=i,a.Utils=j,a.escapeExpression=j.escapeExpression,a.VM=k,a.template=function(b){return k.template(b,a)},a},m=l();return m.create=l,m["default"]=m,f=m}(d,a,c,b,e),g=function(a){"use strict";function b(a){a=a||{},this.firstLine=a.first_line,this.firstColumn=a.first_column,this.lastColumn=a.last_column,this.lastLine=a.last_line}var c,d=a,e={ProgramNode:function(a,c,d){b.call(this,d),this.type="program",this.statements=a,this.strip=c},MustacheNode:function(a,c,d,f,g){if(b.call(this,g),this.type="mustache",this.strip=f,null!=d&&d.charAt){var h=d.charAt(3)||d.charAt(2);this.escaped="{"!==h&&"&"!==h}else this.escaped=!!d;a instanceof e.SexprNode?this.sexpr=a:this.sexpr=new e.SexprNode(a,c),this.id=this.sexpr.id,this.params=this.sexpr.params,this.hash=this.sexpr.hash,this.eligibleHelper=this.sexpr.eligibleHelper,this.isHelper=this.sexpr.isHelper},SexprNode:function(a,c,d){b.call(this,d),this.type="sexpr",this.hash=c;var e=this.id=a[0],f=this.params=a.slice(1);this.isHelper=!(!f.length&&!c),this.eligibleHelper=this.isHelper||e.isSimple},PartialNode:function(a,c,d,e,f){b.call(this,f),this.type="partial",this.partialName=a,this.context=c,this.hash=d,this.strip=e,this.strip.inlineStandalone=!0},BlockNode:function(a,c,d,e,f){b.call(this,f),this.type="block",this.mustache=a,this.program=c,this.inverse=d,this.strip=e,d&&!c&&(this.isInverse=!0)},RawBlockNode:function(a,c,f,g){if(b.call(this,g),a.sexpr.id.original!==f)throw new d(a.sexpr.id.original+" doesn't match "+f,this);c=new e.ContentNode(c,g),this.type="block",this.mustache=a,this.program=new e.ProgramNode([c],{},g)},ContentNode:function(a,c){b.call(this,c),this.type="content",this.original=this.string=a},HashNode:function(a,c){b.call(this,c),this.type="hash",this.pairs=a},IdNode:function(a,c){b.call(this,c),this.type="ID";for(var e="",f=[],g=0,h="",i=0,j=a.length;j>i;i++){var k=a[i].part;if(e+=(a[i].separator||"")+k,".."===k||"."===k||"this"===k){if(f.length>0)throw new d("Invalid path: "+e,this);".."===k?(g++,h+="../"):this.isScoped=!0}else f.push(k)}this.original=e,this.parts=f,this.string=f.join("."),this.depth=g,this.idName=h+this.string,this.isSimple=1===a.length&&!this.isScoped&&0===g,this.stringModeValue=this.string},PartialNameNode:function(a,c){b.call(this,c),this.type="PARTIAL_NAME",this.name=a.original},DataNode:function(a,c){b.call(this,c),this.type="DATA",this.id=a,this.stringModeValue=a.stringModeValue,this.idName="@"+a.stringModeValue},StringNode:function(a,c){b.call(this,c),this.type="STRING",this.original=this.string=this.stringModeValue=a},NumberNode:function(a,c){b.call(this,c),this.type="NUMBER",this.original=this.number=a,this.stringModeValue=Number(a)},BooleanNode:function(a,c){b.call(this,c),this.type="BOOLEAN",this.bool=a,this.stringModeValue="true"===a},CommentNode:function(a,c){b.call(this,c),this.type="comment",this.comment=a,this.strip={inlineStandalone:!0}}};return c=e}(c),h=function(){"use strict";var a,b=function(){function a(){this.yy={}}var b={trace:function(){},yy:{},symbols_:{error:2,root:3,program:4,EOF:5,program_repetition0:6,statement:7,mustache:8,block:9,rawBlock:10,partial:11,CONTENT:12,COMMENT:13,openRawBlock:14,END_RAW_BLOCK:15,OPEN_RAW_BLOCK:16,sexpr:17,CLOSE_RAW_BLOCK:18,openBlock:19,block_option0:20,closeBlock:21,openInverse:22,block_option1:23,OPEN_BLOCK:24,CLOSE:25,OPEN_INVERSE:26,inverseAndProgram:27,INVERSE:28,OPEN_ENDBLOCK:29,path:30,OPEN:31,OPEN_UNESCAPED:32,CLOSE_UNESCAPED:33,OPEN_PARTIAL:34,partialName:35,param:36,partial_option0:37,partial_option1:38,sexpr_repetition0:39,sexpr_option0:40,dataName:41,STRING:42,NUMBER:43,BOOLEAN:44,OPEN_SEXPR:45,CLOSE_SEXPR:46,hash:47,hash_repetition_plus0:48,hashSegment:49,ID:50,EQUALS:51,DATA:52,pathSegments:53,SEP:54,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",12:"CONTENT",13:"COMMENT",15:"END_RAW_BLOCK",16:"OPEN_RAW_BLOCK",18:"CLOSE_RAW_BLOCK",24:"OPEN_BLOCK",25:"CLOSE",26:"OPEN_INVERSE",28:"INVERSE",29:"OPEN_ENDBLOCK",31:"OPEN",32:"OPEN_UNESCAPED",33:"CLOSE_UNESCAPED",34:"OPEN_PARTIAL",42:"STRING",43:"NUMBER",44:"BOOLEAN",45:"OPEN_SEXPR",46:"CLOSE_SEXPR",50:"ID",51:"EQUALS",52:"DATA",54:"SEP"},productions_:[0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[10,3],[14,3],[9,4],[9,4],[19,3],[22,3],[27,2],[21,3],[8,3],[8,3],[11,5],[11,4],[17,3],[17,1],[36,1],[36,1],[36,1],[36,1],[36,1],[36,3],[47,1],[49,3],[35,1],[35,1],[35,1],[41,2],[30,1],[53,3],[53,1],[6,0],[6,2],[20,0],[20,1],[23,0],[23,1],[37,0],[37,1],[38,0],[38,1],[39,0],[39,2],[40,0],[40,1],[48,1],[48,2]],performAction:function(a,b,c,d,e,f,g){var h=f.length-1;switch(e){case 1:return d.prepareProgram(f[h-1].statements,!0),f[h-1];case 2:this.$=new d.ProgramNode(d.prepareProgram(f[h]),{},this._$);break;case 3:this.$=f[h];break;case 4:this.$=f[h];break;case 5:this.$=f[h];break;case 6:this.$=f[h];break;case 7:this.$=new d.ContentNode(f[h],this._$);break;case 8:this.$=new d.CommentNode(f[h],this._$);break;case 9:this.$=new d.RawBlockNode(f[h-2],f[h-1],f[h],this._$);break;case 10:this.$=new d.MustacheNode(f[h-1],null,"","",this._$);break;case 11:this.$=d.prepareBlock(f[h-3],f[h-2],f[h-1],f[h],!1,this._$);break;case 12:this.$=d.prepareBlock(f[h-3],f[h-2],f[h-1],f[h],!0,this._$);break;case 13:this.$=new d.MustacheNode(f[h-1],null,f[h-2],d.stripFlags(f[h-2],f[h]),this._$);break;case 14:this.$=new d.MustacheNode(f[h-1],null,f[h-2],d.stripFlags(f[h-2],f[h]),this._$);break;case 15:this.$={strip:d.stripFlags(f[h-1],f[h-1]),program:f[h]};break;case 16:this.$={path:f[h-1],strip:d.stripFlags(f[h-2],f[h])};break;case 17:this.$=new d.MustacheNode(f[h-1],null,f[h-2],d.stripFlags(f[h-2],f[h]),this._$);break;case 18:this.$=new d.MustacheNode(f[h-1],null,f[h-2],d.stripFlags(f[h-2],f[h]),this._$);break;case 19:this.$=new d.PartialNode(f[h-3],f[h-2],f[h-1],d.stripFlags(f[h-4],f[h]),this._$);break;case 20:this.$=new d.PartialNode(f[h-2],void 0,f[h-1],d.stripFlags(f[h-3],f[h]),this._$);break;case 21:this.$=new d.SexprNode([f[h-2]].concat(f[h-1]),f[h],this._$);break;case 22:this.$=new d.SexprNode([f[h]],null,this._$);break;case 23:this.$=f[h];break;case 24:this.$=new d.StringNode(f[h],this._$);break;case 25:this.$=new d.NumberNode(f[h],this._$);break;case 26:this.$=new d.BooleanNode(f[h],this._$);break;case 27:this.$=f[h];break;case 28:f[h-1].isHelper=!0,this.$=f[h-1];break;case 29:this.$=new d.HashNode(f[h],this._$);break;case 30:this.$=[f[h-2],f[h]];break;case 31:this.$=new d.PartialNameNode(f[h],this._$);break;case 32:this.$=new d.PartialNameNode(new d.StringNode(f[h],this._$),this._$);break;case 33:this.$=new d.PartialNameNode(new d.NumberNode(f[h],this._$));break;case 34:this.$=new d.DataNode(f[h],this._$);break;case 35:this.$=new d.IdNode(f[h],this._$);break;case 36:f[h-2].push({part:f[h],separator:f[h-1]}),this.$=f[h-2];break;case 37:this.$=[{part:f[h]}];break;case 38:this.$=[];break;case 39:f[h-1].push(f[h]);break;case 48:this.$=[];break;case 49:f[h-1].push(f[h]);break;case 52:this.$=[f[h]];break;case 53:f[h-1].push(f[h])}},table:[{3:1,4:2,5:[2,38],6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],31:[2,38],32:[2,38],34:[2,38]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:[1,10],13:[1,11],14:16,16:[1,20],19:14,22:15,24:[1,18],26:[1,19],28:[2,2],29:[2,2],31:[1,12],32:[1,13],34:[1,17]},{1:[2,1]},{5:[2,39],12:[2,39],13:[2,39],16:[2,39],24:[2,39],26:[2,39],28:[2,39],29:[2,39],31:[2,39],32:[2,39],34:[2,39]},{5:[2,3],12:[2,3],13:[2,3],16:[2,3],24:[2,3],26:[2,3],28:[2,3],29:[2,3],31:[2,3],32:[2,3],34:[2,3]},{5:[2,4],12:[2,4],13:[2,4],16:[2,4],24:[2,4],26:[2,4],28:[2,4],29:[2,4],31:[2,4],32:[2,4],34:[2,4]},{5:[2,5],12:[2,5],13:[2,5],16:[2,5],24:[2,5],26:[2,5],28:[2,5],29:[2,5],31:[2,5],32:[2,5],34:[2,5]},{5:[2,6],12:[2,6],13:[2,6],16:[2,6],24:[2,6],26:[2,6],28:[2,6],29:[2,6],31:[2,6],32:[2,6],34:[2,6]},{5:[2,7],12:[2,7],13:[2,7],16:[2,7],24:[2,7],26:[2,7],28:[2,7],29:[2,7],31:[2,7],32:[2,7],34:[2,7]},{5:[2,8],12:[2,8],13:[2,8],16:[2,8],24:[2,8],26:[2,8],28:[2,8],29:[2,8],31:[2,8],32:[2,8],34:[2,8]},{17:21,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:27,30:22,41:23,50:[1,26],52:[1,25],53:24},{4:28,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{4:29,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{12:[1,30]},{30:32,35:31,42:[1,33],43:[1,34],50:[1,26],53:24},{17:35,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:36,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:37,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[1,38]},{18:[2,48],25:[2,48],33:[2,48],39:39,42:[2,48],43:[2,48],44:[2,48],45:[2,48],46:[2,48],50:[2,48],52:[2,48]},{18:[2,22],25:[2,22],33:[2,22],46:[2,22]},{18:[2,35],25:[2,35],33:[2,35],42:[2,35],43:[2,35],44:[2,35],45:[2,35],46:[2,35],50:[2,35],52:[2,35],54:[1,40]},{30:41,50:[1,26],53:24},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],52:[2,37],54:[2,37]},{33:[1,42]},{20:43,27:44,28:[1,45],29:[2,40]},{23:46,27:47,28:[1,45],29:[2,42]},{15:[1,48]},{25:[2,46],30:51,36:49,38:50,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],47:57,48:58,49:60,50:[1,59],52:[1,25],53:24},{25:[2,31],42:[2,31],43:[2,31],44:[2,31],45:[2,31],50:[2,31],52:[2,31]},{25:[2,32],42:[2,32],43:[2,32],44:[2,32],45:[2,32],50:[2,32],52:[2,32]},{25:[2,33],42:[2,33],43:[2,33],44:[2,33],45:[2,33],50:[2,33],52:[2,33]},{25:[1,61]},{25:[1,62]},{18:[1,63]},{5:[2,17],12:[2,17],13:[2,17],16:[2,17],24:[2,17],26:[2,17],28:[2,17],29:[2,17],31:[2,17],32:[2,17],34:[2,17]},{18:[2,50],25:[2,50],30:51,33:[2,50],36:65,40:64,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],46:[2,50],47:66,48:58,49:60,50:[1,59],52:[1,25],53:24},{50:[1,67]},{18:[2,34],25:[2,34],33:[2,34],42:[2,34],43:[2,34],44:[2,34],45:[2,34],46:[2,34],50:[2,34],52:[2,34]},{5:[2,18],12:[2,18],13:[2,18],16:[2,18],24:[2,18],26:[2,18],28:[2,18],29:[2,18],31:[2,18],32:[2,18],34:[2,18]},{21:68,29:[1,69]},{29:[2,41]},{4:70,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{21:71,29:[1,69]},{29:[2,43]},{5:[2,9],12:[2,9],13:[2,9],16:[2,9],24:[2,9],26:[2,9],28:[2,9],29:[2,9],31:[2,9],32:[2,9],34:[2,9]},{25:[2,44],37:72,47:73,48:58,49:60,50:[1,74]},{25:[1,75]},{18:[2,23],25:[2,23],33:[2,23],42:[2,23],43:[2,23],44:[2,23],45:[2,23],46:[2,23],50:[2,23],52:[2,23]},{18:[2,24],25:[2,24],33:[2,24],42:[2,24],43:[2,24],44:[2,24],45:[2,24],46:[2,24],50:[2,24],52:[2,24]},{18:[2,25],25:[2,25],33:[2,25],42:[2,25],43:[2,25],44:[2,25],45:[2,25],46:[2,25],50:[2,25],52:[2,25]},{18:[2,26],25:[2,26],33:[2,26],42:[2,26],43:[2,26],44:[2,26],45:[2,26],46:[2,26],50:[2,26],52:[2,26]},{18:[2,27],25:[2,27],33:[2,27],42:[2,27],43:[2,27],44:[2,27],45:[2,27],46:[2,27],50:[2,27],52:[2,27]},{17:76,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[2,47]},{18:[2,29],25:[2,29],33:[2,29],46:[2,29],49:77,50:[1,74]},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],51:[1,78],52:[2,37],54:[2,37]},{18:[2,52],25:[2,52],33:[2,52],46:[2,52],50:[2,52]},{12:[2,13],13:[2,13],16:[2,13],24:[2,13],26:[2,13],28:[2,13],29:[2,13],31:[2,13],32:[2,13],34:[2,13]},{12:[2,14],13:[2,14],16:[2,14],24:[2,14],26:[2,14],28:[2,14],29:[2,14],31:[2,14],32:[2,14],34:[2,14]},{12:[2,10]},{18:[2,21],25:[2,21],33:[2,21],46:[2,21]},{18:[2,49],25:[2,49],33:[2,49],42:[2,49],43:[2,49],44:[2,49],45:[2,49],46:[2,49],50:[2,49],52:[2,49]},{18:[2,51],25:[2,51],33:[2,51],46:[2,51]},{18:[2,36],25:[2,36],33:[2,36],42:[2,36],43:[2,36],44:[2,36],45:[2,36],46:[2,36],50:[2,36],52:[2,36],54:[2,36]},{5:[2,11],12:[2,11],13:[2,11],16:[2,11],24:[2,11],26:[2,11],28:[2,11],29:[2,11],31:[2,11],32:[2,11],34:[2,11]},{30:79,50:[1,26],53:24},{29:[2,15]},{5:[2,12],12:[2,12],13:[2,12],16:[2,12],24:[2,12],26:[2,12],28:[2,12],29:[2,12],31:[2,12],32:[2,12],34:[2,12]},{25:[1,80]},{25:[2,45]},{51:[1,78]},{5:[2,20],12:[2,20],13:[2,20],16:[2,20],24:[2,20],26:[2,20],28:[2,20],29:[2,20],31:[2,20],32:[2,20],34:[2,20]},{46:[1,81]},{18:[2,53],25:[2,53],33:[2,53],46:[2,53],50:[2,53]},{30:51,36:82,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],50:[1,26],52:[1,25],53:24},{25:[1,83]},{5:[2,19],12:[2,19],13:[2,19],16:[2,19],24:[2,19],26:[2,19],28:[2,19],29:[2,19],31:[2,19],32:[2,19],34:[2,19]},{18:[2,28],25:[2,28],33:[2,28],42:[2,28],43:[2,28],44:[2,28],45:[2,28],46:[2,28],50:[2,28],52:[2,28]},{18:[2,30],25:[2,30],33:[2,30],46:[2,30],50:[2,30]},{5:[2,16],12:[2,16],13:[2,16],16:[2,16],24:[2,16],26:[2,16],28:[2,16],29:[2,16],31:[2,16],32:[2,16],34:[2,16]}],defaultActions:{4:[2,1],44:[2,41],47:[2,43],57:[2,47],63:[2,10],70:[2,15],73:[2,45]},parseError:function(a,b){throw new Error(a)},parse:function(a){function b(){var a;return a=c.lexer.lex()||1,"number"!=typeof a&&(a=c.symbols_[a]||a),a}var c=this,d=[0],e=[null],f=[],g=this.table,h="",i=0,j=0,k=0;this.lexer.setInput(a),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,this.yy.parser=this,"undefined"==typeof this.lexer.yylloc&&(this.lexer.yylloc={});var l=this.lexer.yylloc;f.push(l);var m=this.lexer.options&&this.lexer.options.ranges;"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var n,o,p,q,r,s,t,u,v,w={};;){if(p=d[d.length-1],this.defaultActions[p]?q=this.defaultActions[p]:((null===n||"undefined"==typeof n)&&(n=b()),q=g[p]&&g[p][n]),"undefined"==typeof q||!q.length||!q[0]){var x="";if(!k){v=[];for(s in g[p])this.terminals_[s]&&s>2&&v.push("'"+this.terminals_[s]+"'");x=this.lexer.showPosition?"Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+v.join(", ")+", got '"+(this.terminals_[n]||n)+"'":"Parse error on line "+(i+1)+": Unexpected "+(1==n?"end of input":"'"+(this.terminals_[n]||n)+"'"),this.parseError(x,{text:this.lexer.match,token:this.terminals_[n]||n,line:this.lexer.yylineno,loc:l,expected:v})}}if(q[0]instanceof Array&&q.length>1)throw new Error("Parse Error: multiple actions possible at state: "+p+", token: "+n);switch(q[0]){case 1:d.push(n),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(q[1]),n=null,o?(n=o,o=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,l=this.lexer.yylloc,k>0&&k--);break;case 2:if(t=this.productions_[q[1]][1],w.$=e[e.length-t],w._$={first_line:f[f.length-(t||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(t||1)].first_column,last_column:f[f.length-1].last_column},m&&(w._$.range=[f[f.length-(t||1)].range[0],f[f.length-1].range[1]]),r=this.performAction.call(w,h,j,i,this.yy,q[1],e,f),"undefined"!=typeof r)return r;t&&(d=d.slice(0,-1*t*2),e=e.slice(0,-1*t),f=f.slice(0,-1*t)),d.push(this.productions_[q[1]][0]),e.push(w.$),f.push(w._$),u=g[d[d.length-2]][d[d.length-1]],d.push(u);break;case 3:return!0}}return!0}},c=function(){var a={EOF:1,parseError:function(a,b){if(!this.yy.parser)throw new Error(a);this.yy.parser.parseError(a,b)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.offset++,this.match+=a,this.matched+=a;var b=a.match(/(?:\r\n?|\n).*/g);return b?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),a},unput:function(a){var b=a.length,c=a.split(/(?:\r\n?|\n)/g);this._input=a+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-b-1),this.offset-=b;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var e=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===d.length?this.yylloc.first_column:0)+d[d.length-c.length].length-c[0].length:this.yylloc.first_column-b},this.options.ranges&&(this.yylloc.range=[e[0],e[0]+this.yyleng-b]),this},more:function(){return this._more=!0,this},less:function(a){this.unput(this.match.slice(a))},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=new Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e;this._more||(this.yytext="",this.match="");for(var f=this._currentRules(),g=0;gb[0].length)||(b=c,d=g,this.options.flex));g++);return b?(e=b[0].match(/(?:\r\n?|\n).*/g),e&&(this.yylineno+=e.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:e?e[e.length-1].length-e[e.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,f[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),a?a:void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return"undefined"!=typeof a?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(a){this.begin(a)}};return a.options={},a.performAction=function(a,b,c,d){function e(a,c){return b.yytext=b.yytext.substr(a,b.yyleng-c)}switch(c){case 0:if("\\\\"===b.yytext.slice(-2)?(e(0,1),this.begin("mu")):"\\"===b.yytext.slice(-1)?(e(0,1),this.begin("emu")):this.begin("mu"),b.yytext)return 12;break;case 1:return 12;case 2:return this.popState(),12;case 3:return b.yytext=b.yytext.substr(5,b.yyleng-9),this.popState(),15;case 4:return 12;case 5:return e(0,4),this.popState(),13;case 6:return 45;case 7:return 46;case 8:return 16;case 9:return this.popState(),this.begin("raw"),18;case 10:return 34;case 11:return 24;case 12:return 29;case 13:return this.popState(),28;case 14:return this.popState(),28;case 15:return 26;case 16:return 26;case 17:return 32;case 18:return 31;case 19:this.popState(),this.begin("com");break;case 20:return e(3,5),this.popState(),13;case 21:return 31;case 22:return 51;case 23:return 50;case 24:return 50;case 25:return 54;case 26:break;case 27:return this.popState(),33;case 28:return this.popState(),25;case 29:return b.yytext=e(1,2).replace(/\\"/g,'"'),42;case 30:return b.yytext=e(1,2).replace(/\\'/g,"'"),42;case 31:return 52;case 32:return 44;case 33:return 44;case 34:return 43;case 35:return 50;case 36:return b.yytext=e(1,2),50;case 37:return"INVALID";case 38:return 5}},a.rules=[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/],a.conditions={mu:{rules:[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[5],inclusive:!1},raw:{rules:[3,4],inclusive:!1},INITIAL:{rules:[0,1,38],inclusive:!0}},a}();return b.lexer=c,a.prototype=b,b.Parser=a,new a}();return a=b}(),i=function(a){"use strict";function b(a,b){return{left:"~"===a.charAt(2),right:"~"===b.charAt(b.length-3)}}function c(a,b,c,d,i,k){if(a.sexpr.id.original!==d.path.original)throw new j(a.sexpr.id.original+" doesn't match "+d.path.original,a);var l=c&&c.program,m={left:a.strip.left,right:d.strip.right,openStandalone:f(b.statements),closeStandalone:e((l||b).statements)};if(a.strip.right&&g(b.statements,null,!0),l){var n=c.strip;n.left&&h(b.statements,null,!0),n.right&&g(l.statements,null,!0),d.strip.left&&h(l.statements,null,!0),e(b.statements)&&f(l.statements)&&(h(b.statements),g(l.statements))}else d.strip.left&&h(b.statements,null,!0);return i?new this.BlockNode(a,l,b,m,k):new this.BlockNode(a,b,l,m,k)}function d(a,b){for(var c=0,d=a.length;d>c;c++){var i=a[c],j=i.strip;if(j){var k=e(a,c,b,"partial"===i.type),l=f(a,c,b),m=j.openStandalone&&k,n=j.closeStandalone&&l,o=j.inlineStandalone&&k&&l;j.right&&g(a,c,!0),j.left&&h(a,c,!0),o&&(g(a,c),h(a,c)&&"partial"===i.type&&(i.indent=/([ \t]+$)/.exec(a[c-1].original)?RegExp.$1:"")),m&&(g((i.program||i.inverse).statements),h(a,c)),n&&(g(a,c),h((i.inverse||i.program).statements))}}return a}function e(a,b,c){void 0===b&&(b=a.length);var d=a[b-1],e=a[b-2];return d?"content"===d.type?(e||!c?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(d.original):void 0:c}function f(a,b,c){void 0===b&&(b=-1);var d=a[b+1],e=a[b+2];return d?"content"===d.type?(e||!c?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(d.original):void 0:c}function g(a,b,c){var d=a[null==b?0:b+1];if(d&&"content"===d.type&&(c||!d.rightStripped)){var e=d.string;d.string=d.string.replace(c?/^\s+/:/^[ \t]*\r?\n?/,""),d.rightStripped=d.string!==e}}function h(a,b,c){var d=a[null==b?a.length-1:b-1];if(d&&"content"===d.type&&(c||!d.leftStripped)){var e=d.string;return d.string=d.string.replace(c?/\s+$/:/[ \t]+$/,""),d.leftStripped=d.string!==e,d.leftStripped}}var i={},j=a;return i.stripFlags=b,i.prepareBlock=c,i.prepareProgram=d,i}(c),j=function(a,b,c,d){"use strict";function e(a){return a.constructor===h.ProgramNode?a:(g.yy=k,g.parse(a))}var f={},g=a,h=b,i=c,j=d.extend;f.parser=g;var k={};return j(k,i,h),f.parse=e,f}(h,g,i,b),k=function(a,b){"use strict";function c(){}function d(a,b,c){if(null==a||"string"!=typeof a&&a.constructor!==c.AST.ProgramNode)throw new h("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var d=c.parse(a),e=(new c.Compiler).compile(d,b);return(new c.JavaScriptCompiler).compile(e,b)}function e(a,b,c){function d(){var d=c.parse(a),e=(new c.Compiler).compile(d,b),f=(new c.JavaScriptCompiler).compile(e,b,void 0,!0);return c.template(f)}if(null==a||"string"!=typeof a&&a.constructor!==c.AST.ProgramNode)throw new h("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var e,f=function(a,b){return e||(e=d()),e.call(this,a,b)};return f._setup=function(a){return e||(e=d()),e._setup(a)},f._child=function(a,b,c){return e||(e=d()),e._child(a,b,c)},f}function f(a,b){if(a===b)return!0;if(i(a)&&i(b)&&a.length===b.length){for(var c=0;cc;c++){var d=this.opcodes[c],e=a.opcodes[c];if(d.opcode!==e.opcode||!f(d.args,e.args))return!1}for(b=this.children.length,c=0;b>c;c++)if(!this.children[c].equals(a.children[c]))return!1;return!0},guid:0,compile:function(a,b){this.opcodes=[],this.children=[],this.depths={list:[]},this.options=b,this.stringParams=b.stringParams,this.trackIds=b.trackIds;var c=this.options.knownHelpers;if(this.options.knownHelpers={helperMissing:!0,blockHelperMissing:!0,each:!0,"if":!0,unless:!0,"with":!0,log:!0,lookup:!0},c)for(var d in c)this.options.knownHelpers[d]=c[d];return this.accept(a)},accept:function(a){return this[a.type](a)},program:function(a){for(var b=a.statements,c=0,d=b.length;d>c;c++)this.accept(b[c]);return this.isSimple=1===d,this.depths.list=this.depths.list.sort(function(a,b){return a-b}),
-this},compileProgram:function(a){var b,c=(new this.compiler).compile(a,this.options),d=this.guid++;this.usePartial=this.usePartial||c.usePartial,this.children[d]=c;for(var e=0,f=c.depths.list.length;f>e;e++)b=c.depths.list[e],2>b||this.addDepth(b-1);return d},block:function(a){var b=a.mustache,c=a.program,d=a.inverse;c&&(c=this.compileProgram(c)),d&&(d=this.compileProgram(d));var e=b.sexpr,f=this.classifySexpr(e);"helper"===f?this.helperSexpr(e,c,d):"simple"===f?(this.simpleSexpr(e),this.opcode("pushProgram",c),this.opcode("pushProgram",d),this.opcode("emptyHash"),this.opcode("blockValue",e.id.original)):(this.ambiguousSexpr(e,c,d),this.opcode("pushProgram",c),this.opcode("pushProgram",d),this.opcode("emptyHash"),this.opcode("ambiguousBlockValue")),this.opcode("append")},hash:function(a){var b,c,d=a.pairs;for(this.opcode("pushHash"),b=0,c=d.length;c>b;b++)this.pushParam(d[b][1]);for(;b--;)this.opcode("assignToHash",d[b][0]);this.opcode("popHash")},partial:function(a){var b=a.partialName;this.usePartial=!0,a.hash?this.accept(a.hash):this.opcode("push","undefined"),a.context?this.accept(a.context):(this.opcode("getContext",0),this.opcode("pushContext")),this.opcode("invokePartial",b.name,a.indent||""),this.opcode("append")},content:function(a){a.string&&this.opcode("appendContent",a.string)},mustache:function(a){this.sexpr(a.sexpr),a.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},ambiguousSexpr:function(a,b,c){var d=a.id,e=d.parts[0],f=null!=b||null!=c;this.opcode("getContext",d.depth),this.opcode("pushProgram",b),this.opcode("pushProgram",c),this.ID(d),this.opcode("invokeAmbiguous",e,f)},simpleSexpr:function(a){var b=a.id;"DATA"===b.type?this.DATA(b):b.parts.length?this.ID(b):(this.addDepth(b.depth),this.opcode("getContext",b.depth),this.opcode("pushContext")),this.opcode("resolvePossibleLambda")},helperSexpr:function(a,b,c){var d=this.setupFullMustacheParams(a,b,c),e=a.id,f=e.parts[0];if(this.options.knownHelpers[f])this.opcode("invokeKnownHelper",d.length,f);else{if(this.options.knownHelpersOnly)throw new h("You specified knownHelpersOnly, but used the unknown helper "+f,a);e.falsy=!0,this.ID(e),this.opcode("invokeHelper",d.length,e.original,e.isSimple)}},sexpr:function(a){var b=this.classifySexpr(a);"simple"===b?this.simpleSexpr(a):"helper"===b?this.helperSexpr(a):this.ambiguousSexpr(a)},ID:function(a){this.addDepth(a.depth),this.opcode("getContext",a.depth);var b=a.parts[0];b?this.opcode("lookupOnContext",a.parts,a.falsy,a.isScoped):this.opcode("pushContext")},DATA:function(a){this.options.data=!0,this.opcode("lookupData",a.id.depth,a.id.parts)},STRING:function(a){this.opcode("pushString",a.string)},NUMBER:function(a){this.opcode("pushLiteral",a.number)},BOOLEAN:function(a){this.opcode("pushLiteral",a.bool)},comment:function(){},opcode:function(a){this.opcodes.push({opcode:a,args:j.call(arguments,1)})},addDepth:function(a){0!==a&&(this.depths[a]||(this.depths[a]=!0,this.depths.list.push(a)))},classifySexpr:function(a){var b=a.isHelper,c=a.eligibleHelper,d=this.options;if(c&&!b){var e=a.id.parts[0];d.knownHelpers[e]?b=!0:d.knownHelpersOnly&&(c=!1)}return b?"helper":c?"ambiguous":"simple"},pushParams:function(a){for(var b=0,c=a.length;c>b;b++)this.pushParam(a[b])},pushParam:function(a){this.stringParams?(a.depth&&this.addDepth(a.depth),this.opcode("getContext",a.depth||0),this.opcode("pushStringParam",a.stringModeValue,a.type),"sexpr"===a.type&&this.sexpr(a)):(this.trackIds&&this.opcode("pushId",a.type,a.idName||a.stringModeValue),this.accept(a))},setupFullMustacheParams:function(a,b,c){var d=a.params;return this.pushParams(d),this.opcode("pushProgram",b),this.opcode("pushProgram",c),a.hash?this.hash(a.hash):this.opcode("emptyHash"),d}},g.precompile=d,g.compile=e,g}(c,b),l=function(a,b){"use strict";function c(a){this.value=a}function d(){}var e,f=a.COMPILER_REVISION,g=a.REVISION_CHANGES,h=b;d.prototype={nameLookup:function(a,b){return d.isValidJavaScriptVariableName(b)?a+"."+b:a+"['"+b+"']"},depthedLookup:function(a){return this.aliases.lookup="this.lookup",'lookup(depths, "'+a+'")'},compilerInfo:function(){var a=f,b=g[a];return[a,b]},appendToBuffer:function(a){return this.environment.isSimple?"return "+a+";":{appendToBuffer:!0,content:a,toString:function(){return"buffer += "+a+";"}}},initializeBuffer:function(){return this.quotedString("")},namespace:"Handlebars",compile:function(a,b,c,d){this.environment=a,this.options=b,this.stringParams=this.options.stringParams,this.trackIds=this.options.trackIds,this.precompile=!d,this.name=this.environment.name,this.isChild=!!c,this.context=c||{programs:[],environments:[]},this.preamble(),this.stackSlot=0,this.stackVars=[],this.aliases={},this.registers={list:[]},this.hashes=[],this.compileStack=[],this.inlineStack=[],this.compileChildren(a,b),this.useDepths=this.useDepths||a.depths.list.length||this.options.compat;var e,f,g,i=a.opcodes;for(f=0,g=i.length;g>f;f++)e=i[f],this[e.opcode].apply(this,e.args);if(this.pushSource(""),this.stackSlot||this.inlineStack.length||this.compileStack.length)throw new h("Compile completed with content left on stack");var j=this.createFunctionContext(d);if(this.isChild)return j;var k={compiler:this.compilerInfo(),main:j},l=this.context.programs;for(f=0,g=l.length;g>f;f++)l[f]&&(k[f]=l[f]);return this.environment.usePartial&&(k.usePartial=!0),this.options.data&&(k.useData=!0),this.useDepths&&(k.useDepths=!0),this.options.compat&&(k.compat=!0),d||(k.compiler=JSON.stringify(k.compiler),k=this.objectLiteral(k)),k},preamble:function(){this.lastContext=0,this.source=[]},createFunctionContext:function(a){var b="",c=this.stackVars.concat(this.registers.list);c.length>0&&(b+=", "+c.join(", "));for(var d in this.aliases)this.aliases.hasOwnProperty(d)&&(b+=", "+d+"="+this.aliases[d]);var e=["depth0","helpers","partials","data"];this.useDepths&&e.push("depths");var f=this.mergeSource(b);return a?(e.push(f),Function.apply(this,e)):"function("+e.join(",")+") {\n "+f+"}"},mergeSource:function(a){for(var b,c,d="",e=!this.forceBuffer,f=0,g=this.source.length;g>f;f++){var h=this.source[f];h.appendToBuffer?b=b?b+"\n + "+h.content:h.content:(b&&(d?d+="buffer += "+b+";\n ":(c=!0,d=b+";\n "),b=void 0),d+=h+"\n ",this.environment.isSimple||(e=!1))}return e?(b||!d)&&(d+="return "+(b||'""')+";\n"):(a+=", buffer = "+(c?"":this.initializeBuffer()),d+=b?"return buffer + "+b+";\n":"return buffer;\n"),a&&(d="var "+a.substring(2)+(c?"":";\n ")+d),d},blockValue:function(a){this.aliases.blockHelperMissing="helpers.blockHelperMissing";var b=[this.contextName(0)];this.setupParams(a,0,b);var c=this.popStack();b.splice(1,0,c),this.push("blockHelperMissing.call("+b.join(", ")+")")},ambiguousBlockValue:function(){this.aliases.blockHelperMissing="helpers.blockHelperMissing";var a=[this.contextName(0)];this.setupParams("",0,a,!0),this.flushInline();var b=this.topStack();a.splice(1,0,b),this.pushSource("if (!"+this.lastHelper+") { "+b+" = blockHelperMissing.call("+a.join(", ")+"); }")},appendContent:function(a){this.pendingContent&&(a=this.pendingContent+a),this.pendingContent=a},append:function(){this.flushInline();var a=this.popStack();this.pushSource("if ("+a+" != null) { "+this.appendToBuffer(a)+" }"),this.environment.isSimple&&this.pushSource("else { "+this.appendToBuffer("''")+" }")},appendEscaped:function(){this.aliases.escapeExpression="this.escapeExpression",this.pushSource(this.appendToBuffer("escapeExpression("+this.popStack()+")"))},getContext:function(a){this.lastContext=a},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(a,b,c){var d=0,e=a.length;for(c||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(a[d++]));e>d;d++)this.replaceStack(function(c){var e=this.nameLookup(c,a[d],"context");return b?" && "+e:" != null ? "+e+" : "+c})},lookupData:function(a,b){a?this.pushStackLiteral("this.data(data, "+a+")"):this.pushStackLiteral("data");for(var c=b.length,d=0;c>d;d++)this.replaceStack(function(a){return" && "+this.nameLookup(a,b[d],"data")})},resolvePossibleLambda:function(){this.aliases.lambda="this.lambda",this.push("lambda("+this.popStack()+", "+this.contextName(0)+")")},pushStringParam:function(a,b){this.pushContext(),this.pushString(b),"sexpr"!==b&&("string"==typeof a?this.pushString(a):this.pushStackLiteral(a))},emptyHash:function(){this.pushStackLiteral("{}"),this.trackIds&&this.push("{}"),this.stringParams&&(this.push("{}"),this.push("{}"))},pushHash:function(){this.hash&&this.hashes.push(this.hash),this.hash={values:[],types:[],contexts:[],ids:[]}},popHash:function(){var a=this.hash;this.hash=this.hashes.pop(),this.trackIds&&this.push("{"+a.ids.join(",")+"}"),this.stringParams&&(this.push("{"+a.contexts.join(",")+"}"),this.push("{"+a.types.join(",")+"}")),this.push("{\n "+a.values.join(",\n ")+"\n }")},pushString:function(a){this.pushStackLiteral(this.quotedString(a))},push:function(a){return this.inlineStack.push(a),a},pushLiteral:function(a){this.pushStackLiteral(a)},pushProgram:function(a){null!=a?this.pushStackLiteral(this.programExpression(a)):this.pushStackLiteral(null)},invokeHelper:function(a,b,c){this.aliases.helperMissing="helpers.helperMissing";var d=this.popStack(),e=this.setupHelper(a,b),f=(c?e.name+" || ":"")+d+" || helperMissing";this.push("(("+f+").call("+e.callParams+"))")},invokeKnownHelper:function(a,b){var c=this.setupHelper(a,b);this.push(c.name+".call("+c.callParams+")")},invokeAmbiguous:function(a,b){this.aliases.functionType='"function"',this.aliases.helperMissing="helpers.helperMissing",this.useRegister("helper");var c=this.popStack();this.emptyHash();var d=this.setupHelper(0,a,b),e=this.lastHelper=this.nameLookup("helpers",a,"helper");this.push("((helper = (helper = "+e+" || "+c+") != null ? helper : helperMissing"+(d.paramsInit?"),("+d.paramsInit:"")+"),(typeof helper === functionType ? helper.call("+d.callParams+") : helper))")},invokePartial:function(a,b){var c=[this.nameLookup("partials",a,"partial"),"'"+b+"'","'"+a+"'",this.popStack(),this.popStack(),"helpers","partials"];this.options.data?c.push("data"):this.options.compat&&c.push("undefined"),this.options.compat&&c.push("depths"),this.push("this.invokePartial("+c.join(", ")+")")},assignToHash:function(a){var b,c,d,e=this.popStack();this.trackIds&&(d=this.popStack()),this.stringParams&&(c=this.popStack(),b=this.popStack());var f=this.hash;b&&f.contexts.push("'"+a+"': "+b),c&&f.types.push("'"+a+"': "+c),d&&f.ids.push("'"+a+"': "+d),f.values.push("'"+a+"': ("+e+")")},pushId:function(a,b){"ID"===a||"DATA"===a?this.pushString(b):"sexpr"===a?this.pushStackLiteral("true"):this.pushStackLiteral("null")},compiler:d,compileChildren:function(a,b){for(var c,d,e=a.children,f=0,g=e.length;g>f;f++){c=e[f],d=new this.compiler;var h=this.matchExistingProgram(c);null==h?(this.context.programs.push(""),h=this.context.programs.length,c.index=h,c.name="program"+h,this.context.programs[h]=d.compile(c,b,this.context,!this.precompile),this.context.environments[h]=c,this.useDepths=this.useDepths||d.useDepths):(c.index=h,c.name="program"+h)}},matchExistingProgram:function(a){for(var b=0,c=this.context.environments.length;c>b;b++){var d=this.context.environments[b];if(d&&d.equals(a))return b}},programExpression:function(a){var b=this.environment.children[a],c=(b.depths.list,this.useDepths),d=[b.index,"data"];return c&&d.push("depths"),"this.program("+d.join(", ")+")"},useRegister:function(a){this.registers[a]||(this.registers[a]=!0,this.registers.list.push(a))},pushStackLiteral:function(a){return this.push(new c(a))},pushSource:function(a){this.pendingContent&&(this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent))),this.pendingContent=void 0),a&&this.source.push(a)},pushStack:function(a){this.flushInline();var b=this.incrStack();return this.pushSource(b+" = "+a+";"),this.compileStack.push(b),b},replaceStack:function(a){var b,d,e,f="";this.isInline();if(!this.isInline())throw new h("replaceStack on non-inline");var g=this.popStack(!0);if(g instanceof c)f=b=g.value,e=!0;else{d=!this.stackSlot;var i=d?this.incrStack():this.topStackName();f="("+this.push(i)+" = "+g+")",b=this.topStack()}var j=a.call(this,b);e||this.popStack(),d&&this.stackSlot--,this.push("("+f+j+")")},incrStack:function(){return this.stackSlot++,this.stackSlot>this.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var a=this.inlineStack;if(a.length){this.inlineStack=[];for(var b=0,d=a.length;d>b;b++){var e=a[b];e instanceof c?this.compileStack.push(e):this.pushStack(e)}}},isInline:function(){return this.inlineStack.length},popStack:function(a){var b=this.isInline(),d=(b?this.inlineStack:this.compileStack).pop();if(!a&&d instanceof c)return d.value;if(!b){if(!this.stackSlot)throw new h("Invalid stack pop");this.stackSlot--}return d},topStack:function(){var a=this.isInline()?this.inlineStack:this.compileStack,b=a[a.length-1];return b instanceof c?b.value:b},contextName:function(a){return this.useDepths&&a?"depths["+a+"]":"depth"+a},quotedString:function(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")+'"'},objectLiteral:function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(this.quotedString(c)+":"+a[c]);return"{"+b.join(",")+"}"},setupHelper:function(a,b,c){var d=[],e=this.setupParams(b,a,d,c),f=this.nameLookup("helpers",b,"helper");return{params:d,paramsInit:e,name:f,callParams:[this.contextName(0)].concat(d).join(", ")}},setupOptions:function(a,b,c){var d,e,f,g={},h=[],i=[],j=[];g.name=this.quotedString(a),g.hash=this.popStack(),this.trackIds&&(g.hashIds=this.popStack()),this.stringParams&&(g.hashTypes=this.popStack(),g.hashContexts=this.popStack()),e=this.popStack(),f=this.popStack(),(f||e)&&(f||(f="this.noop"),e||(e="this.noop"),g.fn=f,g.inverse=e);for(var k=b;k--;)d=this.popStack(),c[k]=d,this.trackIds&&(j[k]=this.popStack()),this.stringParams&&(i[k]=this.popStack(),h[k]=this.popStack());return this.trackIds&&(g.ids="["+j.join(",")+"]"),this.stringParams&&(g.types="["+i.join(",")+"]",g.contexts="["+h.join(",")+"]"),this.options.data&&(g.data="data"),g},setupParams:function(a,b,c,d){var e=this.objectLiteral(this.setupOptions(a,b,c));return d?(this.useRegister("options"),c.push("options"),"options="+e):(c.push(e),"")}};for(var i="break else new var case finally return void catch for switch while continue function this with default if throw delete in try do instanceof typeof abstract enum int short boolean export interface static byte extends long super char final native synchronized class float package throws const goto private transient debugger implements protected volatile double import public let yield".split(" "),j=d.RESERVED_WORDS={},k=0,l=i.length;l>k;k++)j[i[k]]=!0;return d.isValidJavaScriptVariableName=function(a){return!d.RESERVED_WORDS[a]&&/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(a)},e=d}(d,c),m=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c.parser,j=c.parse,k=d.Compiler,l=d.compile,m=d.precompile,n=e,o=g.create,p=function(){var a=o();return a.compile=function(b,c){return l(b,c,a)},a.precompile=function(b,c){return m(b,c,a)},a.AST=h,a.Compiler=k,a.JavaScriptCompiler=n,a.Parser=i,a.parse=j,a};return g=p(),g.create=p,g["default"]=g,f=g}(f,g,j,k,l);return m});
\ No newline at end of file
+!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):"object"==typeof exports?exports.Handlebars=e():t.Handlebars=e()}(this,function(){return function(t){function e(i){if(s[i])return s[i].exports;var r=s[i]={exports:{},id:i,loaded:!1};return t[i].call(r.exports,r,r.exports,e),r.loaded=!0,r.exports}var s={};return e.m=t,e.c=s,e.p="",e(0)}([function(t,e,s){"use strict";function i(){var t=v();return t.compile=function(e,s){return l.compile(e,s,t)},t.precompile=function(e,s){return l.precompile(e,s,t)},t.AST=h["default"],t.Compiler=l.Compiler,t.JavaScriptCompiler=u["default"],t.Parser=c.parser,t.parse=c.parse,t}var r=s(8)["default"];e.__esModule=!0;var n=s(1),a=r(n),o=s(2),h=r(o),c=s(3),l=s(4),p=s(5),u=r(p),f=s(6),d=r(f),m=s(7),g=r(m),v=a["default"].create,y=i();y.create=i,g["default"](y),y.Visitor=d["default"],y["default"]=y,e["default"]=y,t.exports=e["default"]},function(t,e,s){"use strict";function i(){var t=new o.HandlebarsEnvironment;return f.extend(t,o),t.SafeString=c["default"],t.Exception=p["default"],t.Utils=f,t.escapeExpression=f.escapeExpression,t.VM=m,t.template=function(e){return m.template(e,t)},t}var r=s(9)["default"],n=s(8)["default"];e.__esModule=!0;var a=s(10),o=r(a),h=s(11),c=n(h),l=s(12),p=n(l),u=s(13),f=r(u),d=s(14),m=r(d),g=s(7),v=n(g),y=i();y.create=i,v["default"](y),y["default"]=y,e["default"]=y,t.exports=e["default"]},function(t,e,s){"use strict";e.__esModule=!0;var i={Program:function(t,e,s,i){this.loc=i,this.type="Program",this.body=t,this.blockParams=e,this.strip=s},MustacheStatement:function(t,e,s,i,r,n){this.loc=n,this.type="MustacheStatement",this.path=t,this.params=e||[],this.hash=s,this.escaped=i,this.strip=r},BlockStatement:function(t,e,s,i,r,n,a,o,h){this.loc=h,this.type="BlockStatement",this.path=t,this.params=e||[],this.hash=s,this.program=i,this.inverse=r,this.openStrip=n,this.inverseStrip=a,this.closeStrip=o},PartialStatement:function(t,e,s,i,r){this.loc=r,this.type="PartialStatement",this.name=t,this.params=e||[],this.hash=s,this.indent="",this.strip=i},ContentStatement:function(t,e){this.loc=e,this.type="ContentStatement",this.original=this.value=t},CommentStatement:function(t,e,s){this.loc=s,this.type="CommentStatement",this.value=t,this.strip=e},SubExpression:function(t,e,s,i){this.loc=i,this.type="SubExpression",this.path=t,this.params=e||[],this.hash=s},PathExpression:function(t,e,s,i,r){this.loc=r,this.type="PathExpression",this.data=t,this.original=i,this.parts=s,this.depth=e},StringLiteral:function(t,e){this.loc=e,this.type="StringLiteral",this.original=this.value=t},NumberLiteral:function(t,e){this.loc=e,this.type="NumberLiteral",this.original=this.value=Number(t)},BooleanLiteral:function(t,e){this.loc=e,this.type="BooleanLiteral",this.original=this.value="true"===t},UndefinedLiteral:function(t){this.loc=t,this.type="UndefinedLiteral",this.original=this.value=void 0},NullLiteral:function(t){this.loc=t,this.type="NullLiteral",this.original=this.value=null},Hash:function(t,e){this.loc=e,this.type="Hash",this.pairs=t},HashPair:function(t,e,s){this.loc=s,this.type="HashPair",this.key=t,this.value=e},helpers:{helperExpression:function(t){return!("SubExpression"!==t.type&&!t.params.length&&!t.hash)},scopedId:function(t){return/^\.|this\b/.test(t.original)},simpleId:function(t){return 1===t.parts.length&&!i.helpers.scopedId(t)&&!t.depth}}};e["default"]=i,t.exports=e["default"]},function(t,e,s){"use strict";function i(t,e){if("Program"===t.type)return t;o["default"].yy=m,m.locInfo=function(t){return new m.SourceLocation(e&&e.srcName,t)};var s=new p["default"];return s.accept(o["default"].parse(t))}var r=s(8)["default"],n=s(9)["default"];e.__esModule=!0,e.parse=i;var a=s(15),o=r(a),h=s(2),c=r(h),l=s(16),p=r(l),u=s(17),f=n(u),d=s(13);e.parser=o["default"];var m={};d.extend(m,f,c["default"])},function(t,e,s){"use strict";function i(){}function r(t,e,s){if(null==t||"string"!=typeof t&&"Program"!==t.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+t);e=e||{},"data"in e||(e.data=!0),e.compat&&(e.useDepths=!0);var i=s.parse(t,e),r=(new s.Compiler).compile(i,e);return(new s.JavaScriptCompiler).compile(r,e)}function n(t,e,s){function i(){var e=s.parse(t,n),i=(new s.Compiler).compile(e,n),r=(new s.JavaScriptCompiler).compile(i,n,void 0,!0);return s.template(r)}function r(t,e){return a||(a=i()),a.call(this,t,e)}var n=void 0===arguments[1]?{}:arguments[1];if(null==t||"string"!=typeof t&&"Program"!==t.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+t);"data"in n||(n.data=!0),n.compat&&(n.useDepths=!0);var a=void 0;return r._setup=function(t){return a||(a=i()),a._setup(t)},r._child=function(t,e,s,r){return a||(a=i()),a._child(t,e,s,r)},r}function a(t,e){if(t===e)return!0;if(p.isArray(t)&&p.isArray(e)&&t.length===e.length){for(var s=0;ss;s++){var i=this.opcodes[s],r=t.opcodes[s];if(i.opcode!==r.opcode||!a(i.args,r.args))return!1}e=this.children.length;for(var s=0;e>s;s++)if(!this.children[s].equals(t.children[s]))return!1;return!0},guid:0,compile:function(t,e){this.sourceNode=[],this.opcodes=[],this.children=[],this.options=e,this.stringParams=e.stringParams,this.trackIds=e.trackIds,e.blockParams=e.blockParams||[];var s=e.knownHelpers;if(e.knownHelpers={helperMissing:!0,blockHelperMissing:!0,each:!0,"if":!0,unless:!0,"with":!0,log:!0,lookup:!0},s)for(var i in s)i in s&&(e.knownHelpers[i]=s[i]);return this.accept(t)},compileProgram:function(t){var e=new this.compiler,s=e.compile(t,this.options),i=this.guid++;return this.usePartial=this.usePartial||s.usePartial,this.children[i]=s,this.useDepths=this.useDepths||s.useDepths,i},accept:function(t){this.sourceNode.unshift(t);var e=this[t.type](t);return this.sourceNode.shift(),e},Program:function(t){this.options.blockParams.unshift(t.blockParams);for(var e=t.body,s=e.length,i=0;s>i;i++)this.accept(e[i]);return this.options.blockParams.shift(),this.isSimple=1===s,this.blockParams=t.blockParams?t.blockParams.length:0,this},BlockStatement:function(t){o(t);var e=t.program,s=t.inverse;e=e&&this.compileProgram(e),s=s&&this.compileProgram(s);var i=this.classifySexpr(t);"helper"===i?this.helperSexpr(t,e,s):"simple"===i?(this.simpleSexpr(t),this.opcode("pushProgram",e),this.opcode("pushProgram",s),this.opcode("emptyHash"),this.opcode("blockValue",t.path.original)):(this.ambiguousSexpr(t,e,s),this.opcode("pushProgram",e),this.opcode("pushProgram",s),this.opcode("emptyHash"),this.opcode("ambiguousBlockValue")),this.opcode("append")},PartialStatement:function(t){this.usePartial=!0;var e=t.params;if(e.length>1)throw new l["default"]("Unsupported number of partial arguments: "+e.length,t);e.length||e.push({type:"PathExpression",parts:[],depth:0});var s=t.name.original,i="SubExpression"===t.name.type;i&&this.accept(t.name),this.setupFullMustacheParams(t,void 0,void 0,!0);var r=t.indent||"";this.options.preventIndent&&r&&(this.opcode("appendContent",r),r=""),this.opcode("invokePartial",i,s,r),this.opcode("append")},MustacheStatement:function(t){this.SubExpression(t),this.opcode(t.escaped&&!this.options.noEscape?"appendEscaped":"append")},ContentStatement:function(t){t.value&&this.opcode("appendContent",t.value)},CommentStatement:function(){},SubExpression:function(t){o(t);var e=this.classifySexpr(t);"simple"===e?this.simpleSexpr(t):"helper"===e?this.helperSexpr(t):this.ambiguousSexpr(t)},ambiguousSexpr:function(t,e,s){var i=t.path,r=i.parts[0],n=null!=e||null!=s;this.opcode("getContext",i.depth),this.opcode("pushProgram",e),this.opcode("pushProgram",s),this.accept(i),this.opcode("invokeAmbiguous",r,n)},simpleSexpr:function(t){this.accept(t.path),this.opcode("resolvePossibleLambda")},helperSexpr:function(t,e,s){var i=this.setupFullMustacheParams(t,e,s),r=t.path,n=r.parts[0];if(this.options.knownHelpers[n])this.opcode("invokeKnownHelper",i.length,n);else{if(this.options.knownHelpersOnly)throw new l["default"]("You specified knownHelpersOnly, but used the unknown helper "+n,t);r.falsy=!0,this.accept(r),this.opcode("invokeHelper",i.length,r.original,f["default"].helpers.simpleId(r))}},PathExpression:function(t){this.addDepth(t.depth),this.opcode("getContext",t.depth);var e=t.parts[0],s=f["default"].helpers.scopedId(t),i=!t.depth&&!s&&this.blockParamIndex(e);i?this.opcode("lookupBlockParam",i,t.parts):e?t.data?(this.options.data=!0,this.opcode("lookupData",t.depth,t.parts)):this.opcode("lookupOnContext",t.parts,t.falsy,s):this.opcode("pushContext")},StringLiteral:function(t){this.opcode("pushString",t.value)},NumberLiteral:function(t){this.opcode("pushLiteral",t.value)},BooleanLiteral:function(t){this.opcode("pushLiteral",t.value)},UndefinedLiteral:function(){this.opcode("pushLiteral","undefined")},NullLiteral:function(){this.opcode("pushLiteral","null")},Hash:function(t){var e=t.pairs,s=0,i=e.length;for(this.opcode("pushHash");i>s;s++)this.pushParam(e[s].value);for(;s--;)this.opcode("assignToHash",e[s].key);this.opcode("popHash")},opcode:function(t){this.opcodes.push({opcode:t,args:d.call(arguments,1),loc:this.sourceNode[0].loc})},addDepth:function(t){t&&(this.useDepths=!0)},classifySexpr:function(t){var e=f["default"].helpers.simpleId(t.path),s=e&&!!this.blockParamIndex(t.path.parts[0]),i=!s&&f["default"].helpers.helperExpression(t),r=!s&&(i||e);if(r&&!i){var n=t.path.parts[0],a=this.options;a.knownHelpers[n]?i=!0:a.knownHelpersOnly&&(r=!1)}return i?"helper":r?"ambiguous":"simple"},pushParams:function(t){for(var e=0,s=t.length;s>e;e++)this.pushParam(t[e])},pushParam:function(t){var e=null!=t.value?t.value:t.original||"";if(this.stringParams)e.replace&&(e=e.replace(/^(\.?\.\/)*/g,"").replace(/\//g,".")),t.depth&&this.addDepth(t.depth),this.opcode("getContext",t.depth||0),this.opcode("pushStringParam",e,t.type),"SubExpression"===t.type&&this.accept(t);else{if(this.trackIds){var s=void 0;if(!t.parts||f["default"].helpers.scopedId(t)||t.depth||(s=this.blockParamIndex(t.parts[0])),s){var i=t.parts.slice(1).join(".");this.opcode("pushId","BlockParam",s,i)}else e=t.original||e,e.replace&&(e=e.replace(/^\.\//g,"").replace(/^\.$/g,"")),this.opcode("pushId",t.type,e)}this.accept(t)}},setupFullMustacheParams:function(t,e,s,i){var r=t.params;return this.pushParams(r),this.opcode("pushProgram",e),this.opcode("pushProgram",s),t.hash?this.accept(t.hash):this.opcode("emptyHash",i),r},blockParamIndex:function(t){for(var e=0,s=this.options.blockParams.length;s>e;e++){var i=this.options.blockParams[e],r=i&&p.indexOf(i,t);if(i&&r>=0)return[e,r]}}}},function(t,e,s){"use strict";function i(t){this.value=t}function r(){}function n(t,e,s,i){var r=e.popStack(),n=0,a=s.length;for(t&&a--;a>n;n++)r=e.nameLookup(r,s[n],i);return t?[e.aliasable("this.strict"),"(",r,", ",e.quotedString(s[n]),")"]:r}var a=s(8)["default"];e.__esModule=!0;var o=s(10),h=s(12),c=a(h),l=s(13),p=s(18),u=a(p);r.prototype={nameLookup:function(t,e){return r.isValidJavaScriptVariableName(e)?[t,".",e]:[t,"['",e,"']"]},depthedLookup:function(t){return[this.aliasable("this.lookup"),'(depths, "',t,'")']},compilerInfo:function(){var t=o.COMPILER_REVISION,e=o.REVISION_CHANGES[t];return[t,e]},appendToBuffer:function(t,e,s){return l.isArray(t)||(t=[t]),t=this.source.wrap(t,e),this.environment.isSimple?["return ",t,";"]:s?["buffer += ",t,";"]:(t.appendToBuffer=!0,t)},initializeBuffer:function(){return this.quotedString("")},compile:function(t,e,s,i){this.environment=t,this.options=e,this.stringParams=this.options.stringParams,this.trackIds=this.options.trackIds,this.precompile=!i,this.name=this.environment.name,this.isChild=!!s,this.context=s||{programs:[],environments:[]},this.preamble(),this.stackSlot=0,this.stackVars=[],this.aliases={},this.registers={list:[]},this.hashes=[],this.compileStack=[],this.inlineStack=[],this.blockParams=[],this.compileChildren(t,e),this.useDepths=this.useDepths||t.useDepths||this.options.compat,this.useBlockParams=this.useBlockParams||t.useBlockParams;var r=t.opcodes,n=void 0,a=void 0,o=void 0,h=void 0;for(o=0,h=r.length;h>o;o++)n=r[o],this.source.currentLocation=n.loc,a=a||n.loc,this[n.opcode].apply(this,n.args);if(this.source.currentLocation=a,this.pushSource(""),this.stackSlot||this.inlineStack.length||this.compileStack.length)throw new c["default"]("Compile completed with content left on stack");var l=this.createFunctionContext(i);if(this.isChild)return l;var p={compiler:this.compilerInfo(),main:l},u=this.context.programs;for(o=0,h=u.length;h>o;o++)u[o]&&(p[o]=u[o]);return this.environment.usePartial&&(p.usePartial=!0),this.options.data&&(p.useData=!0),this.useDepths&&(p.useDepths=!0),this.useBlockParams&&(p.useBlockParams=!0),this.options.compat&&(p.compat=!0),i?p.compilerOptions=this.options:(p.compiler=JSON.stringify(p.compiler),this.source.currentLocation={start:{line:1,column:0}},p=this.objectLiteral(p),e.srcName?(p=p.toStringWithSourceMap({file:e.destName}),p.map=p.map&&p.map.toString()):p=p.toString()),p},preamble:function(){this.lastContext=0,this.source=new u["default"](this.options.srcName)},createFunctionContext:function(t){var e="",s=this.stackVars.concat(this.registers.list);s.length>0&&(e+=", "+s.join(", "));var i=0;for(var r in this.aliases){var n=this.aliases[r];this.aliases.hasOwnProperty(r)&&n.children&&n.referenceCount>1&&(e+=", alias"+ ++i+"="+r,n.children[0]="alias"+i)}var a=["depth0","helpers","partials","data"];(this.useBlockParams||this.useDepths)&&a.push("blockParams"),this.useDepths&&a.push("depths");var o=this.mergeSource(e);return t?(a.push(o),Function.apply(this,a)):this.source.wrap(["function(",a.join(","),") {\n ",o,"}"])},mergeSource:function(t){var e=this.environment.isSimple,s=!this.forceBuffer,i=void 0,r=void 0,n=void 0,a=void 0;return this.source.each(function(t){t.appendToBuffer?(n?t.prepend(" + "):n=t,a=t):(n&&(r?n.prepend("buffer += "):i=!0,a.add(";"),n=a=void 0),r=!0,e||(s=!1))}),s?n?(n.prepend("return "),a.add(";")):r||this.source.push('return "";'):(t+=", buffer = "+(i?"":this.initializeBuffer()),n?(n.prepend("return buffer + "),a.add(";")):this.source.push("return buffer;")),t&&this.source.prepend("var "+t.substring(2)+(i?"":";\n")),this.source.merge()},blockValue:function(t){var e=this.aliasable("helpers.blockHelperMissing"),s=[this.contextName(0)];this.setupHelperArgs(t,0,s);var i=this.popStack();s.splice(1,0,i),this.push(this.source.functionCall(e,"call",s))},ambiguousBlockValue:function(){var t=this.aliasable("helpers.blockHelperMissing"),e=[this.contextName(0)];this.setupHelperArgs("",0,e,!0),this.flushInline();var s=this.topStack();e.splice(1,0,s),this.pushSource(["if (!",this.lastHelper,") { ",s," = ",this.source.functionCall(t,"call",e),"}"])},appendContent:function(t){this.pendingContent?t=this.pendingContent+t:this.pendingLocation=this.source.currentLocation,this.pendingContent=t},append:function(){if(this.isInline())this.replaceStack(function(t){return[" != null ? ",t,' : ""']}),this.pushSource(this.appendToBuffer(this.popStack()));else{var t=this.popStack();this.pushSource(["if (",t," != null) { ",this.appendToBuffer(t,void 0,!0)," }"]),this.environment.isSimple&&this.pushSource(["else { ",this.appendToBuffer("''",void 0,!0)," }"])}},appendEscaped:function(){this.pushSource(this.appendToBuffer([this.aliasable("this.escapeExpression"),"(",this.popStack(),")"]))},getContext:function(t){this.lastContext=t},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(t,e,s){var i=0;s||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(t[i++])),this.resolvePath("context",t,i,e)},lookupBlockParam:function(t,e){this.useBlockParams=!0,this.push(["blockParams[",t[0],"][",t[1],"]"]),this.resolvePath("context",e,1)},lookupData:function(t,e){this.pushStackLiteral(t?"this.data(data, "+t+")":"data"),this.resolvePath("data",e,0,!0)},resolvePath:function(t,e,s,i){var r=this;if(this.options.strict||this.options.assumeObjects)return void this.push(n(this.options.strict,this,e,t));for(var a=e.length;a>s;s++)this.replaceStack(function(n){var a=r.nameLookup(n,e[s],t);return i?[" && ",a]:[" != null ? ",a," : ",n]})},resolvePossibleLambda:function(){this.push([this.aliasable("this.lambda"),"(",this.popStack(),", ",this.contextName(0),")"])},pushStringParam:function(t,e){this.pushContext(),this.pushString(e),"SubExpression"!==e&&("string"==typeof t?this.pushString(t):this.pushStackLiteral(t))},emptyHash:function(t){this.trackIds&&this.push("{}"),this.stringParams&&(this.push("{}"),this.push("{}")),this.pushStackLiteral(t?"undefined":"{}")},pushHash:function(){this.hash&&this.hashes.push(this.hash),this.hash={values:[],types:[],contexts:[],ids:[]}},popHash:function(){var t=this.hash;this.hash=this.hashes.pop(),this.trackIds&&this.push(this.objectLiteral(t.ids)),this.stringParams&&(this.push(this.objectLiteral(t.contexts)),this.push(this.objectLiteral(t.types))),this.push(this.objectLiteral(t.values))},pushString:function(t){this.pushStackLiteral(this.quotedString(t))},pushLiteral:function(t){this.pushStackLiteral(t)},pushProgram:function(t){this.pushStackLiteral(null!=t?this.programExpression(t):null)},invokeHelper:function(t,e,s){var i=this.popStack(),r=this.setupHelper(t,e),n=s?[r.name," || "]:"",a=["("].concat(n,i);this.options.strict||a.push(" || ",this.aliasable("helpers.helperMissing")),a.push(")"),this.push(this.source.functionCall(a,"call",r.callParams))},invokeKnownHelper:function(t,e){var s=this.setupHelper(t,e);this.push(this.source.functionCall(s.name,"call",s.callParams))},invokeAmbiguous:function(t,e){this.useRegister("helper");var s=this.popStack();this.emptyHash();var i=this.setupHelper(0,t,e),r=this.lastHelper=this.nameLookup("helpers",t,"helper"),n=["(","(helper = ",r," || ",s,")"];this.options.strict||(n[0]="(helper = ",n.push(" != null ? helper : ",this.aliasable("helpers.helperMissing"))),this.push(["(",n,i.paramsInit?["),(",i.paramsInit]:[],"),","(typeof helper === ",this.aliasable('"function"')," ? ",this.source.functionCall("helper","call",i.callParams)," : helper))"])},invokePartial:function(t,e,s){var i=[],r=this.setupParams(e,1,i,!1);t&&(e=this.popStack(),delete r.name),s&&(r.indent=JSON.stringify(s)),r.helpers="helpers",r.partials="partials",i.unshift(t?e:this.nameLookup("partials",e,"partial")),this.options.compat&&(r.depths="depths"),r=this.objectLiteral(r),i.push(r),this.push(this.source.functionCall("this.invokePartial","",i))},assignToHash:function(t){var e=this.popStack(),s=void 0,i=void 0,r=void 0;this.trackIds&&(r=this.popStack()),this.stringParams&&(i=this.popStack(),s=this.popStack());var n=this.hash;s&&(n.contexts[t]=s),i&&(n.types[t]=i),r&&(n.ids[t]=r),n.values[t]=e},pushId:function(t,e,s){"BlockParam"===t?this.pushStackLiteral("blockParams["+e[0]+"].path["+e[1]+"]"+(s?" + "+JSON.stringify("."+s):"")):"PathExpression"===t?this.pushString(e):this.pushStackLiteral("SubExpression"===t?"true":"null")},compiler:r,compileChildren:function(t,e){for(var s=t.children,i=void 0,r=void 0,n=0,a=s.length;a>n;n++){i=s[n],r=new this.compiler;var o=this.matchExistingProgram(i);null==o?(this.context.programs.push(""),o=this.context.programs.length,i.index=o,i.name="program"+o,this.context.programs[o]=r.compile(i,e,this.context,!this.precompile),this.context.environments[o]=i,this.useDepths=this.useDepths||r.useDepths,this.useBlockParams=this.useBlockParams||r.useBlockParams):(i.index=o,i.name="program"+o,this.useDepths=this.useDepths||i.useDepths,this.useBlockParams=this.useBlockParams||i.useBlockParams)}},matchExistingProgram:function(t){for(var e=0,s=this.context.environments.length;s>e;e++){var i=this.context.environments[e];if(i&&i.equals(t))return e}},programExpression:function(t){var e=this.environment.children[t],s=[e.index,"data",e.blockParams];return(this.useBlockParams||this.useDepths)&&s.push("blockParams"),this.useDepths&&s.push("depths"),"this.program("+s.join(", ")+")"},useRegister:function(t){this.registers[t]||(this.registers[t]=!0,this.registers.list.push(t))},push:function(t){return t instanceof i||(t=this.source.wrap(t)),this.inlineStack.push(t),t},pushStackLiteral:function(t){this.push(new i(t))},pushSource:function(t){this.pendingContent&&(this.source.push(this.appendToBuffer(this.source.quotedString(this.pendingContent),this.pendingLocation)),this.pendingContent=void 0),t&&this.source.push(t)},replaceStack:function(t){var e=["("],s=void 0,r=void 0,n=void 0;if(!this.isInline())throw new c["default"]("replaceStack on non-inline");var a=this.popStack(!0);if(a instanceof i)s=[a.value],e=["(",s],n=!0;else{r=!0;var o=this.incrStack();e=["((",this.push(o)," = ",a,")"],s=this.topStack()}var h=t.call(this,s);n||this.popStack(),r&&this.stackSlot--,this.push(e.concat(h,")"))},incrStack:function(){return this.stackSlot++,this.stackSlot>this.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var t=this.inlineStack;this.inlineStack=[];for(var e=0,s=t.length;s>e;e++){var r=t[e];if(r instanceof i)this.compileStack.push(r);else{var n=this.incrStack();this.pushSource([n," = ",r,";"]),this.compileStack.push(n)}}},isInline:function(){return this.inlineStack.length},popStack:function(t){var e=this.isInline(),s=(e?this.inlineStack:this.compileStack).pop();if(!t&&s instanceof i)return s.value;if(!e){if(!this.stackSlot)throw new c["default"]("Invalid stack pop");this.stackSlot--}return s},topStack:function(){var t=this.isInline()?this.inlineStack:this.compileStack,e=t[t.length-1];return e instanceof i?e.value:e},contextName:function(t){return this.useDepths&&t?"depths["+t+"]":"depth"+t},quotedString:function(t){return this.source.quotedString(t)},objectLiteral:function(t){return this.source.objectLiteral(t)},aliasable:function(t){var e=this.aliases[t];return e?(e.referenceCount++,e):(e=this.aliases[t]=this.source.wrap(t),e.aliasable=!0,e.referenceCount=1,e)},setupHelper:function(t,e,s){var i=[],r=this.setupHelperArgs(e,t,i,s),n=this.nameLookup("helpers",e,"helper");return{params:i,paramsInit:r,name:n,callParams:[this.contextName(0)].concat(i)}},setupParams:function(t,e,s){var i={},r=[],n=[],a=[],o=void 0;i.name=this.quotedString(t),i.hash=this.popStack(),this.trackIds&&(i.hashIds=this.popStack()),this.stringParams&&(i.hashTypes=this.popStack(),i.hashContexts=this.popStack());var h=this.popStack(),c=this.popStack();(c||h)&&(i.fn=c||"this.noop",i.inverse=h||"this.noop");for(var l=e;l--;)o=this.popStack(),s[l]=o,this.trackIds&&(a[l]=this.popStack()),this.stringParams&&(n[l]=this.popStack(),r[l]=this.popStack());return this.trackIds&&(i.ids=this.source.generateArray(a)),this.stringParams&&(i.types=this.source.generateArray(n),i.contexts=this.source.generateArray(r)),this.options.data&&(i.data="data"),this.useBlockParams&&(i.blockParams="blockParams"),i},setupHelperArgs:function(t,e,s,i){var r=this.setupParams(t,e,s,!0);return r=this.objectLiteral(r),i?(this.useRegister("options"),s.push("options"),["options=",r]):(s.push(r),"")}},function(){for(var t="break else new var case finally return void catch for switch while continue function this with default if throw delete in try do instanceof typeof abstract enum int short boolean export interface static byte extends long super char final native synchronized class float package throws const goto private transient debugger implements protected volatile double import public let yield await null true false".split(" "),e=r.RESERVED_WORDS={},s=0,i=t.length;i>s;s++)e[t[s]]=!0}(),r.isValidJavaScriptVariableName=function(t){return!r.RESERVED_WORDS[t]&&/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(t)},e["default"]=r,t.exports=e["default"]},function(t,e,s){"use strict";function i(){this.parents=[]}var r=s(8)["default"];e.__esModule=!0;var n=s(12),a=r(n),o=s(2),h=r(o);i.prototype={constructor:i,mutating:!1,acceptKey:function(t,e){var s=this.accept(t[e]);if(this.mutating){if(s&&(!s.type||!h["default"][s.type]))throw new a["default"]('Unexpected node type "'+s.type+'" found when accepting '+e+" on "+t.type);t[e]=s}},acceptRequired:function(t,e){if(this.acceptKey(t,e),!t[e])throw new a["default"](t.type+" requires "+e)},acceptArray:function(t){for(var e=0,s=t.length;s>e;e++)this.acceptKey(t,e),t[e]||(t.splice(e,1),e--,s--)},accept:function(t){if(t){this.current&&this.parents.unshift(this.current),this.current=t;var e=this[t.type](t);return this.current=this.parents.shift(),!this.mutating||e?e:e!==!1?t:void 0}},Program:function(t){this.acceptArray(t.body)},MustacheStatement:function(t){this.acceptRequired(t,"path"),this.acceptArray(t.params),this.acceptKey(t,"hash")},BlockStatement:function(t){this.acceptRequired(t,"path"),this.acceptArray(t.params),this.acceptKey(t,"hash"),this.acceptKey(t,"program"),this.acceptKey(t,"inverse")},PartialStatement:function(t){this.acceptRequired(t,"name"),this.acceptArray(t.params),this.acceptKey(t,"hash")},ContentStatement:function(){},CommentStatement:function(){},SubExpression:function(t){this.acceptRequired(t,"path"),this.acceptArray(t.params),this.acceptKey(t,"hash")},PathExpression:function(){},StringLiteral:function(){},NumberLiteral:function(){},BooleanLiteral:function(){},UndefinedLiteral:function(){},NullLiteral:function(){},Hash:function(t){this.acceptArray(t.pairs)},HashPair:function(t){this.acceptRequired(t,"value")}},e["default"]=i,t.exports=e["default"]},function(t,e,s){(function(s){"use strict";e.__esModule=!0,e["default"]=function(t){var e="undefined"!=typeof s?s:window,i=e.Handlebars;t.noConflict=function(){e.Handlebars===t&&(e.Handlebars=i)}},t.exports=e["default"]}).call(e,function(){return this}())},function(t,e,s){"use strict";e["default"]=function(t){return t&&t.__esModule?t:{"default":t}},e.__esModule=!0},function(t,e,s){"use strict";e["default"]=function(t){if(t&&t.__esModule)return t;var e={};if("object"==typeof t&&null!==t)for(var s in t)Object.prototype.hasOwnProperty.call(t,s)&&(e[s]=t[s]);return e["default"]=t,e},e.__esModule=!0},function(t,e,s){"use strict";function i(t,e){this.helpers=t||{},this.partials=e||{},r(this)}function r(t){t.registerHelper("helperMissing",function(){if(1===arguments.length)return void 0;throw new p["default"]('Missing helper: "'+arguments[arguments.length-1].name+'"')}),t.registerHelper("blockHelperMissing",function(e,s){var i=s.inverse,r=s.fn;if(e===!0)return r(this);if(e===!1||null==e)return i(this);if(m(e))return e.length>0?(s.ids&&(s.ids=[s.name]),t.helpers.each(e,s)):i(this);if(s.data&&s.ids){var a=n(s.data);a.contextPath=c.appendContextPath(s.data.contextPath,s.name),s={data:a}}return r(e,s)}),t.registerHelper("each",function(t,e){function s(e,s,r){h&&(h.key=e,h.index=s,h.first=0===s,h.last=!!r,l&&(h.contextPath=l+e)),o+=i(t[e],{data:h,blockParams:c.blockParams([t[e],e],[l+e,null])})}if(!e)throw new p["default"]("Must pass iterator to #each");var i=e.fn,r=e.inverse,a=0,o="",h=void 0,l=void 0;if(e.data&&e.ids&&(l=c.appendContextPath(e.data.contextPath,e.ids[0])+"."),g(t)&&(t=t.call(this)),e.data&&(h=n(e.data)),t&&"object"==typeof t)if(m(t))for(var u=t.length;u>a;a++)s(a,a,a===t.length-1);else{var f=void 0;for(var d in t)t.hasOwnProperty(d)&&(f&&s(f,a-1),f=d,a++);f&&s(f,a-1,!0)}return 0===a&&(o=r(this)),o}),t.registerHelper("if",function(t,e){return g(t)&&(t=t.call(this)),!e.hash.includeZero&&!t||c.isEmpty(t)?e.inverse(this):e.fn(this)}),t.registerHelper("unless",function(e,s){return t.helpers["if"].call(this,e,{fn:s.inverse,inverse:s.fn,hash:s.hash})}),t.registerHelper("with",function(t,e){g(t)&&(t=t.call(this));var s=e.fn;if(c.isEmpty(t))return e.inverse(this);if(e.data&&e.ids){var i=n(e.data);i.contextPath=c.appendContextPath(e.data.contextPath,e.ids[0]),e={data:i}}return s(t,e)}),t.registerHelper("log",function(e,s){var i=s.data&&null!=s.data.level?parseInt(s.data.level,10):1;t.log(i,e)}),t.registerHelper("lookup",function(t,e){return t&&t[e]})}function n(t){var e=c.extend({},t);return e._parent=t,e}var a=s(9)["default"],o=s(8)["default"];e.__esModule=!0,e.HandlebarsEnvironment=i,e.createFrame=n;var h=s(13),c=a(h),l=s(12),p=o(l),u="3.0.1";e.VERSION=u;var f=6;e.COMPILER_REVISION=f;var d={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1"};e.REVISION_CHANGES=d;var m=c.isArray,g=c.isFunction,v=c.toString,y="[object Object]";i.prototype={constructor:i,logger:k,log:S,registerHelper:function(t,e){if(v.call(t)===y){if(e)throw new p["default"]("Arg not supported with multiple helpers");c.extend(this.helpers,t)}else this.helpers[t]=e},unregisterHelper:function(t){delete this.helpers[t]},registerPartial:function(t,e){if(v.call(t)===y)c.extend(this.partials,t);else{if("undefined"==typeof e)throw new p["default"]("Attempting to register a partial as undefined");this.partials[t]=e}},unregisterPartial:function(t){delete this.partials[t]}};var k={methodMap:{0:"debug",1:"info",2:"warn",3:"error"},DEBUG:0,INFO:1,WARN:2,ERROR:3,level:1,log:function(t,e){if("undefined"!=typeof console&&k.level<=t){var s=k.methodMap[t];(console[s]||console.log).call(console,e)}}};e.logger=k;var S=k.log;e.log=S},function(t,e,s){"use strict";function i(t){this.string=t}e.__esModule=!0,i.prototype.toString=i.prototype.toHTML=function(){return""+this.string},e["default"]=i,t.exports=e["default"]},function(t,e,s){"use strict";function i(t,e){var s=e&&e.loc,n=void 0,a=void 0;s&&(n=s.start.line,a=s.start.column,t+=" - "+n+":"+a);for(var o=Error.prototype.constructor.call(this,t),h=0;hs;s++)if(t[s]===e)return s;return-1}function a(t){if("string"!=typeof t){if(t&&t.toHTML)return t.toHTML();if(null==t)return"";if(!t)return t+"";t=""+t}return u.test(t)?t.replace(p,i):t}function o(t){return t||0===t?m(t)&&0===t.length?!0:!1:!0}function h(t,e){return t.path=e,t}function c(t,e){return(t?t+".":"")+e}e.__esModule=!0,e.extend=r,e.indexOf=n,e.escapeExpression=a,e.isEmpty=o,e.blockParams=h,e.appendContextPath=c;var l={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},p=/[&<>"'`]/g,u=/[&<>"'`]/,f=Object.prototype.toString;e.toString=f;var d=function(t){return"function"==typeof t};d(/x/)&&(e.isFunction=d=function(t){return"function"==typeof t&&"[object Function]"===f.call(t)});var d;e.isFunction=d;var m=Array.isArray||function(t){return t&&"object"==typeof t?"[object Array]"===f.call(t):!1};e.isArray=m},function(t,e,s){"use strict";function i(t){var e=t&&t[0]||1,s=g.COMPILER_REVISION;if(e!==s){if(s>e){var i=g.REVISION_CHANGES[s],r=g.REVISION_CHANGES[e];throw new m["default"]("Template was precompiled with an older version of Handlebars than the current runtime. Please update your precompiler to a newer version ("+i+") or downgrade your runtime to an older version ("+r+").")}throw new m["default"]("Template was precompiled with a newer version of Handlebars than the current runtime. Please update your runtime to a newer version ("+t[1]+").")}}function r(t,e){function s(s,i,r){r.hash&&(i=f.extend({},i,r.hash)),s=e.VM.resolvePartial.call(this,s,i,r);var n=e.VM.invokePartial.call(this,s,i,r);if(null==n&&e.compile&&(r.partials[r.name]=e.compile(s,t.compilerOptions,e),
+n=r.partials[r.name](i,r)),null!=n){if(r.indent){for(var a=n.split("\n"),o=0,h=a.length;h>o&&(a[o]||o+1!==h);o++)a[o]=r.indent+a[o];n=a.join("\n")}return n}throw new m["default"]("The partial "+r.name+" could not be compiled when running in runtime-only mode")}function i(e){var s=void 0===arguments[1]?{}:arguments[1],n=s.data;i._setup(s),!s.partial&&t.useData&&(n=c(e,n));var a=void 0,o=t.useBlockParams?[]:void 0;return t.useDepths&&(a=s.depths?[e].concat(s.depths):[e]),t.main.call(r,e,r.helpers,r.partials,n,o,a)}if(!e)throw new m["default"]("No environment passed to template");if(!t||!t.main)throw new m["default"]("Unknown template object: "+typeof t);e.VM.checkRevision(t.compiler);var r={strict:function(t,e){if(!(e in t))throw new m["default"]('"'+e+'" not defined in '+t);return t[e]},lookup:function(t,e){for(var s=t.length,i=0;s>i;i++)if(t[i]&&null!=t[i][e])return t[i][e]},lambda:function(t,e){return"function"==typeof t?t.call(e):t},escapeExpression:f.escapeExpression,invokePartial:s,fn:function(e){return t[e]},programs:[],program:function(t,e,s,i,r){var a=this.programs[t],o=this.fn(t);return e||r||i||s?a=n(this,t,o,e,s,i,r):a||(a=this.programs[t]=n(this,t,o)),a},data:function(t,e){for(;t&&e--;)t=t._parent;return t},merge:function(t,e){var s=t||e;return t&&e&&t!==e&&(s=f.extend({},e,t)),s},noop:e.VM.noop,compilerInfo:t.compiler};return i.isTop=!0,i._setup=function(s){s.partial?(r.helpers=s.helpers,r.partials=s.partials):(r.helpers=r.merge(s.helpers,e.helpers),t.usePartial&&(r.partials=r.merge(s.partials,e.partials)))},i._child=function(e,s,i,a){if(t.useBlockParams&&!i)throw new m["default"]("must pass block params");if(t.useDepths&&!a)throw new m["default"]("must pass parent depths");return n(r,e,t[e],s,0,i,a)},i}function n(t,e,s,i,r,n,a){function o(e){var r=void 0===arguments[1]?{}:arguments[1];return s.call(t,e,t.helpers,t.partials,r.data||i,n&&[r.blockParams].concat(n),a&&[e].concat(a))}return o.program=e,o.depth=a?a.length:0,o.blockParams=r||0,o}function a(t,e,s){return t?t.call||s.name||(s.name=t,t=s.partials[t]):t=s.partials[s.name],t}function o(t,e,s){if(s.partial=!0,void 0===t)throw new m["default"]("The partial "+s.name+" could not be found");return t instanceof Function?t(e,s):void 0}function h(){return""}function c(t,e){return e&&"root"in e||(e=e?g.createFrame(e):{},e.root=t),e}var l=s(9)["default"],p=s(8)["default"];e.__esModule=!0,e.checkRevision=i,e.template=r,e.wrapProgram=n,e.resolvePartial=a,e.invokePartial=o,e.noop=h;var u=s(13),f=l(u),d=s(12),m=p(d),g=s(10)},function(t,e,s){"use strict";e.__esModule=!0;var i=function(){function t(){this.yy={}}var e={trace:function(){},yy:{},symbols_:{error:2,root:3,program:4,EOF:5,program_repetition0:6,statement:7,mustache:8,block:9,rawBlock:10,partial:11,content:12,COMMENT:13,CONTENT:14,openRawBlock:15,END_RAW_BLOCK:16,OPEN_RAW_BLOCK:17,helperName:18,openRawBlock_repetition0:19,openRawBlock_option0:20,CLOSE_RAW_BLOCK:21,openBlock:22,block_option0:23,closeBlock:24,openInverse:25,block_option1:26,OPEN_BLOCK:27,openBlock_repetition0:28,openBlock_option0:29,openBlock_option1:30,CLOSE:31,OPEN_INVERSE:32,openInverse_repetition0:33,openInverse_option0:34,openInverse_option1:35,openInverseChain:36,OPEN_INVERSE_CHAIN:37,openInverseChain_repetition0:38,openInverseChain_option0:39,openInverseChain_option1:40,inverseAndProgram:41,INVERSE:42,inverseChain:43,inverseChain_option0:44,OPEN_ENDBLOCK:45,OPEN:46,mustache_repetition0:47,mustache_option0:48,OPEN_UNESCAPED:49,mustache_repetition1:50,mustache_option1:51,CLOSE_UNESCAPED:52,OPEN_PARTIAL:53,partialName:54,partial_repetition0:55,partial_option0:56,param:57,sexpr:58,OPEN_SEXPR:59,sexpr_repetition0:60,sexpr_option0:61,CLOSE_SEXPR:62,hash:63,hash_repetition_plus0:64,hashSegment:65,ID:66,EQUALS:67,blockParams:68,OPEN_BLOCK_PARAMS:69,blockParams_repetition_plus0:70,CLOSE_BLOCK_PARAMS:71,path:72,dataName:73,STRING:74,NUMBER:75,BOOLEAN:76,UNDEFINED:77,NULL:78,DATA:79,pathSegments:80,SEP:81,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",13:"COMMENT",14:"CONTENT",16:"END_RAW_BLOCK",17:"OPEN_RAW_BLOCK",21:"CLOSE_RAW_BLOCK",27:"OPEN_BLOCK",31:"CLOSE",32:"OPEN_INVERSE",37:"OPEN_INVERSE_CHAIN",42:"INVERSE",45:"OPEN_ENDBLOCK",46:"OPEN",49:"OPEN_UNESCAPED",52:"CLOSE_UNESCAPED",53:"OPEN_PARTIAL",59:"OPEN_SEXPR",62:"CLOSE_SEXPR",66:"ID",67:"EQUALS",69:"OPEN_BLOCK_PARAMS",71:"CLOSE_BLOCK_PARAMS",74:"STRING",75:"NUMBER",76:"BOOLEAN",77:"UNDEFINED",78:"NULL",79:"DATA",81:"SEP"},productions_:[0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[12,1],[10,3],[15,5],[9,4],[9,4],[22,6],[25,6],[36,6],[41,2],[43,3],[43,1],[24,3],[8,5],[8,5],[11,5],[57,1],[57,1],[58,5],[63,1],[65,3],[68,3],[18,1],[18,1],[18,1],[18,1],[18,1],[18,1],[18,1],[54,1],[54,1],[73,2],[72,1],[80,3],[80,1],[6,0],[6,2],[19,0],[19,2],[20,0],[20,1],[23,0],[23,1],[26,0],[26,1],[28,0],[28,2],[29,0],[29,1],[30,0],[30,1],[33,0],[33,2],[34,0],[34,1],[35,0],[35,1],[38,0],[38,2],[39,0],[39,1],[40,0],[40,1],[44,0],[44,1],[47,0],[47,2],[48,0],[48,1],[50,0],[50,2],[51,0],[51,1],[55,0],[55,2],[56,0],[56,1],[60,0],[60,2],[61,0],[61,1],[64,1],[64,2],[70,1],[70,2]],performAction:function(t,e,s,i,r,n,a){var o=n.length-1;switch(r){case 1:return n[o-1];case 2:this.$=new i.Program(n[o],null,{},i.locInfo(this._$));break;case 3:this.$=n[o];break;case 4:this.$=n[o];break;case 5:this.$=n[o];break;case 6:this.$=n[o];break;case 7:this.$=n[o];break;case 8:this.$=new i.CommentStatement(i.stripComment(n[o]),i.stripFlags(n[o],n[o]),i.locInfo(this._$));break;case 9:this.$=new i.ContentStatement(n[o],i.locInfo(this._$));break;case 10:this.$=i.prepareRawBlock(n[o-2],n[o-1],n[o],this._$);break;case 11:this.$={path:n[o-3],params:n[o-2],hash:n[o-1]};break;case 12:this.$=i.prepareBlock(n[o-3],n[o-2],n[o-1],n[o],!1,this._$);break;case 13:this.$=i.prepareBlock(n[o-3],n[o-2],n[o-1],n[o],!0,this._$);break;case 14:this.$={path:n[o-4],params:n[o-3],hash:n[o-2],blockParams:n[o-1],strip:i.stripFlags(n[o-5],n[o])};break;case 15:this.$={path:n[o-4],params:n[o-3],hash:n[o-2],blockParams:n[o-1],strip:i.stripFlags(n[o-5],n[o])};break;case 16:this.$={path:n[o-4],params:n[o-3],hash:n[o-2],blockParams:n[o-1],strip:i.stripFlags(n[o-5],n[o])};break;case 17:this.$={strip:i.stripFlags(n[o-1],n[o-1]),program:n[o]};break;case 18:var h=i.prepareBlock(n[o-2],n[o-1],n[o],n[o],!1,this._$),c=new i.Program([h],null,{},i.locInfo(this._$));c.chained=!0,this.$={strip:n[o-2].strip,program:c,chain:!0};break;case 19:this.$=n[o];break;case 20:this.$={path:n[o-1],strip:i.stripFlags(n[o-2],n[o])};break;case 21:this.$=i.prepareMustache(n[o-3],n[o-2],n[o-1],n[o-4],i.stripFlags(n[o-4],n[o]),this._$);break;case 22:this.$=i.prepareMustache(n[o-3],n[o-2],n[o-1],n[o-4],i.stripFlags(n[o-4],n[o]),this._$);break;case 23:this.$=new i.PartialStatement(n[o-3],n[o-2],n[o-1],i.stripFlags(n[o-4],n[o]),i.locInfo(this._$));break;case 24:this.$=n[o];break;case 25:this.$=n[o];break;case 26:this.$=new i.SubExpression(n[o-3],n[o-2],n[o-1],i.locInfo(this._$));break;case 27:this.$=new i.Hash(n[o],i.locInfo(this._$));break;case 28:this.$=new i.HashPair(i.id(n[o-2]),n[o],i.locInfo(this._$));break;case 29:this.$=i.id(n[o-1]);break;case 30:this.$=n[o];break;case 31:this.$=n[o];break;case 32:this.$=new i.StringLiteral(n[o],i.locInfo(this._$));break;case 33:this.$=new i.NumberLiteral(n[o],i.locInfo(this._$));break;case 34:this.$=new i.BooleanLiteral(n[o],i.locInfo(this._$));break;case 35:this.$=new i.UndefinedLiteral(i.locInfo(this._$));break;case 36:this.$=new i.NullLiteral(i.locInfo(this._$));break;case 37:this.$=n[o];break;case 38:this.$=n[o];break;case 39:this.$=i.preparePath(!0,n[o],this._$);break;case 40:this.$=i.preparePath(!1,n[o],this._$);break;case 41:n[o-2].push({part:i.id(n[o]),original:n[o],separator:n[o-1]}),this.$=n[o-2];break;case 42:this.$=[{part:i.id(n[o]),original:n[o]}];break;case 43:this.$=[];break;case 44:n[o-1].push(n[o]);break;case 45:this.$=[];break;case 46:n[o-1].push(n[o]);break;case 53:this.$=[];break;case 54:n[o-1].push(n[o]);break;case 59:this.$=[];break;case 60:n[o-1].push(n[o]);break;case 65:this.$=[];break;case 66:n[o-1].push(n[o]);break;case 73:this.$=[];break;case 74:n[o-1].push(n[o]);break;case 77:this.$=[];break;case 78:n[o-1].push(n[o]);break;case 81:this.$=[];break;case 82:n[o-1].push(n[o]);break;case 85:this.$=[];break;case 86:n[o-1].push(n[o]);break;case 89:this.$=[n[o]];break;case 90:n[o-1].push(n[o]);break;case 91:this.$=[n[o]];break;case 92:n[o-1].push(n[o])}},table:[{3:1,4:2,5:[2,43],6:3,13:[2,43],14:[2,43],17:[2,43],27:[2,43],32:[2,43],46:[2,43],49:[2,43],53:[2,43]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:10,13:[1,11],14:[1,18],15:16,17:[1,21],22:14,25:15,27:[1,19],32:[1,20],37:[2,2],42:[2,2],45:[2,2],46:[1,12],49:[1,13],53:[1,17]},{1:[2,1]},{5:[2,44],13:[2,44],14:[2,44],17:[2,44],27:[2,44],32:[2,44],37:[2,44],42:[2,44],45:[2,44],46:[2,44],49:[2,44],53:[2,44]},{5:[2,3],13:[2,3],14:[2,3],17:[2,3],27:[2,3],32:[2,3],37:[2,3],42:[2,3],45:[2,3],46:[2,3],49:[2,3],53:[2,3]},{5:[2,4],13:[2,4],14:[2,4],17:[2,4],27:[2,4],32:[2,4],37:[2,4],42:[2,4],45:[2,4],46:[2,4],49:[2,4],53:[2,4]},{5:[2,5],13:[2,5],14:[2,5],17:[2,5],27:[2,5],32:[2,5],37:[2,5],42:[2,5],45:[2,5],46:[2,5],49:[2,5],53:[2,5]},{5:[2,6],13:[2,6],14:[2,6],17:[2,6],27:[2,6],32:[2,6],37:[2,6],42:[2,6],45:[2,6],46:[2,6],49:[2,6],53:[2,6]},{5:[2,7],13:[2,7],14:[2,7],17:[2,7],27:[2,7],32:[2,7],37:[2,7],42:[2,7],45:[2,7],46:[2,7],49:[2,7],53:[2,7]},{5:[2,8],13:[2,8],14:[2,8],17:[2,8],27:[2,8],32:[2,8],37:[2,8],42:[2,8],45:[2,8],46:[2,8],49:[2,8],53:[2,8]},{18:22,66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{18:33,66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{4:34,6:3,13:[2,43],14:[2,43],17:[2,43],27:[2,43],32:[2,43],37:[2,43],42:[2,43],45:[2,43],46:[2,43],49:[2,43],53:[2,43]},{4:35,6:3,13:[2,43],14:[2,43],17:[2,43],27:[2,43],32:[2,43],42:[2,43],45:[2,43],46:[2,43],49:[2,43],53:[2,43]},{12:36,14:[1,18]},{18:38,54:37,58:39,59:[1,40],66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{5:[2,9],13:[2,9],14:[2,9],16:[2,9],17:[2,9],27:[2,9],32:[2,9],37:[2,9],42:[2,9],45:[2,9],46:[2,9],49:[2,9],53:[2,9]},{18:41,66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{18:42,66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{18:43,66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{31:[2,73],47:44,59:[2,73],66:[2,73],74:[2,73],75:[2,73],76:[2,73],77:[2,73],78:[2,73],79:[2,73]},{21:[2,30],31:[2,30],52:[2,30],59:[2,30],62:[2,30],66:[2,30],69:[2,30],74:[2,30],75:[2,30],76:[2,30],77:[2,30],78:[2,30],79:[2,30]},{21:[2,31],31:[2,31],52:[2,31],59:[2,31],62:[2,31],66:[2,31],69:[2,31],74:[2,31],75:[2,31],76:[2,31],77:[2,31],78:[2,31],79:[2,31]},{21:[2,32],31:[2,32],52:[2,32],59:[2,32],62:[2,32],66:[2,32],69:[2,32],74:[2,32],75:[2,32],76:[2,32],77:[2,32],78:[2,32],79:[2,32]},{21:[2,33],31:[2,33],52:[2,33],59:[2,33],62:[2,33],66:[2,33],69:[2,33],74:[2,33],75:[2,33],76:[2,33],77:[2,33],78:[2,33],79:[2,33]},{21:[2,34],31:[2,34],52:[2,34],59:[2,34],62:[2,34],66:[2,34],69:[2,34],74:[2,34],75:[2,34],76:[2,34],77:[2,34],78:[2,34],79:[2,34]},{21:[2,35],31:[2,35],52:[2,35],59:[2,35],62:[2,35],66:[2,35],69:[2,35],74:[2,35],75:[2,35],76:[2,35],77:[2,35],78:[2,35],79:[2,35]},{21:[2,36],31:[2,36],52:[2,36],59:[2,36],62:[2,36],66:[2,36],69:[2,36],74:[2,36],75:[2,36],76:[2,36],77:[2,36],78:[2,36],79:[2,36]},{21:[2,40],31:[2,40],52:[2,40],59:[2,40],62:[2,40],66:[2,40],69:[2,40],74:[2,40],75:[2,40],76:[2,40],77:[2,40],78:[2,40],79:[2,40],81:[1,45]},{66:[1,32],80:46},{21:[2,42],31:[2,42],52:[2,42],59:[2,42],62:[2,42],66:[2,42],69:[2,42],74:[2,42],75:[2,42],76:[2,42],77:[2,42],78:[2,42],79:[2,42],81:[2,42]},{50:47,52:[2,77],59:[2,77],66:[2,77],74:[2,77],75:[2,77],76:[2,77],77:[2,77],78:[2,77],79:[2,77]},{23:48,36:50,37:[1,52],41:51,42:[1,53],43:49,45:[2,49]},{26:54,41:55,42:[1,53],45:[2,51]},{16:[1,56]},{31:[2,81],55:57,59:[2,81],66:[2,81],74:[2,81],75:[2,81],76:[2,81],77:[2,81],78:[2,81],79:[2,81]},{31:[2,37],59:[2,37],66:[2,37],74:[2,37],75:[2,37],76:[2,37],77:[2,37],78:[2,37],79:[2,37]},{31:[2,38],59:[2,38],66:[2,38],74:[2,38],75:[2,38],76:[2,38],77:[2,38],78:[2,38],79:[2,38]},{18:58,66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{28:59,31:[2,53],59:[2,53],66:[2,53],69:[2,53],74:[2,53],75:[2,53],76:[2,53],77:[2,53],78:[2,53],79:[2,53]},{31:[2,59],33:60,59:[2,59],66:[2,59],69:[2,59],74:[2,59],75:[2,59],76:[2,59],77:[2,59],78:[2,59],79:[2,59]},{19:61,21:[2,45],59:[2,45],66:[2,45],74:[2,45],75:[2,45],76:[2,45],77:[2,45],78:[2,45],79:[2,45]},{18:65,31:[2,75],48:62,57:63,58:66,59:[1,40],63:64,64:67,65:68,66:[1,69],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{66:[1,70]},{21:[2,39],31:[2,39],52:[2,39],59:[2,39],62:[2,39],66:[2,39],69:[2,39],74:[2,39],75:[2,39],76:[2,39],77:[2,39],78:[2,39],79:[2,39],81:[1,45]},{18:65,51:71,52:[2,79],57:72,58:66,59:[1,40],63:73,64:67,65:68,66:[1,69],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{24:74,45:[1,75]},{45:[2,50]},{4:76,6:3,13:[2,43],14:[2,43],17:[2,43],27:[2,43],32:[2,43],37:[2,43],42:[2,43],45:[2,43],46:[2,43],49:[2,43],53:[2,43]},{45:[2,19]},{18:77,66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{4:78,6:3,13:[2,43],14:[2,43],17:[2,43],27:[2,43],32:[2,43],45:[2,43],46:[2,43],49:[2,43],53:[2,43]},{24:79,45:[1,75]},{45:[2,52]},{5:[2,10],13:[2,10],14:[2,10],17:[2,10],27:[2,10],32:[2,10],37:[2,10],42:[2,10],45:[2,10],46:[2,10],49:[2,10],53:[2,10]},{18:65,31:[2,83],56:80,57:81,58:66,59:[1,40],63:82,64:67,65:68,66:[1,69],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{59:[2,85],60:83,62:[2,85],66:[2,85],74:[2,85],75:[2,85],76:[2,85],77:[2,85],78:[2,85],79:[2,85]},{18:65,29:84,31:[2,55],57:85,58:66,59:[1,40],63:86,64:67,65:68,66:[1,69],69:[2,55],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{18:65,31:[2,61],34:87,57:88,58:66,59:[1,40],63:89,64:67,65:68,66:[1,69],69:[2,61],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{18:65,20:90,21:[2,47],57:91,58:66,59:[1,40],63:92,64:67,65:68,66:[1,69],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{31:[1,93]},{31:[2,74],59:[2,74],66:[2,74],74:[2,74],75:[2,74],76:[2,74],77:[2,74],78:[2,74],79:[2,74]},{31:[2,76]},{21:[2,24],31:[2,24],52:[2,24],59:[2,24],62:[2,24],66:[2,24],69:[2,24],74:[2,24],75:[2,24],76:[2,24],77:[2,24],78:[2,24],79:[2,24]},{21:[2,25],31:[2,25],52:[2,25],59:[2,25],62:[2,25],66:[2,25],69:[2,25],74:[2,25],75:[2,25],76:[2,25],77:[2,25],78:[2,25],79:[2,25]},{21:[2,27],31:[2,27],52:[2,27],62:[2,27],65:94,66:[1,95],69:[2,27]},{21:[2,89],31:[2,89],52:[2,89],62:[2,89],66:[2,89],69:[2,89]},{21:[2,42],31:[2,42],52:[2,42],59:[2,42],62:[2,42],66:[2,42],67:[1,96],69:[2,42],74:[2,42],75:[2,42],76:[2,42],77:[2,42],78:[2,42],79:[2,42],81:[2,42]},{21:[2,41],31:[2,41],52:[2,41],59:[2,41],62:[2,41],66:[2,41],69:[2,41],74:[2,41],75:[2,41],76:[2,41],77:[2,41],78:[2,41],79:[2,41],81:[2,41]},{52:[1,97]},{52:[2,78],59:[2,78],66:[2,78],74:[2,78],75:[2,78],76:[2,78],77:[2,78],78:[2,78],79:[2,78]},{52:[2,80]},{5:[2,12],13:[2,12],14:[2,12],17:[2,12],27:[2,12],32:[2,12],37:[2,12],42:[2,12],45:[2,12],46:[2,12],49:[2,12],53:[2,12]},{18:98,66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{36:50,37:[1,52],41:51,42:[1,53],43:100,44:99,45:[2,71]},{31:[2,65],38:101,59:[2,65],66:[2,65],69:[2,65],74:[2,65],75:[2,65],76:[2,65],77:[2,65],78:[2,65],79:[2,65]},{45:[2,17]},{5:[2,13],13:[2,13],14:[2,13],17:[2,13],27:[2,13],32:[2,13],37:[2,13],42:[2,13],45:[2,13],46:[2,13],49:[2,13],53:[2,13]},{31:[1,102]},{31:[2,82],59:[2,82],66:[2,82],74:[2,82],75:[2,82],76:[2,82],77:[2,82],78:[2,82],79:[2,82]},{31:[2,84]},{18:65,57:104,58:66,59:[1,40],61:103,62:[2,87],63:105,64:67,65:68,66:[1,69],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{30:106,31:[2,57],68:107,69:[1,108]},{31:[2,54],59:[2,54],66:[2,54],69:[2,54],74:[2,54],75:[2,54],76:[2,54],77:[2,54],78:[2,54],79:[2,54]},{31:[2,56],69:[2,56]},{31:[2,63],35:109,68:110,69:[1,108]},{31:[2,60],59:[2,60],66:[2,60],69:[2,60],74:[2,60],75:[2,60],76:[2,60],77:[2,60],78:[2,60],79:[2,60]},{31:[2,62],69:[2,62]},{21:[1,111]},{21:[2,46],59:[2,46],66:[2,46],74:[2,46],75:[2,46],76:[2,46],77:[2,46],78:[2,46],79:[2,46]},{21:[2,48]},{5:[2,21],13:[2,21],14:[2,21],17:[2,21],27:[2,21],32:[2,21],37:[2,21],42:[2,21],45:[2,21],46:[2,21],49:[2,21],53:[2,21]},{21:[2,90],31:[2,90],52:[2,90],62:[2,90],66:[2,90],69:[2,90]},{67:[1,96]},{18:65,57:112,58:66,59:[1,40],66:[1,32],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{5:[2,22],13:[2,22],14:[2,22],17:[2,22],27:[2,22],32:[2,22],37:[2,22],42:[2,22],45:[2,22],46:[2,22],49:[2,22],53:[2,22]},{31:[1,113]},{45:[2,18]},{45:[2,72]},{18:65,31:[2,67],39:114,57:115,58:66,59:[1,40],63:116,64:67,65:68,66:[1,69],69:[2,67],72:23,73:24,74:[1,25],75:[1,26],76:[1,27],77:[1,28],78:[1,29],79:[1,31],80:30},{5:[2,23],13:[2,23],14:[2,23],17:[2,23],27:[2,23],32:[2,23],37:[2,23],42:[2,23],45:[2,23],46:[2,23],49:[2,23],53:[2,23]},{62:[1,117]},{59:[2,86],62:[2,86],66:[2,86],74:[2,86],75:[2,86],76:[2,86],77:[2,86],78:[2,86],79:[2,86]},{62:[2,88]},{31:[1,118]},{31:[2,58]},{66:[1,120],70:119},{31:[1,121]},{31:[2,64]},{14:[2,11]},{21:[2,28],31:[2,28],52:[2,28],62:[2,28],66:[2,28],69:[2,28]},{5:[2,20],13:[2,20],14:[2,20],17:[2,20],27:[2,20],32:[2,20],37:[2,20],42:[2,20],45:[2,20],46:[2,20],49:[2,20],53:[2,20]},{31:[2,69],40:122,68:123,69:[1,108]},{31:[2,66],59:[2,66],66:[2,66],69:[2,66],74:[2,66],75:[2,66],76:[2,66],77:[2,66],78:[2,66],79:[2,66]},{31:[2,68],69:[2,68]},{21:[2,26],31:[2,26],52:[2,26],59:[2,26],62:[2,26],66:[2,26],69:[2,26],74:[2,26],75:[2,26],76:[2,26],77:[2,26],78:[2,26],79:[2,26]},{13:[2,14],14:[2,14],17:[2,14],27:[2,14],32:[2,14],37:[2,14],42:[2,14],45:[2,14],46:[2,14],49:[2,14],53:[2,14]},{66:[1,125],71:[1,124]},{66:[2,91],71:[2,91]},{13:[2,15],14:[2,15],17:[2,15],27:[2,15],32:[2,15],42:[2,15],45:[2,15],46:[2,15],49:[2,15],53:[2,15]},{31:[1,126]},{31:[2,70]},{31:[2,29]},{66:[2,92],71:[2,92]},{13:[2,16],14:[2,16],17:[2,16],27:[2,16],32:[2,16],37:[2,16],42:[2,16],45:[2,16],46:[2,16],49:[2,16],53:[2,16]}],defaultActions:{4:[2,1],49:[2,50],51:[2,19],55:[2,52],64:[2,76],73:[2,80],78:[2,17],82:[2,84],92:[2,48],99:[2,18],100:[2,72],105:[2,88],107:[2,58],110:[2,64],111:[2,11],123:[2,70],124:[2,29]},parseError:function(t,e){throw new Error(t)},parse:function(t){function e(){var t;return t=s.lexer.lex()||1,"number"!=typeof t&&(t=s.symbols_[t]||t),t}var s=this,i=[0],r=[null],n=[],a=this.table,o="",h=0,c=0,l=0;this.lexer.setInput(t),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,this.yy.parser=this,"undefined"==typeof this.lexer.yylloc&&(this.lexer.yylloc={});var p=this.lexer.yylloc;n.push(p);var u=this.lexer.options&&this.lexer.options.ranges;"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var f,d,m,g,v,y,k,S,b,P={};;){if(m=i[i.length-1],this.defaultActions[m]?g=this.defaultActions[m]:((null===f||"undefined"==typeof f)&&(f=e()),g=a[m]&&a[m][f]),"undefined"==typeof g||!g.length||!g[0]){var _="";if(!l){b=[];for(y in a[m])this.terminals_[y]&&y>2&&b.push("'"+this.terminals_[y]+"'");_=this.lexer.showPosition?"Parse error on line "+(h+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+b.join(", ")+", got '"+(this.terminals_[f]||f)+"'":"Parse error on line "+(h+1)+": Unexpected "+(1==f?"end of input":"'"+(this.terminals_[f]||f)+"'"),this.parseError(_,{text:this.lexer.match,token:this.terminals_[f]||f,line:this.lexer.yylineno,loc:p,expected:b})}}if(g[0]instanceof Array&&g.length>1)throw new Error("Parse Error: multiple actions possible at state: "+m+", token: "+f);switch(g[0]){case 1:i.push(f),r.push(this.lexer.yytext),n.push(this.lexer.yylloc),i.push(g[1]),f=null,d?(f=d,d=null):(c=this.lexer.yyleng,o=this.lexer.yytext,h=this.lexer.yylineno,p=this.lexer.yylloc,l>0&&l--);break;case 2:if(k=this.productions_[g[1]][1],P.$=r[r.length-k],P._$={first_line:n[n.length-(k||1)].first_line,last_line:n[n.length-1].last_line,first_column:n[n.length-(k||1)].first_column,last_column:n[n.length-1].last_column},u&&(P._$.range=[n[n.length-(k||1)].range[0],n[n.length-1].range[1]]),v=this.performAction.call(P,o,c,h,this.yy,g[1],r,n),"undefined"!=typeof v)return v;k&&(i=i.slice(0,-1*k*2),r=r.slice(0,-1*k),n=n.slice(0,-1*k)),i.push(this.productions_[g[1]][0]),r.push(P.$),n.push(P._$),S=a[i[i.length-2]][i[i.length-1]],i.push(S);break;case 3:return!0}}return!0}},s=function(){var t={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t){return this._input=t,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t;var e=t.match(/(?:\r\n?|\n).*/g);return e?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,s=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e-1),this.offset-=e;var i=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),s.length-1&&(this.yylineno-=s.length-1);var r=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:s?(s.length===i.length?this.yylloc.first_column:0)+i[i.length-s.length].length-s[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[r[0],r[0]+this.yyleng-e]),this},more:function(){return this._more=!0,this},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var t,e,s,i,r;this._more||(this.yytext="",this.match="");for(var n=this._currentRules(),a=0;ae[0].length)||(e=s,i=a,this.options.flex));a++);return e?(r=e[0].match(/(?:\r\n?|\n).*/g),r&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.matches=e,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],t=this.performAction.call(this,this.yy,this,n[i],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),t?t:void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return"undefined"!=typeof t?t:this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(t){this.begin(t)}};return t.options={},t.performAction=function(t,e,s,i){function r(t,s){return e.yytext=e.yytext.substr(t,e.yyleng-s)}switch(s){case 0:if("\\\\"===e.yytext.slice(-2)?(r(0,1),this.begin("mu")):"\\"===e.yytext.slice(-1)?(r(0,1),this.begin("emu")):this.begin("mu"),e.yytext)return 14;break;case 1:return 14;case 2:return this.popState(),14;case 3:return e.yytext=e.yytext.substr(5,e.yyleng-9),this.popState(),16;case 4:return 14;case 5:return this.popState(),13;case 6:return 59;case 7:return 62;case 8:return 17;case 9:return this.popState(),this.begin("raw"),21;case 10:return 53;case 11:return 27;case 12:return 45;case 13:return this.popState(),42;case 14:return this.popState(),42;case 15:return 32;case 16:return 37;case 17:return 49;case 18:return 46;case 19:this.unput(e.yytext),this.popState(),this.begin("com");break;case 20:return this.popState(),13;case 21:return 46;case 22:return 67;case 23:return 66;case 24:return 66;case 25:return 81;case 26:break;case 27:return this.popState(),52;case 28:return this.popState(),31;case 29:return e.yytext=r(1,2).replace(/\\"/g,'"'),74;case 30:return e.yytext=r(1,2).replace(/\\'/g,"'"),74;case 31:return 79;case 32:return 76;case 33:return 76;case 34:return 77;case 35:return 78;case 36:return 75;case 37:return 69;case 38:return 71;case 39:return 66;case 40:return 66;case 41:return"INVALID";case 42:return 5}},t.rules=[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:undefined(?=([~}\s)])))/,/^(?:null(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/],t.conditions={mu:{rules:[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[5],inclusive:!1},raw:{rules:[3,4],inclusive:!1},INITIAL:{rules:[0,1,42],inclusive:!0}},t}();return e.lexer=s,t.prototype=e,e.Parser=t,new t}();e["default"]=i,t.exports=e["default"]},function(t,e,s){"use strict";function i(){}function r(t,e,s){void 0===e&&(e=t.length);var i=t[e-1],r=t[e-2];return i?"ContentStatement"===i.type?(r||!s?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(i.original):void 0:s}function n(t,e,s){void 0===e&&(e=-1);var i=t[e+1],r=t[e+2];return i?"ContentStatement"===i.type?(r||!s?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(i.original):void 0:s}function a(t,e,s){var i=t[null==e?0:e+1];if(i&&"ContentStatement"===i.type&&(s||!i.rightStripped)){var r=i.value;i.value=i.value.replace(s?/^\s+/:/^[ \t]*\r?\n?/,""),i.rightStripped=i.value!==r}}function o(t,e,s){var i=t[null==e?t.length-1:e-1];if(i&&"ContentStatement"===i.type&&(s||!i.leftStripped)){var r=i.value;return i.value=i.value.replace(s?/\s+$/:/[ \t]+$/,""),i.leftStripped=i.value!==r,i.leftStripped}}var h=s(8)["default"];e.__esModule=!0;var c=s(6),l=h(c);i.prototype=new l["default"],i.prototype.Program=function(t){var e=!this.isRootSeen;this.isRootSeen=!0;for(var s=t.body,i=0,h=s.length;h>i;i++){var c=s[i],l=this.accept(c);if(l){var p=r(s,i,e),u=n(s,i,e),f=l.openStandalone&&p,d=l.closeStandalone&&u,m=l.inlineStandalone&&p&&u;l.close&&a(s,i,!0),l.open&&o(s,i,!0),m&&(a(s,i),o(s,i)&&"PartialStatement"===c.type&&(c.indent=/([ \t]+$)/.exec(s[i-1].original)[1])),f&&(a((c.program||c.inverse).body),o(s,i)),d&&(a(s,i),o((c.inverse||c.program).body))}}return t},i.prototype.BlockStatement=function(t){this.accept(t.program),this.accept(t.inverse);var e=t.program||t.inverse,s=t.program&&t.inverse,i=s,h=s;if(s&&s.chained)for(i=s.body[0].program;h.chained;)h=h.body[h.body.length-1].program;var c={open:t.openStrip.open,close:t.closeStrip.close,openStandalone:n(e.body),closeStandalone:r((i||e).body)};if(t.openStrip.close&&a(e.body,null,!0),s){var l=t.inverseStrip;l.open&&o(e.body,null,!0),l.close&&a(i.body,null,!0),t.closeStrip.open&&o(h.body,null,!0),r(e.body)&&n(i.body)&&(o(e.body),a(i.body))}else t.closeStrip.open&&o(e.body,null,!0);return c},i.prototype.MustacheStatement=function(t){return t.strip},i.prototype.PartialStatement=i.prototype.CommentStatement=function(t){var e=t.strip||{};return{inlineStandalone:!0,open:e.open,close:e.close}},e["default"]=i,t.exports=e["default"]},function(t,e,s){"use strict";function i(t,e){this.source=t,this.start={line:e.first_line,column:e.first_column},this.end={line:e.last_line,column:e.last_column}}function r(t){return/^\[.*\]$/.test(t)?t.substr(1,t.length-2):t}function n(t,e){return{open:"~"===t.charAt(2),close:"~"===e.charAt(e.length-3)}}function a(t){return t.replace(/^\{\{~?\!-?-?/,"").replace(/-?-?~?\}\}$/,"")}function o(t,e,s){s=this.locInfo(s);for(var i=t?"@":"",r=[],n=0,a="",o=0,h=e.length;h>o;o++){var c=e[o].part,l=e[o].original!==c;if(i+=(e[o].separator||"")+c,l||".."!==c&&"."!==c&&"this"!==c)r.push(c);else{if(r.length>0)throw new f["default"]("Invalid path: "+i,{loc:s});".."===c&&(n++,a+="../")}}return new this.PathExpression(t,n,r,i,s)}function h(t,e,s,i,r,n){var a=i.charAt(3)||i.charAt(2),o="{"!==a&&"&"!==a;return new this.MustacheStatement(t,e,s,o,r,this.locInfo(n))}function c(t,e,s,i){if(t.path.original!==s){var r={loc:t.path.loc};throw new f["default"](t.path.original+" doesn't match "+s,r)}i=this.locInfo(i);var n=new this.Program([e],null,{},i);return new this.BlockStatement(t.path,t.params,t.hash,n,void 0,{},{},{},i)}function l(t,e,s,i,r,n){if(i&&i.path&&t.path.original!==i.path.original){var a={loc:t.path.loc};throw new f["default"](t.path.original+" doesn't match "+i.path.original,a)}e.blockParams=t.blockParams;var o=void 0,h=void 0;return s&&(s.chain&&(s.program.body[0].closeStrip=i.strip),h=s.strip,o=s.program),r&&(r=o,o=e,e=r),new this.BlockStatement(t.path,t.params,t.hash,e,o,t.strip,h,i&&i.strip,this.locInfo(n))}var p=s(8)["default"];e.__esModule=!0,e.SourceLocation=i,e.id=r,e.stripFlags=n,e.stripComment=a,e.preparePath=o,e.prepareMustache=h,e.prepareRawBlock=c,e.prepareBlock=l;var u=s(12),f=p(u)},function(t,e,s){"use strict";function i(t,e,s){if(n.isArray(t)){for(var i=[],r=0,a=t.length;a>r;r++)i.push(e.wrap(t[r],s));return i}return"boolean"==typeof t||"number"==typeof t?t+"":t}function r(t){this.srcFile=t,this.source=[]}e.__esModule=!0;var n=s(13),a=void 0;try{}catch(o){}a||(a=function(t,e,s,i){this.src="",i&&this.add(i)},a.prototype={add:function(t){n.isArray(t)&&(t=t.join("")),this.src+=t},prepend:function(t){n.isArray(t)&&(t=t.join("")),this.src=t+this.src},toStringWithSourceMap:function(){return{code:this.toString()}},toString:function(){return this.src}}),r.prototype={prepend:function(t,e){this.source.unshift(this.wrap(t,e))},push:function(t,e){this.source.push(this.wrap(t,e))},merge:function(){var t=this.empty();return this.each(function(e){t.add([" ",e,"\n"])}),t},each:function(t){for(var e=0,s=this.source.length;s>e;e++)t(this.source[e])},empty:function(){var t=void 0===arguments[0]?this.currentLocation||{start:{}}:arguments[0];return new a(t.start.line,t.start.column,this.srcFile)},wrap:function(t){var e=void 0===arguments[1]?this.currentLocation||{start:{}}:arguments[1];return t instanceof a?t:(t=i(t,this,e),new a(e.start.line,e.start.column,this.srcFile,t))},functionCall:function(t,e,s){return s=this.generateList(s),this.wrap([t,e?"."+e+"(":"(",s,")"]);
+
+},quotedString:function(t){return'"'+(t+"").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")+'"'},objectLiteral:function(t){var e=[];for(var s in t)if(t.hasOwnProperty(s)){var r=i(t[s],this);"undefined"!==r&&e.push([this.quotedString(s),":",r])}var n=this.generateList(e);return n.prepend("{"),n.add("}"),n},generateList:function(t,e){for(var s=this.empty(e),r=0,n=t.length;n>r;r++)r&&s.add(","),s.add(i(t[r],this,e));return s},generateArray:function(t,e){var s=this.generateList(t,e);return s.prepend("["),s.add("]"),s}},e["default"]=r,t.exports=e["default"]}])});
\ No newline at end of file
diff --git a/config/config.inc.php b/config/config.inc.php
index 940f088b9..df3193a58 100644
--- a/config/config.inc.php
+++ b/config/config.inc.php
@@ -52,6 +52,25 @@ define('_XE_PATH_', str_replace('config/config.inc.php', '', str_replace('\\', '
// Set can use other method instead cookie to store session id(for file upload)
ini_set('session.use_only_cookies', 0);
+// Set default charset as UTF-8
+$charset = 'UTF-8';
+ini_set('default_charset', $charset);
+
+if (function_exists('iconv_set_encoding') && version_compare(PHP_VERSION, '5.6', '<'))
+{
+ iconv_set_encoding('internal_encoding', $charset);
+}
+
+if (function_exists('mb_internal_encoding'))
+{
+ mb_internal_encoding($charset);
+}
+
+if (function_exists('mb_regex_encoding'))
+{
+ mb_regex_encoding($charset);
+}
+
if(file_exists(_XE_PATH_ . 'config/package.inc.php'))
{
@@ -72,12 +91,12 @@ else
/**
* Location site
*/
- define('_XE_LOCATION_SITE_', 'http://www.xpressengine.com/');
+ define('_XE_LOCATION_SITE_', 'https://www.xpressengine.com/');
/**
* Download server
*/
- define('_XE_DOWNLOAD_SERVER_', 'http://download.xpressengine.com/');
+ define('_XE_DOWNLOAD_SERVER_', 'https://download.xpressengine.com/');
}
/*
diff --git a/config/package.inc.php b/config/package.inc.php
index 46eb98584..46b31a6b4 100644
--- a/config/package.inc.php
+++ b/config/package.inc.php
@@ -3,5 +3,5 @@
define('_XE_PACKAGE_', 'XE');
define('_XE_LOCATION_', 'ko');
-define('_XE_LOCATION_SITE_', 'http://www.xpressengine.com/');
-define('_XE_DOWNLOAD_SERVER_', 'http://download.xpressengine.com/');
+define('_XE_LOCATION_SITE_', 'https://www.xpressengine.com/');
+define('_XE_DOWNLOAD_SERVER_', 'https://download.xpressengine.com/');
diff --git a/layouts/default/layout.html b/layouts/default/layout.html
index dfcc79ca4..4adf5a76c 100644
--- a/layouts/default/layout.html
+++ b/layouts/default/layout.html
@@ -104,6 +104,6 @@
diff --git a/layouts/xe_official/layout.html b/layouts/xe_official/layout.html
index 82e0a0073..97dddf058 100644
--- a/layouts/xe_official/layout.html
+++ b/layouts/xe_official/layout.html
@@ -53,6 +53,6 @@ body{background:url({$layout_info->background_image}) repeat-x left top;}
diff --git a/layouts/xedition/demo/welcome_main.html b/layouts/xedition/demo/welcome_main.html
index 08ea671c4..c4156d6cc 100644
--- a/layouts/xedition/demo/welcome_main.html
+++ b/layouts/xedition/demo/welcome_main.html
@@ -3,7 +3,7 @@
이 서버는 안전하지 않은 PHP 버전을 사용하고 있으며, PHP를 최신 안정 버전으로의 업그레이드를 권장합니다.