"Fossies" - the Fresh Open Source Software Archive

Member "yii-1.1.24.a5ab20/framework/web/helpers/CHtml.php" (7 Jun 2021, 118645 Bytes) of package /linux/www/yii-1.1.24.a5ab20.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.23.445827_vs_1.1.24.a5ab20.

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