diff options
-rw-r--r-- | themes/xtemplate/images/druplicon.gif | bin | 0 -> 3509 bytes | |||
-rw-r--r-- | themes/xtemplate/xtemplate.css | 127 | ||||
-rw-r--r-- | themes/xtemplate/xtemplate.inc | 517 | ||||
-rw-r--r-- | themes/xtemplate/xtemplate.theme | 140 | ||||
-rw-r--r-- | themes/xtemplate/xtemplate.xtmpl | 70 |
5 files changed, 854 insertions, 0 deletions
diff --git a/themes/xtemplate/images/druplicon.gif b/themes/xtemplate/images/druplicon.gif Binary files differnew file mode 100644 index 000000000..ab6e59c3c --- /dev/null +++ b/themes/xtemplate/images/druplicon.gif diff --git a/themes/xtemplate/xtemplate.css b/themes/xtemplate/xtemplate.css new file mode 100644 index 000000000..e1cf7fd5a --- /dev/null +++ b/themes/xtemplate/xtemplate.css @@ -0,0 +1,127 @@ +body { + margin: 0px; + padding: 0px; + color: #000000; + background-color: #fff; + font-family: verdana, arial, helvetica, sans-serif; +} +body, td, tr { + font-size: 90%; +} +a { + text-decoration: none; + font-weight: normal; +} +a:link { + color: #378CE0; +} +a:visited { + color: #036; +} +a:hover { + text-decoration: underline; +} +p { + margin: 0 0 1em 0; + padding: 0; +} +img { + border-width: 0; +} +table #menu { + background-color: #69c; + padding: 5px 5px 0px 5px; +} +#menu logo { + vertical-align: bottom; + float: left; +} +#menu #links { + text-align: right; + float: right; + color: #9cf; +} +#menu #links #primary { + font-size: 1.1em; +} +#menu #links #primary a { + color: #fff; + font-weight: bold; +} +#menu #links #secundary { + font-size: 0.7em; +} +#menu #links #secundary a { + color: #9cf; + font-weight: bold; +} +#message { + background-color: #369; + padding: 10px 10px 10px 10px; + color: #fff; +} +#main { + margin: 10px 10px 10px 10px; + padding: 15px 15px 0px 15px; +} +#sidebar { + background-color: #ddd; +} +table #node { + padding-bottom: 25px; +} +table #block { + padding: 15px 15px 15px 15px; + margin: 5px 0px 5px 0px; +} +table #comment { + border: 1px solid #bbb; + padding: 15px 15px 15px 15px; + margin: 5px 0px 5px 0px; +} +#node #title, #block #title, #node #title a { + padding-bottom: 5px; + font-weight: bold; + font-size: 1.2em; + color: #888; +} +#node #author, #comment #author { + color: #999; + font-size: 0.8em; + padding-bottom: 10px; +} +#node #taxonomy { + color: #999; + font-size: 0.8em; + padding: 15px; +} +#node #taxonomy a { + color: #369; +} +#node #content, #comment #content { + padding-top: 5px; +} +#node #links, #comment #links { + padding-top: 10px; + color: #999; +} +#node #links a, #comment #links a { + font-weight: bold; + color: #369; +} +#comment #title { + font-weight: bold; + font-size: 1.1em; + color: #888; +} +#comment #new { + text-align: right; + font-weight: bold; + font-size: 0.7em; + float: right; + color: red; +} +#block #content { + font-size: 0.9em/1.1em; +} + diff --git a/themes/xtemplate/xtemplate.inc b/themes/xtemplate/xtemplate.inc new file mode 100644 index 000000000..363943ac6 --- /dev/null +++ b/themes/xtemplate/xtemplate.inc @@ -0,0 +1,517 @@ +<? + +class XTemplate { + +/* + xtemplate class 0.3pre + html generation with templates - fast & easy + copyright (c) 2000-2001 Barnabás Debreceni [cranx@users.sourceforge.net] + + contributors: + Ivar Smolin <okul@linux.ee> (14-march-2001) + - made some code optimizations + Bert Jandehoop <bert.jandehoop@users.info.wau.nl> (26-june-2001) + - new feature to substitute template files by other templates + - new method array_loop() + + !!! {FILE {VAR}} file variable interpolation may still be buggy !!! + + latest stable & CVS versions always available @ + http://sourceforge.net/projects/xtpl/ + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + version 2.1 as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details at + http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + $Id$ + +*/ + +/***[ variables ]***********************************************************/ + +var $filecontents=""; /* raw contents of template file */ +var $blocks=array(); /* unparsed blocks */ +var $parsed_blocks=array(); /* parsed blocks */ +var $preparsed_blocks=array(); /* preparsed blocks, for file includes */ +var $block_parse_order=array(); /* block parsing order for recursive parsing (sometimes reverse:) */ +var $sub_blocks=array(); /* store sub-block names for fast resetting */ +var $VARS=array(); /* variables array */ +var $FILEVARS=array(); /* file variables array */ +var $filevar_parent=array(); /* filevars' parent block */ +var $filecache=array(); /* file caching */ + +var $tpldir=""; /* location of template files */ +var $FILES=null; /* file names lookup table */ + +var $file_delim="/\{FILE\s*\"([^\"]+)\"\s*\}/m"; /* regexp for file includes */ +var $filevar_delim="/\{FILE\s*\{([A-Za-z0-9\._]+?)\}\s*\}/m"; /* regexp for file includes */ +var $filevar_delim_nl="/^\s*\{FILE\s*\{([A-Za-z0-9\._]+?)\}\s*\}\s*\n/m"; /* regexp for file includes w/ newlines */ +var $block_start_delim="<!-- "; /* block start delimiter */ +var $block_end_delim="-->"; /* block end delimiter */ +var $block_start_word="BEGIN:"; /* block start word */ +var $block_end_word="END:"; /* block end word */ + +/* this makes the delimiters look like: <!-- BEGIN: block_name --> if you use my syntax. */ + +var $NULL_STRING=array(""=>""); /* null string for unassigned vars */ +var $NULL_BLOCK=array(""=>""); /* null string for unassigned blocks */ +var $mainblock="main"; +var $ERROR=""; +var $AUTORESET=1; /* auto-reset sub blocks */ + +/***[ constructor ]*********************************************************/ + + +function XTemplate ($file,$tpldir="",$files=null,$mainblock="main") { + $this->tpldir = $tpldir; + if (gettype($files)=="array") + $this->FILES = $files; + $this->mainblock=$mainblock; + $this->filecontents=$this->r_getfile($file); /* read in template file */ + $this->blocks=$this->maketree($this->filecontents,""); /* preprocess some stuff */ + $this->filevar_parent=$this->store_filevar_parents($this->blocks); + $this->scan_globals(); +} + + +/***************************************************************************/ +/***[ public stuff ]********************************************************/ +/***************************************************************************/ + +/***[ assign ]**************************************************************/ +/* + assign a variable +*/ + +function assign ($name,$val="") { + if (gettype($name)=="array") + foreach ($name as $k=>$v) + $this->VARS[$k]=$v; + else + $this->VARS[$name]=$val; +} + +/***[ assign_file ]*********************************************************/ +/* + assign a file variable +*/ + +function assign_file ($name,$val="") { + if (gettype($name)=="array") + foreach ($name as $k=>$v) + $this->assign_file_($k,$v); + else + $this->assign_file_($name,$val); +} + +function assign_file_ ($name,$val) { + if (isset($this->filevar_parent[$name])) { + if ($val!="") { + $val=$this->r_getfile($val); + foreach($this->filevar_parent[$name] as $parent) { + if (isset($this->preparsed_blocks[$parent]) and !isset($this->FILEVARS[$name])) + $copy=$this->preparsed_blocks[$parent]; + else if (isset($this->blocks[$parent])) + $copy=$this->blocks[$parent]; + preg_match_all($this->filevar_delim,$copy,$res,PREG_SET_ORDER); + foreach ($res as $v) { + $copy=preg_replace("/".preg_quote($v[0])."/","$val",$copy); + $this->preparsed_blocks=array_merge($this->preparsed_blocks,$this->maketree($copy,$parent)); + $this->filevar_parent=array_merge($this->filevar_parent,$this->store_filevar_parents($this->preparsed_blocks)); + } + } + } + } + $this->FILEVARS[$name]=$val; +} + +/***[ parse ]***************************************************************/ +/* + parse a block +*/ + +function parse ($bname) { + + if (isset($this->preparsed_blocks[$bname])) { + $copy=$this->preparsed_blocks[$bname]; + } + else if (isset($this->blocks[$bname])) + $copy=$this->blocks[$bname]; + else + $this->set_error ("parse: blockname [$bname] does not exist"); + + /* from there we should have no more {FILE } directives */ + $copy=preg_replace($this->filevar_delim_nl,"",$copy); + + /* find & replace variables+blocks */ + preg_match_all("/\{([A-Za-z0-9\._]+?)}/",$copy,$var_array); + $var_array=$var_array[1]; + foreach ($var_array as $k=>$v) { + $sub=explode(".",$v); + if ($sub[0]=="_BLOCK_") { + unset($sub[0]); + $bname2=implode(".",$sub); + $var=$this->parsed_blocks[$bname2]; + $nul=(!isset($this->NULL_BLOCK[$bname2])) ? $this->NULL_BLOCK[""] : $this->NULL_BLOCK[$bname2]; + if ($var=="") { + if ($nul=="") { + $copy=preg_replace("/^\s*\{".$v."\}\s*\n*/m","",$copy); + } else { + $copy=preg_replace("/\{".$v."\}/","$nul",$copy); + } + } else { + $var=trim($var); + $copy=preg_replace("/\{".$v."\}/","$var",$copy); + } + } else { + $var=$this->VARS; + foreach ($sub as $v1) + $var=$var[$v1]; + $nul=(!isset($this->NULL_STRING[$v])) ? ($this->NULL_STRING[""]) : ($this->NULL_STRING[$v]); + $var=(!isset($var))?$nul:$var; + if ($var=="") + $copy=preg_replace("/^\s*\{".$v."\}\s*\n/m","",$copy); + $copy=preg_replace("/\{".$v."\}/","$var",$copy); + } + } + $this->parsed_blocks[$bname].=$copy; + + /* reset sub-blocks */ + if ($this->AUTORESET && (!empty($this->sub_blocks[$bname]))) { + reset($this->sub_blocks[$bname]); + foreach ($this->sub_blocks[$bname] as $k=>$v) + $this->reset($v); + } +} + +/***[ rparse ]**************************************************************/ +/* + returns the parsed text for a block, including all sub-blocks. +*/ + +function rparse($bname) { + if (!empty($this->sub_blocks[$bname])) { + reset($this->sub_blocks[$bname]); + foreach ($this->sub_blocks[$bname] as $k=>$v) + if (!empty($v)) + $this->rparse($v); + } + $this->parse($bname); +} + +/***[ insert_loop ]*********************************************************/ +/* + inserts a loop ( call assign & parse ) +*/ + +function insert_loop($bname,$var,$value="") { + $this->assign($var,$value); + $this->parse($bname); +} + +/***[ array_loop ]*********************************************************/ +/* + parses a block for every set of data in the values array +*/ + +function array_loop($bname, $var, &$values) +{ + if (gettype($values)=="array") + { + foreach($values as $v) + { + $this->assign($var, $v); + $this->parse($bname); + } + } +} + +/***[ text ]****************************************************************/ +/* + returns the parsed text for a block +*/ + +function text($bname) { + return $this->parsed_blocks[isset($bname) ? $bname :$this->mainblock]; +} + +/***[ out ]*****************************************************************/ +/* + prints the parsed text +*/ + +function out ($bname) { + $length=strlen($this->text($bname)); + header("Content-Length: ".$length); + echo $this->text($bname); +} + +/***[ reset ]***************************************************************/ +/* + resets the parsed text +*/ + +function reset ($bname) { + $this->parsed_blocks[$bname]=""; +} + +/***[ parsed ]**************************************************************/ +/* + returns true if block was parsed, false if not +*/ + +function parsed ($bname) { + return (!empty($this->parsed_blocks[$bname])); +} + +/***[ SetNullString ]*******************************************************/ +/* + sets the string to replace in case the var was not assigned +*/ + +function SetNullString($str,$varname="") { + $this->NULL_STRING[$varname]=$str; +} + +/***[ SetNullBlock ]********************************************************/ +/* + sets the string to replace in case the block was not parsed +*/ + +function SetNullBlock($str,$bname="") { + $this->NULL_BLOCK[$bname]=$str; +} + +/***[ set_autoreset ]*******************************************************/ +/* + sets AUTORESET to 1. (default is 1) + if set to 1, parse() automatically resets the parsed blocks' sub blocks + (for multiple level blocks) +*/ + +function set_autoreset() { + $this->AUTORESET=1; +} + +/***[ clear_autoreset ]*****************************************************/ +/* + sets AUTORESET to 0. (default is 1) + if set to 1, parse() automatically resets the parsed blocks' sub blocks + (for multiple level blocks) +*/ + +function clear_autoreset() { + $this->AUTORESET=0; +} + +/***[ scan_globals ]********************************************************/ +/* + scans global variables +*/ + +function scan_globals() { + reset($GLOBALS); + foreach ($GLOBALS as $k=>$v) + $GLOB[$k]=$v; + $this->assign("PHP",$GLOB); /* access global variables as {PHP.HTTP_HOST} in your template! */ +} + +/****** + + WARNING + PUBLIC FUNCTIONS BELOW THIS LINE DIDN'T GET TESTED + +******/ + + +/***************************************************************************/ +/***[ private stuff ]*******************************************************/ +/***************************************************************************/ + +/***[ maketree ]************************************************************/ +/* + generates the array containing to-be-parsed stuff: + $blocks["main"],$blocks["main.table"],$blocks["main.table.row"], etc. + also builds the reverse parse order. +*/ + + +function maketree($con,$parentblock="") { + $blocks=array(); + $con2=explode($this->block_start_delim,$con); + if (!empty($parentblock)) { + $block_names=explode(".",$parentblock); + $level=sizeof($block_names); + } else { + $block_names=array(); + $level=0; + } + foreach ($con2 as $k=>$v) { + $patt="($this->block_start_word|$this->block_end_word)\s*(\w+)\s*$this->block_end_delim(.*)"; + if (preg_match_all("/$patt/ims",$v,$res,PREG_SET_ORDER)) { + // $res[0][1] = BEGIN or END + // $res[0][2] = block name + // $res[0][3] = kinda content + if ($res[0][1]==$this->block_start_word) { + $parent_name=implode(".",$block_names); + $block_names[++$level]=$res[0][2]; /* add one level - array("main","table","row")*/ + $cur_block_name=implode(".",$block_names); /* make block name (main.table.row) */ + $this->block_parse_order[]=$cur_block_name; /* build block parsing order (reverse) */ + $blocks[$cur_block_name].=$res[0][3]; /* add contents */ + $blocks[$parent_name].="{_BLOCK_.$cur_block_name}"; /* add {_BLOCK_.blockname} string to parent block */ + $this->sub_blocks[$parent_name][]=$cur_block_name; /* store sub block names for autoresetting and recursive parsing */ + $this->sub_blocks[$cur_block_name][]=""; /* store sub block names for autoresetting */ + } else if ($res[0][1]==$this->block_end_word) { + unset($block_names[$level--]); + $parent_name=implode(".",$block_names); + $blocks[$parent_name].=$res[0][3]; /* add rest of block to parent block */ + } + } else { /* no block delimiters found */ + if ($k) + $blocks[implode(".",$block_names)].=$this->block_start_delim; + $blocks[implode(".",$block_names)].=$v; + } + } + return $blocks; +} + +/***[ store_filevar_parents ]***********************************************/ +/* + store container block's name for file variables +*/ + +function store_filevar_parents($blocks){ + $parents=array(); + foreach ($blocks as $bname=>$con) { + preg_match_all($this->filevar_delim,$con,$res); + foreach ($res[1] as $k=>$v) + $parents[$v][]=$bname; + } + return $parents; +} + +/***[ error stuff ]*********************************************************/ +/* + sets and gets error +*/ + +function get_error() { + return ($this->ERROR=="")?0:$this->ERROR; +} + + +function set_error($str) { + $this->ERROR="<b>[XTemplate]</b> <i>".$str."</i>"; + trigger_error($this->get_error()); +} + +/***[ getfile ]*************************************************************/ +/* + returns the contents of a file +*/ + +function getfile($file) { + if (!isset($file)) { + $this->set_error("!isset file name!"); + return ""; + } + + // check if filename is mapped to other filename + if (isset($this->FILES)) + { + if (isset($this->FILES[$file])) + $file = $this->FILES[$file]; + } + // prepend template dir + if (!empty($this->tpldir)) + $file = $this->tpldir."/".$file; + + if (isset($this->filecache[$file])) + $file_text=$this->filecache[$file]; + else { + if (is_file($file)) { + if (!($fh=fopen($file,"r"))) { + $this->set_error("Cannot open file: $file"); + return ""; + } + + $file_text=fread($fh,filesize($file)); + fclose($fh); + } else { + $this->set_error("[$file] does not exist"); + $file_text="<b>__XTemplate fatal error: file [$file] does not exist__</b>"; + } + $this->filecache[$file]=$file_text; + } + return $file_text; +} + +/***[ r_getfile ]***********************************************************/ +/* + recursively gets the content of a file with {FILE "filename.tpl"} directives +*/ + + +function r_getfile($file) { + $text=$this->getfile($file); + while (preg_match($this->file_delim,$text,$res)) { + $text2=$this->getfile($res[1]); + $text=preg_replace("'".preg_quote($res[0])."'",$text2,$text); + } + + return $text; +} + + +} /* end of XTemplate class. */ + +/* + $Log$ + Revision 1.1 2003/01/20 21:00:31 dries + + - Added a template driven theme. + + Revision 1.1 2002/12/10 23:11:59 ax + [argh - cvs @*#! didn't add any files with the last commit ... now, then (we should really give subversion a try: "Directories, renames, and file meta-data are versioned"] + + - renamed from "ax" to "XTemplate_Tableless" to make people looking for ... this find it + - updated to CVS + - fixed some styles + - em/%'ized stylesheet (relative sizes) and "chaptered" into sections + - added 3 screenshots + + Revision 1.2 2001/09/19 14:11:25 cranx + fixed a bug in the whitespace-stripping block variable interpolating regexp. + + Revision 1.1 2001/07/11 10:42:39 cranx + added: + - filename substitution, no nested arrays for the moment, sorry + (including happens when assigning, so assign filevar in the outside blocks first!) + + Revision 1.5 2001/07/11 10:39:08 cranx + added: + - we can now specify base dir + - array_loop() + - trigger_error in set_error + + modified: + - newline bugs fixed (for XML) + - in out(): content-length header added + - whiles changed to foreach + - from now on, the class is php4 only :P + + +*/ + + +?> diff --git a/themes/xtemplate/xtemplate.theme b/themes/xtemplate/xtemplate.theme new file mode 100644 index 000000000..15a3e82b4 --- /dev/null +++ b/themes/xtemplate/xtemplate.theme @@ -0,0 +1,140 @@ +<?php + +class Theme_xtemplate extends BaseTheme { + + var $primary_links = "edit me"; + var $secundary_links = "edit me"; + var $message = "edit me"; + + function system($field) { + $system["name"] = "xtemplate"; + + return $system[$field]; + } + + function Theme_xtemplate() { + include_once("themes/xtemplate/xtemplate.inc"); + + $this->template = new XTemplate("themes/xtemplate/xtemplate.xtmpl"); + $this->template->SetNullBlock(" "); // "" doesnt work! + } + + function node($node, $main) { + $terms = array(); + if (function_exists("taxonomy_node_get_terms")) { + foreach (taxonomy_node_get_terms($node->nid) as $term) { + $terms[] = l($term->name, array("or" => $term->tid), "index"); + } + } + + $this->template->assign(array ( + "title" => ucfirst($node->title), + "taxonomy" => $this->links($terms), + "author" => format_name($node), + "date" => format_date($node->created), + "content" => ($main && $node->teaser) ? + check_output($node->teaser) : + check_output($node->body))); + + if ($links = link_node($node, $main)) { + $this->template->assign("links", $this->links($links)); + } + + $this->template->parse("node"); + print $this->template->text("node"); + $this->template->reset("node"); + } + + function comment($comment, $link = 0) { + $this->template->assign(array ( + "title" => ucfirst($comment->subject), + "author" => format_name($comment), + "date" => format_date($comment->timestamp), + "content" => check_output($comment->comment), + "links" => $link)); + + if ($comment->new) { + $this->template->parse("comment_new"); + print $this->template->text("comment_new"); + $this->template->reset("comment_new"); + } + else { + $this->template->parse("comment_old"); + print $this->template->text("comment_old"); + $this->template->reset("comment_old"); + } + } + + function header() { + $this->template->assign(array( + "name" => variable_get("site_name", ""), + "slogan" => variable_get("site_slogan", ""))); + + $this->template->parse("header"); + print $this->template->text("header"); + + ?> + <table border="0" cellpadding="0" cellspacing="0"> + <tr> + <td colspan="2" id="menu"> + <?php + + $this->template->assign(array( + "primary" => $this->primary_links, + "secundary" => $this->secundary_links)); + + $this->template->parse("menu"); + print $this->template->text("menu"); + + ?> + </td> + </tr> + <tr> + <td valign="top" width="100%"> + <?php + + // the description block is only shown on the main page + if (!arg(0)) { + $this->template->assign(array( + "message" => $this->message)); + + $this->template->parse("message"); + print $this->template->text("message"); + } + + ?> + <div id="main"> + <?php + } + + function box($title, $content, $region = "main") { + if ($title && $content) { + $this->template->assign(array( + "title" => $title, + "content" => $content)); + + $this->template->parse("block"); + print $this->template->text("block"); + $this->template->reset("block"); + } + } + + function footer() { + global $user; + + ?> + <td valign="top" rowspan="2" id="sidebar"> + <?php + theme_blocks("all", $this); + ?> + </div> + </td> + </tr> + </table> + <?php + + $this->template->parse("footer"); + print $this->template->text("footer"); + } +} +?> diff --git a/themes/xtemplate/xtemplate.xtmpl b/themes/xtemplate/xtemplate.xtmpl new file mode 100644 index 000000000..7dd8660aa --- /dev/null +++ b/themes/xtemplate/xtemplate.xtmpl @@ -0,0 +1,70 @@ +<!-- BEGIN: header --> +<?xml version=\"1.0\" encoding=\"iso-8859-1\"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> + <head> + <title>{name} - {slogan}</title> + <style type="text/css" media="all"> + @import "themes/xtemplate/xtemplate.css"; + </style> + </head> + <body> +<!-- END: header --> + +<!-- BEGIN: menu --> + <span id="links"> + <div id="secundary">{secundary}</div> + <div id="primary">{primary}</div> + </span> + <span id="logo"><a href="index.php"><img src="themes/xtemplate/images/druplicon.gif" alt="" /></a></span> +<!-- END: menu --> + + +<!-- BEGIN: message --> + <div id="message">{message}</div> +<!-- END: message --> + + +<!-- BEGIN: node --> + <div id="node"> + <div id="title">{title}</div> + <span id="author">Submitted by {author} on {date}.</span> + <span id="taxonomy">{taxonomy}</span> + <div id="content">{content}</div> + <div id="links">» {links}</div> + </div> +<!-- END: node --> + +<!-- BEGIN: comment_new --> + <div id="comment"> + <span id="new">new</span> + <div id="title">{title}</div> + <div id="author">Posted by {author} on {date}.</div> + <div id="content">{content}</div> + <div id="links">» {links}</div> + </div> +<!-- END: comment_new --> + +<!-- BEGIN: comment_old --> + <div id="comment"> + <div id="title">{title}</div> + <div id="author">Posted by {author} on {date}.</div> + <div id="content">{content}</div> + <div id="links">» {links}</div> + </div> +<!-- END: comment_old --> + + +<!-- BEGIN: block --> + <div id="block"> + <div id="title">{title}</div> + <div id="content">{content}</div> + </div> +<!-- END: block --> + + +<!-- BEGIN: footer --> + </body> +</html> +<!-- END: footer --> |