JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
* New template system.
authorJosh Grams <josh@qualdan.com>
Mon, 3 Aug 2009 10:38:11 +0000 (06:38 -0400)
committerJosh Grams <josh@qualdan.com>
Mon, 3 Aug 2009 10:38:11 +0000 (06:38 -0400)
messages.php
misc.php
run.php
session_messages.php
template.php

index 9db80a1..71d19be 100644 (file)
 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
-
-# This file is useful for putting message boxe(s) on the screen.
+# This file is useful for putting message box(es) on the screen.
 #
 # Just call message("message here") whenever you have something to report.
 #
 # Once a template is loaded, call display_messages(). Your template should have
-# a <!--~message_box start~--> section with ~message_text.html~ tag in it.
+# a <!--~message?~--> section with a ~text:html~ tag in it.
 #
-# If you want a divider (any text between message boxes when there are multiple
-# boxes) provide a sub-template section named "message_divider" INSIDE
-# "message_box" at the begining of it.
+# If you want a divider (any text between message boxes when there are
+# multiple boxes) provide a sub-template section named "separator"
+# INSIDE "message" at the begining of it.
 #
 # If you'd like something around the group of all message boxes, you can put
-# the whole thing in a sub-template section called "message_container"
+# the whole thing in a sub-template section called "messages"
 
 # Simple example:
 #
-#    <!--~message_box start~-->
-#        <p>~message_text.html~</p>
-#    <!--~end~-->
+#    <!--~message?~-->
+#        <p>~text:html~</p>
+#    <!--~.~-->
 
 # Full-featured example:
 #
-#    <!--~message_container start~-->
+#    <!--~messages?~-->
 #         <div style="border: 2px solid red; background: #f88; padding: 5px">
-#         <!--~message_box start~-->
-#             <!--~message_divider start~-->
+#         <!--~message?~-->
+#             <!--~separator?~-->
 #                 <hr />
-#             <!--~end~-->
-#             <p style="font-size: 120%">~message_text.html~</p>
-#         <!--~end~-->
+#             <!--~separator.~-->
+#             <p style="font-size: 120%">~text:html~</p>
+#         <!--~message.~-->
 #         </div>
-#    <!--~end~-->
+#    <!--~messages.~-->
 
 require_once('code/wfpl/template.php');
 
 function message($msg) {
        if(!isset($GLOBALS['wfpl_messages'])) {
-               $GLOBALS['wfpl_messages'] = array();
+               if(function_exists('session_restore_messages')) {
+                       $GLOBALS['wfpl_messages'] = session_restore_messages();
+               } else $GLOBALS['wfpl_messages'] = array();
        }
 
        $GLOBALS['wfpl_messages'][] = $msg;
 }
 
-# if you want the messages in a template other than the default one, pass it like so:
-#
-# display_messages(ref($my_template));
-function display_messages($template = 0) {
-       $first = true;
-       if($template === 0) {
-               $template = &$GLOBALS['wfpl_template'];
-       } else {
-               $template = &$template->ref;
-       }
-
-       if(function_exists('session_restore_messages')) {
-               session_restore_messages();
-       }
+function display_messages(&$tem = NULL) {
+       if(!$tem) $tem = &$GLOBALS['wfpl_template'];
 
-       if($GLOBALS['wfpl_messages']) {
-               foreach($GLOBALS['wfpl_messages'] as $msg) {
-                       if($first) {
-                               $first = false;
-                       } else {
-                               $template->sub('message_divider');
-                       }
-                       $template->set('message_text', $msg);
-                       $template->sub('message_box');
-               }
-               $template->sub('message_container');
-               unset($GLOBALS['wfpl_messages']);
+       foreach($GLOBALS['wfpl_messages'] as $msg) {
+               $sub = array('text' => $msg);
+               $tem['message'][] = $sub;
+               $tem['messages'] = TRUE;
        }
 }
-
-?>
index 857f6db..b18abd4 100644 (file)
--- a/misc.php
+++ b/misc.php
@@ -126,4 +126,8 @@ function ref(&$foo) {
        return new stupid_reference($foo);
 }
 
+function &last(&$array) {
+       if(count($array)) return $array[count($array) - 1];
+}
+
 ?>
diff --git a/run.php b/run.php
index 52aac2e..26a8b36 100644 (file)
--- a/run.php
+++ b/run.php
@@ -54,7 +54,7 @@ if(file_exists('code/config.php')) {
 # pass http://foo.com/bar.html to redirect to a full directory
 function run_php($dest = false) {
        if($dest) {
-               # if it's got a : it must be a full URL, redirect
+               # if it has a : it must be a full URL, redirect
                if(strpos($dest, ':')) {
                        redirect($dest);
                        exit();
@@ -78,60 +78,22 @@ function run_php($dest = false) {
                }
        }
 
-       $GLOBALS['basename'] = $basename;
-
        $html_file = "$basename.html";
        $php_file = "$basename.php";
 
        $html_exists = file_exists($html_file);
        $php_exists = file_exists($php_file);
 
-       if(file_exists('template.html')) {
-               $GLOBALS['wfpl_main_template'] = new tem();
-               $GLOBALS['wfpl_main_template']->load("template.html");
-               $GLOBALS['wfpl_main_template']->set('basename', $basename);
-
-               # This helps put in a stylesheet link if you have pages with custom css
-               if(file_exists("$basename.css")) {
-                       $GLOBALS['wfpl_main_template']->set('css_link', "$basename.css");
-                       $GLOBALS['wfpl_main_template']->sub('css_links');
-               }
-       }
-
        # cms_get can return one of:
        # 1) false to indicate that there's no cms content for this basename
-       # 2) a string to indicate a soft/full redirect just as foo_main()
-       # 3) a hash of key/value pairs to be tem_set(key,value) on the template
-       if(function_exists('cms_display')) {
-               $cms_content = cms_display($basename, $GLOBALS['wfpl_main_template']);
+       # 2) a string to request a soft/full redirect just like foo_main()
+       # 3) a hash of key/value pairs to be added to the template
+       if(function_exists('cms_get')) {
+               $cms_content = cms_get($basename);
                if(is_string($cms_content)) {
                        run_php($cms_content);
                        return;
                }
-       } else {
-               $cms_content = false;
-       }
-
-       if(!$php_exists && !$html_exists && !$cms_content) {
-               header('HTTP/1.0 404 File Not Found');
-               if(file_exists('error_404.php') || file_exists('error_404.html')) {
-                       $GLOBALS['error_basename'] = $basename;
-                       run_php('error_404');
-                       return;
-               } else {
-                       echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html><head><title>404</title></head><body><h1>404 File Not Found</h1></body></html>';
-                       exit();
-               }
-       }
-
-       # If there's no template.html we don't want to parse $html_file.
-       if($html_exists && !$php_exists && !file_exists('template.html')) {
-               readfile($html_file);
-               exit();
-       }
-
-       if($html_exists) {
-               tem_load_new($html_file);
        }
 
        if($php_exists) {
@@ -141,42 +103,41 @@ function run_php($dest = false) {
                        run_php($other);
                        return;
                }
-       } else {
-               $sub_names = tem_top_sub_names();
-               foreach($sub_names as $sub_name) {
-                       tem_sub($sub_name);
+       } elseif($html_exists) {
+               readfile($html_file);
+               exit();
+       } elseif(!$cms_content) {
+               header('HTTP/1.0 404 File Not Found');
+               if(file_exists('404.php') || file_exists('404.html')) {
+                       run_php('404');
+                       return;
+               } else {
+                       echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html><head><title>404</title></head><body><h1>404 File Not Found</h1></body></html>';
                }
        }
 
-       # Check for $GLOBALS['wfpl_template'] because it might have been set (or unset) by the php script.
-       if($GLOBALS['wfpl_template'] || $GLOBALS['wfpl_main_template']) {
-               if($GLOBALS['wfpl_main_template']) {
-                       # if there was a template for that page, and one for the whole
-                       # site, copy all template sections that have been show()n to the
-                       # site-wide template
-                       if($GLOBALS['wfpl_template']) {
-                               $sections = tem_top_subs();
-                               if($sections) foreach($sections as $name => $val) {
-                                       $GLOBALS['wfpl_main_template']->append($name, $val);
-                               }
-                       }
-
-                       $GLOBALS['wfpl_template'] = $GLOBALS['wfpl_main_template'];
-               }
+       $data = &$GLOBALS['wfpl_template'];
+       $data['basename'] = $basename;
+       if(function_exists('display_messages')) {
+               display_messages();
+       }
+       if($cms_content) foreach($cms_content as $name => $value) {
+               $data[$name] .= $value;
+       }
+       if(file_exists("$basename.css")) {
+               $data['css_link'] = "$basename.css";
+       }
 
-               # If you have a site-wide template (template.html) then messages will
-               # be displayed there. If you instead want messages displayed on your
-               # page, call display_messages() from your page_main().
-               #
-               # Either way, you'll need to require_once('code/wfpl/messages.php')
-               # or require_once('code/wfpl/session_messages.php'). code/config.php
-               # is a nice place to do this.
-               if(function_exists('display_messages')) {
-                       display_messages();
+       if(file_exists("template.html")) {
+               $template = parse_template_file("template.html");
+               if($html_exists) {
+                       $subs = parse_template_file($html_file);
+                       $template = merge_templates($template, $subs);
                }
-
-               tem_output();
+       } elseif($html_exists) {
+               $template = parse_template_file("$html_file");
        }
+       if($template) print fill_template($data, $template);
 }
 
 run_php();
index 481b87a..6f0148c 100644 (file)
@@ -40,18 +40,12 @@ function session_save_messages() {
 }
 
 function session_restore_messages() {
-       if(!session_exists()) {
-               return false;
-       }
-       $messages = session_get('wfpl_messages');
-       if($messages !== false) {
-               $messages = string_to_array($messages);
-               if(!(isset($GLOBALS['wfpl_messages']) && is_array($GLOBALS['wfpl_messages']))) {
-                       $GLOBALS['wfpl_messages'] = array();
+       if(session_exists()) {
+               $messages = session_get('wfpl_messages');
+               if($messages !== false) {
+                       session_clear('wfpl_messages');
+                       return string_to_array($messages);
                }
-               # messages from the previous run happened first
-               $GLOBALS['wfpl_messages'] = array_merge($messages, $GLOBALS['wfpl_messages']);
-
        }
-       session_clear('wfpl_messages');
+       return array();
 }
index 1c77513..9ccce87 100644 (file)
 <?php
 
-
-# This file contains generally useful template handling code. It is wrapped in
-# an object so that if you want/need to you can make more than one instance of
-# it and they won't step on each other's toes. Also there are a set of global
-# functions at the bottom so you don't have to mess around with objects if you
-# don't want to. The documentation will be on the object methods, but just know
-# that each has a straight function wrapper at the bottom with 'tem_' prepended
-# to the name.
-
-# This is designed to be as simple as it can be for your project. The simple
-# way to use it is to set some key/value pairs with tem_set() then call
-# tem_output('filename.html') to output the page. A more complex example
-# including the use of sub-templates can be found in tem_test.php
-
-# FIXME: sub-sub templates need to be cleared when the sub template containing
-# them is run
+#  Copyright (C) 2008,2009 Joshua Grams <josh@qualdan.com>
+#
+#  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 <http://www.gnu.org/licenses/>.
+
+
+# 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/misc.php');
 require_once('code/wfpl/file.php');
+require_once('code/wfpl/misc.php');
 
-class tem {
-       var $keyval;        # an array containing key/value pairs 
-       var $filename;      # template filename (sometimes not set)
-       var $template;      # contents of template
-       var $sub_templates; # tag-name/template-string pairs
-       var $sub_subs;      # key: sub-template name  value: array of names of the sub-templates of this one
-
-       # initialize variables
-       function tem() {
-               $this->keyval = array('' => '~'); # so that ~~ in the template creates a single ~
-               $this->sub_templates = array();
-       }
 
-       # set a key/value pair. if a ~tag~ in the template matches key it will be replaced by value
-       function set($key, $value) {
-               $this->keyval[$key] = $value;
-       }
+# Public functions
+# ----------------
 
-       # like set() but appends
-       function append($key, $value) {
-               $this->keyval[$key] .= $value;
-       }
+function template($data, $template) {
+       return fill_template($data, parse_template($template));
+}
 
-       # like set() but prepends
-       function prepend($key, $value) {
-               $this->keyval[$key] = $value . $this->keyval[$key];
-       }
+function template_file($data, $filename) {
+       return fill_template($data, parse_template_file($filename));
+}
 
-       # clear a value. Functionally equivalent to set($key, '') but cleaner and more efficient
-       function clear($key) {
-               unset($this->keyval[$key]);
-       }
+function parse_template_file($filename) {
+       return parse_template(file_get_contents($filename));
+}
 
-       # grab a value you stuck in earlier with set()
-       function get($key) {
-               return $this->keyval[$key];
-       }
+# 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.
 
-       # depricated (renamed show())
-       function sub($sub_template_name) {
-               $this->show($sub_template_name);
-       }
+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 '<!--' but not '-->'.
+       $matches = preg_split("/(<!--)?(~[^~]*~)(?(1)-->)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
+       foreach($matches as $match) {
+               if(substr($match,0,1) == '~') {
+                       $args = explode(' ', substr($match,1,-1));
 
-       # run the template engine on one of the sub-templates and append the result
-       # to the keyval in the main array. See tem_test.php for an example of how
-       # this can be used.
-       function show($sub_template_name) {
-               $this->keyval[$sub_template_name] .= template_run($this->sub_templates[$sub_template_name], $this->keyval);
+                       if(count($args) == 1 and $args[0] == '}') $name = '';
+                       else $name = array_shift($args);
 
-               # after running a sub-template, clear its sub-templates
-               if(isset($this->sub_subs[$sub_template_name])) {
-                       foreach($this->sub_subs[$sub_template_name] as $sub_sub) {
-                               $this->clear($sub_sub);
+                       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 != '<!--') {  # static string
+                       $tem['pieces'][] = $match;
                }
        }
+       return $tem;
+}
 
-       function show_separated($sub_template_name) {
-               if($this->get($sub_template_name)) {
-                       $this->show($sub_template_name . '_sep');
-               }
-               $this->show($sub_template_name);
-       }
-
-       # this is used by tem::load() and should be otherwise useless
-       function _load(&$in, &$out, &$parents, &$parent) {
-               while($in) {
-                       # scan for one of: 1) the begining of a sub-template 2) the end of this one 3) the end of the file
-                       $n = strpos($in, '<!--~');
-                       if($n === false) { # not found
-                               # we hit the end of the file
-                               $out .= $in;
-                               $in = '';
-                               return;
-                       }
-
-                       # move everything up to (but not including) <!-- to the output
-                       $out .= substr($in, 0, $n);
-                       $in = substr($in, $n);
-
-                       # we found something.
-                       # is it an end tag?
-                       if(strcmp('<!--~end~-->', substr($in, 0, 12)) == 0) {
-                               $in = substr($in, 12);
-                               $parent = array_pop($parents);
-                               return;
-                       }
-
-                       $matches = array();
-                       # this limits sub_template names to 50 chars
-                       if(ereg('^<!--~([^~]*) start~-->', substr($in, 0, 65), $matches)) {
-                               list($start_tag, $tag_name) = $matches;
+# Then we do a depth-first traversal of the template tree,
+# replacing all tags with the data values.
 
-                               # keep track of the tree
-                               if(!isset($this->sub_subs[$parent])) {
-                                       $this->sub_subs[$parent] = array();
+function fill_template($data, $template, $context = NULL, $keychain = NULL) {
+       $context[] = $data;
+       foreach($template['pieces'] as $piece) {
+               if(is_string($piece)) $output .= $piece;
+               else {
+                       if($piece['pieces']) {  # sub-template
+                               $keychain[] = $piece['name'];
+                               $data = tem_get($piece, $context, $keychain);
+                               foreach(template_rows($data) as $key => $row) {
+                                       $keychain[] = $key;
+                                       $output .= fill_template($row, $piece, $context, $keychain);
+                                       array_pop($keychain);
                                }
-                               array_push($this->sub_subs[$parent], $tag_name);
-                               array_push($parents, $parent);
-                               $parent = $tag_name;
-
-                               $out .= '~' . $tag_name . '~';
-                               $in = substr($in, strlen($start_tag));
-                               $this->sub_templates[$tag_name] = '';
-                               $this->_load($in, $this->sub_templates[$tag_name], $parents, $parent);
-                       } else {
-                               # it's not a start tag or end tag, so let's pass it through:
-                               $out .= substr($in, 0, 5);
-                               $in = substr($in, 5);
-                       }
-               } #repeat
-       }
-
-       # like load() except you pass a string instead of a filename
-       function load_str($str) {
-               $this->template = '';
-               $parents = array('top_level_subs' => array());
-               $parent = 'top_level_subs';
-               $this->_load($str, $this->template, $parents, $parent);
-       }
-
-       # This is useful when you have sub-templates that you want to mess with
-       # before the main template is run. But can also be used to simply specify
-       # the filename ahead of time.
-       function load($filename) {
-               $this->filename = $filename;
-               $this->load_str(read_whole_file($filename));
-       }
-               
-       # Run the template. Pass a filename, or a string, unless you've already
-       # specified a template with load()
-       function run($templ = false) {
-               $template_string = $this->template;
-               $template_file = $this->file;
-               if($templ !== false) {
-                       if(strlen($templ) < 150 && file_exists($templ)) {
-                               $template_file = $templ;
-                               unset($template_string);
-                       } else {
-                               $template_string = $templ;
-                       }
+                               array_pop($keychain);
+                       } else $output .= tem_get_enc($piece, $context);
                }
+       }
+       return $output;
+}
 
-               if(!$template_string) {
-                       if(!$template_file) {
-                               print "sorry, no template to run\n";
-                               exit(1);
-                       }
 
-                       $template_string = read_whole_file($template_file);
-               }
-               
-               return template_run($template_string, $this->keyval);
-       }       
+# Replace top-level values in $main with top-level templates from $tem.
+function merge_templates($main, $tem) {
+       $out = array('name' => $main['name'], 'pieces' => array());
 
-       # same as run() except the output is print()ed
-       function output($templ = false) {
-               print($this->run($templ));
-       }
+       $subs = top_sub_templates($tem);
 
-       # return the names of the top level subs, or an empty array
-       function top_sub_names() {
-               if(isset($this->sub_subs['top_level_subs'])) {
-                       return $this->sub_subs['top_level_subs'];
-               } else {
-                       return array();
+       foreach($main['pieces'] as $piece) {
+               if(is_array($piece) and !$piece['pieces'] and $subs[$piece['name']]) {
+                       $piece = $subs[$piece['name']];
                }
+               $out['pieces'][] = $piece;
        }
-
-       # return the contents of the top-level sub-templates
-       #
-       # this does not run the sub-templates, so if you've not called tem_show() on them, they will be blank.
-       #
-       # Return a hash.
-       #     keys: name of top level sub-template.
-       #     values: contents of said sub-template.
-       function top_subs() {
-               $ret = array();
-               $names = $this->top_sub_names();
-               foreach($names as $name) {
-                       $ret[$name] = $this->get($name);
-               }
-               return $ret;
-       }
+       return $out;
 }
 
-# Below are functions so you can use the above class without allocating or
-# keeping track of it.
 
-# get a reference to the current template object
-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($templ = false) {
-       tem_init();
-       return $GLOBALS['wfpl_template']->run($templ);
-}
+# tem_auto functions
+# ------------------
+#
+# If a { tag has an argument, the corresponding tem_auto function is called.
+# This allows it to mangle the data to automate some common cases.
 
-# depricated (renamed tem_show())
-function tem_sub($sub_template_name) {
-       tem_show($sub_template_name);
+# 'sep' (separator) sections will be shown for all but the last parent row.
+# Sample usage:
+#      <!--~rows~-->
+#              <!--~row~-->
+#                      row content...
+#                      <!--~separator sep {~--><hr><!--~separator }"-->
+#              <!--~row~-->
+#      <!--~rows~-->
+#
+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;
 }
 
-function tem_show($sub_template_name) {
-       tem_init();
-       $GLOBALS['wfpl_template']->show($sub_template_name);
-}
+# '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_show_separated($sub_template_name) {
-       tem_init();
-       $GLOBALS['wfpl_template']->show_separated($sub_template_name);
+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_load($filename) {
-       tem_init();
-       $GLOBALS['wfpl_template']->load($filename);
+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;
 }
 
-function tem_output($filename = false) {
-       tem_init();
-       $GLOBALS['wfpl_template']->output($filename);
-}
 
 
+# Internal functions
+# ------------------
+#
+# Of course, nothing stops you from using these, but I don't know
+# why you would want to...
 
-# this is used in template_run() and should be of no other use
-function template_filler($matches) {
-       $match = array_pop($matches);
-       list($tag, $enc) = explode('.', $match, 2);
-       $value = $GLOBALS['wfpl_template_keyval'][$tag];
-       if($enc) {
-               $encs = explode('.', $enc);
-               foreach($encs as $enc) {
-                       $enc = "enc_$enc";
-                       if(function_exists($enc)) {
-                               $value = $enc($value, $tag);
-                       } else {
-                               print "ERROR: encoder function '$enc' not found.<br>\n";
-                               exit(1);
-                       }
-               }
+
+# 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();
        }
-       return $value;
 }
 
-
-# pass a template string and an associative array of the key/values and it
-# returns the result.
-function template_run($template, &$keyval) {
-       $GLOBALS['wfpl_template_keyval'] =& $keyval;
-       return preg_replace_callback('`<!--~([^~]*)~-->|~([^~]*)~|<span class="template">([^<]*)</span>|<p class="template">([^<]*)</p>`', 'template_filler', $template);
+function _tem_get($key, $context) {
+       while($context) {
+               $data = array_pop($context);
+               if(array_key_exists($key, $data)) return $data[$key];
+       }
 }
 
-function tem_top_sub_names() {
-       tem_init();
-       return $GLOBALS['wfpl_template']->top_sub_names();
+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.<br>\n");
+       } else return _tem_get($piece['name'], $context);
 }
 
-function tem_top_subs() {
-       tem_init();
-       return $GLOBALS['wfpl_template']->top_subs();
+# $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.<br>\n");
+       }
+       return $value;
 }
 
-# replaces currently set template, and returns the old.
-function tem_load_new($file) {
-       $old = $GLOBALS['wfpl_template'];
-       $GLOBALS['wfpl_template'] = new tem();
-       $GLOBALS['wfpl_template']->load($file);
-       return $old;
+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;
 }
 
 ?>