diff --git a/framework/yii/bootstrap/Alert.php b/framework/yii/bootstrap/Alert.php index f84a70b..d57bcbe 100644 --- a/framework/yii/bootstrap/Alert.php +++ b/framework/yii/bootstrap/Alert.php @@ -140,7 +140,7 @@ class Alert extends Widget 'class' => 'fade in', ), $this->options); - $this->addCssClass($this->options, 'alert'); + Html::addCssClass($this->options, 'alert'); if ($this->closeButton !== null) { $this->closeButton = array_merge(array( diff --git a/framework/yii/bootstrap/Button.php b/framework/yii/bootstrap/Button.php index 856c420..c351ea0 100644 --- a/framework/yii/bootstrap/Button.php +++ b/framework/yii/bootstrap/Button.php @@ -48,7 +48,7 @@ class Button extends Widget { parent::init(); $this->clientOptions = false; - $this->addCssClass($this->options, 'btn'); + Html::addCssClass($this->options, 'btn'); } /** diff --git a/framework/yii/bootstrap/ButtonDropdown.php b/framework/yii/bootstrap/ButtonDropdown.php index fec042e..5d94bdc 100644 --- a/framework/yii/bootstrap/ButtonDropdown.php +++ b/framework/yii/bootstrap/ButtonDropdown.php @@ -64,7 +64,7 @@ class ButtonDropdown extends Widget public function init() { parent::init(); - $this->addCssClass($this->options, 'btn-group'); + Html::addCssClass($this->options, 'btn-group'); } /** @@ -85,12 +85,12 @@ class ButtonDropdown extends Widget */ protected function renderButton() { - $this->addCssClass($this->buttonOptions, 'btn'); + Html::addCssClass($this->buttonOptions, 'btn'); if ($this->split) { $tag = 'button'; $options = $this->buttonOptions; $this->buttonOptions['data-toggle'] = 'dropdown'; - $this->addCssClass($this->buttonOptions, 'dropdown-toggle'); + Html::addCssClass($this->buttonOptions, 'dropdown-toggle'); $splitButton = Button::widget(array( 'label' => '', 'encodeLabel' => false, @@ -103,7 +103,7 @@ class ButtonDropdown extends Widget if (!isset($options['href'])) { $options['href'] = '#'; } - $this->addCssClass($options, 'dropdown-toggle'); + Html::addCssClass($options, 'dropdown-toggle'); $options['data-toggle'] = 'dropdown'; $splitButton = ''; } diff --git a/framework/yii/bootstrap/ButtonGroup.php b/framework/yii/bootstrap/ButtonGroup.php index e5bf4e9..3606241 100644 --- a/framework/yii/bootstrap/ButtonGroup.php +++ b/framework/yii/bootstrap/ButtonGroup.php @@ -61,7 +61,7 @@ class ButtonGroup extends Widget { parent::init(); $this->clientOptions = false; - $this->addCssClass($this->options, 'btn-group'); + Html::addCssClass($this->options, 'btn-group'); } /** diff --git a/framework/yii/bootstrap/Carousel.php b/framework/yii/bootstrap/Carousel.php index f8904fa..c2c68a7 100644 --- a/framework/yii/bootstrap/Carousel.php +++ b/framework/yii/bootstrap/Carousel.php @@ -70,7 +70,7 @@ class Carousel extends Widget public function init() { parent::init(); - $this->addCssClass($this->options, 'carousel'); + Html::addCssClass($this->options, 'carousel'); } /** @@ -96,7 +96,7 @@ class Carousel extends Widget for ($i = 0, $count = count($this->items); $i < $count; $i++) { $options = array('data-target' => '#' . $this->options['id'], 'data-slide-to' => $i); if ($i === 0) { - $this->addCssClass($options, 'active'); + Html::addCssClass($options, 'active'); } $indicators[] = Html::tag('li', '', $options); } @@ -140,9 +140,9 @@ class Carousel extends Widget throw new InvalidConfigException('The "content" option is required.'); } - $this->addCssClass($options, 'item'); + Html::addCssClass($options, 'item'); if ($index === 0) { - $this->addCssClass($options, 'active'); + Html::addCssClass($options, 'active'); } return Html::tag('div', $content . "\n" . $caption, $options); diff --git a/framework/yii/bootstrap/Collapse.php b/framework/yii/bootstrap/Collapse.php index fdcaae1..8aed0b1 100644 --- a/framework/yii/bootstrap/Collapse.php +++ b/framework/yii/bootstrap/Collapse.php @@ -66,7 +66,7 @@ class Collapse extends Widget public function init() { parent::init(); - $this->addCssClass($this->options, 'accordion'); + Html::addCssClass($this->options, 'accordion'); } /** @@ -90,7 +90,7 @@ class Collapse extends Widget $index = 0; foreach ($this->items as $header => $item) { $options = ArrayHelper::getValue($item, 'options', array()); - $this->addCssClass($options, 'accordion-group'); + Html::addCssClass($options, 'accordion-group'); $items[] = Html::tag('div', $this->renderItem($header, $item, ++$index), $options); } @@ -111,7 +111,7 @@ class Collapse extends Widget $id = $this->options['id'] . '-collapse' . $index; $options = ArrayHelper::getValue($item, 'contentOptions', array()); $options['id'] = $id; - $this->addCssClass($options, 'accordion-body collapse'); + Html::addCssClass($options, 'accordion-body collapse'); $header = Html::a($header, '#' . $id, array( 'class' => 'accordion-toggle', diff --git a/framework/yii/bootstrap/Dropdown.php b/framework/yii/bootstrap/Dropdown.php index 827e6cc..0568fe4 100644 --- a/framework/yii/bootstrap/Dropdown.php +++ b/framework/yii/bootstrap/Dropdown.php @@ -46,7 +46,7 @@ class Dropdown extends Widget public function init() { parent::init(); - $this->addCssClass($this->options, 'dropdown-menu'); + Html::addCssClass($this->options, 'dropdown-menu'); } /** @@ -81,7 +81,7 @@ class Dropdown extends Widget $linkOptions['tabindex'] = '-1'; if (isset($item['items'])) { - $this->addCssClass($options, 'dropdown-submenu'); + Html::addCssClass($options, 'dropdown-submenu'); $content = Html::a($label, '#', $linkOptions) . $this->renderItems($item['items']); } else { $content = Html::a($label, ArrayHelper::getValue($item, 'url', '#'), $linkOptions); diff --git a/framework/yii/bootstrap/Modal.php b/framework/yii/bootstrap/Modal.php index 0608fbe..f676273 100644 --- a/framework/yii/bootstrap/Modal.php +++ b/framework/yii/bootstrap/Modal.php @@ -197,7 +197,7 @@ class Modal extends Widget $this->options = array_merge(array( 'class' => 'modal hide', ), $this->options); - $this->addCssClass($this->options, 'modal'); + Html::addCssClass($this->options, 'modal'); $this->clientOptions = array_merge(array( 'show' => false, diff --git a/framework/yii/bootstrap/Nav.php b/framework/yii/bootstrap/Nav.php index 8069699..7b003f4 100644 --- a/framework/yii/bootstrap/Nav.php +++ b/framework/yii/bootstrap/Nav.php @@ -79,7 +79,7 @@ class Nav extends Widget public function init() { parent::init(); - $this->addCssClass($this->options, 'nav'); + Html::addCssClass($this->options, 'nav'); } /** @@ -125,13 +125,13 @@ class Nav extends Widget $linkOptions = ArrayHelper::getValue($item, 'linkOptions', array()); if (ArrayHelper::getValue($item, 'active')) { - $this->addCssClass($options, 'active'); + Html::addCssClass($options, 'active'); } if ($items !== null) { $linkOptions['data-toggle'] = 'dropdown'; - $this->addCssClass($options, 'dropdown'); - $this->addCssClass($urlOptions, 'dropdown-toggle'); + Html::addCssClass($options, 'dropdown'); + Html::addCssClass($urlOptions, 'dropdown-toggle'); $label .= ' ' . Html::tag('b', '', array('class' => 'caret')); if (is_array($items)) { $items = Dropdown::widget(array( diff --git a/framework/yii/bootstrap/NavBar.php b/framework/yii/bootstrap/NavBar.php index 17a938c..337e449 100644 --- a/framework/yii/bootstrap/NavBar.php +++ b/framework/yii/bootstrap/NavBar.php @@ -107,8 +107,8 @@ class NavBar extends Widget { parent::init(); $this->clientOptions = false; - $this->addCssClass($this->options, 'navbar'); - $this->addCssClass($this->brandOptions, 'brand'); + Html::addCssClass($this->options, 'navbar'); + Html::addCssClass($this->brandOptions, 'brand'); } /** diff --git a/framework/yii/bootstrap/Progress.php b/framework/yii/bootstrap/Progress.php index 7c0473e..ae44619 100644 --- a/framework/yii/bootstrap/Progress.php +++ b/framework/yii/bootstrap/Progress.php @@ -94,7 +94,7 @@ class Progress extends Widget public function init() { parent::init(); - $this->addCssClass($this->options, 'progress'); + Html::addCssClass($this->options, 'progress'); } /** @@ -139,7 +139,7 @@ class Progress extends Widget */ protected function renderBar($percent, $label = '', $options = array()) { - $this->addCssClass($options, 'bar'); + Html::addCssClass($options, 'bar'); $options['style'] = "width:{$percent}%"; return Html::tag('div', $label, $options); } diff --git a/framework/yii/bootstrap/Tabs.php b/framework/yii/bootstrap/Tabs.php index 4a85b9a..90c9794 100644 --- a/framework/yii/bootstrap/Tabs.php +++ b/framework/yii/bootstrap/Tabs.php @@ -93,7 +93,7 @@ class Tabs extends Widget public function init() { parent::init(); - $this->addCssClass($this->options, 'nav nav-tabs'); + Html::addCssClass($this->options, 'nav nav-tabs'); } /** @@ -123,10 +123,10 @@ class Tabs extends Widget if (isset($item['items'])) { $label .= ' '; - $this->addCssClass($headerOptions, 'dropdown'); + Html::addCssClass($headerOptions, 'dropdown'); if ($this->renderDropdown($item['items'], $panes)) { - $this->addCssClass($headerOptions, 'active'); + Html::addCssClass($headerOptions, 'active'); } $header = Html::a($label, "#", array('class' => 'dropdown-toggle', 'data-toggle' => 'dropdown')) . "\n" @@ -135,10 +135,10 @@ class Tabs extends Widget $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', array())); $options['id'] = ArrayHelper::getValue($options, 'id', $this->options['id'] . '-tab' . $n); - $this->addCssClass($options, 'tab-pane'); + Html::addCssClass($options, 'tab-pane'); if (ArrayHelper::remove($item, 'active')) { - $this->addCssClass($options, 'active'); - $this->addCssClass($headerOptions, 'active'); + Html::addCssClass($options, 'active'); + Html::addCssClass($headerOptions, 'active'); } $header = Html::a($label, '#' . $options['id'], array('data-toggle' => 'tab', 'tabindex' => '-1')); $panes[] = Html::tag('div', $item['content'], $options); @@ -175,10 +175,10 @@ class Tabs extends Widget $content = ArrayHelper::remove($item, 'content'); $options = ArrayHelper::remove($item, 'contentOptions', array()); - $this->addCssClass($options, 'tab-pane'); + Html::addCssClass($options, 'tab-pane'); if (ArrayHelper::remove($item, 'active')) { - $this->addCssClass($options, 'active'); - $this->addCssClass($item['options'], 'active'); + Html::addCssClass($options, 'active'); + Html::addCssClass($item['options'], 'active'); $itemActive = true; } diff --git a/framework/yii/bootstrap/Widget.php b/framework/yii/bootstrap/Widget.php index 48b0331..004f040 100644 --- a/framework/yii/bootstrap/Widget.php +++ b/framework/yii/bootstrap/Widget.php @@ -82,20 +82,4 @@ class Widget extends \yii\base\Widget $view->registerJs(implode("\n", $js)); } } - - /** - * Adds a CSS class to the specified options. - * This method will ensure that the CSS class is unique and the "class" option is properly formatted. - * @param array $options the options to be modified. - * @param string $class the CSS class to be added - */ - protected function addCssClass(&$options, $class) - { - if (isset($options['class'])) { - $classes = preg_split('/\s+/', $options['class'] . ' ' . $class, -1, PREG_SPLIT_NO_EMPTY); - $options['class'] = implode(' ', array_unique($classes)); - } else { - $options['class'] = $class; - } - } } diff --git a/framework/yii/helpers/base/Html.php b/framework/yii/helpers/base/Html.php index 47385e2..ba9a889 100644 --- a/framework/yii/helpers/base/Html.php +++ b/framework/yii/helpers/base/Html.php @@ -1373,6 +1373,44 @@ class Html } /** + * Adds a CSS class to the specified options. + * If the CSS class is already in the options, it will not be added again. + * @param array $options the options to be modified. + * @param string $class the CSS class to be added + */ + public static function addCssClass(&$options, $class) + { + if (isset($options['class'])) { + $classes = ' ' . $options['class'] . ' '; + if (($pos = strpos($classes, ' ' . $class . ' ')) === false) { + $options['class'] .= ' ' . $class; + } + } else { + $options['class'] = $class; + } + } + + /** + * Removes a CSS class from the specified options. + * @param array $options the options to be modified. + * @param string $class the CSS class to be removed + */ + public static function removeCssClass(&$options, $class) + { + if (isset($options['class'])) { + $classes = array_unique(preg_split('/\s+/', $options['class'] . ' ' . $class, -1, PREG_SPLIT_NO_EMPTY)); + if (($index = array_search($class, $classes)) !== false) { + unset($classes[$index]); + } + if (empty($classes)) { + unset($options['class']); + } else { + $options['class'] = implode(' ', $classes); + } + } + } + + /** * Returns the real attribute name from the given attribute expression. * * An attribute expression is an attribute name prefixed and/or suffixed with array indexes. diff --git a/tests/unit/framework/helpers/HtmlTest.php b/tests/unit/framework/helpers/HtmlTest.php index 2311321..0399a4e 100644 --- a/tests/unit/framework/helpers/HtmlTest.php +++ b/tests/unit/framework/helpers/HtmlTest.php @@ -434,6 +434,38 @@ EOD; Html::$showBooleanAttributeValues = true; } + public function testAddCssClass() + { + $options = array(); + Html::addCssClass($options, 'test'); + $this->assertEquals(array('class' => 'test'), $options); + Html::addCssClass($options, 'test'); + $this->assertEquals(array('class' => 'test'), $options); + Html::addCssClass($options, 'test2'); + $this->assertEquals(array('class' => 'test test2'), $options); + Html::addCssClass($options, 'test'); + $this->assertEquals(array('class' => 'test test2'), $options); + Html::addCssClass($options, 'test2'); + $this->assertEquals(array('class' => 'test test2'), $options); + Html::addCssClass($options, 'test3'); + $this->assertEquals(array('class' => 'test test2 test3'), $options); + Html::addCssClass($options, 'test2'); + $this->assertEquals(array('class' => 'test test2 test3'), $options); + } + + public function testRemoveCssClass() + { + $options = array('class' => 'test test2 test3'); + Html::removeCssClass($options, 'test2'); + $this->assertEquals(array('class' => 'test test3'), $options); + Html::removeCssClass($options, 'test2'); + $this->assertEquals(array('class' => 'test test3'), $options); + Html::removeCssClass($options, 'test'); + $this->assertEquals(array('class' => 'test3'), $options); + Html::removeCssClass($options, 'test3'); + $this->assertEquals(array(), $options); + } + protected function getDataItems() { return array(