summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Schneider2008-11-07 15:33:07 +0000
committerChristian Schneider2008-11-07 15:33:07 +0000
commit2ef85ec182278544ed7a625a319b8b0ee4edbc74 (patch)
tree6b21c3b4e843c409fb08ba74f29080092c5cd060
parentf2ba0bb34e65be3097b957a144643feeb44f3931 (diff)
downloaditools-devel-it_html.tar.gz
itools-devel-it_html.tar.bz2
itools-devel-it_html.zip
First version of it_html based on objectsdevel-it_html
-rw-r--r--it_html.class157
-rw-r--r--it_q.class52
-rw-r--r--it_tag.class113
-rw-r--r--itjs.class3
-rw-r--r--itools.lib2
5 files changed, 247 insertions, 80 deletions
diff --git a/it_html.class b/it_html.class
index 75ebe9b..657a5f0 100644
--- a/it_html.class
+++ b/it_html.class
@@ -56,6 +56,8 @@ function it_html($p = array())
'tags' => 'a,b,br,button,div,em,fieldset,form,h1,h2,h3,h4,h5,h6,hr,input,label,legend,li,meta,noscript,p,span,style,table,td,textarea,th,tr,ul',
'title' => '', # HTML title (default: no title added)
'use_it_state' => false, # If true, generate code needed by state.js (aka 'history iframe')
+ 'autoquote' => false, # Automatically pass plain strings through Q()
+ 'reportquote' => 0, # Report plain strings with this error level (e.g. E_USER_NOTICE)
);
# We know these doctypes. If you need something else, supply 'doctype' in p
@@ -72,7 +74,7 @@ function it_html($p = array())
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); }";
+ $code[$func] = "function $func() { \$args = func_get_args(); return new it_tag('$func', \$args); }";
}
# Create global functions for it_html methods
@@ -119,13 +121,20 @@ function configure($p)
*/
function html($args)
{
+ $result = new it_tag('html');
+
list($data, $p) = $this->_parse_args($args);
$p += $this->p;
- $html = ($p['doctype'] ? $p['doctype'] : $this->doctypes[$p['htmltype']]) . "\n" .
- '<html ' . ($p['htmltype'] == "xhtml" ? 'xmlns="http://www.w3.org/1999/xhtml" ' : '') . ($p['htmltype'] == "xhtml-mobile" ? 'xml:lang' : 'lang') . "=\"{$p['lang']}\">\n" . $data . ($p['omit_endhtml'] ? '' : "</html>\n");
+ $result->prefix = EDC('upd') ? "" : (($p['doctype'] ? $p['doctype'] : $this->doctypes[$p['htmltype']]) . "\n");
+
+ if ($p['htmltype'] == "xhtml")
+ $result['xmlns'] = "http://www.w3.org/1999/xhtml";
+
+ $result[$p['htmltype'] == "xhtml-mobile" ? "xml:lang" : "lang"] = $p['lang'];
+ $result[] = $data;
- return EDC('upd') ? it::replace(array('<!DOCTYPE.*<head>' => ''), $html, array('singleline' => true)) : $html;
+ return $result;
}
@@ -142,30 +151,40 @@ function html($args)
*/
function head($args = array())
{
+ $result = array();
+
if (!$this->head_sent++)
{
+ $result = new it_tag('head');
+
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'])) : "";
+ if ($p['show_content_type'])
+ $result[] = new it_tag('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]));
+ $result[] = new it_tag('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"));
+ $result[] = new it_tag('link', array('rel' => "shortcut", '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)));
+ $result[] = new it_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'])));
+ $result[] = new it_tag('style', array('type' => "text/css", "\n" . preg_replace(array('/\s*\/\*[^\*]+\*\//Um', '/\s*\{\s*/', '/;\s+/'), array('', '{', ';'), $p['cssinline'])));
+
- $header .= $p['head'] . ($p['title'] ? tag('title', Q($p['title'])) : "");
+ if ($p['head'])
+ $result[] = $p['head'];
+
+ $result[] = new it_tag('title', new it_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
@@ -185,10 +204,13 @@ function head($args = array())
$js .= $this->_itjs($p['jsinline'], 'inline');
if ($js)
- $header .= $this->js(array($js));
+ $result[] = $this->js(array($js));
- return tag('head', $header, $data);
+ foreach ($data as $value)
+ $result[] = $value;
}
+
+ return $result;
}
@@ -197,13 +219,17 @@ function head($args = array())
*/
function body($args)
{
+ $result = $this->head();
+
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)));
+ array_unshift($args, new it_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"));
+ $args[] = new it_tag('div', array('id' => "it_boot_dom", 'style' => "visibility:hidden"));
+
+ $result[] = new it_tag('body', $args);
- return $this->head() . $this->_tag('body', $args);
+ return $result;
}
@@ -214,6 +240,8 @@ function body($args)
function _parse_args($args)
{
$p = array();
+ $data = array();
+
foreach ($args as $arg)
{
if (is_array($arg))
@@ -221,13 +249,13 @@ function _parse_args($args)
foreach ($arg as $key => $value)
{
if (is_int($key))
- $data .= it_taintcheck($value);
+ $data[] = $value;
else
$p[$key] = $value;
}
}
else
- $data .= it_taintcheck($arg);
+ $data[] = $arg;
}
return array($data, $p);
@@ -245,46 +273,6 @@ function _parse_args($args)
* @see _tag()
*/ #:}
-/**
- * INTERNAL: Create html tag from name and args array (the arguments of the parent function)
- */
-function _tag($name, $args)
-{
- list($data, $attr) = $this->_parse_args($args);
-
- $newline = isset($this->hasnonewline[$name]) ? "" : "\n";
-
- # Ultra XML PrettyPrinter 3000 [\] by SCA
- if ($this->p['prettyprint'] && $newline && (substr($data, -1, 1) == "\n") && (strpos($data, '<textarea') === false && strpos($data, '<pre') === false) && ($data != strip_tags($data)))
- $data = str_replace("\n", "\n ", "\n" . trim($data)) . "\n";
-
- # debugging aid: add backtrace
- if (($levels = intval($GLOBALS['debug_srclines'])) && !it::match('^(head|meta|title|script)', $name))
- $attr = array('title' => it_debug::backtrace(array('levels' => $levels, 'skipfiles' => "_html\\.class"))) + $attr;
-
- $result .= "<$name";
-
- # add attributes. If $value === true, 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 === 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", "&#10;", Q($value)) : it_untaint($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|pre|script|span|td|textarea)$/i', $name))
- $result .= ">$data</$name>$newline";
- elseif ($this->p['htmltype'] == 'html')
- $result .= ">$newline";
- else
- $result .= " />$newline";
-
- return $result;
-}
/**
@@ -296,7 +284,7 @@ function _tag($name, $args)
function tag($args)
{
$name = array_shift($args);
- return $this->_tag($name, $args);
+ return new it_tag($name, $args);
}
/**
@@ -309,14 +297,16 @@ 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);
+ return new it_tag("img", $args);
}
@@ -331,6 +321,8 @@ function img($args)
*/
function select($tags, $options, $selected = null)
{
+ $result = array();
+
# Transmogrify key:val{,key:val} to array(key => val)
if (!is_array($options))
{
@@ -351,16 +343,16 @@ function select($tags, $options, $selected = null)
{
if (is_array($option))
{
- $grouphtml = "";
+ $group = new it_tag('optgroup', array('label' => $value));
foreach($option as $optval => $opt)
- $grouphtml .= $this->_tag("option", array(array('value' => $optval, 'selected' => in_array((string)$optval, $selected)), Q($opt)));
- $html .= $this->_tag("optgroup", array(array('label' => $value, $grouphtml)));
+ $group[] = new it_tag("option", array(array('value' => $optval, 'selected' => in_array((string)$optval, $selected)), new it_q($opt)));
+ $result[] = $group;
}
else
- $html .= $this->_tag("option", array(array('value' => $value, 'selected' => in_array((string)$value, $selected), 'disabled' => $option === ""), (trim($option) === "") ? "&nbsp;" : Q($option)));
+ $result[] = new it_tag("option", array(array('value' => $value, 'selected' => in_array((string)$value, $selected), 'disabled' => $option === ""), (trim($option) === "") ? "&nbsp;" : new it_q($option)));
}
- return $this->_tag("select", array($tags, $html));
+ return new it_tag("select", array($tags, $result));
}
@@ -388,13 +380,13 @@ function sanitize($html)
{
# Link tags, keeps only href attribute
list($head, $href, $content, $tail) = $tag;
- $result .= it_html::sanitize($head) . '<a href="' . it_html::Q(html_entity_decode($href)) . '">' . it_html::sanitize($content) . "</a>" . it_html::sanitize($tail);
+ $result .= it_html::sanitize($head) . '<a href="' . new it_q(html_entity_decode($href)) . '">' . it_html::sanitize($content) . "</a>" . it_html::sanitize($tail);
}
else if ($tag = it::match('(.*)<img[^>]+?src="(' . $urlpattern . ')"[^>]*?>(.*)', $html))
{
# Image tags, keeps only src attribute
list($head, $src, $tail) = $tag;
- $result .= it_html::sanitize($head) . '<img src="' . it_html::Q(html_entity_decode($src)) . '" alt="" />' . it_html::sanitize($tail);
+ $result .= it_html::sanitize($head) . '<img src="' . new it_q(html_entity_decode($src)) . '" alt="" />' . it_html::sanitize($tail);
}
else if ($tag = it::match("(.*)<(br)[^>]*>(.*)", $html))
{
@@ -404,7 +396,7 @@ function sanitize($html)
$result .= it_html::sanitize($head) . "<$tagname />" . it_html::sanitize($tail);
}
else
- $result = it_html::Q(it::replace(array('&#\d+;' => ""), html_entity_decode(strip_tags($html))));
+ $result = new it_q(it::replace(array('&#\d+;' => ""), html_entity_decode(strip_tags($html))));
return $result;
}
@@ -412,15 +404,24 @@ function sanitize($html)
/**
* Shortcut: return htmlspecialchars($string) and encode forbidden characters 80-9f if latin1 is output
- * @param $string String to encode with htmlspecialchars()
- * @return htmlspecialchars($string)
+ * @param $args Data to encode with htmlspecialchars()
+ * @return htmlspecialchars($args)
*/
-function q($string)
+function q($args)
{
- if ($GLOBALS['it_html']->p['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")));
+ $args = (array)$args;
+ return new it_q($args[0]);
+}
- return htmlspecialchars($string);
+/**
+ * Mark text as not to be encoded (e.g. html from db)
+ * @param $args Data not to be encoded
+ * @return Data marked as not to be encoded
+ */
+function nq($args)
+{
+ $args = (array)$args;
+ return new it_q($args[0], false);
}
@@ -433,9 +434,7 @@ function u(/* ... */)
$args = func_get_args();
list($base, $params) = it_html::_parse_args($args);
- if (!isset($base))
- $base = $_SERVER['PHP_SELF'];
-
+ $base = count($base) == 0 ? $_SERVER['PHP_SELF'] : join("", $base);
$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
@@ -460,7 +459,7 @@ function js($args)
}
array_unshift($args, array('type' => 'text/javascript'));
- return $this->_tag('script', $args);
+ return new it_tag('script', $args);
}
@@ -478,7 +477,7 @@ function _itjs($files, $mode)
if ($mode == "files")
{
foreach ($filenames as $file)
- $result .= tag('script', array('type' => "text/javascript", 'src' => "/itjs/" . basename($file) . "?v=" . itjs::checksum($file)));
+ $result .= new it_tag('script', array('type' => "text/javascript", 'src' => "/itjs/" . basename($file) . "?v=" . itjs::checksum($file)));
}
else if ($mode == "inline")
{
@@ -490,7 +489,7 @@ function _itjs($files, $mode)
$result .= itjs::strip($jsfile);
}
else
- $result .= tag('script', array('type' => "text/javascript", 'src' => "/itjs/$files?v=" . itjs::checksum($filenames)));
+ $result .= new it_tag('script', array('type' => "text/javascript", 'src' => "/itjs/$files?v=" . itjs::checksum($filenames)));
}
return $result;
diff --git a/it_q.class b/it_q.class
new file mode 100644
index 0000000..3458b10
--- /dev/null
+++ b/it_q.class
@@ -0,0 +1,52 @@
+<?php
+/*
+** $Id$
+**
+** Copyright (C) 1995-2008 by the ITools Authors.
+** This file is part of ITools - the Internet Tools Library
+**
+** ITools 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.
+**
+** ITools 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/>.
+**
+** UltraHTML 3000 tool layer. Texts are now objects, used by it_html
+**
+**/
+
+class it_q
+{
+ var $quote;
+ var $value;
+
+function __construct($value, $quote = true)
+{
+ $this->quote = $quote;
+ $this->value = $value;
+}
+
+/**
+ * Return htmlspecialchars(strval($this)) and encode forbidden characters 80-9f if latin1 is output
+ * @return Encoded string value of this object
+ */
+function __toString()
+{
+ $result = strval($this->value);
+
+ if ($GLOBALS['it_html']->p['charset'] == "iso-8859-1")
+ $result = preg_replace('/[\x80-\x9f]/', ' ', strtr($result, array("\x80" => "EUR", "\x82" => "'", "\x84" => "\"", "\x85" => "...", "\x8a" => "S", "\x8c" => "OE", "\x8e" => "Z", "\x91" => "'", "\x92" => "'", "\x93" => "\"", "\x94" => "\"", "\x96" => "-", "\x97" => "-", "\x9a" => "s", "\x9e" => "z")));
+
+ return $this->quote ? htmlspecialchars($result) : $result;
+}
+
+}
+
+?>
diff --git a/it_tag.class b/it_tag.class
new file mode 100644
index 0000000..f2b0fdc
--- /dev/null
+++ b/it_tag.class
@@ -0,0 +1,113 @@
+<?php
+/*
+** $Id$
+**
+** Copyright (C) 1995-2008 by the ITools Authors.
+** This file is part of ITools - the Internet Tools Library
+**
+** ITools 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.
+**
+** ITools 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/>.
+**
+** UltraHTML 3000 tool layer. Tags are now objects, used by it_html
+**
+**/
+
+class it_tag extends ArrayObject
+{
+ var $tag;
+ var $attributes = array();
+ var $prefix = "";
+ var $suffix = "";
+
+function __construct($tag, $values = array())
+{
+ $this->tag = $tag;
+ $this->offsetSet(0, $values);
+}
+
+function offsetSet($index, $value)
+{
+ if (is_string($index))
+ {
+ $this->attributes[$index] = $value;
+ }
+ else if (is_array($value))
+ {
+ foreach ($value as $key => $val)
+ $this->offsetSet($key, $val);
+ }
+ else
+ {
+ if (is_string($value))
+ {
+ if ($errortype = $GLOBALS['it_html']->p['reportquote'])
+ trigger_error("Unquoted '$value' at " . it_debug::backtrace(), $errortype);
+ if ($GLOBALS['it_html']->p['autoquote'])
+ $value = new it_q($value);
+ }
+
+ parent::offsetSet($index, $value);
+ }
+}
+
+function __toString()
+{
+ $result = array($this->prefix, "<$this->tag");
+
+ foreach ($this->attributes 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", "&#10;", new it_q($value)) : $value) . '"';
+ else # true: tag without value
+ $result[] = ($GLOBALS['it_html']->p['htmltype'] == 'html') ? " $key" : " $key=\"$key\"";
+ }
+
+ $newline = isset($GLOBALS['it_html']->hasnonewline[$this->tag]) ? "" : "\n";
+
+ if (count($this) || preg_match('/^(a|div|iframe|script|span|td|textarea)$/i', $this->tag))
+ {
+ $result[] = ">";
+
+ $data = join("", $this->getArrayCopy());
+
+ # Ultra XML PrettyPrinter 3000 [\] by SCA
+ if ($GLOBALS['it_html']->p['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[] = $data;
+
+ $result[] = "</$this->tag>$newline";
+ }
+ else if ($GLOBALS['it_html']->p['htmltype'] == 'html')
+ $result[] = ">$newline";
+ else
+ $result[] = " />$newline";
+
+ if ($GLOBALS['debug_srclines'])
+ array_unshift($result, "<!-- " . trim(it::replace(array('^(it_\w+.class:\d+\s+)+' => "", '^(([\w.]+:\d+\s+){2}).*' => '$1'), it_debug::backtrace())) . " -->");
+
+ $result[] = $this->suffix;
+
+ return join("", $result);
+}
+
+function clear()
+{
+ $this->exchangeArray(array());
+}
+
+}
+
+?>
diff --git a/itjs.class b/itjs.class
index 40d65b8..bafc103 100644
--- a/itjs.class
+++ b/itjs.class
@@ -89,7 +89,8 @@ function encode($values)
$result .= 'false';
else if (!is_array($value))
{
- $quote = (strval(intval($value)) === strval($value)) ? "" : '"';
+ $value = strval($value);
+ $quote = (strval(intval($value)) === $value) ? "" : '"';
$string = strtr($value, array("\0" => '\\0', '"' => '\\"', "</"=>"<\\/", "\n" => '\\n', "\r" => '\\r', "\t" => '\\t', "\\" => '\\\\'));
$string = $GLOBALS['itjs_defaultconfig']['latin2unicode'] ? preg_replace('/([\xa0-\xff])/e', 'sprintf("\\u%04x", ord("\\1"))', $string) : $string;
$result .= $quote . $string . $quote;
diff --git a/itools.lib b/itools.lib
index 3b6a2f1..b472d38 100644
--- a/itools.lib
+++ b/itools.lib
@@ -23,6 +23,8 @@ require_once 'itools/it.class';
require_once 'itools/it_browser.class';
require_once 'itools/it_dbi.class';
require_once 'itools/it_debug.class';
+require_once 'itools/it_q.class';
+require_once 'itools/it_tag.class';
require_once 'itools/it_html.class';
require_once 'itools/itjs.class';
require_once 'itools/it_mail.class';