Overview

Packages

  • bootstrap
    • behaviors
    • components
    • form
    • gii
    • helpers
    • widgets

Classes

  • TbArray
  • TbHtml
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * TbHtml class file.
   4:  * @author Antonio Ramirez <ramirez.cobos@gmail.com>
   5:  * @author Christoffer Niska <christoffer.niska@gmail.com>
   6:  * @copyright Copyright &copy; Christoffer Niska 2013-
   7:  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
   8:  * @package bootstrap.helpers
   9:  */
  10: 
  11: Yii::import('bootstrap.helpers.TbArray');
  12: 
  13: /**
  14:  * Bootstrap HTML helper.
  15:  */
  16: class TbHtml extends CHtml // required in order to access the protected methods in CHtml
  17: {
  18:     //
  19:     // TYPOGRAPHY
  20:     // --------------------------------------------------
  21: 
  22:     const TEXT_ALIGN_LEFT = 'left';
  23:     const TEXT_ALIGN_CENTER = 'center';
  24:     const TEXT_ALIGN_RIGHT = 'right';
  25: 
  26:     const TEXT_COLOR_DEFAULT = '';
  27:     const TEXT_COLOR_WARNING = 'warning';
  28:     const TEXT_COLOR_ERROR = 'error';
  29:     const TEXT_COLOR_INFO = 'info';
  30:     const TEXT_COLOR_SUCCESS = 'success';
  31: 
  32:     const HELP_TYPE_INLINE = 'inline';
  33:     const HELP_TYPE_BLOCK = 'block';
  34: 
  35:     //
  36:     // FORM
  37:     // --------------------------------------------------
  38: 
  39:     const FORM_LAYOUT_VERTICAL = 'vertical';
  40:     const FORM_LAYOUT_HORIZONTAL = 'horizontal';
  41:     const FORM_LAYOUT_INLINE = 'inline';
  42:     const FORM_LAYOUT_SEARCH = 'search';
  43: 
  44:     const INPUT_TYPE_TEXT = 'textField';
  45:     const INPUT_TYPE_PASSWORD = 'passwordField';
  46:     const INPUT_TYPE_URL = 'urlField';
  47:     const INPUT_TYPE_EMAIL = 'emailField';
  48:     const INPUT_TYPE_NUMBER = 'numberField';
  49:     const INPUT_TYPE_RANGE = 'rangeField';
  50:     const INPUT_TYPE_DATE = 'dateField';
  51:     const INPUT_TYPE_TEXTAREA = 'textArea';
  52:     const INPUT_TYPE_FILE = 'fileField';
  53:     const INPUT_TYPE_RADIOBUTTON = 'radioButton';
  54:     const INPUT_TYPE_CHECKBOX = 'checkBox';
  55:     const INPUT_TYPE_DROPDOWNLIST = 'dropDownList';
  56:     const INPUT_TYPE_LISTBOX = 'listBox';
  57:     const INPUT_TYPE_CHECKBOXLIST = 'checkBoxList';
  58:     const INPUT_TYPE_INLINECHECKBOXLIST = 'inlineCheckBoxList';
  59:     const INPUT_TYPE_RADIOBUTTONLIST = 'radioButtonList';
  60:     const INPUT_TYPE_INLINERADIOBUTTONLIST = 'inlineRadioButtonList';
  61:     const INPUT_TYPE_UNEDITABLE = 'uneditableField';
  62:     const INPUT_TYPE_SEARCH = 'searchQuery';
  63:     const INPUT_TYPE_CUSTOM = 'widget';
  64: 
  65:     const INPUT_SIZE_MINI = 'mini';
  66:     const INPUT_SIZE_SMALL = 'small';
  67:     const INPUT_SIZE_DEFAULT = '';
  68:     const INPUT_SIZE_MEDIUM = 'medium';
  69:     const INPUT_SIZE_LARGE = 'large';
  70:     const INPUT_SIZE_XLARGE = 'xlarge';
  71:     const INPUT_SIZE_XXLARGE = 'xxlarge';
  72: 
  73:     const INPUT_COLOR_DEFAULT = '';
  74:     const INPUT_COLOR_WARNING = 'warning';
  75:     const INPUT_COLOR_ERROR = 'error';
  76:     const INPUT_COLOR_INFO = 'info';
  77:     const INPUT_COLOR_SUCCESS = 'success';
  78: 
  79:     //
  80:     // BUTTONS
  81:     // --------------------------------------------------
  82: 
  83:     const BUTTON_TYPE_LINK = 'link';
  84:     const BUTTON_TYPE_HTML = 'htmlButton';
  85:     const BUTTON_TYPE_SUBMIT = 'submitButton';
  86:     const BUTTON_TYPE_RESET = 'resetButton';
  87:     const BUTTON_TYPE_IMAGE = 'imageButton';
  88:     const BUTTON_TYPE_LINKBUTTON = 'linkButton';
  89:     const BUTTON_TYPE_AJAXLINK = 'ajaxLink';
  90:     const BUTTON_TYPE_AJAXBUTTON = 'ajaxButton';
  91:     const BUTTON_TYPE_INPUTBUTTON = 'inputButton';
  92:     const BUTTON_TYPE_INPUTSUBMIT = 'inputSubmit';
  93: 
  94:     const BUTTON_COLOR_DEFAULT = '';
  95:     const BUTTON_COLOR_PRIMARY = 'primary';
  96:     const BUTTON_COLOR_INFO = 'info';
  97:     const BUTTON_COLOR_SUCCESS = 'success';
  98:     const BUTTON_COLOR_WARNING = 'warning';
  99:     const BUTTON_COLOR_DANGER = 'danger';
 100:     const BUTTON_COLOR_INVERSE = 'inverse';
 101:     const BUTTON_COLOR_LINK = 'link';
 102: 
 103:     const BUTTON_SIZE_MINI = 'mini';
 104:     const BUTTON_SIZE_SMALL = 'small';
 105:     const BUTTON_SIZE_DEFAULT = '';
 106:     const BUTTON_SIZE_LARGE = 'large';
 107: 
 108:     const BUTTON_TOGGLE_CHECKBOX = 'checkbox';
 109:     const BUTTON_TOGGLE_RADIO = 'radio';
 110: 
 111:     //
 112:     // IMAGES
 113:     // --------------------------------------------------
 114: 
 115:     const IMAGE_TYPE_ROUNDED = 'rounded';
 116:     const IMAGE_TYPE_CIRCLE = 'circle';
 117:     const IMAGE_TYPE_POLAROID = 'polaroid';
 118: 
 119:     //
 120:     // NAV
 121:     // --------------------------------------------------
 122: 
 123:     const NAV_TYPE_NONE = '';
 124:     const NAV_TYPE_TABS = 'tabs';
 125:     const NAV_TYPE_PILLS = 'pills';
 126:     const NAV_TYPE_LIST = 'list';
 127: 
 128:     const TABS_PLACEMENT_ABOVE = '';
 129:     const TABS_PLACEMENT_BELOW = 'below';
 130:     const TABS_PLACEMENT_LEFT = 'left';
 131:     const TABS_PLACEMENT_RIGHT = 'right';
 132: 
 133:     //
 134:     // NAVBAR
 135:     // --------------------------------------------------
 136: 
 137:     const NAVBAR_DISPLAY_NONE = '';
 138:     const NAVBAR_DISPLAY_FIXEDTOP = 'fixed-top';
 139:     const NAVBAR_DISPLAY_FIXEDBOTTOM = 'fixed-bottom';
 140:     const NAVBAR_DISPLAY_STATICTOP = 'static-top';
 141: 
 142:     const NAVBAR_COLOR_INVERSE = 'inverse';
 143: 
 144:     //
 145:     // PAGINATION
 146:     // --------------------------------------------------
 147: 
 148:     const PAGINATION_SIZE_MINI = 'mini';
 149:     const PAGINATION_SIZE_SMALL = 'small';
 150:     const PAGINATION_SIZE_DEFAULT = '';
 151:     const PAGINATION_SIZE_LARGE = 'large';
 152: 
 153:     const PAGINATION_ALIGN_LEFT = 'left';
 154:     const PAGINATION_ALIGN_CENTER = 'centered';
 155:     const PAGINATION_ALIGN_RIGHT = 'right';
 156: 
 157:     //
 158:     // LABELS AND BADGES
 159:     // --------------------------------------------------
 160: 
 161:     const LABEL_COLOR_DEFAULT = '';
 162:     const LABEL_COLOR_SUCCESS = 'success';
 163:     const LABEL_COLOR_WARNING = 'warning';
 164:     const LABEL_COLOR_IMPORTANT = 'important';
 165:     const LABEL_COLOR_INFO = 'info';
 166:     const LABEL_COLOR_INVERSE = 'inverse';
 167: 
 168:     const BADGE_COLOR_DEFAULT = '';
 169:     const BADGE_COLOR_SUCCESS = 'success';
 170:     const BADGE_COLOR_WARNING = 'warning';
 171:     const BADGE_COLOR_IMPORTANT = 'important';
 172:     const BADGE_COLOR_INFO = 'info';
 173:     const BADGE_COLOR_INVERSE = 'inverse';
 174: 
 175:     //
 176:     // TOOLTIPS AND POPOVERS
 177:     // --------------------------------------------------
 178: 
 179:     const TOOLTIP_PLACEMENT_TOP = 'top';
 180:     const TOOLTIP_PLACEMENT_BOTTOM = 'bottom';
 181:     const TOOLTIP_PLACEMENT_LEFT = 'left';
 182:     const TOOLTIP_PLACEMENT_RIGHT = 'right';
 183: 
 184:     const TOOLTIP_TRIGGER_CLICK = 'click';
 185:     const TOOLTIP_TRIGGER_HOVER = 'hover';
 186:     const TOOLTIP_TRIGGER_FOCUS = 'focus';
 187:     const TOOLTIP_TRIGGER_MANUAL = 'manual';
 188: 
 189:     const POPOVER_PLACEMENT_TOP = 'top';
 190:     const POPOVER_PLACEMENT_BOTTOM = 'bottom';
 191:     const POPOVER_PLACEMENT_LEFT = 'left';
 192:     const POPOVER_PLACEMENT_RIGHT = 'right';
 193: 
 194:     const POPOVER_TRIGGER_CLICK = 'click';
 195:     const POPOVER_TRIGGER_HOVER = 'hover';
 196:     const POPOVER_TRIGGER_FOCUS = 'focus';
 197:     const POPOVER_TRIGGER_MANUAL = 'manual';
 198: 
 199:     //
 200:     // ALERT
 201:     // --------------------------------------------------
 202: 
 203:     const ALERT_COLOR_DEFAULT = '';
 204:     const ALERT_COLOR_INFO = 'info';
 205:     const ALERT_COLOR_SUCCESS = 'success';
 206:     const ALERT_COLOR_WARNING = 'warning';
 207:     const ALERT_COLOR_ERROR = 'error';
 208:     const ALERT_COLOR_DANGER = 'danger';
 209: 
 210:     //
 211:     // PROGRESS BARS
 212:     // --------------------------------------------------
 213: 
 214:     const PROGRESS_COLOR_DEFAULT = '';
 215:     const PROGRESS_COLOR_INFO = 'info';
 216:     const PROGRESS_COLOR_SUCCESS = 'success';
 217:     const PROGRESS_COLOR_WARNING = 'warning';
 218:     const PROGRESS_COLOR_DANGER = 'danger';
 219: 
 220:     //
 221:     // MISC
 222:     // --------------------------------------------------
 223: 
 224:     const WELL_SIZE_SMALL = 'small';
 225:     const WELL_SIZE_DEFAULT = '';
 226:     const WELL_SIZE_LARGE = 'large';
 227: 
 228:     const PULL_LEFT = 'left';
 229:     const PULL_RIGHT = 'right';
 230: 
 231:     const CLOSE_DISMISS_ALERT = 'alert';
 232:     const CLOSE_DISMISS_MODAL = 'modal';
 233: 
 234:     //
 235:     // DETAIL VIEW
 236:     // --------------------------------------------------
 237: 
 238:     const DETAIL_TYPE_STRIPED = 'striped';
 239:     const DETAIL_TYPE_BORDERED = 'bordered';
 240:     const DETAIL_TYPE_CONDENSED = 'condensed';
 241:     const DETAIL_TYPE_HOVER = 'hover';
 242: 
 243:     //
 244:     // GRID VIEW
 245:     // --------------------------------------------------
 246: 
 247:     const GRID_TYPE_STRIPED = 'striped';
 248:     const GRID_TYPE_BORDERED = 'bordered';
 249:     const GRID_TYPE_CONDENSED = 'condensed';
 250:     const GRID_TYPE_HOVER = 'hover';
 251: 
 252:     //
 253:     // AFFIX
 254:     // --------------------------------------------------
 255: 
 256:     const AFFIX_POSITION_TOP = 'top';
 257:     const AFFIX_POSITION_BOTTOM = 'bottom';
 258: 
 259:     //
 260:     // ICON
 261:     // --------------------------------------------------
 262: 
 263:     const ICON_COLOR_DEFAULT = '';
 264:     const ICON_COLOR_WHITE = 'white';
 265: 
 266:     const ICON_GLASS = 'icon-glass';
 267:     const ICON_MUSIC = 'icon-music';
 268:     const ICON_SEARCH = 'icon-search';
 269:     const ICON_ENVELOPE = 'icon-envelope';
 270:     const ICON_HEART = 'icon-heart';
 271:     const ICON_STAR = 'icon-star';
 272:     const ICON_STAR_EMPTY = 'icon-star-empty';
 273:     const ICON_USER = 'icon-user';
 274:     const ICON_FILM = 'icon-film';
 275:     const ICON_TH_LARGE = 'icon-th-large';
 276:     const ICON_TH = 'icon-th';
 277:     const ICON_TH_LIST = 'icon-th-list';
 278:     const ICON_OK = 'icon-ok';
 279:     const ICON_REMOVE = 'icon-remove';
 280:     const ICON_ZOOM_IN = 'icon-zoom-in';
 281:     const ICON_ZOOM_OUT = 'icon-zoom-out';
 282:     const ICON_OFF = 'icon-off';
 283:     const ICON_SIGNAL = 'icon-signal';
 284:     const ICON_COG = 'icon-cog';
 285:     const ICON_TRASH = 'icon-trash';
 286:     const ICON_HOME = 'icon-home';
 287:     const ICON_FILE = 'icon-file';
 288:     const ICON_TIME = 'icon-time';
 289:     const ICON_ROAD = 'icon-road';
 290:     const ICON_DOWNLOAD_ALT = 'icon-download-alt';
 291:     const ICON_DOWNLOAD = 'icon-download';
 292:     const ICON_UPLOAD = 'icon-upload';
 293:     const ICON_INBOX = 'icon-inbox';
 294:     const ICON_PLAY_CIRCLE = 'icon-play-circle';
 295:     const ICON_REPEAT = 'icon-repeat';
 296:     const ICON_REFRESH = 'icon-refresh';
 297:     const ICON_LIST_ALT = 'icon-list-alt';
 298:     const ICON_LOCK = 'icon-lock';
 299:     const ICON_FLAG = 'icon-flag';
 300:     const ICON_HEADPHONES = 'icon-headphones';
 301:     const ICON_VOLUME_OFF = 'icon-volume-off';
 302:     const ICON_VOLUME_DOWN = 'icon-volume-down';
 303:     const ICON_VOLUME_UP = 'icon-volume-up';
 304:     const ICON_QRCODE = 'icon-qrcode';
 305:     const ICON_BARCODE = 'icon-barcode';
 306:     const ICON_TAG = 'icon-tag';
 307:     const ICON_TAGS = 'icon-tags';
 308:     const ICON_BOOK = 'icon-book';
 309:     const ICON_BOOKMARK = 'icon-bookmark';
 310:     const ICON_PRINT = 'icon-print';
 311:     const ICON_CAMERA = 'icon-camera';
 312:     const ICON_FONT = 'icon-font';
 313:     const ICON_BOLD = 'icon-bold';
 314:     const ICON_ITALIC = 'icon-italic';
 315:     const ICON_TEXT_HEIGHT = 'icon-text-height';
 316:     const ICON_TEXT_WIDTH = 'icon-text-width';
 317:     const ICON_ALIGN_LEFT = 'icon-align-left';
 318:     const ICON_ALIGN_CENTER = 'icon-align-center';
 319:     const ICON_ALIGN_RIGHT = 'icon-align-right';
 320:     const ICON_ALIGN_JUSTIFY = 'icon-align-justify';
 321:     const ICON_LIST = 'icon-list';
 322:     const ICON_INDENT_LEFT = 'icon-indent-left';
 323:     const ICON_INDENT_RIGHT = 'icon-indent-right';
 324:     const ICON_FACETIME_VIDEO = 'icon-facetime-video';
 325:     const ICON_PICTURE = 'icon-picture';
 326:     const ICON_PENCIL = 'icon-pencil';
 327:     const ICON_MAP_MARKER = 'icon-map-marker';
 328:     const ICON_ADJUST = 'icon-adjust';
 329:     const ICON_TINT = 'icon-tint';
 330:     const ICON_EDIT = 'icon-edit';
 331:     const ICON_SHARE = 'icon-share';
 332:     const ICON_CHECK = 'icon-check';
 333:     const ICON_MOVE = 'icon-move';
 334:     const ICON_STEP_BACKWARD = 'icon-step-backward';
 335:     const ICON_FAST_BACKWARD = 'icon-fast-backward';
 336:     const ICON_BACKWARD = 'icon-backward';
 337:     const ICON_PLAY = 'icon-play';
 338:     const ICON_PAUSE = 'icon-pause';
 339:     const ICON_STOP = 'icon-pause';
 340:     const ICON_FORWARD = 'icon-forward';
 341:     const ICON_FAST_FORWARD = 'icon-fast-forward';
 342:     const ICON_STEP_FORWARD = 'icon-step-forward';
 343:     const ICON_EJECT = 'icon-eject';
 344:     const ICON_CHEVRON_LEFT = 'icon-chevron-left';
 345:     const ICON_CHEVRON_RIGHT = 'icon-chevron-right';
 346:     const ICON_PLUS_SIGN = 'icon-plus-sign';
 347:     const ICON_MINUS_SIGN = 'icon-minus-sign';
 348:     const ICON_REMOVE_SIGN = 'icon-remove-sign';
 349:     const ICON_OK_SIGN = 'icon-ok-sign';
 350:     const ICON_QUESTION_SIGN = 'icon-question-sign';
 351:     const ICON_INFO_SIGN = 'icon-info-sign';
 352:     const ICON_SCREENSHOT = 'icon-screenshot';
 353:     const ICON_REMOVE_CIRCLE = 'icon-remove-circle';
 354:     const ICON_OK_CIRCLE = 'icon-ok-circle';
 355:     const ICON_BAN_CIRCLE = 'icon-ban-circle';
 356:     const ICON_ARROW_LEFT = 'icon-arrow-left';
 357:     const ICON_ARROW_RIGHT = 'icon-arrow-right';
 358:     const ICON_ARROW_UP = 'icon-arrow-up';
 359:     const ICON_ARROW_DOWN = 'icon-arrow-down';
 360:     const ICON_SHARE_ALT = 'icon-share-alt';
 361:     const ICON_RESIZE_FULL = 'icon-resize-full';
 362:     const ICON_RESIZE_SMALL = 'icon-resize-small';
 363:     const ICON_PLUS = 'icon-plus';
 364:     const ICON_MINUS = 'icon-minus';
 365:     const ICON_ASTERISK = 'icon-asterisk';
 366:     const ICON_EXCLAMATION_SIGN = 'icon-exclamation-sign';
 367:     const ICON_GIFT = 'icon-gift';
 368:     const ICON_LEAF = 'icon-leaf';
 369:     const ICON_FIRE = 'icon-fire';
 370:     const ICON_EYE_OPEN = 'icon-eye-open';
 371:     const ICON_EYE_CLOSE = 'icon-eye-close';
 372:     const ICON_WARNING_SIGN = 'icon-warning-sign';
 373:     const ICON_PLANE = 'icon-plane';
 374:     const ICON_CALENDAR = 'icon-calendar';
 375:     const ICON_RANDOM = 'icon-random';
 376:     const ICON_COMMENT = 'icon-comment';
 377:     const ICON_MAGNET = 'icon-magnet';
 378:     const ICON_CHEVRON_UP = 'icon-chevron-up';
 379:     const ICON_CHEVRON_DOWN = 'icon-chevron-down';
 380:     const ICON_RETWEET = 'icon-retweet';
 381:     const ICON_SHOPPING_CART = 'icon-shopping-cart';
 382:     const ICON_FOLDER_CLOSE = 'icon-folder-close';
 383:     const ICON_FOLDER_OPEN = 'icon-folder-open';
 384:     const ICON_RESIZE_VERTICAL = 'icon-resize-vertical';
 385:     const ICON_RESIZE_HORIZONTAL = 'icon-resize-horizontal';
 386:     const ICON_HDD = 'icon-hdd';
 387:     const ICON_BULLHORN = 'icon-bullhorn';
 388:     const ICON_BELL = 'icon-bell';
 389:     const ICON_CERTFICATE = 'icon-certificate';
 390:     const ICON_THUMBS_UP = 'icon-thumbs-up';
 391:     const ICON_THUMBS_DOWN = 'icon-thumbs-down';
 392:     const ICON_HAND_RIGHT = 'icon-hand-right';
 393:     const ICON_HAND_LEFT = 'icon-hand-left';
 394:     const ICON_HAND_UP = 'icon-hand-up';
 395:     const ICON_HAND_DOWN = 'icon-hand-down';
 396:     const ICON_CIRCLE_ARROW_RIGHT = 'icon-circle-arrow-right';
 397:     const ICON_CIRCLE_ARROW_LEFT = 'icon-circle-arrow-left';
 398:     const ICON_CIRCLE_ARROW_UP = 'icon-circle-arrow-up';
 399:     const ICON_CIRCLE_ARROW_DOWN = 'icon-circle-arrow-down';
 400:     const ICON_GLOBE = 'icon-globe';
 401:     const ICON_WRENCH = 'icon-wrench';
 402:     const ICON_TASKS = 'icon-tasks';
 403:     const ICON_FILTER = 'icon-filter';
 404:     const ICON_BRIEFCASE = 'icon-briefcase';
 405:     const ICON_FULLSCREEN = 'icon-fullscreen';
 406: 
 407:     // Default close text.
 408:     const CLOSE_TEXT = '&times;';
 409: 
 410:     /**
 411:      * @var string the CSS class for displaying error summaries.
 412:      */
 413:     public static $errorSummaryCss = 'alert alert-block alert-error';
 414: 
 415:     //
 416:     // BASE CSS
 417:     // --------------------------------------------------
 418: 
 419:     // Typography
 420:     // http://twitter.github.io/bootstrap/2.3.2/base-css.html#typography
 421:     // --------------------------------------------------
 422: 
 423:     /**
 424:      * Generates a paragraph that stands out.
 425:      * @param string $text the lead text.
 426:      * @param array $htmlOptions additional HTML attributes.
 427:      * @return string the generated paragraph.
 428:      */
 429:     public static function lead($text, $htmlOptions = array())
 430:     {
 431:         self::addCssClass('lead', $htmlOptions);
 432:         return self::tag('p', $htmlOptions, $text);
 433:     }
 434: 
 435:     /**
 436:      * Generates small text.
 437:      * @param string $text the text to style.
 438:      * @param array $htmlOptions additional HTML attributes.
 439:      * @return string the generated text.
 440:      */
 441:     public static function small($text, $htmlOptions = array())
 442:     {
 443:         return self::tag('small', $htmlOptions, $text);
 444:     }
 445: 
 446:     /**
 447:      * Generates bold text.
 448:      * @param string $text the text to style.
 449:      * @param array $htmlOptions additional HTML attributes.
 450:      * @return string the generated text.
 451:      */
 452:     public static function b($text, $htmlOptions = array())
 453:     {
 454:         return self::tag('strong', $htmlOptions, $text);
 455:     }
 456: 
 457:     /**
 458:      * Generates italic text.
 459:      * @param string $text the text to style.
 460:      * @param array $htmlOptions additional HTML attributes.
 461:      * @return string the generated text.
 462:      */
 463:     public static function i($text, $htmlOptions = array())
 464:     {
 465:         return self::tag('em', $htmlOptions, $text);
 466:     }
 467: 
 468:     /**
 469:      * Generates an emphasized text.
 470:      * @param string $style the text style.
 471:      * @param string $text the text to emphasize.
 472:      * @param array $htmlOptions additional HTML attributes.
 473:      * @param string $tag the HTML tag.
 474:      * @return string the generated text.
 475:      */
 476:     public static function em($text, $htmlOptions = array(), $tag = 'p')
 477:     {
 478:         $color = TbArray::popValue('color', $htmlOptions);
 479:         if (TbArray::popValue('muted', $htmlOptions, false)) {
 480:             self::addCssClass('muted', $htmlOptions);
 481:         } else {
 482:             if (!empty($color)) {
 483:                 self::addCssClass('text-' . $color, $htmlOptions);
 484:             }
 485:         }
 486:         return self::tag($tag, $htmlOptions, $text);
 487:     }
 488: 
 489:     /**
 490:      * Generates a muted text block.
 491:      * @param string $text the text.
 492:      * @param array $htmlOptions additional HTML attributes.
 493:      * @param string $tag the HTML tag.
 494:      * @return string the generated text block.
 495:      */
 496:     public static function muted($text, $htmlOptions = array(), $tag = 'p')
 497:     {
 498:         $htmlOptions['muted'] = true;
 499:         return self::em($text, $htmlOptions, $tag);
 500:     }
 501: 
 502:     /**
 503:      * Generates a muted span.
 504:      * @param string $text the text.
 505:      * @param array $htmlOptions additional HTML attributes.
 506:      * @param string $tag the HTML tag.
 507:      * @return string the generated span.
 508:      */
 509:     public static function mutedSpan($text, $htmlOptions = array())
 510:     {
 511:         return self::muted($text, $htmlOptions, 'span');
 512:     }
 513: 
 514:     /**
 515:      * Generates an abbreviation with a help text.
 516:      * @param string $text the abbreviation.
 517:      * @param string $word the word the abbreviation is for.
 518:      * @param array $htmlOptions additional HTML attributes.
 519:      * @return string the generated abbreviation.
 520:      */
 521:     public static function abbr($text, $word, $htmlOptions = array())
 522:     {
 523:         if (TbArray::popValue('small', $htmlOptions, false)) {
 524:             self::addCssClass('initialism', $htmlOptions);
 525:         }
 526:         $htmlOptions['title'] = $word;
 527:         return self::tag('abbr', $htmlOptions, $text);
 528:     }
 529: 
 530:     /**
 531:      * Generates a small abbreviation with a help text.
 532:      * @param string $text the abbreviation.
 533:      * @param string $word the word the abbreviation is for.
 534:      * @param array $htmlOptions additional HTML attributes.
 535:      * @return string the generated abbreviation.
 536:      */
 537:     public static function smallAbbr($text, $word, $htmlOptions = array())
 538:     {
 539:         $htmlOptions['small'] = true;
 540:         return self::abbr($text, $word, $htmlOptions);
 541:     }
 542: 
 543:     /**
 544:      * Generates an address block.
 545:      * @param string $quote the address text.
 546:      * @param array $htmlOptions additional HTML attributes.
 547:      * @return string the generated block.
 548:      */
 549:     public static function address($text, $htmlOptions = array())
 550:     {
 551:         return self::tag('address', $htmlOptions, $text);
 552:     }
 553: 
 554:     /**
 555:      * Generates a quote.
 556:      * @param string $text the quoted text.
 557:      * @param array $htmlOptions additional HTML attributes.
 558:      * @return string the generated quote.
 559:      */
 560:     public static function quote($text, $htmlOptions = array())
 561:     {
 562:         $paragraphOptions = TbArray::popValue('paragraphOptions', $htmlOptions, array());
 563:         $source = TbArray::popValue('source', $htmlOptions);
 564:         $sourceOptions = TbArray::popValue('sourceOptions', $htmlOptions, array());
 565:         $cite = TbArray::popValue('cite', $htmlOptions);
 566:         $citeOptions = TbArray::popValue('citeOptions', $htmlOptions, array());
 567:         $cite = isset($cite) ? ' ' . self::tag('cite', $citeOptions, $cite) : '';
 568:         $source = isset($source) ? self::tag('small', $sourceOptions, $source . $cite) : '';
 569:         $text = self::tag('p', $paragraphOptions, $text) . $source;
 570:         return self::tag('blockquote', $htmlOptions, $text);
 571:     }
 572: 
 573:     /**
 574:      * Generates a help text.
 575:      * @param string $text the help text.
 576:      * @param array $htmlOptions additional HTML attributes.
 577:      * @return string the generated text.
 578:      */
 579:     public static function help($text, $htmlOptions = array())
 580:     {
 581:         $type = TbArray::popValue('type', $htmlOptions, self::HELP_TYPE_INLINE);
 582:         self::addCssClass('help-' . $type, $htmlOptions);
 583:         return self::tag($type === self::HELP_TYPE_INLINE ? 'span' : 'p', $htmlOptions, $text);
 584:     }
 585: 
 586:     /**
 587:      * Generates a help block.
 588:      * @param string $text the help text.
 589:      * @param array $htmlOptions additional HTML attributes.
 590:      * @return string the generated block.
 591:      */
 592:     public static function helpBlock($text, $htmlOptions = array())
 593:     {
 594:         $htmlOptions['type'] = self::HELP_TYPE_BLOCK;
 595:         return self::help($text, $htmlOptions);
 596:     }
 597: 
 598:     // Code
 599:     // http://twitter.github.io/bootstrap/2.3.2/base-css.html#code
 600:     // --------------------------------------------------
 601: 
 602:     /**
 603:      * Generates inline code.
 604:      * @param string $code the code.
 605:      * @param array $htmlOptions additional HTML attributes.
 606:      * @return string the generated code.
 607:      */
 608:     public static function code($code, $htmlOptions = array())
 609:     {
 610:         return self::tag('code', $htmlOptions, $code);
 611:     }
 612: 
 613:     /**
 614:      * Generates a code block.
 615:      * @param string $code the code.
 616:      * @param array $htmlOptions additional HTML attributes.
 617:      * @return string the generated block.
 618:      */
 619:     public static function codeBlock($code, $htmlOptions = array())
 620:     {
 621:         return self::tag('pre', $htmlOptions, $code);
 622:     }
 623: 
 624:     /**
 625:      * Generates an HTML element.
 626:      * @param string $tag the tag name.
 627:      * @param array $htmlOptions the element attributes.
 628:      * @param mixed $content the content to be enclosed between open and close element tags.
 629:      * @param boolean $closeTag whether to generate the close tag.
 630:      * @return string the generated HTML element tag.
 631:      */
 632:     public static function tag($tag, $htmlOptions = array(), $content = false, $closeTag = true)
 633:     {
 634:         self::addSpanClass($htmlOptions);
 635:         self::addPullClass($htmlOptions);
 636:         self::addTextAlignClass($htmlOptions);
 637:         return parent::tag($tag, $htmlOptions, $content, $closeTag);
 638:     }
 639: 
 640:     /**
 641:      * Generates an open HTML element.
 642:      * @param string $tag the tag name.
 643:      * @param array $htmlOptions the element attributes.
 644:      * @return string the generated HTML element tag.
 645:      */
 646:     public static function openTag($tag, $htmlOptions = array())
 647:     {
 648:         return self::tag($tag, $htmlOptions, false, false);
 649:     }
 650: 
 651:     // Tables
 652:     // http://twitter.github.io/bootstrap/2.3.2/base-css.html#forms
 653:     // --------------------------------------------------
 654: 
 655:     // todo: create table methods here.
 656: 
 657:     // Forms
 658:     // http://twitter.github.io/bootstrap/2.3.2/base-css.html#tables
 659:     // --------------------------------------------------
 660: 
 661:     /**
 662:      * Generates a form tag.
 663:      * @param string $layout the form layout.
 664:      * @param string $action the form action URL.
 665:      * @param string $method form method (e.g. post, get).
 666:      * @param array $htmlOptions additional HTML attributes.
 667:      * @return string the generated tag.
 668:      */
 669:     public static function formTb(
 670:         $layout = self::FORM_LAYOUT_VERTICAL,
 671:         $action = '',
 672:         $method = 'post',
 673:         $htmlOptions = array()
 674:     ) {
 675:         return self::beginFormTb($layout, $action, $method, $htmlOptions);
 676:     }
 677: 
 678:     /**
 679:      * Generates an open form tag.
 680:      * @param string $layout the form layout.
 681:      * @param string $action the form action URL.
 682:      * @param string $method form method (e.g. post, get).
 683:      * @param array $htmlOptions additional HTML attributes.
 684:      * @return string the generated tag.
 685:      */
 686:     public static function beginFormTb(
 687:         $layout = self::FORM_LAYOUT_VERTICAL,
 688:         $action = '',
 689:         $method = 'post',
 690:         $htmlOptions = array()
 691:     ) {
 692:         if (!empty($layout)) {
 693:             self::addCssClass('form-' . $layout, $htmlOptions);
 694:         }
 695:         return parent::beginForm($action, $method, $htmlOptions);
 696:     }
 697: 
 698:     /**
 699:      * Generates a stateful form tag.
 700:      * @param mixed $action the form action URL.
 701:      * @param string $method form method (e.g. post, get).
 702:      * @param array $htmlOptions additional HTML attributes.
 703:      * @return string the generated form tag.
 704:      */
 705:     public static function statefulFormTb(
 706:         $layout = self::FORM_LAYOUT_VERTICAL,
 707:         $action = '',
 708:         $method = 'post',
 709:         $htmlOptions = array()
 710:     ) {
 711:         return self::formTb($layout, $action, $method, $htmlOptions)
 712:         . self::tag('div', array('style' => 'display: none'), parent::pageStateField(''));
 713:     }
 714: 
 715:     /**
 716:      * Generates a text field input.
 717:      * @param string $name the input name.
 718:      * @param string $value the input value.
 719:      * @param array $htmlOptions additional HTML attributes.
 720:      * @return string the generated input field.
 721:      * @see self::textInputField
 722:      */
 723:     public static function textField($name, $value = '', $htmlOptions = array())
 724:     {
 725:         return self::textInputField('text', $name, $value, $htmlOptions);
 726:     }
 727: 
 728:     /**
 729:      * Generates a password field input.
 730:      * @param string $name the input name.
 731:      * @param string $value the input value.
 732:      * @param array $htmlOptions additional HTML attributes.
 733:      * @return string the generated input field.
 734:      * @see self::textInputField
 735:      */
 736:     public static function passwordField($name, $value = '', $htmlOptions = array())
 737:     {
 738:         return self::textInputField('password', $name, $value, $htmlOptions);
 739:     }
 740: 
 741:     /**
 742:      * Generates an url field input.
 743:      * @param string $name the input name.
 744:      * @param string $value the input value.
 745:      * @param array $htmlOptions additional HTML attributes.
 746:      * @return string the generated input field.
 747:      * @see self::textInputField
 748:      */
 749:     public static function urlField($name, $value = '', $htmlOptions = array())
 750:     {
 751:         return self::textInputField('url', $name, $value, $htmlOptions);
 752:     }
 753: 
 754:     /**
 755:      * Generates an email field input.
 756:      * @param string $name the input name.
 757:      * @param string $value the input value.
 758:      * @param array $htmlOptions additional HTML attributes.
 759:      * @return string the generated input field.
 760:      * @see self::textInputField
 761:      */
 762:     public static function emailField($name, $value = '', $htmlOptions = array())
 763:     {
 764:         return self::textInputField('email', $name, $value, $htmlOptions);
 765:     }
 766: 
 767:     /**
 768:      * Generates a number field input.
 769:      * @param string $name the input name.
 770:      * @param string $value the input value.
 771:      * @param array $htmlOptions additional HTML attributes.
 772:      * @return string the generated input field.
 773:      * @see self::textInputField
 774:      */
 775:     public static function numberField($name, $value = '', $htmlOptions = array())
 776:     {
 777:         return self::textInputField('number', $name, $value, $htmlOptions);
 778:     }
 779: 
 780:     /**
 781:      * Generates a range field input.
 782:      * @param string $name the input name.
 783:      * @param string $value the input value.
 784:      * @param array $htmlOptions additional HTML attributes.
 785:      * @return string the generated input field.
 786:      * @see self::textInputField
 787:      */
 788:     public static function rangeField($name, $value = '', $htmlOptions = array())
 789:     {
 790:         return self::textInputField('range', $name, $value, $htmlOptions);
 791:     }
 792: 
 793:     /**
 794:      * Generates a date field input.
 795:      * @param string $name the input name.
 796:      * @param string $value the input value.
 797:      * @param array $htmlOptions additional HTML attributes.
 798:      * @return string the generated input field.
 799:      * @see self::textInputField
 800:      */
 801:     public static function dateField($name, $value = '', $htmlOptions = array())
 802:     {
 803:         return self::textInputField('date', $name, $value, $htmlOptions);
 804:     }
 805: 
 806:     /**
 807:      * Generates a file field input.
 808:      * @param string $name the input name.
 809:      * @param string $value the input value.
 810:      * @param array $htmlOptions additional HTML attributes.
 811:      * @return string the generated input field.
 812:      * @see CHtml::fileField
 813:      */
 814:     public static function fileField($name, $value = '', $htmlOptions = array())
 815:     {
 816:         return parent::fileField($name, $value, $htmlOptions);
 817:     }
 818: 
 819:     /**
 820:      * Generates a text area input.
 821:      * @param string $name the input name.
 822:      * @param string $value the input value.
 823:      * @param array $htmlOptions additional HTML attributes.
 824:      * @return string the generated text area.
 825:      */
 826:     public static function textArea($name, $value = '', $htmlOptions = array())
 827:     {
 828:         $htmlOptions = self::normalizeInputOptions($htmlOptions);
 829:         return parent::textArea($name, $value, $htmlOptions);
 830:     }
 831: 
 832:     /**
 833:      * Generates a radio button.
 834:      * @param string $name the input name.
 835:      * @param boolean $checked whether the radio button is checked.
 836:      * @param array $htmlOptions additional HTML attributes.
 837:      * @return string the generated radio button.
 838:      */
 839:     public static function radioButton($name, $checked = false, $htmlOptions = array())
 840:     {
 841:         $label = TbArray::popValue('label', $htmlOptions, false);
 842:         $labelOptions = TbArray::popValue('labelOptions', $htmlOptions, array());
 843:         self::addCssClass('radio', $labelOptions);
 844:         $radioButton = parent::radioButton($name, $checked, $htmlOptions);
 845:         return $label !== false ? self::tag('label', $labelOptions, $radioButton . ' ' . $label) : $radioButton;
 846:     }
 847: 
 848:     /**
 849:      * Generates a check box.
 850:      * @param string $name the input name.
 851:      * @param boolean $checked whether the check box is checked.
 852:      * @param array $htmlOptions additional HTML attributes.
 853:      * @return string the generated check box.
 854:      */
 855:     public static function checkBox($name, $checked = false, $htmlOptions = array())
 856:     {
 857:         $label = TbArray::popValue('label', $htmlOptions, false);
 858:         $labelOptions = TbArray::popValue('labelOptions', $htmlOptions, array());
 859:         self::addCssClass('checkbox', $labelOptions);
 860:         $checkBox = parent::checkBox($name, $checked, $htmlOptions);
 861:         return $label !== false ? self::tag('label', $labelOptions, $checkBox . ' ' . $label) : $checkBox;
 862:     }
 863: 
 864:     /**
 865:      * Generates a drop down list.
 866:      * @param string $name the input name.
 867:      * @param string $select the selected value.
 868:      * @param array $data data for generating the list options (value=>display).
 869:      * @return string the generated drop down list.
 870:      */
 871:     public static function dropDownList($name, $select, $data, $htmlOptions = array())
 872:     {
 873:         $displaySize = TbArray::popValue('displaySize', $htmlOptions);
 874:         $htmlOptions = self::normalizeInputOptions($htmlOptions);
 875:         if (!empty($displaySize)) {
 876:             $htmlOptions['size'] = $displaySize;
 877:         }
 878:         return parent::dropDownList($name, $select, $data, $htmlOptions);
 879:     }
 880: 
 881:     /**
 882:      * Generates a list box.
 883:      * @param string $name the input name.
 884:      * @param mixed $select the selected value(s).
 885:      * @param array $data data for generating the list options (value=>display).
 886:      * @param array $htmlOptions additional HTML attributes.
 887:      * @return string the generated list box
 888:      */
 889:     public static function listBox($name, $select, $data, $htmlOptions = array())
 890:     {
 891:         if (isset($htmlOptions['multiple'])) {
 892:             if (substr($name, -2) !== '[]') {
 893:                 $name .= '[]';
 894:             }
 895:         }
 896:         TbArray::defaultValue('displaySize', 4, $htmlOptions);
 897:         return self::dropDownList($name, $select, $data, $htmlOptions);
 898:     }
 899: 
 900:     /**
 901:      * Generates a radio button list.
 902:      * @param string $name name of the radio button list.
 903:      * @param mixed $select selection of the radio buttons.
 904:      * @param array $data $data value-label pairs used to generate the radio button list.
 905:      * @param array $htmlOptions additional HTML attributes.
 906:      * @return string the generated list.
 907:      */
 908:     public static function radioButtonList($name, $select, $data, $htmlOptions = array())
 909:     {
 910:         $inline = TbArray::popValue('inline', $htmlOptions, false);
 911:         $separator = TbArray::popValue('separator', $htmlOptions, ' ');
 912:         $container = TbArray::popValue('container', $htmlOptions, 'span');
 913:         $containerOptions = TbArray::popValue('containerOptions', $htmlOptions, array());
 914:         $labelOptions = TbArray::popValue('labelOptions', $htmlOptions, array());
 915: 
 916:         $items = array();
 917:         $baseID = $containerOptions['id'] = TbArray::popValue('baseID', $htmlOptions, parent::getIdByName($name));
 918: 
 919:         $id = 0;
 920:         foreach ($data as $value => $label) {
 921:             $checked = !strcmp($value, $select);
 922:             $htmlOptions['value'] = $value;
 923:             $htmlOptions['id'] = $baseID . '_' . $id++;
 924:             if ($inline) {
 925:                 $htmlOptions['label'] = $label;
 926:                 self::addCssClass('inline', $labelOptions);
 927:                 $htmlOptions['labelOptions'] = $labelOptions;
 928:                 $items[] = self::radioButton($name, $checked, $htmlOptions);
 929:             } else {
 930:                 $option = self::radioButton($name, $checked, $htmlOptions);
 931:                 self::addCssClass('radio', $labelOptions);
 932:                 $items[] = self::label($option . ' ' . $label, false, $labelOptions);
 933:             }
 934:         }
 935: 
 936:         $inputs = implode($separator, $items);
 937:         return !empty($container) ? self::tag($container, $containerOptions, $inputs) : $inputs;
 938:     }
 939: 
 940:     /**
 941:      * Generates an inline radio button list.
 942:      * @param string $name name of the radio button list.
 943:      * @param mixed $select selection of the radio buttons.
 944:      * @param array $data $data value-label pairs used to generate the radio button list.
 945:      * @param array $htmlOptions additional HTML attributes.
 946:      * @return string the generated list.
 947:      */
 948:     public static function inlineRadioButtonList($name, $select, $data, $htmlOptions = array())
 949:     {
 950:         $htmlOptions['inline'] = true;
 951:         return self::radioButtonList($name, $select, $data, $htmlOptions);
 952:     }
 953: 
 954:     /**
 955:      * Generates a check box list.
 956:      * @param string $name name of the check box list.
 957:      * @param mixed $select selection of the check boxes.
 958:      * @param array $data $data value-label pairs used to generate the check box list.
 959:      * @param array $htmlOptions additional HTML attributes.
 960:      * @return string the generated list.
 961:      */
 962:     public static function checkBoxList($name, $select, $data, $htmlOptions = array())
 963:     {
 964:         $inline = TbArray::popValue('inline', $htmlOptions, false);
 965:         $separator = TbArray::popValue('separator', $htmlOptions, ' ');
 966:         $container = TbArray::popValue('container', $htmlOptions, 'span');
 967:         $containerOptions = TbArray::popValue('containerOptions', $htmlOptions, array());
 968:         $labelOptions = TbArray::popValue('labelOptions', $htmlOptions, array());
 969: 
 970:         if (substr($name, -2) !== '[]') {
 971:             $name .= '[]';
 972:         }
 973: 
 974:         $checkAll = TbArray::popValue('checkAll', $htmlOptions);
 975:         $checkAllLast = TbArray::popValue('checkAllLast', $htmlOptions);
 976:         if ($checkAll !== null) {
 977:             $checkAllLabel = $checkAll;
 978:             $checkAllLast = $checkAllLast !== null;
 979:         }
 980: 
 981:         $items = array();
 982:         $baseID = $containerOptions['id'] = TbArray::popValue('baseID', $htmlOptions, parent::getIdByName($name));
 983:         $id = 0;
 984:         $checkAll = true;
 985: 
 986:         foreach ($data as $value => $label) {
 987:             $checked = !is_array($select) && !strcmp($value, $select) || is_array($select) && in_array($value, $select);
 988:             $checkAll = $checkAll && $checked;
 989:             $htmlOptions['value'] = $value;
 990:             $htmlOptions['id'] = $baseID . '_' . $id++;
 991:             if ($inline) {
 992:                 $htmlOptions['label'] = $label;
 993:                 self::addCssClass('inline', $labelOptions);
 994:                 $htmlOptions['labelOptions'] = $labelOptions;
 995:                 $items[] = self::checkBox($name, $checked, $htmlOptions);
 996:             } else {
 997:                 self::addCssClass('checkbox', $labelOptions);
 998:                 $option = self::checkBox($name, $checked, $htmlOptions);
 999:                 $items[] = self::label($option . ' ' . $label, false, $labelOptions);
1000:             }
1001:         }
1002: 
1003:         if (isset($checkAllLabel)) {
1004:             $htmlOptions['value'] = 1;
1005:             $htmlOptions['id'] = $id = $baseID . '_all';
1006:             $option = self::checkBox($id, $checkAll, $htmlOptions);
1007:             $label = self::label($checkAllLabel, $id, $labelOptions);
1008:             $item = $option . ' ' . $label;
1009:             if ($checkAllLast) {
1010:                 $items[] = $item;
1011:             } else {
1012:                 array_unshift($items, $item);
1013:             }
1014:             $name = strtr($name, array('[' => '\\[', ']' => '\\]'));
1015:             $js = <<<EOD
1016: jQuery('#$id').click(function() {
1017:     jQuery("input[name='$name']").prop('checked', this.checked);
1018: });
1019: jQuery("input[name='$name']").click(function() {
1020:     jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
1021: });
1022: jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
1023: EOD;
1024:             $cs = Yii::app()->getClientScript();
1025:             $cs->registerCoreScript('jquery');
1026:             $cs->registerScript($id, $js);
1027:         }
1028: 
1029:         $inputs = implode($separator, $items);
1030:         return !empty($container) ? self::tag($container, $containerOptions, $inputs) : $inputs;
1031:     }
1032: 
1033:     /**
1034:      * Generates an inline check box list.
1035:      * @param string $name name of the check box list.
1036:      * @param mixed $select selection of the check boxes.
1037:      * @param array $data $data value-label pairs used to generate the check box list.
1038:      * @param array $htmlOptions additional HTML attributes.
1039:      * @return string the generated list.
1040:      */
1041:     public static function inlineCheckBoxList($name, $select, $data, $htmlOptions = array())
1042:     {
1043:         $htmlOptions['inline'] = true;
1044:         return self::checkBoxList($name, $select, $data, $htmlOptions);
1045:     }
1046: 
1047:     /**
1048:      * Generates an uneditable input.
1049:      * @param string $value the value.
1050:      * @param array $htmlOptions additional HTML attributes.
1051:      * @return string the generated input.
1052:      */
1053:     public static function uneditableField($value, $htmlOptions = array())
1054:     {
1055:         self::addCssClass('uneditable-input', $htmlOptions);
1056:         $htmlOptions = self::normalizeInputOptions($htmlOptions);
1057:         return self::tag('span', $htmlOptions, $value);
1058:     }
1059: 
1060:     /**
1061:      * Generates a search input.
1062:      * @param string $name the input name.
1063:      * @param string $value the input value.
1064:      * @param array $htmlOptions additional HTML attributes.
1065:      * @return string the generated input.
1066:      */
1067:     public static function searchQueryField($name, $value = '', $htmlOptions = array())
1068:     {
1069:         self::addCssClass('search-query', $htmlOptions);
1070:         return self::textField($name, $value, $htmlOptions);
1071:     }
1072: 
1073:     /**
1074:      * Generates a control group with a text field.
1075:      * @param string $name the input name.
1076:      * @param string $value the input value.
1077:      * @param array $htmlOptions additional HTML attributes.
1078:      * @return string the generated control group.
1079:      * @see self::controlGroup
1080:      */
1081:     public static function textFieldControlGroup($name, $value = '', $htmlOptions = array())
1082:     {
1083:         return self::controlGroup(self::INPUT_TYPE_TEXT, $name, $value, $htmlOptions);
1084:     }
1085: 
1086:     /**
1087:      * Generates a control group with a password field.
1088:      * @param string $name the input name.
1089:      * @param string $value the input value.
1090:      * @param array $htmlOptions additional HTML attributes.
1091:      * @return string the generated control group.
1092:      * @see self::textInputField
1093:      */
1094:     public static function passwordFieldControlGroup($name, $value = '', $htmlOptions = array())
1095:     {
1096:         return self::controlGroup(self::INPUT_TYPE_PASSWORD, $name, $value, $htmlOptions);
1097:     }
1098: 
1099:     /**
1100:      * Generates a control group with an url field.
1101:      * @param string $name the input name.
1102:      * @param string $value the input value.
1103:      * @param array $htmlOptions additional HTML attributes.
1104:      * @return string the generated control group.
1105:      * @see self::controlGroup
1106:      */
1107:     public static function urlFieldControlGroup($name, $value = '', $htmlOptions = array())
1108:     {
1109:         return self::controlGroup(self::INPUT_TYPE_URL, $name, $value, $htmlOptions);
1110:     }
1111: 
1112:     /**
1113:      * Generates a control group with an email field.
1114:      * @param string $name the input name.
1115:      * @param string $value the input value.
1116:      * @param array $htmlOptions additional HTML attributes.
1117:      * @return string the generated control group.
1118:      * @see self::controlGroup
1119:      */
1120:     public static function emailFieldControlGroup($name, $value = '', $htmlOptions = array())
1121:     {
1122:         return self::controlGroup(self::INPUT_TYPE_EMAIL, $name, $value, $htmlOptions);
1123:     }
1124: 
1125:     /**
1126:      * Generates a control group with a number field.
1127:      * @param string $name the input name.
1128:      * @param string $value the input value.
1129:      * @param array $htmlOptions additional HTML attributes.
1130:      * @return string the generated control group.
1131:      * @see self::textInputField
1132:      */
1133:     public static function numberFieldControlGroup($name, $value = '', $htmlOptions = array())
1134:     {
1135:         return self::controlGroup(self::INPUT_TYPE_NUMBER, $name, $value, $htmlOptions);
1136:     }
1137: 
1138:     /**
1139:      * Generates a control group with a range field.
1140:      * @param string $name the input name
1141:      * @param string $value the input value
1142:      * @param array $htmlOptions additional HTML attributes.
1143:      * @return string the generated control group.
1144:      * @see self::controlGroup
1145:      */
1146:     public static function rangeFieldControlGroup($name, $value = '', $htmlOptions = array())
1147:     {
1148:         return self::controlGroup(self::INPUT_TYPE_RANGE, $name, $value, $htmlOptions);
1149:     }
1150: 
1151:     /**
1152:      * Generates a control group with a file field.
1153:      * @param string $name the input name.
1154:      * @param string $value the input value.
1155:      * @param array $htmlOptions additional HTML attributes.
1156:      * @return string the generated control group.
1157:      * @see self::controlGroup
1158:      */
1159:     public static function dateFieldControlGroup($name, $value = '', $htmlOptions = array())
1160:     {
1161:         return self::controlGroup(self::INPUT_TYPE_DATE, $name, $value, $htmlOptions);
1162:     }
1163: 
1164:     /**
1165:      * Generates a control group with a text area.
1166:      * @param string $name the input name.
1167:      * @param string $value the input value.
1168:      * @param array $htmlOptions additional HTML attributes.
1169:      * @return string the generated control group.
1170:      * @see self::controlGroup
1171:      */
1172:     public static function textAreaControlGroup($name, $value = '', $htmlOptions = array())
1173:     {
1174:         return self::controlGroup(self::INPUT_TYPE_TEXTAREA, $name, $value, $htmlOptions);
1175:     }
1176: 
1177:     /**
1178:      * Generates a control group with a file field.
1179:      * @param string $name the input name.
1180:      * @param string $value the input value.
1181:      * @param array $htmlOptions additional HTML attributes.
1182:      * @return string the generated control group.
1183:      * @see self::controlGroup
1184:      */
1185:     public static function fileFieldControlGroup($name, $value = '', $htmlOptions = array())
1186:     {
1187:         return self::controlGroup(self::INPUT_TYPE_FILE, $name, $value, $htmlOptions);
1188:     }
1189: 
1190:     /**
1191:      * Generates a control group with a radio button.
1192:      * @param string $name the input name.
1193:      * @param string $checked whether the radio button is checked.
1194:      * @param array $htmlOptions additional HTML attributes.
1195:      * @return string the generated control group.
1196:      * @see self::controlGroup
1197:      */
1198:     public static function radioButtonControlGroup($name, $checked = false, $htmlOptions = array())
1199:     {
1200:         return self::controlGroup(self::INPUT_TYPE_RADIOBUTTON, $name, $checked, $htmlOptions);
1201:     }
1202: 
1203:     /**
1204:      * Generates a control group with a check box.
1205:      * @param string $name the input name.
1206:      * @param string $checked whether the check box is checked.
1207:      * @param array $htmlOptions additional HTML attributes.
1208:      * @return string the generated control group.
1209:      * @see self::controlGroup
1210:      */
1211:     public static function checkBoxControlGroup($name, $checked = false, $htmlOptions = array())
1212:     {
1213:         return self::controlGroup(self::INPUT_TYPE_CHECKBOX, $name, $checked, $htmlOptions);
1214:     }
1215: 
1216:     /**
1217:      * Generates a control group with a drop down list.
1218:      * @param string $name the input name.
1219:      * @param string $select the selected value.
1220:      * @param array $data data for generating the list options (value=>display).
1221:      * @param array $htmlOptions additional HTML attributes.
1222:      * @return string the generated control group.
1223:      * @see self::controlGroup
1224:      */
1225:     public static function dropDownListControlGroup($name, $select = '', $data = array(), $htmlOptions = array())
1226:     {
1227:         return self::controlGroup(self::INPUT_TYPE_DROPDOWNLIST, $name, $select, $htmlOptions, $data);
1228:     }
1229: 
1230:     /**
1231:      * Generates a control group with a list box.
1232:      * @param string $name the input name.
1233:      * @param string $select the selected value.
1234:      * @param array $data data for generating the list options (value=>display).
1235:      * @param array $htmlOptions additional HTML attributes.
1236:      * @return string the generated control group.
1237:      * @see self::controlGroup
1238:      */
1239:     public static function listBoxControlGroup($name, $select = '', $data = array(), $htmlOptions = array())
1240:     {
1241:         return self::controlGroup(self::INPUT_TYPE_LISTBOX, $name, $select, $htmlOptions, $data);
1242:     }
1243: 
1244:     /**
1245:      * Generates a control group with a radio button list.
1246:      * @param string $name the input name.
1247:      * @param string $select the selected value.
1248:      * @param array $data data for generating the list options (value=>display).
1249:      * @param array $htmlOptions additional HTML attributes.
1250:      * @return string the generated control group.
1251:      * @see self::controlGroup
1252:      */
1253:     public static function radioButtonListControlGroup($name, $select = '', $data = array(), $htmlOptions = array())
1254:     {
1255:         return self::controlGroup(self::INPUT_TYPE_RADIOBUTTONLIST, $name, $select, $htmlOptions, $data);
1256:     }
1257: 
1258:     /**
1259:      * Generates a control group with an inline radio button list.
1260:      * @param string $name the input name.
1261:      * @param string $select the selected value.
1262:      * @param array $data data for generating the list options (value=>display).
1263:      * @param array $htmlOptions additional HTML attributes.
1264:      * @return string the generated control group.
1265:      * @see self::controlGroup
1266:      */
1267:     public static function inlineRadioButtonListControlGroup(
1268:         $name,
1269:         $select = '',
1270:         $data = array(),
1271:         $htmlOptions = array()
1272:     ) {
1273:         return self::controlGroup(self::INPUT_TYPE_INLINERADIOBUTTONLIST, $name, $select, $htmlOptions, $data);
1274:     }
1275: 
1276:     /**
1277:      * Generates a control group with a check box list.
1278:      * @param string $name the input name.
1279:      * @param string $select the selected value.
1280:      * @param array $data data for generating the list options (value=>display).
1281:      * @param array $htmlOptions additional HTML attributes.
1282:      * @return string the generated control group.
1283:      * @see self::controlGroup
1284:      */
1285:     public static function checkBoxListControlGroup($name, $select = '', $data = array(), $htmlOptions = array())
1286:     {
1287:         return self::controlGroup(self::INPUT_TYPE_CHECKBOXLIST, $name, $select, $htmlOptions, $data);
1288:     }
1289: 
1290:     /**
1291:      * Generates a control group with an inline check box list.
1292:      * @param string $name the input name.
1293:      * @param string $select the selected value.
1294:      * @param array $data data for generating the list options (value=>display).
1295:      * @param array $htmlOptions additional HTML attributes.
1296:      * @return string the generated control group.
1297:      * @see self::controlGroup
1298:      */
1299:     public static function inlineCheckBoxListControlGroup($name, $select = '', $data = array(), $htmlOptions = array())
1300:     {
1301:         return self::controlGroup(self::INPUT_TYPE_INLINECHECKBOXLIST, $name, $select, $htmlOptions, $data);
1302:     }
1303: 
1304:     /**
1305:      * Generates a control group with an uneditable field.
1306:      * @param string $select the input value.
1307:      * @param array $htmlOptions additional HTML attributes.
1308:      * @return string the generated control group.
1309:      * @see self::controlGroup
1310:      */
1311:     public static function uneditableFieldControlGroup($value = '', $htmlOptions = array())
1312:     {
1313:         return self::controlGroup(self::INPUT_TYPE_UNEDITABLE, '', $value, $htmlOptions);
1314:     }
1315: 
1316:     /**
1317:      * Generates a control group with a search field.
1318:      * @param string $name the input name.
1319:      * @param string $select the input value.
1320:      * @param array $htmlOptions additional HTML attributes.
1321:      * @return string the generated control group.
1322:      * @see self::controlGroup
1323:      */
1324:     public static function searchQueryControlGroup($name, $value = '', $htmlOptions = array())
1325:     {
1326:         return self::controlGroup(self::INPUT_TYPE_SEARCH, $name, $value, $htmlOptions);
1327:     }
1328: 
1329:     /**
1330:      * Generates a form control group.
1331:      * @param string $type the input type.
1332:      * @param string $name the input name.
1333:      * @param string $value the input value.
1334:      * @param array $htmlOptions additional HTML attributes.
1335:      * @param array $data data for multiple select inputs.
1336:      * @return string the generated control group.
1337:      */
1338:     public static function controlGroup($type, $name, $value = '', $htmlOptions = array(), $data = array())
1339:     {
1340:         $color = TbArray::popValue('color', $htmlOptions);
1341:         $groupOptions = TbArray::popValue('groupOptions', $htmlOptions, array());
1342:         $controlOptions = TbArray::popValue('controlOptions', $htmlOptions, array());
1343:         $label = TbArray::popValue('label', $htmlOptions);
1344:         $labelOptions = TbArray::popValue('labelOptions', $htmlOptions, array());
1345: 
1346:         if (in_array($type, array(self::INPUT_TYPE_CHECKBOX, self::INPUT_TYPE_RADIOBUTTON))) {
1347:             $htmlOptions['label'] = $label;
1348:             $htmlOptions['labelOptions'] = $labelOptions;
1349:             $label = false;
1350:         }
1351: 
1352:         $help = TbArray::popValue('help', $htmlOptions, '');
1353:         $helpOptions = TbArray::popValue('helpOptions', $htmlOptions, array());
1354:         if (!empty($help)) {
1355:             $help = self::inputHelp($help, $helpOptions);
1356:         }
1357: 
1358:         $input = isset($htmlOptions['input'])
1359:             ? $htmlOptions['input']
1360:             : self::createInput($type, $name, $value, $htmlOptions, $data);
1361: 
1362:         self::addCssClass('control-group', $groupOptions);
1363:         if (!empty($color)) {
1364:             self::addCssClass($color, $groupOptions);
1365:         }
1366:         self::addCssClass('control-label', $labelOptions);
1367:         $output = self::openTag('div', $groupOptions);
1368:         if ($label !== false) {
1369:             $output .= parent::label($label, $name, $labelOptions);
1370:         }
1371:         $output .= self::controls($input . $help, $controlOptions);
1372:         $output .= '</div>';
1373:         return $output;
1374:     }
1375: 
1376:     /**
1377:      * Generates a custom (pre-rendered) form control group.
1378:      * @param string $input the rendered input.
1379:      * @param string $name the input name.
1380:      * @param array $htmlOptions additional HTML attributes.
1381:      * @return string the generated control group.
1382:      */
1383:     public static function customControlGroup($input, $name, $htmlOptions = array())
1384:     {
1385:         $htmlOptions['input'] = $input;
1386:         return self::controlGroup(self::INPUT_TYPE_CUSTOM, $name, '', $htmlOptions);
1387:     }
1388: 
1389:     /**
1390:      * Creates a form input of the given type.
1391:      * @param string $type the input type.
1392:      * @param string $name the input name.
1393:      * @param string $value the input value.
1394:      * @param array $htmlOptions additional HTML attributes.
1395:      * @param array $data data for multiple select inputs.
1396:      * @return string the input.
1397:      * @throws CException if the input type is invalid.
1398:      */
1399:     public static function createInput($type, $name, $value, $htmlOptions = array(), $data = array())
1400:     {
1401:         switch ($type) {
1402:             case self::INPUT_TYPE_TEXT:
1403:                 return self::textField($name, $value, $htmlOptions);
1404:             case self::INPUT_TYPE_PASSWORD:
1405:                 return self::passwordField($name, $value, $htmlOptions);
1406:             case self::INPUT_TYPE_URL:
1407:                 return self::urlField($name, $value, $htmlOptions);
1408:             case self::INPUT_TYPE_EMAIL:
1409:                 return self::emailField($name, $value, $htmlOptions);
1410:             case self::INPUT_TYPE_NUMBER:
1411:                 return self::numberField($name, $value, $htmlOptions);
1412:             case self::INPUT_TYPE_RANGE:
1413:                 return self::rangeField($name, $value, $htmlOptions);
1414:             case self::INPUT_TYPE_DATE:
1415:                 return self::dateField($name, $value, $htmlOptions);
1416:             case self::INPUT_TYPE_TEXTAREA:
1417:                 return self::textArea($name, $value, $htmlOptions);
1418:             case self::INPUT_TYPE_FILE:
1419:                 return self::fileField($name, $value, $htmlOptions);
1420:             case self::INPUT_TYPE_RADIOBUTTON:
1421:                 return self::radioButton($name, $value, $htmlOptions);
1422:             case self::INPUT_TYPE_CHECKBOX:
1423:                 return self::checkBox($name, $value, $htmlOptions);
1424:             case self::INPUT_TYPE_DROPDOWNLIST:
1425:                 return self::dropDownList($name, $value, $data, $htmlOptions);
1426:             case self::INPUT_TYPE_LISTBOX:
1427:                 return self::listBox($name, $value, $data, $htmlOptions);
1428:             case self::INPUT_TYPE_CHECKBOXLIST:
1429:                 return self::checkBoxList($name, $value, $data, $htmlOptions);
1430:             case self::INPUT_TYPE_INLINECHECKBOXLIST:
1431:                 return self::inlineCheckBoxList($name, $value, $data, $htmlOptions);
1432:             case self::INPUT_TYPE_RADIOBUTTONLIST:
1433:                 return self::radioButtonList($name, $value, $data, $htmlOptions);
1434:             case self::INPUT_TYPE_INLINERADIOBUTTONLIST:
1435:                 return self::inlineRadioButtonList($name, $value, $data, $htmlOptions);
1436:             case self::INPUT_TYPE_UNEDITABLE:
1437:                 return self::uneditableField($value, $htmlOptions);
1438:             case self::INPUT_TYPE_SEARCH:
1439:                 return self::searchQueryField($name, $value, $htmlOptions);
1440:             default:
1441:                 throw new CException('Invalid input type "' . $type . '".');
1442:         }
1443:     }
1444: 
1445:     /**
1446:      * Generates an input HTML tag.
1447:      * This method generates an input HTML tag based on the given input name and value.
1448:      * @param string $type the input type.
1449:      * @param string $name the input name.
1450:      * @param string $value the input value.
1451:      * @param array $htmlOptions additional HTML attributes.
1452:      * @return string the generated input tag.
1453:      */
1454:     protected static function textInputField($type, $name, $value, $htmlOptions)
1455:     {
1456:         parent::clientChange('change', $htmlOptions);
1457: 
1458:         $htmlOptions = self::normalizeInputOptions($htmlOptions);
1459: 
1460:         $addOnClasses = self::getAddOnClasses($htmlOptions);
1461:         $addOnOptions = TbArray::popValue('addOnOptions', $htmlOptions, array());
1462:         self::addCssClass($addOnClasses, $addOnOptions);
1463: 
1464:         $prepend = TbArray::popValue('prepend', $htmlOptions, '');
1465:         $prependOptions = TbArray::popValue('prependOptions', $htmlOptions, array());
1466:         if (!empty($prepend)) {
1467:             $prepend = self::inputAddOn($prepend, $prependOptions);
1468:         }
1469: 
1470:         $append = TbArray::popValue('append', $htmlOptions, '');
1471:         $appendOptions = TbArray::popValue('appendOptions', $htmlOptions, array());
1472:         if (!empty($append)) {
1473:             $append = self::inputAddOn($append, $appendOptions);
1474:         }
1475: 
1476:         $output = '';
1477:         if (!empty($addOnClasses)) {
1478:             $output .= self::openTag('div', $addOnOptions);
1479:         }
1480:         $output .= $prepend . parent::inputField($type, $name, $value, $htmlOptions) . $append;
1481:         if (!empty($addOnClasses)) {
1482:             $output .= '</div>';
1483:         }
1484:         return $output;
1485:     }
1486: 
1487:     /**
1488:      * Generates a text field input for a model attribute.
1489:      * @param CModel $model the data model.
1490:      * @param string $attribute the attribute.
1491:      * @param array $htmlOptions additional HTML attributes.
1492:      * @return string the generated input field.
1493:      * @see self::activeTextInputField
1494:      */
1495:     public static function activeTextField($model, $attribute, $htmlOptions = array())
1496:     {
1497:         return self::activeTextInputField('text', $model, $attribute, $htmlOptions);
1498:     }
1499: 
1500:     /**
1501:      * Generates a password field input for a model attribute.
1502:      * @param CModel $model the data model.
1503:      * @param string $attribute the attribute.
1504:      * @param array $htmlOptions additional HTML attributes.
1505:      * @return string the generated input field.
1506:      * @see self::activeTextInputField
1507:      */
1508:     public static function activePasswordField($model, $attribute, $htmlOptions = array())
1509:     {
1510:         return self::activeTextInputField('password', $model, $attribute, $htmlOptions);
1511:     }
1512: 
1513:     /**
1514:      * Generates an url field input for a model attribute.
1515:      * @param CModel $model the data model.
1516:      * @param string $attribute the attribute.
1517:      * @param array $htmlOptions additional HTML attributes.
1518:      * @return string the generated input field.
1519:      * @see self::activeTextInputField
1520:      */
1521:     public static function activeUrlField($model, $attribute, $htmlOptions = array())
1522:     {
1523:         return self::activeTextInputField('url', $model, $attribute, $htmlOptions);
1524:     }
1525: 
1526:     /**
1527:      * Generates an email field input for a model attribute.
1528:      * @param CModel $model the data model.
1529:      * @param string $attribute the attribute.
1530:      * @param array $htmlOptions additional HTML attributes.
1531:      * @return string the generated input field.
1532:      * @see self::activeTextInputField
1533:      */
1534:     public static function activeEmailField($model, $attribute, $htmlOptions = array())
1535:     {
1536:         return self::activeTextInputField('email', $model, $attribute, $htmlOptions);
1537:     }
1538: 
1539:     /**
1540:      * Generates a number field input for a model attribute.
1541:      * @param CModel $model the data model.
1542:      * @param string $attribute the attribute.
1543:      * @param array $htmlOptions additional HTML attributes.
1544:      * @return string the generated input field.
1545:      * @see self::activeTextInputField
1546:      */
1547:     public static function activeNumberField($model, $attribute, $htmlOptions = array())
1548:     {
1549:         return self::activeTextInputField('number', $model, $attribute, $htmlOptions);
1550:     }
1551: 
1552:     /**
1553:      * Generates a range field input for a model attribute.
1554:      * @param CModel $model the data model.
1555:      * @param string $attribute the attribute.
1556:      * @param array $htmlOptions additional HTML attributes.
1557:      * @return string the generated input field.
1558:      * @see self::activeTextInputField
1559:      */
1560:     public static function activeRangeField($model, $attribute, $htmlOptions = array())
1561:     {
1562:         return self::activeTextInputField('range', $model, $attribute, $htmlOptions);
1563:     }
1564: 
1565:     /**
1566:      * Generates a date field input for a model attribute.
1567:      * @param CModel $model the data model.
1568:      * @param string $attribute the attribute.
1569:      * @param array $htmlOptions additional HTML attributes.
1570:      * @return string the generated input field.
1571:      * @see self::activeTextInputField
1572:      */
1573:     public static function activeDateField($model, $attribute, $htmlOptions = array())
1574:     {
1575:         return self::activeTextInputField('date', $model, $attribute, $htmlOptions);
1576:     }
1577: 
1578:     /**
1579:      * Generates a file field input for a model attribute.
1580:      * @param CModel $model the data model.
1581:      * @param string $attribute the attribute.
1582:      * @param array $htmlOptions additional HTML attributes.
1583:      * @return string the generated input field.
1584:      * @see CHtml::activeFileField
1585:      */
1586:     public static function activeFileField($model, $attribute, $htmlOptions = array())
1587:     {
1588:         return parent::activeFileField($model, $attribute, $htmlOptions);
1589:     }
1590: 
1591:     /**
1592:      * Generates a text area input for a model attribute.
1593:      * @param CModel $model the data model.
1594:      * @param string $attribute the attribute.
1595:      * @param array $htmlOptions additional HTML attributes.
1596:      * @return string the generated text area.
1597:      */
1598:     public static function activeTextArea($model, $attribute, $htmlOptions = array())
1599:     {
1600:         $htmlOptions = self::normalizeInputOptions($htmlOptions);
1601:         return parent::activeTextArea($model, $attribute, $htmlOptions);
1602:     }
1603: 
1604:     /**
1605:      * Generates a radio button for a model attribute.
1606:      * @param CModel $model the data model.
1607:      * @param string $attribute the attribute.
1608:      * @param array $htmlOptions additional HTML attributes.
1609:      * @return string the generated radio button.
1610:      */
1611:     public static function activeRadioButton($model, $attribute, $htmlOptions = array())
1612:     {
1613:         $label = TbArray::popValue('label', $htmlOptions, false);
1614:         $labelOptions = TbArray::popValue('labelOptions', $htmlOptions, array());
1615:         $radioButton = parent::activeRadioButton($model, $attribute, $htmlOptions);
1616:         self::addCssClass('radio', $labelOptions);
1617:         return $label !== false ? self::tag('label', $labelOptions, $radioButton . ' ' . $label) : $radioButton;
1618:     }
1619: 
1620:     /**
1621:      * Generates a check box for a model attribute.
1622:      * @param CModel $model the data model.
1623:      * @param string $attribute the attribute.
1624:      * @param array $htmlOptions additional HTML attributes.
1625:      * @return string the generated check box.
1626:      */
1627:     public static function activeCheckBox($model, $attribute, $htmlOptions = array())
1628:     {
1629:         $label = TbArray::popValue('label', $htmlOptions, false);
1630:         $labelOptions = TbArray::popValue('labelOptions', $htmlOptions, array());
1631:         $checkBox = parent::activeCheckBox($model, $attribute, $htmlOptions);
1632:         self::addCssClass('checkbox', $labelOptions);
1633:         return $label !== false ? self::tag('label', $labelOptions, $checkBox . ' ' . $label) : $checkBox;
1634:     }
1635: 
1636:     /**
1637:      * Generates a drop down list for a model attribute.
1638:      * @param CModel $model the data model.
1639:      * @param string $attribute the attribute.
1640:      * @param array $data data for generating the list options (value=>display).
1641:      * @return string the generated drop down list.
1642:      */
1643:     public static function activeDropDownList($model, $attribute, $data, $htmlOptions = array())
1644:     {
1645:         $displaySize = TbArray::popValue('displaySize', $htmlOptions);
1646:         $htmlOptions = self::normalizeInputOptions($htmlOptions);
1647:         if (!empty($displaySize)) {
1648:             $htmlOptions['size'] = $displaySize;
1649:         }
1650:         return parent::activeDropDownList($model, $attribute, $data, $htmlOptions);
1651:     }
1652: 
1653:     /**
1654:      * Generates a list box for a model attribute.
1655:      * @param CModel $model the data model.
1656:      * @param string $attribute the attribute.
1657:      * @param array $data data for generating the list options (value=>display).
1658:      * @param array $htmlOptions additional HTML attributes.
1659:      * @return string the generated list box
1660:      */
1661:     public static function activeListBox($model, $attribute, $data, $htmlOptions = array())
1662:     {
1663:         TbArray::defaultValue('displaySize', 4, $htmlOptions);
1664:         return self::activeDropDownList($model, $attribute, $data, $htmlOptions);
1665:     }
1666: 
1667:     /**
1668:      * Generates a radio button list for a model attribute.
1669:      * @param CModel $model the data model.
1670:      * @param string $attribute the attribute.
1671:      * @param array $data $data value-label pairs used to generate the radio button list.
1672:      * @param array $htmlOptions additional HTML attributes.
1673:      * @return string the generated list.
1674:      */
1675:     public static function activeRadioButtonList($model, $attribute, $data, $htmlOptions = array())
1676:     {
1677:         parent::resolveNameID($model, $attribute, $htmlOptions);
1678:         $selection = parent::resolveValue($model, $attribute);
1679:         $name = TbArray::popValue('name', $htmlOptions);
1680:         $unCheck = TbArray::popValue('uncheckValue', $htmlOptions, '');
1681:         $hiddenOptions = isset($htmlOptions['id']) ? array('id' => parent::ID_PREFIX . $htmlOptions['id']) : array('id' => false);
1682:         $hidden = $unCheck !== null ? parent::hiddenField($name, $unCheck, $hiddenOptions) : '';
1683:         return $hidden . self::radioButtonList($name, $selection, $data, $htmlOptions);
1684:     }
1685: 
1686:     /**
1687:      * Generates an inline radio button list for a model attribute.
1688:      * @param CModel $model the data model.
1689:      * @param string $attribute the attribute.
1690:      * @param array $data $data value-label pairs used to generate the radio button list.
1691:      * @param array $htmlOptions additional HTML attributes.
1692:      * @return string the generated list.
1693:      */
1694:     public static function activeInlineRadioButtonList($model, $attribute, $data, $htmlOptions = array())
1695:     {
1696:         $htmlOptions['inline'] = true;
1697:         return self::activeRadioButtonList($model, $attribute, $data, $htmlOptions);
1698:     }
1699: 
1700:     /**
1701:      * Generates a check box list for a model attribute.
1702:      * @param CModel $model the data model.
1703:      * @param string $attribute the attribute.
1704:      * @param array $data $data value-label pairs used to generate the check box list.
1705:      * @param array $htmlOptions additional HTML attributes.
1706:      * @return string the generated list.
1707:      */
1708:     public static function activeCheckBoxList($model, $attribute, $data, $htmlOptions = array())
1709:     {
1710:         parent::resolveNameID($model, $attribute, $htmlOptions);
1711:         $selection = parent::resolveValue($model, $attribute);
1712:         if ($model->hasErrors($attribute)) {
1713:             parent::addErrorCss($htmlOptions);
1714:         }
1715:         $name = TbArray::popValue('name', $htmlOptions);
1716:         $unCheck = TbArray::popValue('uncheckValue', $htmlOptions, '');
1717:         $hiddenOptions = isset($htmlOptions['id']) ? array('id' => parent::ID_PREFIX . $htmlOptions['id']) : array('id' => false);
1718:         $hidden = $unCheck !== null ? parent::hiddenField($name, $unCheck, $hiddenOptions) : '';
1719:         return $hidden . self::checkBoxList($name, $selection, $data, $htmlOptions);
1720:     }
1721: 
1722:     /**
1723:      * Generates an inline check box list for a model attribute.
1724:      * @param CModel $model the data model.
1725:      * @param string $attribute the attribute.
1726:      * @param array $data $data value-label pairs used to generate the check box list.
1727:      * @param array $htmlOptions additional HTML attributes.
1728:      * @return string the generated list.
1729:      */
1730:     public static function activeInlineCheckBoxList($model, $attribute, $data, $htmlOptions = array())
1731:     {
1732:         $htmlOptions['inline'] = true;
1733:         return self::activeCheckBoxList($model, $attribute, $data, $htmlOptions);
1734:     }
1735: 
1736:     /**
1737:      * Generates an uneditable input for a model attribute.
1738:      * @param CModel $model the data model.
1739:      * @param string $attribute the attribute.
1740:      * @param array $htmlOptions additional HTML attributes.
1741:      * @return string the generated input.
1742:      */
1743:     public static function activeUneditableField($model, $attribute, $htmlOptions = array())
1744:     {
1745:         parent::resolveNameID($model, $attribute, $htmlOptions);
1746:         $value = parent::resolveValue($model, $attribute);
1747:         TbArray::removeValues(array('name', 'id'), $htmlOptions);
1748:         return self::uneditableField($value, $htmlOptions);
1749:     }
1750: 
1751:     /**
1752:      * Generates a search query input for a model attribute.
1753:      * @param CModel $model the data model.
1754:      * @param string $attribute the attribute.
1755:      * @param array $htmlOptions additional HTML attributes.
1756:      * @return string the generated input.
1757:      */
1758:     public static function activeSearchQueryField($model, $attribute, $htmlOptions = array())
1759:     {
1760:         self::addCssClass('search-query', $htmlOptions);
1761:         return self::activeTextField($model, $attribute, $htmlOptions);
1762:     }
1763: 
1764:     /**
1765:      * Generates a control group with a text field for a model attribute.
1766:      * @param CModel $model the data model.
1767:      * @param string $attribute the attribute.
1768:      * @param array $htmlOptions additional HTML attributes.
1769:      * @return string the generated control group.
1770:      * @see self::activeControlGroup
1771:      */
1772:     public static function activeTextFieldControlGroup($model, $attribute, $htmlOptions = array())
1773:     {
1774:         return self::activeControlGroup(self::INPUT_TYPE_TEXT, $model, $attribute, $htmlOptions);
1775:     }
1776: 
1777:     /**
1778:      * Generates a control group with a password field 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 control group.
1783:      * @see self::activeControlGroup
1784:      */
1785:     public static function activePasswordFieldControlGroup($model, $attribute, $htmlOptions = array())
1786:     {
1787:         return self::activeControlGroup(self::INPUT_TYPE_PASSWORD, $model, $attribute, $htmlOptions);
1788:     }
1789: 
1790:     /**
1791:      * Generates a control group with a url field for a model attribute.
1792:      * @param CModel $model the data model.
1793:      * @param string $attribute the attribute.
1794:      * @param array $htmlOptions additional HTML attributes.
1795:      * @return string the generated control group.
1796:      * @see self::activeControlGroup
1797:      */
1798:     public static function activeUrlFieldControlGroup($model, $attribute, $htmlOptions = array())
1799:     {
1800:         return self::activeControlGroup(self::INPUT_TYPE_URL, $model, $attribute, $htmlOptions);
1801:     }
1802: 
1803:     /**
1804:      * Generates a control group with a email field for a model attribute.
1805:      * @param CModel $model the data model.
1806:      * @param string $attribute the attribute.
1807:      * @param array $htmlOptions additional HTML attributes.
1808:      * @return string the generated control group.
1809:      * @see self::activeControlGroup
1810:      */
1811:     public static function activeEmailFieldControlGroup($model, $attribute, $htmlOptions = array())
1812:     {
1813:         return self::activeControlGroup(self::INPUT_TYPE_EMAIL, $model, $attribute, $htmlOptions);
1814:     }
1815: 
1816:     /**
1817:      * Generates a control group with a number field for a model attribute.
1818:      * @param CModel $model the data model.
1819:      * @param string $attribute the attribute.
1820:      * @param array $htmlOptions additional HTML attributes.
1821:      * @return string the generated control group.
1822:      * @see self::activeControlGroup
1823:      */
1824:     public static function activeNumberFieldControlGroup($model, $attribute, $htmlOptions = array())
1825:     {
1826:         return self::activeControlGroup(self::INPUT_TYPE_NUMBER, $model, $attribute, $htmlOptions);
1827:     }
1828: 
1829:     /**
1830:      * Generates a control group with a range field for a model attribute.
1831:      * @param CModel $model the data model.
1832:      * @param string $attribute the attribute.
1833:      * @param array $htmlOptions additional HTML attributes.
1834:      * @return string the generated control group.
1835:      * @see self::activeControlGroup
1836:      */
1837:     public static function activeRangeFieldControlGroup($model, $attribute, $htmlOptions = array())
1838:     {
1839:         return self::activeControlGroup(self::INPUT_TYPE_RANGE, $model, $attribute, $htmlOptions);
1840:     }
1841: 
1842:     /**
1843:      * Generates a control group with a date field for a model attribute.
1844:      * @param CModel $model the data model.
1845:      * @param string $attribute the attribute.
1846:      * @param array $htmlOptions additional HTML attributes.
1847:      * @return string the generated control group.
1848:      * @see self::activeControlGroup
1849:      */
1850:     public static function activeDateFieldControlGroup($model, $attribute, $htmlOptions = array())
1851:     {
1852:         return self::activeControlGroup(self::INPUT_TYPE_DATE, $model, $attribute, $htmlOptions);
1853:     }
1854: 
1855:     /**
1856:      * Generates a control group with a text area for a model attribute.
1857:      * @param CModel $model the data model.
1858:      * @param string $attribute the attribute.
1859:      * @param array $htmlOptions additional HTML attributes.
1860:      * @return string the generated control group.
1861:      * @see self::activeControlGroup
1862:      */
1863:     public static function activeTextAreaControlGroup($model, $attribute, $htmlOptions = array())
1864:     {
1865:         return self::activeControlGroup(self::INPUT_TYPE_TEXTAREA, $model, $attribute, $htmlOptions);
1866:     }
1867: 
1868:     /**
1869:      * Generates a control group with a file field for a model attribute.
1870:      * @param CModel $model the data model.
1871:      * @param string $attribute the attribute.
1872:      * @param array $htmlOptions additional HTML attributes.
1873:      * @return string the generated control group.
1874:      * @see self::activeControlGroup
1875:      */
1876:     public static function activeFileFieldControlGroup($model, $attribute, $htmlOptions = array())
1877:     {
1878:         return self::activeControlGroup(self::INPUT_TYPE_FILE, $model, $attribute, $htmlOptions);
1879:     }
1880: 
1881:     /**
1882:      * Generates a control group with a radio button for a model attribute.
1883:      * @param CModel $model the data model.
1884:      * @param string $attribute the attribute.
1885:      * @param array $htmlOptions additional HTML attributes.
1886:      * @return string the generated control group.
1887:      * @see self::activeControlGroup
1888:      */
1889:     public static function activeRadioButtonControlGroup($model, $attribute, $htmlOptions = array())
1890:     {
1891:         return self::activeControlGroup(self::INPUT_TYPE_RADIOBUTTON, $model, $attribute, $htmlOptions);
1892:     }
1893: 
1894:     /**
1895:      * Generates a control group with a check box for a model attribute.
1896:      * @param string $name the input name.
1897:      * @param string $checked whether the check box is checked.
1898:      * @param array $htmlOptions additional HTML attributes.
1899:      * @return string the generated control group.
1900:      * @see self::activeControlGroup
1901:      */
1902:     public static function activeCheckBoxControlGroup($model, $attribute, $htmlOptions = array())
1903:     {
1904:         return self::activeControlGroup(self::INPUT_TYPE_CHECKBOX, $model, $attribute, $htmlOptions);
1905:     }
1906: 
1907:     /**
1908:      * Generates a control group with a drop down list for a model attribute.
1909:      * @param CModel $model the data model.
1910:      * @param string $attribute the attribute.
1911:      * @param array $data data for generating the list options (value=>display).
1912:      * @param array $htmlOptions additional HTML attributes.
1913:      * @return string the generated control group.
1914:      * @see self::activeControlGroup
1915:      */
1916:     public static function activeDropDownListControlGroup($model, $attribute, $data = array(), $htmlOptions = array())
1917:     {
1918:         return self::activeControlGroup(self::INPUT_TYPE_DROPDOWNLIST, $model, $attribute, $htmlOptions, $data);
1919:     }
1920: 
1921:     /**
1922:      * Generates a control group with a list box for a model attribute.
1923:      * @param string $name the input name.
1924:      * @param string $select the selected value.
1925:      * @param array $data data for generating the list options (value=>display).
1926:      * @param array $htmlOptions additional HTML attributes.
1927:      * @return string the generated control group.
1928:      * @see self::activeControlGroup
1929:      */
1930:     public static function activeListBoxControlGroup($model, $attribute, $data = array(), $htmlOptions = array())
1931:     {
1932:         return self::activeControlGroup(self::INPUT_TYPE_LISTBOX, $model, $attribute, $htmlOptions, $data);
1933:     }
1934: 
1935:     /**
1936:      * Generates a control group with a radio button list for a model attribute.
1937:      * @param CModel $model the data model.
1938:      * @param string $attribute the attribute.
1939:      * @param array $data data for generating the list options (value=>display).
1940:      * @param array $htmlOptions additional HTML attributes.
1941:      * @return string the generated control group.
1942:      * @see self::activeControlGroup
1943:      */
1944:     public static function activeRadioButtonListControlGroup(
1945:         $model,
1946:         $attribute,
1947:         $data = array(),
1948:         $htmlOptions = array()
1949:     ) {
1950:         return self::activeControlGroup(self::INPUT_TYPE_RADIOBUTTONLIST, $model, $attribute, $htmlOptions, $data);
1951:     }
1952: 
1953:     /**
1954:      * Generates a control group with an inline radio button list for a model attribute.
1955:      * @param string $name the input name.
1956:      * @param string $select the selected value.
1957:      * @param array $data data for generating the list options (value=>display).
1958:      * @param array $htmlOptions additional HTML attributes.
1959:      * @return string the generated control group.
1960:      * @see self::activeControlGroup
1961:      */
1962:     public static function activeInlineRadioButtonListControlGroup(
1963:         $model,
1964:         $attribute,
1965:         $data = array(),
1966:         $htmlOptions = array()
1967:     ) {
1968:         return self::activeControlGroup(
1969:             self::INPUT_TYPE_INLINERADIOBUTTONLIST,
1970:             $model,
1971:             $attribute,
1972:             $htmlOptions,
1973:             $data
1974:         );
1975:     }
1976: 
1977:     /**
1978:      * Generates a control group with a check box list for a model attribute.
1979:      * @param CModel $model the data model.
1980:      * @param string $attribute the attribute.
1981:      * @param array $data data for generating the list options (value=>display).
1982:      * @param array $htmlOptions additional HTML attributes.
1983:      * @return string the generated control group.
1984:      * @see self::activeControlGroup
1985:      */
1986:     public static function activeCheckBoxListControlGroup($model, $attribute, $data = array(), $htmlOptions = array())
1987:     {
1988:         return self::activeControlGroup(self::INPUT_TYPE_CHECKBOXLIST, $model, $attribute, $htmlOptions, $data);
1989:     }
1990: 
1991:     /**
1992:      * Generates a control group with an inline check box list for a model attribute.
1993:      * @param CModel $model the data model.
1994:      * @param string $attribute the attribute.
1995:      * @param array $data data for generating the list options (value=>display).
1996:      * @param array $htmlOptions additional HTML attributes.
1997:      * @return string the generated control group.
1998:      * @see self::activeControlGroup
1999:      */
2000:     public static function activeInlineCheckBoxListControlGroup(
2001:         $model,
2002:         $attribute,
2003:         $data = array(),
2004:         $htmlOptions = array()
2005:     ) {
2006:         return self::activeControlGroup(self::INPUT_TYPE_INLINECHECKBOXLIST, $model, $attribute, $htmlOptions, $data);
2007:     }
2008: 
2009:     /**
2010:      * Generates a control group with a uneditable field for a model attribute.
2011:      * @param CModel $model the data model.
2012:      * @param string $attribute the attribute.
2013:      * @param array $htmlOptions additional HTML attributes.
2014:      * @return string the generated control group.
2015:      * @see self::activeControlGroup
2016:      */
2017:     public static function activeUneditableFieldControlGroup($model, $attribute, $htmlOptions = array())
2018:     {
2019:         return self::activeControlGroup(self::INPUT_TYPE_UNEDITABLE, $model, $attribute, $htmlOptions);
2020:     }
2021: 
2022:     /**
2023:      * Generates a control group with a search field for a model attribute.
2024:      * @param CModel $model the data model.
2025:      * @param string $attribute the attribute.
2026:      * @param array $htmlOptions additional HTML attributes.
2027:      * @return string the generated control group.
2028:      * @see self::activeControlGroup
2029:      */
2030:     public static function activeSearchQueryControlGroup($model, $attribute, $htmlOptions = array())
2031:     {
2032:         return self::activeControlGroup(self::INPUT_TYPE_SEARCH, $model, $attribute, $htmlOptions);
2033:     }
2034: 
2035:     /**
2036:      * Generates an active form row.
2037:      * @param string $type the input type.
2038:      * @param CModel $model the data model.
2039:      * @param string $attribute the attribute.
2040:      * @param array $htmlOptions additional HTML attributes.
2041:      * @param array $data data for multiple select inputs.
2042:      * @return string the generated control group.
2043:      */
2044:     public static function activeControlGroup($type, $model, $attribute, $htmlOptions = array(), $data = array())
2045:     {
2046:         $color = TbArray::popValue('color', $htmlOptions);
2047:         $groupOptions = TbArray::popValue('groupOptions', $htmlOptions, array());
2048:         $controlOptions = TbArray::popValue('controlOptions', $htmlOptions, array());
2049:         $label = TbArray::popValue('label', $htmlOptions);
2050:         $labelOptions = TbArray::popValue('labelOptions', $htmlOptions, array());
2051: 
2052:         if (in_array($type, array(self::INPUT_TYPE_CHECKBOX, self::INPUT_TYPE_RADIOBUTTON))) {
2053:             $htmlOptions['label'] = isset($label) ? $label : $model->getAttributeLabel($attribute);
2054:             $htmlOptions['labelOptions'] = $labelOptions;
2055:             $label = false;
2056:         }
2057:         if (isset($label) && $label !== false) {
2058:             $labelOptions['label'] = $label;
2059:         }
2060: 
2061:         $help = TbArray::popValue('help', $htmlOptions, '');
2062:         $helpOptions = TbArray::popValue('helpOptions', $htmlOptions, array());
2063:         if (!empty($help)) {
2064:             $help = self::inputHelp($help, $helpOptions);
2065:         }
2066:         $error = TbArray::popValue('error', $htmlOptions, '');
2067: 
2068:         $input = isset($htmlOptions['input'])
2069:             ? $htmlOptions['input']
2070:             : self::createActiveInput($type, $model, $attribute, $htmlOptions, $data);
2071: 
2072:         self::addCssClass('control-group', $groupOptions);
2073:         if (!empty($color)) {
2074:             self::addCssClass($color, $groupOptions);
2075:         }
2076:         self::addCssClass('control-label', $labelOptions);
2077:         $output = self::openTag('div', $groupOptions);
2078:         if ($label !== false) {
2079:             $output .= parent::activeLabelEx($model, $attribute, $labelOptions);
2080:         }
2081:         $output .= self::controls($input . $error . $help, $controlOptions);
2082:         $output .= '</div>';
2083:         return $output;
2084:     }
2085: 
2086:     /**
2087:      * Generates a custom (pre-rendered) active form control group.
2088:      * @param string $input the rendered input.
2089:      * @param CModel $model the data model.
2090:      * @param string $attribute the attribute.
2091:      * @param array $htmlOptions additional HTML attributes.
2092:      * @return string the generated control group.
2093:      */
2094:     public static function customActiveControlGroup($input, $model, $attribute, $htmlOptions = array())
2095:     {
2096:         $htmlOptions['input'] = $input;
2097:         return self::activeControlGroup(self::INPUT_TYPE_CUSTOM, $model, $attribute, $htmlOptions);
2098:     }
2099: 
2100:     /**
2101:      * Creates an active form input of the given type.
2102:      * @param string $type the input type.
2103:      * @param CModel $model the model instance.
2104:      * @param string $attribute the attribute name.
2105:      * @param array $htmlOptions additional HTML attributes.
2106:      * @param array $data data for multiple select inputs.
2107:      * @return string the input.
2108:      * @throws CException if the input type is invalid.
2109:      */
2110:     public static function createActiveInput($type, $model, $attribute, $htmlOptions = array(), $data = array())
2111:     {
2112:         switch ($type) {
2113:             case self::INPUT_TYPE_TEXT:
2114:                 return self::activeTextField($model, $attribute, $htmlOptions);
2115:             case self::INPUT_TYPE_PASSWORD:
2116:                 return self::activePasswordField($model, $attribute, $htmlOptions);
2117:             case self::INPUT_TYPE_URL:
2118:                 return self::activeUrlField($model, $attribute, $htmlOptions);
2119:             case self::INPUT_TYPE_EMAIL:
2120:                 return self::activeEmailField($model, $attribute, $htmlOptions);
2121:             case self::INPUT_TYPE_NUMBER:
2122:                 return self::activeNumberField($model, $attribute, $htmlOptions);
2123:             case self::INPUT_TYPE_RANGE:
2124:                 return self::activeRangeField($model, $attribute, $htmlOptions);
2125:             case self::INPUT_TYPE_DATE:
2126:                 return self::activeDateField($model, $attribute, $htmlOptions);
2127:             case self::INPUT_TYPE_TEXTAREA:
2128:                 return self::activeTextArea($model, $attribute, $htmlOptions);
2129:             case self::INPUT_TYPE_FILE:
2130:                 return self::activeFileField($model, $attribute, $htmlOptions);
2131:             case self::INPUT_TYPE_RADIOBUTTON:
2132:                 return self::activeRadioButton($model, $attribute, $htmlOptions);
2133:             case self::INPUT_TYPE_CHECKBOX:
2134:                 return self::activeCheckBox($model, $attribute, $htmlOptions);
2135:             case self::INPUT_TYPE_DROPDOWNLIST:
2136:                 return self::activeDropDownList($model, $attribute, $data, $htmlOptions);
2137:             case self::INPUT_TYPE_LISTBOX:
2138:                 return self::activeListBox($model, $attribute, $data, $htmlOptions);
2139:             case self::INPUT_TYPE_CHECKBOXLIST:
2140:                 return self::activeCheckBoxList($model, $attribute, $data, $htmlOptions);
2141:             case self::INPUT_TYPE_INLINECHECKBOXLIST:
2142:                 return self::activeInlineCheckBoxList($model, $attribute, $data, $htmlOptions);
2143:             case self::INPUT_TYPE_RADIOBUTTONLIST:
2144:                 return self::activeRadioButtonList($model, $attribute, $data, $htmlOptions);
2145:             case self::INPUT_TYPE_INLINERADIOBUTTONLIST:
2146:                 return self::activeInlineRadioButtonList($model, $attribute, $data, $htmlOptions);
2147:             case self::INPUT_TYPE_UNEDITABLE:
2148:                 return self::activeUneditableField($model, $attribute, $htmlOptions);
2149:             case self::INPUT_TYPE_SEARCH:
2150:                 return self::activeSearchQueryField($model, $attribute, $htmlOptions);
2151:             default:
2152:                 throw new CException('Invalid input type "' . $type . '".');
2153:         }
2154:     }
2155: 
2156:     /**
2157:      * Displays a summary of validation errors for one or several models.
2158:      * @param mixed $model the models whose input errors are to be displayed.
2159:      * @param string $header a piece of HTML code that appears in front of the errors.
2160:      * @param string $footer a piece of HTML code that appears at the end of the errors.
2161:      * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
2162:      * @return string the error summary. Empty if no errors are found.
2163:      */
2164:     public static function errorSummary($model, $header = null, $footer = null, $htmlOptions = array())
2165:     {
2166:         // kind of a quick fix but it will do for now.
2167:         self::addCssClass(self::$errorSummaryCss, $htmlOptions);
2168:         return parent::errorSummary($model, $header, $footer, $htmlOptions);
2169:     }
2170: 
2171:     /**
2172:      * Displays the first validation error for a model attribute.
2173:      * @param CModel $model the data model.
2174:      * @param string $attribute the attribute name.
2175:      * @param array $htmlOptions additional HTML attributes.
2176:      * @return string the rendered error. Empty if no errors are found.
2177:      */
2178:     public static function error($model, $attribute, $htmlOptions = array())
2179:     {
2180:         parent::resolveName($model, $attribute); // turn [a][b]attr into attr
2181:         $error = $model->getError($attribute);
2182:         return !empty($error) ? self::help($error, $htmlOptions) : '';
2183:     }
2184: 
2185:     /**
2186:      * Generates an input HTML tag  for a model attribute.
2187:      * This method generates an input HTML tag based on the given input name and value.
2188:      * @param string $type the input type.
2189:      * @param CModel $model the data model.
2190:      * @param string $attribute the attribute.
2191:      * @param array $htmlOptions additional HTML attributes.
2192:      * @return string the generated input tag.
2193:      */
2194:     protected static function activeTextInputField($type, $model, $attribute, $htmlOptions)
2195:     {
2196:         parent::resolveNameID($model, $attribute, $htmlOptions);
2197:         parent::clientChange('change', $htmlOptions);
2198: 
2199:         $htmlOptions = self::normalizeInputOptions($htmlOptions);
2200: 
2201:         $addOnClasses = self::getAddOnClasses($htmlOptions);
2202:         $addOnOptions = TbArray::popValue('addOnOptions', $htmlOptions, array());
2203:         self::addCssClass($addOnClasses, $addOnOptions);
2204: 
2205:         $prepend = TbArray::popValue('prepend', $htmlOptions, '');
2206:         $prependOptions = TbArray::popValue('prependOptions', $htmlOptions, array());
2207:         if (!empty($prepend)) {
2208:             $prepend = self::inputAddOn($prepend, $prependOptions);
2209:         }
2210: 
2211:         $append = TbArray::popValue('append', $htmlOptions, '');
2212:         $appendOptions = TbArray::popValue('appendOptions', $htmlOptions, array());
2213:         if (!empty($append)) {
2214:             $append = self::inputAddOn($append, $appendOptions);
2215:         }
2216: 
2217:         $output = '';
2218:         if (!empty($addOnClasses)) {
2219:             $output .= self::openTag('div', $addOnOptions);
2220:         }
2221:         $output .= $prepend . parent::activeInputField($type, $model, $attribute, $htmlOptions) . $append;
2222:         if (!empty($addOnClasses)) {
2223:             $output .= '</div>';
2224:         }
2225:         return $output;
2226:     }
2227: 
2228:     /**
2229:      * Returns the add-on classes based on the given options.
2230:      * @param array $htmlOptions the options.
2231:      * @return string the classes.
2232:      */
2233:     protected static function getAddOnClasses($htmlOptions)
2234:     {
2235:         $classes = array();
2236:         if (TbArray::getValue('append', $htmlOptions)) {
2237:             $classes[] = 'input-append';
2238:         }
2239:         if (TbArray::getValue('prepend', $htmlOptions)) {
2240:             $classes[] = 'input-prepend';
2241:         }
2242:         return !empty($classes) ? implode(' ', $classes) : $classes;
2243:     }
2244: 
2245:     /**
2246:      * Generates an add-on for an input field.
2247:      * @param string $addOn the add-on.
2248:      * @param array $htmlOptions additional HTML attributes.
2249:      * @return string the generated add-on.
2250:      */
2251:     protected static function inputAddOn($addOn, $htmlOptions)
2252:     {
2253:         $addOnOptions = TbArray::popValue('addOnOptions', $htmlOptions, array());
2254:         self::addCssClass('add-on', $addOnOptions);
2255:         return strpos($addOn, 'btn') === false // buttons should not be wrapped in a span
2256:             ? self::tag('span', $addOnOptions, $addOn)
2257:             : $addOn;
2258:     }
2259: 
2260:     /**
2261:      * Generates a help text for an input field.
2262:      * @param string $help the help text.
2263:      * @param array $htmlOptions additional HTML attributes.
2264:      * @return string the generated help text.
2265:      */
2266:     protected static function inputHelp($help, $htmlOptions)
2267:     {
2268:         $type = TbArray::popValue('type', $htmlOptions, self::HELP_TYPE_INLINE);
2269:         return $type === self::HELP_TYPE_INLINE
2270:             ? self::help($help, $htmlOptions)
2271:             : self::helpBlock($help, $htmlOptions);
2272:     }
2273: 
2274:     /**
2275:      * Normalizes input options.
2276:      * @param array $options the options.
2277:      * @return array the normalized options.
2278:      */
2279:     protected static function normalizeInputOptions($options)
2280:     {
2281:         self::addSpanClass($options);
2282:         self::addTextAlignClass($options);
2283:         $size = TbArray::popValue('size', $options);
2284:         if (TbArray::popValue('block', $options, false)) {
2285:             self::addCssClass('input-block-level', $options);
2286:         } else {
2287:             if (!empty($size)) {
2288:                 self::addCssClass('input-' . $size, $options);
2289:             }
2290:         }
2291:         return $options;
2292:     }
2293: 
2294:     /**
2295:      * Generates form controls.
2296:      * @param mixed $controls the controls.
2297:      * @param array $htmlOptions additional HTML attributes.
2298:      * @return string the generated controls.
2299:      */
2300:     public static function controls($controls, $htmlOptions = array())
2301:     {
2302:         self::addCssClass('controls', $htmlOptions);
2303:         if (TbArray::popValue('row', $htmlOptions, false)) {
2304:             self::addCssClass('controls-row', $htmlOptions);
2305:         }
2306:         $before = TbArray::popValue('before', $htmlOptions, '');
2307:         $after = TbArray::popValue('after', $htmlOptions, '');
2308:         if (is_array($controls)) {
2309:             $controls = implode('', $controls);
2310:         }
2311:         $content = $before . $controls . $after;
2312:         return self::tag('div', $htmlOptions, $content);
2313:     }
2314: 
2315:     /**
2316:      * Generates form controls row.
2317:      * @param mixed $controls the controls.
2318:      * @param array $htmlOptions additional HTML attributes.
2319:      * @return string the generated controls.
2320:      */
2321:     public static function controlsRow($controls, $htmlOptions = array())
2322:     {
2323:         $htmlOptions['row'] = true;
2324:         return self::controls($controls, $htmlOptions);
2325:     }
2326: 
2327:     /**
2328:      * Generates form actions.
2329:      * @param mixed $actions the actions.
2330:      * @param array $htmlOptions additional HTML attributes.
2331:      * @return string the generated actions.
2332:      */
2333:     public static function formActions($actions, $htmlOptions = array())
2334:     {
2335:         self::addCssClass('form-actions', $htmlOptions);
2336:         if (is_array($actions)) {
2337:             $actions = implode(' ', $actions);
2338:         }
2339:         return self::tag('div', $htmlOptions, $actions);
2340:     }
2341: 
2342:     /**
2343:      * Generates a search form.
2344:      * @param mixed $action the form action URL.
2345:      * @param string $method form method (e.g. post, get).
2346:      * @param array $htmlOptions additional HTML options.
2347:      * @return string the generated form.
2348:      */
2349:     public static function searchForm($action, $method = 'post', $htmlOptions = array())
2350:     {
2351:         self::addCssClass('form-search', $htmlOptions);
2352:         $inputOptions = TbArray::popValue('inputOptions', $htmlOptions, array());
2353:         $inputOptions = TbArray::merge(array('type' => 'text', 'placeholder' => 'Search'), $inputOptions);
2354:         $name = TbArray::popValue('name', $inputOptions, 'search');
2355:         $value = TbArray::popValue('value', $inputOptions, '');
2356:         $output = self::beginFormTb(self::FORM_LAYOUT_SEARCH, $action, $method, $htmlOptions);
2357:         $output .= self::searchQueryField($name, $value, $inputOptions);
2358:         $output .= parent::endForm();
2359:         return $output;
2360:     }
2361: 
2362:     // Buttons
2363:     // http://twitter.github.io/bootstrap/2.3.2/base-css.html#buttons
2364:     // --------------------------------------------------
2365: 
2366:     /**
2367:      * Generates a hyperlink tag.
2368:      * @param string $text link body. It will NOT be HTML-encoded.
2369:      * @param mixed $url a URL or an action route that can be used to create a URL.
2370:      * @param array $htmlOptions additional HTML attributes.
2371:      * @return string the generated hyperlink
2372:      */
2373:     public static function link($text, $url = '#', $htmlOptions = array())
2374:     {
2375:         $htmlOptions['href'] = parent::normalizeUrl($url);
2376:         self::clientChange('click', $htmlOptions);
2377:         return self::tag('a', $htmlOptions, $text);
2378:     }
2379: 
2380:     /**
2381:      * Generates an button.
2382:      * @param string $label the button label text.
2383:      * @param array $htmlOptions additional HTML attributes.
2384:      * @return string the generated button.
2385:      */
2386:     public static function button($label = 'Button', $htmlOptions = array())
2387:     {
2388:         return self::htmlButton($label, $htmlOptions);
2389:     }
2390: 
2391:     /**
2392:      * Generates an image submit button.
2393:      * @param string $src the image URL
2394:      * @param array $htmlOptions additional HTML attributes.
2395:      * @return string the generated button.
2396:      */
2397:     public static function htmlButton($label = 'Button', $htmlOptions = array())
2398:     {
2399:         return self::btn(self::BUTTON_TYPE_HTML, $label, $htmlOptions);
2400:     }
2401: 
2402:     /**
2403:      * Generates a submit button.
2404:      * @param string $label the button label
2405:      * @param array $htmlOptions additional HTML attributes.
2406:      * @return string the generated button.
2407:      */
2408:     public static function submitButton($label = 'Submit', $htmlOptions = array())
2409:     {
2410:         return self::btn(self::BUTTON_TYPE_SUBMIT, $label, $htmlOptions);
2411:     }
2412: 
2413:     /**
2414:      * Generates a reset button.
2415:      * @param string $label the button label
2416:      * @param array $htmlOptions additional HTML attributes.
2417:      * @return string the generated button.
2418:      */
2419:     public static function resetButton($label = 'Reset', $htmlOptions = array())
2420:     {
2421:         return self::btn(self::BUTTON_TYPE_RESET, $label, $htmlOptions);
2422:     }
2423: 
2424:     /**
2425:      * Generates an image submit button.
2426:      * @param string $src the image URL
2427:      * @param array $htmlOptions additional HTML attributes.
2428:      * @return string the generated button.
2429:      */
2430:     public static function imageButton($src, $htmlOptions = array())
2431:     {
2432:         return self::btn(self::BUTTON_TYPE_IMAGE, $src, $htmlOptions);
2433:     }
2434: 
2435:     /**
2436:      * Generates a link submit button.
2437:      * @param string $label the button label.
2438:      * @param array $htmlOptions additional HTML attributes.
2439:      * @return string the generated button tag.
2440:      */
2441:     public static function linkButton($label = 'Submit', $htmlOptions = array())
2442:     {
2443:         return self::btn(self::BUTTON_TYPE_LINK, $label, $htmlOptions);
2444:     }
2445: 
2446:     /**
2447:      * Generates a link that can initiate AJAX requests.
2448:      * @param string $text the link body (it will NOT be HTML-encoded.)
2449:      * @param mixed $url the URL for the AJAX request.
2450:      * @param array $ajaxOptions AJAX options.
2451:      * @param array $htmlOptions additional HTML attributes.
2452:      * @return string the generated link.
2453:      */
2454:     public static function ajaxLink($text, $url, $ajaxOptions = array(), $htmlOptions = array())
2455:     {
2456:         if (!isset($htmlOptions['href'])) {
2457:             $htmlOptions['href'] = '#';
2458:         }
2459:         $ajaxOptions['url'] = $url;
2460:         $htmlOptions['ajax'] = $ajaxOptions;
2461:         parent::clientChange('click', $htmlOptions);
2462:         return self::tag('a', $htmlOptions, $text);
2463:     }
2464: 
2465:     /**
2466:      * Generates a push button that can initiate AJAX requests.
2467:      * @param string $label the button label.
2468:      * @param mixed $url the URL for the AJAX request.
2469:      * @param array $ajaxOptions AJAX options.
2470:      * @param array $htmlOptions additional HTML attributes.
2471:      * @return string the generated button.
2472:      */
2473:     public static function ajaxButton($label, $url, $ajaxOptions = array(), $htmlOptions = array())
2474:     {
2475:         $ajaxOptions['url'] = $url;
2476:         $htmlOptions['ajaxOptions'] = $ajaxOptions;
2477:         return self::btn(self::BUTTON_TYPE_AJAXBUTTON, $label, $htmlOptions);
2478:     }
2479: 
2480:     /**
2481:      * Generates a push button that can submit the current form in POST method.
2482:      * @param string $label the button label
2483:      * @param mixed $url the URL for the AJAX request.
2484:      * @param array $ajaxOptions AJAX options.
2485:      * @param array $htmlOptions additional HTML attributes.
2486:      * @return string the generated button.
2487:      */
2488:     public static function ajaxSubmitButton($label, $url, $ajaxOptions = array(), $htmlOptions = array())
2489:     {
2490:         $ajaxOptions['type'] = 'POST';
2491:         $htmlOptions['type'] = 'submit';
2492:         return self::ajaxButton($label, $url, $ajaxOptions, $htmlOptions);
2493:     }
2494: 
2495:     // todo: add methods for input button and input submit.
2496: 
2497:     /**
2498:      * Generates a button.
2499:      * @param string $type the button type.
2500:      * @param string $label the button label text.
2501:      * @param array $htmlOptions additional HTML attributes.
2502:      * @return string the generated button.
2503:      */
2504:     public static function btn($type, $label, $htmlOptions = array())
2505:     {
2506:         self::addCssClass('btn', $htmlOptions);
2507:         $color = TbArray::popValue('color', $htmlOptions);
2508:         if (!empty($color)) {
2509:             self::addCssClass('btn-' . $color, $htmlOptions);
2510:         }
2511:         $size = TbArray::popValue('size', $htmlOptions);
2512:         if (!empty($size)) {
2513:             self::addCssClass('btn-' . $size, $htmlOptions);
2514:         }
2515:         if (TbArray::popValue('block', $htmlOptions, false)) {
2516:             self::addCssClass('btn-block', $htmlOptions);
2517:         }
2518:         if (TbArray::popValue('disabled', $htmlOptions, false)) {
2519:             self::addCssClass('disabled', $htmlOptions);
2520:         }
2521:         $loading = TbArray::popValue('loading', $htmlOptions);
2522:         if (!empty($loading)) {
2523:             $htmlOptions['data-loading-text'] = $loading;
2524:         }
2525:         if (TbArray::popValue('toggle', $htmlOptions, false)) {
2526:             $htmlOptions['data-toggle'] = 'button';
2527:         }
2528:         $icon = TbArray::popValue('icon', $htmlOptions);
2529:         $iconOptions = TbArray::popValue('iconOptions', $htmlOptions, array());
2530:         if (strpos($type, 'input') === false) {
2531:             if (!empty($icon)) {
2532:                 $label = self::icon($icon, $iconOptions) . ' ' . $label;
2533:             }
2534:             $items = TbArray::popValue('items', $htmlOptions);
2535:         }
2536:         $dropdownOptions = $htmlOptions;
2537:         TbArray::removeValues(array('groupOptions', 'menuOptions', 'dropup'), $htmlOptions);
2538:         self::addSpanClass($htmlOptions); // must be called here as parent renders buttons
2539:         self::addPullClass($htmlOptions); // must be called here as parent renders buttons
2540:         return isset($items)
2541:             ? self::btnDropdown($type, $label, $items, $dropdownOptions)
2542:             : self::createButton($type, $label, $htmlOptions);
2543:     }
2544: 
2545:     /**
2546:      * Generates a button dropdown.
2547:      * @param string $type the button type.
2548:      * @param string $label the button label text.
2549:      * @param array $items the menu items.
2550:      * @param array $htmlOptions additional HTML attributes.
2551:      * @return string the generated button.
2552:      */
2553:     protected static function btnDropdown($type, $label, $items, $htmlOptions)
2554:     {
2555:         $menuOptions = TbArray::popValue('menuOptions', $htmlOptions, array());
2556:         $groupOptions = TbArray::popValue('groupOptions', $htmlOptions, array());
2557:         self::addCssClass('btn-group', $groupOptions);
2558:         if (TbArray::popValue('dropup', $htmlOptions, false)) {
2559:             self::addCssClass('dropup', $groupOptions);
2560:         }
2561:         $output = self::openTag('div', $groupOptions);
2562:         if (TbArray::popValue('split', $htmlOptions, false)) {
2563:             $output .= self::createButton($type, $label, $htmlOptions);
2564:             $output .= self::dropdownToggleButton('', $htmlOptions);
2565:         } else {
2566:             $output .= self::dropdownToggleLink($label, $htmlOptions);
2567:         }
2568:         $output .= self::dropdown($items, $menuOptions);
2569:         $output .= '</div>';
2570:         return $output;
2571:     }
2572: 
2573:     /**
2574:      * Creates a button the of given type.
2575:      * @param string $type the button type.
2576:      * @param string $label the button label.
2577:      * @param array $htmlOptions additional HTML attributes.
2578:      * @return string the button.
2579:      * @throws CException if the button type is valid.
2580:      */
2581:     protected static function createButton($type, $label, $htmlOptions)
2582:     {
2583:         $url = TbArray::popValue('url', $htmlOptions, '#');
2584:         $ajaxOptions = TbArray::popValue('ajaxOptions', $htmlOptions, array());
2585:         switch ($type) {
2586:             case self::BUTTON_TYPE_HTML:
2587:                 return parent::htmlButton($label, $htmlOptions);
2588: 
2589:             case self::BUTTON_TYPE_SUBMIT:
2590:                 $htmlOptions['type'] = 'submit';
2591:                 return parent::htmlButton($label, $htmlOptions);
2592: 
2593:             case self::BUTTON_TYPE_RESET:
2594:                 $htmlOptions['type'] = 'reset';
2595:                 return parent::htmlButton($label, $htmlOptions);
2596: 
2597:             case self::BUTTON_TYPE_IMAGE:
2598:                 return parent::imageButton($label, $htmlOptions);
2599: 
2600:             case self::BUTTON_TYPE_LINKBUTTON:
2601:                 return parent::linkButton($label, $htmlOptions);
2602: 
2603:             case self::BUTTON_TYPE_AJAXLINK:
2604:                 return parent::ajaxLink($label, $url, $ajaxOptions, $htmlOptions);
2605: 
2606:             case self::BUTTON_TYPE_AJAXBUTTON:
2607:                 $htmlOptions['ajax'] = $ajaxOptions;
2608:                 return parent::htmlButton($label, $htmlOptions);
2609: 
2610:             case self::BUTTON_TYPE_INPUTBUTTON:
2611:                 return parent::button($label, $htmlOptions);
2612: 
2613:             case self::BUTTON_TYPE_INPUTSUBMIT:
2614:                 $htmlOptions['type'] = 'submit';
2615:                 return parent::button($label, $htmlOptions);
2616: 
2617:             case self::BUTTON_TYPE_LINK:
2618:                 return self::link($label, $url, $htmlOptions);
2619: 
2620:             default:
2621:                 throw new CException('Invalid button type "' . $type . '".');
2622:         }
2623:     }
2624: 
2625:     // Images
2626:     // http://twitter.github.io/bootstrap/2.3.2/base-css.html#images
2627:     // --------------------------------------------------
2628: 
2629:     /**
2630:      * Generates an image tag with rounded corners.
2631:      * @param string $src the image URL.
2632:      * @param string $alt the alternative text display.
2633:      * @param array $htmlOptions additional HTML attributes.
2634:      * @return string the generated image tag.
2635:      */
2636:     public static function imageRounded($src, $alt = '', $htmlOptions = array())
2637:     {
2638:         $htmlOptions['type'] = self::IMAGE_TYPE_ROUNDED;
2639:         return self::image($src, $alt, $htmlOptions);
2640:     }
2641: 
2642:     /**
2643:      * Generates an image tag with circle.
2644:      * @param string $src the image URL.
2645:      * @param string $alt the alternative text display.
2646:      * @param array $htmlOptions additional HTML attributes.
2647:      * @return string the generated image tag.
2648:      */
2649:     public static function imageCircle($src, $alt = '', $htmlOptions = array())
2650:     {
2651:         $htmlOptions['type'] = self::IMAGE_TYPE_CIRCLE;
2652:         return self::image($src, $alt, $htmlOptions);
2653:     }
2654: 
2655:     /**
2656:      * Generates an image tag within polaroid frame.
2657:      * @param string $src the image URL.
2658:      * @param string $alt the alternative text display.
2659:      * @param array $htmlOptions additional HTML attributes.
2660:      * @return string the generated image tag.
2661:      */
2662:     public static function imagePolaroid($src, $alt = '', $htmlOptions = array())
2663:     {
2664:         $htmlOptions['type'] = self::IMAGE_TYPE_POLAROID;
2665:         return self::image($src, $alt, $htmlOptions);
2666:     }
2667: 
2668:     /**
2669:      * Generates an image tag.
2670:      * @param string $src the image URL.
2671:      * @param string $alt the alternative text display.
2672:      * @param array $htmlOptions additional HTML attributes.
2673:      * @return string the generated image tag.
2674:      */
2675:     public static function image($src, $alt = '', $htmlOptions = array())
2676:     {
2677:         $type = TbArray::popValue('type', $htmlOptions);
2678:         if (!empty($type)) {
2679:             self::addCssClass('img-' . $type, $htmlOptions);
2680:         }
2681:         return parent::image($src, $alt, $htmlOptions);
2682:     }
2683: 
2684:     // Icons by Glyphicons
2685:     // http://twitter.github.io/bootstrap/2.3.2/base-css.html#icons
2686:     // --------------------------------------------------
2687: 
2688:     /**
2689:      * Generates an icon.
2690:      * @param string $icon the icon type.
2691:      * @param array $htmlOptions additional HTML attributes.
2692:      * @param string $tagName the icon HTML tag.
2693:      * @return string the generated icon.
2694:      */
2695:     public static function icon($icon, $htmlOptions = array(), $tagName = 'i')
2696:     {
2697:         if (is_string($icon)) {
2698:             if (strpos($icon, 'icon') === false) {
2699:                 $icon = 'icon-' . implode(' icon-', explode(' ', $icon));
2700:             }
2701:             self::addCssClass($icon, $htmlOptions);
2702:             $color = TbArray::popValue('color', $htmlOptions);
2703:             if (!empty($color) && $color === self::ICON_COLOR_WHITE) {
2704:                 self::addCssClass('icon-white', $htmlOptions);
2705:             }
2706:             return self::openTag($tagName, $htmlOptions) . parent::closeTag($tagName); // tag won't work in this case
2707:         }
2708:         return '';
2709:     }
2710: 
2711:     //
2712:     // COMPONENTS
2713:     // --------------------------------------------------
2714: 
2715:     // Dropdowns
2716:     // http://twitter.github.io/bootstrap/2.3.2/components.html#dropdowns
2717:     // --------------------------------------------------
2718: 
2719:     /**
2720:      * Generates a dropdown menu.
2721:      * @param array $items the menu items.
2722:      * @param array $htmlOptions additional HTML attributes.
2723:      * @return string the generated menu.
2724:      */
2725:     protected static function dropdown($items, $htmlOptions = array())
2726:     {
2727:         TbArray::defaultValue('role', 'menu', $htmlOptions);
2728:         self::addCssClass('dropdown-menu', $htmlOptions);
2729:         return self::menu($items, $htmlOptions);
2730:     }
2731: 
2732:     /**
2733:      * Generates a dropdown toggle link.
2734:      * @param string $label the link label text.
2735:      * @param array $htmlOptions additional HTML attributes.
2736:      * @return string the generated link.
2737:      */
2738:     public static function dropdownToggleLink($label, $htmlOptions = array())
2739:     {
2740:         return self::dropdownToggle(self::BUTTON_TYPE_LINK, $label, $htmlOptions);
2741:     }
2742: 
2743:     /**
2744:      * Generates a dropdown toggle button.
2745:      * @param string $label the button label text.
2746:      * @param array $htmlOptions additional HTML attributes.
2747:      * @return string the generated button.
2748:      */
2749:     public static function dropdownToggleButton($label = '', $htmlOptions = array())
2750:     {
2751:         return self::dropdownToggle(self::BUTTON_TYPE_HTML, $label, $htmlOptions);
2752:     }
2753: 
2754:     /**
2755:      * Generates a dropdown toggle element.
2756:      * @param string $tag the HTML tag.
2757:      * @param string $label the element text.
2758:      * @param array $htmlOptions additional HTML attributes.
2759:      * @return string the generated element.
2760:      */
2761:     protected static function dropdownToggle($type, $label, $htmlOptions)
2762:     {
2763:         self::addCssClass('dropdown-toggle', $htmlOptions);
2764:         $label .= ' <b class="caret"></b>';
2765:         $htmlOptions['data-toggle'] = 'dropdown';
2766:         return self::btn($type, $label, $htmlOptions);
2767:     }
2768: 
2769:     /**
2770:      * Generates a dropdown toggle menu item.
2771:      * @param string $label the menu item text.
2772:      * @param string $url the menu item URL.
2773:      * @param array $htmlOptions additional HTML attributes.
2774:      * @param int $depth the menu depth at which this link is located
2775:      * @return string the generated menu item.
2776:      */
2777:     public static function dropdownToggleMenuLink($label, $url = '#', $htmlOptions = array(), $depth = 0)
2778:     {
2779:         self::addCssClass('dropdown-toggle', $htmlOptions);
2780:         if ($depth === 0) {
2781:             $label .= ' <b class="caret"></b>';
2782:         }
2783:         $htmlOptions['data-toggle'] = 'dropdown';
2784:         return self::link($label, $url, $htmlOptions);
2785:     }
2786: 
2787:     // Button groups
2788:     // http://twitter.github.io/bootstrap/2.3.2/components.html#buttonGroups
2789:     // --------------------------------------------------
2790: 
2791:     /**
2792:      * Generates a button group.
2793:      * @param array $buttons the button configurations.
2794:      * @param array $htmlOptions additional HTML options.
2795:      * @return string the generated button group.
2796:      */
2797:     public static function buttonGroup(array $buttons, $htmlOptions = array())
2798:     {
2799:         if (!empty($buttons)) {
2800:             self::addCssClass('btn-group', $htmlOptions);
2801:             if (TbArray::popValue('vertical', $htmlOptions, false)) {
2802:                 self::addCssClass('btn-group-vertical', $htmlOptions);
2803:             }
2804:             $toggle = TbArray::popValue('toggle', $htmlOptions);
2805:             if (!empty($toggle)) {
2806:                 $htmlOptions['data-toggle'] = 'buttons-' . $toggle;
2807:             }
2808:             $parentOptions = array(
2809:                 'color' => TbArray::popValue('color', $htmlOptions),
2810:                 'size' => TbArray::popValue('size', $htmlOptions),
2811:                 'disabled' => TbArray::popValue('disabled', $htmlOptions)
2812:             );
2813:             $output = self::openTag('div', $htmlOptions);
2814:             foreach ($buttons as $buttonOptions) {
2815:                 if (isset($buttonOptions['visible']) && $buttonOptions['visible'] === false) {
2816:                     continue;
2817:                 }
2818:                 // todo: consider removing the support for htmlOptions.
2819:                 $options = TbArray::popValue('htmlOptions', $buttonOptions, array());
2820:                 if (!empty($options)) {
2821:                     $buttonOptions = TbArray::merge($options, $buttonOptions);
2822:                 }
2823:                 $buttonLabel = TbArray::popValue('label', $buttonOptions, '');
2824:                 $buttonOptions = TbArray::copyValues(array('color', 'size', 'disabled'), $parentOptions, $buttonOptions);
2825:                 $items = TbArray::popValue('items', $buttonOptions, array());
2826:                 if (!empty($items)) {
2827:                     $output .= self::buttonDropdown($buttonLabel, $items, $buttonOptions);
2828:                 } else {
2829:                     $output .= self::linkButton($buttonLabel, $buttonOptions);
2830:                 }
2831:             }
2832:             $output .= '</div>';
2833:             return $output;
2834:         }
2835:         return '';
2836:     }
2837: 
2838:     /**
2839:      * Generates a vertical button group.
2840:      * @param array $buttons the button configurations.
2841:      * @param array $htmlOptions additional HTML options.
2842:      * @return string the generated button group.
2843:      */
2844:     public static function verticalButtonGroup(array $buttons, $htmlOptions = array())
2845:     {
2846:         $htmlOptions['vertical'] = true;
2847:         return self::buttonGroup($buttons, $htmlOptions);
2848:     }
2849: 
2850:     /**
2851:      * Generates a button toolbar.
2852:      * @param array $groups the button group configurations.
2853:      * @param array $htmlOptions additional HTML options.
2854:      * @return string the generated button toolbar.
2855:      */
2856:     public static function buttonToolbar(array $groups, $htmlOptions = array())
2857:     {
2858:         if (!empty($groups)) {
2859:             self::addCssClass('btn-toolbar', $htmlOptions);
2860:             $parentOptions = array(
2861:                 'color' => TbArray::popValue('color', $htmlOptions),
2862:                 'size' => TbArray::popValue('size', $htmlOptions),
2863:                 'disabled' => TbArray::popValue('disabled', $htmlOptions)
2864:             );
2865:             $output = self::openTag('div', $htmlOptions);
2866:             foreach ($groups as $groupOptions) {
2867:                 if (isset($groupOptions['visible']) && $groupOptions['visible'] === false) {
2868:                     continue;
2869:                 }
2870:                 $items = TbArray::popValue('items', $groupOptions, array());
2871:                 if (empty($items)) {
2872:                     continue;
2873:                 }
2874:                 // todo: consider removing the support for htmlOptions.
2875:                 $options = TbArray::popValue('htmlOptions', $groupOptions, array());
2876:                 if (!empty($options)) {
2877:                     $groupOptions = TbArray::merge($options, $groupOptions);
2878:                 }
2879:                 $groupOptions = TbArray::copyValues(array('color', 'size', 'disabled'), $parentOptions, $groupOptions);
2880:                 $output .= self::buttonGroup($items, $groupOptions);
2881:             }
2882:             $output .= '</div>';
2883:             return $output;
2884:         }
2885:         return '';
2886:     }
2887: 
2888:     // Button dropdowns
2889:     // http://twitter.github.io/bootstrap/2.3.2/components.html#buttonDropdowns
2890:     // --------------------------------------------------
2891: 
2892:     /**
2893:      * Generates a button with a dropdown menu.
2894:      * @param string $label the button label text.
2895:      * @param array $items the menu items.
2896:      * @param array $htmlOptions additional HTML attributes.
2897:      * @return string the generated button.
2898:      */
2899:     public static function buttonDropdown($label, $items, $htmlOptions = array())
2900:     {
2901:         $htmlOptions['items'] = $items;
2902:         $type = TbArray::popValue('type', $htmlOptions, self::BUTTON_TYPE_LINKBUTTON);
2903:         return self::btn($type, $label, $htmlOptions);
2904:     }
2905: 
2906:     /**
2907:      * Generates a button with a split dropdown menu.
2908:      * @param string $label the button label text.
2909:      * @param array $items the menu items.
2910:      * @param array $htmlOptions additional HTML attributes.
2911:      * @return string the generated button.
2912:      */
2913:     public static function splitButtonDropdown($label, $items, $htmlOptions = array())
2914:     {
2915:         $htmlOptions['split'] = true;
2916:         return self::buttonDropdown($label, $items, $htmlOptions);
2917:     }
2918: 
2919:     // Navs
2920:     // http://twitter.github.io/bootstrap/2.3.2/components.html#navs
2921:     // --------------------------------------------------
2922: 
2923:     /**
2924:      * Generates a tab navigation.
2925:      * @param array $items the menu items.
2926:      * @param array $htmlOptions additional HTML attributes.
2927:      * @return string the generated menu.
2928:      */
2929:     public static function tabs($items, $htmlOptions = array())
2930:     {
2931:         return self::nav(self::NAV_TYPE_TABS, $items, $htmlOptions);
2932:     }
2933: 
2934:     /**
2935:      * Generates a stacked tab navigation.
2936:      * @param array $items the menu items.
2937:      * @param array $htmlOptions additional HTML attributes.
2938:      * @return string the generated menu.
2939:      */
2940:     public static function stackedTabs($items, $htmlOptions = array())
2941:     {
2942:         $htmlOptions['stacked'] = true;
2943:         return self::tabs($items, $htmlOptions);
2944:     }
2945: 
2946:     /**
2947:      * Generates a pills navigation.
2948:      * @param array $items the menu items.
2949:      * @param array $htmlOptions additional HTML attributes.
2950:      * @return string the generated menu.
2951:      */
2952:     public static function pills($items, $htmlOptions = array())
2953:     {
2954:         return self::nav(self::NAV_TYPE_PILLS, $items, $htmlOptions);
2955:     }
2956: 
2957:     /**
2958:      * Generates a stacked pills navigation.
2959:      * @param array $items the menu items.
2960:      * @param array $htmlOptions additional HTML attributes.
2961:      * @return string the generated menu.
2962:      */
2963:     public static function stackedPills($items, $htmlOptions = array())
2964:     {
2965:         $htmlOptions['stacked'] = true;
2966:         return self::pills($items, $htmlOptions);
2967:     }
2968: 
2969:     /**
2970:      * Generates a list navigation.
2971:      * @param array $items the menu items.
2972:      * @param array $htmlOptions additional HTML attributes.
2973:      * @return string the generated menu.
2974:      */
2975:     public static function navList($items, $htmlOptions = array())
2976:     {
2977:         foreach ($items as $i => $itemOptions) {
2978:             if (is_string($itemOptions)) {
2979:                 continue;
2980:             }
2981:             if (!isset($itemOptions['url']) && !isset($itemOptions['items'])) {
2982:                 $label = TbArray::popValue('label', $itemOptions, '');
2983:                 $items[$i] = self::menuHeader($label, $itemOptions);
2984:             }
2985:         }
2986:         return self::nav(self::NAV_TYPE_LIST, $items, $htmlOptions);
2987:     }
2988: 
2989:     /**
2990:      * Generates a navigation menu.
2991:      * @param string $type the menu type.
2992:      * @param array $items the menu items.
2993:      * @param array $htmlOptions additional HTML attributes.
2994:      * @return string the generated menu.
2995:      */
2996:     public static function nav($type, $items, $htmlOptions = array())
2997:     {
2998:         self::addCssClass('nav', $htmlOptions);
2999:         if (!empty($type)) {
3000:             self::addCssClass('nav-' . $type, $htmlOptions);
3001:         }
3002:         $stacked = TbArray::popValue('stacked', $htmlOptions, false);
3003:         if ($type !== self::NAV_TYPE_LIST && $stacked) {
3004:             self::addCssClass('nav-stacked', $htmlOptions);
3005:         }
3006:         return self::menu($items, $htmlOptions);
3007:     }
3008: 
3009:     /**
3010:      * Generates a menu.
3011:      * @param array $items the menu items.
3012:      * @param array $htmlOptions additional HTML attributes.
3013:      * @param integer $depth the current depth.
3014:      * @return string the generated menu.
3015:      */
3016:     public static function menu(array $items, $htmlOptions = array(), $depth = 0)
3017:     {
3018:         // todo: consider making this method protected.
3019:         if (!empty($items)) {
3020:             $htmlOptions['role'] = 'menu';
3021:             $output = self::openTag('ul', $htmlOptions);
3022:             foreach ($items as $itemOptions) {
3023:                 if (is_string($itemOptions)) {
3024:                     $output .= $itemOptions;
3025:                 } else {
3026:                     if (isset($itemOptions['visible']) && $itemOptions['visible'] === false) {
3027:                         continue;
3028:                     }
3029:                     // todo: consider removing the support for htmlOptions.
3030:                     $options = TbArray::popValue('htmlOptions', $itemOptions, array());
3031:                     if (!empty($options)) {
3032:                         $itemOptions = TbArray::merge($options, $itemOptions);
3033:                     }
3034:                     $label = TbArray::popValue('label', $itemOptions, '');
3035:                     if (TbArray::popValue('active', $itemOptions, false)) {
3036:                         self::addCssClass('active', $itemOptions);
3037:                     }
3038:                     if (TbArray::popValue('disabled', $itemOptions, false)) {
3039:                         self::addCssClass('disabled', $itemOptions);
3040:                     }
3041:                     if (!isset($itemOptions['linkOptions'])) {
3042:                         $itemOptions['linkOptions'] = array();
3043:                     }
3044:                     $icon = TbArray::popValue('icon', $itemOptions);
3045:                     if (!empty($icon)) {
3046:                         $label = self::icon($icon) . ' ' . $label;
3047:                     }
3048:                     $items = TbArray::popValue('items', $itemOptions, array());
3049:                     $url = TbArray::popValue('url', $itemOptions, false);
3050:                     if (empty($items)) {
3051:                         if (!$url) {
3052:                             $output .= self::menuHeader($label);
3053:                         } else {
3054:                             $itemOptions['linkOptions']['tabindex'] = -1;
3055:                             $output .= self::menuLink($label, $url, $itemOptions);
3056:                         }
3057:                     } else {
3058:                         $output .= self::menuDropdown($label, $url, $items, $itemOptions, $depth);
3059:                     }
3060:                 }
3061:             }
3062:             $output .= '</ul>';
3063:             return $output;
3064:         } else {
3065:             return '';
3066:         }
3067:     }
3068: 
3069:     /**
3070:      * Generates a menu link.
3071:      * @param string $label the link label.
3072:      * @param array $url the link url.
3073:      * @param array $htmlOptions additional HTML attributes.
3074:      * @return string the generated menu item.
3075:      */
3076:     public static function menuLink($label, $url, $htmlOptions = array())
3077:     {
3078:         TbArray::defaultValue('role', 'menuitem', $htmlOptions);
3079:         $linkOptions = TbArray::popValue('linkOptions', $htmlOptions, array());
3080:         $content = self::link($label, $url, $linkOptions);
3081:         return self::tag('li', $htmlOptions, $content);
3082:     }
3083: 
3084:     /**
3085:      * Generates a menu dropdown.
3086:      * @param string $label the link label.
3087:      * @param string $url the link URL.
3088:      * @param array $items the menu configuration.
3089:      * @param array $htmlOptions additional HTML attributes.
3090:      * @param integer $depth the current depth.
3091:      * @return string the generated dropdown.
3092:      */
3093:     protected static function menuDropdown($label, $url, $items, $htmlOptions, $depth = 0)
3094:     {
3095:         self::addCssClass($depth === 0 ? 'dropdown' : 'dropdown-submenu', $htmlOptions);
3096:         TbArray::defaultValue('role', 'menuitem', $htmlOptions);
3097:         $linkOptions = TbArray::popValue('linkOptions', $htmlOptions, array());
3098:         $menuOptions = TbArray::popValue('menuOptions', $htmlOptions, array());
3099:         self::addCssClass('dropdown-menu', $menuOptions);
3100:         if ($depth === 0) {
3101:             $defaultId = parent::ID_PREFIX . parent::$count++;
3102:             TbArray::defaultValue('id', $defaultId, $menuOptions);
3103:             $menuOptions['aria-labelledby'] = $menuOptions['id'];
3104:             $menuOptions['role'] = 'menu';
3105:         }
3106:         $output = self::openTag('li', $htmlOptions);
3107:         $output .= self::dropdownToggleMenuLink($label, $url, $linkOptions, $depth);
3108:         $output .= self::menu($items, $menuOptions, $depth + 1);
3109:         $output .= '</li>';
3110:         return $output;
3111:     }
3112: 
3113:     /**
3114:      * Generates a menu header.
3115:      * @param string $label the header text.
3116:      * @param array $htmlOptions additional HTML options.
3117:      * @return string the generated header.
3118:      */
3119:     public static function menuHeader($label, $htmlOptions = array())
3120:     {
3121:         self::addCssClass('nav-header', $htmlOptions);
3122:         return self::tag('li', $htmlOptions, $label);
3123:     }
3124: 
3125:     /**
3126:      * Generates a menu divider.
3127:      * @param array $htmlOptions additional HTML attributes.
3128:      * @return string the generated menu item.
3129:      */
3130:     public static function menuDivider($htmlOptions = array())
3131:     {
3132:         self::addCssClass('divider', $htmlOptions);
3133:         return self::tag('li', $htmlOptions);
3134:     }
3135: 
3136:     /**
3137:      * Generates a tabbable tabs menu.
3138:      * @param array $tabs the tab configurations.
3139:      * @param array $htmlOptions additional HTML attributes.
3140:      * @return string the generated menu.
3141:      */
3142:     public static function tabbableTabs($tabs, $htmlOptions = array())
3143:     {
3144:         return self::tabbable(self::NAV_TYPE_TABS, $tabs, $htmlOptions);
3145:     }
3146: 
3147:     /**
3148:      * Generates a tabbable pills menu.
3149:      * @param array $tabs the tab configurations.
3150:      * @param array $htmlOptions additional HTML attributes.
3151:      * @return string the generated menu.
3152:      */
3153:     public static function tabbablePills($pills, $htmlOptions = array())
3154:     {
3155:         return self::tabbable(self::NAV_TYPE_PILLS, $pills, $htmlOptions);
3156:     }
3157: 
3158:     /**
3159:      * Generates a tabbable menu.
3160:      * @param string $type the menu type.
3161:      * @param array $tabs the tab configurations.
3162:      * @param array $htmlOptions additional HTML attributes.
3163:      * @return string the generated menu.
3164:      */
3165:     public static function tabbable($type, $tabs, $htmlOptions = array())
3166:     {
3167:         self::addCssClass('tabbable', $htmlOptions);
3168:         $placement = TbArray::popValue('placement', $htmlOptions);
3169:         if (!empty($placement)) {
3170:             self::addCssClass('tabs-' . $placement, $htmlOptions);
3171:         }
3172:         $menuOptions = TbArray::popValue('menuOptions', $htmlOptions, array());
3173:         $contentOptions = TbArray::popValue('contentOptions', $htmlOptions, array());
3174:         self::addCssClass('tab-content', $contentOptions);
3175:         $panes = array();
3176:         $items = self::normalizeTabs($tabs, $panes);
3177:         $menu = self::nav($type, $items, $menuOptions);
3178:         $content = self::tag('div', $contentOptions, implode('', $panes));
3179:         $output = self::openTag('div', $htmlOptions);
3180:         $output .= $placement === self::TABS_PLACEMENT_BELOW ? $content . $menu : $menu . $content;
3181:         $output .= '</div>';
3182:         return $output;
3183:     }
3184: 
3185:     /**
3186:      * Normalizes the tab configuration.
3187:      * @param array $tabs the tab configuration.
3188:      * @param array $panes a reference to the panes array.
3189:      * @param integer $i the running index.
3190:      * @return array the items.
3191:      */
3192:     protected static function normalizeTabs($tabs, &$panes, $i = 0)
3193:     {
3194:         $menuItems = array();
3195:         foreach ($tabs as $tabOptions) {
3196:             if (isset($tabOptions['visible']) && $tabOptions['visible'] === false) {
3197:                 continue;
3198:             }
3199:             $menuItem = array();
3200:             $menuItem['icon'] = TbArray::popValue('icon', $tabOptions);
3201:             $menuItem['label'] = TbArray::popValue('label', $tabOptions, '');
3202:             $menuItem['active'] = TbArray::getValue('active', $tabOptions, false);
3203:             $menuItem['disabled'] = TbArray::popValue('disabled', $tabOptions, false);
3204:             $menuItem['linkOptions'] = TbArray::popValue('linkOptions', $tabOptions, array());
3205:             $items = TbArray::popValue('items', $tabOptions, array());
3206:             if (!empty($items)) {
3207:                 $menuItem['linkOptions']['data-toggle'] = 'dropdown';
3208:                 $menuItem['items'] = self::normalizeTabs($items, $panes, $i);
3209:             } else {
3210:                 $paneOptions = TbArray::popValue('paneOptions', $tabOptions, array());
3211:                 $id = $paneOptions['id'] = TbArray::popValue('id', $tabOptions, 'tab_' . ++$i);
3212:                 $menuItem['linkOptions']['data-toggle'] = 'tab';
3213:                 $menuItem['url'] = '#' . $id;
3214:                 self::addCssClass('tab-pane', $paneOptions);
3215:                 if (TbArray::popValue('fade', $tabOptions, true)) {
3216:                     self::addCssClass('fade', $paneOptions);
3217:                 }
3218:                 if (TbArray::popValue('active', $tabOptions, false)) {
3219:                     self::addCssClass('active in', $paneOptions);
3220:                 }
3221:                 $paneContent = TbArray::popValue('content', $tabOptions, '');
3222:                 $panes[] = self::tag('div', $paneOptions, $paneContent);
3223:             }
3224:             $menuItems[] = $menuItem;
3225:         }
3226:         return $menuItems;
3227:     }
3228: 
3229:     // Navbar
3230:     // http://twitter.github.io/bootstrap/2.3.2/components.html#navbar
3231:     // --------------------------------------------------
3232: 
3233:     /**
3234:      * Generates a navbar.
3235:      * @param string $content the navbar content.
3236:      * @param array $htmlOptions additional HTML attributes.
3237:      * @return string the generated navbar.
3238:      */
3239:     public static function navbar($content, $htmlOptions = array())
3240:     {
3241:         self::addCssClass('navbar', $htmlOptions);
3242:         $display = TbArray::popValue('display', $htmlOptions);
3243:         if (!empty($display)) {
3244:             self::addCssClass('navbar-' . $display, $htmlOptions);
3245:         }
3246:         $color = TbArray::popValue('color', $htmlOptions);
3247:         if (!empty($color)) {
3248:             self::addCssClass('navbar-' . $color, $htmlOptions);
3249:         }
3250:         $innerOptions = TbArray::popValue('innerOptions', $htmlOptions, array());
3251:         self::addCssClass('navbar-inner', $innerOptions);
3252:         $output = self::openTag('div', $htmlOptions);
3253:         $output .= self::tag('div', $innerOptions, $content);
3254:         $output .= '</div>';
3255:         return $output;
3256:     }
3257: 
3258:     /**
3259:      * Generates a brand link for the navbar.
3260:      * @param string $label the link label text.
3261:      * @param string $url the link url.
3262:      * @param array $htmlOptions additional HTML attributes.
3263:      * @return string the generated link.
3264:      */
3265:     public static function navbarBrandLink($label, $url, $htmlOptions = array())
3266:     {
3267:         self::addCssClass('brand', $htmlOptions);
3268:         return self::link($label, $url, $htmlOptions);
3269:     }
3270: 
3271:     /**
3272:      * Generates a text for the navbar.
3273:      * @param string $text the text.
3274:      * @param array $htmlOptions additional HTML attributes.
3275:      * @param string $tag the HTML tag.
3276:      * @return string the generated text block.
3277:      */
3278:     public static function navbarText($text, $htmlOptions = array(), $tag = 'p')
3279:     {
3280:         self::addCssClass('navbar-text', $htmlOptions);
3281:         return self::tag($tag, $htmlOptions, $text);
3282:     }
3283: 
3284:     /**
3285:      * Generates a menu divider for the navbar.
3286:      * @param array $htmlOptions additional HTML attributes.
3287:      * @return string the generated divider.
3288:      */
3289:     public static function navbarMenuDivider($htmlOptions = array())
3290:     {
3291:         self::addCssClass('divider-vertical', $htmlOptions);
3292:         return self::tag('li', $htmlOptions);
3293:     }
3294: 
3295:     /**
3296:      * Generates a navbar form.
3297:      * @param mixed $action the form action URL.
3298:      * @param string $method form method (e.g. post, get).
3299:      * @param array $htmlOptions additional HTML attributes
3300:      * @return string the generated form.
3301:      */
3302:     public static function navbarForm($action, $method = 'post', $htmlOptions = array())
3303:     {
3304:         self::addCssClass('navbar-form', $htmlOptions);
3305:         return self::form($action, $method, $htmlOptions);
3306:     }
3307: 
3308:     /**
3309:      * Generates a navbar search form.
3310:      * @param mixed $action the form action URL.
3311:      * @param string $method form method (e.g. post, get).
3312:      * @param array $htmlOptions additional HTML attributes
3313:      * @return string the generated form.
3314:      */
3315:     public static function navbarSearchForm($action, $method = 'post', $htmlOptions = array())
3316:     {
3317:         self::addCssClass('navbar-search', $htmlOptions);
3318:         return self::searchForm($action, $method, $htmlOptions);
3319:     }
3320: 
3321:     /**
3322:      * Generates a collapse element.
3323:      * @param string $target the CSS selector for the target element.
3324:      * @param array $htmlOptions additional HTML attributes.
3325:      * @return string the generated icon.
3326:      */
3327:     public static function navbarCollapseLink($target, $htmlOptions = array())
3328:     {
3329:         self::addCssClass('btn btn-navbar', $htmlOptions);
3330:         $htmlOptions['data-toggle'] = 'collapse';
3331:         $htmlOptions['data-target'] = $target;
3332:         $content = '<span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span>';
3333:         return self::tag('a', $htmlOptions, $content);
3334:     }
3335: 
3336:     // Breadcrumbs
3337:     // http://twitter.github.io/bootstrap/2.3.2/components.html#breadcrumbs
3338:     // --------------------------------------------------
3339: 
3340:     /**
3341:      * Generates a breadcrumb menu.
3342:      * @param array $links the breadcrumb links.
3343:      * @param array $htmlOptions additional HTML attributes.
3344:      * @return string the generated breadcrumb.
3345:      */
3346:     public static function breadcrumbs($links, $htmlOptions = array())
3347:     {
3348:         $divider = TbArray::popValue('divider', $htmlOptions, '/');
3349:         self::addCssClass('breadcrumb', $htmlOptions);
3350:         $output = self::openTag('ul', $htmlOptions);
3351:         foreach ($links as $label => $url) {
3352:             if (is_string($label)) {
3353:                 $output .= self::openTag('li');
3354:                 $output .= self::link($label, $url);
3355:                 $output .= self::tag('span', array('class' => 'divider'), $divider);
3356:                 $output .= '</li>';
3357:             } else {
3358:                 $output .= self::tag('li', array('class' => 'active'), $url);
3359:             }
3360:         }
3361:         $output .= '</ul>';
3362:         return $output;
3363:     }
3364: 
3365:     // Pagination
3366:     // http://twitter.github.io/bootstrap/2.3.2/components.html#pagination
3367:     // --------------------------------------------------
3368: 
3369:     /**
3370:      * Generates a pagination.
3371:      * @param array $items the pagination buttons.
3372:      * @param array $htmlOptions additional HTML attributes.
3373:      * @return string the generated pagination.
3374:      */
3375:     public static function pagination(array $items, $htmlOptions = array())
3376:     {
3377:         if (!empty($items)) {
3378:             self::addCssClass('pagination', $htmlOptions);
3379:             $size = TbArray::popValue('size', $htmlOptions);
3380:             if (!empty($size)) {
3381:                 self::addCssClass('pagination-' . $size, $htmlOptions);
3382:             }
3383:             $align = TbArray::popValue('align', $htmlOptions);
3384:             if (!empty($align)) {
3385:                 self::addCssClass('pagination-' . $align, $htmlOptions);
3386:             }
3387:             $listOptions = TbArray::popValue('listOptions', $htmlOptions, array());
3388:             $output = self::openTag('div', $htmlOptions);
3389:             $output .= self::openTag('ul', $listOptions);
3390:             foreach ($items as $itemOptions) {
3391:                 // todo: consider removing the support for htmlOptions.
3392:                 $options = TbArray::popValue('htmlOptions', $itemOptions, array());
3393:                 if (!empty($options)) {
3394:                     $itemOptions = TbArray::merge($options, $itemOptions);
3395:                 }
3396:                 $label = TbArray::popValue('label', $itemOptions, '');
3397:                 $url = TbArray::popValue('url', $itemOptions, false);
3398:                 $output .= self::paginationLink($label, $url, $itemOptions);
3399:             }
3400:             $output .= '</ul></div>';
3401:             return $output;
3402:         }
3403:         return '';
3404:     }
3405: 
3406:     /**
3407:      * Generates a pagination link.
3408:      * @param string $label the link label text.
3409:      * @param mixed $url the link url.
3410:      * @param array $htmlOptions additional HTML attributes.
3411:      * @return string the generated link.
3412:      */
3413:     public static function paginationLink($label, $url, $htmlOptions = array())
3414:     {
3415:         $linkOptions = TbArray::popValue('linkOptions', $htmlOptions, array());
3416:         if (TbArray::popValue('active', $htmlOptions, false)) {
3417:             self::addCssClass('active', $htmlOptions);
3418:         }
3419:         if (TbArray::popValue('disabled', $htmlOptions, false)) {
3420:             self::addCssClass('disabled', $htmlOptions);
3421:         }
3422:         $content = self::link($label, $url, $linkOptions);
3423:         return self::tag('li', $htmlOptions, $content);
3424:     }
3425: 
3426:     /**
3427:      * Generates a pager.
3428:      * @param array $links the pager buttons.
3429:      * @param array $htmlOptions additional HTML attributes.
3430:      * @return string the generated pager.
3431:      */
3432:     public static function pager(array $links, $htmlOptions = array())
3433:     {
3434:         if (!empty($links)) {
3435:             self::addCssClass('pager', $htmlOptions);
3436:             $output = self::openTag('ul', $htmlOptions);
3437:             foreach ($links as $itemOptions) {
3438:                 // todo: consider removing the support for htmlOptions.
3439:                 $options = TbArray::popValue('htmlOptions', $itemOptions, array());
3440:                 if (!empty($options)) {
3441:                     $itemOptions = TbArray::merge($options, $itemOptions);
3442:                 }
3443:                 $label = TbArray::popValue('label', $itemOptions, '');
3444:                 $url = TbArray::popValue('url', $itemOptions, false);
3445:                 $output .= self::pagerLink($label, $url, $itemOptions);
3446:             }
3447:             $output .= '</ul>';
3448:             return $output;
3449:         }
3450:         return '';
3451:     }
3452: 
3453:     /**
3454:      * Generates a pager link.
3455:      * @param string $label the link label text.
3456:      * @param mixed $url the link url.
3457:      * @param array $htmlOptions additional HTML attributes.
3458:      * @return string the generated link.
3459:      */
3460:     public static function pagerLink($label, $url, $htmlOptions = array())
3461:     {
3462:         $linkOptions = TbArray::popValue('linkOptions', $htmlOptions, array());
3463:         if (TbArray::popValue('previous', $htmlOptions, false)) {
3464:             self::addCssClass('previous', $htmlOptions);
3465:         }
3466:         if (TbArray::popValue('next', $htmlOptions, false)) {
3467:             self::addCssClass('next', $htmlOptions);
3468:         }
3469:         if (TbArray::popValue('disabled', $htmlOptions, false)) {
3470:             self::addCssClass('disabled', $htmlOptions);
3471:         }
3472:         $content = self::link($label, $url, $linkOptions);
3473:         return self::tag('li', $htmlOptions, $content);
3474:     }
3475: 
3476:     // Labels and badges
3477:     // http://twitter.github.io/bootstrap/2.3.2/components.html#labels-badges
3478:     // --------------------------------------------------
3479: 
3480:     /**
3481:      * Generates a label span.
3482:      * @param string $label the label text.
3483:      * @param array $htmlOptions additional HTML attributes.
3484:      * @return string the generated span.
3485:      */
3486:     public static function labelTb($label, $htmlOptions = array())
3487:     {
3488:         self::addCssClass('label', $htmlOptions);
3489:         $color = TbArray::popValue('color', $htmlOptions);
3490:         if (!empty($color)) {
3491:             self::addCssClass('label-' . $color, $htmlOptions);
3492:         }
3493:         return self::tag('span', $htmlOptions, $label);
3494:     }
3495: 
3496:     /**
3497:      * Generates a badge span.
3498:      * @param string $label the badge text.
3499:      * @param array $htmlOptions additional HTML attributes.
3500:      * @return string the generated span.
3501:      */
3502:     public static function badge($label, $htmlOptions = array())
3503:     {
3504:         self::addCssClass('badge', $htmlOptions);
3505:         $color = TbArray::popValue('color', $htmlOptions);
3506:         if (!empty($color)) {
3507:             self::addCssClass('badge-' . $color, $htmlOptions);
3508:         }
3509:         return self::tag('span', $htmlOptions, $label);
3510:     }
3511: 
3512:     // Typography
3513:     // http://twitter.github.io/bootstrap/2.3.2/components.html#typography
3514:     // --------------------------------------------------
3515: 
3516:     /**
3517:      * Generates a hero unit.
3518:      * @param string $heading the heading text.
3519:      * @param string $content the content text.
3520:      * @param array $htmlOptions additional HTML attributes.
3521:      * @return string the generated hero unit.
3522:      */
3523:     public static function heroUnit($heading, $content, $htmlOptions = array())
3524:     {
3525:         self::addCssClass('hero-unit', $htmlOptions);
3526:         $headingOptions = TbArray::popValue('headingOptions', $htmlOptions, array());
3527:         $output = self::openTag('div', $htmlOptions);
3528:         $output .= self::tag('h1', $headingOptions, $heading);
3529:         $output .= $content;
3530:         $output .= '</div>';
3531:         return $output;
3532:     }
3533: 
3534:     /**
3535:      * Generates a pager header.
3536:      * @param string $heading the heading text.
3537:      * @param string $subtext the subtext.
3538:      * @param array $htmlOptions additional HTML attributes.
3539:      * @return string the generated pager header.
3540:      */
3541:     public static function pageHeader($heading, $subtext, $htmlOptions = array())
3542:     {
3543:         self::addCssClass('page-header', $htmlOptions);
3544:         $headerOptions = TbArray::popValue('headerOptions', $htmlOptions, array());
3545:         $subtextOptions = TbArray::popValue('subtextOptions', $htmlOptions, array());
3546:         $output = self::openTag('div', $htmlOptions);
3547:         $output .= self::openTag('h1', $headerOptions);
3548:         $output .= parent::encode($heading) . ' ' . self::tag('small', $subtextOptions, $subtext);
3549:         $output .= '</h1>';
3550:         $output .= '</div>';
3551:         return $output;
3552:     }
3553: 
3554:     // Thumbnails
3555:     // http://twitter.github.io/bootstrap/2.3.2/components.html#thumbnails
3556:     // --------------------------------------------------
3557: 
3558:     /**
3559:      * Generates a list of thumbnails.
3560:      * @param array $thumbnails the list configuration.
3561:      * @param array $htmlOptions additional HTML attributes.
3562:      * @return string the generated thumbnails.
3563:      */
3564:     public static function thumbnails(array $thumbnails, $htmlOptions = array())
3565:     {
3566:         if (!empty($thumbnails)) {
3567:             self::addCssClass('thumbnails', $htmlOptions);
3568:             $defaultSpan = TbArray::popValue('span', $htmlOptions, 3);
3569:             $output = self::openTag('ul', $htmlOptions);
3570:             foreach ($thumbnails as $thumbnailOptions) {
3571:                 if (isset($thumbnailOptions['visible']) && $thumbnailOptions['visible'] === false) {
3572:                     continue;
3573:                 }
3574:                 // todo: consider removing the support for htmlOptions.
3575:                 $options = TbArray::popValue('htmlOptions', $thumbnailOptions, array());
3576:                 if (!empty($options)) {
3577:                     $thumbnailOptions = TbArray::merge($options, $thumbnailOptions);
3578:                 }
3579:                 $thumbnailOptions['itemOptions']['span'] = TbArray::popValue('span', $thumbnailOptions, $defaultSpan);
3580:                 $caption = TbArray::popValue('caption', $thumbnailOptions, '');
3581:                 $captionOptions = TbArray::popValue('captionOptions', $thumbnailOptions, array());
3582:                 self::addCssClass('caption', $captionOptions);
3583:                 $label = TbArray::popValue('label', $thumbnailOptions);
3584:                 $labelOptions = TbArray::popValue('labelOptions', $thumbnailOptions, array());
3585:                 if (!empty($label)) {
3586:                     $caption = self::tag('h3', $labelOptions, $label) . $caption;
3587:                 }
3588:                 $content = !empty($caption) ? self::tag('div', $captionOptions, $caption) : '';
3589:                 $image = TbArray::popValue('image', $thumbnailOptions);
3590:                 $imageOptions = TbArray::popValue('imageOptions', $thumbnailOptions, array());
3591:                 $imageAlt = TbArray::popValue('alt', $imageOptions, '');
3592:                 if (!empty($image)) {
3593:                     $content = parent::image($image, $imageAlt, $imageOptions) . $content;
3594:                 }
3595:                 $url = TbArray::popValue('url', $thumbnailOptions, false);
3596:                 $output .= $url !== false
3597:                     ? self::thumbnailLink($content, $url, $thumbnailOptions)
3598:                     : self::thumbnail($content, $thumbnailOptions);
3599:             }
3600:             $output .= '</ul>';
3601:             return $output;
3602:         } else {
3603:             return '';
3604:         }
3605:     }
3606: 
3607:     /**
3608:      * Generates a thumbnail.
3609:      * @param string $content the thumbnail content.
3610:      * @param array $htmlOptions additional HTML attributes.
3611:      * @return string the generated thumbnail.
3612:      */
3613:     public static function thumbnail($content, $htmlOptions = array())
3614:     {
3615:         $itemOptions = TbArray::popValue('itemOptions', $htmlOptions, array());
3616:         self::addCssClass('thumbnail', $htmlOptions);
3617:         $output = self::openTag('li', $itemOptions);
3618:         $output .= self::tag('div', $htmlOptions, $content);
3619:         $output .= '</li>';
3620:         return $output;
3621:     }
3622: 
3623:     /**
3624:      * Generates a link thumbnail.
3625:      * @param string $content the thumbnail content.
3626:      * @param mixed $url the url that the thumbnail links to.
3627:      * @param array $htmlOptions additional HTML attributes.
3628:      * @return string the generated thumbnail.
3629:      */
3630:     public static function thumbnailLink($content, $url = '#', $htmlOptions = array())
3631:     {
3632:         $itemOptions = TbArray::popValue('itemOptions', $htmlOptions, array());
3633:         self::addCssClass('thumbnail', $htmlOptions);
3634:         $content = self::link($content, $url, $htmlOptions);
3635:         return self::tag('li', $itemOptions, $content);
3636:     }
3637: 
3638:     // Alerts
3639:     // http://twitter.github.io/bootstrap/2.3.2/components.html#alerts
3640:     // --------------------------------------------------
3641: 
3642:     /**
3643:      * Generates an alert.
3644:      * @param string $color the color of the alert.
3645:      * @param string $message the message to display.
3646:      * @param array $htmlOptions additional HTML options.
3647:      * @return string the generated alert.
3648:      */
3649:     public static function alert($color, $message, $htmlOptions = array())
3650:     {
3651:         self::addCssClass('alert', $htmlOptions);
3652:         if (!empty($color)) {
3653:             self::addCssClass('alert-' . $color, $htmlOptions);
3654:         }
3655:         if (TbArray::popValue('in', $htmlOptions, true)) {
3656:             self::addCssClass('in', $htmlOptions);
3657:         }
3658:         if (TbArray::popValue('block', $htmlOptions, false)) {
3659:             self::addCssClass('alert-block', $htmlOptions);
3660:         }
3661:         if (TbArray::popValue('fade', $htmlOptions, true)) {
3662:             self::addCssClass('fade', $htmlOptions);
3663:         }
3664:         $closeText = TbArray::popValue('closeText', $htmlOptions, self::CLOSE_TEXT);
3665:         $closeOptions = TbArray::popValue('closeOptions', $htmlOptions, array());
3666:         $closeOptions['dismiss'] = self::CLOSE_DISMISS_ALERT;
3667:         $output = self::openTag('div', $htmlOptions);
3668:         $output .= $closeText !== false ? self::closeLink($closeText, '#', $closeOptions) : '';
3669:         $output .= $message;
3670:         $output .= '</div>';
3671:         return $output;
3672:     }
3673: 
3674:     /**
3675:      * Generates an alert block.
3676:      * @param string $color the color of the alert.
3677:      * @param string $message the message to display.
3678:      * @param array $htmlOptions additional HTML options.
3679:      * @return string the generated alert.
3680:      */
3681:     public static function blockAlert($color, $message, $htmlOptions = array())
3682:     {
3683:         $htmlOptions['block'] = true;
3684:         return self::alert($color, $message, $htmlOptions);
3685:     }
3686: 
3687:     // Progress bars
3688:     // http://twitter.github.io/bootstrap/2.3.2/components.html#progress
3689:     // --------------------------------------------------
3690: 
3691:     /**
3692:      * Generates a progress bar.
3693:      * @param integer $width the progress in percent.
3694:      * @param array $htmlOptions additional HTML attributes.
3695:      * @return string the generated progress bar.
3696:      */
3697:     public static function progressBar($width = 0, $htmlOptions = array())
3698:     {
3699:         self::addCssClass('progress', $htmlOptions);
3700:         $color = TbArray::popValue('color', $htmlOptions);
3701:         if (!empty($color)) {
3702:             self::addCssClass('progress-' . $color, $htmlOptions);
3703:         }
3704:         if (TbArray::popValue('striped', $htmlOptions, false)) {
3705:             self::addCssClass('progress-striped', $htmlOptions);
3706:         }
3707:         if (TbArray::popValue('animated', $htmlOptions, false)) {
3708:             self::addCssClass('active', $htmlOptions);
3709:         }
3710:         $barOptions = TbArray::popValue('barOptions', $htmlOptions, array());
3711:         $content = TbArray::popValue('content', $htmlOptions);
3712:         if (!empty($content)) {
3713:             $barOptions['content'] = $content;
3714:         }
3715:         $content = self::bar($width, $barOptions);
3716:         return self::tag('div', $htmlOptions, $content);
3717:     }
3718: 
3719:     /**
3720:      * Generates a striped progress bar.
3721:      * @param integer $width the progress in percent.
3722:      * @param array $htmlOptions additional HTML attributes.
3723:      * @return string the generated progress bar.
3724:      */
3725:     public static function stripedProgressBar($width = 0, $htmlOptions = array())
3726:     {
3727:         $htmlOptions['striped'] = true;
3728:         return self::progressBar($width, $htmlOptions);
3729:     }
3730: 
3731:     /**
3732:      * Generates an animated progress bar.
3733:      * @param integer $width the progress in percent.
3734:      * @param array $htmlOptions additional HTML attributes.
3735:      * @return string the generated progress bar.
3736:      */
3737:     public static function animatedProgressBar($width = 0, $htmlOptions = array())
3738:     {
3739:         $htmlOptions['animated'] = true;
3740:         return self::stripedProgressBar($width, $htmlOptions);
3741:     }
3742: 
3743:     /**
3744:      * Generates a stacked progress bar.
3745:      * @param array $bars the bar configurations.
3746:      * @param array $htmlOptions additional HTML attributes.
3747:      * @return string the generated progress bar.
3748:      */
3749:     public static function stackedProgressBar(array $bars, $htmlOptions = array())
3750:     {
3751:         if (!empty($bars)) {
3752:             self::addCssClass('progress', $htmlOptions);
3753:             $output = self::openTag('div', $htmlOptions);
3754:             $totalWidth = 0;
3755:             foreach ($bars as $barOptions) {
3756:                 if (isset($barOptions['visible']) && !$barOptions['visible']) {
3757:                     continue;
3758:                 }
3759:                 $width = TbArray::popValue('width', $barOptions, 0);
3760:                 $tmp = $totalWidth;
3761:                 $totalWidth += $width;
3762:                 if ($totalWidth > 100) {
3763:                     $width = 100 - $tmp;
3764:                 }
3765:                 $output .= self::bar($width, $barOptions);
3766:             }
3767:             $output .= '</div>';
3768:             return $output;
3769:         }
3770:         return '';
3771:     }
3772: 
3773:     /**
3774:      * Generates a progress bar.
3775:      * @param integer $width the progress in percent.
3776:      * @param array $htmlOptions additional HTML attributes.
3777:      * @return string the generated bar.
3778:      */
3779:     protected static function bar($width = 0, $htmlOptions = array())
3780:     {
3781:         self::addCssClass('bar', $htmlOptions);
3782:         $color = TbArray::popValue('color', $htmlOptions);
3783:         if (!empty($color)) {
3784:             self::addCssClass('bar-' . $color, $htmlOptions);
3785:         }
3786:         if ($width < 0) {
3787:             $width = 0;
3788:         }
3789:         if ($width > 100) {
3790:             $width = 100;
3791:         }
3792:         if ($width > 0) {
3793:             $width .= '%';
3794:         }
3795:         self::addCssStyle("width: {$width};", $htmlOptions);
3796:         $content = TbArray::popValue('content', $htmlOptions, '');
3797:         return self::tag('div', $htmlOptions, $content);
3798:     }
3799: 
3800:     // Media objects
3801:     // http://twitter.github.io/bootstrap/2.3.2/components.html#media
3802:     // --------------------------------------------------
3803: 
3804:     /**
3805:      * Generates a list of media objects.
3806:      * @param array $items item configurations.
3807:      * @param array $htmlOptions additional HTML attributes.
3808:      * @return string generated list.
3809:      */
3810:     public static function mediaList(array $items, $htmlOptions = array())
3811:     {
3812:         if (!empty($items)) {
3813:             self::addCssClass('media-list', $htmlOptions);
3814:             $output = '';
3815:             $output .= self::openTag('ul', $htmlOptions);
3816:             $output .= self::medias($items, 'li');
3817:             $output .= '</ul>';
3818:             return $output;
3819:         }
3820:         return '';
3821:     }
3822: 
3823:     /**
3824:      * Generates multiple media objects.
3825:      * @param array $items item configurations.
3826:      * @param string $tag the item tag name.
3827:      * @return string generated objects.
3828:      */
3829:     public static function medias(array $items, $tag = 'div')
3830:     {
3831:         if (!empty($items)) {
3832:             $output = '';
3833:             foreach ($items as $itemOptions) {
3834:                 if (isset($itemOptions['visible']) && $itemOptions['visible'] === false) {
3835:                     continue;
3836:                 }
3837:                 // todo: consider removing the support for htmlOptions.
3838:                 $options = TbArray::popValue('htmlOptions', $itemOptions, array());
3839:                 if (!empty($options)) {
3840:                     $itemOptions = TbArray::merge($options, $itemOptions);
3841:                 }
3842:                 $image = TbArray::popValue('image', $itemOptions);
3843:                 $heading = TbArray::popValue('heading', $itemOptions, '');
3844:                 $content = TbArray::popValue('content', $itemOptions, '');
3845:                 TbArray::defaultValue('tag', $tag, $itemOptions);
3846:                 $output .= self::media($image, $heading, $content, $itemOptions);
3847:             }
3848:             return $output;
3849:         }
3850:         return '';
3851:     }
3852: 
3853:     /**
3854:      * Generates a single media object.
3855:      * @param string $image the image url.
3856:      * @param string $heading the heading text.
3857:      * @param string $content the content text.
3858:      * @param array $htmlOptions additional HTML attributes.
3859:      * @return string the media object.
3860:      */
3861:     public static function media($image, $heading, $content, $htmlOptions = array())
3862:     {
3863:         $tag = TbArray::popValue('tag', $htmlOptions, 'div');
3864:         self::addCssClass('media', $htmlOptions);
3865:         $linkOptions = TbArray::popValue('linkOptions', $htmlOptions, array());
3866:         TbArray::defaultValue('pull', self::PULL_LEFT, $linkOptions);
3867:         $imageOptions = TbArray::popValue('imageOptions', $htmlOptions, array());
3868:         self::addCssClass('media-object', $imageOptions);
3869:         $contentOptions = TbArray::popValue('contentOptions', $htmlOptions, array());
3870:         self::addCssClass('media-body', $contentOptions);
3871:         $headingOptions = TbArray::popValue('headingOptions', $htmlOptions, array());
3872:         self::addCssClass('media-heading', $headingOptions);
3873:         $items = TbArray::popValue('items', $htmlOptions);
3874: 
3875:         $output = self::openTag($tag, $htmlOptions);
3876:         $alt = TbArray::popValue('alt', $imageOptions, '');
3877:         $href = TbArray::popValue('href', $linkOptions, '#');
3878:         if (!empty($image)) {
3879:             $output .= self::link(parent::image($image, $alt, $imageOptions), $href, $linkOptions);
3880:         }
3881:         $output .= self::openTag('div', $contentOptions);
3882:         $output .= self::tag('h4', $headingOptions, $heading);
3883:         $output .= $content;
3884:         if (!empty($items)) {
3885:             $output .= self::medias($items);
3886:         }
3887:         $output .= '</div>';
3888:         $output .= self::closeTag($tag);
3889:         return $output;
3890:     }
3891: 
3892:     // Misc
3893:     // http://twitter.github.io/bootstrap/2.3.2/components.html#misc
3894:     // --------------------------------------------------
3895: 
3896:     /**
3897:      * Generates a well element.
3898:      * @param string $content the well content.
3899:      * @param array $htmlOptions additional HTML attributes.
3900:      * @return string the generated well.
3901:      */
3902:     public static function well($content, $htmlOptions = array())
3903:     {
3904:         self::addCssClass('well', $htmlOptions);
3905:         $size = TbArray::popValue('size', $htmlOptions);
3906:         if (!empty($size)) {
3907:             self::addCssClass('well-' . $size, $htmlOptions);
3908:         }
3909:         return self::tag('div', $htmlOptions, $content);
3910:     }
3911: 
3912:     /**
3913:      * Generates a close link.
3914:      * @param string $label the link label text.
3915:      * @param mixed $url the link url.
3916:      * @param array $htmlOptions additional HTML attributes.
3917:      * @return string the generated link.
3918:      */
3919:     public static function closeLink($label = self::CLOSE_TEXT, $url = '#', $htmlOptions = array())
3920:     {
3921:         $htmlOptions['href'] = $url;
3922:         return self::close('a', $label, $htmlOptions);
3923:     }
3924: 
3925:     /**
3926:      * Generates a close button.
3927:      * @param string $label the button label text.
3928:      * @param array $htmlOptions the HTML options for the button.
3929:      * @return string the generated button.
3930:      */
3931:     public static function closeButton($label = self::CLOSE_TEXT, $htmlOptions = array())
3932:     {
3933:         return self::close('button', $label, $htmlOptions);
3934:     }
3935: 
3936:     /**
3937:      * Generates a close element.
3938:      * @param string $tag the tag name.
3939:      * @param string $label the element label text.
3940:      * @param array $htmlOptions additional HTML attributes.
3941:      * @return string the generated element.
3942:      */
3943:     protected static function close($tag, $label, $htmlOptions = array())
3944:     {
3945:         self::addCssClass('close', $htmlOptions);
3946:         $dismiss = TbArray::popValue('dismiss', $htmlOptions);
3947:         if (!empty($dismiss)) {
3948:             $htmlOptions['data-dismiss'] = $dismiss;
3949:         }
3950:         $htmlOptions['type'] = 'button';
3951:         return self::tag($tag, $htmlOptions, $label);
3952:     }
3953: 
3954:     /**
3955:      * Generates a collapse link.
3956:      * @param string $label the link label.
3957:      * @param string $target the CSS selector.
3958:      * @param array $htmlOptions additional HTML attributes.
3959:      * @return string the generated link.
3960:      */
3961:     public static function collapseLink($label, $target, $htmlOptions = array())
3962:     {
3963:         $htmlOptions['data-toggle'] = 'collapse';
3964:         return self::link($label, $target, $htmlOptions);
3965:     }
3966: 
3967:     //
3968:     // JAVASCRIPT
3969:     // --------------------------------------------------
3970: 
3971:     // Modals
3972:     // http://twitter.github.io/bootstrap/2.3.2/javascript.html#modals
3973:     // --------------------------------------------------
3974: 
3975:     /**
3976:      * Generates a modal header.
3977:      * @param string $content the header content.
3978:      * @param array $htmlOptions additional HTML attributes.
3979:      * @return string the generated header.
3980:      */
3981:     public static function modalHeader($content, $htmlOptions = array())
3982:     {
3983:         self::addCssClass('modal-header', $htmlOptions);
3984:         $closeOptions = TbArray::popValue('closeOptions', $htmlOptions, array());
3985:         $closeOptions['dismiss'] = 'modal';
3986:         $headingOptions = TbArray::popValue('headingOptions', $htmlOptions, array());
3987:         $closeLabel = TbArray::popValue('closeLabel', $htmlOptions, self::CLOSE_TEXT);
3988:         $closeButton = self::closeButton($closeLabel, $closeOptions);
3989:         $header = self::tag('h3', $headingOptions, $content);
3990:         return self::tag('div', $htmlOptions, $closeButton . $header);
3991:     }
3992: 
3993:     /**
3994:      * Generates a modal body.
3995:      * @param string $content the body content.
3996:      * @param array $htmlOptions additional HTML attributes.
3997:      * @return string the generated body.
3998:      */
3999:     public static function modalBody($content, $htmlOptions = array())
4000:     {
4001:         self::addCssClass('modal-body', $htmlOptions);
4002:         return self::tag('div', $htmlOptions, $content);
4003:     }
4004: 
4005:     /**
4006:      * Generates a modal footer.
4007:      * @param string $content the footer content.
4008:      * @param array $htmlOptions additional HTML attributes.
4009:      * @return string the generated footer.
4010:      */
4011:     public static function modalFooter($content, $htmlOptions = array())
4012:     {
4013:         self::addCssClass('modal-footer', $htmlOptions);
4014:         return self::tag('div', $htmlOptions, $content);
4015:     }
4016: 
4017:     // Tooltips and Popovers
4018:     // http://twitter.github.io/bootstrap/2.3.2/javascript.html#tooltips
4019:     // http://twitter.github.io/bootstrap/2.3.2/javascript.html#popovers
4020:     // --------------------------------------------------
4021: 
4022:     /**
4023:      * Generates a tooltip.
4024:      * @param string $label the tooltip link label text.
4025:      * @param mixed $url the link url.
4026:      * @param string $content the tooltip content text.
4027:      * @param array $htmlOptions additional HTML attributes.
4028:      * @return string the generated tooltip.
4029:      */
4030:     public static function tooltip($label, $url, $content, $htmlOptions = array())
4031:     {
4032:         $htmlOptions['rel'] = 'tooltip';
4033:         return self::tooltipPopover($label, $url, $content, $htmlOptions);
4034:     }
4035: 
4036:     /**
4037:      * Generates a popover.
4038:      * @param string $label the popover link label text.
4039:      * @param string $title the popover title text.
4040:      * @param string $content the popover content text.
4041:      * @param array $htmlOptions additional HTML attributes.
4042:      * @return string the generated popover.
4043:      */
4044:     public static function popover($label, $title, $content, $htmlOptions = array())
4045:     {
4046:         $htmlOptions['rel'] = 'popover';
4047:         $htmlOptions['data-content'] = $content;
4048:         $htmlOptions['data-toggle'] = 'popover';
4049:         return self::tooltipPopover($label, '#', $title, $htmlOptions);
4050:     }
4051: 
4052:     /**
4053:      * Generates a base tooltip.
4054:      * @param string $label the tooltip link label text.
4055:      * @param mixed $url the link url.
4056:      * @param string $title the tooltip title text.
4057:      * @param array $htmlOptions additional HTML attributes.
4058:      * @return string the generated tooltip.
4059:      */
4060:     protected static function tooltipPopover($label, $url, $title, $htmlOptions)
4061:     {
4062:         $htmlOptions['title'] = $title;
4063:         if (TbArray::popValue('animation', $htmlOptions)) {
4064:             $htmlOptions['data-animation'] = true;
4065:         }
4066:         if (TbArray::popValue('html', $htmlOptions)) {
4067:             $htmlOptions['data-html'] = true;
4068:         }
4069:         if (TbArray::popValue('selector', $htmlOptions)) {
4070:             $htmlOptions['data-selector'] = true;
4071:         }
4072:         $placement = TbArray::popValue('placement', $htmlOptions);
4073:         if (!empty($placement)) {
4074:             $htmlOptions['data-placement'] = $placement;
4075:         }
4076:         $trigger = TbArray::popValue('trigger', $htmlOptions);
4077:         if (!empty($trigger)) {
4078:             $htmlOptions['data-trigger'] = $trigger;
4079:         }
4080:         if (($delay = TbArray::popValue('delay', $htmlOptions)) !== null) {
4081:             $htmlOptions['data-delay'] = $delay;
4082:         }
4083:         return self::link($label, $url, $htmlOptions);
4084:     }
4085: 
4086:     // Carousel
4087:     // http://twitter.github.io/bootstrap/2.3.2/javascript.html#carousel
4088:     // --------------------------------------------------
4089: 
4090:     /**
4091:      * Generates an image carousel.
4092:      * @param array $items the item configurations.
4093:      * @param array $htmlOptions additional HTML attributes.
4094:      * @return string the generated carousel.
4095:      */
4096:     public static function carousel(array $items, $htmlOptions = array())
4097:     {
4098:         if (!empty($items)) {
4099:             $id = TbArray::getValue('id', $htmlOptions, parent::ID_PREFIX . parent::$count++);
4100:             TbArray::defaultValue('id', $id, $htmlOptions);
4101:             $selector = '#' . $id;
4102:             self::addCssClass('carousel', $htmlOptions);
4103:             if (TbArray::popValue('slide', $htmlOptions, true)) {
4104:                 self::addCssClass('slide', $htmlOptions);
4105:             }
4106:             $interval = TbArray::popValue('data-interval', $htmlOptions);
4107:             if ($interval) {
4108:                 $htmlOptions['data-interval'] = $interval;
4109:             }
4110:             $pause = TbArray::popValue('data-pause', $htmlOptions);
4111:             if ($pause) {
4112:                 $htmlOptions['data-pause'] = $pause;
4113:             }
4114:             $indicatorOptions = TbArray::popValue('indicatorOptions', $htmlOptions, array());
4115:             $innerOptions = TbArray::popValue('innerOptions', $htmlOptions, array());
4116:             self::addCssClass('carousel-inner', $innerOptions);
4117:             $prevOptions = TbArray::popValue('prevOptions', $htmlOptions, array());
4118:             $prevLabel = TbArray::popValue('label', $prevOptions, '&lsaquo;');
4119:             $nextOptions = TbArray::popValue('nextOptions', $htmlOptions, array());
4120:             $nextLabel = TbArray::popValue('label', $nextOptions, '&rsaquo;');
4121:             $hidePrevAndNext = TbArray::popValue('hidePrevAndNext', $htmlOptions, false);
4122:             $output = self::openTag('div', $htmlOptions);
4123:             $output .= self::carouselIndicators($selector, count($items), $indicatorOptions);
4124:             $output .= self::openTag('div', $innerOptions);
4125:             foreach ($items as $i => $itemOptions) {
4126:                 if (isset($itemOptions['visible']) && $itemOptions['visible'] === false) {
4127:                     continue;
4128:                 }
4129:                 if ($i === 0) { // first item should be active
4130:                     self::addCssClass('active', $itemOptions);
4131:                 }
4132:                 $content = TbArray::popValue('content', $itemOptions, '');
4133:                 $image = TbArray::popValue('image', $itemOptions, '');
4134:                 $imageOptions = TbArray::popValue('imageOptions', $itemOptions, array());
4135:                 $imageAlt = TbArray::popValue('alt', $imageOptions, '');
4136:                 if (!empty($image)) {
4137:                     $content = parent::image($image, $imageAlt, $imageOptions);
4138:                 }
4139:                 $label = TbArray::popValue('label', $itemOptions);
4140:                 $caption = TbArray::popValue('caption', $itemOptions);
4141:                 $output .= self::carouselItem($content, $label, $caption, $itemOptions);
4142:             }
4143:             $output .= '</div>';
4144:             if (!$hidePrevAndNext) {
4145:                 $output .= self::carouselPrevLink($prevLabel, $selector, $prevOptions);
4146:                 $output .= self::carouselNextLink($nextLabel, $selector, $nextOptions);
4147:             }
4148:             $output .= '</div>';
4149:             return $output;
4150:         }
4151:         return '';
4152:     }
4153: 
4154:     /**
4155:      * Generates a carousel item.
4156:      * @param string $content the content.
4157:      * @param string $label the item label text.
4158:      * @param string $caption the item caption text.
4159:      * @param array $htmlOptions additional HTML attributes.
4160:      * @return string the generated item.
4161:      */
4162:     public static function carouselItem($content, $label, $caption, $htmlOptions = array())
4163:     {
4164:         self::addCssClass('item', $htmlOptions);
4165:         $overlayOptions = TbArray::popValue('overlayOptions', $htmlOptions, array());
4166:         self::addCssClass('carousel-caption', $overlayOptions);
4167:         $labelOptions = TbArray::popValue('labelOptions', $htmlOptions, array());
4168:         $captionOptions = TbArray::popValue('captionOptions', $htmlOptions, array());
4169:         $url = TbArray::popValue('url', $htmlOptions, false);
4170:         if ($url !== false) {
4171:             $content = self::link($content, $url);
4172:         }
4173:         $output = self::openTag('div', $htmlOptions);
4174:         $output .= $content;
4175:         if (isset($label) || isset($caption)) {
4176:             $output .= self::openTag('div', $overlayOptions);
4177:             if ($label) {
4178:                 $output .= self::tag('h4', $labelOptions, $label);
4179:             }
4180:             if ($caption) {
4181:                 $output .= self::tag('p', $captionOptions, $caption);
4182:             }
4183:             $output .= '</div>';
4184:         }
4185:         $output .= '</div>';
4186:         return $output;
4187:     }
4188: 
4189:     /**
4190:      * Generates a previous link for the carousel.
4191:      * @param string $label the link label text.
4192:      * @param mixed $url the link url.
4193:      * @param array $htmlOptions additional HTML attributes.
4194:      * @return string the generated link.
4195:      */
4196:     public static function carouselPrevLink($label, $url = '#', $htmlOptions = array())
4197:     {
4198:         self::addCssClass('carousel-control left', $htmlOptions);
4199:         $htmlOptions['data-slide'] = 'prev';
4200:         return self::link($label, $url, $htmlOptions);
4201:     }
4202: 
4203:     /**
4204:      * Generates a next link for the carousel.
4205:      * @param string $label the link label text.
4206:      * @param mixed $url the link url.
4207:      * @param array $htmlOptions additional HTML attributes.
4208:      * @return string the generated link.
4209:      */
4210:     public static function carouselNextLink($label, $url = '#', $htmlOptions = array())
4211:     {
4212:         self::addCssClass('carousel-control right', $htmlOptions);
4213:         $htmlOptions['data-slide'] = 'next';
4214:         return self::link($label, $url, $htmlOptions);
4215:     }
4216: 
4217:     /**
4218:      * Generates an indicator for the carousel.
4219:      * @param string $target the CSS selector for the target element.
4220:      * @param integer $numSlides the number of slides.
4221:      * @param array $htmlOptions additional HTML attributes.
4222:      * @return string the generated indicators.
4223:      */
4224:     public static function carouselIndicators($target, $numSlides, $htmlOptions = array())
4225:     {
4226:         self::addCssClass('carousel-indicators', $htmlOptions);
4227:         $output = self::openTag('ol', $htmlOptions);
4228:         for ($i = 0; $i < $numSlides; $i++) {
4229:             $itemOptions = array('data-target' => $target, 'data-slide-to' => $i);
4230:             if ($i === 0) {
4231:                 $itemOptions['class'] = 'active';
4232:             }
4233:             $output .= self::tag('li', $itemOptions, '', true);
4234:         }
4235:         $output .= '</ol>';
4236:         return $output;
4237:     }
4238: 
4239:     // UTILITIES
4240:     // --------------------------------------------------
4241: 
4242:     /**
4243:      * Appends new class names to the given options..
4244:      * @param mixed $className the class(es) to append.
4245:      * @param array $htmlOptions the options.
4246:      * @return array the options.
4247:      */
4248:     public static function addCssClass($className, &$htmlOptions)
4249:     {
4250:         // Always operate on arrays
4251:         if (is_string($className)) {
4252:             $className = explode(' ', $className);
4253:         }
4254:         if (isset($htmlOptions['class'])) {
4255:             $classes = array_filter(explode(' ', $htmlOptions['class']));
4256:             foreach ($className as $class) {
4257:                 $class = trim($class);
4258:                 // Don't add the class if it already exists
4259:                 if (array_search($class, $classes) === false) {
4260:                     $classes[] = $class;
4261:                 }
4262:             }
4263:             $className = $classes;
4264:         }
4265:         $htmlOptions['class'] = implode(' ', $className);
4266:     }
4267: 
4268:     /**
4269:      * Appends a CSS style string to the given options.
4270:      * @param string $style the CSS style string.
4271:      * @param array $htmlOptions the options.
4272:      * @return array the options.
4273:      */
4274:     public static function addCssStyle($style, &$htmlOptions)
4275:     {
4276:         if (is_array($style)) {
4277:             $style = implode('; ', $style);
4278:         }
4279:         $style = rtrim($style, ';');
4280:         $htmlOptions['style'] = isset($htmlOptions['style'])
4281:             ? rtrim($htmlOptions['style'], ';') . '; ' . $style
4282:             : $style;
4283:     }
4284: 
4285:     /**
4286:      * Adds the grid span class to the given options is applicable.
4287:      * @param array $htmlOptions the HTML attributes.
4288:      */
4289:     protected static function addSpanClass(&$htmlOptions)
4290:     {
4291:         $span = TbArray::popValue('span', $htmlOptions);
4292:         if (!empty($span)) {
4293:             self::addCssClass('span' . $span, $htmlOptions);
4294:         }
4295:     }
4296: 
4297:     /**
4298:      * Adds the pull class to the given options is applicable.
4299:      * @param array $htmlOptions the HTML attributes.
4300:      */
4301:     protected static function addPullClass(&$htmlOptions)
4302:     {
4303:         $pull = TbArray::popValue('pull', $htmlOptions);
4304:         if (!empty($pull)) {
4305:             self::addCssClass('pull-' . $pull, $htmlOptions);
4306:         }
4307:     }
4308: 
4309:     /**
4310:      * Adds the text align class to the given options if applicable.
4311:      * @param array $htmlOptions the HTML attributes.
4312:      */
4313:     protected static function addTextAlignClass(&$htmlOptions)
4314:     {
4315:         $align = TbArray::popValue('textAlign', $htmlOptions);
4316:         if (!empty($align)) {
4317:             self::addCssClass('text-' . $align, $htmlOptions);
4318:         }
4319:     }
4320: }
4321: 
Yiistrap API documentation generated by ApiGen 2.8.0