* @since  1.0
 */
class ActiveField extends YiiActiveField
{
    /**
     * An empty string value
     */
    const NOT_SET = '';
    /**
     * HTML radio input type
     */
    const TYPE_RADIO = 'radio';
    /**
     * HTML checkbox input type
     */
    const TYPE_CHECKBOX = 'checkbox';
    /**
     * Inline style flag for rendering checkboxes (as per bootstrap styling)
     */
    const STYLE_INLINE = 'inline';
    /**
     * The default height for the Krajee multi select input
     */
    const MULTI_SELECT_HEIGHT = '145px';
    /**
     * Default hint type that is displayed below the input
     */
    const HINT_DEFAULT = 1;
    /**
     * Special hint type that allows display via an indicator icon or on hover/click of the field label
     */
    const HINT_SPECIAL = 2;
    /**
     * @var array the list of hint keys that will be used by ActiveFieldHint jQuery plugin
     */
    protected static $_pluginHintKeys = [
        'iconCssClass',
        'labelCssClass',
        'contentCssClass',
        'hideOnEscape',
        'hideOnClickOut',
        'title',
        'placement',
        'container',
        'animation',
        'delay',
        'template',
        'selector',
        'viewport',
    ];
    /**
     * @var boolean whether to override the form layout styles and skip field formatting as per the form layout.
     * Defaults to `false`.
     */
    public $skipFormLayout = false;
    /**
     * @var integer the hint display type. If set to `self::HINT_DEFAULT`, the hint will be displayed as a text block below
     * each input. If set to `self::HINT_SPECIAL`, then the `hintSettings` will be applied to display the field
     * hint.
     */
    public $hintType = self::HINT_DEFAULT;
    /**
     * @var array the settings for displaying the hint. These settings are parsed only if `hintType` is set to
     * `self::HINT_SPECIAL`. The following properties are supported:
     * - `showIcon`: _boolean_, whether to display the hint via a help icon indicator. Defaults to `true`.
     * - `icon`: _string_, the markup to display the help icon. Defaults to ``.
     * - `iconBesideInput`: _boolean_, whether to display the icon beside the input. Defaults to `false`. The following
     *   actions will be taken based on this setting:
     *   - if set to `false` the help icon is displayed beside the label and the `labelTemplate` setting is used to
     *     render the icon and label markups.
     *   - if set to `true` the help icon is displayed beside the input and the `inputTemplate` setting is used to
     *     render the icon and input markups.
     * - `labelTemplate`: _string_, the template to render the help icon and the field label. Defaults to `{label}{help}`,
     *   where
     *   - `{label}` will be replaced by the ActiveField label content
     *   - `{help}` will be replaced by the help icon indicator markup
     * - `inputTemplate`: _string_, the template to render the help icon and the field input. Defaults to `'{input}
{help}
',`, where
     *   - `{input}` will be replaced by the ActiveField input content
     *   - `{help}` will be replaced by the help icon indicator markup
     * - `onLabelClick`: _boolean_, whether to display the hint on clicking the label. Defaults to `false`.
     * - `onLabelHover`: _boolean_, whether to display the hint on hover of the label. Defaults to `true`.
     * - `onIconClick`: _boolean_, whether to display the hint on clicking the icon. Defaults to `true`.
     * - `onIconHover`: _boolean_, whether to display the hint on hover of the icon. Defaults to `false`.
     * - `iconCssClass`: _string_, the CSS class appended to the `span` container enclosing the icon.
     * - `labelCssClass`: _string_, the CSS class appended to the `span` container enclosing label text within label tag.
     * - `contentCssClass`: _string_, the CSS class appended to the `span` container displaying help content within
     *   popover.
     * - `hideOnEscape`: _boolean_, whether to hide the popover on clicking escape button on the keyboard. Defaults to `true`.
     * - `hideOnClickOut`: _boolean_, whether to hide the popover on clicking outside the popover. Defaults to `true`.
     * - `title`: _string_, the title heading for the popover dialog. Defaults to empty string, whereby the heading is not
     *   displayed.
     * - `placement`: _string_, the placement of the help popover on hover or click of the icon or label. Defaults to
     *   `top`.
     * - `container`: _string_, the specific element to which the popover will be appended to. Defaults to `table` when
     *   `iconBesideInput` is `true`, else defaults to `form`
     * - `animation`: _boolean_, whether to add a CSS fade transition effect when opening and closing the popover. Defaults to
     *   `true`.
     * - `delay``: _integer_|_array_, the number of milliseconds it will take to open and close the popover. Defaults to `0`.
     * - `selector`: _integer_, the specified selector to add the popover to. Defaults to boolean `false`.
     * - `viewport`: _string_|_array_, the element within which the popover will be bounded to. Defaults to
     *   `['selector' => 'body', 'padding' => 0]`.
     */
    public $hintSettings = [];
    /**
     * @var array the feedback icon configuration (applicable for [bootstrap text inputs](http://getbootstrap.com/css/#with-optional-icons)).
     * This must be setup as an array containing the following keys:
     *
     * - `type`: _string_, the icon type to use. Should be one of `raw` or `icon`. Defaults to `icon`, where the `default`,
     *   `error` and `success` settings will be treated as an icon CSS suffix name. If set to `raw`, they will be
     *   treated as a raw content markup.
     * - `prefix`: _string_, the icon CSS class prefix to use if `type` is `icon`. Defaults to `glyphicon glyphicon-`.
     * - `default`: _string_, the icon (CSS class suffix name or raw markup) to show by default. If not set will not be
     *   shown.
     * - `error`: _string_, the icon (CSS class suffix name or raw markup) to use when input has an error validation. If
     *   not set will not be shown.
     * - `success`: _string_, the icon (CSS class suffix name or raw markup) to use when input has a success validation. If
     *   not set will not be shown.
     * - `defaultOptions`: _array_, the HTML attributes to apply for default icon. The special attribute `description` can
     *   be set to describe this feedback as an `aria` attribute for accessibility. Defaults to `(default)`.
     * - `errorOptions`: _array_, the HTML attributes to apply for error icon. The special attribute `description` can be
     *   set to describe this feedback as an `aria` attribute for accessibility. Defaults to `(error)`.
     * - `successOptions`: _array_, the HTML attributes to apply for success icon. The special attribute `description` can
     *   be set to describe this feedback as an `aria` attribute for accessibility. Defaults to `(success)`.
     *
     * @see http://getbootstrap.com/css/#with-optional-icons
     */
    public $feedbackIcon = [];
    /**
     * @var string content to be placed before label
     */
    public $contentBeforeLabel = '';
    /**
     * @var string content to be placed after label
     */
    public $contentAfterLabel = '';
    /**
     * @var string content to be placed before input
     */
    public $contentBeforeInput = '';
    /**
     * @var string content to be placed after input
     */
    public $contentAfterInput = '';
    /**
     * @var string content to be placed before error block
     */
    public $contentBeforeError = '';
    /**
     * @var string content to be placed after error block
     */
    public $contentAfterError = '';
    /**
     * @var string content to be placed before hint block
     */
    public $contentBeforeHint = '';
    /**
     * @var string content to be placed after hint block
     */
    public $contentAfterHint = '';
    /**
     * @var array addon options for text and password inputs. The following settings can be configured:
     * - `prepend`: _array_, the prepend addon configuration
     * - `content`: _string_, the prepend addon content
     * - `asButton`: _boolean_, whether the addon is a button or button group. Defaults to false.
     * - `options`: _array_, the HTML attributes to be added to the container.
     * - `append`: _array_, the append addon configuration
     * - `content`: _string_|_array_, the append addon content
     * - `asButton`: _boolean_, whether the addon is a button or button group. Defaults to false.
     * - `options`: _array_, the HTML attributes to be added to the container.
     * - `groupOptions`: _array_, HTML options for the input group
     * - `contentBefore`: _string_, content placed before addon
     * - `contentAfter`: _string_, content placed after addon
     */
    public $addon = [];
    /**
     * @var string CSS classname to add to the input
     */
    public $addClass = 'form-control';
    /**
     * @var string the static value for the field to be displayed for the static input OR when the form is in
     * staticOnly mode. This value is not HTML encoded.
     */
    public $staticValue;
    /**
     * @var boolean|string whether to show labels for the field. Should be one of the following values:
     * - `true`: show labels for the field
     * - `false`: hide labels for the field
     * - `ActiveForm::SCREEN_READER`: show in screen reader only (hide from normal display)
     */
    public $showLabels;
    /**
     * @var boolean whether to show errors for the field
     */
    public $showErrors;
    /**
     * @var boolean whether to show hints for the field
     */
    public $showHints;
    /**
     * @var boolean whether the label is to be hidden and auto-displayed as a placeholder
     */
    public $autoPlaceholder;
    /**
     * @var string inherits and overrides values from parent class. The value can be overridden within
     * [[ActiveForm::field()]] method. The following tokens are supported:
     * - `{beginLabel}`: Container begin tag for labels (to be used typically along with `{labelTitle}` token
     *   when you do not wish to directly use the `{label}` token)
     * - `{labelTitle}`: Label content without tags (to be used typically when you do not wish to directly use 
     *   the `{label` token)
     * - `{endLabel}`: Container end tag for labels (to be used typically along with `{labelTitle}` token
     *   when you do not wish to directly use the `{label}` token)
     * - `{label}`: Full label tag with begin tag, content and end tag
     * - `{beginWrapper}`: Container for input,error and hint start tag. Uses a `` tag if there is a input wrapper
     *    CSS detected, else defaults to empty string.
     * - `{input}`: placeholder for input control whatever it is
     * - `{hint}`: placeholder for hint/help text including sub container
     * - `{error}`: placeholder for error text including sub container
     * - `{endWrapper}`: end tag for `{beginWrapper}`. Defaults to `
` if there is a input wrapper CSS detected,
     *    else defaults to empty string.
     */
    public $template = "{label}\n{beginWrapper}\n{input}\n{hint}\n{error}\n{endWrapper}";
    /**
     *
     * @var integer the bootstrap grid column width (usually between 1 to 12)
     */
    public $labelSpan;
    /**
     *
     * @var string one of the bootstrap sizes (refer the ActiveForm::SIZE constants)
     */
    public $deviceSize;
    /**
     * @var boolean whether to render the error. Default is `true` except for layout `inline`.
     */
    public $enableError;
    /**
     * @var boolean whether to render the label. Default is `true`.
     */
    public $enableLabel;
    /**
     * @var null|array CSS grid classes for horizontal layout. This must be an array with these keys:
     * - `offset`: the offset grid class to append to the wrapper if no label is rendered
     * - `label`: the label grid class
     * - `wrapper`: the wrapper grid class
     * - `error`: the error grid class
     * - `hint`: the hint grid class
     * These options are compatible and similar to [[\yii\bootstrap\ActiveForm]] and provide a complete flexible
     * container. If `labelSpan` is set in [[ActiveForm::formConfig]] and `wrapper` is also set, then both css options
     * are concatenated. If `wrapper` contains a 'col-' class wrapper, it overrides the tag from `labelSpan`.
     */
    public $horizontalCssClasses;
    /**
     * @var boolean whether the input is to be offset (like for checkbox or radio).
     */
    protected $_offset = false;
    /**
     * @var boolean the container for multi select
     */
    protected $_multiselect = '';
    /**
     * @var boolean is it a static input
     */
    protected $_isStatic = false;
    /**
     * @var array the settings for the active field layout
     */
    protected $_settings = [
        'input' => '{input}',
        'error' => '{error}',
        'hint' => '{hint}',
        'showLabels' => true,
        'showErrors' => true,
        'labelSpan' => ActiveForm::DEFAULT_LABEL_SPAN,
        'deviceSize' => ActiveForm::SIZE_MEDIUM,
    ];
    /**
     * @var boolean whether there is a feedback icon configuration set
     */
    protected $_hasFeedback = false;
    /**
     * @var boolean whether there is a feedback icon configuration set
     */
    protected $_isHintSpecial = false;
    /**
     * @var string the label additional css class for horizontal forms and special inputs like checkbox and radio.
     */
    private $_labelCss;
    /**
     * @var string the input container additional css class for horizontal forms and special inputs like checkbox and
     * radio.
     */
    private $_inputCss;
    /**
     * @var string the offset class for error and hint for horizontal forms or for special inputs like checkbox and
     * radio.
     */
    private $_offsetCss;
    /**
     * @var boolean whether the hint icon is beside the input.
     */
    private $_iconBesideInput = false;
    /**
     * @var string the identifier for the hint popover container.
     */
    private $_hintPopoverContainer;
    /**
     * Parses and returns addon content
     *
     * @param string|array $addon the addon parameter or the array of addon parameters
     *
     * @return string
     */
    public static function getAddonContent($addon)
    {
        if (!is_array($addon)) {
            return $addon;
        }
        if (!ArrayHelper::isIndexed($addon)) {
            $addon = [$addon]; //pack existing array into indexed array
        }
        $html = "";
        foreach ($addon as $addonItem) {
            $content = ArrayHelper::getValue($addonItem, 'content', '');
            if (empty($content)) {
                continue;
            }
            $options = ArrayHelper::getValue($addonItem, 'options', []);
            $suffix = ArrayHelper::getValue($addonItem, 'asButton', false) ? 'btn' : 'addon';
            Html::addCssClass($options, 'input-group-' . $suffix);
            $html .= Html::tag('span', $content, $options);
        }
        return $html;
    }
    /**
     * @inheritdoc
     */
    public function begin()
    {
        if ($this->_hasFeedback) {
            Html::addCssClass($this->options, 'has-feedback');
        }
        return parent::begin();
    }
    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        $this->initActiveField();
    }
    /**
     * Renders a checkbox. This method will generate the "checked" tag attribute according to the model attribute value.
     *
     * @param array $options the tag options in terms of name-value pairs. The following options are specially
     * handled:
     *
     * - `uncheck`: _string_, the value associated with the uncheck state of the checkbox. If not set, it will take
     *   the default value `0`. This method will render a hidden input so that if the checkbox is not checked and is
     *   submitted, the value of this attribute will still be submitted to the server via the hidden input.
     * - `label`: _string_, a label displayed next to the checkbox. It will NOT be HTML-encoded. Therefore you can
     *   pass in HTML code such as an image tag. If this is is coming from end users, you should [[Html::encode()]]
     *   it to prevent XSS attacks. When this option is specified, the checkbox will be enclosed by a label tag.
     * - `labelOptions`: _array_, the HTML attributes for the label tag. This is only used when the "label" option is
     *   specified.
     * - `container: boolean|array, the HTML attributes for the checkbox container. If this is set to false, no
     *   container will be rendered. The special option `tag` will be recognized which defaults to `div`. This
     *   defaults to:
     *   `['tag' => 'div', 'class'=>'radio']`
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will be
     * HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
     *
     * @param boolean $enclosedByLabel whether to enclose the radio within the label. If `true`, the method will
     * still use [[template]] to layout the checkbox and the error message except that the radio is enclosed by
     * the label tag.
     *
     * @return ActiveField object
     */
    public function checkbox($options = [], $enclosedByLabel = true)
    {
        return $this->getToggleField(self::TYPE_CHECKBOX, $options, $enclosedByLabel);
    }
    /**
     * Renders a list of checkboxes. A checkbox list allows multiple selection, like [[listBox()]]. As a result, the
     * corresponding submitted value is an array. The selection of the checkbox list is taken from the value of the
     * model attribute.
     *
     * @param array $items the data item used to generate the checkboxes. The array values are the labels, while the
     * array keys are the corresponding checkbox values. Note that the labels will NOT be HTML-encoded, while the
     * values will be encoded.
     * @param array $options options (name => config) for the checkbox list. The following options are specially
     * handled:
     * - `unselect`: _string_, the value that should be submitted when none of the checkboxes is selected. By setting this
     *   option, a hidden input will be generated.
     * - `separator`: _string_, the HTML code that separates items.
     * - `inline`: _boolean_, whether the list should be displayed as a series on the same line, default is false
     * - `item: callable, a callback that can be used to customize the generation of the HTML code corresponding to a
     *   single item in $items. The signature of this callback must be:
     * ~~~
     * function ($index, $label, $name, $checked, $value)
     * ~~~
     *
     * where `$index` is the zero-based index of the checkbox in the whole list; `$label` is the label for the checkbox;
     * and `$name`, `$value` and `$checked` represent the name, value and the checked status of the checkbox input.
     *
     * @return ActiveField object
     */
    public function checkboxList($items, $options = [])
    {
        return $this->getToggleFieldList(self::TYPE_CHECKBOX, $items, $options);
    }
    /**
     * @inheritdoc
     */
    public function dropDownList($items, $options = [])
    {
        $this->initDisability($options);
        Html::addCssClass($options, $this->addClass);
        return parent::dropDownList($items, $options);
    }
    /**
     * @inheritdoc
     */
    public function hint($content, $options = [])
    {
        if ($this->getConfigParam('showHints') === false) {
            $this->parts['{hint}'] = '';
            return $this;
        }
        if ($this->_isHintSpecial) {
            Html::addCssClass($options, 'kv-hint-block');
        }
        return parent::hint($this->generateHint($content), $options);
    }
    /**
     * @inheritdoc
     */
    public function input($type, $options = [])
    {
        $this->initPlaceholder($options);
        if ($type != 'range' || $type != 'color') {
            Html::addCssClass($options, $this->addClass);
        }
        $this->initDisability($options);
        return parent::input($type, $options);
    }
    /**
     * @inheritdoc
     */
    public function label($label = null, $options = [])
    {
        $hasLabels = $this->hasLabels();
        $processLabels = $label !== false && $this->_isHintSpecial && $hasLabels !== false &&
            $hasLabels !== ActiveForm::SCREEN_READER && ($this->getHintData('onLabelClick') || $this->getHintData(
                    'onLabelHover'
                ));
        if ($processLabels) {
            if ($label === null) {
                $label = $this->model->getAttributeLabel($this->attribute);
            }
            $opts = ['class' => 'kv-type-label'];
            Html::addCssClass($opts, $this->getHintIconCss('Label'));
            $label = Html::tag('span', $label, $opts);
            if ($this->getHintData('showIcon') && !$this->getHintData('iconBesideInput')) {
                $label = strtr(
                    $this->getHintData('labelTemplate'),
                    ['{label}' => $label, '{help}' => $this->getHintIcon()]
                );
            }
        }
        if (strpos($this->template, '{beginLabel}') !== false) {
            $this->renderLabelParts($label, $options);
        }
        return parent::label($label, $options);
    }
    /**
     * @inheritdoc
     */
    public function listBox($items, $options = [])
    {
        $this->initDisability($options);
        Html::addCssClass($options, $this->addClass);
        return parent::listBox($items, $options);
    }
    /**
     * @inheritdoc
     */
    public function passwordInput($options = [])
    {
        $this->initPlaceholder($options);
        Html::addCssClass($options, $this->addClass);
        $this->initDisability($options);
        return parent::passwordInput($options);
    }
    /**
     * Renders a radio button. This method will generate the "checked" tag attribute according to the model attribute
     * value.
     *
     * @param array $options the tag options in terms of name-value pairs. The following options are specially
     * handled:
     * - `uncheck`: _string_, the value associated with the uncheck state of the radio button. If not set, it will take the
     *   default value '0'. This method will render a hidden input so that if the radio button is not checked and is
     *   submitted, the value of this attribute will still be submitted to the server via the hidden input.
     * - `label`: _string_, a label displayed next to the radio button. It will NOT be HTML-encoded. Therefore you can pass
     *   in HTML code such as an image tag. If this is is coming from end users, you should [[Html::encode()]] it to
     *   prevent XSS attacks. When this option is specified, the radio button will be enclosed by a label tag.
     * - `labelOptions`: _array_, the HTML attributes for the label tag. This is only used when the "label" option is
     *   specified.
     * - `container: boolean|array, the HTML attributes for the checkbox container. If this is set to false, no
     *   container will be rendered. The special option `tag` will be recognized which defaults to `div`. This
     *   defaults to: `['tag' => 'div', 'class'=>'radio']`
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded
     *   using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
     *
     * @param boolean $enclosedByLabel whether to enclose the radio within the label. If `true`, the method will still
     * use [[template]] to layout the checkbox and the error message except that the radio is enclosed by the label tag.
     *
     * @return ActiveField object
     */
    public function radio($options = [], $enclosedByLabel = true)
    {
        return $this->getToggleField(self::TYPE_RADIO, $options, $enclosedByLabel);
    }
    /**
     * Renders a list of radio buttons. A radio button list is like a checkbox list, except that it only allows single
     * selection. The selection of the radio buttons is taken from the value of the model attribute.
     *
     * @param array $items the data item used to generate the radio buttons. The array keys are the labels, while the
     * array values are the corresponding radio button values. Note that the labels will NOT be HTML-encoded, while
     * the values will.
     * @param array $options options (name => config) for the radio button list. The following options are specially
     * handled:
     *
     * - `unselect`: _string_, the value that should be submitted when none of the radio buttons is selected. By setting
     *   this option, a hidden input will be generated.
     * - `separator`: _string_, the HTML code that separates items.
     * - `inline`: _boolean_, whether the list should be displayed as a series on the same line, default is false
     * - `item: callable, a callback that can be used to customize the generation of the HTML code corresponding to a
     *   single item in $items. The signature of this callback must be:
     *
     * ~~~
     * function ($index, $label, $name, $checked, $value)
     * ~~~
     *
     * where `$index` is the zero-based index of the radio button in the whole list; `$label` is the label for the radio
     * button; and `$name`, `$value` and `$checked` represent the name, value and the checked status of the radio button
     * input.
     *
     * @return ActiveField object
     */
    public function radioList($items, $options = [])
    {
        return $this->getToggleFieldList(self::TYPE_RADIO, $items, $options);
    }
    /**
     * @inheritdoc
     */
    public function render($content = null)
    {
        if ($this->getConfigParam('showHints') === false) {
            $this->hintOptions['hint'] = '';
        } else {
            if ($content === null && !isset($this->parts['{hint}']) && !isset($this->hintOptions['hint'])) {
                $this->hintOptions['hint'] = $this->generateHint();
            }
            $this->template = strtr($this->template, ['{hint}' => $this->_settings['hint']]);
        }
        if ($this->form->staticOnly === true) {
            $this->buildTemplate();
            $this->staticInput();
        } else {
            $this->initPlaceholder($this->inputOptions);
            $this->initDisability($this->inputOptions);
            $this->buildTemplate();
        }
        return parent::render($content);
    }
    /**
     * @inheritdoc
     */
    public function textInput($options = [])
    {
        $this->initPlaceholder($options);
        Html::addCssClass($options, $this->addClass);
        $this->initDisability($options);
        return parent::textInput($options);
    }
    /**
     * @inheritdoc
     */
    public function textarea($options = [])
    {
        $this->initPlaceholder($options);
        Html::addCssClass($options, $this->addClass);
        $this->initDisability($options);
        return parent::textarea($options);
    }
    /**
     * @inheritdoc
     */
    public function widget($class, $config = [])
    {
        if (property_exists($class, 'disabled') && property_exists($class, 'readonly')) {
            $this->initDisability($config);
        }
        return parent::widget($class, $config);
    }
    /**
     * Renders a static input (display only).
     *
     * @param array $options the tag options in terms of name-value pairs.
     *
     * @return ActiveField object
     */
    public function staticInput($options = [])
    {
        $content = isset($this->staticValue) ? $this->staticValue :
            Html::getAttributeValue($this->model, $this->attribute);
        Html::addCssClass($options, 'form-control-static');
        $this->parts['{input}'] = Html::tag('div', $content, $options);
        $this->_isStatic = true;
        return $this;
    }
    /**
     * Renders a multi select list box. This control extends the checkboxList and radioList available in
     * [[YiiActiveField]] - to display a scrolling multi select list box.
     *
     * @param array $items the data item used to generate the checkboxes or radio.
     * @param array $options the options for checkboxList or radioList. Additional parameters
     * - `height`: _string_, the height of the multiselect control - defaults to 145px
     * - `selector`: _string_, whether checkbox or radio - defaults to checkbox
     * - `container`: _array_, options for the multiselect container
     * - `unselect`: _string_, the value that should be submitted when none of the radio buttons is selected. By setting
     *   this option, a hidden input will be generated.
     * - `separator`: _string_, the HTML code that separates items.
     * - `item: callable, a callback that can be used to customize the generation of the HTML code corresponding to a
     *   single item in $items. The signature of this callback must be:
     * - `inline`: _boolean_, whether the list should be displayed as a series on the same line, default is false
     * - `selector`: _string_, whether the selection input is [[TYPE_RADIO]] or [[TYPE_CHECKBOX]]
     *
     * @return ActiveField object
     */
    public function multiselect($items, $options = [])
    {
        $this->initDisability($options);
        $options['encode'] = false;
        $height = ArrayHelper::remove($options, 'height', self::MULTI_SELECT_HEIGHT);
        $selector = ArrayHelper::remove($options, 'selector', self::TYPE_CHECKBOX);
        $container = ArrayHelper::remove($options, 'container', []);
        Html::addCssStyle($container, 'height:' . $height, true);
        Html::addCssClass($container, $this->addClass . ' input-multiselect');
        $container['tabindex'] = 0;
        $this->_multiselect = Html::tag('div', '{input}', $container);
        return $selector == self::TYPE_RADIO ? $this->radioList($items, $options) :
            $this->checkboxList($items, $options);
    }
    /**
     * Renders a list of radio toggle buttons.
     *
     * @see http://getbootstrap.com/javascript/#buttons-checkbox-radio
     *
     * @param array $items the data item used to generate the radios. The array values are the labels, while the array
     * keys are the corresponding radio values. Note that the labels will NOT be HTML-encoded, while the values
     * will be encoded.
     * @param array $options options (name => config) for the radio button list. The following options are specially
     * handled:
     *
     * - `unselect`: _string_, the value that should be submitted when none of the radios is selected. By setting this
     *   option, a hidden input will be generated. If you do not want any hidden input, you should explicitly set
     *   this option as null.
     * - `separator`: _string_, the HTML code that separates items.
     * - `item: callable, a callback that can be used to customize the generation of the HTML code corresponding to a
     *   single item in $items. The signature of this callback must be:
     *
     * ~~~
     * function ($index, $label, $name, $checked, $value)
     * ~~~
     *
     * where $index is the zero-based index of the radio button in the whole list; $label is the label for the radio
     * button; and $name, $value and $checked represent the name, value and the checked status of the radio button
     * input.
     *
     * @return ActiveField object
     */
    public function radioButtonGroup($items, $options = [])
    {
        return $this->getToggleFieldList(self::TYPE_RADIO, $items, $options, true);
    }
    /**
     * Renders a list of checkbox toggle buttons.
     *
     * @see http://getbootstrap.com/javascript/#buttons-checkbox-radio
     *
     * @param array $items the data item used to generate the checkboxes. The array values are the labels, while the
     * array keys are the corresponding checkbox values. Note that the labels will NOT be HTML-encoded, while the
     * values will.
     * @param array $options options (name => config) for the checkbox button list. The following options are specially
     * handled:
     *
     * - `unselect`: _string_, the value that should be submitted when none of the checkboxes is selected. By setting this
     *   option, a hidden input will be generated. If you do not want any hidden input, you should explicitly set
     *   this option as null.
     * - `separator`: _string_, the HTML code that separates items.
     * - `item: callable, a callback that can be used to customize the generation of the HTML code corresponding to a
     *   single item in $items. The signature of this callback must be:
     *
     * ~~~
     * function ($index, $label, $name, $checked, $value)
     * ~~~
     *
     * where $index is the zero-based index of the checkbox button in the whole list; $label is the label for the
     * checkbox button; and $name, $value and $checked represent the name, value and the checked status of the
     * checkbox button input.
     *
     * @return ActiveField object
     */
    public function checkboxButtonGroup($items, $options = [])
    {
        return $this->getToggleFieldList(self::TYPE_CHECKBOX, $items, $options, true);
    }
    /**
     * Generates the hint icon
     *
     * @return string
     */
    protected function getHintIcon()
    {
        if (!$this->getHintData('showIcon')) {
            return '';
        }
        $options = [];
        Html::addCssClass($options, $this->getHintIconCss('Icon'));
        return Html::tag('span', $this->getHintData('icon'), $options);
    }
    /**
     * Generates a toggle field (checkbox or radio)
     *
     * @param string $type the toggle input type 'checkbox' or 'radio'.
     * @param array $options options (name => config) for the toggle input list container tag.
     * @param boolean $enclosedByLabel whether the input is enclosed by the label tag
     *
     * @return ActiveField object
     */
    protected function getToggleField($type = self::TYPE_CHECKBOX, $options = [], $enclosedByLabel = true)
    {
        $this->initDisability($options);
        $inputType = 'active' . ucfirst($type);
        $disabled = ArrayHelper::getValue($options, 'disabled', false);
        $css = $disabled ? $type . ' disabled' : $type;
        $container = ArrayHelper::remove($options, 'container', ['class' => $css]);
        if ($enclosedByLabel) {
            $this->_offset = true;
            $this->parts['{label}'] = '';
            $showLabels = $this->hasLabels();
            if ($showLabels === false) {
                $options['label'] = '';
                $this->showLabels = true;
            }
        } else {
            $this->_offset = false;
            if (isset($options['label']) && !isset($this->parts['{label}'])) {
                $this->parts['label'] = $options['label'];
                if (!empty($options['labelOptions'])) {
                    $this->labelOptions = $options['labelOptions'];
                }
            }
            $options['label'] = null;
            $container = false;
            unset($options['labelOptions']);
        }
        $input = Html::$inputType($this->model, $this->attribute, $options);
        if (is_array($container)) {
            $tag = ArrayHelper::remove($container, 'tag', 'div');
            $input = Html::tag($tag, $input, $container);
        }
        $this->parts['{input}'] = $input;
        $this->adjustLabelFor($options);
        return $this;
    }
    /**
     * Validates and sets disabled or readonly inputs
     *
     * @param array $options the HTML attributes for the input
     */
    protected function initDisability(&$options)
    {
        if ($this->form->disabled && !isset($options['disabled'])) {
            $options['disabled'] = true;
        }
        if ($this->form->readonly && !isset($options['readonly'])) {
            $options['readonly'] = true;
        }
    }
    /**
     * Gets configuration parameter from formConfig
     *
     * @param string $param the parameter name
     * @param mixed $default the default parameter value
     *
     * @return bool the parsed parameter value
     */
    protected function getConfigParam($param, $default = true)
    {
        return isset($this->$param) ? $this->$param : ArrayHelper::getValue($this->form->formConfig, $param, $default);
    }
    /**
     * Generates the hint.
     *
     * @param string $content the hint content
     *
     * @return string
     */
    protected function generateHint($content = null)
    {
        if ($content === null && method_exists($this->model, 'getAttributeHint')) {
            $content = $this->model->getAttributeHint($this->attribute);
        }
        return $this->contentBeforeHint . $content . $this->contentAfterHint;
    }
    /**
     * Initialize the active field
     */
    protected function initActiveField()
    {
        if (isset($this->enableError)) {
            $this->showErrors = $this->enableError;
        }
        if (isset($this->enableLabel)) {
            $this->showLabels = $this->enableLabel;
        }
        $showLabels = $this->getConfigParam('showLabels');
        $this->_isHintSpecial = $this->hintType === self::HINT_SPECIAL;
        if ($this->form->type === ActiveForm::TYPE_INLINE && !isset($this->autoPlaceholder) && $showLabels !== true) {
            $this->autoPlaceholder = true;
        } elseif (!isset($this->autoPlaceholder)) {
            $this->autoPlaceholder = false;
        }
        if ($this->form->type === ActiveForm::TYPE_HORIZONTAL || $this->form->type === ActiveForm::TYPE_VERTICAL) {
            Html::addCssClass($this->labelOptions, 'control-label');
        }
        if ($showLabels === ActiveForm::SCREEN_READER) {
            Html::addCssClass($this->labelOptions, ActiveForm::SCREEN_READER);
        }
        if ($this->form->type === ActiveForm::TYPE_HORIZONTAL) {
            $this->initHorizontal();
        }
        $this->initLabels();
        $this->initHints();
        $this->_hasFeedback = !empty($this->feedbackIcon) && is_array($this->feedbackIcon);
        $this->_iconBesideInput = ArrayHelper::getValue($this->hintSettings, 'iconBesideInput') ? true : false;
        if ($this->_iconBesideInput) {
            $id = ArrayHelper::getValue($this->options, 'id', '');
            $this->_hintPopoverContainer = $id ? "#{$id}-table" : '';
        } else {
            $id = ArrayHelper::getValue($this->form->options, 'id', '');
            $this->_hintPopoverContainer = $id ? "#{$id}" : '';
        }
    }
    /**
     * Initialize label options
     */
    protected function initLabels()
    {
        $labelCss = $this->_labelCss;
        if ($this->hasLabels() === ActiveForm::SCREEN_READER) {
            Html::addCssClass($this->labelOptions, ActiveForm::SCREEN_READER);
        } elseif ($labelCss != self::NOT_SET) {
            Html::addCssClass($this->labelOptions, $labelCss);
        }
    }
    /**
     * Validate label display status
     *
     * @return boolean|string whether labels are to be shown
     */
    protected function hasLabels()
    {
        $showLabels = $this->getConfigParam('showLabels'); // plus abfrage $this-showLabels kombinieren.
        if ($this->autoPlaceholder && $showLabels !== ActiveForm::SCREEN_READER) {
            $showLabels = false;
        }
        return $showLabels;
    }
    /**
     * Prepares bootstrap grid col classes for horizontal layout including label and input tags and initiate private
     * CSS variables. The process order for 'labelSpan' and 'wrapper' is as follows:
     *
     * - Step 1: Check `$labelSpan` and `$deviceSize`.
     * - Step 2: Check `formConfig(['labelSpan' => x, 'deviceSize' => xy]) and build css tag.
     * - If `horizontalCssClasses['wrapper']` is set and no 'col-' tag then add this to css tag from Step 1.
     * - If `horizontalCssClasses['wrapper']` is set and wrapper has 'col-' tag then override css tag completely.
     * - If no `$labelSpan` and no `horizontalCssClasses['wrapper']` is set then use default from [[$_settings]].
     *   Similar behavior to `horizontalCssClasses['label']`.
     */
    protected function initHorizontal()
    {
        $hor = $this->horizontalCssClasses;
        $span = $this->getConfigParam('labelSpan', '');
        $size = $this->getConfigParam('deviceSize', '');
        // check horizontalCssClasses['wrapper'] if there is a col- class
        if (isset($hor['wrapper']) && strpos($hor['wrapper'], 'col-') !== false) {
            $span = '';
        }
        if (empty($span) && !isset($hor['wrapper'])) {
            $span = $this->_settings['labelSpan'];
        }
        if (empty($size)) {
            $size = ArrayHelper::getValue($this->_settings, 'deviceSize');
        }
        $this->deviceSize = $size;
        if ($span != self::NOT_SET && intval($span) > 0) {
            $span = intval($span);
            // validate if invalid labelSpan is passed - else set to DEFAULT_LABEL_SPAN
            if ($span <= 0 || $span >= $this->form->fullSpan) {
                $span = $this->form->fullSpan;
            }
            // validate if invalid deviceSize is passed - else default to SIZE_MEDIUM
            $sizes = [ActiveForm::SIZE_TINY, ActiveForm::SIZE_SMALL, ActiveForm::SIZE_MEDIUM, ActiveForm::SIZE_LARGE];
            if ($size == self::NOT_SET || !in_array($size, $sizes)) {
                $size = ActiveForm::SIZE_MEDIUM;
            }
            $this->labelSpan = $span;
            $prefix = "col-{$size}-";
            $this->_labelCss = $prefix . $span;
            $this->_inputCss = $prefix . ($this->form->fullSpan - $span);
            $this->_offsetCss = $prefix . "offset-" . $span;
        }
        if (isset($hor['wrapper'])) {
            if ($span !== self::NOT_SET) {
                $this->_inputCss .= " ";
            }
            $this->_inputCss .= $hor['wrapper'];
        }
        if (isset($hor['offset'])) {
            $this->_offsetCss = $hor['offset'];
        }
        if (isset($hor['label'])) {
            if ($span !== self::NOT_SET) {
                $this->_labelCss .= " ";
            }
            $this->_labelCss .= $hor['label'];
        }
        if (isset($hor['error'])) {
            Html::addCssClass($this->errorOptions, $hor['error']);
        }
    }
    /**
     * Initialize layout settings for label, input, error and hint blocks and for various bootstrap 3 form layouts
     */
    protected function initLayout()
    {
        $showLabels = $this->hasLabels();
        $showErrors = $this->getConfigParam('showErrors');
        $this->mergeSettings($showLabels, $showErrors);
        $this->buildLayoutParts($showLabels, $showErrors);
    }
    /**
     * Merges the parameters for layout settings
     *
     * @param boolean $showLabels whether to show labels
     * @param boolean $showErrors whether to show errors
     */
    protected function mergeSettings($showLabels, $showErrors)
    {
        $this->_settings['showLabels'] = $showLabels;
        $this->_settings['showErrors'] = $showErrors;
    }
    /**
     * Builds the field layout parts
     *
     * @param boolean $showLabels whether to show labels
     * @param boolean $showErrors whether to show errors
     */
    protected function buildLayoutParts($showLabels, $showErrors)
    {
        if (!$showErrors) {
            $this->_settings['error'] = '';
        }
        $this->parts['{beginWrapper}'] = '';
        $this->parts['{endWrapper}'] = '';
        if ($this->skipFormLayout) {
            $this->mergeSettings($showLabels, $showErrors);
            $this->parts['{beginLabel}'] = '';
            $this->parts['{labelTitle}'] = '';
            $this->parts['{endLabel}'] = '';
            return;
        }
        if (!empty($this->_inputCss)) {
            $offsetDivClass = $this->_offsetCss . " " . $this->_inputCss;
            $inputDivClass = ($this->_offset) ? $offsetDivClass : $this->_inputCss;
            if ($showLabels === false || $showLabels === ActiveForm::SCREEN_READER) {
                $inputDivClass = "col-{$this->deviceSize}-{$this->form->fullSpan}";
            }
            $this->parts['{beginWrapper}'] = Html::beginTag('div', ['class' => $inputDivClass]);
            $this->parts['{endWrapper}'] = Html::endTag('div');
        }
        $this->mergeSettings($showLabels, $showErrors);
    }
    /**
     * Sets the layout element container
     *
     * @param string $type the layout element type
     * @param string $css the css class for the container
     * @param boolean $chk whether to create the container for the layout element
     */
    protected function setLayoutContainer($type, $css = '', $chk = true)
    {
        if (!empty($css) && $chk) {
            $this->_settings[$type] = "{{$type}}
";
        }
    }
    /**
     * Initialize hint settings
     */
    protected function initHints()
    {
        if ($this->hintType !== self::HINT_SPECIAL) {
            return;
        }
        $container = $this->_hintPopoverContainer;
        if ($container === '') {
            $container = $this->_iconBesideInput ? 'table' : 'form';
        }
        $defaultSettings = [
            'showIcon' => true,
            'iconBesideInput' => false,
            'labelTemplate' => '{label}{help}',
            'inputTemplate' => '',
            'onLabelClick' => false,
            'onLabelHover' => true,
            'onIconClick' => true,
            'onIconHover' => false,
            'labelCssClass' => 'kv-hint-label',
            'iconCssClass' => 'kv-hint-icon',
            'contentCssClass' => 'kv-hint-content',
            'icon' => '',
            'hideOnEscape' => true,
            'hideOnClickOut' => true,
            'placement' => 'top',
            'container' => $container,
            'viewport' => ['selector' => 'body', 'padding' => 0],
        ];
        $this->hintSettings = array_replace_recursive($defaultSettings, $this->hintSettings);
        Html::addCssClass($this->options, 'kv-hint-special');
        foreach (static::$_pluginHintKeys as $key) {
            $this->setHintData($key);
        }
    }
    /**
     * Sets a hint property setting as a data attribute within `self::$options`
     *
     * @param string $key the hint property key
     */
    protected function setHintData($key)
    {
        if (isset($this->hintSettings[$key])) {
            $value = $this->hintSettings[$key];
            $this->options['data-' . Inflector::camel2id($key)] = is_bool($value) ? (int)$value : $value;
        }
    }
    /**
     * Initializes placeholder based on $autoPlaceholder
     *
     * @param array $options the HTML attributes for the input
     */
    protected function initPlaceholder(&$options)
    {
        if ($this->autoPlaceholder) {
            $label = $this->model->getAttributeLabel(Html::getAttributeName($this->attribute));
            $this->inputOptions['placeholder'] = $label;
            $options['placeholder'] = $label;
        }
    }
    /**
     * Gets a hint configuration setting value
     *
     * @param string $key the hint setting key to fetch
     * @param mixed $default the default value if not set
     *
     * @return mixed
     */
    protected function getHintData($key, $default = null)
    {
        return ArrayHelper::getValue($this->hintSettings, $key, $default);
    }
    /**
     * Gets the hint icon css based on `hintSettings`
     *
     * @param string $type whether `Label` or `Icon`
     *
     * @return array the css to be applied
     */
    protected function getHintIconCss($type)
    {
        $css = ["kv-hintable"];
        if ($type === 'Icon') {
            $css[] = 'hide';
        }
        if (!empty($this->hintSettings["on{$type}Click"])) {
            $css[] = "kv-hint-click";
        }
        if (!empty($this->hintSettings["on{$type}Hover"])) {
            $css[] = "kv-hint-hover";
        }
        return $css;
    }
    /**
     * Builds the final template based on the bootstrap form type, display settings for label, error, and hint, and
     * content before and after label, input, error, and hint.
     */
    protected function buildTemplate()
    {
        $showLabels = $showErrors = $input = $error = null;
        extract($this->_settings);
        if ($this->_isStatic && $this->showErrors !== true) {
            $showErrors = false;
        }
        $showLabels = $showLabels && $this->hasLabels();
        $this->buildLayoutParts($showLabels, $showErrors);
        extract($this->_settings);
        if (!empty($this->_multiselect)) {
            $input = str_replace('{input}', $this->_multiselect, $input);
        }
        if ($this->_isHintSpecial && $this->getHintData('iconBesideInput') && $this->getHintData('showIcon')) {
            $id = $this->_hintPopoverContainer ? ' id="' . $this->_hintPopoverContainer . '"' : '';
            $help = strtr($this->getHintData('inputTemplate'), ['{help}' => $this->getHintIcon(), '{id}' => $id,]);
            $input = str_replace('{input}', $help, $input);
        }
        $newInput = $this->contentBeforeInput . $this->generateAddon() . $this->renderFeedbackIcon() .
            $this->contentAfterInput;
        $newError = "{$this->contentBeforeError}{error}{$this->contentAfterError}";
        $config = [
            '{beginLabel}' => $showLabels ? '{beginLabel}' : "",
            '{endLabel}' => $showLabels ? '{endLabel}' : "",
            '{label}' => $showLabels ? "{$this->contentBeforeLabel}{label}{$this->contentAfterLabel}" : "",
            '{labelTitle}' => $showLabels ? "{$this->contentBeforeLabel}{labelTitle}{$this->contentAfterLabel}" : "",
            '{input}' => str_replace('{input}', $newInput, $input),
            '{error}' => $showErrors ? str_replace('{error}', $newError, $error) : '',
        ];
        $this->template = strtr($this->template, $config);
    }
    /**
     * Generates the addon markup
     *
     * @return string
     */
    protected function generateAddon()
    {
        if (empty($this->addon)) {
            return '{input}';
        }
        $addon = $this->addon;
        $prepend = static::getAddonContent(ArrayHelper::getValue($addon, 'prepend', ''));
        $append = static::getAddonContent(ArrayHelper::getValue($addon, 'append', ''));
        $content = $prepend . '{input}' . $append;
        $group = ArrayHelper::getValue($addon, 'groupOptions', []);
        Html::addCssClass($group, 'input-group');
        $contentBefore = ArrayHelper::getValue($addon, 'contentBefore', '');
        $contentAfter = ArrayHelper::getValue($addon, 'contentAfter', '');
        $content = Html::tag('div', $contentBefore . $content . $contentAfter, $group);
        return $content;
    }
    /**
     * Render the bootstrap feedback icon
     *
     * @see http://getbootstrap.com/css/#with-optional-icons
     *
     * @return string
     */
    protected function renderFeedbackIcon()
    {
        if (!$this->_hasFeedback) {
            return '';
        }
        $config = $this->feedbackIcon;
        $type = ArrayHelper::getValue($config, 'type', 'icon');
        $prefix = ArrayHelper::getValue($config, 'prefix', 'glyphicon glyphicon-');
        $id = Html::getInputId($this->model, $this->attribute);
        return $this->getFeedbackIcon($config, 'default', $type, $prefix, $id) .
        $this->getFeedbackIcon($config, 'success', $type, $prefix, $id) .
        $this->getFeedbackIcon($config, 'error', $type, $prefix, $id);
    }
    /**
     * Render the label parts
     *
     * @param string|null $label the label or null to use model label
     * @param array $options the tag options
     */
    protected function renderLabelParts($label = null, $options = [])
    {
        $options = array_merge($this->labelOptions, $options);
        if ($label === null) {
            if (isset($options['label'])) {
                $label = $options['label'];
                unset($options['label']);
            } else {
                $attribute = Html::getAttributeName($this->attribute);
                $label = Html::encode($this->model->getAttributeLabel($attribute));
            }
        }
        if (!isset($options['for'])) {
            $options['for'] = Html::getInputId($this->model, $this->attribute);
        }
        $this->parts['{beginLabel}'] = Html::beginTag('label', $options);
        $this->parts['{endLabel}'] = Html::endTag('label');
        if (!isset($this->parts['{labelTitle}'])) {
            $this->parts['{labelTitle}'] = $label;
        }
    }
    /**
     * Generates a feedback icon
     *
     * @param array $config the feedback icon configuration
     * @param string $cat the feedback icon category
     * @param string $type the feedback icon type
     * @param string $prefix the feedback icon prefix
     * @param string $id the input attribute identifier
     *
     * @return string
     */
    protected function getFeedbackIcon($config, $cat, $type, $prefix, $id)
    {
        $markup = ArrayHelper::getValue($config, $cat, null);
        if ($markup === null) {
            return '';
        }
        $desc = ArrayHelper::remove($options, 'description', "({$cat})");
        $options = ArrayHelper::getValue($config, $cat . 'Options', []);
        $options['aria-hidden'] = true;
        $key = $id . '-' . $cat;
        $this->inputOptions['aria-describedby'] = empty($this->inputOptions['aria-describedby']) ? $key :
            $this->inputOptions['aria-describedby'] . ' ' . $key;
        Html::addCssClass($options, 'form-control-feedback');
        Html::addCssClass($options, 'kv-feedback-' . $cat);
        $icon = $type === 'raw' ? $markup : Html::tag('i', '', ['class' => $prefix . $markup]);
        return Html::tag('span', $icon, $options) . Html::tag('span', $desc, ['id' => $key, 'class' => 'sr-only']);
    }
    /**
     * Renders a list of checkboxes / radio buttons. The selection of the checkbox / radio buttons is taken from the
     * value of the model attribute.
     *
     * @param string $type the toggle input type 'checkbox' or 'radio'.
     * @param array $items the data item used to generate the checkbox / radio buttons. The array keys are the labels,
     * while the array values are the corresponding checkbox / radio button values. Note that the labels will NOT
     * be HTML-encoded, while the values will be encoded.
     * @param array $options options (name => config) for the checkbox / radio button list. The following options are
     * specially handled:
     *
     * - `unselect`: _string_, the value that should be submitted when none of the checkbox / radio buttons is selected. By
     *   setting this option, a hidden input will be generated.
     * - `separator`: _string_, the HTML code that separates items.
     * - `inline`: _boolean_, whether the list should be displayed as a series on the same line, default is false
     * - `disabledItems`: _array_, the list of values that will be disabled.
     * - `readonlyItems`: _array_, the list of values that will be readonly.
     * - `item: callable, a callback that can be used to customize the generation of the HTML code corresponding to a
     *   single item in $items. The signature of this callback must be:
     *
     * ~~~
     * function ($index, $label, $name, $checked, $value)
     * ~~~
     *
     * where $index is the zero-based index of the checkbox/ radio button in the whole list; $label is the label for
     * the checkbox/ radio button; and $name, $value and $checked represent the name, value and the checked status
     * of the checkbox/ radio button input.
     *
     * @param boolean $asButtonGroup whether to generate the toggle list as a bootstrap button group
     *
     * @return ActiveField object
     */
    protected function getToggleFieldList($type, $items, $options = [], $asButtonGroup = false)
    {
        $disabled = ArrayHelper::remove($options, 'disabledItems', []);
        $readonly = ArrayHelper::remove($options, 'readonlyItems', []);
        if ($asButtonGroup) {
            Html::addCssClass($options, 'btn-group');
            $options['data-toggle'] = 'buttons';
            $options['inline'] = true;
            if (!isset($options['itemOptions']['labelOptions']['class'])) {
                $options['itemOptions']['labelOptions']['class'] = 'btn btn-default';
            }
        }
        $inline = ArrayHelper::remove($options, 'inline', false);
        $inputType = "{$type}List";
        $opts = ArrayHelper::getValue($options, 'itemOptions', []);
        $this->initDisability($opts);
        $css = $this->form->disabled ? ' disabled' : '';
        $css .= $this->form->readonly ? ' readonly' : '';
        if ($inline && !isset($options['itemOptions']['labelOptions']['class'])) {
            $options['itemOptions']['labelOptions']['class'] = "{$type}-inline{$css}";
        } elseif (!isset($options['item'])) {
            $labelOptions = ArrayHelper::getValue($opts, 'labelOptions', []);
            $options['item'] = function ($index, $label, $name, $checked, $value)
            use ($type, $css, $disabled, $readonly, $asButtonGroup, $labelOptions, $opts) {
                $opts += [
                    'data-index' => $index,
                    'label' => $label,
                    'value' => $value,
                    'disabled' => $this->form->disabled,
                    'readonly' => $this->form->readonly,
                ];
                if ($asButtonGroup && $checked) {
                    Html::addCssClass($labelOptions, 'active');
                }
                if (!empty($disabled) && in_array($value, $disabled) || $this->form->disabled) {
                    Html::addCssClass($labelOptions, 'disabled');
                    $opts['disabled'] = true;
                }
                if (!empty($readonly) && in_array($value, $readonly) || $this->form->readonly) {
                    Html::addCssClass($labelOptions, 'disabled');
                    $opts['readonly'] = true;
                }
                $opts['labelOptions'] = $labelOptions;
                $out = Html::$type($name, $checked, $opts);
                return $asButtonGroup ? $out : "{$out}
";
            };
        }
        return parent::$inputType($items, $options);
    }
}