Commits

Christoffer Niska committed eba4c78 Merge

merged heads

Comments (0)

Files changed (35)

components/Bootstrap.php

 	}
 
 	/**
-	 * Sets the target element for the scrollspy.
-	 * @param string $selector the CSS selector
-	 * @param string $target the target CSS selector
-	 * @param string $offset the offset
-	 */
-	public function spyOn($selector, $target = null, $offset = null)
-	{
-		$script = "jQuery('{$selector}').attr('data-spy', 'scroll');";
-
-		if (isset($target))
-			$script .= "jQuery('{$selector}').attr('data-target', '{$target}');";
-
-		if (isset($offset))
-			$script .= "jQuery('{$selector}').attr('data-offset', '{$offset}');";
-
-		Yii::app()->clientScript->registerScript(__CLASS__.'.spyOn.'.$selector, $script, CClientScript::POS_BEGIN);
-	}
-
-	/**
 	 * Registers a Bootstrap JavaScript plugin.
 	 * @param string $name the name of the plugin
 	 * @param string $selector the CSS selector

demo/less/styles.less

 	}
 }
 
-#bootNavbar .navbar-inner > .container {
+#tbNavbar .navbar-inner > .container {
 	width: auto;
 }
 
-#bootMenu .menuCol {
+#tbMenu .menuCol {
 	min-height: 140px;
 }
 
-#bootTabbable .tabbable {
+#tbTabs .togglable-tabs {
 	margin-bottom: 20px;
-
-	.tab-content {
-		width: auto;
-	}
-
-	&.tabbable-placed {
-		width: 340px;
-	}
 }
 
-#bootThumbnails .list-view {
+#tbThumbnails .list-view {
 	padding-top: 30px;
 }
 
-#bootActiveForm .form-vertical .control-group > label {
+#tbActiveForm .form-vertical .control-group > label {
 	font-weight: bold;
 }
 
-#bootHero .hero-unit {
+#tbHero .hero-unit {
 	width: 650px;
 
 	h1 {
 	}
 }
 
-#bootCarousel .carousel {
+#tbCarousel .carousel {
 	width: 770px;
 
 	.carousel-caption p {
 	}
 }
 
-#bootTypeahead input {
+#tbTypeahead input {
 	margin-bottom: 0;
 }
 

demo/protected/views/site/index.php

     <h3>Sub navigation</h3>
 
     <?php $this->widget('bootstrap.widgets.TbNavbar', array(
-    'type'=>'subnav',
     'fixed'=>false,
     'brand'=>false,
     'collapse'=>true, // requires bootstrap-responsive.css
+    'subnav'=>true,
     'items'=>array(
         array(
             'class'=>'bootstrap.widgets.TbMenu',
     <h4>Source code</h4>
 
     <?php echo $phpLighter->highlight("<?php \$this->widget('bootstrap.widgets.TbNavbar', array(
-	'type'=>'subnav',
     'fixed'=>false,
     'brand'=>false,
     'collapse'=>true, // requires bootstrap-responsive.css
+    'subnav'=>true,
     'items'=>array(
         array(
             'class'=>'bootstrap.widgets.TbMenu',
         'type'=>'horizontal',
     )); ?>
 
-    <?php $this->widget('bootstrap.widgets.TbTabbable', array(
+    <?php $this->widget('bootstrap.widgets.TbTabs', array(
         'tabs'=>$this->getTabularFormTabs($form, $model),
     )); ?>
 
     'type'=>'horizontal',
 )); ?>
 
-<?php \$this->widget('bootstrap.widgets.TbTabbable', array(
+<?php \$this->widget('bootstrap.widgets.TbTabs', array(
     'tabs'=>\$this->getTabularFormTabs(\$form, \$model),
 )); ?>
 
 
 </section>
 
-<section id="tbTabbable">
+<section id="tbTabs">
 
-    <h2>Tabbable <small>bootstrap.widgets.TbTabbable</small></h2>
+    <h2>Togglable tabs <small>bootstrap.widgets.TbTabs</small></h2>
 
-    <?php $this->widget('bootstrap.widgets.TbTabbable', array(
+    <?php $this->widget('bootstrap.widgets.TbTabs', array(
     'type'=>'tabs', // 'tabs' or 'pills'
-    'htmlOptions'=>array('class'=>'tabbable'),
+    'htmlOptions'=>array('class'=>'togglable-tabs'),
     'tabs'=>$tabs,
     'events'=>array(
-        'show'=>"js:function() { console.log('Tabbable show.'); }",
-        'shown'=>"js:function() { console.log('Tabbable shown.'); }",
+        'show'=>"js:function() { console.log('Tabs show.'); }",
+        'shown'=>"js:function() { console.log('Tabs shown.'); }",
     ),
 )); ?>
 
     <h4>Source code</h4>
 
-    <?php echo $phpLighter->highlight("<?php \$this->widget('bootstrap.widgets.TbTabbable', array(
+    <?php echo $phpLighter->highlight("<?php \$this->widget('bootstrap.widgets.TbTabs', array(
 	'type'=>'tabs', // 'tabs' or 'pills'
 	'tabs'=>array(
 		array('label'=>'Home', 'content'=>'Raw denim you probably haven\'t heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.', 'active'=>true),
 	),
 )); ?>"); ?>
 
-    <div class="alert alert-block alert-warning">
-        <strong>Note!</strong>
-        Because of a bug in the current version of Bootstrap tab-content fall below the tabs even when it shouldn't. This can be fixed by setting tab-content width to auto.
-    </div>
-
     <div class="row">
 
         <div class="span6">
 
             <h3>Tabs on the top</h3>
 
-            <?php $this->widget('bootstrap.widgets.TbTabbable', array(
-            'htmlOptions'=>array('class'=>'tabbable tabbable-placed'),
-            'tabs'=>$tabbable,
-        )); ?>
+            <?php $this->widget('bootstrap.widgets.TbTabs', array(
+                'htmlOptions'=>array('class'=>'togglable-tabs'),
+                'tabs'=>$tabbable,
+            )); ?>
 
             <h3>Tabs on the left</h3>
 
-            <?php $this->widget('bootstrap.widgets.TbTabbable', array(
-            'placement'=>'left',
-            'htmlOptions'=>array('class'=>'tabbable tabbable-placed'),
-            'tabs'=>$tabbable,
-        )); ?>
+            <?php $this->widget('bootstrap.widgets.TbTabs', array(
+                'placement'=>'left',
+                'htmlOptions'=>array('class'=>'togglable-tabs'),
+                'tabs'=>$tabbable,
+            )); ?>
 
         </div>
 
 
             <h3>Tabs on the bottom</h3>
 
-            <?php $this->widget('bootstrap.widgets.TbTabbable', array(
-            'placement'=>'below',
-            'htmlOptions'=>array('class'=>'tabbable tabbable-placed'),
-            'tabs'=>$tabbable,
-        )); ?>
+            <?php $this->widget('bootstrap.widgets.TbTabs', array(
+                'placement'=>'below',
+                'htmlOptions'=>array('class'=>'togglable-tabs'),
+                'tabs'=>$tabbable,
+            )); ?>
 
             <h3>Tabs on the right</h3>
 
-            <?php $this->widget('bootstrap.widgets.TbTabbable', array(
-            'placement'=>'right',
-            'htmlOptions'=>array('class'=>'tabbable tabbable-placed'),
-            'tabs'=>$tabbable,
-        )); ?>
+            <?php $this->widget('bootstrap.widgets.TbTabs', array(
+                'placement'=>'right',
+                'htmlOptions'=>array('class'=>'togglable-tabs'),
+                'tabs'=>$tabbable,
+            )); ?>
 
         </div>
 
     </div>
 
-    <?php echo $phpLighter->highlight("<?php \$this->widget('bootstrap.widgets.TbTabbable', array(
+    <?php echo $phpLighter->highlight("<?php \$this->widget('bootstrap.widgets.TbTabs', array(
 	'type'=>'tabs',
 	'placement'=>'below', // 'above', 'right', 'below' or 'left'
 	'tabs'=>array(
 </section>
 
 <?php $this->widget('bootstrap.widgets.TbNavbar', array(
-    'type'=>'subnav',
+    'fixed'=>'top',
     'brand'=>false,
-    'fixed'=>'top',
     'collapse'=>true,
+    'subnav'=>true,
     'items'=>array(
         array(
             'class'=>'bootstrap.widgets.TbMenu',
-            'scrollspy'=>array('spy'=>'.navbar-subnav', 'offset'=>0),
+            'scrollspy'=>'.navbar-subnav',
             'items'=>array(
-                array('label'=>'Buttons', 'items'=>array(
-                    array('label'=>'Basic buttons', 'url'=>'#tbButton'),
-                    array('label'=>'Buttons groups', 'url'=>'#tbButtonGroup'),
-                )),
+                array('label'=>'Buttons', 'url'=>'#tbButton'),
+                array('label'=>'Buttons groups', 'url'=>'#tbButtonGroup'),
                 array('label'=>'Navigation', 'items'=>array(
                     array('label'=>'Breadcrumb', 'url'=>'#tbBreadcrumbs'),
                     array('label'=>'Menu', 'url'=>'#tbMenu'),
                     array('label'=>'Carousel', 'url'=>'#tbCarousel'),
                     array('label'=>'Modal', 'url'=>'#tbModal'),
                     array('label'=>'Popover', 'url'=>'#tbPopover'),
-                    array('label'=>'Tabbable', 'url'=>'#tbTabbable'),
+                    array('label'=>'Tabs', 'url'=>'#tbTabs'),
                     array('label'=>'Tooltip', 'url'=>'#tbTooltip'),
                     array('label'=>'Typeahead', 'url'=>'#tbTypeahead'),
                 )),

demo/protected/views/site/setup.php

 
 </section>
 
-<div class="subnav well">
-
-	<?php $this->widget('bootstrap.widgets.TbMenu', array(
-		'type'=>'list',
-		'scrollspy'=>array('spy'=>'.subnav', 'offset'=>50),
-		'items'=>array(
-			array('label'=>'Setup', 'url'=>'#setup'),
-			array('label'=>'Configuration', 'url'=>'#config'),
-			array('label'=>'Using LESS', 'url'=>'#less'),
-			array('label'=>'Plugin API', 'url'=>'#api')
-		),
-	)); ?>
+<?php $this->widget('bootstrap.widgets.TbNavbar', array(
+    'fixed'=>'top',
+    'brand'=>false,
+    'collapse'=>true,
+    'subnav'=>true,
+    'items'=>array(
+        array(
+            'class'=>'bootstrap.widgets.TbMenu',
+            'scrollspy'=>'.navbar-subnav',
+            'items'=>array(
+                array('label'=>'Setup', 'url'=>'#setup'),
+                array('label'=>'Configuration', 'url'=>'#config'),
+                array('label'=>'Using LESS', 'url'=>'#less'),
+                array('label'=>'Plugin API', 'url'=>'#api')
+            ),
+        ),
+    ),
+)); ?>
 
 </div>

widgets/TbActiveForm.php

 <?php
 /**
- * BootActiveForm class file.
+ * TbActiveForm class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License

widgets/TbAlert.php

 <?php
 /**
- * BootAlert class file.
+ * TbAlert class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright  Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap alert widget.
+ * @see http://twitter.github.com/bootstrap/javascript.html#alerts
  */
 class TbAlert extends CWidget
 {
 	 */
 	public $template = '<div class="alert alert-block alert-{key}{class}"><a class="close" data-dismiss="alert">&times;</a>{message}</div>';
 	/**
-	 * @var string[] the JavaScript event handlers.
+	 * @var string[] the Javascript event handlers.
 	 */
 	public $events = array();
 	/**
 	 */
 	public function init()
 	{
-		parent::init();
-
 		if (!isset($this->htmlOptions['id']))
 			$this->htmlOptions['id'] = $this->getId();
 	}
 	 */
 	public function run()
 	{
-		$id = $this->id;
+		$id = $this->htmlOptions['id'];
 
 		if (is_string($this->keys))
 			$this->keys = array($this->keys);
 		foreach ($this->events as $name => $handler)
 		{
 			$handler = CJavaScript::encode($handler);
-			$cs->registerScript(__CLASS__.'#'.$id.'_'.$name, "jQuery('{$selector}').on('".$name."', {$handler});");
+			$cs->registerScript(__CLASS__.'#'.$id.'_'.$name, "jQuery('{$selector}').on('{$name}', {$handler});");
 		}
 	}
 }

widgets/TbBadge.php

 <?php
 /**
- * BootBadge class file.
+ * TbBadge class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright  Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap badge widget.
+ * @see http://twitter.github.com/bootstrap/components.html#badges
  */
 class TbBadge extends CWidget
 {
 	const TYPE_INVERSE = 'inverse';
 
 	/**
-	 * @var string the badge type (defaults to '').
+	 * @var string the badge type.
 	 * Valid types are 'success', 'warning', 'important', 'info' and 'inverse'.
 	 */
 	public $type;

widgets/TbBreadcrumbs.php

 <?php
 /**
- * BootCrumb class file.
+ * TbCrumb class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap breadcrumb widget.
+ * @see http://twitter.github.com/bootstrap/components.html#breadcrumbs
  */
 class TbBreadcrumbs extends CBreadcrumbs
 {
 	/**
-	 * @var string the separator between links in the breadcrumbs (defaults to ' / ').
+	 * @var string the separator between links in the breadcrumbs. Defaults to '/'.
 	 */
 	public $separator = '/';
 
 	 */
 	public function init()
 	{
-		$classes = 'breadcrumb';
 		if (isset($this->htmlOptions['class']))
-			$this->htmlOptions['class'] .= ' '.$classes;
+			$this->htmlOptions['class'] .= ' breadcrumb';
 		else
-			$this->htmlOptions['class'] = $classes;
+			$this->htmlOptions['class'] = 'breadcrumb';
 	}
 
 	/**
 	 */
 	public function run()
 	{
+        // Hide empty breadcrumbs.
 		if (empty($this->links))
 			return;
 
 				$links[] = $this->renderItem($this->encodeLabel ? CHtml::encode($url) : $url, true);
 		}
 
-		echo CHtml::openTag('ul', $this->htmlOptions);
-		echo implode('', $links);
-		echo '</ul>';
+		echo CHtml::tag('ul', $this->htmlOptions, implode('', $links));
 	}
 
 	/**

widgets/TbButton.php

 <?php
 /**
- * BootButton class file.
+ * TbButton class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap button widget.
+ * @see http://twitter.github.com/bootstrap/base-css.html#buttons
  */
 class TbButton extends CWidget
 {
 	// Button sizes.
 	const SIZE_MINI = 'mini';
 	const SIZE_SMALL = 'small';
-	const SIZE_NORMAL = '';
 	const SIZE_LARGE = 'large';
 
 	/**
 	public $type;
 	/**
 	 * @var string the button size.
-	 * Valid values are '', 'small' and 'large'.
+	 * Valid values are 'small' and 'large'.
 	 */
-	public $size = self::SIZE_NORMAL;
+	public $size;
 	/**
 	 * @var string the button icon, e.g. 'ok' or 'remove white'.
 	 */
 			$this->label = '<i class="'.$this->icon.'"></i> '.$this->label;
 		}
 
-		$this->initHTML5Data();
-	}
+        if (isset($this->toggle))
+            $this->htmlOptions['data-toggle'] = 'button';
 
-	/**
-	 * Initializes the HTML5 data attributes used by the data-api.
-	 */
-	protected function initHTML5Data()
-	{
-		if (isset($this->toggle) || isset($this->loadingText) || isset($this->completeText))
-		{
-			if (isset($this->toggle))
-				$this->htmlOptions['data-toggle'] = 'button';
+        if (isset($this->loadingText))
+            $this->htmlOptions['data-loading-text'] = $this->loadingText;
 
-			if (isset($this->loadingText))
-				$this->htmlOptions['data-loading-text'] = $this->loadingText;
-
-			if (isset($this->completeText))
-				$this->htmlOptions['data-complete-text'] = $this->completeText;
-		}
+        if (isset($this->completeText))
+            $this->htmlOptions['data-complete-text'] = $this->completeText;
 	}
 
 	/**

widgets/TbButtonColumn.php

 <?php
 /**
- * BootButtonColumn class file.
+ * TbButtonColumn class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright  Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License

widgets/TbButtonGroup.php

 <?php
 /**
- * BootButtonGroup class file.
+ * TbButtonGroup class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap button group widget.
+ * @see http://twitter.github.com/bootstrap/components.html#buttonGroups
  */
 class TbButtonGroup extends CWidget
 {
 	 * @var string the button size.
 	 * @see BootButton::size
 	 */
-	public $size = TbButton::SIZE_NORMAL;
+	public $size;
 	/**
 	 * @var boolean indicates whether to encode the button labels.
 	 */
 
 		foreach ($this->buttons as $button)
 		{
-            if (isset($button['visible']) && !$button['visible'] === false)
+            if (isset($button['visible']) && $button['visible'] === false)
                 continue;
 
 			$this->controller->widget('bootstrap.widgets.TbButton', array(
 				'buttonType'=>isset($button['buttonType']) ? $button['buttonType'] : $this->buttonType,
 				'type'=>isset($button['type']) ? $button['type'] : $this->type,
-				'size'=>$this->size,
+				'size'=>$this->size, // all buttons in a group cannot vary in size
 				'icon'=>isset($button['icon']) ? $button['icon'] : null,
 				'label'=>isset($button['label']) ? $button['label'] : null,
 				'url'=>isset($button['url']) ? $button['url'] : null,

widgets/TbCarousel.php

 <?php
 /**
- * BootCarousel class file.
+ * TbCarousel class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap carousel widget.
+ * @see http://twitter.github.com/bootstrap/javascript.html#carousel
  */
 class TbCarousel extends CWidget
 {
 	/**
-	 * @var string the previous button content.
+	 * @var string the previous button label. Defaults to '&lsaquo;'.
 	 */
-	public $prev = '&lsaquo;';
+	public $prevLabel = '&lsaquo;';
 	/**
-	 * @var string the next button content.
+	 * @var string the next button label. Defaults to '&rsaquo;'.
 	 */
-	public $next = '&rsaquo;';
+	public $nextLabel = '&rsaquo;';
     /**
-     * @var boolean whether the carousel should slide items.
+     * @var boolean indicates whether the carousel should slide items.
      */
     public $slide = true;
 	/**
-	 * @var boolean whether to display the previous and next links.
+	 * @var boolean indicates whether to display the previous and next links.
 	 */
 	public $displayPrevAndNext = true;
 	/**
 	 */
 	public $items = array();
 	/**
-	 * @var array the options for the Bootstrap JavaScript plugin.
+	 * @var array the options for the Bootstrap Javascript plugin.
 	 */
 	public $options = array();
 	/**
-	 * @var string[] the JavaScript event handlers.
+	 * @var string[] the Javascript event handlers.
 	 */
 	public $events = array();
 	/**
 		if ($this->displayPrevAndNext)
 		{
 			echo '</div>';
-			echo '<a class="carousel-control left" href="#'.$id.'" data-slide="prev">'.$this->prev.'</a>';
-			echo '<a class="carousel-control right" href="#'.$id.'" data-slide="next">'.$this->next.'</a>';
+			echo '<a class="carousel-control left" href="#'.$id.'" data-slide="prev">'.$this->prevLabel.'</a>';
+			echo '<a class="carousel-control right" href="#'.$id.'" data-slide="next">'.$this->nextLabel.'</a>';
 			echo '</div>';
 		}
 
 		foreach ($this->events as $name => $handler)
 		{
 			$handler = CJavaScript::encode($handler);
-			$cs->registerScript(__CLASS__.'#'.$id.'_'.$name, "jQuery('#{$id}').on('".$name."', {$handler});");
+			$cs->registerScript(__CLASS__.'#'.$id.'_'.$name, "jQuery('#{$id}').on('{$name}', {$handler});");
 		}
 	}
 
 			if (!is_array($item))
 				continue;
 
+            if (isset($item['visible']) && $item['visible'] === false)
+                continue;
+
 			if (!isset($item['itemOptions']))
 				$item['itemOptions'] = array();
 
 			if ($i === 0)
 				$classes[] = 'active';
 
-			$classes = implode(' ', $classes);
-			if (isset($item['itemOptions']['class']))
-				$item['itemOptions']['class'] .= ' '.$classes;
-			else
-				$item['itemOptions']['class'] = $classes;
+            if (!empty($classes))
+            {
+                $classes = implode(' ', $classes);
+                if (isset($item['itemOptions']['class']))
+                    $item['itemOptions']['class'] .= ' '.$classes;
+                else
+                    $item['itemOptions']['class'] = $classes;
+            }
 
 			echo CHtml::openTag('div', $item['itemOptions']);
 
 				if (!isset($item['captionOptions']))
 					$item['captionOptions'] = array();
 
-				$classes = 'carousel-caption';
 				if (isset($item['captionOptions']['class']))
-					$item['captionOptions']['class'] .= ' '.$classes;
+					$item['captionOptions']['class'] .= ' carousel-caption';
 				else
-					$item['captionOptions']['class'] = $classes;
+					$item['captionOptions']['class'] = 'carousel-caption';
 
 				echo CHtml::openTag('div', $item['captionOptions']);
 

widgets/TbCollapse.php

+<?php
+/**
+ * TbCollapse class file.
+ * @author Christoffer Niska <ChristofferNiska@gmail.com>
+ * @copyright Copyright &copy; Christoffer Niska 2012-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @package bootstrap.widgets
+ * @since 1.0.0
+ */
+
+/**
+ * Bootstrap collapse widget.
+ * @see http://twitter.github.com/bootstrap/javascript.html#collapse
+ */
+class TbCollapse extends CWidget
+{
+    /**
+     * @var string the name of the collapse element. Defaults to 'a'.
+     */
+    public $tagName = 'div';
+    /**
+     * @var boolean the CSS selector for element to collapse. Default to 'false'.
+     */
+    public $parent = false;
+    /**
+     * @var boolean indicates whether to toggle the collapsible element on invocation.
+     */
+    public $toggle = true;
+    /**
+     * @var array the options for the Bootstrap Javascript plugin.
+     */
+    public $options = array();
+    /**
+     * @var string[] the Javascript event handlers.
+     */
+    public $events = array();
+    /**
+    * @var array the HTML attributes for the widget container.
+    */
+    public $htmlOptions = array();
+
+    /**
+     * Initializes the widget.
+     */
+    public function init()
+    {
+        if (!isset($this->htmlOptions['id']))
+            $this->htmlOptions['id'] = $this->getId();
+
+        if (isset($this->parent) && !isset($this->options['parent']))
+            $this->options['parent'] = $this->parent;
+
+        if (isset($this->toggle) && !isset($this->options['toggle']))
+            $this->options['toggle'] = $this->toggle;
+
+        echo CHtml::tag($this->tagName, $this->htmlOptions);
+    }
+
+    /**
+     * Runs the widget.
+     */
+    public function run()
+    {
+        $id = $this->htmlOptions['id'];
+
+        echo CHtml::closeTag($this->tagName);
+
+        /** @var CClientScript $cs */
+        $cs = Yii::app()->getClientScript();
+        $options = !empty($this->options) ? CJavaScript::encode($this->options) : '';
+        $cs->registerScript(__CLASS__.'#'.$id, "jQuery('#{$id}').collapse({$options});");
+
+        foreach ($this->events as $name => $handler)
+        {
+            $handler = CJavaScript::encode($handler);
+            $cs->registerScript(__CLASS__.'#'.$id.'_'.$name, "jQuery('#{$id}').on('{$name}', {$handler});");
+        }
+    }
+}
+

widgets/TbDataColumn.php

 <?php
 /**
- * BootDataColumn class file.
+ * TbDataColumn class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 Yii::import('zii.widgets.grid.CDataColumn');
 
 /**
- * Bootstrap grid data column
+ * Bootstrap grid data column.
  */
 class TbDataColumn extends CDataColumn
 {

widgets/TbDetailView.php

 <?php
 /**
- * BootDetailView class file.
+ * TbDetailView class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 Yii::import('zii.widgets.CDetailView');
 
 /**
- * Bootstrap detail view widget.
- * Used for setting default HTML classes and disabling the default CSS.
+ * Bootstrap Zii detail view.
  */
 class TbDetailView extends CDetailView
 {

widgets/TbDropdown.php

 
 Yii::import('bootstrap.widgets.TbBaseMenu');
 
+/**
+ * Bootstrap dropdown menu.
+ * @see http://twitter.github.com/bootstrap/javascript.html#dropdowns
+ */
 class TbDropdown extends TbBaseMenu
 {
     /**
     }
 
     /**
-     * Returns the divider css class.
+     * Returns the divider CSS class.
      * @return string the class name
      */
     public function getDividerCssClass()

widgets/TbGridView.php

 <?php
 /**
- * BootGridView class file.
+ * TbGridView class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 Yii::import('bootstrap.widgets.TbDataColumn');
 
 /**
- * Bootstrap grid view widget.
- * Used for setting default HTML classes, disabling the default CSS and enable the bootstrap pager.
+ * Bootstrap Zii grid view.
  */
 class TbGridView extends CGridView
 {
 	 */
 	public $type;
 	/**
-	 * @var string the CSS class name for the pager container.
-	 * Defaults to 'pagination'.
+	 * @var string the CSS class name for the pager container. Defaults to 'pagination'.
 	 */
 	public $pagerCssClass = 'pagination';
 	/**
 
 		$column = new TbDataColumn($this);
 		$column->name = $matches[1];
+
 		if (isset($matches[3]) && $matches[3] !== '')
 			$column->type = $matches[3];
 

widgets/TbHeroUnit.php

 <?php
 /**
- * BootHero class file.
+ * TbHeroUnit class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright  Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  */
 
 /**
- * Modest bootstrap hero widget.
+ * Modest bootstrap hero unit widget.
  * Thanks to Christphe Boulain for suggesting content capturing.
+ * @see http://twitter.github.com/bootstrap/components.html#typography
  */
 class TbHeroUnit extends CWidget
 {
 	 * @var array the HTML attributes for the widget container.
 	 */
 	public $htmlOptions = array();
+    /**
+     * @var array the HTML attributes for the heading element.
+     * @since 1.0.0
+     */
+    public $headingOptions = array();
 
 	/**
 	 * Initializes the widget.
 		if ($this->encodeHeading)
 			$this->heading = CHtml::encode($this->heading);
 
-		ob_start();
-		ob_implicit_flush(false);
+        echo CHtml::openTag('div', $this->htmlOptions);
+
+        if (isset($this->heading))
+            echo CHtml::tag('h1', $this->headingOptions, $this->heading);
 	}
 
 	/**
 	 */
 	public function run()
 	{
-		$content = ob_get_clean();
-		echo CHtml::openTag('div', $this->htmlOptions);
-
-		if (isset($this->heading))
-			echo CHtml::tag('h1', array(), $this->heading);
-
-		echo $content;
 		echo '</div>';
 	}
 }

widgets/TbLabel.php

 <?php
 /**
- * BootLabel class file.
+ * TbLabel class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright  Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap label widget.
+ * @see http://twitter.github.com/bootstrap/components.html#labels
  */
 class TbLabel extends CWidget
 {
 	const TYPE_INVERSE = 'inverse';
 
 	/**
-	 * @var string the label type (defaults to '').
+	 * @var string the label type.
 	 * Valid types are 'success', 'warning', 'important', 'info' and 'inverse'.
 	 */
 	public $type;

widgets/TbListView.php

 <?php
 /**
- * BootListView class file.
+ * TbListView class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 Yii::import('zii.widgets.CListView');
 
 /**
- * Bootstrap list view.
- * Used to enable the bootstrap pager.
+ * Bootstrap Zii list view.
  */
 class TbListView extends CListView
 {
 	public $pagerCssClass = 'pagination';
 	/**
 	 * @var array the configuration for the pager.
+     * Defaults to <code>array('class'=>'ext.bootstrap.widgets.TbPager')</code>.
 	 */
 	public $pager = array('class'=>'bootstrap.widgets.TbPager');
 	/**

widgets/TbMenu.php

 
 Yii::import('bootstrap.widgets.TbBaseMenu');
 
+/**
+ * Bootstrap menu.
+ * @see http://twitter.github.com/bootstrap/components.html#navs
+ */
 class TbMenu extends TbBaseMenu
 {
     // Menu types.
 
     /**
      * @var string the menu type.
-     * Valid values are 'tabs' and 'pills'. Defaults to ''.
+     * Valid values are 'tabs' and 'pills'.
      */
     public $type;
     /**
      */
     public $stacked = false;
     /**
-     * @var array the scroll-spy configuration.
+     * @var string|array the scrollspy configuration.
      */
     public $scrollspy;
 	/**
     	if ($this->dropup === true)
 			$classes[] = 'dropup';
 
+        if (isset($this->scrollspy))
+        {
+            $scrollspy = is_string($this->scrollspy) ? array('target'=>$this->scrollspy) : $this->scrollspy;
+            $this->widget('bootstrap.widgets.TbScrollSpy', $scrollspy);
+        }
+
         if (!empty($classes))
         {
             $classes = implode(' ', $classes);
             else
                 $this->htmlOptions['class'] = $classes;
         }
-
-        if (isset($this->scrollspy) && is_array($this->scrollspy) && isset($this->scrollspy['spy']))
-        {
-            if (!isset($this->scrollspy['subject']))
-                $this->scrollspy['subject'] = 'body';
-
-            if (!isset($this->scrollspy['offset']))
-                $this->scrollspy['offset'] = null;
-
-            Yii::app()->bootstrap->spyOn($this->scrollspy['subject'], $this->scrollspy['spy'], $this->scrollspy['offset']);
-        }
     }
 
     /**

widgets/TbModal.php

 <?php
 /**
- * BootModal class file.
+ * TbModal class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap modal widget.
+ * @see http://twitter.github.com/bootstrap/javascript.html#modals
  */
 class TbModal extends CWidget
 {
 	/**
-	 * @var boolean indicates whether to automatically open the modal when initialized.
+	 * @var boolean indicates whether to automatically open the modal when initialized. Defaults to 'false'.
 	 */
 	public $autoOpen = false;
     /**
-     * @var boolean indicates whether the modal should use transitions.
+     * @var boolean indicates whether the modal should use transitions. Defaults to 'true'.
      */
     public $fade = true;
 	/**
-	 * @var array the options for the Bootstrap JavaScript plugin.
+	 * @var array the options for the Bootstrap Javascript plugin.
 	 */
 	public $options = array();
 	/**
-	 * @var string[] the JavaScript event handlers.
+	 * @var string[] the Javascript event handlers.
 	 */
 	public $events = array();
 	/**
 		if (!isset($this->htmlOptions['id']))
 			$this->htmlOptions['id'] = $this->getId();
 
-		if (!$this->autoOpen && !isset($this->options['show']))
+		if ($this->autoOpen === false && !isset($this->options['show']))
 			$this->options['show'] = false;
 
         $classes = array('modal');
                 $this->htmlOptions['class'] = $classes;
         }
 
-		echo CHtml::openTag('div', $this->htmlOptions).PHP_EOL;
+		echo CHtml::openTag('div', $this->htmlOptions);
 	}
 
 	/**
 	 */
 	public function run()
 	{
-		$id = $this->id;
+		$id = $this->htmlOptions['id'];
 
 		echo '</div>';
 
 		foreach ($this->events as $name => $handler)
 		{
 			$handler = CJavaScript::encode($handler);
-			$cs->registerScript(__CLASS__.'#'.$id.'_'.$name, "jQuery('#{$id}').on('".$name."', {$handler});");
+			$cs->registerScript(__CLASS__.'#'.$id.'_'.$name, "jQuery('#{$id}').on('{$name}', {$handler});");
 		}
 	}
 }

widgets/TbNavbar.php

 <?php
 /**
- * BootNavbar class file.
+ * TbNavbar class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 	const FIXED_TOP = 'top';
 	const FIXED_BOTTOM = 'bottom';
 
-    /**
-     * @var string the navbar type.
-     */
-    public $type;
 	/**
 	 * @var string the text for the brand.
 	 */
 	 * @var boolean whether to enable collapsing on narrow screens. Default to false.
 	 */
 	public $collapse = false;
+    /**
+     * @var string indicates whether this is a sub navigation.
+     * @since 1.0.0
+     */
+    public $subnav = false;
 	/**
 	 * @var array the HTML attributes for the widget container.
 	 */
 
 		$classes = array('navbar');
 
-        if (isset($this->type) && in_array($this->type, array(self::TYPE_SUBNAV)))
-            $classes[] = 'navbar-'.$this->type;
+        if (isset($this->subnav) && $this->subnav === true)
+            $classes[] = 'navbar-subnav';
 
 		if ($this->fixed !== false)
 		{
             {
 				$classes[] = 'navbar-fixed-'.$this->fixed;
 
-                if ($this->type === self::TYPE_SUBNAV)
+                if (isset($this->subnav) && $this->subnav === true)
                     $classes[] = 'navbar-subnav-fixed';
             }
 		}
             echo CHtml::openTag('a', $this->brandOptions).$this->brand.'</a>';
 
 		if ($this->collapse)
-			echo '<div class="nav-collapse '.$this->getCollapseCssClass().'">';
+        {
+            $this->controller->beginWidget('bootstrap.widgets.TbCollapse', array(
+                'htmlOptions'=>array('class'=>$this->getCollapseCssClass()),
+            ));
+        }
 
 		foreach ($this->items as $item)
 		{
 		}
 
 		if ($this->collapse)
-			echo '</div>';
+            $this->controller->endWidget();
 
 		echo '</div></div></div>';
 	}
      */
     protected function getCollapseTarget()
     {
-        return !isset($this->type) ? 'nav-collapse' : 'subnav-collapse';
+        return isset($this->subnav) && $this->subnav === true ? 'subnav-collapse' : 'nav-collapse';
     }
 
     /**
      */
     protected function getCollapseCssClass()
     {
-        return !isset($this->type) ? 'nav-collapse' : 'nav-collapse subnav-collapse';
+        return isset($this->subnav) && $this->subnav === true ? 'nav-collapse subnav-collapse' : 'nav-collapse';
     }
 }

widgets/TbPager.php

 <?php
 /**
- * BootPager class file.
+ * TbPager class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  */
 
 /**
- * Bootstrap pager widget.
+ * Bootstrap pager.
+ * @see http://twitter.github.com/bootstrap/components.html#pagination
  */
 class TbPager extends CLinkPager
 {
 	const ALIGNMENT_RIGHT = 'right';
 
 	/**
-	 * @var string the pager alignment (default to '').
-	 * Valid values are 'left', 'centered' and 'right'.
+	 * @var string the pager alignment. Valid values are 'centered' and 'right'.
 	 */
 	public $alignment;
 	/**
-	 * @var string the text shown before page buttons (defaults to '').
+	 * @var string the text shown before page buttons.
+     * Defaults to an empty string, meaning that no header will be displayed.
 	 */
 	public $header = '';
 	/**

widgets/TbProgress.php

 <?php
 /**
- * BootProgress class file.
+ * TbProgress class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap progress bar widget.
+ * @see http://twitter.github.com/bootstrap/components.html#progress
  */
 class TbProgress extends CWidget
 {
 	const TYPE_DANGER = 'danger';
 
 	/**
-	 * @var string the bar type.
-	 * Valid values are 'info', 'success', and 'danger'.
+	 * @var string the bar type. Valid values are 'info', 'success', and 'danger'.
 	 */
 	public $type;
 	/**
-	 * @var boolean whether the bar is striped.
+	 * @var boolean indicates whether the bar is striped.
 	 */
 	public $striped = false;
 	/**
-	 * @var boolean whether the bar is animated.
+	 * @var boolean indicates whether the bar is animated.
 	 */
 	public $animated = false;
 	/**
-	 * @var integer the progress.
+	 * @var integer the amount of progress in percent.
 	 */
 	public $percent = 0;
 	/**

widgets/TbScrollSpy.php

+<?php
+/**
+ * TbScrollSpy class file.
+ * @author Christoffer Niska <ChristofferNiska@gmail.com>
+ * @copyright Copyright &copy; Christoffer Niska 2012-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @package bootstrap.widgets
+ * @since 1.0.0
+ */
+
+/**
+ * Bootstrap scrollspy widget.
+ * @see http://twitter.github.com/bootstrap/javascript.html#scrollspy
+ */
+class TbScrollSpy extends CWidget
+{
+    /**
+     * @var string the CSS selector for the scrollspy element. Defaults to 'body'.
+     */
+    public $selector = 'body';
+    /**
+     * @var string the CSS selector for the spying element.
+     */
+    public $target;
+    /**
+     * @var integer the scroll offset (in pixels).
+     */
+    public $offset;
+    /**
+     * @var array string[] the Javascript event handlers.
+     */
+    public $events = array();
+
+    /**
+     * Runs the widget.
+     */
+    public function run()
+    {
+        $script = "jQuery('{$this->selector}').attr('data-spy', 'scroll');";
+
+        if (isset($this->target))
+            $script .= "jQuery('{$this->selector}').attr('data-target', '{$this->target}');";
+
+        if (isset($this->offset))
+            $script .= "jQuery('{$this->selector}').attr('data-offset', '{$this->offset}');";
+
+        /** @var CClientScript $cs */
+        $cs = Yii::app()->getClientScript();
+        $cs->registerScript(__CLASS__.'#'.$this->selector, $script, CClientScript::POS_BEGIN);
+
+        foreach ($this->events as $name => $handler)
+        {
+            $handler = CJavaScript::encode($handler);
+            $cs->registerScript(__CLASS__.'#'.$this->selector.'_'.$name, "jQuery('{$this->selector}').on('{$name}', {$handler});");
+        }
+    }
+}
+

widgets/TbTabbable.php

-<?php
-/**
- * BootTabbable class file.
- * @author Christoffer Niska <ChristofferNiska@gmail.com>
- * @copyright Copyright &copy; Christoffer Niska 2011-
- * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
- * @package bootstrap.widgets
- */
-
-Yii::import('bootstrap.widgets.TbMenu');
-
-/**
- * Bootstrap JavaScript tabs widget.
- * @since 0.9.8
- */
-class TbTabbable extends CWidget
-{
-	// Tab placements.
-	const PLACEMENT_ABOVE = 'above';
-	const PLACEMENT_BELOW = 'below';
-	const PLACEMENT_LEFT = 'left';
-	const PLACEMENT_RIGHT = 'right';
-
-	/**
-	 * @var string the type of tabs to display. Defaults to 'tabs'.
-	 * Valid values are 'tabs' and 'pills'.
-	 * Please not that JavaScript pills are not fully supported in Bootstrap!
-	 */
-    public $type = TbMenu::TYPE_TABS;
-	/**
-	 * @var string the placement of the tabs.
-	 * Valid values are 'above', 'below', 'left' and 'right'.
-	 */
-	public $placement;
-	/**
-	 * @var array the tab configuration.
-	 */
-    public $tabs = array();
-	/**
-	 * @var boolean whether to encode item labels.
-	 */
-	public $encodeLabel = true;
-	/**
-	 * @var string[] the JavaScript event handlers.
-	 */
-	public $events = array();
-	/**
-	 * @var array the HTML attributes for the widget container.
-	 */
-	public $htmlOptions = array();
-
-    /**
-     * Initializes the widget.
-     */
-    public function init()
-    {
-		if (!isset($this->htmlOptions['id']))
-			$this->htmlOptions['id'] = $this->getId();
-
-        $classes = array();
-
-        $validPlacements = array(self::PLACEMENT_ABOVE, self::PLACEMENT_BELOW, self::PLACEMENT_LEFT, self::PLACEMENT_RIGHT);
-
-        if (isset($this->placement) && in_array($this->placement, $validPlacements))
-            $classes[] = 'tabs-'.$this->placement;
-
-        if (!empty($classes))
-		{
-			$classes = implode(' ', $classes);
-			if (isset($this->htmlOptions['class']))
-				$this->htmlOptions['class'] .= ' '.$classes;
-			else
-				$this->htmlOptions['class'] = $classes;
-		}
-    }
-
-    /**
-     * Run this widget.
-     */
-    public function run()
-    {
-	    $id = $this->id;
-	    $content = array();
-	    $items = $this->normalizeTabs($this->tabs, $content);
-
-		ob_start();
-		$this->controller->widget('bootstrap.widgets.TbMenu', array(
-			'type'=>$this->type,
-			'encodeLabel'=>$this->encodeLabel,
-			'items'=>$items,
-		));
-		$tabs = ob_get_clean();
-
-		ob_start();
-		echo '<div class="tab-content">';
-		echo implode('', $content);
-		echo '</div>';
-		$content = ob_get_clean();
-
-		echo CHtml::openTag('div', $this->htmlOptions);
-		echo $this->placement === self::PLACEMENT_BELOW ? $content.$tabs : $tabs.$content;
-		echo '</div>';
-
-	    /** @var CClientScript $cs */
-	    $cs = Yii::app()->getClientScript();
-	    $cs->registerScript(__CLASS__.'#'.$id, "jQuery('#{$id}').tab('show');");
-
-	    foreach ($this->events as $name => $handler)
-        {
-            $handler = CJavaScript::encode($handler);
-            $cs->registerScript(__CLASS__.'#'.$id.'_'.$name, "jQuery('#{$id}').on('".$name."', {$handler});");
-        }
-    }
-
-	/**
-	 * Normalizes the tab configuration.
-	 * @param array $tabs the tab configuration
-	 * @param array $panes a reference to the panes array
-	 * @param integer $i the current index
-	 * @return array the items
-	 */
-	protected function normalizeTabs($tabs, &$panes, &$i = 0)
-	{
-		$id = $this->getId();
-		$items = array();
-
-	    foreach ($tabs as $tab)
-	    {
-			$item = $tab;
-
-		    if (isset($item['visible']) && !$item['visible'])
-                continue;
-
-			if (!isset($item['itemOptions']))
-				$item['itemOptions'] = array();
-
-			$item['linkOptions']['data-toggle'] = 'tab';
-
-		    if (isset($tab['items']))
-				$item['items'] = $this->normalizeTabs($item['items'], $panes, $i);
-			else
-			{
-				if (!isset($item['id']))
-					$item['id'] = $id.'_tab_'.($i + 1);
-
-				$item['url'] = '#'.$item['id'];
-
-				if (!isset($item['content']))
-					$item['content'] = '';
-
-				$content = $item['content'];
-				unset($item['content']);
-
-				if (!isset($item['paneOptions']))
-					$item['paneOptions'] = array();
-
-				$paneOptions = $item['paneOptions'];
-				unset($item['paneOptions']);
-
-				$paneOptions['id'] = $item['id'];
-
-				$classes = array('tab-pane fade');
-
-				if (isset($item['active']) && $item['active'])
-					$classes[] = 'active in';
-
-				$classes = implode(' ', $classes);
-				if (isset($paneOptions['class']))
-					$paneOptions['class'] .= ' '.$classes;
-				else
-					$paneOptions['class'] = $classes;
-
-				$panes[] = CHtml::tag('div', $paneOptions, $content);
-
-				$i++; // increment the tab-index
-			}
-
-			$items[] = $item;
-	    }
-
-		return $items;
-	}
-}

widgets/TbTabs.php

+<?php
+/**
+ * TbTabs class file.
+ * @author Christoffer Niska <ChristofferNiska@gmail.com>
+ * @copyright Copyright &copy; Christoffer Niska 2011-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @package bootstrap.widgets
+ */
+
+Yii::import('bootstrap.widgets.TbMenu');
+
+/**
+ * Bootstrap Javascript tabs widget.
+ * @see http://twitter.github.com/bootstrap/javascript.html#tabs
+ */
+class TbTabs extends CWidget
+{
+	// Tab placements.
+	const PLACEMENT_ABOVE = 'above';
+	const PLACEMENT_BELOW = 'below';
+	const PLACEMENT_LEFT = 'left';
+	const PLACEMENT_RIGHT = 'right';
+
+	/**
+	 * @var string the type of tabs to display. Defaults to 'tabs'. Valid values are 'tabs' and 'pills'.
+	 * Please not that Javascript pills are not fully supported in Bootstrap yet!
+     * @see TbMenu::$type
+	 */
+    public $type = TbMenu::TYPE_TABS;
+	/**
+	 * @var string the placement of the tabs.
+	 * Valid values are 'above', 'below', 'left' and 'right'.
+	 */
+	public $placement;
+	/**
+	 * @var array the tab configuration.
+	 */
+    public $tabs = array();
+	/**
+	 * @var boolean whether to encode item labels.
+	 */
+	public $encodeLabel = true;
+	/**
+	 * @var string[] the Javascript event handlers.
+	 */
+	public $events = array();
+	/**
+	 * @var array the HTML attributes for the widget container.
+	 */
+	public $htmlOptions = array();
+
+    /**
+     * Initializes the widget.
+     */
+    public function init()
+    {
+		if (!isset($this->htmlOptions['id']))
+			$this->htmlOptions['id'] = $this->getId();
+
+        $classes = array();
+
+        $validPlacements = array(self::PLACEMENT_ABOVE, self::PLACEMENT_BELOW, self::PLACEMENT_LEFT, self::PLACEMENT_RIGHT);
+
+        if (isset($this->placement) && in_array($this->placement, $validPlacements))
+            $classes[] = 'tabs-'.$this->placement;
+
+        if (!empty($classes))
+		{
+			$classes = implode(' ', $classes);
+			if (isset($this->htmlOptions['class']))
+				$this->htmlOptions['class'] .= ' '.$classes;
+			else
+				$this->htmlOptions['class'] = $classes;
+		}
+    }
+
+    /**
+     * Run this widget.
+     */
+    public function run()
+    {
+	    $id = $this->id;
+	    $content = array();
+	    $items = $this->normalizeTabs($this->tabs, $content);
+
+		ob_start();
+		$this->controller->widget('bootstrap.widgets.TbMenu', array(
+			'type'=>$this->type,
+			'encodeLabel'=>$this->encodeLabel,
+			'items'=>$items,
+		));
+		$tabs = ob_get_clean();
+
+		ob_start();
+		echo '<div class="tab-content">';
+		echo implode('', $content);
+		echo '</div>';
+		$content = ob_get_clean();
+
+		echo CHtml::openTag('div', $this->htmlOptions);
+		echo $this->placement === self::PLACEMENT_BELOW ? $content.$tabs : $tabs.$content;
+		echo '</div>';
+
+	    /** @var CClientScript $cs */
+	    $cs = Yii::app()->getClientScript();
+	    $cs->registerScript(__CLASS__.'#'.$id, "jQuery('#{$id}').tab('show');");
+
+	    foreach ($this->events as $name => $handler)
+        {
+            $handler = CJavaScript::encode($handler);
+            $cs->registerScript(__CLASS__.'#'.$id.'_'.$name, "jQuery('#{$id}').on('{$name}', {$handler});");
+        }
+    }
+
+	/**
+	 * Normalizes the tab configuration.
+	 * @param array $tabs the tab configuration
+	 * @param array $panes a reference to the panes array
+	 * @param integer $i the current index
+	 * @return array the items
+	 */
+	protected function normalizeTabs($tabs, &$panes, &$i = 0)
+	{
+		$id = $this->getId();
+		$items = array();
+
+	    foreach ($tabs as $tab)
+	    {
+			$item = $tab;
+
+		    if (isset($item['visible']) && $item['visible'] === false)
+                continue;
+
+			if (!isset($item['itemOptions']))
+				$item['itemOptions'] = array();
+
+			$item['linkOptions']['data-toggle'] = 'tab';
+
+		    if (isset($tab['items']))
+				$item['items'] = $this->normalizeTabs($item['items'], $panes, $i);
+			else
+			{
+				if (!isset($item['id']))
+					$item['id'] = $id.'_tab_'.($i + 1);
+
+				$item['url'] = '#'.$item['id'];
+
+				if (!isset($item['content']))
+					$item['content'] = '';
+
+				$content = $item['content'];
+				unset($item['content']);
+
+				if (!isset($item['paneOptions']))
+					$item['paneOptions'] = array();
+
+				$paneOptions = $item['paneOptions'];
+				unset($item['paneOptions']);
+
+				$paneOptions['id'] = $item['id'];
+
+				$classes = array('tab-pane fade');
+
+				if (isset($item['active']) && $item['active'])
+					$classes[] = 'active in';
+
+				$classes = implode(' ', $classes);
+				if (isset($paneOptions['class']))
+					$paneOptions['class'] .= ' '.$classes;
+				else
+					$paneOptions['class'] = $classes;
+
+				$panes[] = CHtml::tag('div', $paneOptions, $content);
+
+				$i++; // increment the tab-index
+			}
+
+			$items[] = $item;
+	    }
+
+		return $items;
+	}
+}

widgets/TbThumbnails.php

 <?php
 /**
- * BootThumbs class file.
+ * TbThumbnails class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 
 /**
  * Bootstrap thumbnails widget.
+ * @see http://twitter.github.com/bootstrap/components.html#thumbnails
  */
 class TbThumbnails extends TbListView
 {

widgets/TbTypeahead.php

 <?php
 /**
- * BootTypeahead class file.
+ * TbTypeahead class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  */
 
 /**
- * Bootstrap type-a-head widget.
+ * Bootstrap typeahead widget.
+ * @see http://twitter.github.com/bootstrap/javascript.html#typeahead
  */
 class TbTypeahead extends CInputWidget
 {
 	/**
-	 * @var array the options for the Bootstrap JavaScript plugin.
+	 * @var array the options for the Bootstrap Javascript plugin.
 	 */
 	public $options = array();
 
 	 */
 	public function init()
 	{
-		if (!isset($this->htmlOptions['id']))
-			$this->htmlOptions['id'] = $this->getId();
-
 		$this->htmlOptions['type'] = 'text';
 		$this->htmlOptions['data-provide'] = 'typeahead';
 	}

widgets/input/TbInput.php

 <?php
 /**
- * BootInput class file.
+ * TbInput class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License

widgets/input/TbInputHorizontal.php

 <?php
 /**
- * BootInputHorizontal class file.
+ * TbInputHorizontal class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License

widgets/input/TbInputInline.php

 <?php
 /**
- * BootInputInline class file.
+ * TbInputInline class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License

widgets/input/TbInputSearch.php

 <?php
 /**
- * BootInputSearch class file.
+ * TbInputSearch class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License

widgets/input/TbInputVertical.php

 <?php
 /**
- * BootInputVertical class file.
+ * TbInputVertical class file.
  * @author Christoffer Niska <ChristofferNiska@gmail.com>
  * @copyright Copyright &copy; Christoffer Niska 2011-
  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License