Import Horde_Ui from CVS HEAD
authorMichael M Slusarz <slusarz@curecanti.org>
Thu, 19 Nov 2009 19:59:42 +0000 (12:59 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Thu, 19 Nov 2009 20:11:27 +0000 (13:11 -0700)
framework/Ui/lib/Horde/Ui/JsCalendar.php [new file with mode: 0644]
framework/Ui/lib/Horde/Ui/Language.php [new file with mode: 0644]
framework/Ui/lib/Horde/Ui/Pager.php [new file with mode: 0644]
framework/Ui/lib/Horde/Ui/Tabs.php [new file with mode: 0644]
framework/Ui/lib/Horde/Ui/TagCloud.php [new file with mode: 0644]
framework/Ui/lib/Horde/Ui/VarRenderer.php [new file with mode: 0644]
framework/Ui/lib/Horde/Ui/VarRenderer/Html.php [new file with mode: 0644]
framework/Ui/lib/Horde/Ui/VarRenderer/TablesetHtml.php [new file with mode: 0644]
framework/Ui/lib/Horde/Ui/Widget.php [new file with mode: 0644]
framework/Ui/package.xml [new file with mode: 0644]

diff --git a/framework/Ui/lib/Horde/Ui/JsCalendar.php b/framework/Ui/lib/Horde/Ui/JsCalendar.php
new file mode 100644 (file)
index 0000000..a831194
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/**
+ * The Horde_Ui_JsCalendar:: class generates the necessary javascript code
+ * to allow the javascript calendar widget to be displayed on the page.
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Michael Slusarz <slusarz@curecanti.org>
+ * @package Horde_Ui
+ */
+class Horde_Ui_JsCalendar
+{
+    /**
+     * Output the necessary javascript code to allow display of the calendar
+     * widget.
+     */
+    static public function init()
+    {
+        Horde::addScriptFile('calendar.js', 'horde');
+        Horde::addInlineScript(array(
+            'Horde_Calendar.firstDayOfWeek = ' . (isset($GLOBALS['prefs']) ? intval($GLOBALS['prefs']->getValue('first_week_day')) : 1),
+            'Horde_Calendar.weekdays = ' . Horde_Serialize::serialize(self::weekdays(), Horde_Serialize::JSON, Horde_Nls::getCharset()),
+            'Horde_Calendar.months = ' . Horde_Serialize::serialize(self::months(), Horde_Serialize::JSON, Horde_Nls::getCharset()),
+        ));
+    }
+
+    /**
+     * Return the list of localized abbreviated weekdays.
+     *
+     * @return array  Abbreviated weekdays.
+     */
+    static public function weekdays()
+    {
+        return array(
+            _("Su"),
+            _("Mo"),
+            _("Tu"),
+            _("We"),
+            _("Th"),
+            _("Fr"),
+            _("Sa")
+        );
+    }
+
+    /**
+     * Return the localized list of months.
+     *
+     * @return array  Month list.
+     */
+    static public function months()
+    {
+        return array(
+            _("January"),
+            _("February"),
+            _("March"),
+            _("April"),
+            _("May"),
+            _("June"),
+            _("July"),
+            _("August"),
+            _("September"),
+            _("October"),
+            _("November"),
+            _("December")
+        );
+    }
+
+}
diff --git a/framework/Ui/lib/Horde/Ui/Language.php b/framework/Ui/lib/Horde/Ui/Language.php
new file mode 100644 (file)
index 0000000..d5feee4
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * The Horde_Ui_Language:: class provides a widget for changing the
+ * currently selected language.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Jason M. Felice <jason.m.felice@gmail.com>
+ * @package Horde_Ui
+ */
+class Horde_Ui_Language {
+
+    /**
+     * Render the language selection.
+     *
+     * @param boolean $form  Return the selection box as a complete standalone
+     *                       form.
+     *
+     * @return string  The HTML selection box.
+     */
+    static public function render()
+    {
+        $html = '';
+
+        if (!$GLOBALS['prefs']->isLocked('language')) {
+            $_SESSION['horde_language'] = Horde_Nls::select();
+            $html = sprintf('<form name="language" action="%s">',
+                            Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/language.php', false, -1));
+            $html .= '<input type="hidden" name="url" value="' . @htmlspecialchars(Horde::selfUrl(false, false, true)) . '" />';
+            $html .= '<select name="new_lang" onchange="document.language.submit()">';
+            foreach ($GLOBALS['nls']['languages'] as $key => $val) {
+                $sel = ($key == $_SESSION['horde_language']) ? ' selected="selected"' : '';
+                $html .= "<option value=\"$key\"$sel>$val</option>";
+            }
+            $html .= '</select></form>';
+        }
+
+        return $html;
+    }
+
+}
diff --git a/framework/Ui/lib/Horde/Ui/Pager.php b/framework/Ui/lib/Horde/Ui/Pager.php
new file mode 100644 (file)
index 0000000..c08fa04
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+/**
+ * The Horde_Ui_Pager:: provides links to individual pages.
+ *
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Ben Chavet <ben@chavet.net>
+ * @author  Joel Vandal <joel@scopserv.com>
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @package Horde_Ui
+ */
+class Horde_Ui_Pager extends Horde_Ui_Widget
+{
+    /**
+     * Constructor
+     *
+     * TODO
+     */
+    public function __construct($name, &$vars, $config)
+    {
+        if (!isset($config['page_limit'])) {
+            $config['page_limit'] = 10;
+        }
+
+        if (!isset($config['perpage'])) {
+            $config['perpage'] = 100;
+        }
+
+        parent::__construct($name, $vars, $config);
+    }
+
+    /**
+     * Render the pager.
+     *
+     * @return string  HTML code containing a centered table with the pager
+     *                 links.
+     */
+    public function render()
+    {
+        global $prefs, $registry, $conf;
+
+        $num = $this->_config['num'];
+        $url = $this->_config['url'];
+
+        $page_limit = $this->_config['page_limit'];
+        $perpage = $this->_config['perpage'];
+
+        $current_page = $this->_vars->get($this->_name);
+
+        // Figure out how many pages there will be.
+        $pages = ($num / $perpage);
+        if (is_integer($pages)) {
+            $pages--;
+        }
+        $pages = (int)$pages;
+
+        // Return nothing if there is only one page.
+        if ($pages == 0 || $num == 0) {
+            return '';
+        }
+
+        $html = '<div class="pager">';
+
+        if ($current_page > 0) {
+            // Create the '<< Prev' link if we are not on the first page.
+            $link = Horde_Util::addParameter($url, $this->_name, $current_page - 1);
+            $link = $this->_addPreserved($link);
+            $link = $this->_link($link);
+            if (isset($this->_config['previousHTML'])) {
+                $html .= Horde::link($link, '', 'prev') . $this->_config['previousHTML'] . '</a>';
+            } else {
+                $html .= Horde::link($link, '', 'prev') . htmlspecialchars(_("<Previous")) . '</a>';
+            }
+        }
+
+        // Figure out the top & bottom display limits.
+        $bottom = max(0, $current_page - ($page_limit / 2) + 1);
+        $top = $bottom + $page_limit - 1;
+        if ($top - 1 > $pages) {
+            $bottom -= ($top - 1) - $pages;
+            $top = $pages + 1;
+        }
+
+        // Create bottom '[x-y]' link if necessary.
+        $link = $this->_addPreserved(Horde_Util::addParameter($url, $this->_name, $bottom - 1));
+        $link = $this->_link($link);
+
+        if ($bottom > 0) {
+            $html .= ' ' . Horde::link($link, '', 'prevRange') . '[' . ($bottom == 1 ? $bottom : '1-' . $bottom) . ']</a>';
+        }
+
+        // Create links to individual pages between limits.
+        for ($i = $bottom; $i <= $top && $i <= $pages; ++$i) {
+            if ($i == $current_page) {
+                $html .= ' <strong>(' . ($i + 1) . ')</strong>';
+            } elseif ($i >= 0 && $i <= $pages) {
+                $link = $this->_addPreserved(Horde_Util::addParameter($url, $this->_name, $i));
+                $link = $this->_link($link);
+                $html .= ' ' . Horde::link($link) . ($i + 1) . '</a>';
+            }
+        }
+
+        // Create top '[x-y]' link if necessary.
+        if ($top < $pages) {
+            $link = $this->_addPreserved(Horde_Util::addParameter($url, $this->_name, $top + 1));
+            $link = $this->_link($link);
+
+            $html .= ' ' . Horde::link($link, '', 'nextRange') . '[' .
+                ($top + 2 == $pages + 1 ? $pages + 1 : ($top + 2) . '-' . ($pages + 1)) . ']</a>';
+        }
+
+        // Create the 'Next>>' link if we are not on the last page.
+        if ($current_page < $pages) {
+            $link = $this->_addPreserved(Horde_Util::addParameter($url, $this->_name, $current_page + 1));
+            $link = $this->_link($link);
+            if (isset($this->_config['nextHTML'])) {
+                $html .= Horde::link($link, '', 'next') . $this->_config['nextHTML'] . '</a>';
+            } else {
+                $html .= ' ' . Horde::link($link, '', 'next') . htmlspecialchars(_("Next>")) . '</a>';
+            }
+        }
+
+        return $html . '</div>';
+    }
+
+}
diff --git a/framework/Ui/lib/Horde/Ui/Tabs.php b/framework/Ui/lib/Horde/Ui/Tabs.php
new file mode 100644 (file)
index 0000000..0353f6f
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * The Horde_Ui_Tabs:: class manages and renders a tab-like interface.
+ *
+ * Copyright 2001-2003 Robert E. Coyle <robertecoyle@hotmail.com>
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Jason M. Felice <jason.m.felice@gmail.com>
+ * @since   Horde_Ui 0.0.1
+ * @package Horde_Ui
+ */
+class Horde_Ui_Tabs extends Horde_Ui_Widget
+{
+    /**
+     * The array of tabs.
+     *
+     * @var array
+     */
+    protected $_tabs = array();
+
+    /**
+     * Adds a tab to the interface.
+     *
+     * @param string $title  The text which appears on the tab.
+     * @param string $link   The target page.
+     * @param mixed $params  Either a string value to set the tab variable to,
+     *                       or a hash of parameters. If an array, the tab
+     *                       variable can be set by the 'tabname' key.
+     */
+    public function addTab($title, $link, $params = array())
+    {
+        if (!is_array($params)) {
+            $params = array('tabname' => $params);
+        }
+
+        $this->_tabs[] = array_merge(array('title' => $title,
+                                           'link' => $link,
+                                           'tabname' => null),
+                                     $params);
+    }
+
+    /**
+     * Returns the title of the tab with the specified name.
+     *
+     * @param string $tabname  The name of the tab.
+     *
+     * @return string  The tab's title.
+     */
+    public function getTitleFromAction($tabname)
+    {
+        foreach ($this->_tabs as $tab) {
+            if ($tab['tabname'] == $tabname) {
+                return $tab['title'];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Renders the tabs.
+     *
+     * @param string $active_tab  If specified, the name of the active tab. If
+     *                            not, the active tab is determined
+     *                            automatically.
+     */
+    public function render($active_tab = null)
+    {
+        $html = "<div class=\"tabset\"><ul>\n";
+
+        $first = true;
+        $active = $_SERVER['PHP_SELF'] . $this->_vars->get($this->_name);
+
+        foreach ($this->_tabs as $tab) {
+            $link = $this->_addPreserved($tab['link']);
+            if (!is_null($this->_name) && !is_null($tab['tabname'])) {
+                $link = Horde_Util::addParameter($link, $this->_name,
+                                           $tab['tabname']);
+            }
+
+            $class = '';
+            if ((!is_null($active_tab) && $active_tab == $tab['tabname']) ||
+                ($active == $tab['link'] . $tab['tabname'])) {
+                $class = ' class="activeTab"';
+            }
+
+            $id = '';
+            if (!empty($tab['id'])) {
+                $id = ' id="' . htmlspecialchars($tab['id']) . '"';
+            }
+
+            if (!isset($tab['target'])) {
+                $tab['target'] = '';
+            }
+
+            if (!isset($tab['onclick'])) {
+                $tab['onclick'] = '';
+            }
+
+            $accesskey = Horde::getAccessKey($tab['title']);
+
+            $html .= '<li' . $class . $id . '>' .
+                Horde::link($link, '', '', $tab['target'], $tab['onclick'], null, $accesskey) .
+                Horde::highlightAccessKey(str_replace(' ', '&nbsp;', $tab['title']), $accesskey) .
+                "</a> </li>\n";
+        }
+
+        return $html . "</ul></div><br class=\"clear\" />\n";
+    }
+
+}
diff --git a/framework/Ui/lib/Horde/Ui/TagCloud.php b/framework/Ui/lib/Horde/Ui/TagCloud.php
new file mode 100644 (file)
index 0000000..716299e
--- /dev/null
@@ -0,0 +1,317 @@
+<?php
+/**
+ * Horde_Ui_TagCloud:: for creating and displaying tag clouds.
+ *
+ * Based on a striped down version of Pear's HTML_TagCloud
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @package Horde_Ui
+ */
+class Horde_Ui_TagCloud
+{
+    /**
+     * @var integer
+     */
+    public $basefontsize = 24;
+
+    /**
+     * @var integer
+     */
+    public $fontsizerange = 12;
+
+    /**
+     * @var string
+     */
+    public $css_class = 'tagcloud';
+
+    /**
+     * @var    string
+     * mm,cm,in,pt,pc,px,em
+     */
+    public $size_suffix = 'px';
+
+    /**
+     * @var integer
+     */
+    public $factor;
+
+    /**
+     * @var array
+     */
+    public $epoc_level = array(
+        'earliest',
+        'earlier',
+        'later',
+        'latest'
+    );
+
+    /**
+     * @var array
+     */
+    protected $_elements = array();
+
+    /**
+     * @var integer
+     */
+    protected $_max = 0;
+
+    /**
+     * @var integer
+     */
+    protected $_min = 0;
+
+    /**
+     * @var integer
+     */
+    protected $_max_epoc;
+
+    /**
+     * @var integer
+     */
+    protected $_min_epoc;
+
+    /**
+     * @var array
+     */
+    protected $_map = array();
+
+    /**
+     * Constructor
+     *
+     * @param integer $basefontsize   Base font size of output tag (option).
+     * @param integer $fontsizerange  Font size range.
+     */
+    public function __construct($basefontsize = 24, $fontsizerange = 12)
+    {
+        $this->basefontsize = $basefontsize;
+        $this->minfontsize = ($this->basefontsize - $this->fontsizerange > 0)
+            ? $this->basefontsize - $this->fontsizerange
+            : 0;
+        $this->maxfontsize = $this->basefontsize + $this->fontsizerange;
+    }
+
+    /**
+     * Add a Tag Element to build Tag Cloud.
+     *
+     * @param string $tag         TODO
+     * @param string $url         TODO
+     * @param integer $count      TODO
+     * @param integer $timestamp  UNIX timestamp.
+     * @param string $onclick     Javascript onclick event handler.
+     */
+    public function addElement($name, $url ='', $count = 0, $timestamp = null,
+                               $onclick = null)
+    {
+
+        if (isset($this->_map[$name])) {
+            $i = $this->_map[$name];
+            // Increase the count
+            $this->_elements[$i]['count'] += $count;
+
+            // Keep the latest timestamp
+            if (!empty($timestamp) &&
+                $timestamp > $this->_elements[$i]['timestamp']) {
+                $this->_elements[$i]['timestamp'] = $timestamp;
+            }
+            // For onclick and url we will simply overwrite the existing values
+            // instead of checking if they are empty, then overwriting.
+            $this->_elements[$i]['onclick'] = $onclick;
+            $this->elements[$i]['url'] = $url;
+        } else {
+            $i = count($this->_elements);
+            $this->_elements[$i]['name'] = $name;
+            $this->_elements[$i]['url'] = $url;
+            $this->_elements[$i]['count'] = $count;
+            $this->_elements[$i]['timestamp'] = $timestamp == null ? time() : $timestamp;
+            $this->_elements[$i]['onclick'] = $onclick;
+            $this->_map[$name] = $i;
+        }
+    }
+
+    /**
+     * Add a Tag Element to build Tag Cloud.
+     *
+     * @param array $tags  Associative array to $this->_elements.
+     */
+    public function addElements($tags)
+    {
+        $this->_elements = array_merge($this->_elements, $tags);
+    }
+
+    /**
+     * Clear Tag Elements.
+     */
+    public function clearElements()
+    {
+        $this->_elements = array();
+    }
+
+    /**
+     * Build HTML part.
+     *
+     * @param array $param  'limit' => int limit of generation tag num.
+     *
+     * @return string   HTML
+     */
+    public function buildHTML($param = array())
+    {
+        return $this->_wrapDiv($this->_buidHTMLTags($param));
+    }
+
+    /**
+     * Calc Tag level and create whole HTML of each Tags.
+     *
+     * @param array $param  Limit of Tag Number.
+     *
+     * @return string  HTML
+     */
+    protected function _buidHTMLTags($param)
+    {
+        $this->total = count($this->_elements);
+        // no tags elements
+        if ($this->total == 0) {
+            return '';
+        } elseif ($this->total == 1) {
+            $tag = $this->_elements[0];
+            return $this->_createHTMLTag($tag, 'latest', $this->basefontsize);
+        }
+
+        $limit = array_key_exists('limit', $param) ? $param['limit'] : 0;
+        $this->_sortTags($limit);
+        $this->_calcMumCount();
+        $this->_calcMumEpoc();
+
+        $range = $this->maxfontsize - $this->minfontsize;
+        $this->factor = ($this->_max == $this->_min)
+            ? 1
+            : $range / (sqrt($this->_max) - sqrt($this->_min));
+        $this->epoc_factor = ($this->_max_epoc == $this->_min_epoc)
+            ? 1
+            : count($this->epoc_level) / (sqrt($this->_max_epoc) - sqrt($this->_min_epoc));
+        $rtn = array();
+        foreach ($this->_elements as $tag){
+            $count_lv = $this->_getCountLevel($tag['count']);
+            if(! isset($tag['timestamp']) || empty($tag['timestamp'])){
+                $epoc_lv = count($this->epoc_level) - 1;
+            }else{
+                $epoc_lv  = $this->_getEpocLevel($tag['timestamp']);
+            }
+            $color_type = $this->epoc_level[$epoc_lv];
+            $font_size  = $this->minfontsize + $count_lv;
+            $rtn[] = $this->_createHTMLTag($tag, $color_type, $font_size);
+        }
+        return implode('', $rtn);
+    }
+
+    /**
+     * Create a Element of HTML part.
+     *
+     * @param array $tag         TODO
+     * @param string $type       CSS class of time line param.
+     * @param integer $fontsize  TODO
+     *
+     * @return  string a Element of Tag HTML
+     */
+    protected function _createHTMLTag($tag, $type, $fontsize)
+    {
+        return sprintf("<a style=\"font-size: %d%s\" class=\"%s\" href=\"%s\" %s >%s</a>\n",
+                       $fontsize,
+                       $this->size_suffix,
+                       $type,
+                       $tag['url'],
+                       (empty($tag['onclick']) ? '' : 'onClick="' . $tag['onclick'] . '"'),
+                       htmlspecialchars($tag['name']));
+    }
+
+    /**
+     * Sort tags by name.
+     *
+     * @param integer $limit  Limit element number of create TagCloud.
+     */
+    protected function _sortTags($limit = 0)
+    {
+        usort($this->_elements, array($this, 'cmpElementsName'));
+        if ($limit != 0){
+            $this->_elements = array_splice($this->_elements, 0, $limit);
+        }
+    }
+
+    /**
+     * Using for usort().
+     *
+     * @return integer  TODO
+     */
+    public function cmpElementsName($a, $b)
+    {
+        return ($a['name'] == $b['name'])
+            ? 0
+            : (($a['name'] < $b['name']) ? -1 : 1);
+    }
+
+    /**
+     * Calc max and min tag count of use.
+     */
+    protected function _calcMumCount()
+    {
+        foreach($this->_elements as $item){
+            $array[] = $item['count'];
+        }
+        $this->_min = min($array);
+        $this->_max = max($array);
+    }
+
+    /**
+     * Calc max and min timestamp.
+     */
+    protected function _calcMumEpoc()
+    {
+        foreach($this->_elements as $item){
+            $array[] = $item['timestamp'];
+        }
+        $this->_min_epoc = min($array);
+        $this->_max_epoc = max($array);
+    }
+
+    /**
+     * Calc Tag Level of size.
+     *
+     * @param integer $count  TODO
+     *
+     * @return integer  Level.
+     */
+    protected function _getCountLevel($count = 0)
+    {
+        return (int)(sqrt($count) - sqrt($this->_min)) * $this->factor;
+    }
+
+    /**
+     * Calc timeline level of Tag.
+     *
+     * @param integer $timestamp  TODO
+     *
+     * @return integer  Level of timeline.
+     */
+    protected function _getEpocLevel($timestamp = 0)
+    {
+        return (int)(sqrt($timestamp) - sqrt($this->_min_epoc)) * $this->epoc_factor;
+    }
+
+    /**
+     * Wrap div tag.
+     *
+     * @param string $html  TODO
+     *
+     * @return string  TODO
+     */
+    protected function _wrapDiv($html)
+    {
+        return ($html == '')
+            ? ''
+            : sprintf("<div class=\"%s\">\n%s</div>\n", $this->css_class, $html);
+    }
+
+}
diff --git a/framework/Ui/lib/Horde/Ui/VarRenderer.php b/framework/Ui/lib/Horde/Ui/VarRenderer.php
new file mode 100644 (file)
index 0000000..0cb07e2
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+/**
+ * The Horde_Ui_VarRenderer:: class provides base functionality for
+ * other Horde UI elements.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Jason M. Felice <jason.m.felice@gmail.com>
+ * @package Horde_Ui
+ */
+class Horde_Ui_VarRenderer
+{
+    /**
+     * Parameters which change this renderer's behavior.
+     *
+     * @var array
+     */
+    protected $_params;
+
+    /**
+     * Charset to use for output.
+     *
+     * @var string
+     */
+    protected $_charset;
+
+    /**
+     * Constructs a new renderer.
+     *
+     * @param array $params  The name of the variable which will track this UI
+     *                       widget's state.
+     */
+    public function __construct($params = array())
+    {
+        $this->_params = $params;
+        $this->_charset = Horde_Nls::getCharset();
+    }
+
+    /**
+     * Constructs a new instance.
+     *
+     * @param mixed $driver  This is the renderer subclass we will instantiate.
+     *                       If an array is passed, the first element is the
+     *                       library path and the second element is the driver
+     *                       name.
+     * @param array $params  Parameters specific to the subclass.
+     *
+     * @return Horde_Ui_VarRenderer  A subclass instance.
+     * @throws Horde_Exception
+     */
+    static public function factory($driver, $params = array())
+    {
+        if (is_array($driver)) {
+            $app = $driver[0];
+            $driver = $driver[1];
+        }
+
+        $driver = ucfirst(basename($driver));
+        $class = 'Horde_Ui_VarRenderer_' . $driver;
+
+        if (!class_exists($class)) {
+            throw new Horde_Exception('Class definition of ' . $class . ' not found.');
+        }
+
+        return new $class($params);
+    }
+
+    /**
+     * Renders a variable.
+     *
+     * @param Horde_Form $form           A Horde_Form instance,
+     *                                   or null if none is available.
+     * @param Horde_Form_Variable &$var  Reference to a Horde_Form_Variable.
+     * @param Variables &$vars           A Variables instance.
+     * @param boolean $isInput           Whether this is an input field.
+     */
+    public function render($form, &$var, &$vars, $isInput = false)
+    {
+        $state = $isInput ? 'Input' : 'Display';
+        $method = "_renderVar${state}_" . $var->type->getTypeName();
+        if (!method_exists($this, $method)) {
+            $method = "_renderVar${state}Default";
+        }
+
+        return $this->$method($form, $var, $vars);
+    }
+
+    /**
+     * Finishes rendering after all fields are output.
+     *
+     * @return string  TODO
+     */
+    public function renderEnd()
+    {
+        return '';
+    }
+
+}
diff --git a/framework/Ui/lib/Horde/Ui/VarRenderer/Html.php b/framework/Ui/lib/Horde/Ui/VarRenderer/Html.php
new file mode 100644 (file)
index 0000000..cd65252
--- /dev/null
@@ -0,0 +1,1656 @@
+<?php
+/**
+ * The Horde_Ui_VarRenderer_html:: class renders variables to HTML.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Jason M. Felice <jason.m.felice@gmail.com>
+ * @package Horde_Ui
+ */
+class Horde_Ui_VarRenderer_Html extends Horde_Ui_VarRenderer
+{
+    protected $_onLoadJS = array();
+
+    protected function _renderVarInputDefault($form, &$var, &$vars)
+    {
+        return '<strong>Warning:</strong> Unknown variable type ' .
+            @htmlspecialchars($var->getTypeName(), ENT_QUOTES, $this->_charset);
+    }
+
+    protected function _renderVarInput_number($form, &$var, &$vars)
+    {
+        $value = $var->getValue($vars);
+        if ($var->type->getProperty('fraction')) {
+            $value = sprintf('%01.' . $var->type->getProperty('fraction') . 'f', $value);
+        }
+        $linfo = Horde_Nls::getLocaleInfo();
+        /* Only if there is a mon_decimal_point do the
+         * substitution. */
+        if (!empty($linfo['mon_decimal_point'])) {
+            $value = str_replace('.', $linfo['mon_decimal_point'], $value);
+        }
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" size="5" name="%s" id="%s" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       $value,
+                       $this->_getActionScripts($form, $var)
+               );
+    }
+
+    protected function _renderVarInput_int($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" size="5" name="%s" id="%s" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var)
+               );
+    }
+
+    protected function _renderVarInput_octal($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" size="5" name="%s" id="%s" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       sprintf('0%o', octdec($var->getValue($vars))),
+                       $this->_getActionScripts($form, $var)
+               );
+    }
+
+    protected function _renderVarInput_intlist($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" name="%s" id="%s" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var)
+               );
+    }
+
+    protected function _renderVarInput_text($form, &$var, &$vars)
+    {
+        $maxlength = $var->type->getMaxLength();
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" name="%s" id="%s" size="%s" value="%s" %s%s%s />',
+                       $varname,
+                       $varname,
+                       $var->type->getSize(),
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset),
+                       $var->isDisabled() ? ' disabled="disabled" ' : '',
+                       empty($maxlength) ? '' : ' maxlength="' . $maxlength . '"',
+                       $this->_getActionScripts($form, $var)
+               );
+    }
+
+    protected function _renderVarInput_stringlist($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" size="60" name="%s" id="%s" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var)
+               );
+    }
+
+    protected function _renderVarInput_stringarray($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" size="60" name="%s" id="%s" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars(implode(', ', $var->getValue($vars)), ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var)
+               );
+    }
+
+    protected function _renderVarInput_phone($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" name="%s" id="%s" size="15" value="%s" %s%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset),
+                       $var->isDisabled() ? ' disabled="disabled" ' : '',
+                       $this->_getActionScripts($form, $var)
+               );
+    }
+
+    protected function _renderVarInput_cellphone($form, &$var, &$vars)
+    {
+        return $this->_renderVarInput_phone($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_ipaddress($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" name="%s" id="%s" size="16" value="%s" %s%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset),
+                       $var->isDisabled() ? ' disabled="disabled" ' : '',
+                       $this->_getActionScripts($form, $var)
+               );
+    }
+
+    protected function _renderVarInput_file($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="file" size="30" name="%s" id="%s"%s />',
+                       $varname,
+                       $varname,
+                       $this->_getActionScripts($form, $var));
+    }
+
+    /**
+     * @todo Show image dimensions in the width/height boxes.
+     */
+    protected function _renderVarInput_image($form, &$var, &$vars)
+    {
+        $varname = htmlspecialchars($var->getVarName());
+        $image = $var->type->getImage($vars, $var);
+
+        Horde::addScriptFile('image.js', 'horde');
+        $graphics_dir = $GLOBALS['registry']->getImageDir('horde');
+        $img_dir = $graphics_dir . '/image';
+
+        $html = '';
+
+        /* Check if there is existing img information stored. */
+        if (isset($image['img'])) {
+            /* Hidden tag to store the preview image id. */
+            $html = sprintf('<input type="hidden" name="%s" id="%s" value="%s" />',
+                            $varname . '[hash]',
+                            $varname . '[hash]',
+                            $var->type->getRandomId());
+        }
+
+        /* Output MAX_FILE_SIZE parameter to limit large files. */
+        if ($var->type->getProperty('max_filesize')) {
+            $html .= sprintf('<input type="hidden" name="MAX_FILE_SIZE" value="%d" />',
+                             $var->type->getProperty('max_filesize'));
+        }
+
+        /* Output the input tag. */
+        $html .= sprintf('<input type="file" size="30" name="%s" id="%s" />',
+                         $varname . '[new]',
+                         $varname . '[new]');
+
+        /* Output the button to upload/reset the image. */
+        if ($var->type->getProperty('show_upload')) {
+            $html .= '&nbsp;';
+            $html .= sprintf('<input class="button" name="%s" id="%s" type="submit" value="%s" /> ',
+                             '_do_' . $varname,
+                             '_do_' . $varname,
+                             _("Upload"));
+        }
+
+        if (!empty($image['img'])) {
+            /* Image information stored, show preview, add buttons for image
+             * manipulation. */
+            $html .= '<br />';
+            $img = Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/images/view.php');
+            if (isset($image['img']['vfs_id'])) {
+                /* Calling an image from VFS. */
+                $img = Horde_Util::addParameter($img, array('f' => $image['img']['vfs_id'],
+                                                      's' => 'vfs',
+                                                      'p' => $image['img']['vfs_path']));
+            } else {
+                /* Calling an image from a tmp directory (uploads). */
+                $img = Horde_Util::addParameter($img, 'f', $image['img']['file']);
+            }
+
+            /* Reset. */
+            $html .= Horde::link('#', _("Reset"), '', '', 'showImage(\'' . $img . '\', \'_p_' . $varname . '\', true);') . Horde::img('refresh.png', _("Reset"), '', $img_dir) . '</a>';
+
+            /* Rotate 270. */
+            $html .= Horde::link('#', _("Rotate Left"), '', '', 'showImage(\'' . Horde_Util::addParameter($img, array('a' => 'rotate', 'v' => '270')) . '\', \'_p_' . $varname . '\', true);') . Horde::img('rotate-270.png', _("Rotate Left"), '', $img_dir) . '</a>';
+
+            /* Rotate 180. */
+            $html .= Horde::link('#', _("Rotate 180"), '', '', 'showImage(\'' . Horde_Util::addParameter($img, array('a' => 'rotate', 'v' => '180')) . '\', \'_p_' . $varname . '\', true);') . Horde::img('rotate-180.png', _("Rotate 180"), '', $img_dir) . '</a>';
+
+            /* Rotate 90. */
+            $html .= Horde::link('#', _("Rotate Right"), '', '', 'showImage(\'' . Horde_Util::addParameter($img, array('a' => 'rotate', 'v' => '90')) . '\', \'_p_' . $varname . '\', true);') . Horde::img('rotate-90.png', _("Rotate Right"), '', $img_dir) . '</a>';
+
+            /* Flip image. */
+            $html .= Horde::link('#', _("Flip"), '', '', 'showImage(\'' . Horde_Util::addParameter($img, 'a', 'flip') . '\', \'_p_' . $varname . '\', true);') . Horde::img('flip.png', _("Flip"), '', $img_dir) . '</a>';
+
+            /* Mirror image. */
+            $html .= Horde::link('#', _("Mirror"), '', '', 'showImage(\'' . Horde_Util::addParameter($img, 'a', 'mirror') . '\', \'_p_' . $varname . '\', true);') . Horde::img('mirror.png', _("Mirror"), '', $img_dir) . '</a>';
+
+            /* Apply grayscale. */
+            $html .= Horde::link('#', _("Grayscale"), '', '', 'showImage(\'' . Horde_Util::addParameter($img, 'a', 'grayscale') . '\', \'_p_' . $varname . '\', true);') . Horde::img('grayscale.png', _("Grayscale"), '', $img_dir) . '</a>';
+
+            /* Resize width. */
+            $html .= sprintf('%s<input type="text" size="4" onchange="src=getResizeSrc(\'%s\', \'%s\');showImage(src, \'_p_%s\', true);" %s />',
+                   _("w:"),
+                   Horde_Util::addParameter($img, 'a', 'resize'),
+                   $varname,
+                   $varname,
+                   $this->_genID('_w_' . $varname));
+
+            /* Resize height. */
+            $html .= sprintf('%s<input type="text" size="4" onchange="src=getResizeSrc(\'%s\', \'%s\');showImage(src, \'_p_%s\', true);" %s />',
+                   _("h:"),
+                   Horde_Util::addParameter($img, 'a', 'resize'),
+                   $varname,
+                   $varname,
+                   $this->_genID('_h_' . $varname));
+
+            /* Apply fixed ratio resize. */
+            $html .= Horde::link('#', _("Fix ratio"), '', '', 'src=getResizeSrc(\'' . Horde_Util::addParameter($img, 'a', 'resize') . '\', \'' . $varname . '\', \'1\');showImage(src, \'_p_' . $varname . '\', true);') . Horde::img('ratio.png', _("Fix ratio"), '', $img_dir) . '</a>';
+
+            /* Keep also original if it has been requested. */
+            if ($var->type->getProperty('show_keeporig')) {
+                $html .= sprintf('<input type="checkbox" class="checkbox" name="%s" id="%s"%s />%s' . "\n",
+                                 $varname . '[keep_orig]',
+                                 $varname . '[keep_orig]',
+                                 !empty($image['keep_orig']) ? ' checked="checked"' : '',
+                                 _("Keep original?"));
+            }
+
+            /* The preview image element. */
+            $html .= '<br /><img src="' . $img . '" ' . $this->_genID('_p_' . $varname) . ">\n";
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarInput_longtext($form, &$var, &$vars)
+    {
+        global $browser;
+
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        $html = sprintf('<textarea id="%s" name="%s" cols="%s" rows="%s"%s%s>%s</textarea>',
+                        $varname,
+                        $varname,
+                        (int)$var->type->getCols(),
+                        (int)$var->type->getRows(),
+                        $this->_getActionScripts($form, $var),
+                        $var->isDisabled() ? ' disabled="disabled"' : '',
+                        @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset));
+
+        if ($var->type->hasHelper('rte') && $browser->hasFeature('rte')) {
+            $editor = Horde_Editor::singleton('fckeditor', array('id' => $varname, 'relativelinks' => $var->type->hasHelper('relativelinks')));
+        }
+
+        if ($var->type->hasHelper() && $browser->hasFeature('javascript')) {
+            $html .= '<br /><table cellspacing="0"><tr><td>';
+            Horde::addScriptFile('open_html_helper.js', 'horde', array('direct' => false));
+            $imgId = $this->_genID($var->getVarName(), false) . 'ehelper';
+            if ($var->type->hasHelper('emoticons')) {
+                $html .= Horde::link('#', _("Emoticons"), '', '', 'openHtmlHelper(\'emoticons\', \'' . $var->getVarName() . '\'); return false;') . Horde::img('smile.png', _("Emoticons"), 'id="' . $imgId . '"', $GLOBALS['registry']->getImageDir('horde') . '/emoticons') . '</a>';
+            }
+            $html .= '</td></tr><tr><td><div ' . $this->_genID('htmlhelper_' . $var->getVarName()) . ' class="control"></div></td></tr></table>' . "\n";
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarInput_countedtext($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<textarea name="%s" id="%s" cols="%s" rows="%s"%s%s>%s</textarea>',
+                       $varname,
+                       $varname,
+                       (int)$var->type->getCols(),
+                       (int)$var->type->getRows(),
+                       $this->_getActionScripts($form, $var),
+                       $var->isDisabled() ? ' disabled="disabled"' : '',
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset));
+    }
+
+    protected function _renderVarInput_address($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<textarea name="%s" id="%s" cols="%s" rows="%s"%s%s>%s</textarea>',
+                       $varname,
+                       $varname,
+                       (int)$var->type->getCols(),
+                       (int)$var->type->getRows(),
+                       $this->_getActionScripts($form, $var),
+                       $var->isDisabled() ? ' disabled="disabled"' : '',
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset));
+    }
+
+    protected function _renderVarInput_addresslink($form, &$var, &$vars)
+    {
+        return '';
+    }
+
+    protected function _renderVarInput_pgp($form, &$var, &$vars)
+    {
+        return $this->_renderVarInput_longtext($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_smime($form, &$var, &$vars)
+    {
+        return $this->_renderVarInput_longtext($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_country($form, &$var, &$vars)
+    {
+        return $this->_renderVarInput_enum($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_date($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" name="%s" id="%s" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var));
+    }
+
+    protected function _renderVarInput_time($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" size="5" name="%s" id="%s" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var));
+    }
+
+    protected function _renderVarInput_hourminutesecond($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        $time = $var->type->getTimeParts($var->getValue($vars));
+
+        /* Output hours. */
+        $hours = array('' => _("hh"));
+        for ($i = 0; $i <= 23; $i++) {
+            $hours[$i] = $i;
+        }
+        $html = sprintf('<select name="%s[hour]" id="%s[hour]"%s>%s</select>',
+                        $varname,
+                        $varname,
+                        $this->_getActionScripts($form, $var),
+                        $this->_selectOptions($hours, ($time['hour'] === '') ? '' : $time['hour']));
+
+        /* Output minutes. */
+        $minutes = array('' => _("mm"));
+        for ($i = 0; $i <= 59; $i++) {
+            $m = sprintf('%02d', $i);
+            $minutes[$m] = $m;
+        }
+        $html .= sprintf('<select name="%s[minute]" id="%s[minute]"%s>%s</select>',
+                         $varname,
+                         $varname,
+                         $this->_getActionScripts($form, $var),
+                         $this->_selectOptions($minutes, ($time['minute'] === '') ? '' : sprintf('%02d', $time['minute'])));
+
+        /* Return if seconds are not required. */
+        if (!$var->type->getProperty('show_seconds')) {
+            return $html;
+        }
+
+        /* Output seconds. */
+        $seconds = array('' => _("ss"));
+        for ($i = 0; $i <= 59; $i++) {
+            $s = sprintf('%02d', $i);
+            $seconds[$s] = $s;
+        }
+        return $html . sprintf('<select name="%s[second]" id="%s[second]"%s>%s</select>',
+                               $varname,
+                               $varname,
+                               $this->_getActionScripts($form, $var),
+                               $this->_selectOptions($seconds, ($time['second'] === '') ? '' : sprintf('%02d', $time['second'])));
+    }
+
+    protected function _renderVarInput_monthyear($form, &$var, &$vars)
+    {
+        $dates = array();
+        $dates['month'] = array('' => _("MM"),
+                                1 => _("January"),
+                                2 => _("February"),
+                                3 => _("March"),
+                                4 => _("April"),
+                                5 => _("May"),
+                                6 => _("June"),
+                                7 => _("July"),
+                                8 => _("August"),
+                                9 => _("September"),
+                                10 => _("October"),
+                                11 => _("November"),
+                                12 => _("December"));
+        $dates['year'] = array('' => _("YYYY"));
+        if ($var->type->getProperty('start_year') > $var->type->getProperty('end_year')) {
+            for ($i = $var->type->getProperty('start_year'); $i >= $var->type->getProperty('end_year'); $i--) {
+                $dates['year'][$i] = $i;
+            }
+        } else {
+            for ($i = $var->type->getProperty('start_year'); $i <= $var->type->getProperty('end_year'); $i++) {
+                $dates['year'][$i] = $i;
+            }
+        }
+        return sprintf('<select name="%s" id="%s"%s>%s</select>',
+                       $var->type->getMonthVar($var),
+                       $var->type->getMonthVar($var),
+                       $this->_getActionScripts($form, $var),
+                       $this->_selectOptions($dates['month'], $vars->get($var->type->getMonthVar($var)))) .
+            sprintf('<select name="%s" id="%s"%s>%s</select>',
+                    $var->type->getYearVar($var),
+                    $var->type->getYearVar($var),
+                    $this->_getActionScripts($form, $var),
+                    $this->_selectOptions($dates['year'], $vars->get($var->type->getYearVar($var))));
+    }
+
+    protected function _renderVarInput_monthdayyear($form, &$var, &$vars)
+    {
+        $dates = array();
+        $dates['month'] = array(''   => _("MM"),
+                                '1'  => _("January"),
+                                '2'  => _("February"),
+                                '3'  => _("March"),
+                                '4'  => _("April"),
+                                '5'  => _("May"),
+                                '6'  => _("June"),
+                                '7'  => _("July"),
+                                '8'  => _("August"),
+                                '9'  => _("September"),
+                                '10' => _("October"),
+                                '11' => _("November"),
+                                '12' => _("December"));
+        $dates['day'] = array('' => _("DD"));
+        for ($i = 1; $i <= 31; $i++) {
+            $dates['day'][$i] = $i;
+        }
+        $dates['year'] = array('' => _("YYYY"));
+        if ($var->type->getProperty('start_year') > $var->type->getProperty('end_year')) {
+            for ($i = $var->type->getProperty('start_year'); $i >= $var->type->getProperty('end_year'); $i--) {
+                $dates['year'][$i] = $i;
+            }
+        } else {
+            for ($i = $var->type->getProperty('start_year'); $i <= $var->type->getProperty('end_year'); $i++) {
+                $dates['year'][$i] = $i;
+            }
+        }
+        $date = $var->type->getDateParts($var->getValue($vars));
+
+        // TODO: use NLS to get the order right for the Rest Of The
+        // World.
+        $html = $js_code = '';
+        $date_parts = array('month', 'day', 'year');
+        foreach ($date_parts as $part) {
+            $varname = @htmlspecialchars($var->getVarName() . '[' . $part . ']', ENT_QUOTES, $this->_charset);
+            $html .= sprintf('<select name="%s" id="%s"%s>%s</select>',
+                             $varname,
+                             $varname,
+                             $this->_getActionScripts($form, $var),
+                             $this->_selectOptions($dates[$part], $date[$part]));
+            $js_code .= '$(\'' . $varname . '\').setValue(data.' . ($part == 'month' ? 'm + 1' : ($part == 'day' ? 'd' : 'y')) . ');';
+        }
+
+        if ($var->type->getProperty('picker') &&
+            $GLOBALS['browser']->hasFeature('javascript')) {
+            Horde_Ui_JsCalendar::init();
+            $imgId = $this->_genID($var->getVarName(), false) . 'goto';
+            $html .= Horde::link('#', _("Select a date"), '', '', 'Horde_Calendar.open(\'' . $imgId . '\', {}, function(data) { ' . $js_code . ')') . Horde::img('calendar.png', _("Calendar"), 'id="' . $imgId . '"', $GLOBALS['registry']->getImageDir('horde')) . "</a>\n";
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarInput_datetime(&$form, &$var, &$vars)
+    {
+        return $this->_renderVarInput_monthdayyear($form, $var, $vars) .
+            $this->_renderVarInput_hourminutesecond($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_sound(&$form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        $value = @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset);
+        $html = '<ul class="sound-list">';
+        if (!$var->isRequired()) {
+            $html .= '<li><label><input type="radio" id="' . $varname . '" name="' . $varname . '" value=""' . (!$value ? ' checked="checked"' : '') . ' /> ' . _("No Sound") . '</label></li>';
+        }
+        foreach ($var->type->getSounds() as $sound) {
+            $sound = @htmlspecialchars($sound, ENT_QUOTES, $this->_charset);
+            $html .= '<li><label><input type="radio" id="' . $varname . '" name="' . $varname . '" value="' . $sound . '"' . ($value == $sound ? ' checked="checked"' : '') . ' />' . $sound . '</label>'
+                . ' <embed autostart="false" src="'. $GLOBALS['registry']->get('themesuri', 'horde') . '/sounds/' . $sound . '" /></li>';
+        }
+        return $html . '</ul>';
+    }
+
+    protected function _renderVarInput_colorpicker($form, &$var, &$vars)
+    {
+        global $registry, $browser;
+
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        $color = $var->getValue($vars);
+        if ($color) {
+            $style = ' style="background-color:' . $color . ';color:'
+                . (Horde_Image::brightness($color) < 128 ? '#fff' : '#000') . '"';
+        } else {
+            $style = '';
+        }
+        $html = '<table cellspacing="0"><tr><td>'
+            . '<input type="text" size="10" maxlength="7" name="'
+            . $varname . '" id="' . $varname . '"' . $style
+            . ' value="' . @htmlspecialchars($color, ENT_QUOTES, $this->_charset)
+            . '" /></td>';
+        if ($browser->hasFeature('javascript')) {
+            Horde::addScriptFile('prototype.js', 'horde');
+            Horde::addScriptFile('colorpicker.js', 'horde');
+            $html .= '<td>'
+                . Horde::link('#', _("Color Picker"), '', '',
+                              'new ColorPicker({ color: \'' . htmlspecialchars($color) . '\', offsetParent: Event.element(event), update: [[\'' . $varname . '\', \'value\'], [\'' . $varname . '\', \'background\']] }); return false;')
+                . Horde::img('colorpicker.png', _("Color Picker"), 'height="16"', $registry->getImageDir('horde')) . '</a></td>';
+        }
+        return $html . '</tr></table>';
+    }
+
+    protected function _renderVarInput_sorter($form, &$var, &$vars)
+    {
+        global $registry;
+
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        $instance = $var->type->getProperty('instance');
+
+        Horde::addScriptFile('sorter.js', 'horde');
+
+        return '<input type="hidden" name="' . $varname .
+            '[array]" value="" ' . $this->_genID($varname . '[array]') . '/>' .
+            '<select class="leftFloat" multiple="multiple" size="' .
+            (int)$var->type->getSize() . '" name="' . $varname .
+            '[list]" onchange="' . $instance . '.deselectHeader();" ' .
+            $this->_genID($varname . '[list]') . '>' .
+            $var->type->getOptions($var->getValue($vars)) . '</select><div class="leftFloat">' .
+            Horde::link('#', _("Move up"), '', '', $instance . '.moveColumnUp(); return false;') . Horde::img('nav/up.png', _("Move up"), '', $registry->getImageDir('horde')) . '</a><br />' .
+            Horde::link('#', _("Move up"), '', '', $instance . '.moveColumnDown(); return false;') . Horde::img('nav/down.png', _("Move down"), '', $registry->getImageDir('horde')) . '</a></div>' .
+            '<script type="text/javascript">' . "\n" .
+            sprintf('%1$s = new Horde_Form_Sorter(\'%1$s\', \'%2$s\', \'%3$s\');' . "\n",
+                    $instance, $varname, $var->type->getHeader()) .
+            sprintf("%s.setHidden();\n</script>\n", $instance);
+    }
+
+    protected function _renderVarInput_assign($form, &$var, &$vars)
+    {
+        global $registry;
+
+        Horde::addScriptFile('form_assign.js', 'horde');
+
+        $name = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        $size = $var->type->getSize();
+        $width = $var->type->getWidth();
+        $lhdr = (bool)$var->type->getHeader(0);
+        $rhdr = (bool)$var->type->getHeader(1);
+        $this->_addOnLoadJavascript('Horde_Form_Assign.setField(\'' . $form->getName() . '\', \'' . $var->getVarName() . '\');');
+
+        return '<input type="hidden" name="' . $name . '__values" />' .
+            '<table style="width:auto"><tr><td>' .
+            sprintf('<select name="%s__left" multiple="multiple" size="%d" style="width:%s"%s>',
+                    $name, $size, $width,
+                    $lhdr ? ' onchange="Horde_Form_Assign.deselectHeaders(\'' . $form->getName() . '\', \'' . $var->getVarName() . '\', 0);"' : '') .
+            $var->type->getOptions(0, $form->getName(), $var->getVarName()) .
+            '</select></td><td>' .
+            '<a href="#" onclick="Horde_Form_Assign.move(\'' . $form->getName() . '\', \'' . $var->getVarName() . '\', 0); return false;">' .
+            Horde::img('rhand.png', _("Add"), null, $registry->getImageDir('horde')) .
+            '</a><br /><a href="#" onclick="Horde_Form_Assign.move(\'' .
+            $form->getName() . '\', \'' . $var->getVarName() . '\', 1); return false;">' .
+            Horde::img('lhand.png', _("Remove"), null, $registry->getImageDir('horde')) .
+            '</a></td><td>' .
+            sprintf('<select name="%s__right" multiple="multiple" size="%d" style="width:%s"%s>',
+                    $name, $size, $width,
+                    $rhdr ? ' onchange="Horde_Form_Assign.deselectHeaders(\'' . $form->getName() . '\', \'' . $var->getVarName() . '\', 1);"' : '') .
+            $var->type->getOptions(1, $form->getName(), $var->getVarName()) .
+            '</select></td></tr></table>';
+    }
+
+    protected function _renderVarInput_invalid($form, &$var, &$vars)
+    {
+        return $this->_renderVarDisplay_invalid($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_enum($form, &$var, &$vars)
+    {
+        $values = $var->getValues();
+        $prompt = $var->type->getPrompt();
+        $htmlchars = $var->getOption('htmlchars');
+        if (!empty($prompt)) {
+            $prompt = '<option value="">' . ($htmlchars ? $prompt : @htmlspecialchars($prompt, ENT_QUOTES, $this->_charset)) . '</option>';
+        }
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<select name="%s" id="%s" %s>%s%s</select>',
+                       $varname,
+                       $varname,
+                       $this->_getActionScripts($form, $var),
+                       $prompt,
+                       $this->_selectOptions($values, $var->getValue($vars), $htmlchars));
+    }
+
+    protected function _renderVarInput_mlenum($form, &$var, &$vars)
+    {
+        $varname = $var->getVarName();
+        $hvarname = @htmlspecialchars($varname, ENT_QUOTES, $this->_charset);
+        $values = $var->getValues();
+        $prompts = $var->type->getPrompts();
+        $selected = $var->getValue($vars);
+        /* If passing a non-array value need to get the keys. */
+        if (!is_array($selected)) {
+            foreach ($values as $key_1 => $values_2) {
+                if (isset($values_2[$selected])) {
+                    $selected = array('1' => $key_1, '2' => $selected);
+                    break;
+                }
+            }
+        }
+
+        /* Hidden tag to store the current first level. */
+        $html = sprintf('<input type="hidden" name="%s[old]" value="%s" %s />',
+                        $hvarname,
+                        @htmlspecialchars($selected['1'], ENT_QUOTES, $this->_charset),
+                        $this->_genID($varname . '[old]'));
+
+        /* First level. */
+        $values_1 = Horde_Array::valuesToKeys(array_keys($values));
+        $html .= sprintf('<select %s name="%s[1]" onchange="%s"%s>',
+                         $this->_genID($varname . '[1]'),
+                         $hvarname,
+                         'if (this.value) { document.' . $form->getName() . '.formname.value=\'\';' . 'document.' . $form->getName() . '.submit() }',
+                         ($var->hasAction() ? ' ' . $this->_genActionScript($form, $var->_action, $varname) : ''));
+        if (!empty($prompts)) {
+            $html .= '<option value="">' . @htmlspecialchars($prompts[0], ENT_QUOTES, $this->_charset) . '</option>';
+        }
+        $html .= $this->_selectOptions($values_1, $selected['1']);
+        $html .= '</select>';
+
+        /* Second level. */
+        $html .= sprintf('<select %s name="%s[2]"%s>',
+                         $this->_genID($varname . '[2]'),
+                         $hvarname,
+                         ($var->hasAction() ? ' ' . $this->_genActionScript($form, $var->_action, $varname) : ''));
+        if (!empty($prompts)) {
+            $html .= '<option value="">' . @htmlspecialchars($prompts[1], ENT_QUOTES, $this->_charset) . '</option>';
+        }
+        $values_2 = array();
+        if (!empty($selected['1'])) {
+            $values_2 = &$values[$selected['1']];
+        }
+        return $html . $this->_selectOptions($values_2, $selected['2']) . '</select>';
+    }
+
+    protected function _renderVarInput_multienum($form, &$var, &$vars)
+    {
+        $values = $var->getValues();
+        $selected = $vars->getExists($var->getVarName(), $wasset);
+        if (!$wasset) {
+            $selected = $var->getDefault();
+        }
+        return sprintf('<select multiple="multiple" size="%s" name="%s[]" %s>%s</select>',
+                       (int)$var->type->size,
+                       @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var),
+                       $this->_multiSelectOptions($values, $selected)) .
+            "<br />\n" . _("To select multiple items, hold down the Control (PC) or Command (Mac) key while clicking.") . "\n";
+    }
+
+    protected function _renderVarInput_keyval_multienum($form, &$var, &$vars)
+    {
+        return $this->_renderVarInput_multienum($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_radio($form, &$var, &$vars)
+    {
+        return $this->_radioButtons($var->getVarName(),
+                                    $var->getValues(),
+                                    $var->getValue($vars),
+                                    $this->_getActionScripts($form, $var));
+    }
+
+    protected function _renderVarInput_set($form, &$var, &$vars)
+    {
+        $html = $this->_checkBoxes($var->getVarName(),
+                                   $var->getValues(),
+                                   $var->getValue($vars),
+                                   $this->_getActionScripts($form, $var));
+
+        if ($var->type->getProperty('checkAll')) {
+            $form_name = $form->getName();
+            $var_name = $var->getVarName() . '[]';
+            $function_name = 'select'  . $form_name . $var->getVarName();
+            $enable = _("Select all");
+            $disable = _("Select none");
+            $invert = _("Invert selection");
+            $html .= <<<EOT
+<script type="text/javascript">
+function $function_name()
+{
+    for (var i = 0; i < document.$form_name.elements.length; i++) {
+        f = document.$form_name.elements[i];
+        if (f.name != '$var_name') {
+            continue;
+        }
+        if (arguments.length) {
+            f.checked = arguments[0];
+        } else {
+            f.checked = !f.checked;
+        }
+    }
+}
+</script>
+<a href="#" onclick="$function_name(true); return false;">$enable</a>,
+<a href="#" onclick="$function_name(false); return false;">$disable</a>,
+<a href="#" onclick="$function_name(); return false;">$invert</a>
+EOT;
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarInput_link($form, &$var, &$vars)
+    {
+        return $this->_renderVarDisplay_link($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_html($form, &$var, &$vars)
+    {
+        return $this->_renderVarDisplay_html($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_email($form, &$var, &$vars)
+    {
+        return sprintf('<input type="text" name="%s" id="%s" value="%s"%s />',
+                       $var->getVarName(),
+                       $var->getVarName(),
+                       @htmlspecialchars($var->getValue($vars)),
+                       $this->_getActionScripts($form, $var));
+    }
+
+    protected function _renderVarInput_matrix($form, &$var, &$vars)
+    {
+        $varname   = $var->getVarName();
+        $var_array = $var->getValue($vars);
+        $cols      = $var->type->getCols();
+        $rows      = $var->type->getRows();
+        $matrix    = $var->type->getMatrix();
+        $new_input = $var->type->getNewInput();
+
+        $html = '<table cellspacing="0"><tr>';
+
+        $html .= '<td class="rightAlign" width="20%"></td>';
+        foreach ($cols as $col_title) {
+            $html .= '<td align="center" width="1%">' . htmlspecialchars($col_title) . '</td>';
+        }
+        $html .= '<td class="rightAlign" width="60%"></td></tr>';
+
+        /* Offer a new row of data to be added to the matrix? */
+        if ($new_input) {
+            $html .= '<tr><td>';
+            if (is_array($new_input)) {
+                $html .= sprintf('<select %s name="%s[n][r]"><option value="">%s</option>%s</select><br />',
+                       $this->_genID($varname . '[n][r]'),
+                       $varname,
+                       _("-- select --"),
+                       $this->_selectOptions($new_input, $var_array['n']['r']));
+            } elseif ($new_input == true) {
+                $html .= sprintf('<input %s type="text" name="%s[n][r]" value="%s" />',
+                       $this->_genID($varname . '[n][r]'),
+                       $varname,
+                       $var_array['n']['r']);
+            }
+            $html .= ' </td>';
+            foreach ($cols as $col_id => $col_title) {
+                $html .= sprintf('<td align="center"><input type="checkbox" class="checkbox" name="%s[n][v][%s]" /></td>', $varname, $col_id);
+            }
+            $html .= '<td> </td></tr>';
+        }
+
+        /* Loop through the rows and create checkboxes for each column. */
+        foreach ($rows as $row_id => $row_title) {
+            $html .= sprintf('<tr><td>%s</td>', $row_title);
+            foreach ($cols as $col_id => $col_title) {
+                $html .= sprintf('<td align="center"><input type="checkbox" class="checkbox" name="%s[r][%s][%s]"%s /></td>', $varname, $row_id, $col_id, (!empty($matrix[$row_id][$col_id]) ? ' checked="checked"' : ''));
+            }
+            $html .= '<td> </td></tr>';
+        }
+
+        return $html . '</table>';
+    }
+
+    protected function _renderVarInput_password($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="password" name="%s" id="%s" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var));
+    }
+
+    protected function _renderVarInput_emailconfirm($form, &$var, &$vars)
+    {
+        $email = $var->getValue($vars);
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" name="%s[original]" id="%s[original]" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($email['original'], ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var)) .
+            ' ' . sprintf('<input type="text" name="%s[confirm]" id="%s[confirm]" value="%s"%s />',
+                          $varname,
+                          $varname,
+                          @htmlspecialchars($email['confirm'], ENT_QUOTES, $this->_charset),
+                          $this->_getActionScripts($form, $var));
+    }
+
+    protected function _renderVarInput_passwordconfirm($form, &$var, &$vars)
+    {
+        $password = $var->getValue($vars);
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="password" name="%s[original]" id="%s[original]" value="%s"%s />',
+                       $varname,
+                       $varname,
+                       @htmlspecialchars($password['original'], ENT_QUOTES, $this->_charset),
+                       $this->_getActionScripts($form, $var)) .
+            ' ' . sprintf('<input type="password" name="%s[confirm]" id="%s[confirm]" value="%s"%s />',
+                          $varname,
+                          $varname,
+                          @htmlspecialchars($password['confirm'], ENT_QUOTES, $this->_charset),
+                          $this->_getActionScripts($form, $var));
+    }
+
+    protected function _renderVarInput_boolean($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        $html = '<input type="checkbox" class="checkbox" name="' .  $varname . '"' .
+            ' id="' . $varname . '"' . ($var->getValue($vars) ? ' checked="checked"' : '');
+        if ($var->hasAction()) {
+            $html .= $this->_genActionScript($form, $var->_action,
+                                             $var->getVarName());
+        }
+        return $html . ' />';
+    }
+
+    protected function _renderVarInput_creditcard($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        $html = '<input type="text" name="' . $varname . '" id="' . $varname . '" value="' .
+            @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset) . '"';
+        if ($var->hasAction()) {
+            $html .= $this->_genActionScript($form, $var->_action,
+                                             $var->getVarName());
+        }
+        return $html . ' />';
+    }
+
+    protected function _renderVarInput_obrowser($form, &$var, &$vars)
+    {
+        $varname = $var->getVarName();
+        $varvalue = $vars->get($varname);
+        $fieldId = $this->_genID(md5(uniqid(rand(), true)), false) . 'id';
+        $html = '
+            <script type="text/javascript">
+            var obrowserWindowName;
+            function obrowserCallback(name, oid)
+            {
+                if (name == obrowserWindowName) {
+                    document.getElementById(\'' . $fieldId . '\').value = oid;
+                    return false;
+                } else {
+                    return "Invalid window name supplied";
+                }
+            }
+            </script>
+            ';
+        $html .= sprintf('<input type="hidden" name="%s" id="%s"%s value="%s">',
+                         @htmlspecialchars($varname, ENT_QUOTES, $this->_charset),
+                         $fieldId,
+                         $this->_getActionScripts($form, $var),
+                         @htmlspecialchars($varvalue, ENT_QUOTES, $this->_charset));
+        if (!empty($varvalue)) {
+            $html .= $varvalue;
+        }
+
+        if ($GLOBALS['browser']->hasFeature('javascript')) {
+            $html .= Horde::link($GLOBALS['registry']->get('webroot', 'horde') . '/services/obrowser/', _("Select an object"), '', '_blank', 'obrowserWindow = ' . Horde::popupJs($GLOBALS['registry']->get('webroot', 'horde') . '/services/obrowser/', array('urlencode' => true)) . 'obrowserWindowName = obrowserWindow.name; return false;') . Horde::img('tree/leaf.png', _("Object"), '', $GLOBALS['registry']->getImageDir('horde')) . "</a>\n";
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarInput_dblookup($form, &$var, &$vars)
+    {
+        return $this->_renderVarInput_enum($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_figlet($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" name="%s" id="%s" size="%s" value="%s" />',
+                       $varname,
+                       $varname,
+                       strlen($var->type->getText()),
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset)) .
+            '<br />' . _("Enter the letters below:") . '<br />' .
+            $this->_renderVarDisplay_figlet($form, $var, $vars);
+    }
+
+    protected function _renderVarInput_captcha($form, &$var, &$vars)
+    {
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        return sprintf('<input type="text" name="%s" id="%s" size="%s" value="%s" />',
+                       $varname,
+                       $varname,
+                       strlen($var->type->getText()),
+                       @htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset)) .
+            '<br />' . _("Enter the letters below:") . '<br />' .
+            $this->_renderVarDisplay_captcha($form, $var, $vars);
+    }
+
+    protected function _renderVarDisplayDefault($form, &$var, &$vars)
+    {
+        return nl2br(@htmlspecialchars($var->getValue($vars), ENT_QUOTES, $this->_charset));
+    }
+
+    protected function _renderVarDisplay_html($form, &$var, &$vars)
+    {
+        // Since this is an HTML type we explicitly don't escape
+        // it. User beware.
+        return $var->getValue($vars);
+    }
+
+    protected function _renderVarDisplay_email($form, &$var, &$vars)
+    {
+        $email_val = $var->getValue($vars);
+
+        if ($var->type->getProperty('link_compose')) {
+            // Multiple email addresses?
+            $addrs = $var->type->getProperty('allow_multi')
+                ? Horde_Mime_Address::explode($email_val)
+                : array($email_val);
+
+            $link = '';
+            foreach ($addrs as $addr) {
+                $addr = trim($addr);
+
+                $display_email = $addr;
+                if ($var->type->getProperty('strip_domain') && strpos($addr, '@') !== false) {
+                    $display_email = str_replace(array('@', '.'),
+                                                 array(' (at) ', ' (dot) '),
+                                                 $addr);
+                }
+
+                // Format the address according to RFC822.
+                $mailbox_host = explode('@', $addr);
+                if (!isset($mailbox_host[1])) {
+                    $mailbox_host[1] = '';
+                }
+
+                $name = $var->type->getProperty('link_name');
+
+                $address = Horde_Mime_Address::writeAddress($mailbox_host[0], $mailbox_host[1], $name);
+
+                // Get rid of the trailing @ (when no host is included in
+                // the email address).
+                $address = str_replace('@>', '>', $address);
+                $mail_link = $GLOBALS['registry']->call('mail/compose', array(array('to' => addslashes($address))));
+                if (is_a($mail_link, 'PEAR_Error')) {
+                    $mail_link = 'mailto:' . urlencode($address);
+                }
+
+                if (!empty($link)) {
+                    $link .= ', ';
+                }
+                $link .= Horde::link($mail_link, $addr) . @htmlspecialchars($display_email, ENT_QUOTES, $this->_charset) . '</a>';
+            }
+
+            return $link;
+        } else {
+            $email_val = trim($email_val);
+
+            if ($var->type->getProperty('strip_domain') && strpos($email_val, '@') !== false) {
+                $email_val = str_replace(array('@', '.'),
+                                         array(' (at) ', ' (dot) '),
+                                         $email_val);
+            }
+
+            return nl2br(@htmlspecialchars($email_val, ENT_QUOTES, $this->_charset));
+        }
+    }
+
+    protected function _renderVarDisplay_password($form, &$var, &$vars)
+    {
+        return '********';
+    }
+
+    protected function _renderVarDisplay_passwordconfirm($form, &$var, &$vars)
+    {
+        return '********';
+    }
+
+    protected function _renderVarDisplay_octal($form, &$var, &$vars)
+    {
+        return sprintf('0%o', octdec($var->getValue($vars)));
+    }
+
+    protected function _renderVarDisplay_boolean($form, &$var, &$vars)
+    {
+        return $var->getValue($vars) ? _("Yes") : _("No");
+    }
+
+    protected function _renderVarDisplay_enum($form, &$var, &$vars)
+    {
+        $values = $var->getValues();
+        $value = $var->getValue($vars);
+        if (count($values) == 0) {
+            return _("No values");
+        } elseif (isset($values[$value]) && $value != '') {
+            return @htmlspecialchars($values[$value], ENT_QUOTES, $this->_charset);
+        }
+    }
+
+    protected function _renderVarDisplay_radio($form, &$var, &$vars)
+    {
+        $values = $var->getValues();
+        if (count($values) == 0) {
+            return _("No values");
+        } elseif (isset($values[$var->getValue($vars)])) {
+            return @htmlspecialchars($values[$var->getValue($vars)], ENT_QUOTES, $this->_charset);
+        }
+    }
+
+    protected function _renderVarDisplay_multienum($form, &$var, &$vars)
+    {
+        $values = $var->getValues();
+        $on = $var->getValue($vars);
+        if (!count($values) || !count($on)) {
+            return _("No values");
+        } else {
+            $display = array();
+            foreach ($values as $value => $name) {
+                if (in_array($value, $on)) {
+                    $display[] = $name;
+                }
+            }
+            return @htmlspecialchars(implode(', ', $display), ENT_QUOTES, $this->_charset);
+        }
+    }
+
+    protected function _renderVarDisplay_set($form, &$var, &$vars)
+    {
+        $values = $var->getValues();
+        $on = $var->getValue($vars);
+        if (!count($values) || !count($on)) {
+            return _("No values");
+        } else {
+            $display = array();
+            foreach ($values as $value => $name) {
+                if (in_array($value, $on)) {
+                    $display[] = $name;
+                }
+            }
+            return @htmlspecialchars(implode(', ', $display), ENT_QUOTES, $this->_charset);
+        }
+    }
+
+    protected function _renderVarDisplay_image($form, &$var, &$vars)
+    {
+        $image = $var->getValue($vars);
+
+        /* Check if existing image data is being loaded. */
+        $var->type->loadImageData($image);
+
+        if (empty($image['img'])) {
+            return '';
+        }
+
+        $img = Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/images/view.php');
+        if (isset($image['img']['vfs_id'])) {
+            /* Calling an image from VFS. */
+            $img = Horde_Util::addParameter($img, array('f' => $image['img']['vfs_id'],
+                                                  's' => 'vfs',
+                                                  'p' => $image['img']['vfs_path']));
+        } else {
+            /* Calling an image from a tmp directory (uploads). */
+            $img = Horde_Util::addParameter($img, 'f', $image['img']['file']);
+        }
+
+        return Horde::img($img, '', '', '');
+    }
+
+    protected function _renderVarDisplay_phone($form, &$var, &$vars)
+    {
+        global $registry;
+
+        $number = $var->getValue($vars);
+        $html = @htmlspecialchars($number, ENT_QUOTES, $this->_charset);
+
+        if ($number && $registry->hasMethod('telephony/dial')) {
+            $url = $registry->call('telephony/dial', array($number));
+            $label = sprintf(_("Dial %s"), $number);
+            $html .= ' ' . Horde::link($url, $label) . Horde::img('phone.png', $label, '', $registry->getImageDir('horde')) . '</a>';
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarDisplay_cellphone($form, &$var, &$vars)
+    {
+        global $registry;
+
+        $html = $this->_renderVarDisplay_phone($form, $var, $vars);
+
+        $number = $var->getValue($vars);
+        if ($number && $registry->hasMethod('sms/compose')) {
+            $url = $registry->link('sms/compose', array('to' => $number));
+            $html .= ' ' . Horde::link($url, _("Send SMS")) . Horde::img('mobile.png', _("Send SMS"), '', $registry->getImageDir('horde')) . '</a>';
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarDisplay_address($form, &$var, &$vars, $text = true)
+    {
+        global $registry;
+
+        $address = $var->getValue($vars);
+        if (empty($address)) {
+            return '';
+        }
+
+        $info = $var->type->parse($address);
+
+        $google_icon = 'map.png';
+        if (!empty($info['country'])) {
+            switch ($info['country']) {
+            case 'uk':
+                /* Multimap.co.uk generated map */
+                $mapurl = 'http://www.multimap.com/map/browse.cgi?pc='
+                    . urlencode($info['zip']);
+                $desc = _("Multimap UK map");
+                $icon = 'map.png';
+                break;
+
+            case 'au':
+                /* Whereis.com.au generated map */
+                $mapurl = 'http://www.whereis.com.au/whereis/mapping/geocodeAddress.do?';
+                $desc = _("Whereis Australia map");
+                $icon = 'map.png';
+                /* See if it's the street number & name. */
+                if (isset($info['streetNumber']) &&
+                    isset($info['streetName'])) {
+                    $mapurl .= '&streetNumber='
+                        . urlencode($info['streetNumber']) . '&streetName='
+                        . urlencode($info['streetName']);
+                }
+                /* Look for "Suburb, State". */
+                if (isset($info['city'])) {
+                    $mapurl .= '&suburb=' . urlencode($info['city']);
+                }
+                /* Look for "State <4 digit postcode>". */
+                if (isset($info['state'])) {
+                    $mapurl .= '&state=' . urlencode($info['state']);
+                }
+                break;
+
+            case 'us':
+            case 'ca':
+                /* American/Canadian address style. */
+                /* Mapquest generated map */
+                $mapurl = 'http://www.mapquest.com/maps/map.adp?size=big&zoom=7';
+                $desc = _("MapQuest map");
+                $icon = 'map.png';
+                if (!empty($info['street'])) {
+                    $mapurl .= '&address=' . urlencode($info['street']);
+                }
+                if (!empty($info['city'])) {
+                    $mapurl .= '&city=' . urlencode($info['city']);
+                }
+                if (!empty($info['state'])) {
+                    $mapurl .= '&state=' . urlencode($info['state']);
+                }
+                if (!empty($info['zip'])) {
+                    if ($info['country'] == 'ca') {
+                        $mapurl .= '&country=CA';
+                    }
+                    $mapurl .= '&zipcode=' . urlencode($info['zip']);
+                }
+
+                /* Yahoo! generated map. */
+                $mapurl2 = 'http://us.rd.yahoo.com/maps/home/submit_a/*-http://maps.yahoo.com/maps?srchtype=a&getmap=Get+Map&';
+                $desc2 = _("Yahoo! map");
+                $icon2 = 'map.png';
+                if (!empty($info['street'])) {
+                    $mapurl2 .= '&addr=' . urlencode($info['street']);
+                }
+                /* Give precedence to zipcode over city/state */
+                if (empty($info['zip']) &&
+                    !empty($info['city']) && !empty($info['state'])) {
+                    $mapurl2 .= '&csz='
+                        . urlencode($info['city'] . ' ' . $info['state']);
+                }
+                if (!empty($info['zip'])) {
+                    if (preg_match('|([a-zA-Z]\d[a-zA-Z])\s?(\d[a-zA-Z]\d)|', $info['zip'], $pcParts)) {
+                        $mapurl2 .= '&country=ca';
+                        /* make sure the postal-code has a space */
+                        $info['zip'] = $pcParts[1] . ' ' . $pcParts[2];
+                    }
+                    $mapurl2 .= '&csz=' . urlencode($info['zip']);
+                }
+                break;
+
+            default:
+                if (!count($info)) {
+                    break;
+                }
+                /* European address style. */
+                $google_icon = 'map_eu.png';
+                /* Mapquest generated map. */
+                $mapurl2 = 'http://www.mapquest.com/maps/map.adp?country=' . Horde_String::upper($info['country']);
+                $desc2 = _("MapQuest map");
+                $icon2 = 'map_eu.png';
+                if (!empty($info['street'])) {
+                    $mapurl2 .= '&address=' . urlencode($info['street']);
+                }
+                if (!empty($info['zip'])) {
+                    $mapurl2 .= '&zipcode=' . urlencode($info['zip']);
+                }
+                if (!empty($info['city'])) {
+                    $mapurl2 .= '&city=' . urlencode($info['city']);
+                }
+                break;
+            }
+        }
+
+        $html = $text ? nl2br(@htmlspecialchars($address, ENT_QUOTES, $this->_charset)) : '';
+        if (!empty($mapurl)) {
+            $html .= '&nbsp;&nbsp;' . Horde::link(Horde::externalUrl($mapurl), $desc, null, '_blank') . Horde::img($icon, $desc, '', $registry->getImageDir('horde')) . '</a>';
+        }
+        if (!empty($mapurl2)) {
+            $html .= '&nbsp;' . Horde::link(Horde::externalUrl($mapurl2), $desc2, null, '_blank') . Horde::img($icon2, $desc2, '', $registry->getImageDir('horde')) . '</a>';
+        }
+
+        /* Google generated map. */
+        if ($address) {
+            $html .= '&nbsp;' . Horde::link(Horde::externalUrl('http://maps.google.com/maps?q=' . urlencode(preg_replace('/\r?\n/', ',', $address)) . '&hl=en'), _("Google Maps"), null, '_blank') . Horde::img($google_icon, _("Google Maps"), '', $registry->getImageDir('horde')) . '</a>';
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarDisplay_addresslink($form, &$var, &$vars)
+    {
+        return $this->_renderVarDisplay_address($form, $var, $vars, false);
+    }
+
+    protected function _renderVarDisplay_pgp($form, &$var, &$vars)
+    {
+        $key = $var->getValue($vars);
+        if (empty($key)) {
+            return '';
+        }
+        $pgp = Horde_Crypt::factory('pgp', $var->type->getPGPParams());
+        return '<pre>' . $pgp->pgpPrettyKey($key) . '</pre>';
+    }
+
+    protected function _renderVarDisplay_smime($form, &$var, &$vars)
+    {
+        $cert = $var->getValue($vars);
+        if (empty($cert)) {
+            return '';
+        }
+        $smime = Horde_Crypt::factory('smime', $var->type->getSMIMEParams());
+        return $smime->certToHTML($cert);
+    }
+
+    protected function _renderVarDisplay_country($form, &$var, &$vars)
+    {
+        return $this->_renderVarDisplay_enum($form, $var, $vars);
+    }
+
+    protected function _renderVarDisplay_date($form, &$var, &$vars)
+    {
+        return htmlspecialchars($var->type->getFormattedTime($var->getValue($vars)));
+    }
+
+    protected function _renderVarDisplay_hourminutesecond($form, &$var, &$vars)
+    {
+        $time = $var->type->getTimeParts($var->getValue($vars));
+        if (!$var->type->getProperty('show_seconds')) {
+            return (int)$time['hour'] . ':' . sprintf('%02d', (int)$time['minute']);
+        } else {
+            return (int)$time['hour'] . ':' . sprintf('%02d', (int)$time['minute']) . ':' . sprintf('%02d', (int)$time['second']);
+        }
+    }
+
+    protected function _renderVarDisplay_monthyear($form, &$var, &$vars)
+    {
+        return (int)$vars->get($var->getVarName() . '[month]') . ', ' . (int)$vars->get($var->getVarName() . '[year]');
+    }
+
+    protected function _renderVarDisplay_monthdayyear($form, &$var, &$vars)
+    {
+        $date = $var->getValue($vars);
+        if ((is_array($date) && !empty($date['year']) &&
+             !empty($date['month']) && !empty($date['day'])) ||
+            (!is_array($date) && !empty($date) && $date != '0000-00-00')) {
+            return $var->type->formatDate($date);
+        }
+        return '';
+    }
+
+    protected function _renderVarDisplay_datetime($form, &$var, &$vars)
+    {
+        return htmlspecialchars($var->type->formatDate($var->getValue($vars))) . Horde_Form_Type_date::getAgo($var->getValue($vars));
+    }
+
+    protected function _renderVarDisplay_invalid($form, &$var, &$vars)
+    {
+        return '<span class="form-error">' . @htmlspecialchars($var->type->message, ENT_QUOTES, $this->_charset) . '</span>';
+    }
+
+    protected function _renderVarDisplay_link($form, &$var, &$vars)
+    {
+        $values = $var->type->values;
+        if (!isset($values[0])) {
+            $values = array($values);
+        }
+
+        $count = count($values);
+        $html = '';
+        for ($i = 0; $i < $count; $i++) {
+            if (empty($values[$i]['url']) || empty($values[$i]['text'])) {
+                continue;
+            }
+            if (!isset($values[$i]['target'])) {
+                $values[$i]['target'] = '';
+            }
+            if (!isset($values[$i]['onclick'])) {
+                $values[$i]['onclick'] = '';
+            }
+            if (!isset($values[$i]['title'])) {
+                $values[$i]['title'] = '';
+            }
+            if (!isset($values[$i]['accesskey'])) {
+                $values[$i]['accesskey'] = '';
+            }
+            if ($i > 0) {
+                $html .= ' | ';
+            }
+            $html .= Horde::link($values[$i]['url'], $values[$i]['text'], 'widget', $values[$i]['target'], $values[$i]['onclick'], $values[$i]['title'], $values[$i]['accesskey']) . @htmlspecialchars($values[$i]['text'], ENT_QUOTES, $this->_charset) . '</a>';
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarDisplay_dblookup($form, &$var, &$vars)
+    {
+        return $this->_renderVarDisplay_enum($form, $var, $vars);
+    }
+
+    protected function _renderVarDisplay_figlet($form, &$var, &$vars)
+    {
+        static $figlet;
+
+        if (!isset($figlet)) {
+            $figlet = new Text_Figlet();
+        }
+
+        $result = $figlet->loadFont($var->type->getFont());
+        if (is_a($result, 'PEAR_Error')) {
+            return $result->getMessage();
+        }
+
+        return '<pre>' . $figlet->lineEcho($var->type->getText()) . '</pre>';
+    }
+
+    protected function _renderVarDisplay_captcha($form, &$var, &$vars)
+    {
+        static $captcha;
+
+        if (!isset($captcha)) {
+            $captcha = Text_CAPTCHA::factory('Image');
+        }
+
+        $image = $captcha->init(150, 60, $var->type->getText(),
+                                array('font_path' => dirname($var->type->getFont()) . '/',
+                                      'font_file' => basename($var->type->getFont())));
+        if (is_a($image, 'PEAR_Error')) {
+            return $image->getMessage();
+        }
+
+        $cid = md5($var->type->getText());
+        $cache = &Horde_Cache::singleton($GLOBALS['conf']['cache']['driver'],
+                                         Horde::getDriverConfig('cache', $GLOBALS['conf']['cache']['driver']));
+        $cache->set($cid, serialize(array('data' => $captcha->getCAPTCHAAsJPEG(),
+                                          'ctype' => 'image/jpeg')));
+
+        $url = Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/cacheview.php');
+        $url = Horde_Util::addParameter($url, 'cid', $cid, false);
+
+        return '<img src="' . $url . '" />';
+
+    }
+
+    protected function _renderVarInput_selectFiles($form, &$var, &$vars)
+    {
+        /* Needed for gollem js calls */
+        $varname = @htmlspecialchars($var->getVarName(), ENT_QUOTES, $this->_charset);
+        $html = sprintf('<input type="hidden" name="%s" id="%s" value="%s" />',
+                        'selectlist_selectid',
+                        'selectlist_selectid',
+                        $var->type->getProperty('selectid')) .
+            sprintf('<input type="hidden" name="%s" id="%s" />', 'actionID', 'actionID') .
+
+            /* Form field. */
+            sprintf('<input type="hidden" name="%s" id="%s" value="%s" />',
+                    $varname,
+                    $varname,
+                    $var->type->getProperty('selectid'));
+
+        /* Open window link. */
+        $param = array($var->type->getProperty('link_text'),
+                       $var->type->getProperty('link_style'),
+                       $form->getName(),
+                       $var->type->getProperty('icon'),
+                       $var->type->getProperty('selectid'));
+        $html .= $GLOBALS['registry']->call('files/selectlistLink', $param) . "<br />\n";
+
+        if ($var->type->getProperty('selectid')) {
+            $param = array($var->type->getProperty('selectid'));
+            $files = $GLOBALS['registry']->call('files/selectlistResults', $param);
+            if ($files) {
+                $html .= '<ol>';
+                foreach ($files as $id => $file) {
+                    $dir = key($file);
+                    $filename = current($file);
+                    if ($GLOBALS['registry']->hasMethod('files/getViewLink')) {
+                        $filename = basename($filename);
+                        $url = $GLOBALS['registry']->call('files/getViewLink', array($dir, $filename));
+                        $filename = Horde::link($url, _("Preview"), null, 'form_file_view') . @htmlspecialchars(Horde_Util::realPath($dir . '/' . $filename), ENT_QUOTES, $this->_charset) . '</a>';
+                    } else {
+                        if (!empty($dir) && ($dir != '.')) {
+                            $filename = $dir . '/' . $filename;
+                        }
+                        $filename = @htmlspecialchars($filename, ENT_QUOTES, $this->_charset);
+                    }
+                    $html .= '<li>' . $filename . "</li>\n";
+                }
+                $html .= '</ol>';
+            }
+        }
+
+        return $html;
+    }
+
+    protected function _renderVarInput_category($form, &$var, &$vars)
+    {
+        Horde::addScriptFile('form_helpers.js', 'horde');
+        require_once 'Horde/Prefs/CategoryManager.php';
+        $this->_addOnLoadJavascript('addEvent(document.getElementById(\'' . $form->getName() . '\'), \'submit\', checkCategory);');
+        return '<input type="hidden" name="new_category" />'
+            . Prefs_CategoryManager::getJavaScript($form->getName(), $var->getVarName())
+            . Prefs_CategoryManager::getSelect($var->getVarName(), $var->getValue($vars));
+    }
+
+    protected function _selectOptions(&$values, $selectedValue = false,
+                            $htmlchars = false)
+    {
+        $result = '';
+        $sel = false;
+        foreach ($values as $value => $display) {
+            if (!is_null($selectedValue) && !$sel &&
+                $value == $selectedValue &&
+                strlen($value) == strlen($selectedValue)) {
+                $selected = ' selected="selected"';
+                $sel = true;
+            } else {
+                $selected = '';
+            }
+            $result .= ' <option value="';
+            $result .= $htmlchars
+                ? $value
+                : @htmlspecialchars($value, ENT_QUOTES, $this->_charset);
+            $result .= '"' . $selected . '>';
+            $result .= $htmlchars
+                ? $display
+                : @htmlspecialchars($display, ENT_QUOTES, $this->_charset);
+            $result .= "</option>\n";
+        }
+
+        return $result;
+    }
+
+    protected function _multiSelectOptions(&$values, $selectedValues)
+    {
+        if (!is_array($selectedValues)) {
+            $selectedValues = array();
+        } else {
+            $selectedValues = array_flip($selectedValues);
+        }
+
+        $result = '';
+        $sel = false;
+        foreach ($values as $value => $display) {
+            if (isset($selectedValues[$value])) {
+                $selected = ' selected="selected"';
+            } else {
+                $selected = '';
+            }
+            $result .= " <option value=\"" . @htmlspecialchars($value, ENT_QUOTES, $this->_charset) . "\"$selected>" . @htmlspecialchars($display, ENT_QUOTES, $this->_charset) . "</option>\n";
+        }
+
+        return $result;
+    }
+
+    protected function _checkBoxes($name, $values, $checkedValues, $actions = '')
+    {
+        $result = '';
+        if (!is_array($checkedValues)) {
+            $checkedValues = array();
+        }
+        $i = 0;
+        foreach ($values as $value => $display) {
+            $checked = (in_array($value, $checkedValues)) ? ' checked="checked"' : '';
+            $result .= sprintf('<input id="%s%s" type="checkbox" class="checkbox" name="%s[]" value="%s"%s%s /><label for="%s%s">&nbsp;%s</label><br />',
+                               @htmlspecialchars($name, ENT_QUOTES, $this->_charset),
+                               $i,
+                               @htmlspecialchars($name, ENT_QUOTES, $this->_charset),
+                               @htmlspecialchars($value, ENT_QUOTES, $this->_charset),
+                               $checked,
+                               $actions,
+                               @htmlspecialchars($name, ENT_QUOTES, $this->_charset),
+                               $i,
+                               @htmlspecialchars($display, ENT_QUOTES, $this->_charset));
+            $i++;
+        }
+
+        return $result;
+    }
+
+    protected function _radioButtons($name, $values, $checkedValue = null, $actions = '')
+    {
+        $result = '';
+        $i = 0;
+        foreach ($values as $value => $display) {
+            $checked = (!is_null($checkedValue) && $value == $checkedValue) ? ' checked="checked"' : '';
+            $result .= sprintf('<input id="%s%s" type="radio" class="checkbox" name="%s" value="%s"%s%s /><label for="%s%s">&nbsp;%s</label><br />',
+                               @htmlspecialchars($name, ENT_QUOTES, $this->_charset),
+                               $i,
+                               @htmlspecialchars($name, ENT_QUOTES, $this->_charset),
+                               @htmlspecialchars($value, ENT_QUOTES, $this->_charset),
+                               $checked,
+                               $actions,
+                               @htmlspecialchars($name, ENT_QUOTES, $this->_charset),
+                               $i,
+                               @htmlspecialchars($display, ENT_QUOTES, $this->_charset));
+            $i++;
+        }
+
+        return $result;
+    }
+
+    protected function _genID($name, $fulltag = true)
+    {
+        $name = @htmlspecialchars($name, ENT_QUOTES, $this->_charset);
+        return $fulltag ? 'id="' . $name . '"' : $name;
+    }
+
+    protected function _genActionScript($form, $action, $varname)
+    {
+        $html = '';
+        $triggers = $action->getTrigger();
+        if (!is_array($triggers)) {
+            $triggers = array($triggers);
+        }
+        $js = $action->getActionScript($form, $this, $varname);
+        foreach ($triggers as $trigger) {
+            if ($trigger == 'onload') {
+                $this->_addOnLoadJavascript($js);
+            } else {
+                $html .= ' ' . $trigger . '="' . $js . '"';
+            }
+        }
+        return $html;
+    }
+
+    protected function _getActionScripts($form, &$var)
+    {
+        $actions = '';
+        if ($var->hasAction()) {
+            $varname = $var->getVarName();
+            $action = &$var->_action;
+            $triggers = $action->getTrigger();
+            if (!is_array($triggers)) {
+                $triggers = array($triggers);
+            }
+            $js = $action->getActionScript($form, $this, $varname);
+            foreach ($triggers as $trigger) {
+                if ($trigger == 'onload') {
+                    $this->_addOnLoadJavascript($js);
+                } else {
+                    $actions .= ' ' . $trigger . '="' . $js . '"';
+                }
+            }
+        }
+        return $actions;
+    }
+
+    protected function _addOnLoadJavascript($script)
+    {
+        $this->_onLoadJS[] = $script;
+    }
+
+    public function renderEnd()
+    {
+        if (count($this->_onLoadJS)) {
+            return "<script type=\"text/javascript\">" .
+                "<!--\n" .  implode("\n", $this->_onLoadJS) . "\n// -->\n" .
+                "</script>";
+        } else {
+            return '';
+        }
+    }
+
+}
diff --git a/framework/Ui/lib/Horde/Ui/VarRenderer/TablesetHtml.php b/framework/Ui/lib/Horde/Ui/VarRenderer/TablesetHtml.php
new file mode 100644 (file)
index 0000000..36e06f3
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+/**
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @package Horde_Ui
+ */
+class Horde_Ui_VarRenderer_TablesetHtml extends Horde_Ui_VarRenderer_Html
+{
+    protected function _renderVarInput_tableset($form, &$var, &$vars)
+    {
+        $header = $var->type->getHeader();
+        $name   = $var->getVarName();
+        $values = $var->getValues();
+        $form_name = $form->getName();
+        $var_name = $var->getVarName() . '[]';
+        $checkedValues = $var->getValue($vars);
+        $actions = $this->_getActionScripts($form, $var);
+        $function_name = 'select'  . $form_name . $var->getVarName();
+        $enable = _("Select all");
+        $disable = _("Select none");
+        $invert = _("Invert selection");
+
+        Horde::addScriptFile('tables.js', 'horde');
+
+        $html = <<<EOT
+<script type="text/javascript">
+function $function_name()
+{
+    for (var i = 0; i < document.$form_name.elements.length; i++) {
+        f = document.$form_name.elements[i];
+        if (f.name != '$var_name') {
+            continue;
+        }
+        if (arguments.length) {
+            f.checked = arguments[0];
+        } else {
+            f.checked = !f.checked;
+        }
+    }
+}
+</script>
+<a href="#" onclick="$function_name(true); return false;">$enable</a>,
+<a href="#" onclick="$function_name(false); return false;">$disable</a>,
+<a href="#" onclick="$function_name(); return false;">$invert</a>
+<table style="width: 100%" class="sortable striped" id="tableset_' . $name . '"><thead><tr>
+<th>&nbsp;</th>
+EOT;
+
+        foreach ($header as $col_title) {
+            $html .= sprintf('<th class="leftAlign">%s</th>', $col_title);
+        }
+        $html .= '</tr></thead>';
+
+        if (!is_array($checkedValues)) {
+            $checkedValues = array();
+        }
+        $i = 0;
+        foreach ($values as $value => $displays) {
+            $checked = (in_array($value, $checkedValues)) ? ' checked="checked"' : '';
+            $html .= '<tr>' .
+                sprintf('<td style="text-align: center"><input id="%s[]" type="checkbox" name="%s[]" value="%s"%s%s /></td>',
+                        $name,
+                        $name,
+                        $value,
+                        $checked,
+                        $actions);
+            foreach ($displays as $col) {
+                $html .= sprintf('<td>&nbsp;%s</td>', $col);
+            }
+            $html .= '</tr>' . "\n";
+            $i++;
+        }
+
+        $html .= '</table>'
+              . '<a href="#" onclick="' . $function_name . '(true); return false;">' . $enable . '</a>, '
+              . '<a href="#" onclick="' . $function_name . '(false); return false;">' . $disable . '</a>, '
+              . '<a href="#" onclick="' . $function_name . '(); return false;">' . $invert . '</a>';
+
+        return $html;
+    }
+
+    protected function _renderVarDisplay_tableset($form, &$var, &$vars)
+    {
+        $header = $var->type->getHeader();
+        $name   = $var->getVarName();
+        $values = $var->getValues();
+        $checkedValues = $var->getValue($vars);
+        $actions = $this->_getActionScripts($form, $var);
+
+        Horde::addScriptFile('tables.js', 'horde');
+        $html = '<table style="width: 100%" class="sortable striped" id="tableset_' . $name . '"><thead><tr>' .
+            '<th>&nbsp;</th>';
+        foreach ($header as $col_title) {
+            $html .= sprintf('<th class="leftAlign">%s</th>', $col_title);
+        }
+        $html .= '</tr></thead>';
+
+        if (!is_array($checkedValues)) {
+            $checkedValues = array();
+        }
+        $i = 0;
+        foreach ($values as $value => $displays) {
+            $checked = '[ <span style="font-weight: bold; color: '
+                . (in_array($value, $checkedValues)) ? ' green">V' : 'red">X'
+                . '</span> ]';
+            $html .= '<tr>'.
+                sprintf('<td style="text-align: center">%s</td>', $checked);
+            foreach ($displays as $col) {
+                $html .= '<td>&nbsp;' . $col . '</td>';
+            }
+            $html .= '</tr>';
+            $i++;
+        }
+
+        return $html . '</table>';
+    }
+
+}
diff --git a/framework/Ui/lib/Horde/Ui/Widget.php b/framework/Ui/lib/Horde/Ui/Widget.php
new file mode 100644 (file)
index 0000000..1f7c10a
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+/**
+ * The Horde_Ui_Widget:: class provides base functionality for other Horde
+ * UI elements.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Jason M. Felice <jason.m.felice@gmail.com>
+ * @package Horde_Ui
+ */
+abstract class Horde_Ui_Widget
+{
+    /**
+     * Any variables that should be preserved in all of the widget's
+     * links.
+     *
+     * @var array
+     */
+    protected $_preserve = array();
+
+    /**
+     * The name of this widget.  This is used as the basename for variables
+     * we access and manipulate.
+     *
+     * @var string
+     */
+    protected $_name;
+
+    /**
+     * A reference to a Horde_Variables:: object this widget will use and
+     * manipulate.
+     *
+     * @var Horde_Variables
+     */
+    protected $_vars;
+
+    /**
+     * An array of name => value pairs which configure how this widget
+     * behaves.
+     *
+     * @var array
+     */
+    protected $_config;
+
+    /**
+     * Holds the name of a callback function to call on any URLS before they
+     * are used/returned. If an array, it is taken as an object/method name, if
+     * a string, it is taken as a php function.
+     *
+     * @var callable
+     */
+    protected $_url_callback = array('Horde', 'applicationUrl');
+
+    /**
+     * Construct a new UI Widget interface.
+     *
+     * @param string $name            The name of the variable which will
+     *                                track this UI widget's state.
+     * @param Horde_Variables &$vars  A Horde_Variables:: object.
+     * @param array $config           The widget's configuration.
+     */
+    public function __construct($name, &$vars, $config = array())
+    {
+        $this->_name = $name;
+        $this->_vars = &$vars;
+
+        if (array_key_exists('url_callback', $config)) {
+            $this->_url_callback = $config['url_callback'];
+            unset($config['url_callback']);
+        }
+        $this->_config = $config;
+    }
+
+    /**
+     * Instructs widget to preserve a variable or a set of variables.
+     *
+     * @param string|array $var  The name of the variable to preserve, or
+     *                           an array of variables to preserve.
+     * @param mixed $value       If preserving a single key, the value of the
+     *                           variable to preserve.
+     */
+    public function preserve($var, $value = null)
+    {
+        if (!is_array($var)) {
+            $var = array($var);
+        }
+
+        foreach ($var as $key => $value) {
+            $this->_preserve[$key] = $value;
+        }
+    }
+
+    /**
+     * TODO
+     */
+    protected function _addPreserved($link)
+    {
+        foreach ($this->_preserve as $varName => $varValue) {
+            $link = Horde_Util::addParameter($link, $varName, $varValue);
+        }
+
+        return $link;
+    }
+
+    /**
+     * Render the widget.
+     *
+     * @param mixed $data  The widget's state data.
+     */
+    abstract public function render($data);
+
+    /**
+     * TODO
+     */
+    protected function _link($link)
+    {
+        if (is_callable($this->_url_callback)) {
+            return call_user_func($this->_url_callback, $link);
+        }
+
+        return $link;
+    }
+
+}
diff --git a/framework/Ui/package.xml b/framework/Ui/package.xml
new file mode 100644 (file)
index 0000000..5215f72
--- /dev/null
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Ui</name>
+ <channel>pear.horde.org</channel>
+ <summary>Horde&apos;s User Interface Classes</summary>
+ <description>The Horde_Ui package provides several classes for handling UI elements in Horde.
+ </description>
+ <lead>
+  <name>Chuck Hagenbuch</name>
+  <user>chuck</user>
+  <email>chuck@horde.org</email>
+  <active>yes</active>
+ </lead>
+ <developer>
+  <name>Jay Felice</name>
+  <user>eraserhd</user>
+  <email>jason.m.felice@gmail.com</email>
+  <active>yes</active>
+ </developer>
+ <date>2009-11-19</date>
+ <version>
+  <release>0.1.0</release>
+  <api>0.1.0</api>
+ </version>
+ <stability>
+  <release>beta</release>
+  <api>beta</api>
+ </stability>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>* Initial Horde 4 package.
+ </notes>
+ <contents>
+  <dir name="/">
+   <dir name="lib">
+    <dir name="Horde">
+     <dir name="Ui">
+      <dir name="VarRenderer">
+       <file name="Html.php" role="php" />
+       <file name="TablesetHtml.php" role="php" />
+      </dir> <!-- /lib/Horde/Ui/VarRenderer -->
+      <file name="JsCalendar.php" role="php" />
+      <file name="Language.php" role="php" />
+      <file name="Pager.php" role="php" />
+      <file name="Tabs.php" role="php" />
+      <file name="TagCloud.php" role="php" />
+      <file name="VarRenderer.php" role="php" />
+      <file name="Widget.php" role="php" />
+     </dir> <!-- /lib/Horde/Ui -->
+    </dir> <!-- /lib/Horde -->
+   </dir> <!-- /lib -->
+  </dir> <!-- / -->
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.2.0</min>
+   </php>
+   <pearinstaller>
+    <min>1.7.0</min>
+   </pearinstaller>
+  </required>
+ </dependencies>
+ <phprelease>
+  <filelist>
+   <install name="lib/Horde/Ui/VarRenderer/Html.php" as="Horde/Ui/VarRenderer/Html.php" />
+   <install name="lib/Horde/Ui/VarRenderer/TablesetHtml.php" as="Horde/Ui/VarRenderer/TablesetHtml.php" />
+   <install name="lib/Horde/Ui/JsCalendar.php" as="Horde/Ui/JsCalendar.php" />
+   <install name="lib/Horde/Ui/Language.php" as="Horde/Ui/Language.php" />
+   <install name="lib/Horde/Ui/Pager.php" as="Horde/Ui/Pager.php" />
+   <install name="lib/Horde/Ui/Tabs.php" as="Horde/Ui/Tabs.php" />
+   <install name="lib/Horde/Ui/TagCloud.php" as="Horde/Ui/TagCloud.php" />
+   <install name="lib/Horde/Ui/VarRenderer.php" as="Horde/Ui/VarRenderer.php" />
+   <install name="lib/Horde/Ui/Widget.php" as="Horde/Ui/Widget.php" />
+  </filelist>
+ </phprelease>
+ <changelog>
+  <release>
+   <version>
+    <release>0.0.2</release>
+    <api>0.0.2</api>
+   </version>
+   <stability>
+    <release>alpha</release>
+    <api>alpha</api>
+   </stability>
+   <date>2006-05-08</date>
+   <time>23:54:10</time>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>* Added Horde_UI_JsCalendar::.
+   * Converted to package.xml 2.0 for pear.horde.org
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>0.0.1</release>
+    <api>0.0.1</api>
+   </version>
+   <stability>
+    <release>alpha</release>
+    <api>alpha</api>
+   </stability>
+   <date>2003-11-14</date>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>Initial release as a PEAR package
+   </notes>
+  </release>
+ </changelog>
+</package>