<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* HTML QuickForm Alternate Select
*
* This file must be included *after* HTML/QuickForm.php
*
* HTML_QuickForm plugin that changes a select into a group of radio buttons
* or checkboxes with an optional textbox for other options not listed. If
* the select element is listed as multiple, then it will be rendered with
* checkboxes, otherwise it is rendered with radio buttons.
*
* PHP Versions 4 and 5
*
* @category HTML
* @package HTML_QuickForm_altselect
* @author David Sanders (shang.xiao.sanders@gmail.com)
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version Release: @package_version@
* @link http://pyrus.sourceforge.net/HTML_QuickForm_altselect.html
* @see HTML_QuickForm_select
*/
require_once 'HTML/QuickForm/select.php';
/**
* Replace PHP_EOL constant
*
* category PHP
* package PHP_Compat
* @link http://php.net/reserved.constants.core
* @author Aidan Lister <aidan@php.net>
* @since PHP 5.0.2
*/
if (!defined('PHP_EOL')) {
switch (strtoupper(substr(PHP_OS, 0, 3))) {
// Windows
case 'WIN':
define('PHP_EOL', "\r\n");
break;
// Mac
case 'DAR':
define('PHP_EOL', "\r");
break;
// Unix
default:
define('PHP_EOL', "\n");
}
}
// {{{ HTML_QuickForm_altselect
/**
* HTML QuickForm Alternate Select
*
* HTML_QuickForm plugin that changes a select into a group of radio buttons
* or checkboxes with an optional textbox for other options not listed. If
* the select element is listed as multiple, then it will be rendered with
* checkboxes, otherwise it is rendered with radio buttons.
*
* @category HTML
* @package HTML_QuickForm_altselect
* @author David Sanders (shang.xiao.sanders@gmail.com)
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version Release: @package_version@
* @link http://pyrus.sourceforge.net/HTML_QuickForm_altselect.html
* @see HTML_QuickForm_select
*/
class HTML_QuickForm_altselect extends HTML_QuickForm_select
{
// {{{ properties
/**
* Include the other text field for non-listed entry.
*
* @var bool
* @access public
*/
var $includeOther = false;
/**
* Label for the Other option.
*
* @var string
* @access public
*/
var $otherLabel = 'Other';
/**
* Text label to go in front of other text field (singular mode).
*
* @var string
* @access public
*/
var $otherText = 'If other please specify:';
/**
* Text label to go in front of other text field (multiple mode).
*
* @var string
* @access public
*/
var $otherTextMultiple = 'Other:';
/**
* Delimiter between subelements. Use br to go vertical, or nbsp to go
* horizontal.
*
* @var string
* @access public
*/
var $delimiter = '<br />';
/**
* Other value storage.
*
* @var string
* @access private
*/
var $_otherValue;
/**
* Associative array of attributes for each of the individual form elements.
* NOTE: use "_qf_other" for the other radio button, and "_qf_other_text"
* for the text field.
*
* @var array Associative array of attributes (see HTML_Common)
* @access private
*/
var $_individualAttributes;
// }}}
// {{{ HTML_QuickForm_altselect
/**
* Constructor. Used to distinguish the attributes array which should be
* an associative array of options to either a typical HTML attribute string
* or another associative array
*
* @param string $elementName select name attribute
* @param mixed $elementLabel label(s) for the select
* @param mixed $options data to be used to populate options
* @param mixed $attributes an associative array of option value
* -> attributes. Each attribute is either
* a typical HTML attribute string or an
* associative array.
* NOTE: use "_qf_other" for the other radio
* button, and "_qf_other_text" for the
* text field.
* @return void
*/
function HTML_QuickForm_altselect($elementName = null,
$elementLabel = null,
$options = null,
$attributes = null)
{
if (func_get_args()) {
HTML_QuickForm_select::HTML_QuickForm_select($elementName,
$elementLabel,
$options);
$this->_individualAttributes = $attributes;
}
}
// }}}
// {{{ toHtml
/**
* Render the HTML_QuickForm element.
*
* @access public
* @return string The rendered HTML
*/
function toHtml()
{
return $this->getElements(false);
}
// }}}
// {{{ getElements
/**
* Arrange the buttons/boxes and other bits either concatenated as a html
* string or in an array. When this element is registered as a group,
* getElements should act in the same way as HTML_QuickForm_group::getElements().
* (Therefore the default must be to format as an array)
*
* @param bool $formatArray set true for an array (default), false for HTML
* @access public
* @see HTML_QuickForm_group::getElements()
* @return mixed Array or HTML string
*/
function getElements($formatArray = true)
{
$html_func_to_use = $this->_flagFrozen ? 'getFrozenHtml' : 'toHtml';
$is_multiple = $this->getMultiple();
if ($formatArray) {
$elements = array();
} else {
$preHtml = '';
$postHtml = '';
$htmlArray = array();
$tabs = $this->_getTabs();
if ($this->getComment() != '') {
$preHtml .= '<!-- ' . $this->getComment() . ' //-->' . PHP_EOL;
}
}
if ($this->includeOther &&
!defined('HTML_QUICKFORM_ALTSELECT_JS_DISABLE_ELEMENT')) {
$disable_element_js = <<<EOT
<script type="text/javascript">
//<![CDATA[
function _qf_altselect_disableElement(e,disable)
{
if (!disable) {
e.disabled = false;
e.style.backgroundColor = '#ffffff';
} else {
e.disabled = true;
e.style.backgroundColor = '#cccccc';
}
}
//]]>
</script>
EOT;
if ($formatArray && !$this->_flagFrozen) {
$elements['_qf_altselect_disableElement'] =&
HTML_QuickForm::createElement('static',
'_qf_altselect_disableElement',
null,
$disable_element_js);
} else {
$javascript = $disable_element_js;
}
define('HTML_QUICKFORM_ALTSELECT_JS_DISABLE_ELEMENT',true);
} else {
$javascript = '';
}
$myName = $this->getName();
if ($is_multiple) {
$myName .= '[]';
}
foreach ($this->_options as $option) {
if ($is_multiple) {
$element =& HTML_QuickForm::createElement('checkbox',$myName);
//xxx - qf won't take a value as constructor argument
$element->updateAttributes(array('value' => $option['attr']['value']));
} else {
$element =& HTML_QuickForm::createElement('radio',
$myName,
null,
null,
$option['attr']['value']);
if ($this->includeOther) {
$element->updateAttributes(array(
'onClick' => "_qf_altselect_disableElement(this.form.elements[this.name + '_qf_other'],true);"));
}
}
if (isset($this->_individualAttributes[$option['attr']['value']])) {
$element->updateAttributes($this->_individualAttributes[$option['attr']['value']]);
}
if (is_array($this->_values) && in_array((string)$option['attr']['value'], $this->_values)) {
$element->setChecked(true);
}
if ($formatArray) {
if ($this->_flagFrozen) {
$element->freeze();
}
$elements[$option['attr']['value']] =& $element;
} else {
// write our own label instead of adding text to the radio/cbox
// as we may want to render without any text when doing from a group
$htmlArray[] = $tabs .
$element->$html_func_to_use() .
'<label for="' . $element->getAttribute('id') . '">' .
$option['text'] .
'</label>';
}
}
if ($this->includeOther) {
if (!$is_multiple) {
//
// create the other radio button
//
$element =& HTML_QuickForm::createElement('radio',
$myName,
null,
null,
'_qf_other');
if (isset($this->_individualAttributes['_qf_other'])) {
$element->updateAttributes($this->_individualAttributes['_qf_other']);
}
$element->updateAttributes(array(
'onClick'=>"_qf_altselect_disableElement(this.form.elements[this.name+'_qf_other'],false);this.form.elements[this.name+'_qf_other'].focus();this.form.elements[this.name+'_qf_other'].select();"));
if (is_array($this->_values) &&
in_array('_qf_other', $this->_values)) {
$element->setChecked(true);
}
$other_msg = $this->otherText;
if ($formatArray) {
if ($this->_flagFrozen) {
$element->freeze();
}
$elements['_qf_other'] =& $element;
} else {
$htmlArray[] = $tabs .
$element->$html_func_to_use() .
'<label for="' . $element->getAttribute('id') . '">' .
$this->otherLabel .
'</label>';
$preHtml .= $javascript;
}
$textName = $myName.'_qf_other';
} else {
$other_msg = $this->otherTextMultiple;
$textName = $myName;
}
//
// create the 'other' text element
//
$other_id = 'qf_' . uniqid('');
$element =& HTML_QuickForm::createElement('text',
$textName,
null,
array('id'=>$other_id));
if (isset($this->_individualAttributes['_qf_other_text'])) {
$element->updateAttributes($this->_individualAttributes['_qf_other_text']);
}
// if either the other button is selected, or some text is entered
// (meaning _qf_other will also be a value), then set the value of
// the other value in the text field.
if (is_array($this->_values) &&
in_array('_qf_other', $this->_values) &&
isset($this->_otherValue)) {
$element->updateAttributes(array('value' => $this->_otherValue));
}
// otherwise just disable it
// only disable with javascript otherwise if the browser doesn't have
// javascript, then we'll never be able to enter the other text
else if (!$is_multiple) {
$disable_js = <<<EOT
<script type="text/javascript">
//<![CDATA[
_qf_altselect_disableElement(document.getElementById('$other_id'),true);
//]]>
</script>
EOT;
if ($formatArray && !$this->_flagFrozen) {
$elements['disable_js'] =& HTML_QuickForm::createElement('static',
'disable_js',
null,
$disable_js);
} else {
$postHtml .= $disable_js;
}
}
if ($formatArray) {
if ($this->_flagFrozen) {
$element->freeze();
}
$elements[$textName] =& $element;
} else {
$htmlArray[] = $tabs .
'<label for="' . $element->getAttribute('id') . '">' .
$other_msg .
'</label> ' .
$element->$html_func_to_use();
}
}
if ($formatArray) {
return $elements;
} else {
$strHtml = $preHtml . PHP_EOL .
implode($this->delimiter . PHP_EOL, $htmlArray) . PHP_EOL .
$postHtml;
return $strHtml;
}
}
// }}}
// {{{ exportValue
/**
* Exports the value.
*
* If the other value is set, this will be exported if in singular mode
* and the other radio button is selected. Otherwise if in multiple mode
* the other value is added to the array of values.
*
* @param array $submitValues submitValues values submitted
* @param bool $assoc propagate on to
* HTML_QuickForm_select::exportValue()
* @access public
* @return mixed Single value or array of values
*/
function exportValue(&$submitValues, $assoc = false)
{
if (!$this->includeOther) {
return parent::exportValue($submitValues, $assoc);
}
$myName = $this->getName();
if ($this->getMultiple()) {
// kinda defeats the purpose of using exportValue to return only
// allowed options
if (($an_array = $this->_findValue($submitValues)) === null) {
return null;
}
// _findValue may return a scalar if that's what was posted
if (is_array($an_array)) {
// remove the empty string from the other option
foreach ($an_array as $key => $value) {
if ($value === '') {
unset($an_array[$key]);
}
}
}
return $this->_prepareValue($an_array,$assoc);
} else {
// XXX
$this->addOption(null,'_qf_other');
$val = parent::exportValue($submitValues,$assoc);
// XXX
array_pop($this->_options);
if (is_array($val) && $val[$myName] == '_qf_other' ||
$val == '_qf_other') {
return $this->_prepareValue($submitValues[$myName.'_qf_other'],$assoc);
} else {
return $val;
}
}
}
// }}}
// {{{ setSelected
/**
* Set the selected options. If a non-listed option is specified, it
* will go into the other text field. Note at this point, the other and
* multiple attributes may not have been set.
*
* @param mixed $values array or comma delimited string of selected values
* @access public
* @return void
*/
function setSelected($values)
{
parent::setSelected($values);
//
// we need to do some extra work here in case the other
// option will be/has been set...
//
$other_values = array();
foreach ($this->_values as $value) {
// if we are in singular mode and the other button is selected from
// the submit values then we'll need to record the real other value
// in _otherValue
if ($value == '_qf_other') {
$myName = $this->getName();
$this->_otherValue = @$_REQUEST[$myName.'_qf_other'];
// we only need to grasp the first other value, because we
// are in singular mode, so we'll return...
return;
}
// otherwise the real other value might be listed in _values
// from setSelected('junk') or if we're in multiple mode and it was
// submitted...
// if we find something not part of the options then we record it in
// _otherValue and set the _qf_other as part of the values
else {
$found = false;
foreach ($this->_options as $option) {
if ((string) $value == (string) $option['attr']['value']) {
$found = true;
}
}
if (!$found) {
$this->_values[] = '_qf_other';
$other_values[] = $value;
}
}
}
if (!empty($other_values)) {
$this->_otherValue = implode(',',$other_values);
}
}
// }}}
// {{{ setIncludeOther
/**
* Set the select to include the 'other' textfield.
*
* @param bool $include whether to include the other text field
* @access public
* @return void
*/
function setIncludeOther($include = true)
{
$this->includeOther = (bool) $include;
}
// }}}
// {{{ setDelimiter
/**
* Set the delimiter.
*
* @param string $delimiter delimiter to use between the subelements
* @access public
* @return void
*/
function setDelimiter($delimiter)
{
if (!is_string($delimiter)) {
$this->delimiter = '<br />';
} else {
$this->delimiter = $delimiter;
}
}
// }}}
// {{{ setGroup
/**
* Tell this element to act like a group when being accepted.
*
* @param bool $is_group whether to act like a group or not
* @see HTML_QuickForm_element::_type
* @access public
* @return void
*/
function setGroup($is_group = true)
{
$this->_type = $is_group ? 'group' : 'select';
}
// }}}
// {{{ accept
/**
* Accepts a renderer. Overload select in case we'd like to see the
* checkboxes/radio buttons in a group when renderering with another
* renderer.
*
* @param HTML_QuickForm_Renderer $renderer the QF renderer
* @param bool $require whether a group is required
* @param string $error an error message associated with
a group
* @access public
* @return void
*/
function accept(&$renderer, $required = false, $error = null)
{
// if not asked to act like a group, then pass of to regular accept method
if ($this->_type != 'group') {
return parent::accept($renderer,$required,$error);
}
$this->_separator = null;
$this->_appendName = null;
$this->_required = array();
//$this->_createElementsIfNotExist();
$renderer->startGroup($this, $required, $error);
$name = $this->getName();
// --8<--
$this->_elements = $this->getElements();
// -->8--
foreach (array_keys($this->_elements) as $key) {
$element =& $this->_elements[$key];
if ($this->_appendName) {
$elementName = $element->getName();
if (isset($elementName)) {
$element->setName($name .
'[' .
(strlen($elementName)? $elementName: $key) .
']');
} else {
$element->setName($name);
}
}
$required = !$element->isFrozen() &&
in_array($element->getName(), $this->_required);
$element->accept($renderer, $required);
// restore the element's name
if ($this->_appendName) {
$element->setName($elementName);
}
}
$renderer->finishGroup($this);
}
// }}}
// {{{ getElementName
/**
* Returns the name of this element. Used by HTML_QuickForm::getSubmitValue()
* when this element is registered as a group.
*
* @see HTML_QuickForm::getSubmitValue()
* @access public
* @return string
*/
function getElementName()
{
return $this->getName();
}
// }}}
}
// }}}
if (class_exists('HTML_QuickForm')) {
HTML_QuickForm::registerElementType('altselect',
'HTML/QuickForm/altselect.php',
'HTML_QuickForm_altselect');
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* c-hanging-comment-ender-p: nil
* End:
*/
?>