"Fossies" - the Fresh Open Source Software Archive

Member "yii-1.1.22.bf1d26/framework/web/helpers/CHtml.php" (16 Jan 2020, 114600 Bytes) of package /linux/www/yii-1.1.22.bf1d26.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "CHtml.php" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.1.21.733ac5_vs_1.1.22.bf1d26.

    1 <?php
    2 /**
    3  * CHtml class file.
    4  *
    5  * @author Qiang Xue <qiang.xue@gmail.com>
    6  * @link http://www.yiiframework.com/
    7  * @copyright 2008-2013 Yii Software LLC
    8  * @license http://www.yiiframework.com/license/
    9  */
   10 
   11 
   12 /**
   13  * CHtml is a static class that provides a collection of helper methods for creating HTML views.
   14  *
   15  * Nearly all of the methods in this class allow setting additional html attributes for the html
   16  * tags they generate. You can specify for example. 'class', 'style'  or 'id' for an html element.
   17  * For example when using <code>array('class' => 'my-class', 'target' => '_blank')</code> as htmlOptions
   18  * it will result in the html attributes rendered like this: <code>class="my-class" target="_blank"</code>.
   19  *
   20  * @author Qiang Xue <qiang.xue@gmail.com>
   21  * @package system.web.helpers
   22  * @since 1.0
   23  */
   24 class CHtml
   25 {
   26     const ID_PREFIX='yt';
   27     /**
   28      * @var string the CSS class for displaying error summaries (see {@link errorSummary}).
   29      */
   30     public static $errorSummaryCss='errorSummary';
   31     /**
   32      * @var string the CSS class for displaying error messages (see {@link error}).
   33      */
   34     public static $errorMessageCss='errorMessage';
   35     /**
   36      * @var string the CSS class for highlighting error inputs. Form inputs will be appended
   37      * with this CSS class if they have input errors.
   38      */
   39     public static $errorCss='error';
   40     /**
   41      * @var string the tag name for the error container tag. Defaults to 'div'.
   42      * @since 1.1.13
   43      */
   44     public static $errorContainerTag='div';
   45     /**
   46      * @var string the CSS class for required labels. Defaults to 'required'.
   47      * @see label
   48      */
   49     public static $requiredCss='required';
   50     /**
   51      * @var string the HTML code to be prepended to the required label.
   52      * @see label
   53      */
   54     public static $beforeRequiredLabel='';
   55     /**
   56      * @var string the HTML code to be appended to the required label.
   57      * @see label
   58      */
   59     public static $afterRequiredLabel=' <span class="required">*</span>';
   60     /**
   61      * @var integer the counter for generating automatic input field names.
   62      */
   63     public static $count=0;
   64     /**
   65      * Sets the default style for attaching jQuery event handlers.
   66      *
   67      * If set to true (default), event handlers are delegated.
   68      * Event handlers are attached to the document body and can process events
   69      * from descendant elements that are added to the document at a later time.
   70      *
   71      * If set to false, event handlers are directly bound.
   72      * Event handlers are attached directly to the DOM element, that must already exist
   73      * on the page. Elements injected into the page at a later time will not be processed.
   74      *
   75      * You can override this setting for a particular element by setting the htmlOptions delegate attribute
   76      * (see {@link clientChange}).
   77      *
   78      * For more information about attaching jQuery event handler see {@link http://api.jquery.com/on/}
   79      * @since 1.1.9
   80      * @see clientChange
   81      */
   82     public static $liveEvents=true;
   83     /**
   84      * @var boolean whether to close single tags. Defaults to true. Can be set to false for HTML5.
   85      * @since 1.1.13
   86      */
   87     public static $closeSingleTags=true;
   88     /**
   89      * @var boolean whether to render special attributes value. Defaults to true. Can be set to false for HTML5.
   90      * @since 1.1.13
   91      */
   92     public static $renderSpecialAttributesValue=true;
   93     /**
   94      * @var callback the generator used in the {@link CHtml::modelName()} method.
   95      * @since 1.1.14
   96      */
   97     private static $_modelNameConverter;
   98 
   99     /**
  100      * Encodes special characters into HTML entities.
  101      * The {@link CApplication::charset application charset} will be used for encoding.
  102      * @param string $text data to be encoded
  103      * @return string the encoded data
  104      * @see http://www.php.net/manual/en/function.htmlspecialchars.php
  105      */
  106     public static function encode($text)
  107     {
  108         return htmlspecialchars($text,ENT_QUOTES,Yii::app()->charset);
  109     }
  110 
  111     /**
  112      * Decodes special HTML entities back to the corresponding characters.
  113      * This is the opposite of {@link encode()}.
  114      * @param string $text data to be decoded
  115      * @return string the decoded data
  116      * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
  117      * @since 1.1.8
  118      */
  119     public static function decode($text)
  120     {
  121         return htmlspecialchars_decode($text,ENT_QUOTES);
  122     }
  123 
  124     /**
  125      * Encodes special characters in an array of strings into HTML entities.
  126      * Both the array keys and values will be encoded if needed.
  127      * If a value is an array, this method will also encode it recursively.
  128      * The {@link CApplication::charset application charset} will be used for encoding.
  129      * @param array $data data to be encoded
  130      * @return array the encoded data
  131      * @see http://www.php.net/manual/en/function.htmlspecialchars.php
  132      */
  133     public static function encodeArray($data)
  134     {
  135         $d=array();
  136         foreach($data as $key=>$value)
  137         {
  138             if(is_string($key))
  139                 $key=htmlspecialchars($key,ENT_QUOTES,Yii::app()->charset);
  140             if(is_string($value))
  141                 $value=htmlspecialchars($value,ENT_QUOTES,Yii::app()->charset);
  142             elseif(is_array($value))
  143                 $value=self::encodeArray($value);
  144             $d[$key]=$value;
  145         }
  146         return $d;
  147     }
  148 
  149     /**
  150      * Generates an HTML element.
  151      * @param string $tag the tag name
  152      * @param array $htmlOptions the element attributes. The values will be HTML-encoded using {@link encode()}.
  153      * If an 'encode' attribute is given and its value is false,
  154      * the rest of the attribute values will NOT be HTML-encoded.
  155      * Since version 1.1.5, attributes whose value is null will not be rendered.
  156      * @param mixed $content the content to be enclosed between open and close element tags. It will not be HTML-encoded.
  157      * If false, it means there is no body content.
  158      * @param boolean $closeTag whether to generate the close tag.
  159      * @return string the generated HTML element tag
  160      */
  161     public static function tag($tag,$htmlOptions=array(),$content=false,$closeTag=true)
  162     {
  163         $html='<' . $tag . self::renderAttributes($htmlOptions);
  164         if($content===false)
  165             return $closeTag && self::$closeSingleTags ? $html.' />' : $html.'>';
  166         else
  167             return $closeTag ? $html.'>'.$content.'</'.$tag.'>' : $html.'>'.$content;
  168     }
  169 
  170     /**
  171      * Generates an open HTML element.
  172      * @param string $tag the tag name
  173      * @param array $htmlOptions the element attributes. The values will be HTML-encoded using {@link encode()}.
  174      * If an 'encode' attribute is given and its value is false,
  175      * the rest of the attribute values will NOT be HTML-encoded.
  176      * Since version 1.1.5, attributes whose value is null will not be rendered.
  177      * @return string the generated HTML element tag
  178      */
  179     public static function openTag($tag,$htmlOptions=array())
  180     {
  181         return '<' . $tag . self::renderAttributes($htmlOptions) . '>';
  182     }
  183 
  184     /**
  185      * Generates a close HTML element.
  186      * @param string $tag the tag name
  187      * @return string the generated HTML element tag
  188      */
  189     public static function closeTag($tag)
  190     {
  191         return '</'.$tag.'>';
  192     }
  193 
  194     /**
  195      * Encloses the given string within a CDATA tag.
  196      * @param string $text the string to be enclosed
  197      * @return string the CDATA tag with the enclosed content.
  198      */
  199     public static function cdata($text)
  200     {
  201         return '<![CDATA[' . $text . ']]>';
  202     }
  203 
  204     /**
  205      * Generates a meta tag that can be inserted in the head section of HTML page.
  206      * @param string $content content attribute of the meta tag
  207      * @param string $name name attribute of the meta tag. If null, the attribute will not be generated
  208      * @param string $httpEquiv http-equiv attribute of the meta tag. If null, the attribute will not be generated
  209      * @param array $options other options in name-value pairs (e.g. 'scheme', 'lang')
  210      * @return string the generated meta tag
  211      */
  212     public static function metaTag($content,$name=null,$httpEquiv=null,$options=array())
  213     {
  214         if($name!==null)
  215             $options['name']=$name;
  216         if($httpEquiv!==null)
  217             $options['http-equiv']=$httpEquiv;
  218         $options['content']=$content;
  219         return self::tag('meta',$options);
  220     }
  221 
  222     /**
  223      * Generates a link tag that can be inserted in the head section of HTML page.
  224      * Do not confuse this method with {@link link()}. The latter generates a hyperlink.
  225      * @param string $relation rel attribute of the link tag. If null, the attribute will not be generated.
  226      * @param string $type type attribute of the link tag. If null, the attribute will not be generated.
  227      * @param string $href href attribute of the link tag. If null, the attribute will not be generated.
  228      * @param string $media media attribute of the link tag. If null, the attribute will not be generated.
  229      * @param array $options other options in name-value pairs
  230      * @return string the generated link tag
  231      */
  232     public static function linkTag($relation=null,$type=null,$href=null,$media=null,$options=array())
  233     {
  234         if($relation!==null)
  235             $options['rel']=$relation;
  236         if($type!==null)
  237             $options['type']=$type;
  238         if($href!==null)
  239             $options['href']=$href;
  240         if($media!==null)
  241             $options['media']=$media;
  242         return self::tag('link',$options);
  243     }
  244 
  245     /**
  246      * Encloses the given CSS content with a CSS tag.
  247      * @param string $text the CSS content
  248      * @param string $media the media that this CSS should apply to.
  249      * @return string the CSS properly enclosed
  250      */
  251     public static function css($text,$media='')
  252     {
  253         if($media!=='')
  254             $media=' media="'.$media.'"';
  255         return "<style type=\"text/css\"{$media}>\n/*<![CDATA[*/\n{$text}\n/*]]>*/\n</style>";
  256     }
  257 
  258     /**
  259      * Registers a 'refresh' meta tag.
  260      * This method can be invoked anywhere in a view. It will register a 'refresh'
  261      * meta tag with {@link CClientScript} so that the page can be refreshed in
  262      * the specified seconds.
  263      * @param integer $seconds the number of seconds to wait before refreshing the page
  264      * @param string $url the URL to which the page should be redirected to. If empty, it means the current page.
  265      * @since 1.1.1
  266      */
  267     public static function refresh($seconds,$url='')
  268     {
  269         $content="$seconds";
  270         if($url!=='')
  271             $content.=';url='.self::normalizeUrl($url);
  272         Yii::app()->clientScript->registerMetaTag($content,null,'refresh');
  273     }
  274 
  275     /**
  276      * Links to the specified CSS file.
  277      * @param string $url the CSS URL
  278      * @param string $media the media that this CSS should apply to.
  279      * @return string the CSS link.
  280      */
  281     public static function cssFile($url,$media='')
  282     {
  283         return CHtml::linkTag('stylesheet','text/css',$url,$media!=='' ? $media : null);
  284     }
  285 
  286     /**
  287      * Encloses the given JavaScript within a script tag.
  288      * @param string $text the JavaScript to be enclosed
  289      * @param array $htmlOptions additional HTML attributes (see {@link tag})
  290      * @return string the enclosed JavaScript
  291      */
  292     public static function script($text,array $htmlOptions=array())
  293     {
  294         $defaultHtmlOptions=array(
  295             'type'=>'text/javascript',
  296         );
  297         $htmlOptions=array_merge($defaultHtmlOptions,$htmlOptions);
  298         return self::tag('script',$htmlOptions,"\n/*<![CDATA[*/\n{$text}\n/*]]>*/\n");
  299     }
  300 
  301     /**
  302      * Includes a JavaScript file.
  303      * @param string $url URL for the JavaScript file
  304      * @param array $htmlOptions additional HTML attributes (see {@link tag})
  305      * @return string the JavaScript file tag
  306      */
  307     public static function scriptFile($url,array $htmlOptions=array())
  308     {
  309         $defaultHtmlOptions=array(
  310             'type'=>'text/javascript',
  311             'src'=>$url
  312         );
  313         $htmlOptions=array_merge($defaultHtmlOptions,$htmlOptions);
  314         return self::tag('script',$htmlOptions,'');
  315     }
  316 
  317     /**
  318      * Generates an opening form tag.
  319      * This is a shortcut to {@link beginForm}.
  320      * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.)
  321      * @param string $method form method (e.g. post, get)
  322      * @param array $htmlOptions additional HTML attributes (see {@link tag}).
  323      * @return string the generated form tag.
  324      */
  325     public static function form($action='',$method='post',$htmlOptions=array())
  326     {
  327         return self::beginForm($action,$method,$htmlOptions);
  328     }
  329 
  330     /**
  331      * Generates an opening form tag.
  332      * Note, only the open tag is generated. A close tag should be placed manually
  333      * at the end of the form.
  334      * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.)
  335      * @param string $method form method (e.g. post, get)
  336      * @param array $htmlOptions additional HTML attributes (see {@link tag}).
  337      * @return string the generated form tag.
  338      * @see endForm
  339      */
  340     public static function beginForm($action='',$method='post',$htmlOptions=array())
  341     {
  342         $htmlOptions['action']=$url=self::normalizeUrl($action);
  343         if(strcasecmp($method,'get')!==0 && strcasecmp($method,'post')!==0)
  344         {
  345             $customMethod=$method;
  346             $method='post';
  347         }
  348         else
  349             $customMethod=false;
  350 
  351         $htmlOptions['method']=$method;
  352         $form=self::tag('form',$htmlOptions,false,false);
  353         $hiddens=array();
  354         if(!strcasecmp($method,'get') && ($pos=strpos($url,'?'))!==false)
  355         {
  356             foreach(explode('&',substr(preg_replace('/#.+$/','',$url),$pos+1)) as $pair)
  357             {
  358                 if(($pos=strpos($pair,'='))!==false)
  359                     $hiddens[]=self::hiddenField(urldecode(substr($pair,0,$pos)),urldecode(substr($pair,$pos+1)),array('id'=>false));
  360                 else
  361                     $hiddens[]=self::hiddenField(urldecode($pair),'',array('id'=>false));
  362             }
  363         }
  364         $request=Yii::app()->request;
  365         if($request->enableCsrfValidation && !strcasecmp($method,'post'))
  366             $hiddens[]=self::hiddenField($request->csrfTokenName,$request->getCsrfToken(),array('id'=>false));
  367         if($customMethod!==false)
  368             $hiddens[]=self::hiddenField('_method',$customMethod);
  369         if($hiddens!==array())
  370             $form.="\n".implode("\n",$hiddens);
  371         return $form;
  372     }
  373 
  374     /**
  375      * Generates a closing form tag.
  376      * @return string the generated tag
  377      * @see beginForm
  378      */
  379     public static function endForm()
  380     {
  381         return '</form>';
  382     }
  383 
  384     /**
  385      * Generates a stateful form tag.
  386      * A stateful form tag is similar to {@link form} except that it renders an additional
  387      * hidden field for storing persistent page states. You should use this method to generate
  388      * a form tag if you want to access persistent page states when the form is submitted.
  389      * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.)
  390      * @param string $method form method (e.g. post, get)
  391      * @param array $htmlOptions additional HTML attributes (see {@link tag}).
  392      * @return string the generated form tag.
  393      */
  394     public static function statefulForm($action='',$method='post',$htmlOptions=array())
  395     {
  396         return self::form($action,$method,$htmlOptions)."\n".
  397             self::tag('div',array('style'=>'display:none'),self::pageStateField(''));
  398     }
  399 
  400     /**
  401      * Generates a hidden field for storing persistent page states.
  402      * This method is internally used by {@link statefulForm}.
  403      * @param string $value the persistent page states in serialized format
  404      * @return string the generated hidden field
  405      */
  406     public static function pageStateField($value)
  407     {
  408         return '<input type="hidden" name="'.CController::STATE_INPUT_NAME.'" value="'.$value.'" />';
  409     }
  410 
  411     /**
  412      * Generates a hyperlink tag.
  413      * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag.
  414      * @param mixed $url a URL or an action route that can be used to create a URL.
  415      * See {@link normalizeUrl} for more details about how to specify this parameter.
  416      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  417      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  418      * @return string the generated hyperlink
  419      * @see normalizeUrl
  420      * @see clientChange
  421      */
  422     public static function link($text,$url='#',$htmlOptions=array())
  423     {
  424         if($url!=='')
  425             $htmlOptions['href']=self::normalizeUrl($url);
  426         self::clientChange('click',$htmlOptions);
  427         return self::tag('a',$htmlOptions,$text);
  428     }
  429 
  430     /**
  431      * Generates a mailto link.
  432      * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag.
  433      * @param string $email email address. If this is empty, the first parameter (link body) will be treated as the email address.
  434      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  435      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  436      * @return string the generated mailto link
  437      * @see clientChange
  438      */
  439     public static function mailto($text,$email='',$htmlOptions=array())
  440     {
  441         if($email==='')
  442             $email=$text;
  443         return self::link($text,'mailto:'.$email,$htmlOptions);
  444     }
  445 
  446     /**
  447      * Generates an image tag.
  448      * @param string $src the image URL
  449      * @param string $alt the alternative text display
  450      * @param array $htmlOptions additional HTML attributes (see {@link tag}).
  451      * @return string the generated image tag
  452      */
  453     public static function image($src,$alt='',$htmlOptions=array())
  454     {
  455         $htmlOptions['src']=$src;
  456         $htmlOptions['alt']=$alt;
  457         return self::tag('img',$htmlOptions);
  458     }
  459 
  460     /**
  461      * Generates a button.
  462      * @param string $label the button label
  463      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  464      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  465      * @return string the generated button tag
  466      * @see clientChange
  467      */
  468     public static function button($label='button',$htmlOptions=array())
  469     {
  470         if(!isset($htmlOptions['name']))
  471         {
  472             if(!array_key_exists('name',$htmlOptions))
  473                 $htmlOptions['name']=self::ID_PREFIX.self::$count++;
  474         }
  475         if(!isset($htmlOptions['type']))
  476             $htmlOptions['type']='button';
  477         if(!isset($htmlOptions['value']) && $htmlOptions['type']!='image')
  478             $htmlOptions['value']=$label;
  479         self::clientChange('click',$htmlOptions);
  480         return self::tag('input',$htmlOptions);
  481     }
  482 
  483     /**
  484      * Generates a button using HTML button tag.
  485      * This method is similar to {@link button} except that it generates a 'button'
  486      * tag instead of 'input' tag.
  487      * @param string $label the button label. Note that this value will be directly inserted in the button element
  488      * without being HTML-encoded.
  489      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  490      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  491      * @return string the generated button tag
  492      * @see clientChange
  493      */
  494     public static function htmlButton($label='button',$htmlOptions=array())
  495     {
  496         if(!isset($htmlOptions['name']))
  497             $htmlOptions['name']=self::ID_PREFIX.self::$count++;
  498         if(!isset($htmlOptions['type']))
  499             $htmlOptions['type']='button';
  500         self::clientChange('click',$htmlOptions);
  501         return self::tag('button',$htmlOptions,$label);
  502     }
  503 
  504     /**
  505      * Generates a submit button.
  506      * @param string $label the button label
  507      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  508      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  509      * @return string the generated button tag
  510      * @see clientChange
  511      */
  512     public static function submitButton($label='submit',$htmlOptions=array())
  513     {
  514         $htmlOptions['type']='submit';
  515         return self::button($label,$htmlOptions);
  516     }
  517 
  518     /**
  519      * Generates a reset button.
  520      * @param string $label the button label
  521      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  522      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  523      * @return string the generated button tag
  524      * @see clientChange
  525      */
  526     public static function resetButton($label='reset',$htmlOptions=array())
  527     {
  528         $htmlOptions['type']='reset';
  529         return self::button($label,$htmlOptions);
  530     }
  531 
  532     /**
  533      * Generates an image submit button.
  534      * @param string $src the image URL
  535      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  536      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  537      * @return string the generated button tag
  538      * @see clientChange
  539      */
  540     public static function imageButton($src,$htmlOptions=array())
  541     {
  542         $htmlOptions['src']=$src;
  543         $htmlOptions['type']='image';
  544         return self::button('submit',$htmlOptions);
  545     }
  546 
  547     /**
  548      * Generates a link submit button.
  549      * @param string $label the button label
  550      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  551      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  552      * @return string the generated button tag
  553      * @see clientChange
  554      */
  555     public static function linkButton($label='submit',$htmlOptions=array())
  556     {
  557         if(!isset($htmlOptions['submit']))
  558             $htmlOptions['submit']=isset($htmlOptions['href']) ? $htmlOptions['href'] : '';
  559         return self::link($label,'#',$htmlOptions);
  560     }
  561 
  562     /**
  563      * Generates a label tag.
  564      * @param string $label label text. Note, you should HTML-encode the text if needed.
  565      * @param string $for the ID of the HTML element that this label is associated with.
  566      * If this is false, the 'for' attribute for the label tag will not be rendered.
  567      * @param array $htmlOptions additional HTML attributes.
  568      * The following HTML option is recognized:
  569      * <ul>
  570      * <li>required: if this is set and is true, the label will be styled
  571      * with CSS class 'required' (customizable with CHtml::$requiredCss),
  572      * and be decorated with {@link CHtml::beforeRequiredLabel} and
  573      * {@link CHtml::afterRequiredLabel}.</li>
  574      * </ul>
  575      * @return string the generated label tag
  576      */
  577     public static function label($label,$for,$htmlOptions=array())
  578     {
  579         if($for===false)
  580             unset($htmlOptions['for']);
  581         else
  582             $htmlOptions['for']=$for;
  583         if(isset($htmlOptions['required']))
  584         {
  585             if($htmlOptions['required'])
  586             {
  587                 if(isset($htmlOptions['class']))
  588                     $htmlOptions['class'].=' '.self::$requiredCss;
  589                 else
  590                     $htmlOptions['class']=self::$requiredCss;
  591                 $label=self::$beforeRequiredLabel.$label.self::$afterRequiredLabel;
  592             }
  593             unset($htmlOptions['required']);
  594         }
  595         return self::tag('label',$htmlOptions,$label);
  596     }
  597 
  598     /**
  599      * Generates a color picker field input.
  600      * @param string $name the input name
  601      * @param string $value the input value
  602      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  603      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  604      * @return string the generated input field
  605      * @see clientChange
  606      * @see inputField
  607      * @since 1.1.16
  608      */
  609     public static function colorField($name,$value='',$htmlOptions=array())
  610     {
  611         self::clientChange('change',$htmlOptions);
  612         return self::inputField('color',$name,$value,$htmlOptions);
  613     }
  614 
  615     /**
  616      * Generates a text field input.
  617      * @param string $name the input name
  618      * @param string $value the input value
  619      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  620      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  621      * @return string the generated input field
  622      * @see clientChange
  623      * @see inputField
  624      */
  625     public static function textField($name,$value='',$htmlOptions=array())
  626     {
  627         self::clientChange('change',$htmlOptions);
  628         return self::inputField('text',$name,$value,$htmlOptions);
  629     }
  630 
  631     /**
  632      * Generates a search field input.
  633      * @param string $name the input name
  634      * @param string $value the input value
  635      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  636      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  637      * @return string the generated input field
  638      * @see clientChange
  639      * @see inputField
  640      * @since 1.1.16
  641      */
  642     public static function searchField($name,$value='',$htmlOptions=array())
  643     {
  644         self::clientChange('change',$htmlOptions);
  645         return self::inputField('search',$name,$value,$htmlOptions);
  646     }
  647     /**
  648      * Generates a number field input.
  649      * @param string $name the input name
  650      * @param string $value the input value
  651      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  652      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  653      * @return string the generated input field
  654      * @see clientChange
  655      * @see inputField
  656      * @since 1.1.14
  657      */
  658     public static function numberField($name,$value='',$htmlOptions=array())
  659     {
  660         self::clientChange('change',$htmlOptions);
  661         return self::inputField('number',$name,$value,$htmlOptions);
  662     }
  663 
  664     /**
  665      * Generates a range field input.
  666      * @param string $name the input name
  667      * @param string $value the input value
  668      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  669      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  670      * @return string the generated input field
  671      * @see clientChange
  672      * @see inputField
  673      * @since 1.1.14
  674      */
  675     public static function rangeField($name,$value='',$htmlOptions=array())
  676     {
  677         self::clientChange('change',$htmlOptions);
  678         return self::inputField('range',$name,$value,$htmlOptions);
  679     }
  680 
  681     /**
  682      * Generates a date field input.
  683      * @param string $name the input name
  684      * @param string $value the input value
  685      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  686      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  687      * @return string the generated input field
  688      * @see clientChange
  689      * @see inputField
  690      * @since 1.1.14
  691      */
  692     public static function dateField($name,$value='',$htmlOptions=array())
  693     {
  694         self::clientChange('change',$htmlOptions);
  695         return self::inputField('date',$name,$value,$htmlOptions);
  696     }
  697 
  698     /**
  699      * Generates a time field input.
  700      * @param string $name the input name
  701      * @param string $value the input value
  702      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  703      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  704      * @return string the generated input field
  705      * @see clientChange
  706      * @see inputField
  707      * @since 1.1.14
  708      */
  709     public static function timeField($name,$value='',$htmlOptions=array())
  710     {
  711         self::clientChange('change',$htmlOptions);
  712         return self::inputField('time',$name,$value,$htmlOptions);
  713     }
  714 
  715     /**
  716      * Generates a datetime field input.
  717      * @param string $name the input name
  718      * @param string $value the input value
  719      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  720      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  721      * @return string the generated input field
  722      * @see clientChange
  723      * @see inputField
  724      * @since 1.1.16
  725      */
  726     public static function dateTimeField($name,$value='',$htmlOptions=array())
  727     {
  728         self::clientChange('change',$htmlOptions);
  729         return self::inputField('datetime',$name,$value,$htmlOptions);
  730     }
  731 
  732     /**
  733      * Generates a local datetime field input.
  734      * @param string $name the input name
  735      * @param string $value the input value
  736      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  737      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  738      * @return string the generated input field
  739      * @see clientChange
  740      * @see inputField
  741      * @since 1.1.16
  742      */
  743     public static function dateTimeLocalField($name,$value='',$htmlOptions=array())
  744     {
  745         self::clientChange('change',$htmlOptions);
  746         return self::inputField('datetime-local',$name,$value,$htmlOptions);
  747     }
  748 
  749     /**
  750      * Generates a week field input.
  751      * @param string $name the input name
  752      * @param string $value the input value
  753      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  754      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  755      * @return string the generated input field
  756      * @see clientChange
  757      * @see inputField
  758      * @since 1.1.16
  759      */
  760     public static function weekField($name,$value='',$htmlOptions=array())
  761     {
  762         self::clientChange('change',$htmlOptions);
  763         return self::inputField('week',$name,$value,$htmlOptions);
  764     }
  765 
  766     /**
  767      * Generates an email field input.
  768      * @param string $name the input name
  769      * @param string $value the input value
  770      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  771      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  772      * @return string the generated input field
  773      * @see clientChange
  774      * @see inputField
  775      * @since 1.1.14
  776      */
  777     public static function emailField($name,$value='',$htmlOptions=array())
  778     {
  779         self::clientChange('change',$htmlOptions);
  780         return self::inputField('email',$name,$value,$htmlOptions);
  781     }
  782 
  783     /**
  784      * Generates a telephone field input.
  785      * @param string $name the input name
  786      * @param string $value the input value
  787      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  788      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  789      * @return string the generated input field
  790      * @see clientChange
  791      * @see inputField
  792      * @since 1.1.14
  793      */
  794     public static function telField($name,$value='',$htmlOptions=array())
  795     {
  796         self::clientChange('change',$htmlOptions);
  797         return self::inputField('tel',$name,$value,$htmlOptions);
  798     }
  799 
  800     /**
  801      * Generates a URL field input.
  802      * @param string $name the input name
  803      * @param string $value the input value
  804      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  805      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  806      * @return string the generated input field
  807      * @see clientChange
  808      * @see inputField
  809      * @since 1.1.14
  810      */
  811     public static function urlField($name,$value='',$htmlOptions=array())
  812     {
  813         self::clientChange('change',$htmlOptions);
  814         return self::inputField('url',$name,$value,$htmlOptions);
  815     }
  816 
  817     /**
  818      * Generates a hidden input.
  819      * @param string $name the input name
  820      * @param string $value the input value
  821      * @param array $htmlOptions additional HTML attributes (see {@link tag}).
  822      * @return string the generated input field
  823      * @see inputField
  824      */
  825     public static function hiddenField($name,$value='',$htmlOptions=array())
  826     {
  827         return self::inputField('hidden',$name,$value,$htmlOptions);
  828     }
  829 
  830     /**
  831      * Generates a password field input.
  832      * @param string $name the input name
  833      * @param string $value the input value
  834      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  835      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  836      * @return string the generated input field
  837      * @see clientChange
  838      * @see inputField
  839      */
  840     public static function passwordField($name,$value='',$htmlOptions=array())
  841     {
  842         self::clientChange('change',$htmlOptions);
  843         return self::inputField('password',$name,$value,$htmlOptions);
  844     }
  845 
  846     /**
  847      * Generates a file input.
  848      * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'.
  849      * After the form is submitted, the uploaded file information can be obtained via $_FILES[$name] (see
  850      * PHP documentation).
  851      * @param string $name the input name
  852      * @param string $value the input value
  853      * @param array $htmlOptions additional HTML attributes (see {@link tag}).
  854      * @return string the generated input field
  855      * @see inputField
  856      */
  857     public static function fileField($name,$value='',$htmlOptions=array())
  858     {
  859         return self::inputField('file',$name,$value,$htmlOptions);
  860     }
  861 
  862     /**
  863      * Generates a text area input.
  864      * @param string $name the input name
  865      * @param string $value the input value
  866      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  867      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  868      * @return string the generated text area
  869      * @see clientChange
  870      * @see inputField
  871      */
  872     public static function textArea($name,$value='',$htmlOptions=array())
  873     {
  874         $htmlOptions['name']=$name;
  875         if(!isset($htmlOptions['id']))
  876             $htmlOptions['id']=self::getIdByName($name);
  877         elseif($htmlOptions['id']===false)
  878             unset($htmlOptions['id']);
  879         self::clientChange('change',$htmlOptions);
  880         return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $value : self::encode($value));
  881     }
  882 
  883     /**
  884      * Generates a radio button.
  885      * @param string $name the input name
  886      * @param boolean $checked whether the radio button is checked
  887      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  888      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  889      * Since version 1.1.2, a special option named 'uncheckValue' is available that can be used to specify
  890      * the value returned when the radio button is not checked. When set, a hidden field is rendered so that
  891      * when the radio button is not checked, we can still obtain the posted uncheck value.
  892      * If 'uncheckValue' is not set or set to NULL, the hidden field will not be rendered.
  893      * @return string the generated radio button
  894      * @see clientChange
  895      * @see inputField
  896      */
  897     public static function radioButton($name,$checked=false,$htmlOptions=array())
  898     {
  899         if($checked)
  900             $htmlOptions['checked']='checked';
  901         else
  902             unset($htmlOptions['checked']);
  903         $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1;
  904         self::clientChange('click',$htmlOptions);
  905 
  906         if(array_key_exists('uncheckValue',$htmlOptions))
  907         {
  908             $uncheck=$htmlOptions['uncheckValue'];
  909             unset($htmlOptions['uncheckValue']);
  910         }
  911         else
  912             $uncheck=null;
  913 
  914         if($uncheck!==null)
  915         {
  916             // add a hidden field so that if the radio button is not selected, it still submits a value
  917             if(isset($htmlOptions['id']) && $htmlOptions['id']!==false)
  918                 $uncheckOptions=array('id'=>self::ID_PREFIX.$htmlOptions['id']);
  919             else
  920                 $uncheckOptions=array('id'=>false);
  921             if(!empty($htmlOptions['disabled']))
  922                 $uncheckOptions['disabled']=$htmlOptions['disabled'];
  923             $hidden=self::hiddenField($name,$uncheck,$uncheckOptions);
  924         }
  925         else
  926             $hidden='';
  927 
  928         // add a hidden field so that if the radio button is not selected, it still submits a value
  929         return $hidden . self::inputField('radio',$name,$value,$htmlOptions);
  930     }
  931 
  932     /**
  933      * Generates a check box.
  934      * @param string $name the input name
  935      * @param boolean $checked whether the check box is checked
  936      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  937      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
  938      * Since version 1.1.2, a special option named 'uncheckValue' is available that can be used to specify
  939      * the value returned when the checkbox is not checked. When set, a hidden field is rendered so that
  940      * when the checkbox is not checked, we can still obtain the posted uncheck value.
  941      * If 'uncheckValue' is not set or set to NULL, the hidden field will not be rendered.
  942      * @return string the generated check box
  943      * @see clientChange
  944      * @see inputField
  945      */
  946     public static function checkBox($name,$checked=false,$htmlOptions=array())
  947     {
  948         if($checked)
  949             $htmlOptions['checked']='checked';
  950         else
  951             unset($htmlOptions['checked']);
  952         $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1;
  953         self::clientChange('click',$htmlOptions);
  954 
  955         if(array_key_exists('uncheckValue',$htmlOptions))
  956         {
  957             $uncheck=$htmlOptions['uncheckValue'];
  958             unset($htmlOptions['uncheckValue']);
  959         }
  960         else
  961             $uncheck=null;
  962 
  963         if($uncheck!==null)
  964         {
  965             // add a hidden field so that if the check box is not checked, it still submits a value
  966             if(isset($htmlOptions['id']) && $htmlOptions['id']!==false)
  967                 $uncheckOptions=array('id'=>self::ID_PREFIX.$htmlOptions['id']);
  968             else
  969                 $uncheckOptions=array('id'=>false);
  970             if(!empty($htmlOptions['disabled']))
  971                 $uncheckOptions['disabled']=$htmlOptions['disabled'];
  972             $hidden=self::hiddenField($name,$uncheck,$uncheckOptions);
  973         }
  974         else
  975             $hidden='';
  976 
  977         // add a hidden field so that if the check box is not checked, it still submits a value
  978         return $hidden . self::inputField('checkbox',$name,$value,$htmlOptions);
  979     }
  980 
  981     /**
  982      * Generates a drop down list.
  983      * @param string $name the input name
  984      * @param string $select the selected value
  985      * @param array $data data for generating the list options (value=>display).
  986      * You may use {@link listData} to generate this data.
  987      * Please refer to {@link listOptions} on how this data is used to generate the list options.
  988      * Note, the values and labels will be automatically HTML-encoded by this method.
  989      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
  990      * attributes are recognized. See {@link clientChange} and {@link tag} for more details.
  991      * In addition, the following options are also supported specifically for dropdown list:
  992      * <ul>
  993      * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
  994      * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
  995      * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
  996      * The 'empty' option can also be an array of value-label pairs.
  997      * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
  998      * <li>options: array, specifies additional attributes for each OPTION tag.
  999      *   The array keys must be the option values, and the array values are the extra
 1000      *   OPTION tag attributes in the name-value pairs. For example,
 1001      * <pre>
 1002      *   array(
 1003      *       'value1'=>array('disabled'=>true,'label'=>'value 1'),
 1004      *       'value2'=>array('label'=>'value 2'),
 1005      *   );
 1006      * </pre>
 1007      * </li>
 1008      * </ul>
 1009      * Since 1.1.13, a special option named 'unselectValue' is available. It can be used to set the value
 1010      * that will be returned when no option is selected in multiple mode. When set, a hidden field is
 1011      * rendered so that if no option is selected in multiple mode, we can still obtain the posted
 1012      * unselect value. If 'unselectValue' is not set or set to NULL, the hidden field will not be rendered.
 1013      * @return string the generated drop down list
 1014      * @see clientChange
 1015      * @see inputField
 1016      * @see listData
 1017      */
 1018     public static function dropDownList($name,$select,$data,$htmlOptions=array())
 1019     {
 1020         $htmlOptions['name']=$name;
 1021 
 1022         if(!isset($htmlOptions['id']))
 1023             $htmlOptions['id']=self::getIdByName($name);
 1024         elseif($htmlOptions['id']===false)
 1025             unset($htmlOptions['id']);
 1026 
 1027         self::clientChange('change',$htmlOptions);
 1028         $options="\n".self::listOptions($select,$data,$htmlOptions);
 1029         $hidden='';
 1030 
 1031         if(!empty($htmlOptions['multiple']))
 1032         {
 1033             if(substr($htmlOptions['name'],-2)!=='[]')
 1034                 $htmlOptions['name'].='[]';
 1035 
 1036             if(isset($htmlOptions['unselectValue']))
 1037             {
 1038                 $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
 1039                 if(!empty($htmlOptions['disabled']))
 1040                     $hiddenOptions['disabled']=$htmlOptions['disabled'];
 1041                 $hidden=self::hiddenField(substr($htmlOptions['name'],0,-2),$htmlOptions['unselectValue'],$hiddenOptions);
 1042                 unset($htmlOptions['unselectValue']);
 1043             }
 1044         }
 1045         // add a hidden field so that if the option is not selected, it still submits a value
 1046         return $hidden . self::tag('select',$htmlOptions,$options);
 1047     }
 1048 
 1049     /**
 1050      * Generates a list box.
 1051      * @param string $name the input name
 1052      * @param mixed $select the selected value(s). This can be either a string for single selection or an array for multiple selections.
 1053      * @param array $data data for generating the list options (value=>display)
 1054      * You may use {@link listData} to generate this data.
 1055      * Please refer to {@link listOptions} on how this data is used to generate the list options.
 1056      * Note, the values and labels will be automatically HTML-encoded by this method.
 1057      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1058      * attributes are also recognized. See {@link clientChange} and {@link tag} for more details.
 1059      * In addition, the following options are also supported specifically for list box:
 1060      * <ul>
 1061      * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
 1062      * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
 1063      * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
 1064      * The 'empty' option can also be an array of value-label pairs.
 1065      * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
 1066      * <li>options: array, specifies additional attributes for each OPTION tag.
 1067      *   The array keys must be the option values, and the array values are the extra
 1068      *   OPTION tag attributes in the name-value pairs. For example,
 1069      * <pre>
 1070      *   array(
 1071      *       'value1'=>array('disabled'=>true,'label'=>'value 1'),
 1072      *       'value2'=>array('label'=>'value 2'),
 1073      *   );
 1074      * </pre>
 1075      * </li>
 1076      * </ul>
 1077      * @return string the generated list box
 1078      * @see clientChange
 1079      * @see inputField
 1080      * @see listData
 1081      */
 1082     public static function listBox($name,$select,$data,$htmlOptions=array())
 1083     {
 1084         if(!isset($htmlOptions['size']))
 1085             $htmlOptions['size']=4;
 1086         if(!empty($htmlOptions['multiple']))
 1087         {
 1088             if(substr($name,-2)!=='[]')
 1089                 $name.='[]';
 1090         }
 1091         return self::dropDownList($name,$select,$data,$htmlOptions);
 1092     }
 1093 
 1094     /**
 1095      * Generates a check box list.
 1096      * A check box list allows multiple selection, like {@link listBox}.
 1097      * As a result, the corresponding POST value is an array.
 1098      * @param string $name name of the check box list. You can use this name to retrieve
 1099      * the selected value(s) once the form is submitted.
 1100      * @param mixed $select selection of the check boxes. This can be either a string
 1101      * for single selection or an array for multiple selections.
 1102      * @param array $data value-label pairs used to generate the check box list.
 1103      * Note, the values will be automatically HTML-encoded, while the labels will not.
 1104      * @param array $htmlOptions additional HTML options. The options will be applied to
 1105      * each checkbox input. The following special options are recognized:
 1106      * <ul>
 1107      * <li>template: string, specifies how each checkbox is rendered. Defaults
 1108      * to "{input} {label}", where "{input}" will be replaced by the generated
 1109      * check box input tag while "{label}" be replaced by the corresponding check box label,
 1110      * {beginLabel} will be replaced by &lt;label&gt; with labelOptions, {labelTitle} will be replaced
 1111      * by the corresponding check box label title and {endLabel} will be replaced by &lt;/label&gt;</li>
 1112      * <li>separator: string, specifies the string that separates the generated check boxes.</li>
 1113      * <li>checkAll: string, specifies the label for the "check all" checkbox.
 1114      * If this option is specified, a 'check all' checkbox will be displayed. Clicking on
 1115      * this checkbox will cause all checkboxes checked or unchecked.</li>
 1116      * <li>checkAllLast: boolean, specifies whether the 'check all' checkbox should be
 1117      * displayed at the end of the checkbox list. If this option is not set (default)
 1118      * or is false, the 'check all' checkbox will be displayed at the beginning of
 1119      * the checkbox list.</li>
 1120      * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
 1121      * for every label tag in the list.</li>
 1122      * <li>container: string, specifies the checkboxes enclosing tag. Defaults to 'span'.
 1123      * If the value is an empty string, no enclosing tag will be generated</li>
 1124      * <li>baseID: string, specifies the base ID prefix to be used for checkboxes in the list.
 1125      * This option is available since version 1.1.13.</li>
 1126      * </ul>
 1127      * @return string the generated check box list
 1128      */
 1129     public static function checkBoxList($name,$select,$data,$htmlOptions=array())
 1130     {
 1131         $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}';
 1132         $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br');
 1133         $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span';
 1134         unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']);
 1135 
 1136         if(substr($name,-2)!=='[]')
 1137             $name.='[]';
 1138 
 1139         if(isset($htmlOptions['checkAll']))
 1140         {
 1141             $checkAllLabel=$htmlOptions['checkAll'];
 1142             $checkAllLast=isset($htmlOptions['checkAllLast']) && $htmlOptions['checkAllLast'];
 1143         }
 1144         unset($htmlOptions['checkAll'],$htmlOptions['checkAllLast']);
 1145 
 1146         $labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array();
 1147         unset($htmlOptions['labelOptions']);
 1148 
 1149         $items=array();
 1150         $baseID=isset($htmlOptions['baseID']) ? $htmlOptions['baseID'] : self::getIdByName($name);
 1151         unset($htmlOptions['baseID']);
 1152         $id=0;
 1153         $checkAll=true;
 1154 
 1155         foreach($data as $value=>$labelTitle)
 1156         {
 1157             $checked=!is_array($select) && !strcmp($value,$select) || is_array($select) && in_array($value,$select);
 1158             $checkAll=$checkAll && $checked;
 1159             $htmlOptions['value']=$value;
 1160             $htmlOptions['id']=$baseID.'_'.$id++;
 1161             $option=self::checkBox($name,$checked,$htmlOptions);
 1162             $beginLabel=self::openTag('label',$labelOptions);
 1163             $label=self::label($labelTitle,$htmlOptions['id'],$labelOptions);
 1164             $endLabel=self::closeTag('label');
 1165             $items[]=strtr($template,array(
 1166                 '{input}'=>$option,
 1167                 '{beginLabel}'=>$beginLabel,
 1168                 '{label}'=>$label,
 1169                 '{labelTitle}'=>$labelTitle,
 1170                 '{endLabel}'=>$endLabel,
 1171             ));
 1172         }
 1173 
 1174         if(isset($checkAllLabel))
 1175         {
 1176             $htmlOptions['value']=1;
 1177             $htmlOptions['id']=$id=$baseID.'_all';
 1178             $option=self::checkBox($id,$checkAll,$htmlOptions);
 1179             $beginLabel=self::openTag('label',$labelOptions);
 1180             $label=self::label($checkAllLabel,$id,$labelOptions);
 1181             $endLabel=self::closeTag('label');
 1182             $item=strtr($template,array(
 1183                 '{input}'=>$option,
 1184                 '{beginLabel}'=>$beginLabel,
 1185                 '{label}'=>$label,
 1186                 '{labelTitle}'=>$checkAllLabel,
 1187                 '{endLabel}'=>$endLabel,
 1188             ));
 1189             if($checkAllLast)
 1190                 $items[]=$item;
 1191             else
 1192                 array_unshift($items,$item);
 1193             $name=strtr($name,array('['=>'\\[',']'=>'\\]'));
 1194             $js=<<<EOD
 1195 jQuery('#$id').click(function() {
 1196     jQuery("input[name='$name']").prop('checked', this.checked);
 1197 });
 1198 jQuery("input[name='$name']").click(function() {
 1199     jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
 1200 });
 1201 jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
 1202 EOD;
 1203             $cs=Yii::app()->getClientScript();
 1204             $cs->registerCoreScript('jquery');
 1205             $cs->registerScript($id,$js);
 1206         }
 1207 
 1208         if(empty($container))
 1209             return implode($separator,$items);
 1210         else
 1211             return self::tag($container,array('id'=>$baseID),implode($separator,$items));
 1212     }
 1213 
 1214     /**
 1215      * Generates a radio button list.
 1216      * A radio button list is like a {@link checkBoxList check box list}, except that
 1217      * it only allows single selection.
 1218      * @param string $name name of the radio button list. You can use this name to retrieve
 1219      * the selected value(s) once the form is submitted.
 1220      * @param string $select selection of the radio buttons.
 1221      * @param array $data value-label pairs used to generate the radio button list.
 1222      * Note, the values will be automatically HTML-encoded, while the labels will not.
 1223      * @param array $htmlOptions additional HTML options. The options will be applied to
 1224      * each radio button input. The following special options are recognized:
 1225      * <ul>
 1226      * <li>template: string, specifies how each radio button is rendered. Defaults
 1227      * to "{input} {label}", where "{input}" will be replaced by the generated
 1228      * radio button input tag while "{label}" will be replaced by the corresponding radio button label,
 1229      * {beginLabel} will be replaced by &lt;label&gt; with labelOptions, {labelTitle} will be replaced
 1230      * by the corresponding radio button label title and {endLabel} will be replaced by &lt;/label&gt;</li>
 1231      * <li>separator: string, specifies the string that separates the generated radio buttons. Defaults to new line (<br/>).</li>
 1232      * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
 1233      * for every label tag in the list.</li>
 1234      * <li>container: string, specifies the radio buttons enclosing tag. Defaults to 'span'.
 1235      * If the value is an empty string, no enclosing tag will be generated</li>
 1236      * <li>baseID: string, specifies the base ID prefix to be used for radio buttons in the list.
 1237      * This option is available since version 1.1.13.</li>
 1238      * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
 1239      * The 'empty' option can also be an array of value-label pairs.
 1240      * Each pair will be used to render a radio button at the beginning. Note, the text label will NOT be HTML-encoded.
 1241      * This option is available since version 1.1.14.</li>
 1242      * </ul>
 1243      * @return string the generated radio button list
 1244      */
 1245     public static function radioButtonList($name,$select,$data,$htmlOptions=array())
 1246     {
 1247         $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}';
 1248         $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br');
 1249         $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span';
 1250         unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']);
 1251 
 1252         $labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array();
 1253         unset($htmlOptions['labelOptions']);
 1254 
 1255         if(isset($htmlOptions['empty']))
 1256         {
 1257             if(!is_array($htmlOptions['empty']))
 1258                 $htmlOptions['empty']=array(''=>$htmlOptions['empty']);
 1259             $data=CMap::mergeArray($htmlOptions['empty'],$data);
 1260             unset($htmlOptions['empty']);
 1261         }
 1262 
 1263         $items=array();
 1264         $baseID=isset($htmlOptions['baseID']) ? $htmlOptions['baseID'] : self::getIdByName($name);
 1265         unset($htmlOptions['baseID']);
 1266         $id=0;
 1267         foreach($data as $value=>$labelTitle)
 1268         {
 1269             $checked=!strcmp($value,$select);
 1270             $htmlOptions['value']=$value;
 1271             $htmlOptions['id']=$baseID.'_'.$id++;
 1272             $option=self::radioButton($name,$checked,$htmlOptions);
 1273             $beginLabel=self::openTag('label',$labelOptions);
 1274             $label=self::label($labelTitle,$htmlOptions['id'],$labelOptions);
 1275             $endLabel=self::closeTag('label');
 1276             $items[]=strtr($template,array(
 1277                 '{input}'=>$option,
 1278                 '{beginLabel}'=>$beginLabel,
 1279                 '{label}'=>$label,
 1280                 '{labelTitle}'=>$labelTitle,
 1281                 '{endLabel}'=>$endLabel,
 1282             ));
 1283         }
 1284         if(empty($container))
 1285             return implode($separator,$items);
 1286         else
 1287             return self::tag($container,array('id'=>$baseID),implode($separator,$items));
 1288     }
 1289 
 1290     /**
 1291      * Generates a link that can initiate AJAX requests.
 1292      * @param string $text the link body (it will NOT be HTML-encoded.)
 1293      * @param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details.
 1294      * @param array $ajaxOptions AJAX options (see {@link ajax})
 1295      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1296      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1297      * @return string the generated link
 1298      * @see normalizeUrl
 1299      * @see ajax
 1300      */
 1301     public static function ajaxLink($text,$url,$ajaxOptions=array(),$htmlOptions=array())
 1302     {
 1303         if(!isset($htmlOptions['href']))
 1304             $htmlOptions['href']='#';
 1305         $ajaxOptions['url']=$url;
 1306         $htmlOptions['ajax']=$ajaxOptions;
 1307         self::clientChange('click',$htmlOptions);
 1308         return self::tag('a',$htmlOptions,$text);
 1309     }
 1310 
 1311     /**
 1312      * Generates a push button that can initiate AJAX requests.
 1313      * @param string $label the button label
 1314      * @param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details.
 1315      * @param array $ajaxOptions AJAX options (see {@link ajax})
 1316      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1317      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1318      * @return string the generated button
 1319      */
 1320     public static function ajaxButton($label,$url,$ajaxOptions=array(),$htmlOptions=array())
 1321     {
 1322         $ajaxOptions['url']=$url;
 1323         $htmlOptions['ajax']=$ajaxOptions;
 1324         return self::button($label,$htmlOptions);
 1325     }
 1326 
 1327     /**
 1328      * Generates a push button that can submit the current form in POST method.
 1329      * @param string $label the button label
 1330      * @param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details.
 1331      * @param array $ajaxOptions AJAX options (see {@link ajax})
 1332      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1333      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1334      * @return string the generated button
 1335      */
 1336     public static function ajaxSubmitButton($label,$url,$ajaxOptions=array(),$htmlOptions=array())
 1337     {
 1338         $ajaxOptions['type']='POST';
 1339         $htmlOptions['type']='submit';
 1340         return self::ajaxButton($label,$url,$ajaxOptions,$htmlOptions);
 1341     }
 1342 
 1343     /**
 1344      * Generates the JavaScript that initiates an AJAX request.
 1345      * @param array $options AJAX options. The valid options are used in the form of jQuery.ajax([settings])
 1346      * as specified in the jQuery AJAX documentation.
 1347      * The following special options are added for convenience:
 1348      * <ul>
 1349      * <li>update: string, specifies the selector whose HTML content should be replaced
 1350      *   by the AJAX request result.</li>
 1351      * <li>replace: string, specifies the selector whose target should be replaced
 1352      *   by the AJAX request result.</li>
 1353      * </ul>
 1354      * Note, if you specify the 'success' option, the above options will be ignored.
 1355      * @return string the generated JavaScript
 1356      * @see http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings
 1357      */
 1358     public static function ajax($options)
 1359     {
 1360         Yii::app()->getClientScript()->registerCoreScript('jquery');
 1361         if(!isset($options['url']))
 1362             $options['url']=new CJavaScriptExpression('location.href');
 1363         else
 1364             $options['url']=self::normalizeUrl($options['url']);
 1365         if(!isset($options['cache']))
 1366             $options['cache']=false;
 1367         if(!isset($options['data']) && isset($options['type']))
 1368             $options['data']=new CJavaScriptExpression('jQuery(this).parents("form").serialize()');
 1369         foreach(array('beforeSend','complete','error','success') as $name)
 1370         {
 1371             if(isset($options[$name]) && !($options[$name] instanceof CJavaScriptExpression))
 1372                 $options[$name]=new CJavaScriptExpression($options[$name]);
 1373         }
 1374         if(isset($options['update']))
 1375         {
 1376             if(!isset($options['success']))
 1377                 $options['success']=new CJavaScriptExpression('function(html){jQuery("'.$options['update'].'").html(html)}');
 1378             unset($options['update']);
 1379         }
 1380         if(isset($options['replace']))
 1381         {
 1382             if(!isset($options['success']))
 1383                 $options['success']=new CJavaScriptExpression('function(html){jQuery("'.$options['replace'].'").replaceWith(html)}');
 1384             unset($options['replace']);
 1385         }
 1386         return 'jQuery.ajax('.CJavaScript::encode($options).');';
 1387     }
 1388 
 1389     /**
 1390      * Generates the URL for the published assets.
 1391      * @param string $path the path of the asset to be published
 1392      * @param boolean $hashByName whether the published directory should be named as the hashed basename.
 1393      * If false, the name will be the hashed dirname of the path being published.
 1394      * Defaults to false. Set true if the path being published is shared among
 1395      * different extensions.
 1396      * @return string the asset URL
 1397      */
 1398     public static function asset($path,$hashByName=false)
 1399     {
 1400         return Yii::app()->getAssetManager()->publish($path,$hashByName);
 1401     }
 1402 
 1403     /**
 1404      * Normalizes the input parameter to be a valid URL.
 1405      *
 1406      * If the input parameter is an empty string, the currently requested URL will be returned.
 1407      *
 1408      * If the input parameter is a non-empty string, it is treated as a valid URL and will
 1409      * be returned without any change.
 1410      *
 1411      * If the input parameter is an array, it is treated as a controller route and a list of
 1412      * GET parameters, and the {@link CController::createUrl} method will be invoked to
 1413      * create a URL. In this case, the first array element refers to the controller route,
 1414      * and the rest key-value pairs refer to the additional GET parameters for the URL.
 1415      * For example, <code>array('post/list', 'page'=>3)</code> may be used to generate the URL
 1416      * <code>/index.php?r=post/list&page=3</code>.
 1417      *
 1418      * @param mixed $url the parameter to be used to generate a valid URL
 1419      * @return string the normalized URL
 1420      */
 1421     public static function normalizeUrl($url)
 1422     {
 1423         if(is_array($url))
 1424         {
 1425             if(isset($url[0]))
 1426             {
 1427                 if(($c=Yii::app()->getController())!==null)
 1428                     $url=$c->createUrl($url[0],array_splice($url,1));
 1429                 else
 1430                     $url=Yii::app()->createUrl($url[0],array_splice($url,1));
 1431             }
 1432             else
 1433                 $url='';
 1434         }
 1435         return $url==='' ? Yii::app()->getRequest()->getUrl() : $url;
 1436     }
 1437 
 1438     /**
 1439      * Generates an input HTML tag.
 1440      * This method generates an input HTML tag based on the given input name and value.
 1441      * @param string $type the input type (e.g. 'text', 'radio')
 1442      * @param string $name the input name
 1443      * @param string $value the input value
 1444      * @param array $htmlOptions additional HTML attributes for the HTML tag (see {@link tag}).
 1445      * @return string the generated input tag
 1446      */
 1447     protected static function inputField($type,$name,$value,$htmlOptions)
 1448     {
 1449         $htmlOptions['type']=$type;
 1450         $htmlOptions['value']=$value;
 1451         $htmlOptions['name']=$name;
 1452         if(!isset($htmlOptions['id']))
 1453             $htmlOptions['id']=self::getIdByName($name);
 1454         elseif($htmlOptions['id']===false)
 1455             unset($htmlOptions['id']);
 1456         return self::tag('input',$htmlOptions);
 1457     }
 1458 
 1459     /**
 1460      * Generates a label tag for a model attribute.
 1461      * The label text is the attribute label and the label is associated with
 1462      * the input for the attribute (see {@link CModel::getAttributeLabel}.
 1463      * If the attribute has input error, the label's CSS class will be appended with {@link errorCss}.
 1464      * @param CModel $model the data model
 1465      * @param string $attribute the attribute
 1466      * @param array $htmlOptions additional HTML attributes. The following special options are recognized:
 1467      * <ul>
 1468      * <li>required: if this is set and is true, the label will be styled
 1469      * with CSS class 'required' (customizable with CHtml::$requiredCss),
 1470      * and be decorated with {@link CHtml::beforeRequiredLabel} and
 1471      * {@link CHtml::afterRequiredLabel}.</li>
 1472      * <li>label: this specifies the label to be displayed. If this is not set,
 1473      * {@link CModel::getAttributeLabel} will be called to get the label for display.
 1474      * If the label is specified as false, no label will be rendered.</li>
 1475      * </ul>
 1476      * @return string the generated label tag
 1477      */
 1478     public static function activeLabel($model,$attribute,$htmlOptions=array())
 1479     {
 1480         $inputName=self::resolveName($model,$attribute);
 1481         if(isset($htmlOptions['for']))
 1482         {
 1483             $for=$htmlOptions['for'];
 1484             unset($htmlOptions['for']);
 1485         }
 1486         else
 1487             $for=self::getIdByName($inputName);
 1488         if(isset($htmlOptions['label']))
 1489         {
 1490             if(($label=$htmlOptions['label'])===false)
 1491                 return '';
 1492             unset($htmlOptions['label']);
 1493         }
 1494         else
 1495             $label=$model->getAttributeLabel($attribute);
 1496         if($model->hasErrors($attribute))
 1497             self::addErrorCss($htmlOptions);
 1498         return self::label($label,$for,$htmlOptions);
 1499     }
 1500 
 1501     /**
 1502      * Generates a label tag for a model attribute.
 1503      * This is an enhanced version of {@link activeLabel}. It will render additional
 1504      * CSS class and mark when the attribute is required.
 1505      * In particular, it calls {@link CModel::isAttributeRequired} to determine
 1506      * if the attribute is required.
 1507      * If so, it will add a CSS class {@link CHtml::requiredCss} to the label,
 1508      * and decorate the label with {@link CHtml::beforeRequiredLabel} and
 1509      * {@link CHtml::afterRequiredLabel}.
 1510      * @param CModel $model the data model
 1511      * @param string $attribute the attribute
 1512      * @param array $htmlOptions additional HTML attributes.
 1513      * @return string the generated label tag
 1514      */
 1515     public static function activeLabelEx($model,$attribute,$htmlOptions=array())
 1516     {
 1517         $realAttribute=$attribute;
 1518         self::resolveName($model,$attribute); // strip off square brackets if any
 1519         if (!isset($htmlOptions['required']))
 1520             $htmlOptions['required']=$model->isAttributeRequired($attribute);
 1521         return self::activeLabel($model,$realAttribute,$htmlOptions);
 1522     }
 1523 
 1524     /**
 1525      * Generates a text field input for a model attribute.
 1526      * If the attribute has input error, the input field's CSS class will
 1527      * be appended with {@link errorCss}.
 1528      * @param CModel $model the data model
 1529      * @param string $attribute the attribute
 1530      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1531      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1532      * @return string the generated input field
 1533      * @see clientChange
 1534      * @see activeInputField
 1535      */
 1536     public static function activeTextField($model,$attribute,$htmlOptions=array())
 1537     {
 1538         self::resolveNameID($model,$attribute,$htmlOptions);
 1539         self::clientChange('change',$htmlOptions);
 1540         return self::activeInputField('text',$model,$attribute,$htmlOptions);
 1541     }
 1542 
 1543     /**
 1544      * Generates a search field input for a model attribute.
 1545      * If the attribute has input error, the input field's CSS class will
 1546      * be appended with {@link errorCss}.
 1547      * @param CModel $model the data model
 1548      * @param string $attribute the attribute
 1549      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1550      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1551      * @return string the generated input field
 1552      * @see clientChange
 1553      * @see activeInputField
 1554      * @since 1.1.14
 1555      */
 1556     public static function activeSearchField($model,$attribute,$htmlOptions=array())
 1557     {
 1558         self::resolveNameID($model,$attribute,$htmlOptions);
 1559         self::clientChange('change',$htmlOptions);
 1560         return self::activeInputField('search',$model,$attribute,$htmlOptions);
 1561     }
 1562 
 1563     /**
 1564      * Generates a url field input for a model attribute.
 1565      * If the attribute has input error, the input field's CSS class will
 1566      * be appended with {@link errorCss}.
 1567      * @param CModel $model the data model
 1568      * @param string $attribute the attribute
 1569      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1570      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1571      * @return string the generated input field
 1572      * @see clientChange
 1573      * @see activeInputField
 1574      * @since 1.1.11
 1575      */
 1576     public static function activeUrlField($model,$attribute,$htmlOptions=array())
 1577     {
 1578         self::resolveNameID($model,$attribute,$htmlOptions);
 1579         self::clientChange('change',$htmlOptions);
 1580         return self::activeInputField('url',$model,$attribute,$htmlOptions);
 1581     }
 1582 
 1583     /**
 1584      * Generates an email field input for a model attribute.
 1585      * If the attribute has input error, the input field's CSS class will
 1586      * be appended with {@link errorCss}.
 1587      * @param CModel $model the data model
 1588      * @param string $attribute the attribute
 1589      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1590      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1591      * @return string the generated input field
 1592      * @see clientChange
 1593      * @see activeInputField
 1594      * @since 1.1.11
 1595      */
 1596     public static function activeEmailField($model,$attribute,$htmlOptions=array())
 1597     {
 1598         self::resolveNameID($model,$attribute,$htmlOptions);
 1599         self::clientChange('change',$htmlOptions);
 1600         return self::activeInputField('email',$model,$attribute,$htmlOptions);
 1601     }
 1602 
 1603     /**
 1604      * Generates a number field input for a model attribute.
 1605      * If the attribute has input error, the input field's CSS class will
 1606      * be appended with {@link errorCss}.
 1607      * @param CModel $model the data model
 1608      * @param string $attribute the attribute
 1609      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1610      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1611      * @return string the generated input field
 1612      * @see clientChange
 1613      * @see activeInputField
 1614      * @since 1.1.11
 1615      */
 1616     public static function activeNumberField($model,$attribute,$htmlOptions=array())
 1617     {
 1618         self::resolveNameID($model,$attribute,$htmlOptions);
 1619         self::clientChange('change',$htmlOptions);
 1620         return self::activeInputField('number',$model,$attribute,$htmlOptions);
 1621     }
 1622 
 1623     /**
 1624      * Generates a range field input for a model attribute.
 1625      * If the attribute has input error, the input field's CSS class will
 1626      * be appended with {@link errorCss}.
 1627      * @param CModel $model the data model
 1628      * @param string $attribute the attribute
 1629      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1630      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1631      * @return string the generated input field
 1632      * @see clientChange
 1633      * @see activeInputField
 1634      * @since 1.1.11
 1635      */
 1636     public static function activeRangeField($model,$attribute,$htmlOptions=array())
 1637     {
 1638         self::resolveNameID($model,$attribute,$htmlOptions);
 1639         self::clientChange('change',$htmlOptions);
 1640         return self::activeInputField('range',$model,$attribute,$htmlOptions);
 1641     }
 1642 
 1643     /**
 1644      * Generates a date field input for a model attribute.
 1645      * If the attribute has input error, the input field's CSS class will
 1646      * be appended with {@link errorCss}.
 1647      * @param CModel $model the data model
 1648      * @param string $attribute the attribute
 1649      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1650      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1651      * @return string the generated input field
 1652      * @see clientChange
 1653      * @see activeInputField
 1654      * @since 1.1.11
 1655      */
 1656     public static function activeDateField($model,$attribute,$htmlOptions=array())
 1657     {
 1658         self::resolveNameID($model,$attribute,$htmlOptions);
 1659         self::clientChange('change',$htmlOptions);
 1660         return self::activeInputField('date',$model,$attribute,$htmlOptions);
 1661     }
 1662 
 1663     /**
 1664      * Generates a time field input for a model attribute.
 1665      * If the attribute has input error, the input field's CSS class will
 1666      * be appended with {@link errorCss}.
 1667      * @param CModel $model the data model
 1668      * @param string $attribute the attribute
 1669      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1670      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1671      * @return string the generated input field
 1672      * @see clientChange
 1673      * @see activeInputField
 1674      * @since 1.1.14
 1675      */
 1676     public static function activeTimeField($model,$attribute,$htmlOptions=array())
 1677     {
 1678         self::resolveNameID($model,$attribute,$htmlOptions);
 1679         self::clientChange('change',$htmlOptions);
 1680         return self::activeInputField('time',$model,$attribute,$htmlOptions);
 1681     }
 1682 
 1683     /**
 1684      * Generates a datetime field input for a model attribute.
 1685      * If the attribute has input error, the input field's CSS class will
 1686      * be appended with {@link errorCss}.
 1687      * @param CModel $model the data model
 1688      * @param string $attribute the attribute
 1689      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1690      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1691      * @return string the generated input field
 1692      * @see clientChange
 1693      * @see activeInputField
 1694      * @since 1.1.16
 1695      */
 1696     public static function activeDateTimeField($model,$attribute,$htmlOptions=array())
 1697     {
 1698         self::resolveNameID($model,$attribute,$htmlOptions);
 1699         self::clientChange('change',$htmlOptions);
 1700         return self::activeInputField('datetime',$model,$attribute,$htmlOptions);
 1701     }
 1702 
 1703     /**
 1704      * Generates a datetime-local field input for a model attribute.
 1705      * If the attribute has input error, the input field's CSS class will
 1706      * be appended with {@link errorCss}.
 1707      * @param CModel $model the data model
 1708      * @param string $attribute the attribute
 1709      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1710      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1711      * @return string the generated input field
 1712      * @see clientChange
 1713      * @see activeInputField
 1714      * @since 1.1.16
 1715      */
 1716     public static function activeDateTimeLocalField($model,$attribute,$htmlOptions=array())
 1717     {
 1718         self::resolveNameID($model,$attribute,$htmlOptions);
 1719         self::clientChange('change',$htmlOptions);
 1720         return self::activeInputField('datetime-local',$model,$attribute,$htmlOptions);
 1721     }
 1722 
 1723     /**
 1724      * Generates a week field input for a model attribute.
 1725      * If the attribute has input error, the input field's CSS class will
 1726      * be appended with {@link errorCss}.
 1727      * @param CModel $model the data model
 1728      * @param string $attribute the attribute
 1729      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1730      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1731      * @return string the generated input field
 1732      * @see clientChange
 1733      * @see activeInputField
 1734      * @since 1.1.16
 1735      */
 1736     public static function activeWeekField($model,$attribute,$htmlOptions=array())
 1737     {
 1738         self::resolveNameID($model,$attribute,$htmlOptions);
 1739         self::clientChange('change',$htmlOptions);
 1740         return self::activeInputField('week',$model,$attribute,$htmlOptions);
 1741     }
 1742 
 1743     /**
 1744      * Generates a color picker field input for a model attribute.
 1745      * If the attribute has input error, the input field's CSS class will
 1746      * be appended with {@link errorCss}.
 1747      * @param CModel $model the data model
 1748      * @param string $attribute the attribute
 1749      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1750      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1751      * @return string the generated input field
 1752      * @see clientChange
 1753      * @see activeInputField
 1754      * @since 1.1.16
 1755      */
 1756     public static function activeColorField($model,$attribute,$htmlOptions=array())
 1757     {
 1758         self::resolveNameID($model,$attribute,$htmlOptions);
 1759         self::clientChange('change',$htmlOptions);
 1760         return self::activeInputField('color',$model,$attribute,$htmlOptions);
 1761     }
 1762 
 1763     /**
 1764      * Generates a telephone field input for a model attribute.
 1765      * If the attribute has input error, the input field's CSS class will
 1766      * be appended with {@link errorCss}.
 1767      * @param CModel $model the data model
 1768      * @param string $attribute the attribute
 1769      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1770      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1771      * @return string the generated input field
 1772      * @see clientChange
 1773      * @see activeInputField
 1774      * @since 1.1.14
 1775      */
 1776     public static function activeTelField($model,$attribute,$htmlOptions=array())
 1777     {
 1778         self::resolveNameID($model,$attribute,$htmlOptions);
 1779         self::clientChange('change',$htmlOptions);
 1780         return self::activeInputField('tel',$model,$attribute,$htmlOptions);
 1781     }
 1782 
 1783 
 1784     /**
 1785      * Generates a hidden input for a model attribute.
 1786      * @param CModel $model the data model
 1787      * @param string $attribute the attribute
 1788      * @param array $htmlOptions additional HTML attributes.
 1789      * @return string the generated input field
 1790      * @see activeInputField
 1791      */
 1792     public static function activeHiddenField($model,$attribute,$htmlOptions=array())
 1793     {
 1794         self::resolveNameID($model,$attribute,$htmlOptions);
 1795         return self::activeInputField('hidden',$model,$attribute,$htmlOptions);
 1796     }
 1797 
 1798     /**
 1799      * Generates a password field input for a model attribute.
 1800      * If the attribute has input error, the input field's CSS class will
 1801      * be appended with {@link errorCss}.
 1802      * @param CModel $model the data model
 1803      * @param string $attribute the attribute
 1804      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1805      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1806      * @return string the generated input field
 1807      * @see clientChange
 1808      * @see activeInputField
 1809      */
 1810     public static function activePasswordField($model,$attribute,$htmlOptions=array())
 1811     {
 1812         self::resolveNameID($model,$attribute,$htmlOptions);
 1813         self::clientChange('change',$htmlOptions);
 1814         return self::activeInputField('password',$model,$attribute,$htmlOptions);
 1815     }
 1816 
 1817     /**
 1818      * Generates a text area input for a model attribute.
 1819      * If the attribute has input error, the input field's CSS class will
 1820      * be appended with {@link errorCss}.
 1821      * @param CModel $model the data model
 1822      * @param string $attribute the attribute
 1823      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1824      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1825      * @return string the generated text area
 1826      * @see clientChange
 1827      */
 1828     public static function activeTextArea($model,$attribute,$htmlOptions=array())
 1829     {
 1830         self::resolveNameID($model,$attribute,$htmlOptions);
 1831         self::clientChange('change',$htmlOptions);
 1832         if($model->hasErrors($attribute))
 1833             self::addErrorCss($htmlOptions);
 1834         if(isset($htmlOptions['value']))
 1835         {
 1836             $text=$htmlOptions['value'];
 1837             unset($htmlOptions['value']);
 1838         }
 1839         else
 1840             $text=self::resolveValue($model,$attribute);
 1841         return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $text : self::encode($text));
 1842     }
 1843 
 1844     /**
 1845      * Generates a file input for a model attribute.
 1846      * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'.
 1847      * After the form is submitted, the uploaded file information can be obtained via $_FILES (see
 1848      * PHP documentation).
 1849      * @param CModel $model the data model
 1850      * @param string $attribute the attribute
 1851      * @param array $htmlOptions additional HTML attributes (see {@link tag}).
 1852      * @return string the generated input field
 1853      * @see activeInputField
 1854      */
 1855     public static function activeFileField($model,$attribute,$htmlOptions=array())
 1856     {
 1857         self::resolveNameID($model,$attribute,$htmlOptions);
 1858         // add a hidden field so that if a model only has a file field, we can
 1859         // still use isset($_POST[$modelClass]) to detect if the input is submitted
 1860         $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
 1861         if(!empty($htmlOptions['disabled']))
 1862             $hiddenOptions['disabled']=$htmlOptions['disabled'];
 1863         return self::hiddenField($htmlOptions['name'],'',$hiddenOptions)
 1864             . self::activeInputField('file',$model,$attribute,$htmlOptions);
 1865     }
 1866 
 1867     /**
 1868      * Generates a radio button for a model attribute.
 1869      * If the attribute has input error, the input field's CSS class will
 1870      * be appended with {@link errorCss}.
 1871      * @param CModel $model the data model
 1872      * @param string $attribute the attribute
 1873      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1874      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1875      * A special option named 'uncheckValue' is available that can be used to specify
 1876      * the value returned when the radio button is not checked. By default, this value is '0'.
 1877      * Internally, a hidden field is rendered so that when the radio button is not checked,
 1878      * we can still obtain the posted uncheck value.
 1879      * If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
 1880      * @return string the generated radio button
 1881      * @see clientChange
 1882      * @see activeInputField
 1883      */
 1884     public static function activeRadioButton($model,$attribute,$htmlOptions=array())
 1885     {
 1886         self::resolveNameID($model,$attribute,$htmlOptions);
 1887         if(!isset($htmlOptions['value']))
 1888             $htmlOptions['value']=1;
 1889         if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value'])
 1890             $htmlOptions['checked']='checked';
 1891         self::clientChange('click',$htmlOptions);
 1892 
 1893         if(array_key_exists('uncheckValue',$htmlOptions))
 1894         {
 1895             $uncheck=$htmlOptions['uncheckValue'];
 1896             unset($htmlOptions['uncheckValue']);
 1897         }
 1898         else
 1899             $uncheck='0';
 1900 
 1901         $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
 1902         if(!empty($htmlOptions['disabled']))
 1903             $hiddenOptions['disabled']=$htmlOptions['disabled'];
 1904         $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : '';
 1905 
 1906         // add a hidden field so that if the radio button is not selected, it still submits a value
 1907         return $hidden . self::activeInputField('radio',$model,$attribute,$htmlOptions);
 1908     }
 1909 
 1910     /**
 1911      * Generates a check box for a model attribute.
 1912      * The attribute is assumed to take either true or false value.
 1913      * If the attribute has input error, the input field's CSS class will
 1914      * be appended with {@link errorCss}.
 1915      * @param CModel $model the data model
 1916      * @param string $attribute the attribute
 1917      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1918      * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
 1919      * A special option named 'uncheckValue' is available that can be used to specify
 1920      * the value returned when the checkbox is not checked. By default, this value is '0'.
 1921      * Internally, a hidden field is rendered so that when the checkbox is not checked,
 1922      * we can still obtain the posted uncheck value.
 1923      * If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
 1924      * @return string the generated check box
 1925      * @see clientChange
 1926      * @see activeInputField
 1927      */
 1928     public static function activeCheckBox($model,$attribute,$htmlOptions=array())
 1929     {
 1930         self::resolveNameID($model,$attribute,$htmlOptions);
 1931         if(!isset($htmlOptions['value']))
 1932             $htmlOptions['value']=1;
 1933         if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value'])
 1934             $htmlOptions['checked']='checked';
 1935         self::clientChange('click',$htmlOptions);
 1936 
 1937         if(array_key_exists('uncheckValue',$htmlOptions))
 1938         {
 1939             $uncheck=$htmlOptions['uncheckValue'];
 1940             unset($htmlOptions['uncheckValue']);
 1941         }
 1942         else
 1943             $uncheck='0';
 1944 
 1945         $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
 1946         if(!empty($htmlOptions['disabled']))
 1947             $hiddenOptions['disabled']=$htmlOptions['disabled'];
 1948         $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : '';
 1949 
 1950         return $hidden . self::activeInputField('checkbox',$model,$attribute,$htmlOptions);
 1951     }
 1952 
 1953     /**
 1954      * Generates a drop down list for a model attribute.
 1955      * If the attribute has input error, the input field's CSS class will
 1956      * be appended with {@link errorCss}.
 1957      * @param CModel $model the data model
 1958      * @param string $attribute the attribute
 1959      * @param array $data data for generating the list options (value=>display)
 1960      * You may use {@link listData} to generate this data.
 1961      * Please refer to {@link listOptions} on how this data is used to generate the list options.
 1962      * Note, the values and labels will be automatically HTML-encoded by this method.
 1963      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 1964      * attributes are recognized. See {@link clientChange} and {@link tag} for more details.
 1965      * In addition, the following options are also supported:
 1966      * <ul>
 1967      * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
 1968      * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty.  Note, the prompt text will NOT be HTML-encoded.</li>
 1969      * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
 1970      * The 'empty' option can also be an array of value-label pairs.
 1971      * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
 1972      * <li>options: array, specifies additional attributes for each OPTION tag.
 1973      *   The array keys must be the option values, and the array values are the extra
 1974      *   OPTION tag attributes in the name-value pairs. For example,
 1975      * <pre>
 1976      *   array(
 1977      *       'value1'=>array('disabled'=>true,'label'=>'value 1'),
 1978      *       'value2'=>array('label'=>'value 2'),
 1979      *   );
 1980      * </pre>
 1981      * </li>
 1982      * </ul>
 1983      * Since 1.1.13, a special option named 'unselectValue' is available. It can be used to set the value
 1984      * that will be returned when no option is selected in multiple mode. When set, a hidden field is
 1985      * rendered so that if no option is selected in multiple mode, we can still obtain the posted
 1986      * unselect value. If 'unselectValue' is not set or set to NULL, the hidden field will not be rendered.
 1987      * @return string the generated drop down list
 1988      * @see clientChange
 1989      * @see listData
 1990      */
 1991     public static function activeDropDownList($model,$attribute,$data,$htmlOptions=array())
 1992     {
 1993         self::resolveNameID($model,$attribute,$htmlOptions);
 1994         $selection=self::resolveValue($model,$attribute);
 1995         $options="\n".self::listOptions($selection,$data,$htmlOptions);
 1996         self::clientChange('change',$htmlOptions);
 1997 
 1998         if($model->hasErrors($attribute))
 1999             self::addErrorCss($htmlOptions);
 2000 
 2001         $hidden='';
 2002         if(!empty($htmlOptions['multiple']))
 2003         {
 2004             if(substr($htmlOptions['name'],-2)!=='[]')
 2005                 $htmlOptions['name'].='[]';
 2006 
 2007             if(isset($htmlOptions['unselectValue']))
 2008             {
 2009                 $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
 2010                 if(!empty($htmlOptions['disabled']))
 2011                     $hiddenOptions['disabled']=$htmlOptions['disabled'];
 2012                 $hidden=self::hiddenField(substr($htmlOptions['name'],0,-2),$htmlOptions['unselectValue'],$hiddenOptions);
 2013                 unset($htmlOptions['unselectValue']);
 2014             }
 2015         }
 2016         return $hidden . self::tag('select',$htmlOptions,$options);
 2017     }
 2018 
 2019     /**
 2020      * Generates a list box for a model attribute.
 2021      * The model attribute value is used as the selection.
 2022      * If the attribute has input error, the input field's CSS class will
 2023      * be appended with {@link errorCss}.
 2024      * @param CModel $model the data model
 2025      * @param string $attribute the attribute
 2026      * @param array $data data for generating the list options (value=>display)
 2027      * You may use {@link listData} to generate this data.
 2028      * Please refer to {@link listOptions} on how this data is used to generate the list options.
 2029      * Note, the values and labels will be automatically HTML-encoded by this method.
 2030      * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
 2031      * attributes are recognized. See {@link clientChange} and {@link tag} for more details.
 2032      * In addition, the following options are also supported:
 2033      * <ul>
 2034      * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
 2035      * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
 2036      * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
 2037      * The 'empty' option can also be an array of value-label pairs.
 2038      * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
 2039      * <li>options: array, specifies additional attributes for each OPTION tag.
 2040      *   The array keys must be the option values, and the array values are the extra
 2041      *   OPTION tag attributes in the name-value pairs. For example,
 2042      * <pre>
 2043      *   array(
 2044      *       'value1'=>array('disabled'=>true,'label'=>'value 1'),
 2045      *       'value2'=>array('label'=>'value 2'),
 2046      *   );
 2047      * </pre>
 2048      * </li>
 2049      * </ul>
 2050      * @return string the generated list box
 2051      * @see clientChange
 2052      * @see listData
 2053      */
 2054     public static function activeListBox($model,$attribute,$data,$htmlOptions=array())
 2055     {
 2056         if(!isset($htmlOptions['size']))
 2057             $htmlOptions['size']=4;
 2058         return self::activeDropDownList($model,$attribute,$data,$htmlOptions);
 2059     }
 2060 
 2061     /**
 2062      * Generates a check box list for a model attribute.
 2063      * The model attribute value is used as the selection.
 2064      * If the attribute has input error, the input field's CSS class will
 2065      * be appended with {@link errorCss}.
 2066      * Note that a check box list allows multiple selection, like {@link listBox}.
 2067      * As a result, the corresponding POST value is an array. In case no selection
 2068      * is made, the corresponding POST value is an empty string.
 2069      * @param CModel $model the data model
 2070      * @param string $attribute the attribute
 2071      * @param array $data value-label pairs used to generate the check box list.
 2072      * Note, the values will be automatically HTML-encoded, while the labels will not.
 2073      * @param array $htmlOptions additional HTML options. The options will be applied to
 2074      * each checkbox input. The following special options are recognized:
 2075      * <ul>
 2076      * <li>template: string, specifies how each checkbox is rendered. Defaults
 2077      * to "{input} {label}", where "{input}" will be replaced by the generated
 2078      * check box input tag while "{label}" will be replaced by the corresponding check box label.</li>
 2079      * <li>separator: string, specifies the string that separates the generated check boxes.</li>
 2080      * <li>checkAll: string, specifies the label for the "check all" checkbox.
 2081      * If this option is specified, a 'check all' checkbox will be displayed. Clicking on
 2082      * this checkbox will cause all checkboxes checked or unchecked.</li>
 2083      * <li>checkAllLast: boolean, specifies whether the 'check all' checkbox should be
 2084      * displayed at the end of the checkbox list. If this option is not set (default)
 2085      * or is false, the 'check all' checkbox will be displayed at the beginning of
 2086      * the checkbox list.</li>
 2087      * <li>encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true.</li>
 2088      * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
 2089      * for every label tag in the list.</li>
 2090      * <li>container: string, specifies the checkboxes enclosing tag. Defaults to 'span'.
 2091      * If the value is an empty string, no enclosing tag will be generated</li>
 2092      * <li>baseID: string, specifies the base ID prefix to be used for checkboxes in the list.
 2093      * This option is available since version 1.1.13.</li>
 2094      * </ul>
 2095      * Since 1.1.7, a special option named 'uncheckValue' is available. It can be used to set the value
 2096      * that will be returned when the checkbox is not checked. By default, this value is ''.
 2097      * Internally, a hidden field is rendered so when the checkbox is not checked, we can still
 2098      * obtain the value. If 'uncheckValue' is set to NULL, there will be no hidden field rendered.
 2099      * @return string the generated check box list
 2100      * @see checkBoxList
 2101      */
 2102     public static function activeCheckBoxList($model,$attribute,$data,$htmlOptions=array())
 2103     {
 2104         self::resolveNameID($model,$attribute,$htmlOptions);
 2105         $selection=self::resolveValue($model,$attribute);
 2106         if($model->hasErrors($attribute))
 2107             self::addErrorCss($htmlOptions);
 2108         $name=$htmlOptions['name'];
 2109         unset($htmlOptions['name']);
 2110 
 2111         if(array_key_exists('uncheckValue',$htmlOptions))
 2112         {
 2113             $uncheck=$htmlOptions['uncheckValue'];
 2114             unset($htmlOptions['uncheckValue']);
 2115         }
 2116         else
 2117             $uncheck='';
 2118 
 2119         $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
 2120         if(!empty($htmlOptions['disabled']))
 2121             $hiddenOptions['disabled']=$htmlOptions['disabled'];
 2122         $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : '';
 2123 
 2124         return $hidden . self::checkBoxList($name,$selection,$data,$htmlOptions);
 2125     }
 2126 
 2127     /**
 2128      * Generates a radio button list for a model attribute.
 2129      * The model attribute value is used as the selection.
 2130      * If the attribute has input error, the input field's CSS class will
 2131      * be appended with {@link errorCss}.
 2132      * @param CModel $model the data model
 2133      * @param string $attribute the attribute
 2134      * @param array $data value-label pairs used to generate the radio button list.
 2135      * Note, the values will be automatically HTML-encoded, while the labels will not.
 2136      * @param array $htmlOptions additional HTML options. The options will be applied to
 2137      * each radio button input. The following special options are recognized:
 2138      * <ul>
 2139      * <li>template: string, specifies how each radio button is rendered. Defaults
 2140      * to "{input} {label}", where "{input}" will be replaced by the generated
 2141      * radio button input tag while "{label}" will be replaced by the corresponding radio button label,
 2142      * {beginLabel} will be replaced by &lt;label&gt; with labelOptions, {labelTitle} will be replaced
 2143      * by the corresponding radio button label title and {endLabel} will be replaced by &lt;/label&gt;</li>
 2144      * <li>separator: string, specifies the string that separates the generated radio buttons. Defaults to new line (<br/>).</li>
 2145      * <li>encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true.</li>
 2146      * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
 2147      * for every label tag in the list.</li>
 2148      * <li>container: string, specifies the radio buttons enclosing tag. Defaults to 'span'.
 2149      * If the value is an empty string, no enclosing tag will be generated</li>
 2150      * <li>baseID: string, specifies the base ID prefix to be used for radio buttons in the list.
 2151      * This option is available since version 1.1.13.</li>
 2152      * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
 2153      * The 'empty' option can also be an array of value-label pairs.
 2154      * Each pair will be used to render a radio button at the beginning. Note, the text label will NOT be HTML-encoded.
 2155      * This option is available since version 1.1.14.</li>
 2156      * </ul>
 2157      * Since version 1.1.7, a special option named 'uncheckValue' is available that can be used to specify the value
 2158      * returned when the radio button is not checked. By default, this value is ''. Internally, a hidden field is
 2159      * rendered so that when the radio button is not checked, we can still obtain the posted uncheck value.
 2160      * If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
 2161      * @return string the generated radio button list
 2162      * @see radioButtonList
 2163      */
 2164     public static function activeRadioButtonList($model,$attribute,$data,$htmlOptions=array())
 2165     {
 2166         self::resolveNameID($model,$attribute,$htmlOptions);
 2167         $selection=self::resolveValue($model,$attribute);
 2168         if($model->hasErrors($attribute))
 2169             self::addErrorCss($htmlOptions);
 2170         $name=$htmlOptions['name'];
 2171         unset($htmlOptions['name']);
 2172 
 2173         if(array_key_exists('uncheckValue',$htmlOptions))
 2174         {
 2175             $uncheck=$htmlOptions['uncheckValue'];
 2176             unset($htmlOptions['uncheckValue']);
 2177         }
 2178         else
 2179             $uncheck='';
 2180 
 2181         $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
 2182         if(!empty($htmlOptions['disabled']))
 2183             $hiddenOptions['disabled']=$htmlOptions['disabled'];
 2184         $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : '';
 2185 
 2186         return $hidden . self::radioButtonList($name,$selection,$data,$htmlOptions);
 2187     }
 2188 
 2189     /**
 2190      * Displays a summary of validation errors for one or several models.
 2191      * @param mixed $model the models whose input errors are to be displayed. This can be either
 2192      * a single model or an array of models.
 2193      * @param string $header a piece of HTML code that appears in front of the errors
 2194      * @param string $footer a piece of HTML code that appears at the end of the errors
 2195      * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
 2196      * A special option named 'firstError' is recognized, which when set true, will
 2197      * make the error summary to show only the first error message of each attribute.
 2198      * If this is not set or is false, all error messages will be displayed.
 2199      * This option has been available since version 1.1.3.
 2200      * @return string the error summary. Empty if no errors are found.
 2201      * @see CModel::getErrors
 2202      * @see errorSummaryCss
 2203      */
 2204     public static function errorSummary($model,$header=null,$footer=null,$htmlOptions=array())
 2205     {
 2206         $content='';
 2207         if(!is_array($model))
 2208             $model=array($model);
 2209         if(isset($htmlOptions['firstError']))
 2210         {
 2211             $firstError=$htmlOptions['firstError'];
 2212             unset($htmlOptions['firstError']);
 2213         }
 2214         else
 2215             $firstError=false;
 2216         foreach($model as $m)
 2217         {
 2218             foreach($m->getErrors() as $errors)
 2219             {
 2220                 foreach($errors as $error)
 2221                 {
 2222                     if($error!='')
 2223                         $content.= '<li>'.self::encode($error)."</li>\n";
 2224                     if($firstError)
 2225                         break;
 2226                 }
 2227             }
 2228         }
 2229         if($content!=='')
 2230         {
 2231             if($header===null)
 2232                 $header='<p>'.Yii::t('yii','Please fix the following input errors:').'</p>';
 2233             if(!isset($htmlOptions['class']))
 2234                 $htmlOptions['class']=self::$errorSummaryCss;
 2235             return self::tag('div',$htmlOptions,$header."\n<ul>\n$content</ul>".$footer);
 2236         }
 2237         else
 2238             return '';
 2239     }
 2240 
 2241     /**
 2242      * Displays the first validation error for a model attribute.
 2243      * @param CModel $model the data model
 2244      * @param string $attribute the attribute name
 2245      * @param array $htmlOptions additional HTML attributes to be rendered in the container tag.
 2246      * @return string the error display. Empty if no errors are found.
 2247      * @see CModel::getErrors
 2248      * @see errorMessageCss
 2249      * @see $errorContainerTag
 2250      */
 2251     public static function error($model,$attribute,$htmlOptions=array())
 2252     {
 2253         self::resolveName($model,$attribute); // turn [a][b]attr into attr
 2254         $error=self::encode($model->getError($attribute));
 2255         if($error!='')
 2256         {
 2257             if(!isset($htmlOptions['class']))
 2258                 $htmlOptions['class']=self::$errorMessageCss;
 2259             return self::tag(self::$errorContainerTag,$htmlOptions,$error);
 2260         }
 2261         else
 2262             return '';
 2263     }
 2264 
 2265     /**
 2266      * Generates the data suitable for list-based HTML elements.
 2267      * The generated data can be used in {@link dropDownList}, {@link listBox}, {@link checkBoxList},
 2268      * {@link radioButtonList}, and their active-versions (such as {@link activeDropDownList}).
 2269      * Note, this method does not HTML-encode the generated data. You may call {@link encodeArray} to
 2270      * encode it if needed.
 2271      * Please refer to the {@link value} method on how to specify value field, text field and group field.
 2272      * You can also pass anonymous functions as second, third and fourth arguments which calculates
 2273      * text field value (PHP 5.3+ only) since 1.1.13. Your anonymous function should receive one argument,
 2274      * which is the model, the current &lt;option&gt; tag is generated from.
 2275      *
 2276      * <pre>
 2277      * CHtml::listData($posts,'id',function($post) {
 2278      *  return CHtml::encode($post->title);
 2279      * });
 2280      * </pre>
 2281      *
 2282      * @param array $models a list of model objects. This parameter
 2283      * can also be an array of associative arrays (e.g. results of {@link CDbCommand::queryAll}).
 2284      * @param mixed $valueField the attribute name or anonymous function (PHP 5.3+) for list option values
 2285      * @param mixed $textField the attribute name or anonymous function (PHP 5.3+) for list option texts
 2286      * @param mixed $groupField the attribute name or anonymous function (PHP 5.3+) for list option group names. If empty, no group will be generated.
 2287      * @return array the list data that can be used in {@link dropDownList}, {@link listBox}, etc.
 2288      */
 2289     public static function listData($models,$valueField,$textField,$groupField='')
 2290     {
 2291         $listData=array();
 2292         if($groupField==='')
 2293         {
 2294             foreach($models as $model)
 2295             {
 2296                 $value=self::value($model,$valueField);
 2297                 $text=self::value($model,$textField);
 2298                 $listData[$value]=$text;
 2299             }
 2300         }
 2301         else
 2302         {
 2303             foreach($models as $model)
 2304             {
 2305                 $group=self::value($model,$groupField);
 2306                 $value=self::value($model,$valueField);
 2307                 $text=self::value($model,$textField);
 2308                 if($group===null)
 2309                     $listData[$value]=$text;
 2310                 else
 2311                     $listData[$group][$value]=$text;
 2312             }
 2313         }
 2314         return $listData;
 2315     }
 2316 
 2317     /**
 2318      * Evaluates the value of the specified attribute for the given model.
 2319      * The attribute name can be given in a dot syntax. For example, if the attribute
 2320      * is "author.firstName", this method will return the value of "$model->author->firstName".
 2321      * A default value (passed as the last parameter) will be returned if the attribute does
 2322      * not exist or is broken in the middle (e.g. $model->author is null).
 2323      * The model can be either an object or an array. If the latter, the attribute is treated
 2324      * as a key of the array. For the example of "author.firstName", if would mean the array value
 2325      * "$model['author']['firstName']".
 2326      *
 2327      * Anonymous function could also be used for attribute calculation since 1.1.13
 2328      * ($attribute parameter; PHP 5.3+ only) as follows:
 2329      * <pre>
 2330      * $taskClosedSecondsAgo=CHtml::value($closedTask,function($model) {
 2331      *  return time()-$model->closed_at;
 2332      * });
 2333      * </pre>
 2334      * Your anonymous function should receive one argument, which is the model, the current
 2335      * value is calculated from. This feature could be used together with the {@link listData}.
 2336      * Please refer to its documentation for more details.
 2337      *
 2338      * @param mixed $model the model. This can be either an object or an array.
 2339      * @param mixed $attribute the attribute name (use dot to concatenate multiple attributes)
 2340      * or anonymous function (PHP 5.3+). Remember that functions created by "create_function"
 2341      * are not supported by this method. Also note that numeric value is meaningless when
 2342      * first parameter is object typed.
 2343      * @param mixed $defaultValue the default value to return when the attribute does not exist.
 2344      * @return mixed the attribute value.
 2345      */
 2346     public static function value($model,$attribute,$defaultValue=null)
 2347     {
 2348         if(is_scalar($attribute) || $attribute===null)
 2349             foreach(explode('.',$attribute) as $name)
 2350             {
 2351                 if(is_object($model))
 2352                 {
 2353                     if ((version_compare(PHP_VERSION, '7.2.0', '>=')
 2354                         && is_numeric($name))
 2355                         || !isset($model->$name)
 2356                     )
 2357                     {
 2358                         return $defaultValue;
 2359                     }
 2360                     else
 2361                     {
 2362                         $model=$model->$name;
 2363                     }
 2364                 }
 2365 
 2366                 elseif(is_array($model) && isset($model[$name]))
 2367                     $model=$model[$name];
 2368                 else
 2369                     return $defaultValue;
 2370             }
 2371         else
 2372             return call_user_func($attribute,$model);
 2373 
 2374         return $model;
 2375     }
 2376 
 2377     /**
 2378      * Generates a valid HTML ID based on name.
 2379      * @param string $name name from which to generate HTML ID
 2380      * @return string the ID generated based on name.
 2381      */
 2382     public static function getIdByName($name)
 2383     {
 2384         return str_replace(array('[]','][','[',']',' '),array('','_','_','','_'),$name);
 2385     }
 2386 
 2387     /**
 2388      * Generates input field ID for a model attribute.
 2389      * @param CModel|string $model the data model
 2390      * @param string $attribute the attribute
 2391      * @return string the generated input field ID
 2392      */
 2393     public static function activeId($model,$attribute)
 2394     {
 2395         return self::getIdByName(self::activeName($model,$attribute));
 2396     }
 2397 
 2398     /**
 2399      * Generates HTML name for given model.
 2400      * @see CHtml::setModelNameConverter()
 2401      * @param CModel|string $model the data model or the model class name
 2402      * @return string the generated HTML name value
 2403      * @since 1.1.14
 2404      */
 2405     public static function modelName($model)
 2406     {
 2407         if(is_callable(self::$_modelNameConverter))
 2408             return call_user_func(self::$_modelNameConverter,$model);
 2409 
 2410         $className=is_object($model) ? get_class($model) : (string)$model;
 2411         return trim(str_replace('\\','_',$className),'_');
 2412     }
 2413 
 2414     /**
 2415      * Set generator used in the {@link CHtml::modelName()} method. You can use the `null` value to restore default
 2416      * generator.
 2417      *
 2418      * @param callback|null $converter the new generator, the model or class name will be passed to the this callback
 2419      * and result must be a valid value for HTML name attribute.
 2420      * @throws CException if $converter isn't a valid callback
 2421      * @since 1.1.14
 2422      */
 2423     public static function setModelNameConverter($converter)
 2424     {
 2425         if(is_callable($converter))
 2426             self::$_modelNameConverter=$converter;
 2427         elseif($converter===null)
 2428             self::$_modelNameConverter=null;
 2429         else
 2430             throw new CException(Yii::t('yii','The $converter argument must be a valid callback or null.'));
 2431     }
 2432 
 2433     /**
 2434      * Generates input field name for a model attribute.
 2435      * Unlike {@link resolveName}, this method does NOT modify the attribute name.
 2436      * @param CModel|string $model the data model
 2437      * @param string $attribute the attribute
 2438      * @return string the generated input field name
 2439      */
 2440     public static function activeName($model,$attribute)
 2441     {
 2442         $a=$attribute; // because the attribute name may be changed by resolveName
 2443         return self::resolveName($model,$a);
 2444     }
 2445 
 2446     /**
 2447      * Generates an input HTML tag for a model attribute.
 2448      * This method generates an input HTML tag based on the given data model and attribute.
 2449      * If the attribute has input error, the input field's CSS class will
 2450      * be appended with {@link errorCss}.
 2451      * This enables highlighting the incorrect input.
 2452      * @param string $type the input type (e.g. 'text', 'radio')
 2453      * @param CModel $model the data model
 2454      * @param string $attribute the attribute
 2455      * @param array $htmlOptions additional HTML attributes for the HTML tag
 2456      * @return string the generated input tag
 2457      */
 2458     protected static function activeInputField($type,$model,$attribute,$htmlOptions)
 2459     {
 2460         $htmlOptions['type']=$type;
 2461         if($type==='text'||$type==='password'||$type==='color'||$type==='date'||$type==='datetime'||
 2462             $type==='datetime-local'||$type==='email'||$type==='month'||$type==='number'||$type==='range'||
 2463             $type==='search'||$type==='tel'||$type==='time'||$type==='url'||$type==='week')
 2464         {
 2465             if(!isset($htmlOptions['maxlength']))
 2466             {
 2467                 foreach($model->getValidators($attribute) as $validator)
 2468                 {
 2469                     if($validator instanceof CStringValidator && $validator->max!==null)
 2470                     {
 2471                         $htmlOptions['maxlength']=$validator->max;
 2472                         break;
 2473                     }
 2474                 }
 2475             }
 2476             elseif($htmlOptions['maxlength']===false)
 2477                 unset($htmlOptions['maxlength']);
 2478         }
 2479 
 2480         if($type==='file')
 2481             unset($htmlOptions['value']);
 2482         elseif(!isset($htmlOptions['value']))
 2483             $htmlOptions['value']=self::resolveValue($model,$attribute);
 2484         if($model->hasErrors($attribute))
 2485             self::addErrorCss($htmlOptions);
 2486         return self::tag('input',$htmlOptions);
 2487     }
 2488 
 2489     /**
 2490      * Generates the list options.
 2491      * @param mixed $selection the selected value(s). This can be either a string for single selection or an array for multiple selections.
 2492      * @param array $listData the option data (see {@link listData})
 2493      * @param array $htmlOptions additional HTML attributes. The following two special attributes are recognized:
 2494      * <ul>
 2495      * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
 2496      * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
 2497      * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
 2498      * The 'empty' option can also be an array of value-label pairs.
 2499      * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
 2500      * <li>options: array, specifies additional attributes for each OPTION tag.
 2501      *   The array keys must be the option values, and the array values are the extra
 2502      *   OPTION tag attributes in the name-value pairs. For example,
 2503      * <pre>
 2504      *   array(
 2505      *       'value1'=>array('disabled'=>true,'label'=>'value 1'),
 2506      *       'value2'=>array('label'=>'value 2'),
 2507      *   );
 2508      * </pre>
 2509      * </li>
 2510      * <li>key: string, specifies the name of key attribute of the selection object(s).
 2511      * This is used when the selection is represented in terms of objects. In this case,
 2512      * the property named by the key option of the objects will be treated as the actual selection value.
 2513      * This option defaults to 'primaryKey', meaning using the 'primaryKey' property value of the objects in the selection.
 2514      * This option has been available since version 1.1.3.</li>
 2515      * </ul>
 2516      * @return string the generated list options
 2517      */
 2518     public static function listOptions($selection,$listData,&$htmlOptions)
 2519     {
 2520         $raw=isset($htmlOptions['encode']) && !$htmlOptions['encode'];
 2521         $content='';
 2522         if(isset($htmlOptions['prompt']))
 2523         {
 2524             $content.='<option value="">'.strtr($htmlOptions['prompt'],array('<'=>'&lt;','>'=>'&gt;'))."</option>\n";
 2525             unset($htmlOptions['prompt']);
 2526         }
 2527         if(isset($htmlOptions['empty']))
 2528         {
 2529             if(!is_array($htmlOptions['empty']))
 2530                 $htmlOptions['empty']=array(''=>$htmlOptions['empty']);
 2531             foreach($htmlOptions['empty'] as $value=>$label)
 2532                 $content.='<option value="'.self::encode($value).'">'.strtr($label,array('<'=>'&lt;','>'=>'&gt;'))."</option>\n";
 2533             unset($htmlOptions['empty']);
 2534         }
 2535 
 2536         if(isset($htmlOptions['options']))
 2537         {
 2538             $options=$htmlOptions['options'];
 2539             unset($htmlOptions['options']);
 2540         }
 2541         else
 2542             $options=array();
 2543 
 2544         $key=isset($htmlOptions['key']) ? $htmlOptions['key'] : 'primaryKey';
 2545         if(is_array($selection))
 2546         {
 2547             foreach($selection as $i=>$item)
 2548             {
 2549                 if(is_object($item))
 2550                     $selection[$i]=$item->$key;
 2551             }
 2552         }
 2553         elseif(is_object($selection))
 2554             $selection=$selection->$key;
 2555 
 2556         foreach($listData as $key=>$value)
 2557         {
 2558             if(is_array($value))
 2559             {
 2560                 $content.='<optgroup label="'.($raw?$key : self::encode($key))."\">\n";
 2561                 $dummy=array('options'=>$options);
 2562                 if(isset($htmlOptions['encode']))
 2563                     $dummy['encode']=$htmlOptions['encode'];
 2564                 $content.=self::listOptions($selection,$value,$dummy);
 2565                 $content.='</optgroup>'."\n";
 2566             }
 2567             else
 2568             {
 2569                 $attributes=array('value'=>(string)$key,'encode'=>!$raw);
 2570                 if(!is_array($selection) && !strcmp($key,$selection) || is_array($selection) && in_array($key,$selection))
 2571                     $attributes['selected']='selected';
 2572                 if(isset($options[$key]))
 2573                     $attributes=array_merge($attributes,$options[$key]);
 2574                 $content.=self::tag('option',$attributes,$raw?(string)$value : self::encode((string)$value))."\n";
 2575             }
 2576         }
 2577 
 2578         unset($htmlOptions['key']);
 2579 
 2580         return $content;
 2581     }
 2582 
 2583     /**
 2584      * Generates the JavaScript with the specified client changes.
 2585      * @param string $event event name (without 'on')
 2586      * @param array $htmlOptions HTML attributes which may contain the following special attributes
 2587      * specifying the client change behaviors:
 2588      * <ul>
 2589      * <li>submit: string, specifies the URL to submit to. If the current element has a parent form, that form will be
 2590      * submitted, and if 'submit' is non-empty its value will replace the form's URL. If there is no parent form the
 2591      * data listed in 'params' will be submitted instead (via POST method), to the URL in 'submit' or the currently
 2592      * requested URL if 'submit' is empty. Please note that if the 'csrf' setting is true, the CSRF token will be
 2593      * included in the params too.</li>
 2594      * <li>params: array, name-value pairs that should be submitted together with the form. This is only used when 'submit' option is specified.</li>
 2595      * <li>csrf: boolean, whether a CSRF token should be automatically included in 'params' when {@link CHttpRequest::enableCsrfValidation} is true. Defaults to false.
 2596      * You may want to set this to be true if there is no enclosing form around this element.
 2597      * This option is meaningful only when 'submit' option is set.</li>
 2598      * <li>return: boolean, the return value of the javascript. Defaults to false, meaning that the execution of
 2599      * javascript would not cause the default behavior of the event.</li>
 2600      * <li>confirm: string, specifies the message that should show in a pop-up confirmation dialog.</li>
 2601      * <li>ajax: array, specifies the AJAX options (see {@link ajax}).</li>
 2602      * <li>live: boolean, whether the event handler should be delegated or directly bound.
 2603      * If not set, {@link liveEvents} will be used. This option has been available since version 1.1.11.</li>
 2604      * </ul>
 2605      * This parameter has been available since version 1.1.1.
 2606      */
 2607     protected static function clientChange($event,&$htmlOptions)
 2608     {
 2609         if(!isset($htmlOptions['submit']) && !isset($htmlOptions['confirm']) && !isset($htmlOptions['ajax']))
 2610             return;
 2611 
 2612         if(isset($htmlOptions['live']))
 2613         {
 2614             $live=$htmlOptions['live'];
 2615             unset($htmlOptions['live']);
 2616         }
 2617         else
 2618             $live = self::$liveEvents;
 2619 
 2620         if(isset($htmlOptions['return']) && $htmlOptions['return'])
 2621             $return='return true';
 2622         else
 2623             $return='return false';
 2624 
 2625         if(isset($htmlOptions['on'.$event]))
 2626         {
 2627             $handler=trim($htmlOptions['on'.$event],';').';';
 2628             unset($htmlOptions['on'.$event]);
 2629         }
 2630         else
 2631             $handler='';
 2632 
 2633         if(isset($htmlOptions['id']))
 2634             $id=$htmlOptions['id'];
 2635         else
 2636             $id=$htmlOptions['id']=isset($htmlOptions['name'])?$htmlOptions['name']:self::ID_PREFIX.self::$count++;
 2637 
 2638         $cs=Yii::app()->getClientScript();
 2639         $cs->registerCoreScript('jquery');
 2640 
 2641         if(isset($htmlOptions['submit']))
 2642         {
 2643             $cs->registerCoreScript('yii');
 2644             $request=Yii::app()->getRequest();
 2645             if($request->enableCsrfValidation && isset($htmlOptions['csrf']) && $htmlOptions['csrf'])
 2646                 $htmlOptions['params'][$request->csrfTokenName]=$request->getCsrfToken();
 2647             if(isset($htmlOptions['params']))
 2648                 $params=CJavaScript::encode($htmlOptions['params']);
 2649             else
 2650                 $params='{}';
 2651             if($htmlOptions['submit']!=='')
 2652                 $url=CJavaScript::quote(self::normalizeUrl($htmlOptions['submit']));
 2653             else
 2654                 $url='';
 2655             $handler.="jQuery.yii.submitForm(this,'$url',$params);{$return};";
 2656         }
 2657 
 2658         if(isset($htmlOptions['ajax']))
 2659             $handler.=self::ajax($htmlOptions['ajax'])."{$return};";
 2660 
 2661         if(isset($htmlOptions['confirm']))
 2662         {
 2663             $confirm='confirm(\''.CJavaScript::quote($htmlOptions['confirm']).'\')';
 2664             if($handler!=='')
 2665                 $handler="if($confirm) {".$handler."} else return false;";
 2666             else
 2667                 $handler="return $confirm;";
 2668         }
 2669 
 2670         if($live)
 2671             $cs->registerScript('Yii.CHtml.#' . $id,"jQuery('body').on('$event','#$id',function(){{$handler}});");
 2672         else
 2673             $cs->registerScript('Yii.CHtml.#' . $id,"jQuery('#$id').on('$event', function(){{$handler}});");
 2674         unset($htmlOptions['params'],$htmlOptions['submit'],$htmlOptions['ajax'],$htmlOptions['confirm'],$htmlOptions['return'],$htmlOptions['csrf']);
 2675     }
 2676 
 2677     /**
 2678      * Generates input name and ID for a model attribute.
 2679      * This method will update the HTML options by setting appropriate 'name' and 'id' attributes.
 2680      * This method may also modify the attribute name if the name
 2681      * contains square brackets (mainly used in tabular input).
 2682      * @param CModel|string $model the data model
 2683      * @param string $attribute the attribute
 2684      * @param array $htmlOptions the HTML options
 2685      */
 2686     public static function resolveNameID($model,&$attribute,&$htmlOptions)
 2687     {
 2688         if(!isset($htmlOptions['name']))
 2689             $htmlOptions['name']=self::resolveName($model,$attribute);
 2690         if(!isset($htmlOptions['id']))
 2691             $htmlOptions['id']=self::getIdByName($htmlOptions['name']);
 2692         elseif($htmlOptions['id']===false)
 2693             unset($htmlOptions['id']);
 2694     }
 2695 
 2696     /**
 2697      * Generates input name for a model attribute.
 2698      * Note, the attribute name may be modified after calling this method if the name
 2699      * contains square brackets (mainly used in tabular input) before the real attribute name.
 2700      * @param CModel|string $model the data model
 2701      * @param string $attribute the attribute
 2702      * @return string the input name
 2703      */
 2704     public static function resolveName($model,&$attribute)
 2705     {
 2706         $modelName=self::modelName($model);
 2707 
 2708         if(($pos=strpos($attribute,'['))!==false)
 2709         {
 2710             if($pos!==0)  // e.g. name[a][b]
 2711                 return $modelName.'['.substr($attribute,0,$pos).']'.substr($attribute,$pos);
 2712             if(($pos=strrpos($attribute,']'))!==false && $pos!==strlen($attribute)-1)  // e.g. [a][b]name
 2713             {
 2714                 $sub=substr($attribute,0,$pos+1);
 2715                 $attribute=substr($attribute,$pos+1);
 2716                 return $modelName.$sub.'['.$attribute.']';
 2717             }
 2718             if(preg_match('/\](\w+\[.*)$/',$attribute,$matches))
 2719             {
 2720                 $name=$modelName.'['.str_replace(']','][',trim(strtr($attribute,array(']['=>']','['=>']')),']')).']';
 2721                 $attribute=$matches[1];
 2722                 return $name;
 2723             }
 2724         }
 2725         return $modelName.'['.$attribute.']';
 2726     }
 2727 
 2728     /**
 2729      * Evaluates the attribute value of the model.
 2730      * This method can recognize the attribute name written in array format.
 2731      * For example, if the attribute name is 'name[a][b]', the value "$model->name['a']['b']" will be returned.
 2732      * @param CModel $model the data model
 2733      * @param string $attribute the attribute name
 2734      * @return mixed the attribute value
 2735      * @since 1.1.3
 2736      */
 2737     public static function resolveValue($model,$attribute)
 2738     {
 2739         if(($pos=strpos($attribute,'['))!==false)
 2740         {
 2741             if($pos===0) // [a]name[b][c], should ignore [a]
 2742             {
 2743                 if(preg_match('/\](\w+(\[.+)?)/',$attribute,$matches))
 2744                     $attribute=$matches[1]; // we get: name[b][c]
 2745                 if(($pos=strpos($attribute,'['))===false)
 2746                     return $model->$attribute;
 2747             }
 2748             $name=substr($attribute,0,$pos);
 2749             $value=$model->$name;
 2750             foreach(explode('][',rtrim(substr($attribute,$pos+1),']')) as $id)
 2751             {
 2752                 if((is_array($value) || $value instanceof ArrayAccess) && isset($value[$id]))
 2753                     $value=$value[$id];
 2754                 else
 2755                     return null;
 2756             }
 2757             return $value;
 2758         }
 2759         else
 2760             return $model->$attribute;
 2761     }
 2762 
 2763     /**
 2764      * Appends {@link errorCss} to the 'class' attribute.
 2765      * @param array $htmlOptions HTML options to be modified
 2766      */
 2767     protected static function addErrorCss(&$htmlOptions)
 2768     {
 2769         if(empty(self::$errorCss))
 2770             return;
 2771 
 2772         if(isset($htmlOptions['class']))
 2773             $htmlOptions['class'].=' '.self::$errorCss;
 2774         else
 2775             $htmlOptions['class']=self::$errorCss;
 2776     }
 2777 
 2778     /**
 2779      * Renders the HTML tag attributes.
 2780      * Since version 1.1.5, attributes whose value is null will not be rendered.
 2781      * Special attributes, such as 'checked', 'disabled', 'readonly', will be rendered
 2782      * properly based on their corresponding boolean value.
 2783      * @param array $htmlOptions attributes to be rendered
 2784      * @return string the rendering result
 2785      */
 2786     public static function renderAttributes($htmlOptions)
 2787     {
 2788         static $specialAttributes=array(
 2789             'autofocus'=>1,
 2790             'autoplay'=>1,
 2791             'async'=>1,
 2792             'checked'=>1,
 2793             'controls'=>1,
 2794             'declare'=>1,
 2795             'default'=>1,
 2796             'defer'=>1,
 2797             'disabled'=>1,
 2798             'formnovalidate'=>1,
 2799             'hidden'=>1,
 2800             'ismap'=>1,
 2801             'itemscope'=>1,
 2802             'loop'=>1,
 2803             'multiple'=>1,
 2804             'muted'=>1,
 2805             'nohref'=>1,
 2806             'noresize'=>1,
 2807             'novalidate'=>1,
 2808             'open'=>1,
 2809             'readonly'=>1,
 2810             'required'=>1,
 2811             'reversed'=>1,
 2812             'scoped'=>1,
 2813             'seamless'=>1,
 2814             'selected'=>1,
 2815             'typemustmatch'=>1,
 2816         );
 2817 
 2818         if($htmlOptions===array())
 2819             return '';
 2820 
 2821         $html='';
 2822         if(isset($htmlOptions['encode']))
 2823         {
 2824             $raw=!$htmlOptions['encode'];
 2825             unset($htmlOptions['encode']);
 2826         }
 2827         else
 2828             $raw=false;
 2829 
 2830         foreach($htmlOptions as $name=>$value)
 2831         {
 2832             if(isset($specialAttributes[$name]))
 2833             {
 2834                 if($value===false && $name==='async') {
 2835                     $html .= ' ' . $name.'="false"';
 2836                 }
 2837                 elseif($value)
 2838                 {
 2839                     $html .= ' ' . $name;
 2840                     if(self::$renderSpecialAttributesValue)
 2841                         $html .= '="' . $name . '"';
 2842                 }
 2843             }
 2844             elseif($value!==null)
 2845                 $html .= ' ' . $name . '="' . ($raw ? $value : self::encode($value)) . '"';
 2846         }
 2847 
 2848         return $html;
 2849     }
 2850 }