summaryrefslogtreecommitdiff
path: root/html.class
diff options
context:
space:
mode:
Diffstat (limited to 'html.class')
-rw-r--r--html.class516
1 files changed, 516 insertions, 0 deletions
diff --git a/html.class b/html.class
new file mode 100644
index 0000000..a437d54
--- /dev/null
+++ b/html.class
@@ -0,0 +1,516 @@
+<?php
+/*
+** $Id$
+**
+** ITools - the Internet Tools Library
+**
+** Copyright (C) 1995-2003 by the ITools Authors.
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of either the GNU General Public License
+** or the GNU Lesser General Public License, as published by the Free
+** Software Foundation. See http://www.gnu.org/licenses/ for details.
+**
+** UltraHTML 3000 tool layer. Create functions for html tags.
+**
+** new it_html;
+** echo h1('hello.php') . div('contents', 'Hello world!');
+**/
+
+class it_html
+{
+ # Default configuration of html class
+ var $_defaultconfig = array
+ (
+ 'name' => 'it_html', # Name of global variable to use
+ 'oldhtml' => false,
+ 'prettyprint' => false,
+ 'tags' => 'a,br,form,h1,h2,h3,h4,img,input,li,meta,table,td,th,tr,ul',
+ 'moretags' => '',
+ 'nonewlinetags' => 'a,img,span',
+ 'preprocess_attr' => array(),
+ 'charset' => "iso-8859-1",
+ 'show_content_type' => true,
+ 'show_favicon' => true,
+ 'show_boot_dom' => true,
+ );
+ var $tags_seen = array('body' => true); # body always counts as seen
+
+/**
+ * Create a HTML object and global functions for all methods (exlcluding
+ * methods starting with '_') in this class plus the default tags (see below).
+ *
+ * @param $config Configuration settings:
+ * name => Name of global variable $this is assigned to (string)
+ * prettyprint => Should output be indented? (bool)
+ * oldhtml => Should output be in old-style html? (bool)
+ * tags => Comma-separated list of default tag-functions to generate (string)
+ * moretags => Comma-separated list of tag-functions to generate additionally to 'tags' (string)
+ * nonewlinetags => Comma-separated list of tags that do not like newlines before/after them
+ * use_it_state => If true, generate code needed by state.js (aka 'history iframe')
+ */
+function it_html($config = array())
+{
+ # Create current setting vars
+ foreach ($config + $this->_defaultconfig as $key => $value)
+ {
+ $var = "_$key";
+ $this->$var = $value;
+ }
+
+ if (isset($this->_oldhtml) && !$this->_htmltype)
+ $this->_htmltype = $this->_oldhtml ? "html" : "xhtml";
+ $this->_oldhtml = $this->_htmltype == "html";
+
+ # Create global functions from all methods and names in $config['tags'] and $config['moretags']
+ $methods = get_class_methods(get_class($this));
+ $funcs = array_unique(array_merge($methods, explode(',', $this->_tags), explode(',', $this->_moretags)));
+ $nonewlinetags = explode(',', $this->_nonewlinetags);
+
+ foreach($funcs as $func)
+ {
+ # Do not globalise 'private' functions starting in '_' or for our constructor
+ if (preg_match('/^_/', $func) || is_a($this, $func) || empty($func))
+ continue;
+
+ $funcargs = array();
+ if (in_array($func, $nonewlinetags))
+ $funcargs += array('nonewline' => true);
+
+ $funcconfig = $funcargs ? 'array_push($args, '.strtr(var_export($funcargs, true), array("\n" => '')).'); ' : '';
+
+ $code = "function $func() { \$args = func_get_args(); $funcconfig";
+ if (!in_array($func, $methods)) # No special case: generate call to tag()
+ {
+ $code .= "array_unshift(\$args, '$func');";
+ $func = 'tag';
+ }
+
+ $code .= "return call_user_func_array(array(&\$GLOBALS['$this->_name'], '$func'), \$args); }";
+
+ debug(get_class($this).': code: '.$code, 8);
+ eval($code);
+ }
+
+ # Since name is given as param, it is our duty to store it, not our caller's.
+ $GLOBALS[$this->_name] =& $this;
+}
+
+
+/**
+ * Return a <tag> containing optional data.
+ * @param $name tag name ('h1', 'div' etc.)
+ * @param ... any number optional data or array of key => value arguments
+ * @return string containing XML/HTML tag
+ */
+function tag(/* $name, ... */)
+{
+ $args = func_get_args();
+ $name = array_shift($args);
+
+ $data = null;
+ $attr = array();
+
+ foreach($args as $arg)
+ {
+ if (is_array($arg))
+ {
+ foreach ($arg as $key => $value)
+ {
+ if (is_int($key))
+ $data .= $value;
+ else if ($this->_preprocess_attr[$key])
+ $attr[$key] = call_user_func(($this->_preprocess_attr[$key]), $value, $name);
+ else
+ $attr[$key] = $value;
+ }
+ }
+ else
+ $data .= $arg;
+ }
+
+ $newline = empty($attr['nonewline']) ? "\n" : "";
+ unset($attr['nonewline']);
+
+ # Ultra XML PrettyPrinter 3000 [\] by SCA
+ if ($this->_prettyprint && $newline && (substr($data, -1, 1) == "\n") && (strpos($data, '<textarea') === false) && ($data != strip_tags($data)))
+ $data = str_replace("\n", "\n ", "\n".trim($data))."\n";
+
+ $result = "<$name";
+
+ # add attributes. If $value === null, use key only (<td nowrap> instead of <td nowrap=""> for old html, <td nowrap="nowrap"> for xhtml style)
+ foreach($attr as $key => $value)
+ {
+ if ($value === false) # omit whole tag
+ ;
+ else if (isset($value) && $value !== true)
+ $result .= " $key=\"" . it_html::Q($value) . '"';
+ else if ($this->_oldhtml)
+ $result .= " $key";
+ else
+ $result .= " $key=\"$key\"";
+ }
+
+ # Apply a kind of magic... this needs further investigation
+ if (isset($data) || preg_match('/^(a|div|iframe|script|span|td|textarea)$/i', $name))
+ $result .= ">$data</$name>$newline";
+ elseif ($this->_oldhtml)
+ $result .= ">$newline";
+ else
+ $result .= " />$newline";
+
+ if (EDC('srclines'))
+ {
+ $trace = debug_backtrace();
+ $trace = $trace[2];
+ $result = "<!-- " . basename($trace['file']) . ":" . $trace['line'] . " -->" . $result;
+ }
+
+ $this->tags_seen[$name] = true;
+
+ return $result;
+}
+
+
+/**
+ * Shortcut: return a div of a specific class
+ * @param $class class name or null for no class= tag
+ * @param ... any number optional data or array of key => value arguments
+ * @return <div class="$class"...>...</div>
+ */
+function div(/* $class, ... */)
+{
+ $args = func_get_args();
+
+ if (!is_array($args[0]) && ($class = array_shift($args)) !== null)
+ array_unshift($args, compact('class'));
+
+ array_unshift($args, 'div');
+ return call_user_func_array(array(&$this, 'tag'), $args);
+}
+
+
+/**
+ * Shortcut: return a span of a specific class
+ * @param $class class name or null for no class= tag
+ * @param ... any number optional data or array of key => value arguments
+ * @return <span class="$class"...>...</span>
+ */
+function span(/* $class, ... */)
+{
+ $args = func_get_args();
+
+ if (!is_array($args[0]) && ($class = array_shift($args)) !== null)
+ array_unshift($args, compact('class'));
+
+ array_unshift($args, 'span');
+ return call_user_func_array(array(&$this, 'tag'), $args);
+}
+
+
+/**
+ * Shortcut: return htmlspecialchars($string);
+ * @param $string String to encode with htmlspecialchars()
+ * @return htmlspecialchars($string)
+ */
+function Q($string)
+{
+ if ($GLOBALS['it_html']->_charset == "iso-8859-1")
+ $string = preg_replace('/[\x80-\x9f]/', ' ', strtr($string, array("\x80"=>"EUR", "\x82"=>"'", "\x84"=>"\"", "\x85"=>"...", "\x8a"=>"S", "\x8c"=>"OE", "\x8e"=>"Z", "\x91"=>"'", "\x92"=>"'", "\x93"=>"\"", "\x94"=>"\"", "\x96"=>"-", "\x97"=>"-", "\x9a"=>"s", "\x9e"=>"z")));
+
+ return str_replace("\n", "&#10;", htmlspecialchars($string));
+}
+
+
+/**
+ * Build a complete url from base-url and params
+ * @param ... scalar args and numeric indices build base-url, rest as params
+ */
+function U(/* ... */)
+{
+ $args = func_get_args();
+ $base = null;
+ $params = array();
+
+ foreach($args as $arg)
+ {
+ if (is_array($arg))
+ {
+ foreach ($arg as $key => $value)
+ {
+ if (is_int($key))
+ $base .= $value;
+ else
+ $params[$key] = $value;
+ }
+ }
+ else
+ $base .= $arg;
+ }
+
+ if (!isset($base))
+ $base = $_SERVER['PHP_SELF'];
+
+ $base = preg_replace('|\0|', '', $base);
+ $base = preg_replace('|[^\w.+!*(),:@&=/~$-]|e', 'urlencode("$0")', $base);
+ $base = preg_replace('|^(\w+:)?//[^/]*$|', '$0/', $base); # Add slash if absolute url without a path, e.g. http://gna.ch
+ $queryparams = array();
+
+ foreach ($params as $key => $value)
+ {
+ if (is_array($value))
+ {
+ foreach ($value as $arrkey => $arrvalue)
+ {
+ if (strlen($arrvalue))
+ $queryparams[] = urlencode($key) . "[" . urlencode($arrkey) . "]=" . it_url::encode($arrvalue);
+ }
+ }
+ else if (strlen($value))
+ $queryparams[] = urlencode($key) . "=" . it_url::encode($value);
+ }
+
+ return $base . ($queryparams ? ("?" . join("&", $queryparams)) : "");
+}
+
+
+/**
+ * Create a dropdown menu object
+ * @param $tags key => value pairs of <select> tag
+ * @param $options array (value => text) of available options or
+ * string key:val{,key:val} where key will be rawurldecoded so it may contain %2C as comma
+ * @param $selected optional currently selected value
+ */
+function select($tags, $options, $selected = '')
+{
+ # Transmogrify key:val{,key:val} to array(key => val)
+ if (!is_array($options))
+ {
+ $opts = explode(',', $options);
+ $options = array();
+
+ foreach($opts as $opt)
+ {
+ list($key, $value) = explode(':', $opt);
+ $options[rawurldecode($key)] = $value;
+ }
+ }
+
+ $html = '';
+ foreach($options as $value => $text)
+ $html .= '<option value="'.it_html::Q($value).'"'.(($value == $selected) ? ($this->_oldhtml ? ' selected' : ' selected="selected"') : '').'>'.(trim($text) === "" ? "&nbsp;" : it_html::Q($text))."</option>\n";
+
+ return $this->tag('select', $tags, $html);
+}
+
+
+/**
+ * Insert a javascript script
+ * @param ... any number optional data or array of key => value arguments
+ * @return <script type="text/javascript"...>...</script>
+ */
+function js(/* ... */)
+{
+ $args = func_get_args();
+
+ if (!$this->_oldhtml && isset($args[0]))
+ {
+ array_unshift($args, "<!--//--><![CDATA[//><!--\n");
+ $args[] = "\n//--><!]]>";
+ }
+
+ array_unshift($args, 'script', array('type' => 'text/javascript'));
+ return call_user_func_array(array(&$this, 'tag'), $args);
+}
+
+
+/**
+ * Include javascript code or generate HTML to include it
+ */
+function itjs(/* ... */)
+{
+ $result = "";
+ $files = array();
+ $p = array();
+
+ $args = func_get_args();
+
+ foreach ($args as $arg)
+ {
+ if (isset($arg))
+ {
+ if (!is_array($arg))
+ $files[] = $arg;
+ else
+ $p += $arg;
+ }
+ }
+
+ if ($files)
+ {
+ if ($p['mode'] == "files")
+ {
+ foreach ($files as $filelist)
+ {
+ foreach (itjs::filenames($filelist) as $file)
+ $result .= tag('script', array('type' => "text/javascript", 'src' => "/itjs/" . basename($file) . "?v=" . itjs::checksum($file)));
+ }
+ }
+ else if ($p['mode'] == "inline")
+ {
+ $jsfile = "";
+
+ foreach ($files as $filelist)
+ {
+ foreach (itjs::filenames($filelist) as $file)
+ $jsfile .= @file_get_contents($file);
+ }
+
+ $result .= itjs::strip($jsfile);
+ }
+ else
+ {
+ $filenames = array();
+
+ foreach ($files as $filelist)
+ $filenames = array_merge($filenames, itjs::filenames($filelist));
+
+ $result .= tag('script', array('type' => "text/javascript", 'src' => "/itjs/" . join(",", $files) . "?v=" . itjs::checksum($filenames)));
+ }
+ }
+
+ return $result;
+}
+
+
+/**
+ * Return HTML header with correct doctype.
+ *
+ * @param any number of text args or array of key => value:
+ * 'title' HTML title tag
+ * 'content-type' optional content type (default: "text/html; charset=iso-8859-1")
+ * 'description' optional data for <meta name="description"> tag
+ * 'doctype' optional <!DOCTYPE HTML PUBLIC...> tag
+ * 'keywords' optional data for <meta name="keywords"> tag
+ * 'lang' optional language (defaults to 'de')
+ * 'stylesheets' optional array mediatype => url of styleshests
+ */
+function head(/* ... */)
+{
+ if (!$this->head_sent++)
+ {
+ $args = func_get_args();
+ $p = array();
+
+ foreach ($args as $arg)
+ {
+ if (is_array($arg))
+ {
+ foreach ($arg as $key => $value)
+ {
+ if (is_int($key))
+ $data .= $value;
+ else
+ $p[$key] = $value;
+ }
+ }
+ else
+ $data .= $arg;
+ }
+
+ if ($this->_htmltype == "xhtml-mobile")
+ $defdoctype = '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">';
+ else if ($this->_htmltype == "xhtml")
+ $defdoctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
+ else
+ $defdoctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">';
+
+ $p += array
+ (
+ 'content-type' => "text/html; charset=$this->_charset",
+ 'doctype' => $defdoctype,
+ 'lang' => "de",
+ 'js' => $this->_js,
+ 'jsinline' => $this->_jsinline,
+ );
+
+ $header = $p['show_content_type'] ? meta(array('http-equiv' => "Content-Type", 'content' => $p['content-type'])) : "";
+ $header .= tag('title', it_html::Q($p['title']));
+
+ foreach(array('description', 'keywords') as $name)
+ if (!empty($p[$name]))
+ $header .= meta(array('name' => $name, 'content' => $p[$name]));
+
+ # Add favicon if file exists
+ if ($p['show_favicon'] && @file_exists($_SERVER['DOCUMENT_ROOT'].'/favicon.ico'))
+ $header .= tag('link', array('rel' => "shortcut icon", 'href' => "/favicon.ico"));
+
+ foreach((array)$p['stylesheets'] as $type => $url)
+ $header .= tag('link', array('rel' => "stylesheet", 'type' => "text/css", 'href' => $url) + (is_int($type) ? array() : array('media' => $type)));
+
+ if (!empty($p['cssinline']))
+ $header .= tag('style', array('type' => "text/css", "\n" . preg_replace(array('/\s*\/\*[^\*]+\*\//Um', '/\s*\{\s*/', '/;\s+/'), array('', '{', ';'), $p['cssinline'])));
+
+ if($this->_htmltype == "xhtml-mobile" && ereg('W3C_Validator', $_SERVER['HTTP_USER_AGENT']))
+ header("Content-Type: application/xhtml+xml; charset=$this->_charset"); # for validation
+ else if (!headers_sent()) # prevent warnings when ED() in use
+ header("Content-Type: " . $p['content-type']);
+
+ $langvar = $this->_htmltype == "xhtml-mobile" ? "xml:lang" : "lang";
+ $prefix = $GLOBALS['SENT_PREAMBLE'] ? "" : $p['doctype'] . "\n<html $langvar=\"" . it_html::Q($p['lang']) . "\"><head>";
+
+ $js = "";
+
+ if ($p['jsenv'])
+ $js .= "var env = " . itjs::serialize($p['jsenv']);
+
+ if ($p['js'])
+ {
+ $js .= $this->itjs("boot.js", array('mode' => "inline"));
+ $js .= "function it_boot_init(){ window.clearTimeout(window.it_panictimer); " . trim($p['jsboot']) . " }\n";
+ $js .= "it_boot('/itjs/" . $p['js'] . "');\n";
+ }
+
+ $js .= $this->itjs($p['jsinline'], array('mode' => 'inline'));
+
+ if ($js)
+ $header .= $this->js($js);
+
+ return "$prefix$header$data</head>";
+ }
+}
+
+function body(/* ... */)
+{
+ $args = func_get_args();
+
+ foreach ($args as $arg)
+ {
+ if (is_array($arg))
+ {
+ foreach ($arg as $key => $value)
+ {
+ if (is_int($key))
+ $body .= $value;
+ else
+ $attributes .= " $key=\"" . it_html::Q($value) . '"';
+ }
+ }
+ else
+ $body .= $arg;
+ }
+
+ if ($this->_use_it_state)
+ $body = tag('iframe', array('id' => "it_state", 'src' => "/itjs/state.html", 'width' => 1, 'height' => 1, 'frameborder' => 0)) . $body;
+
+ $body .= $this->_show_boot_dom ? div(array('id' => "it_boot_dom", 'style' => "visibility:hidden")) : "";
+
+ return $this->head() . ($this->in_body++ ? "" : "<body$attributes>$body");
+}
+
+function endhtml()
+{
+ return $this->head() . ($this->in_body ? "</body>" : "") . "</html>";
+}
+
+}
+?>