. ** ** 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, configure(), 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) 'head' => '', # Code to put into head() section 'htmltype' => 'xhtml', # '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), XXX Copy and paste in configure() to keep PHP4 compatibility 'nonewlinetags' => 'a,b,em,img,input,span', # tags that do not like newlines after them 'notexported' => 'configure,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->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; } /** * Modify configuration of it_html, e.g. htmltype after it was instantiated. * @param $p Configuration settings. Can be set/overridden in constructor, configure(), html() or head(). * See constructor for a list of supported values */ function configure($p) { $p += array('name' => "it_html"); # XXX Copy and paste from constructor to keep PHP4 compatibility $GLOBALS[$p['name']]->p = $p + (array)$GLOBALS[$p['name']]->p; } /** * 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 .= $p['head'] . 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) { if ($this->p['use_it_state']) array_unshift($args, tag('iframe', array('id' => "it_state", 'src' => "/itjs/state.html", 'width' => 1, 'height' => 1, 'frameborder' => 0))); if ($this->p['show_boot_dom']) $args[] = div(array('id' => "it_boot_dom", 'style' => "visibility:hidden")); return $this->head() . $this->_tag('body', $args); } /** * 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, ' instead of for old html, for xhtml style) foreach($attr as $key => $value) { if (($value === null) || ($value === false)) # null or false: omit whole tag ; else if (isset($value) && $value !== true) # normal case: value $result .= " $key=\"" . (preg_match("/[<>&\"'\n\x80-\x9f]/", $value) ? str_replace("\n", " ", Q($value)) : $value) . '"'; else # true: tag without value $result .= ($this->p['htmltype'] == 'html') ? " $key" : " $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$newline"; elseif ($this->p['htmltype'] == 'html') $result .= ">$newline"; else $result .= " />$newline"; if ($GLOBALS['debug_srclines']) { $trace = debug_backtrace(); $trace = $trace[2]; $result = "" . $result; } return $result; } /** * Return a 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($args) { $name = array_shift($args); return $this->_tag($name, $args); } /** * Special img() function patches png transparency for IE 5.5-6 if ie_png_fix is set * @param ... any number optional data or array of key => value arguments * @return */ function img($args) { if ($this->p['ie_png_fix'] && preg_match('/MSIE [56]/', $_SERVER['HTTP_USER_AGENT'])) { foreach($args as $id => $arg) if (preg_match('/\.png(\?.*)?$/', $arg['src'])) { $args[$id]['style'] = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='{$arg['src']}',sizingMethod='scale');" . $arg['style']; $args[$id]['src'] = $this->p['ie_png_fix']; } } return $this->_tag("img", $args); } /** * Create a dropdown menu object. Warning: encodes html code within options! * @param $tags key => value pairs of