<?php
/**
 * The Horde_Form_Renderer class provides HTML and other renderings
 * of forms for the Horde_Form:: package.
 *
 * $Horde: horde/lib/Form/Renderer.php,v 1.95 2003/08/22 06:19:41 mdjukic Exp $
 * Copyright 2001-2003 Robert E. Coyle <robertecoyle@hotmail.com>
 *
 * See the enclosed file LICENSE for license information (BSD). If you
 * did not receive this file, see http://www.horde.org/licenses/bsdl.php.
 *
 * @author  Robert E. Coyle <robertecoyle@hotmail.com>
 * @version $Revision: 1.95 $
 * @package horde.form
 */
class Horde_Form_Renderer {

    var $_name;
    var $_requiredLegend = false;
    var $_requiredMarker = '*';
    var $_onLoadJS = array();
    var $_showHeader = true;
    var $_cols = 2;

    /**
     * Width of the attributes column, in percent.
     *
     * @access private
     * @var integer $_attrColumnWidth
     */
    var $_attrColumnWidth = 15;

    /**
     * The generic Horde_Form_Renderer constructor doesn't do
     * anything.
     */
    function Horde_Form_Renderer($params = array())
    {
        $this->_requiredMarker = Horde::img('required.gif', '*', '', $GLOBALS['registry']->getParam('graphics', 'horde'));
    }

    function showHeader($bool)
    {
        $this->_showHeader = $bool;
    }

    /**
     * Specify the width of the attribute column in the rendered form.
     *
     * @param integer $percent The width of the attribute column, in percent.
     *
     * @return void
     */
    function setAttrColumnWidth($percent)
    {
        $this->_attrColumnWidth = $percent;
    }

    function open($action, $method, $name, $enctype = null)
    {
        $this->_name = $name;
        echo "<form action=\"$action\" method=\"$method\"" . (empty($name) ? '' : " name=\"$name\"") . (is_null($enctype) ? '' : " enctype=\"$enctype\"") . ">\n";
        Horde::pformInput();
    }

    function beginActive($name)
    {
        $this->_renderBeginActive($name);
    }

    function beginInactive($name)
    {
        $this->_renderBeginInactive($name);
    }

    function _renderSectionBegin(&$form, $section)
    {
        $img_toggle = '';
        $js_toggle = '';
        $section_id = '_section_' . $section;

        /* If no javascript then don't bother with toggle. */
        if ($GLOBALS['browser']->hasFeature('javascript')) {
            $img = ($form->getSectionExpandedState($section)) ? 'collapse.gif' : 'expand.gif';
            $img_toggle = Horde::img($img, _("Expand/collapse section"), 'id="_state_' . $section . '"', $GLOBALS['registry']->getParam('graphics', 'horde'));

            $js_toggle = 'onclick="var section = document.getElementById(\'' . $section_id . '\'); var state = document.getElementById(\'_state_' . $section . '\'); if (section.style.display == \'none\') { section.style.display = \'block\'; state.src=\'' . $GLOBALS['registry']->getParam('graphics', 'horde') . '/collapse.gif' . '\'; } else { section.style.display = \'none\'; state.src=\'' . $GLOBALS['registry']->getParam('graphics', 'horde') . '/expand.gif' . '\'; }; return false;"';
        }
?><tr valign="bottom"><td colspan="<?php echo $this->_cols; ?>"><table width="100%" border="0" cellpadding="0" cellspacing="0"><tr><td width="25%" class="control"><b>&nbsp;&raquo;&nbsp;&nbsp;<?php echo $form->getSectionDesc($section) ?></b></td><td width="75%" align="right" class="control"><a href="" <?php echo $js_toggle ?>><?php echo $img_toggle ?></a></td></tr></table></td></tr></table><div id="<?php echo $section_id; ?>" style="display:<?php echo $form->getSectionExpandedState($section, false); ?>;"><table class="control" border="0" width="100%" cellspacing="0" cellpadding="1"><?php
    }

    function _renderSectionEnd()
    {
?></table></div><table border="0" width="100%" cellspacing="0" cellpadding="1"><?php
    }

    function end()
    {
        $this->_renderEnd();
    }

    function close()
    {
        echo "</form>\n";
    }

    function listFormVars(&$form)
    {
        $variables = &$form->getVariables(true, true);
        $vars = array();
        if ($variables) {
            foreach ($variables as $var) {
                if (is_object($var)) {
                    $vars[$var->getVarName()] = 1;
                } else {
                    $vars[$var] = 1;
                }
            }
        }
        echo '<input type="hidden" name="_formvars" value="' . htmlspecialchars(serialize($vars), ENT_QUOTES, NLS::getCharset()) . '" />';
    }

    function renderFormActive(&$form, &$vars)
    {
        $this->_renderForm($form, $vars, true);
    }

    function renderFormInactive(&$form, &$vars)
    {
        $this->_renderForm($form, $vars, false);
    }

    function _renderForm(&$form, &$vars, $active)
    {
        /* If help is present 3 columns are needed. */
        $this->_cols = $form->hasHelp() ? 3 : 2;

        /* check for a custom renderer for this form. */
        $customRenderer = &$form->getCustomRenderer();

        if ($customRenderer !== null) {
            return $customRenderer->render($form, $vars, $active);
        }

        /* No custom renderer - use the default one. */
        $variables = &$form->getVariables(false);

        /* Check for a form token error. */
        if (($tokenError = $form->getError('__formToken')) !== null) {
?><tr class="<?php echo $this->getRowClass() ?>" valign="top"><td colspan="<?php echo $this->_cols; ?>"><span class="form-error"><?php echo $tokenError ?></span></td></tr><?php
        }

        foreach ($variables as $section_id => $section) {
            if ($section_id != '__base') {
                $this->_renderSectionBegin($form, $section_id);
            }
            foreach ($section as $var) {
                $type = $var->getTypeName();

                switch ($type) {
                case 'header':
                    $this->_renderHeader($var->getHumanName(), $form->getError($var->getVarName()));
                    break;

                case 'description':
                    $this->_renderDescription($var->getHumanName());
                    break;

                case 'spacer':
                    $this->_renderSpacer();
                    break;

                default:
                    $format = (!$active || $var->isReadonly()) ? 'Display' : 'Input';
                    $begin = "_renderVar${format}Begin";
                    $end = "_renderVar${format}End";
                    $method = "_renderVar${format}_$type";

                    $this->$begin($form, $var, $vars);

                    if (@method_exists($this, $method) == false) {
                        $method = "_renderVar${format}Default";
                    }

                    $this->$method($form, $var, $vars);
                    $this->$end($form, $var, $vars);

                    if ($var->hasAction()) { ?><script language="JavaScript" type="text/javascript">
                        <!--
                        <?php $var->_action->printJavaScript() ?>
                        // -->
                        </script>
                    <?php }
                }
            }
            if ($section_id != '__base') {
                $this->_renderSectionEnd();
            }
        }
    }

    function submit($submit = null, $reset = false)
    {
        if (is_null($submit) || empty($submit)) {
            $submit = _("Submit");
        }
        if ($reset === true) {
            $reset = _("Reset");
        }
        $this->_renderSubmit($submit, $reset);
    }

    // Implementation specifics -- begin / end functions
    function _renderBeginActive($name)
    {
        if ($this->_showHeader) {
            $this->_sectionHeader($name);
        }
?><table border="0" width="100%" cellspacing="0" cellpadding="2">
<?php if ($this->_requiredLegend): ?><tr><td></td><td><span class="form-error"><?php echo $this->_requiredMarker ?></span> = Required Field</td></tr><?php endif; ?>
<?php
    }

    function _renderBeginInactive($name)
    {
        if ($this->_showHeader) {
            $this->_sectionHeader($name);
        }
?><table border="0" width="100%" cellspacing="0" cellpadding="1"><?php
    }

    function _renderEnd()
    {
?></table><br /><?php
        if (count($this->_onLoadJS)): ?>
<script language="JavaScript" type="text/javascript">
<!--
<?php echo implode("\n", $this->_onLoadJS); ?>
// -->
</script>
<?php endif;
    }

    function _renderHeader($header, $error = '')
    {
?><tr><td class="control" width="100%" colspan="<?php echo $this->_cols; ?>" valign="bottom"><b>&nbsp;&raquo;&nbsp;&nbsp;<?php echo $header ?></b><?php
        if (!empty($error)) {
?><br /><span class="form-error"><?php echo $error ?></span><?php
        }
?></td></tr><?php
    }

    function _renderDescription($text)
    {
?><tr><td width="100%" colspan="<?php echo $this->_cols; ?>" class="<?php echo $this->getRowClass() ?>"><table cellpadding="8" border="0"><tr><td><?php echo $text ?></td></tr></table></td></tr><?php
    }

    function _renderSpacer()
    {
?><tr><td colspan="<?php echo $this->_cols; ?>">&nbsp;</td></tr><?php
    }

    function _renderSubmit($submit, $reset)
    {
?><tr><td colspan="<?php echo $this->_cols; ?>" class="control">
  <?php if (!is_array($submit)) $submit = array($submit); foreach ($submit as $submitbutton): ?>
    <input class="button" name="submitbutton" type="submit" value="<?php echo $submitbutton ?>" />
  <?php endforeach; ?>
  <?php if (!empty($reset)): ?>
    <input class="button" name="resetbutton" type="reset" value="<?php echo $reset ?>" />
  <?php endif; ?>
</td></tr><?php
    }

    // Implementation specifics -- input variables
    function _renderVarInputBegin(&$form, &$var, &$vars)
    {
        $message = $form->getError($var);
        $isvalid = empty($message);
        $class = $this->getRowClass();
        echo '<tr valign="top">';
        printf('  <td width="%d%%" align="right" class="%s">%s%s%s%s</td>',
               $this->_attrColumnWidth,
               $class,
               $isvalid ? '' : '<span class="form-error">',
               $var->isRequired() ? '<span class="form-error">' . $this->_requiredMarker . '</span>&nbsp;' : '',
               $var->getHumanName(),
               $isvalid ? '' : '<br />' . $message . '</span>');
        printf('  <td width="%d%%"%s class="%s">',
               100 - $this->_attrColumnWidth,
               ((!$var->hasHelp() && $form->hasHelp()) ? ' colspan="2"' : ''),
               $class);
    }

    function _renderVarInputEnd(&$form, &$var, &$vars)
    {
        /* Display any description for the field. */
        if ($var->hasDescription()) {
            echo '<br />' . $var->getDescription();
        }

        /* Display any help for the field. */
        if ($var->hasHelp()) {
            $class = $this->getRowClass(false);
            echo '</td><td align="right" class="' . $class . '">' . Help::link($GLOBALS['registry']->getApp(), $var->getHelp()) . '&nbsp';
        }
?></td></tr><?php
    }

    function _renderVarInputDefault(&$form, &$var, &$vars)
    {
?><strong>Warning:</strong> Unknown variable type <?php echo $var->getTypeName() ?><?php
    }

    function _renderVarInput_number(&$form, &$var, &$vars, $index = null)
    {
?><input type="text" size="5" name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_int(&$form, &$var, &$vars, $index = null)
    {
?><input type="text" size="5" name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_intlist(&$form, &$var, &$vars, $index = null)
    {
?><input type="text" name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_text(&$form, &$var, &$vars, $index = null)
    {
        $maxlength = $var->type->getMaxLength();
        if (!empty($maxlength)) {
            $maxlength = 'maxlength="' . $maxlength . '"';
        }
?><input type="text" size="<?php echo $var->type->getSize() ?>" <?php echo $maxlength ?> name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_stringlist(&$form, &$var, &$vars, $index = null)
    {
?><input type="text" size="60" name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_file(&$form, &$var, &$vars, $index = null)
    {
?><input type="file" size="30" name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_image(&$form, &$var, &$vars, $index = null)
    {
        $var_name = $var->getVarName();
        $var_array = $var->getValue($vars, $index);

        Horde::addScriptFile('image.js', 'horde');
        $graphics_dir = $GLOBALS['registry']->getParam('graphics', 'horde');
        $img_dir = $graphics_dir . '/image';

        /* Hidden tag to store the preview image filename. */
        printf('<input type="hidden" name="%s value="%s" %s />',
               $var_name,
               $var_array['old'],
               $this->_genID($var_name . '[old]'));

        /* Output the input tag. */
        printf('<input type="file" size="30" name="%s" value="" />',
               $var_name . '[new]');

        /* Output the button to upload/reset the image. */
        echo '&nbsp;';
        printf('<input class="button" name="%s" type="submit" value="%s" />',
               '_do_' . $var_name,
               _("Upload"));
        printf('<input class="button" name="%s" type="submit" value="%s" />',
               '_un_' . $var_name,
               _("Reset"));

        $upload = $vars->getVar($var_name);
        require_once HORDE_BASE . '/lib/Serialize.php';
        $upload['old'] = Horde_Serialize::unserialize($upload['old'], SERIALIZE_UTF7_BASIC);

        if (!empty($upload['old'])) {
            echo '<br />';
            $img = Horde::url($GLOBALS['registry']->getParam('webroot', 'horde') . '/util/img.php');
            $img = Horde::addParameter($img, 'f', $upload['old']['tmp_name']);
            /* Add all the buttons to control the image editing. */
            echo Horde::link('#', '', '', '', 'showImage(\'' . Horde::addParameter($img, array('a' => 'rotate', 'v' => '270')) . '\', \'_p_' . $var_name . '\', true);') . Horde::img('rotate-270.png', _("Rotate Left"), 'align="middle"', $img_dir) . '</a>';
            echo Horde::link('#', '', '', '', 'showImage(\'' . Horde::addParameter($img, array('a' => 'rotate', 'v' => '180')) . '\', \'_p_' . $var_name . '\', true);') . Horde::img('rotate-180.png', _("Rotate 180"), 'align="middle"', $img_dir) . '</a>';
            echo Horde::link('#', '', '', '', 'showImage(\'' . Horde::addParameter($img, array('a' => 'rotate', 'v' => '90')) . '\', \'_p_' . $var_name . '\', true);') . Horde::img('rotate-90.png', _("Rotate Right"), 'align="middle"', $img_dir) . '</a>';
            echo Horde::link('#', '', '', '', 'showImage(\'' . Horde::addParameter($img, 'a', 'flip') . '\', \'_p_' . $var_name . '\', true);') . Horde::img('flip.png', _("Flip"), 'align="middle"', $img_dir) . '</a>';
            echo Horde::link('#', '', '', '', 'showImage(\'' . Horde::addParameter($img, 'a', 'mirror') . '\', \'_p_' . $var_name . '\', true);') . Horde::img('mirror.png', _("Mirror"), 'align="middle"', $img_dir) . '</a>';
            echo Horde::link('#', '', '', '', 'showImage(\'' . Horde::addParameter($img, 'a', 'grayscale') . '\', \'_p_' . $var_name . '\', true);') . Horde::img('grayscale.gif', _("Grayscale"), 'align="middle"', $img_dir) . "</a><br />\n";
            /* The preview image element. */
            echo '<img src="' . $img . '" ' . $this->_genID('_p_' . $var_name, $index) . '>';
        }
    }

    function _renderVarInput_longtext(&$form, &$var, &$vars, $index = null)
    {
?><textarea <?php echo $this->_genID($var->getVarName(), $index) ?> name="<?php echo $var->getVarName() ?>" cols="<?php echo $var->type->getCols() ?>" rows="<?php echo $var->type->getRows() ?>"><?php echo $var->getValue($vars, $index) ?></textarea><?php
        if ($var->type->hasHtmlHelper() && $GLOBALS['browser']->hasFeature('javascript')) {
            echo '<br /><table border="0" cellpadding="1" cellspacing="0"><tr><td>';
            Horde::addScriptFile('open_html_helper.js', 'horde');
            $imgId = $this->_genID($var->getVarName(), $index, false) . 'goto';
            if ($var->type->hasHtmlHelper('emoticons')) {
                echo Horde::link('', _("Emoticons"), '', '', 'openHtmlHelper(\'emoticons\', \'' . $var->getVarName() . '\'); return false;') . Horde::img('smile.gif', _("Emoticons"), 'id="' . $imgId . '" align="middle"', $GLOBALS['registry']->getParam('graphics', 'horde') . '/emoticons') . '</a>';
            }
            echo '</td></tr><tr><td><div ' . $this->_genID('htmlhelper_' . $var->getVarName(), $index) . ' class="control"></div></td></tr></table>';
        }
    }

    function _renderVarInput_countedtext(&$form, &$var, &$vars, $index = null)
    {
?><textarea name="<?php echo $var->getVarName() ?>" cols="<?php echo $var->type->getCols() ?>" rows="<?php echo $var->type->getRows() ?>"><?php echo $var->getValue($vars, $index) ?></textarea><?php
    }

    function _renderVarInput_address(&$form, &$var, &$vars, $index = null)
    {
?><textarea <?php echo $this->_genID($var->getVarName(), $index) ?> name="<?php echo $var->getVarName() ?>" cols="<?php echo $var->type->getCols() ?>" rows="<?php echo $var->type->getRows() ?>"><?php echo $var->getValue($vars, $index) ?></textarea><?php
    }

    function _renderVarInput_date(&$form, &$var, &$vars, $index = null)
    {
?><input type="text" name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_time(&$form, &$var, &$vars, $index = null)
    {
?><input type="text" size="5" name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_hourminutesecond(&$form, &$var, &$vars, $index = null)
    {
        $hours = array('' => _("HH"));
        for ($i = 0; $i <= 23; $i++) {
            $hours[sprintf('%02d', $i)] = $i;
        }
        $minutes = array('' => _("MM"));
        for ($i = 0; $i <= 59; $i++) {
            $minutes[sprintf('%02d', $i)] = $i;
        }
        $seconds = array('' => _("SS"));
        for ($i = 0; $i <= 59; $i++) {
            $seconds[sprintf('%02d', $i)] = $i;
        }
        $time = $var->type->getTimeParts($var->getValue($vars, $index));
?>
<select name="<?php echo $var->getVarName() ?>[hour]" id="<?php echo $var->getVarName() ?>[hour]"><?php echo $this->_selectOptions($hours, $time['hour']) ?></select>
<select name="<?php echo $var->getVarName() ?>[minute]" id="<?php echo $var->getVarName() ?>[minute]"><?php echo $this->_selectOptions($minutes, $time['minute']) ?></select>
<select name="<?php echo $var->getVarName() ?>[second]" id="<?php echo $var->getVarName() ?>[second]"><?php echo $this->_selectOptions($seconds, $time['second']) ?></select>
<?php
    }

    function _renderVarInput_monthyear(&$form, &$var, &$vars, $index = null)
    {
        $months = 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"));
        $years = array('' => _("YYYY"));
        if ($var->type->_startYear > $var->type->_endYear) {
            for ($i = $var->type->_startYear; $i >= $var->type->_endYear; $i--) {
                $years[$i] = $i;
            }
        } else {
            for ($i = $var->type->_startYear; $i <= $var->type->_endYear; $i++) {
                $years[$i] = $i;
            }
        }
?><select name="<?php echo $var->type->getMonthVar($var) ?>"><?php echo $this->_selectOptions($months, $vars->getVar($var->type->getMonthVar($var))) ?></select><select name="<?php echo $var->type->getYearVar($var) ?>"><?php echo $this->_selectOptions($years, $vars->getVar($var->type->getYearVar($var))) ?></select><?php
    }

    function _renderVarInput_monthdayyear(&$form, &$var, &$vars, $index = null)
    {
        $months = 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"));
        $days = array('' => _("DD"));
        for ($i = 1; $i <= 31; $i++) {
            $days[$i] = $i;
        }
        $years = array('' => _("YYYY"));
        if ($var->type->_startYear > $var->type->_endYear) {
            for ($i = $var->type->_startYear; $i >= $var->type->_endYear; $i--) {
                $years[$i] = $i;
            }
        } else {
            for ($i = $var->type->_startYear; $i <= $var->type->_endYear; $i++) {
                $years[$i] = $i;
            }
        }
        $date = $var->type->getDateParts($var->getValue($vars, $index));
?>
<select name="<?php echo $var->getVarName() ?>[month]" id="<?php echo $var->getVarName() ?>[month]"><?php echo $this->_selectOptions($months, $date['month']) ?></select>
<select name="<?php echo $var->getVarName() ?>[day]" id="<?php echo $var->getVarName() ?>[day]"><?php echo $this->_selectOptions($days, $date['day']) ?></select>
<select name="<?php echo $var->getVarName() ?>[year]" id="<?php echo $var->getVarName() ?>[year]"><?php echo $this->_selectOptions($years, $date['year']) ?></select>
<?php
        if ($var->type->_picker && $GLOBALS['browser']->hasFeature('javascript')) {
            Horde::addScriptFile('open_calendar.js', 'horde');
            $imgId = $this->_genID($var->getVarName(), $index, false) . 'goto';
            echo '<div id="goto" class="control" style="position:absolute;visibility:hidden;padding:1px"></div>';
            echo Horde::link('', _("Select a date"), '', '', 'openCalendar(\'' . $imgId . '\', \'' . $var->getVarName() . '\'); return false;') . Horde::img('calendar.gif', _("Calendar"), 'id="' . $imgId . '" align="middle"', $GLOBALS['registry']->getParam('graphics', 'horde')) . '</a>';
        }
    }

    function _renderVarInput_colorpicker(&$form, &$var, &$vars, $index = null)
    {
        global $registry, $browser;
?><table border="0" cellpadding="0" cellspacing="0"><tr><td><input type="text" size="10" maxlength="7" name="<?php echo $var->getVarName() ?>" id="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /></td><?php
        if ($browser->hasFeature('javascript')) {
            Horde::addScriptFile('open_colorpicker.js', 'horde');
            echo '<td width="20" id="colordemo_' . $var->getVarName() . '" style="background-color:' . $var->getValue($vars, $index) . '"> </td>';
            echo '<td>' . Horde::link('#', _("Color Picker"), 'widget', '', 'openColorPicker(\'' . $var->getVarName() . '\'); return false;') . Horde::img('colorpicker.gif', _("Color Picker"), 'height="16"', $registry->getParam('graphics', 'horde')) . '</a></td>';
            echo '<td><div id="colorpicker_' . $var->getVarName() . '" class="control"></div></td>';
        }
?></tr></table><?php
    }

    function _renderVarInput_sorter(&$form, &$var, &$vars, $index = null)
    {
        global $registry;

        $var_name = $var->getVarName();
        $instance = $var->type->_instance;

        Horde::addScriptFile('sorter.js', 'horde');

        echo '<input type="hidden" name="' . $var_name . '[array]" value="" ' . $this->_genID($var_name . '[array]') . ' />';
        echo '<table border="0" cellpadding="0" cellspacing="0"><tr><td>';
        echo '<select multiple="multiple" size="' . $var->type->getSize() . '" name="' . $var_name . '[list]" onchange="' . $instance . '.deselectHeader();" ' . $this->_genID($var_name . '[list]') . '>';
        echo $var->type->getOptions();
        echo '</select></td><td>';
        echo Horde::link('#', _("Move up"), '', '', $instance . '.moveColumnUp(); return false;') . Horde::img('nav/up.gif', _("Move up"), '', $registry->getParam('graphics', 'horde')) . '</a><br />';
        echo Horde::link('#', _("Move up"), '', '', $instance . '.moveColumnDown(); return false;') . Horde::img('nav/down.gif', _("Move down"), '', $registry->getParam('graphics', 'horde')) . '</a></td></tr></table>';
        echo '<script language="JavaScript" type="text/javascript">' . "\n";
        printf('%1$s = new Horde_Form_Sorter(\'%1$s\', \'%2$s\', \'%3$s\');' . "\n",
               $instance, $var_name, $var->type->getHeader());

        printf("%s.setHidden();\n</script>",
               $instance);
    }

    function _renderVarInput_invalid(&$form, &$var, &$vars, $index = null)
    {
        return $this->_renderVarDisplay_invalid($form, $var, $vars);
    }

    function _renderVarInput_enum(&$form, &$var, &$vars, $index = null)
    {
        $values = $var->getValues();
        $prompt = $var->type->getPrompt();
        $selected = $var->getValue($vars, $index);
?><select <?php echo $this->_genID($var->getVarName(), $index) ?> name="<?php echo $var->getVarName() ?>"<?php if ($var->hasAction()) $this->_genActionScript($form, $var->_action, $var->getVarName(), $index) ?>>
  <?php if (!empty($prompt)) { echo '<option value="">' . htmlspecialchars($prompt, ENT_QUOTES, NLS::getCharset()) . '</option>'; } echo $this->_selectOptions($values, $selected) ?>
  </select><?php
    }

    function _renderVarInput_multienum(&$form, &$var, &$vars, $index = null)
    {
        $values = $var->getValues();
        $selected = $vars->getVarWasset($var->getVarName(), $wasset);
        if (!$wasset) {
            $selected = $var->getDefault();
        }
?><select multiple="multiple" size="<?php echo $var->type->size ?>" name="<?php echo $var->getVarName() ?>[]">
  <?php echo $this->_multiSelectOptions($values, $selected) ?>
  </select><?php
        echo '<br />' . _("To select multiple items, hold down the Control/Option key while clicking.");
    }

    function _renderVarInput_radio(&$form, &$var, &$vars, $index = null)
    {
        $values = $var->getValues();
        $value  = $var->getValue($vars, $index);
        echo $this->_radioButtons($var->getVarName(), $values, $value);
    }

    function _renderVarInput_set(&$form, &$var, &$vars, $index = null)
    {
        $values = $var->getValues();
        $value  = $var->getValue($vars, $index);
        echo $this->_checkBoxes($var->getVarName(), $values, $value);
    }

    function _renderVarInput_link(&$form, &$var, &$vars, $index = null)
    {
        return _renderVarDisplay_link($form, $var, $vars);
    }

    function _renderVarInput_email(&$form, &$var, &$vars, $index = null)
    {
?><input type="text" name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_password(&$form, &$var, &$vars, $index = null)
    {
?><input type="password" name="<?php echo $var->getVarName() ?>" value="<?php echo $var->getValue($vars, $index) ?>" /><?php
    }

    function _renderVarInput_emailconfirm(&$form, &$var, &$vars, $index = null)
    {
        $email = $var->getValue($vars, $index);
?><input type="text" name="<?php echo $var->getVarName() ?>[original]" value="<?php $email['original'] ?>" /><br />
  <input type="text" name="<?php echo $var->getVarName() ?>[confirm]" value="<?php $email['confirm'] ?>" /><?php
    }

    function _renderVarInput_passwordconfirm(&$form, &$var, &$vars, $index = null)
    {
        $password = $var->getValue($vars, $index);
?><input type="password" name="<?php echo $var->getVarName() ?>[original]" value="<?php echo $password['original'] ?>" /><br />
  <input type="password" name="<?php echo $var->getVarName() ?>[confirm]" value="<?php echo $password['confirm'] ?>" /><?php
    }

    function _renderVarInput_boolean(&$form, &$var, &$vars, $index = null)
    {
?><input type="checkbox" name="<?php echo $var->getVarName() ?>"<?php echo $var->getValue($vars, $index) ? ' checked="checked"' : ''; if ($var->hasAction()) $this->_genActionScript($form, $var->_action, $var->getVarName(), $index) ?> /><?php
    }

    // Implementation specifics -- display variables.
    function _renderVarDisplayBegin(&$form, &$var, &$vars)
    {
        $class = $this->getRowClass();
?><tr valign="top">
  <td width="<?php echo $this->_attrColumnWidth ?>%" align="right" valign="middle" class="<?php echo $class ?>"><b><?php echo $var->getHumanName() ?></b></td>
  <td width="<?php echo 100 - $this->_attrColumnWidth ?>%" class="<?php echo $class ?>"><?php
    }

    function _renderVarDisplayEnd(&$form, &$var, &$vars)
    {
?></td></tr><?php
    }

    function _renderVarDisplayDefault(&$form, &$var, &$vars, $index = null)
    {
        echo nl2br(htmlspecialchars($var->getValue($vars, $index), ENT_QUOTES, NLS::getCharset()));
    }

    function _renderVarDisplay_html(&$form, &$var, &$vars, $index = null)
    {
        echo $var->getValue($vars, $index);
    }

    function _renderVarDisplay_password(&$form, &$var, &$vars, $index = null)
    {
        echo htmlspecialchars('********', ENT_QUOTES, NLS::getCharset());
    }

    function _renderVarDisplay_passwordconfirm(&$form, &$var, &$vars, $index = null)
    {
        echo htmlspecialchars('********', ENT_QUOTES, NLS::getCharset());
    }

    function _renderVarDisplay_boolean(&$form, &$var, &$vars, $index = null)
    {
        echo $var->getValue($vars, $index) == 'on' ? _("On") : _("Off");
    }

    function _renderVarDisplay_enum(&$form, &$var, &$vars, $index = null)
    {
        $values = $var->getValues();
        if (count($values) == 0) {
            echo _("No values");
        } elseif (isset($values[$var->getValue($vars, $index)]) && $var->getValue($vars, $index) != '') {
            echo htmlspecialchars($values[$var->getValue($vars, $index)], ENT_QUOTES, NLS::getCharset());
        }
    }

    function _renderVarDisplay_radio(&$form, &$var, &$vars, $index = null)
    {
        $values = $var->getValues();
        if (count($values) == 0) {
            echo _("No values");
        } elseif (isset($values[$var->getValue($vars, $index)])) {
            echo htmlspecialchars($values[$var->getValue($vars, $index)], ENT_QUOTES, NLS::getCharset());
        }
    }

    function _renderVarDisplay_multienum(&$form, &$var, &$vars, $index = null)
    {
        $values = $var->getValues();
        $on = $var->getValue($vars, $index);
        if (!count($values) || !count($on)) {
            echo _("No values");
        } else {
            $display = array();
            foreach ($values as $value => $name) {
                if (in_array($value, $on)) {
                    $display[] = $name;
                }
            }
            echo htmlspecialchars(implode(', ', $display), ENT_QUOTES, NLS::getCharset());
        }
    }

    function _renderVarDisplay_set(&$form, &$var, &$vars, $index = null)
    {
        $values = $var->getValues();
        $on = $var->getValue($vars, $index);
        if (!count($values) || !count($on)) {
            echo _("No values");
        } else {
            $display = array();
            foreach ($values as $value => $name) {
                if (in_array($value, $on)) {
                    $display[] = $name;
                }
            }
            echo htmlspecialchars(implode(', ', $display), ENT_QUOTES, NLS::getCharset());
        }
    }

    function _renderVarDisplay_address(&$form, &$var, &$vars, $index = null)
    {
        $address = $var->getValue($vars, $index);

        if (preg_match('/(.*)\n(.*)\s*,\s*(\w+)\.?\s+(\d+|[a-zA-Z]\d[a-zA-Z]\s?\d[a-zA-Z]\d)/', $address, $addressParts)) {
            /* American/Canadian address style. */
            $mapurl = 'http://www.mapquest.com/maps/map.adp?size=big&zoom=7';
            if (!empty($addressParts[1])) {
                $mapurl .= '&address=' . urlencode($addressParts[1]);
            }
            if (!empty($addressParts[2])) {
                $mapurl .= '&city=' . urlencode($addressParts[2]);
            }
            if (!empty($addressParts[3])) {
                $mapurl .= '&state=' . urlencode($addressParts[3]);
            }
            if (!empty($addressParts[4])) {
                $mapurl .= '&zipcode=' . urlencode($addressParts[4]);
                if (preg_match('|[a-zA-Z]\d[a-zA-Z]\s?\d[a-zA-Z]\d|', $addressParts[4])) {
                    $mapurl .= '&country=CA';
                }
            }
        } elseif (preg_match('/(.*)\nD-(\d{5})\s+(.*)/i', $address, $addressParts)) {
            /* German address style. */
            $mapurl = 'http://www.map24.de/map24/index.php3?maptype=RELOAD&country0=de&gcf=1';
            if (!empty($addressParts[1])) {
                $mapurl .= '&street0=' . urlencode($addressParts[1]);
            }
            if (!empty($addressParts[2])) {
                $mapurl .= '&zip0=' . urlencode($addressParts[2]);
            }
            if (!empty($addressParts[3])) {
                $mapurl .= '&city0=' . urlencode($addressParts[3]);
            }
        }
        echo nl2br(htmlspecialchars($var->getValue($vars, $index), ENT_QUOTES, NLS::getCharset()));
        if (!empty($mapurl)) {
            global $registry;
            echo Horde::link($mapurl, _("View map"), null, '_blank') . Horde::img('map.gif', _("View map"), 'align="middle"', $registry->getParam('graphics', 'horde')) . '</a>';
        }
    }

    function _renderVarDisplay_date(&$form, &$var, &$vars, $index = null)
    {
        echo $var->type->getFormattedTimeFull($var->getValue($vars, $index));
    }

    function _renderVarDisplay_monthyear(&$form, &$var, &$vars, $index = null)
    {
        echo $vars->getVar($var->getVarName() . '[month]') . ', ' . $vars->getVar($var->getVarName() . '[year]');
    }

    function _renderVarDisplay_monthdayyear(&$form, &$var, &$vars, $index = null)
    {
        $date = $var->getValue($vars, $index);
        if ((is_array($date) && !empty($date['year']) && !empty($date['month']) && !empty($date['day'])) || (!is_array($date) && !empty($date))) {
            echo $var->type->formatDate($date);
        }
    }

    function _renderVarDisplay_invalid(&$form, &$var, &$vars, $index = null)
    {
        echo '<span class="form-error">' . htmlspecialchars($var->type->message, ENT_QUOTES, NLS::getCharset()) . '</span>';
    }

    function _renderVarDisplay_link(&$form, &$var, &$vars, $index = null)
    {
        if (isset($var->type->values[0]) && is_array($var->type->values[0])) {
            $count = count($var->type->values);
            for ($i = 0; $i < $count; $i++) {
                if (empty($var->type->values[$i]['url']) || empty($var->type->values[$i]['text'])) {
                    continue;
                }
                if ($i > 0) {
                    echo ' | ';
                }
                echo Horde::link($var->type->values[$i]['url'], $var->type->values[$i]['text'], 'widget') . $var->type->values[$i]['text'] . '</a>';
            }
        } else {
            if (empty($var->type->values['url']) || empty($var->type->values['text'])) {
                return;
            }
            echo Horde::link($var->type->values['url'], $var->type->values['text'], 'widget') . $var->type->values['text'] . '</a>';
        }
    }

    function _selectOptions(&$values, $selectedValue = false)
    {
        $result = '';
        $sel = false;
        foreach ($values as $value => $display) {
            if (!is_null($selectedValue) && !$sel && $value == $selectedValue) {
                $selected = ' selected="selected"';
                $sel = true;
            } else {
                $selected = '';
            }
            $result .= ' <option value="' . htmlspecialchars($value, ENT_QUOTES, NLS::getCharset()) . '"' . $selected . '>' . htmlspecialchars($display) . "</option>\n";
        }

        return $result;
    }

    function _multiSelectOptions(&$values, $selectedValues)
    {
        $result = '';
        $sel = false;
        foreach ($values as $value => $display) {
            if (@in_array($value, $selectedValues)) {
                $selected = ' selected="selected"';
            } else {
                $selected = '';
            }
            $result .= " <option value=\"" . htmlspecialchars($value, ENT_QUOTES, NLS::getCharset()) . "\"$selected>" . htmlspecialchars($display) . "</option>\n";
        }

        return $result;
    }

    function _checkBoxes($name, $values, $checkedValues)
    {
        $result = '';
        if (!is_array($checkedValues)) {
            $checkedValues = array();
        }
        $i = 0;
        foreach ($values as $value => $display) {
            $checked = (in_array($value, $checkedValues)) ? ' checked="checked"' : '';
            $result .= "<input id=\"${name}${i}\" type=\"checkbox\" name=\"${name}[]\" value=\"$value\"$checked /><label for=\"${name}${i}\">&nbsp;$display</label><br />";
            $i++;
        }

        return $result;
    }

    function _radioButtons($name, $values, $checkedValue = null)
    {
        $result = '';
        $i = 0;
        foreach ($values as $value => $display) {
            $checked = (!is_null($checkedValue) && $value == $checkedValue) ? ' checked="checked"' : '';
            $result .= "<input id=\"${name}${i}\" type=\"radio\" name=\"$name\" value=\"$value\"$checked /><label for=\"${name}${i}\">&nbsp;$display</label><br />";
            $i++;
        }

        return $result;
    }

    function _genID($name, $index = null, $fulltag = true)
    {
        $name = str_replace('[]', '', $name);
        if (!is_null($index)) {
            $name .= $index;
        }
        return $fulltag ? 'id="' . htmlspecialchars($name, ENT_QUOTES, NLS::getCharset()) . '"' : $name;
    }

    function _genActionScript(&$form, $action, $varname, $index = null)
    {
        $triggers = $action->getTrigger();
        if (!is_array($triggers)) {
            $triggers = array($triggers);
        }
        $js = $action->getActionScript($form, $this, $index);
        foreach ($triggers as $trigger) {
            if ($trigger == 'onload') {
                $this->_addOnLoadJavascript($js);
            } else {
                echo ' ' . $trigger . '="' . $js . '"';
            }
        }
    }

    function _addOnLoadJavascript($script)
    {
        $this->_onLoadJS[] = $script;
    }

    function getRowClass($increment = true)
    {
        static $i = 1;

        if (!$increment) {
            return 'item' . ($i % 2);
        }

        return 'item' . (++$i % 2);
    }

    function _sectionHeader($title)
    {
        if (!empty($title)) {
?><table border="0" cellpadding="2" cellspacing="0" width="100%">
<tr><td align="left" class="header"><b><?php echo $title ?></b></td></tr>
</table><?php
        }
    }

    /**
     * Attempts to return a concrete Horde_Form_Renderer instance
     * based on $renderer.
     *
     * @param mixed $renderer  The type of concrete Horde_Form_Renderer subclass to return.
     *                         The code is dynamically included. If $renderer is an array,
     *                         then we will look in $renderer[0]/lib/Form/Renderer/ for
     *                         the subclass implementation named $renderer[1].php.
     * @param array $params    (optional) A hash containing any additional
     *                         configuration a form might need.
     *
     * @return object Horde_Form_Renderer  The concrete Horde_Form_Renderer reference,
     *                                     or false on an error.
     */
    function &factory($renderer, $params = null)
    {
        if (is_array($renderer)) {
            $app = $renderer[0];
            $renderer = $renderer[1];
        }

        /* Return a base Horde_Form_Renderer object if no driver is
         * specified. */
        $renderer = basename($renderer);
        if (empty($renderer) || (strcmp($renderer, 'none') == 0)) {
            return new Horde_Form_Renderer($params);
        }

        if (!empty($app)) {
            include_once $GLOBALS['registry']->getParam('fileroot', $app) . '/lib/Form/Renderer/' . $renderer . '.php';
        } elseif (@file_exists(dirname(__FILE__) . '/Renderer/' . $renderer . '.php')) {
            include_once dirname(__FILE__) . '/Renderer/' . $renderer . '.php';
        } else {
            @include_once 'Horde/Form/Renderer/' . $renderer . '.php';
        }
        $class = 'Horde_Form_Renderer_' . $renderer;
        if (class_exists($class)) {
            return new $class($params);
        } else {
            return PEAR::raiseError('Class definition of ' . $class . ' not found.');
        }
    }

    /**
     * Attempts to return a reference to a concrete
     * Horde_Form_Renderer instance based on $renderer. It will only
     * create a new instance if no Horde_Form_Renderer instance with
     * the same parameters currently exists.
     *
     * This should be used if multiple types of form renderers (and,
     * thus, multiple Horde_Form_Renderer instances) are required.
     *
     * This method must be invoked as: $var =
     * &Horde_Form_Renderer::singleton()
     *
     * @param mixed $renderer  The type of concrete Horde_Form_Renderer subclass to return.
     *                         The code is dynamically included. If $renderer is an array,
     *                         then we will look in $renderer[0]/lib/Form/Renderer/ for
     *                         the subclass implementation named $renderer[1].php.
     * @param array $params    (optional) A hash containing any additional
     *                         configuration a form might need.
     *
     * @return object Horde_Form_Renderer  The concrete Horde_Form_Renderer reference,
     *                                     or false on an error.
     */
    function &singleton($renderer, $params = null)
    {
        static $instances;
        if (!isset($instances)) {
            $instances = array();
        }

        $signature = serialize(array($renderer, $params));
        if (!isset($instances[$signature])) {
            $instances[$signature] = &Horde_Form_Renderer::factory($renderer, $params);
        }

        return $instances[$signature];
    }

}

/**
 * Custom renderer abstract class.
 */
class Horde_Form_CustomRenderer extends Horde_Form_Renderer {

    /**
     * Render method. Must be overridden by the custom renderer
     * implementation.
     *
     * @abstract
     */
    function render(&$form, &$vars, $isActive)
    {
        return PEAR::raiseError('Warning: Horde_Form_CustomRenderer::render() called, should be overridden');
    }

}
