Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • Net
  • None
  • PHP
  • system
    • base
    • caching
      • dependencies
    • collections
    • console
    • db
      • ar
      • schema
        • cubrid
        • mssql
        • mysql
        • oci
        • pgsql
        • sqlite
    • i18n
      • gettext
    • logging
    • test
    • utils
    • validators
    • web
      • actions
      • auth
      • filters
      • form
      • helpers
      • renderers
      • services
      • widgets
        • captcha
        • pagers
  • Text
    • Highlighter
  • zii
    • behaviors
    • widgets
      • grid
      • jui

Classes

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