#
# 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 of the form ~name [arg...]~,
# optionally wrapped in HTML comments (which will be removed along with
# the tag markers when the template is filled out).
#
# { and } as the final argument mark those tags as being the start and
# end of a sub-template (for optional or repeated sections). All other
# tags represent slots to be directly filled by data values. On a }
# tag, the name is optional, but must match the corresponding { tag if
# present.
#
# For a value tag, arguments represent encodings to be applied
# successively. For instance, ~foo html~ will encode it to be safe in
# HTML ('&' to '&', '<' to '<', and so on).
#
# { tags can take one argument, which will call the corresponding
# tem_auto_* function to munge the data, automating certain common use
# cases. See the comments on the tem_auto functions for more details.
require_once('code/wfpl/encode.php');
require_once('code/wfpl/file.php');
require_once('code/wfpl/misc.php');
# Top-Level Functions
# -------------------
function template($template, $data) {
return fill_template(parse_template($template), $data);
}
function template_file($filename, $data) {
return fill_template(parse_template_file($filename), $data);
}
function parse_template_file($filename) {
return parse_template(file_get_contents($filename));
}
# We parse the template string into a tree of strings and sub-templates.
# A template is a hash with a name string, a pieces array, and possibly
# an args array.
function parse_template($string) {
$tem =& tem_push();
$tem['pieces'] = array();
# note: for some reason this captures ''.
$matches = preg_split("/()/", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
foreach($matches as $match) {
if($match == '~~') $match = '~';
if(substr($match,0,1) == '~' and strlen($match) > 2) {
$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); # drop '{'
$tem =& tem_push($tem); # create a new sub-template
$tem['parent']['pieces'][] =& $tem; # as a piece of the parent
$tem['name'] = $name;
$tem['pieces'] = array();
$tem['args'] = $args;
} elseif(last($args) == '}') { # close block
array_pop($args); # drop '}'
$cur = $tem['name'];
if($name && $name != $cur) {
die("Invalid template: tried to close '$name', but '$cur' is current.");
}
$tem =& $tem['parent'];
} else { # value slot
$tem['pieces'][] = array('name' => $name, 'args' => $args);
}
} elseif($match and $match != '
#
# row content...
#
#
#
#
function tem_auto_sep(&$value, $key, $context) {
$rows =& $context['parent']['parent'];
if($rows['cur'] != count($rows['rows'])-1) # last row?
return $value = true; # show once
}
# 'show' sections will be shown unless the corresponding data value
# is false. We check only for false; 0 or '' will not work.
function tem_auto_show(&$value) {
if($value !== false) $value = array(array());
return $value;
}
# 'evenodd' sections are given an 'evenodd' attribute whose value
# alternates between 'even' and 'odd'.
function tem_auto_evenodd(&$values) {
$even = false;
foreach($values as &$value) {
$value['evenodd'] = $even ? 'even' : 'odd';
$even = !$even;
}
return $values;
}
# Backward Compatibility
# ----------------------
# Old-style templates don't show unless explicitly requested.
function tem_auto_hide(&$value, $key, $context) {
unset($context['data'][$key]);
return false;
}
# The old API is being used with the named sub-template,
# so hide it and insert a value slot for its expansion(s).
function &tem_is_old_sub($name, &$template) {
foreach($template['pieces'] as $key => &$piece) {
if(is_array($piece) and $piece['pieces']) {
if($piece['name'] == $name) {
if($piece['args'][0] != 'hide') { # if we haven't already
$piece['args'] = array('hide');
$var = array('name' => $name, 'args' => array());
array_splice($template['pieces'], $key, 0, array($var));
}
return $piece;
}
$tem = tem_is_old_sub($name, $piece);
if($tem) return $tem;
}
}
}
class tem {
var $template;
var $data;
function tem() {
$this->template = array();
$this->data = array();
}
function set($key, $value) {
$this->data[$key] = $value;
}
function append($key, $value) {
$this->data[$key] .= $value;
}
function prepend($key, $value) {
$this->data[$key] = $value . $this->data[$key];
}
function clear($key) {
unset($this->data[$key]);
}
function get($key) {
return $this->data[$key];
}
function show($name) {
$tem = tem_is_old_sub($name, $this->template);
$this->data[$name] .= fill_template($tem, $this->data);
}
function show_separated($name) {
if($this->get($name)) {
$this->show($name . '_sep');
}
$this->show($name);
}
function load_str($str) {
$this->template = parse_template($str);
}
function load($filename) {
$this->template = parse_template_file($filename);
}
function run($tem = false) {
if($tem) {
if(strlen($tem < 150 && file_exists($tem))) $this->load($tem);
else $this->load_str($tem);
}
return fill_template($this->template, $this->data);
}
function output($tem = false) {
print($this->run($tem));
}
function top_sub_names() {
return array_keys(top_sub_templates($this->template));
}
function top_subs() {
$ret = array();
$names = $this->top_sub_names();
foreach($names as $name) {
$ret[$name] = $this->get($name);
}
return $ret;
}
# old name for show (deprecated)
function sub($name) {
$this->show($name);
}
}
function tem_init() {
if(!$GLOBALS['wfpl_template']) {
$GLOBALS['wfpl_template'] = new tem();
}
}
function tem_append($key, $value) {
tem_init();
$GLOBALS['wfpl_template']->append($key, $value);
}
function tem_prepend($key, $value) {
tem_init();
$GLOBALS['wfpl_template']->prepend($key, $value);
}
function tem_set($key, $value) {
tem_init();
$GLOBALS['wfpl_template']->set($key, $value);
}
function tem_get($key) {
tem_init();
return $GLOBALS['wfpl_template']->get($key);
}
function tem_run($tem = false) {
tem_init();
return $GLOBALS['wfpl_template']->run($tem);
}
function tem_show($name) {
tem_init();
return $GLOBALS['wfpl_template']->show($name);
}
function tem_show_separated($name) {
tem_init();
$GLOBALS['wfpl_template']->show_separated($name);
}
function tem_load($filename) {
tem_init();
$GLOBALS['wfpl_template']->load($filename);
}
function tem_output($filename = false) {
tem_init();
$GLOBALS['wfpl_template']->output($filename);
}
function tem_top_subs() {
tem_init();
return $GLOBALS['wfpl_template']->top_subs();
}
function tem_top_sub_names() {
tem_init();
return $GLOBALS['wfpl_template']->top_sub_names();
}
function tem_load_new($filename) {
$old = $GLOBALS['wfpl_template'];
$GLOBALS['wfpl_template'] = new tem();
$GLOBALS['wfpl_template']->load($filename);
return $old;
}
# deprecated (old name for show)
function tem_sub($name) {
tem_show($name);
}
?>