Merge branch 'dev' into feature/higher-order-sections

This commit is contained in:
Justin Hileman
2010-12-08 08:57:37 -05:00
27 changed files with 2366 additions and 110 deletions
+100 -35
View File
@@ -14,9 +14,6 @@
*/
class Mustache {
public $_otag = '{{';
public $_ctag = '}}';
/**
* Should this Mustache throw exceptions when it finds unexpected tags?
*
@@ -85,6 +82,15 @@ class Mustache {
*/
const PRAGMA_UNESCAPED = 'UNESCAPED';
/**
* Constants used for section and tag RegEx
*/
const SECTION_TYPES = '\^#\/';
const TAG_TYPES = '#\^\/=!<>\\{&';
public $_otag = '{{';
public $_ctag = '}}';
protected $_tagRegEx;
protected $_template = '';
@@ -263,6 +269,23 @@ class Mustache {
return $template;
}
/**
* Prepare a section RegEx string for the given opening/closing tags.
*
* @access protected
* @param string $otag
* @param string $ctag
* @return string
*/
protected function _prepareSectionRegEx($otag, $ctag) {
return sprintf(
'/(?:(?<=\\n)[ \\t]*)?%s(?<type>[%s])(?<tag_name>.+?)%s\\n?/s',
preg_quote($otag, '/'),
self::SECTION_TYPES,
preg_quote($ctag, '/')
);
}
/**
* Extract a section from $template.
*
@@ -273,9 +296,7 @@ class Mustache {
* @return array $section, $offset, $type, $tag_name and $content
*/
protected function _findSection($template) {
$otag = preg_quote($this->_otag, '/');
$ctag = preg_quote($this->_ctag, '/');
$regex = '/' . $otag . '([\\^\\#\\/])\\s*(.+?)\\s*' . $ctag . '\\s*/ms';
$regEx = $this->_prepareSectionRegEx($this->_otag, $this->_ctag);
$section_start = null;
$section_type = null;
@@ -285,12 +306,12 @@ class Mustache {
$section_stack = array();
$matches = array();
while (preg_match($regex, $template, $matches, PREG_OFFSET_CAPTURE, $search_offset)) {
while (preg_match($regEx, $template, $matches, PREG_OFFSET_CAPTURE, $search_offset)) {
$match = $matches[0][0];
$offset = $matches[0][1];
$type = $matches[1][0];
$tag_name = trim($matches[2][0]);
$type = $matches['type'][0];
$tag_name = trim($matches['tag_name'][0]);
$search_offset = $offset + strlen($match);
@@ -328,6 +349,22 @@ class Mustache {
}
}
/**
* Prepare a pragma RegEx for the given opening/closing tags.
*
* @access protected
* @param string $otag
* @param string $ctag
* @return string
*/
protected function _preparePragmaRegEx($otag, $ctag) {
return sprintf(
'/%s%%\\s*(?<pragma_name>[\\w_-]+)(?<options_string>(?: [\\w]+=[\\w]+)*)\\s*%s\\n?/s',
preg_quote($otag, '/'),
preg_quote($ctag, '/')
);
}
/**
* Initialize pragmas and remove all pragma tags.
*
@@ -343,10 +380,8 @@ class Mustache {
return $template;
}
$otag = preg_quote($this->_otag, '/');
$ctag = preg_quote($this->_ctag, '/');
$regex = '/' . $otag . '%\\s*([\\w_-]+)((?: [\\w]+=[\\w]+)*)\\s*' . $ctag . '\\n?/s';
return preg_replace_callback($regex, array($this, '_renderPragma'), $template);
$regEx = $this->_preparePragmaRegEx($this->_otag, $this->_ctag);
return preg_replace_callback($regEx, array($this, '_renderPragma'), $template);
}
/**
@@ -359,8 +394,8 @@ class Mustache {
*/
protected function _renderPragma($matches) {
$pragma = $matches[0];
$pragma_name = $matches[1];
$options_string = $matches[2];
$pragma_name = $matches['pragma_name'];
$options_string = $matches['options_string'];
if (!in_array($pragma_name, $this->_pragmasImplemented)) {
throw new MustacheException('Unknown pragma: ' . $pragma_name, MustacheException::UNKNOWN_PRAGMA);
@@ -369,7 +404,7 @@ class Mustache {
$options = array();
foreach (explode(' ', trim($options_string)) as $o) {
if ($p = trim($o)) {
$p = explode('=', trim($p));
$p = explode('=', $p);
$options[$p[0]] = $p[1];
}
}
@@ -427,6 +462,23 @@ class Mustache {
return (isset($this->_throwsExceptions[$exception]) && $this->_throwsExceptions[$exception]);
}
/**
* Prepare a tag RegEx for the given opening/closing tags.
*
* @access protected
* @param string $otag
* @param string $ctag
* @return string
*/
protected function _prepareTagRegEx($otag, $ctag) {
return sprintf(
'/(?<whitespace>(?<=\\n)[ \\t]*)?%s(?<type>[%s]?)(?<tag_name>.+?)(?:\\2|})?%s(?:\\s*(?=\\n))?/s',
preg_quote($otag, '/'),
self::TAG_TYPES,
preg_quote($ctag, '/')
);
}
/**
* Loop through and render individual Mustache tags.
*
@@ -442,22 +494,31 @@ class Mustache {
$otag_orig = $this->_otag;
$ctag_orig = $this->_ctag;
$otag = preg_quote($this->_otag, '/');
$ctag = preg_quote($this->_ctag, '/');
$this->_tagRegEx = '/' . $otag . "([#\^\/=!<>\\{&])?(.+?)\\1?" . $ctag . "+/s";
$this->_tagRegEx = $this->_prepareTagRegEx($this->_otag, $this->_ctag);
$html = '';
$matches = array();
while (preg_match($this->_tagRegEx, $template, $matches, PREG_OFFSET_CAPTURE)) {
$tag = $matches[0][0];
$offset = $matches[0][1];
$modifier = $matches[1][0];
$tag_name = trim($matches[2][0]);
$modifier = $matches['type'][0];
$tag_name = trim($matches['tag_name'][0]);
if (isset($matches['whitespace']) && $matches['whitespace'][1] > -1) {
$whitespace = $matches['whitespace'][0];
} else {
$whitespace = null;
}
$html .= substr($template, 0, $offset);
$html .= $this->_renderTag($modifier, $tag_name);
$template = substr($template, $offset + strlen($tag));
$next_offset = $offset + strlen($tag);
if ((substr($html, -1) == "\n") && (substr($template, $next_offset, 1) == "\n")) {
$next_offset++;
}
$template = substr($template, $next_offset);
$html .= $this->_renderTag($modifier, $tag_name, $whitespace);
}
$this->_otag = $otag_orig;
@@ -478,7 +539,7 @@ class Mustache {
* @throws MustacheException Unmatched section tag encountered.
* @return string
*/
protected function _renderTag($modifier, $tag_name) {
protected function _renderTag($modifier, $tag_name, $whitespace) {
switch ($modifier) {
case '=':
return $this->_changeDelimiter($tag_name);
@@ -488,9 +549,13 @@ class Mustache {
break;
case '>':
case '<':
return $this->_renderPartial($tag_name);
return $this->_renderPartial($tag_name, $whitespace);
break;
case '{':
// strip the trailing } ...
if ($tag_name[(strlen($tag_name) - 1)] == '}') {
$tag_name = substr($tag_name, 0, -1);
}
case '&':
if ($this->_hasPragma(self::PRAGMA_UNESCAPED)) {
return $this->_renderEscaped($tag_name);
@@ -553,9 +618,10 @@ class Mustache {
* @param string $tag_name
* @return string
*/
protected function _renderPartial($tag_name) {
protected function _renderPartial($tag_name, $whitespace = '') {
$view = clone($this);
return $view->render($this->_getPartial($tag_name));
return $whitespace . preg_replace('/\n(?!$)/s', "\n" . $whitespace, $view->render($this->_getPartial($tag_name)));
}
/**
@@ -567,13 +633,12 @@ class Mustache {
* @return string
*/
protected function _changeDelimiter($tag_name) {
$tags = explode(' ', $tag_name);
$this->_otag = $tags[0];
$this->_ctag = $tags[1];
list($otag, $ctag) = explode(' ', $tag_name);
$this->_otag = $otag;
$this->_ctag = $ctag;
$this->_tagRegEx = $this->_prepareTagRegEx($this->_otag, $this->_ctag);
$otag = preg_quote($this->_otag, '/');
$ctag = preg_quote($this->_ctag, '/');
$this->_tagRegEx = '/' . $otag . "([#\^\/=!<>\\{&])?(.+?)\\1?" . $ctag . "+/s";
return '';
}
@@ -625,7 +690,7 @@ class Mustache {
* @return string
*/
protected function _getVariable($tag_name) {
if ($this->_hasPragma(self::PRAGMA_DOT_NOTATION) && $tag_name != '.') {
if ($tag_name != '.' && strpos($tag_name, '.') !== false && $this->_hasPragma(self::PRAGMA_DOT_NOTATION)) {
$chunks = explode('.', $tag_name);
$first = array_shift($chunks);