#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# This is a simple template-handling system. You pass it a big data
# structure with key/value pairs, and a template string to fill out.
#
# Within a template, it recognizes tags delimited by tildes (~). When
# the template is filled out, the tags will be replaced with the
# corresponding data. Tags ending with '?' and '.' mark the start and
# end of a sub-template (for optional or repeated text), and can be
# wrapped in HTML comments (which will be removed along with the tags
# when the template is filled out).
require_once('code/wfpl/encode.php');
require_once('code/wfpl/file.php');
require_once('code/wfpl/misc.php');
# Public functions
# ----------------
function template($data, $template) {
return fill_template($data, parse_template($template));
}
function template_file($data, $filename) {
return fill_template($data, parse_template_file($filename));
}
function parse_template_file($filename) {
return parse_template(file_get_contents($filename));
}
# First we take the template string and break it up into an array
# of strings and sub-arrays. The first item in a sub-array is the name
# of the value or sub-template.
function parse_template($string) {
# Don't mess with the $stack/$tem assignments! Since
# PHP references point to the variable, not the data,
# it really does have to be written exactly like this.
$stack[] = array('name' => 'root', 'pieces' => array());
$tem = &last($stack);
# note: for some reason this captures ''.
$matches = preg_split("/()/", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
foreach($matches as $match) {
if(substr($match,0,1) == '~') {
$args = explode(' ', substr($match,1,-1));
if(count($args) == 1 and $args[0] == '}') $name = '';
else $name = array_shift($args);
if(last($args) == '{') { # open block
array_pop($args);
$stack[] = array('name' => $name, 'pieces' => array(), 'args' => $args);
$tem['pieces'][] = &last($stack);
$tem = &last($stack);
} elseif(last($args) == '}') { # close block
array_pop($args);
$cur = $stack[count($stack)-1]['name'];
if($name && $name != $cur) {
die("Invalid template: tried to close '$name', but '$cur' is current.");
}
array_pop($stack); $tem = &last($stack);
} else { # value slot
$tem['pieces'][] = array('name' => $name, 'args' => $args);
}
} elseif($match and $match != '
#
# row content...
#
#
#
#
function tem_auto_sep($piece, $context, $keychain) {
list($name, $index, $this_name) = array_slice($keychain, -3);
$array = _tem_get($name, $context);
if($index != count($array)-1) return true;
}
# 'once' sections will be shown once unless the corresponding data value
# is false. We check only for false; 0 or '' will not work.
function tem_auto_once($piece, $context, $keychain) {
$value = _tem_get(array_pop($keychain), $context);
if($value !== false) return true;
}
# 'evenodd' sections are given an 'evenodd' attribute whose value
# alternates between 'even' and 'odd'.
function tem_auto_evenodd($piece, $context, $keychain) {
$rows = _tem_get(array_pop($keychain), $context);
$even = 0;
$text = array('even', 'odd');
foreach($rows as $key => $value) {
$rows[$key]['evenodd'] = $text[$even];
$even = 1 - $even;
}
return $rows;
}
# Internal functions
# ------------------
#
# Of course, nothing stops you from using these, but I don't know
# why you would want to...
# Convert value to array of hashes for use in sub-template expansion.
# This adds flexibility to how you represent your data.
function template_rows($value) {
if(is_array($value)) {
# numeric keys, is already array of arrays -- expand sub-template for each.
if(array_key_exists(0, $value)) return $value;
# key/value pairs -- expand sub-template once.
else return array($value);
} elseif($value) {
# value -- expand sub-template once using only parent values
return array(array());
} else {
# empty value -- don't expand sub-template
return array();
}
}
function _tem_get($key, $context) {
while($context) {
$data = array_pop($context);
if(array_key_exists($key, $data)) return $data[$key];
}
}
function tem_get($piece, $context, $keychain)
{
if(count($piece['args'])) {
$func = "tem_auto_" . $piece['args'][0];
if(function_exists($func)) return $func($piece, $context, $keychain);
else die("ERROR: template auto function '$func' not found.
\n");
} else return _tem_get($piece['name'], $context);
}
# $tag is a hash with keys 'name' and 'args'.
function tem_get_enc($tag, $context)
{
$key = $tag['name'];
$value = _tem_get($key, $context);
foreach($tag['args'] as $encoding) {
$func = "enc_$encoding";
if(function_exists($func)) $value = $func($value, $key);
else die("ERROR: encoder function '$func' not found.
\n");
}
return $value;
}
function top_sub_templates($tem) {
$subs = array();
foreach($tem['pieces'] as $piece) {
if(is_array($piece) and $piece['pieces']) {
$subs[$piece['name']] = $piece;
}
}
return $subs;
}
?>