.
**
** UltraHTML 3000 tool layer. Create functions for html tags.
**
** new it_html;
** echo html(head('title' => 'hello'), body(h1('hello'), p('Hello world!')));
**/
class it_html
{
/**
* Create a HTML object and global functions for all methods (exlcluding
* methods starting with '_') in this class plus the default tags (see below).
*
* @param $p Configuration settings. Can be set/overridden in constructor, html() or head().
* See source code for a list of supported values
*/
function it_html($p = array())
{
# Default configuration of html class
$this->p = $p + array(
'charset' => 'iso-8859-1',
'doctype' => null, # Custom doctype (will usually be calculated from htmltype)
'htmltype' => 'html', # 'html' (=old-style), 'xhtml' or 'xhtml-mobile'
'lang' => 'de', # Language code to use in tag
'ie_png_fix' => false, # To enable, supply URL of a transparent gif (like /images/0.gif)
'moretags' => '', # Comma-separated list of tag-functions to generate additionally to 'tags'
'name' => 'it_html', # Name of global variable $this is assigned to (string)
'nonewlinetags' => 'a,b,em,img,input,span', # tags that do not like newlines after them
'notexported' => 'sanitize', # Those methods are not exported
'prettyprint' => false, # Should output be prettily indented?
'show_boot_dom' => false, # If true, append invisible
at the end of body
'show_content_type' => true, # If true, add header
'show_favicon' => true, # If true, add tag to /favicon.ico if it exists
'staticallycallable' => 'q,u,select', # Those methods are statically callable (have same arguments as global stubs) but are a bit slower
'use_it_state' => false, # If true, generate code needed by state.js (aka 'history iframe')
'tags' => 'a,b,br,button,div,em,form,h1,h2,h3,h4,h5,h6,hr,input,label,li,meta,p,span,table,td,textarea,th,tr,ul',
'title' => '', # HTML title (default: empty title)
);
# We know these doctypes. If you need something else, supply 'doctype' in p
$this->doctypes = array(
'html' => '',
'xhtml' => '',
'xhtml-mobile' => ''
);
$this->p['oldhtml'] = $this->p['htmltype'] == "html";
$this->hasnonewline = array_flip(explode(',', "dummy," . $this->p['nonewlinetags'])); # dummy keeps values > 0
$notexported = array_flip(explode(',', "dummy," . $this->p['notexported'])); # dummy keeps values > 0
# Create global functions for _tags
foreach (array_merge(explode(',', $this->p['tags']), explode(',', $this->p['moretags'])) as $func)
{
if (!function_exists($func) && $func)
$code[$func] = "function $func() { \$args = func_get_args(); return \$GLOBALS['{$this->p['name']}']->_tag('$func', \$args); }";
}
# Create global functions for it_html methods
foreach (get_class_methods(get_class($this)) as $func)
{
if (!preg_match('/^_/', $func) && !is_a($this, $func) && $func && !function_exists($func) && !$notexported[$func])
$code[$func] = "function $func() { \$args = func_get_args(); return \$GLOBALS['{$this->p['name']}']->$func(\$args); }";
}
# Create global functions for methods that are statically callable (have same arguments as global stubs)
foreach (explode(',', $this->p['staticallycallable']) as $func)
{
if ($func && !function_exists($func))
$code[$func] = "function $func() { \$args = func_get_args(); return call_user_func_array(array(&\$GLOBALS['{$this->p['name']}'], '$func'), \$args); }";
}
eval(join('', (array)$code));
# Since name is given as param, it is our duty to store it, not our caller's.
$GLOBALS[$this->p['name']] =& $this;
}
/**
* Return doctype and entire HTML page.
* Example application code to render page:
* echo html(head(...), body(...));
*
* @param any number of text args or array of key => value for $p (see constructor for list)
*/
function html($args)
{
list($data, $p) = $this->_parse_args($args);
$p += $this->p;
return ($p['doctype'] ? $p['doctype'] : $this->doctypes[$p['htmltype']]) . "\n" .
'\n" . $data . ($p['omit_endhtml'] ? '' : "\n");
}
/**
* Return HTML header on first call or empty string on subsequent calls
*
* @param any number of text args or array of key => value:
* 'content-type' optional content type (default: "text/html; charset=iso-8859-1")
* 'description' optional data for tag
* 'keywords' optional data for tag
* 'lang' optional language (defaults to 'de')
* 'stylesheets' optional array mediatype => url of styleshests
*/
function head($args = array())
{
if (!$this->head_sent++)
{
list($data, $p) = $this->_parse_args($args);
$p += $this->p;
$this->p = ($p += array('content-type' => "text/html; charset={$p['charset']}"));
$header = $p['show_content_type'] ? meta(array('http-equiv' => "Content-Type", 'content' => $p['content-type'])) : "";
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'])));
$header .= tag('title', Q($p['title']));
if($this->p['htmltype'] == "xhtml-mobile" && strpos($_SERVER['HTTP_USER_AGENT'], 'W3C_Validator'))
header("Content-Type: application/xhtml+xml; charset={$this->p['charset']}"); # for validation
else if (!headers_sent()) # prevent warnings when ED() in use
header("Content-Type: " . $p['content-type']);
$js = $p['jsenv'] ? "var env = " . itjs::serialize($p['jsenv']) . ";\n" : '';
if ($p['js'])
{
$checksum = itjs::checksum(itjs::filenames($p['js']));
$js .= $this->_itjs("boot.js", "inline");
$js .= "function it_boot_start(){ " . trim($p['jsboot']) . " }\n";
$js .= "it_boot('/itjs/" . U($p['js'], array('s' => $checksum)) . "');\n";
}
$js .= $this->_itjs($p['jsinline'], 'inline');
if ($js)
$header .= $this->js(array($js));
return tag('head', $header, $data);
}
}
/**
* Return HTML head (if not already sent) and body with it_state and it_boot magic code
*/
function body($args)
{
return $this->head() .
($this->p['use_it_state'] ? tag('iframe', array('id' => "it_state", 'src' => "/itjs/state.html", 'width' => 1, 'height' => 1, 'frameborder' => 0)) : '') .
$this->_tag('body', $args) .
($this->p['show_boot_dom'] ? div(array('id' => "it_boot_dom", 'style' => "visibility:hidden")) : '');
}
/**
* INTERNAL: Parse an arg array (mixed key=>value pairs and strings) and return it
* as array(0 => all numerical args concatenated, 1 => array(key=>value pairs)
*/
function _parse_args($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;
}
return array($data, $p);
}
/**
* INTERNAL: Create html tag from name and args array
*/
function _tag($name, $args)
{
list($data, $attr) = $this->_parse_args($args);
$newline = $this->hasnonewline[$name] ? "" : "\n";
# Ultra XML PrettyPrinter 3000 [\] by SCA
if ($this->p['prettyprint'] && $newline && (substr($data, -1, 1) == "\n") && (strpos($data, '