Issues (164)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Bootstrap/Bootstrap3.php (17 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace BootPress\Bootstrap;
4
5
use BootPress\Page\Component as Page;
6
use BootPress\Form\Component as Form;
7
8
class Bootstrap3
9
{
10
    use Base;
11
12
    protected $framework;
13
    protected $version;
14
    protected $page;
15
    private $table;
0 ignored issues
show
The property $table is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
16
    private $navbar;
0 ignored issues
show
The property $navbar is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
17
    private $pagination;
0 ignored issues
show
The property $pagination is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
18
19 30
    public function __construct($version = null)
20
    {
21 30
        $this->framework = 'bootstrap';
22 30
        $this->version = $version;
23 30
        $this->page = Page::html();
24 30
    }
25
26 20
    public function __get($name)
27
    {
28
        switch ($name) {
29 20
            case 'framework':
30 20
            case 'version':
31 2
                return $this->$name;
32
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
33 19
            case 'table':
34 19
            case 'navbar':
35 19
            case 'pagination':
36 18
                if (is_null($this->$name)) {
37 4
                    $class = 'BootPress\Bootstrap\Bootstrap3'.ucfirst($name);
38 4
                    $this->$name = new $class();
39 4
                }
40
41 18
                return $this->$name;
42
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
43
        }
44
45 1
        return;
46
    }
47
48
    /**
49
     * This method works in conjunction with ``$bp->col()`` below.  It makes things a little less verbose, but much easier to edit, modify, and see at a glance what in the world is going on.
50
     * 
51
     * @param string $size    This value can be either '**xs**' < 768px, '**sm**' >= 768px, '**md**' >= 992 , or '**lg**' >= 1200. This is the point at which your grid will break, if no smaller size is indicated. With this method you can indicate multiple sizes by simply inserting another argument. All of your ``$size``'s must correspond with the values given in the ``$bp->col()``'s or ``$columns`` below.
0 ignored issues
show
There is no parameter named $size. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
52
     * @param array $columns  An array of ``$bp->col()``'s.  This argument does not need to be the second one in line.  It is merely the last one given.
0 ignored issues
show
There is no parameter named $columns. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
53
     * 
54
     * @return string
55
     * 
56
     * ```php
57
     * echo $bp->row('sm', array(
58
     *     $bp->col(3, 'left'),
59
     *     $bp->col(6, 'center'),
60
     *     $bp->col(3, 'right'),
61
     * ));
62
     * ```
63
     */
64 2
    public function row()
65
    {
66 2
        $html = '';
67 2
        $prefix = array();
68 2
        for ($i = 1; $i <= 12; ++$i) {
69 2
            $prefix[] = 'offset-'.$i;
70 2
            $prefix[] = 'push-'.$i;
71 2
            $prefix[] = 'pull-'.$i;
72 2
            $prefix[] = $i;
73 2
        }
74 2
        $sizes = func_get_args();
75 2
        $columns = array_pop($sizes);
76 2
        foreach ($columns as $cols) {
77 2
            if (is_string($cols)) {
78 1
                $html .= $cols;
79 1
            } else {
80 2
                $content = array_pop($cols);
81 2
                foreach ($cols as $key => $classes) {
82 2
                    $cols[$key] = $this->prefixClasses("col-{$sizes[$key]}", $prefix, $classes, 'exclude_base');
0 ignored issues
show
'exclude_base' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
83 2
                }
84 2
                $html .= '<div class="'.implode(' ', $cols).'">'.$content.'</div>';
85
            }
86 2
        }
87
88 2
        return '<div class="row">'.$html.'</div>';
89
    }
90
91
    /**
92
     * This is a helper method for ``$bp->row()`` above.  It only returns it's own arguments, but it helps to keep things straight.  Including arrays within arrays can get to be a little unwieldly, especially in a Smarty template. Just take a look at the ``$bp->media()`` method.
93
     * 
94
     * @param mixed $number   This parameter must correspond with it's parent ``$bp->row($size)``. It can be an integer between 1 and 12, as long as all of the ``$bp->col()``'s respective numbers add up to 12 or less. To get fancy you can add a space, then an '**offset-**', '**push-**', or '**pull-**' followed by the number of columns that you would like to affect. All of these will be preceded by ``col-{$size}-...``. To include additional classes just keep on going with a space in between each like you normally would.
0 ignored issues
show
There is no parameter named $number. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
95
     * @param string $column  The actual html content you would like to be placed in this column.
0 ignored issues
show
There is no parameter named $column. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
96
     * 
97
     * @return array  An array of it's own parameters.
98
     * 
99
     * ```php
100
     * echo $bp->row('sm', 'md', 'lg', array(
101
     *     $bp->col(12, '9 push-3', '10 push-2', 'content'),
102
     *     $bp->col('6 offset-3 clearfix', '3 pull-9', '2 pull-10', 'sidebar'),
103
     * ));
104
     * ```
105
     */
106 2
    public function col()
107
    {
108 2
        return func_get_args();
109
    }
110
111
    /**
112
     * This assists you in making Ordered, Unordered, and Definition lists. It is especially useful when you are nesting lists within lists. Your code almost looks exactly like you would expect to see it on the big screen. It would have been nice if we could have named this method 'list', but someone has taken that already.
113
     * 
114
     * @param string $tag   Either an '**ol**' (Ordered list), '**ul**' (Unordered list), or a '**dl**' (Definition list). You can add any other classes you like (or not), but the special ones that Bootstrap has blessed us with are:
115
     * 
116
     * - '**list-inline**' - For an unordered list to be displayed horizontally.
117
     * - '**list-unstyled**' - For an unordered list to be unbulleted.
118
     * - '**dl-horizontal**' - For a definition to be displayed beside it's title rather than below.
119
     
120
     
121
     * @param array  $list  For Ordered and Unordered lists this is an ``array($li, $li, ...)``, and to nest another list just make the ``$li`` another array.
122
     * 
123
     * For Definition Lists this is an ``array($title => $definition, ...)``. If you have multiple ``$definition``'s, then just make ``$title`` an array of them.
124
     * 
125
     * @return string
126
     * 
127
     * ```php
128
     * $li = array(
129
     *     'Lorem ipsum dolor sit amet',
130
     *     'Consectetur adipiscing elit' => array(
131
     *         'Phasellus iaculis neque',
132
     *         'Purus sodales ultricies',
133
     *         'Vestibulum laoreet porttitor sem'
134
     *     ),
135
     *     'Faucibus porta lacus fringilla vel',
136
     *     'Aenean sit amet erat nunc'
137
     * );
138
     * 
139
     * echo $bp->lister('ol', $li);
140
     * 
141
     * echo $bp->lister('ul list-unstyled', $li);
142
     * 
143
     * echo $bp->lister('ul list-inline', array('Lorem ipsum', 'Phasellus iaculis', 'Nulla volutpat'));
144
     * 
145
     * echo $bp->lister('dl dl-horizontal', array(
146
     *     'Malesuada porta' => array(
147
     *         'Vestibulum id ligula porta felis euismod semper eget lacinia odio sem nec elit.',
148
     *         'Donec id elit non mi porta gravida at eget metus.'
149
     *     ),
150
     *     'Felis euismod semper eget lacinia' => 'Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.'
151
     * ));
152
     * ```
153
     */
154 2
    public function lister($tag, array $list)
155
    {
156 2
        $html = '';
157 2
        $class = '';
158 2
        if ($space = strpos($tag, ' ')) {
159 1
            $class = trim(substr($tag, $space));
160 1
            $tag = substr($tag, 0, $space);
161 1
        }
162 2
        foreach ($list as $key => $value) {
163 2
            if ($tag == 'dl') {
164 1
                $html .= '<dt>'.$key.'</dt>';
165 1
                $html .= '<dd>'.(is_array($value) ? implode('</dd><dd>', $value) : $value).'</dd>';
166 1
            } else {
167 2
                $html .= '<li>'.(is_array($value) ? $key.$this->lister($tag, $value) : $value).'</li>';
168
            }
169 2
        }
170
171 2
        return $this->page->tag($tag, array('class' => $class), $html);
172
    }
173
174
    /**
175
     * This will assist you in creating a search bar for your site.
176
     * 
177
     * @param string $url   This is the url that you would like the search term to be sent to.
178
     * @param array  $form  To customize the form, you can submit an array with any of the following keys:
179
     * 
180
     * - '**name**' - The name of the input field. The default is '**search**'.
181
     * - '**placeholder**' - Subtle text to indicate what sort of field it is. The default is '**Search**'.
182
     * - '**button**' - The button itself with tags and all, or just a name. The default is ``$bp->icon('search')``.
183
     *   - If you don't want a button at all then just give this an empty value.
184
     * - '**class**' - Any special class(es) to give the ``<form>`` tag. The default is '**form-horizontal**'.
185
     * - '**size**' - Either '**sm**', '**md**' (the default), or '**lg**'.
186
     * 
187
     * @return string
188
     * 
189
     * ```php
190
     * echo $bp->search('http://example.com/search');
191
     * ```
192
     */
193 2
    public function search($url, array $form = array())
194
    {
195 2
        $html = '';
196 2
        $form = array_merge(array(
197 2
            'name' => 'search',
198 2
            'role' => 'search',
199 2
            'class' => 'form-horizontal',
200 2
            'placeholder' => 'Search',
201 2
            'button' => $this->icon('search'),
202 2
            'size' => '',
203 2
        ), $form);
204 2
        $form['method'] = 'get';
205 2
        $form['action'] = $url;
206 2
        $input = array('class' => 'form-control', 'placeholder' => $form['placeholder']);
207 2
        $button = $form['button'];
208 2
        $size = $form['size'];
209 2
        unset($form['placeholder'], $form['button'], $form['size']);
210 2
        $form = new Form($form);
0 ignored issues
show
$form is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
211 2
        $form->validator->set($form->header['name'], 'required');
212 2
        if (!empty($button)) {
213 2
            if (strpos($button, '<button') === false) {
214 2
                $button = '<button type="submit" class="btn btn-default" title="Search">'.$button.'</button>';
215 2
            }
216 2
            $html .= '<div class="'.$this->prefixClasses('input-group', array('sm', 'md', 'lg'), $size).'">';
217 2
            $html .= $form->text($form->header['name'], $input);
218 2
            $html .= '<div class="input-group-btn">'.$button.'</div>';
219 2
            $html .= '</div>';
220 2
        } else {
221 1
            if (!empty($size) && in_array($size, array('sm', 'md', 'lg'))) {
222 1
                $input['class'] .= " input-{$size}";
223 1
            }
224 1
            $html .= $form->text($form->header['name'], $input);
225
        }
226
227 2
        return $form->header().$html.$form->close();
228
    }
229
    
230
    /**
231
     * Returns a BootPress\Form\Component instance with a few methods added for some Bootstrapped good looks.
232
     * 
233
     * @param string $name    The name of the form.
234
     * @param string $method  The form's method for sending.
235
     * 
236
     * @return object
237
     * 
238
     * ```php
239
     * $form = $bp->form('example');
240
     * ```
241
     */
242 1
    public function form($name, $method = 'post')
243
    {
244 1
        return new Bootstrap3Form($name, $method, $this);
245
    }
246
247
    /**
248
     * Though not particularly a Bootstrap feature, this method is a natural for Smarty templates. It returns an ``<img>`` tag along with it's ``$params`` if the ``$src`` is not empty. If the ``$src`` is empty, then it skips the ``$params`` and heads for the ``$alt`` door.
249
     * 
250
     * @param string $src     The image src location.
251
     * @param string $params  Additional parameters you would like included in the ``<img>`` tag.
252
     * @param string $alt     What to display if the ``$src`` is empty.
253
     * @param string $frag    A #fragment to append to the end of ``$src`` (if any)
254
     * 
255
     * @return string
256
     * 
257
     * ```php
258
     * echo $bp->img($image, 'width="75" class="pull-right" style="margin-left:10px;"', 'Sorry, I guess there is no authors image here.', '50x50/seo-name');
259
     * ```
260
     */
261 1
    public function img($src, $params = '', $alt = '', $frag = '')
262
    {
263 1
        if (empty($src)) {
264 1
            return $alt;
265
        }
266 1
        if (!empty($frag)) {
267 1
            $src .= '#'.trim($frag, '#');
268 1
        }
269
270 1
        return trim('<img src="'.$src.'" '.$params).'>';
271
    }
272
273
    /**
274
     * This method helps you cut down on verbosity, but do you remember migrating from icon in v.2 to glyphicon in v.3? If that happens again, then we'll just change the ``$prefix`` default and that will be that.
275
     * 
276
     * @param string $symbol  The icon you would like to display without the base and icon class prefix.
277
     * @param string $prefix  The base and icon class prefix. The default is a Bootstrap icon, but this can be used with any icon font by simply entering their value here.
278
     * @param string $tag     The tag to use for displaying your font. Everyone uses the <i> tag, so that is the default. If $prefix == 'glyphicon' (the default for Bootstrap) then we will use a span element. Why? I don't know, but since v.2 that seems to be what they prefer to use now. If you want to style an icon further then you can do so here. eg. 'i style="font-size:16px;"'
279
     * 
280
     * @return string
281
     * 
282
     * ```php
283
     * echo $bp->icon('asterisk'); // <span class="glyphicon glyphicon-asterisk"></span>
284
     * ```
285
     */
286 5
    public function icon($symbol, $prefix = 'glyphicon', $tag = 'i')
287
    {
288 5
        $base = $prefix;
289 5
        $classes = explode(' ', $symbol);
290 5
        $prefix = array($classes[0]); // ie. only prefix the first class
291 5
        $params = '';
292 5
        if ($space = strpos($tag, ' ')) {
293 2
            $params = ' '.trim(substr($tag, $space));
294 2
            $tag = substr($tag, 0, $space);
295 2
        }
296 5
        if ($base == 'glyphicon') {
297 5
            $tag = 'span';
298 5
        }
299
300 5
        return $this->addClass("<{$tag}{$params}></{$tag}>", array(
301 5
            $tag => $this->prefixClasses($base, $prefix, $classes),
302 5
        ));
303
    }
304
305
    /**
306
     * A button by itself is easy enough, but when you start including dropdowns and groups your markup can get ugly quick. Follow the examples. We'll start simple and go from there.
307
     * 
308
     * @param string $class    The classes: '**xs**', '**sm**', '**lg**', '**block**', '**default**', '**primary**', '**success**', '**info**', '**warning**', '**danger**', and '**link**' will all be prefixed with 'btn', and we include the 'btn' class too. Notice how we left out the 'btn-group' option? Don't worry about that one. Feel free to add any more that you like such as '**disabled**'.
309
     * @param string $name     The text of your button. You may also include badges, labels, icons, etc, but leave the caret up to us. If you are including a dropdown menu and you would like to split the button from the menu, then you can make this an ``array('split' => $name)``.
310
     * @param array  $options  These are all of the attributes that you would like to include in the ``<button>`` tag, except if you include an 'href' key then it will be an ``<a>`` tag. Other potential options include: 'id', 'style', 'title', 'type', 'data-...', etc, but the ones we take notice of and do special things with are:
311
     * 
312
     * - '**dropdown**' - This is an ``array($name => $link, ...)`` of names and their associated links.
313
     *   - If the $name is numeric (ie. not specified) then the $link will be a header (if it is not empty), or a divider if it is.
314
     * - '**dropup**' - The same as dropdown, only the caret and menu goes up instead of down.
315
     * - '**active**' - This is to specify a $link that will receive the "active" class. You can set this value to either the $name or the $link of your dropdown menu, or a number (starting from 1). If you just want it to select the current page then you can specify '**url**' which will match the current url and uri, or '**urlquery**' which will match the current url, uri, and query string.
316
     * - '**disabled**' - This is to specify a link that will receive the "disabled" class. You can set this value to either the $name or the $link of your dropdown menu.
317
     * - '**pull**' - Either 'left' (default) or 'right'. Where you would like the dropdown to be relative to the parent.
318
     * 
319
     * @return string
320
     * 
321
     * ```php
322
     * echo $bp->button('primary', 'Primary'); // <button class="btn btn-primary" type="button">Primary</button>
323
     * 
324
     * echo $bp->button('xs success', 'Link', array('href'=>'#')); // <a class="btn btn-xs btn-success" href="#">Link</a>
325
     * 
326
     * echo $bp->button('danger', 'Dropdown', array(
327
     *     'dropdown' => array(
328
     *         'Dropdown Header',
329
     *         'Action' => '#',
330
     *         'Another action' => '#',
331
     *         'Active Link' => '#', 
332
     *         '',
333
     *         'Separated Link' => '#',
334
     *         'Disabled Link' => '#'
335
     *     ),
336
     *     'active' => 'Active Link',
337
     *     'disabled' => 'Disabled Link'
338
     * ));
339
     * ```
340
     */
341 5
    public function button($class, $name, array $options = array())
342
    {
343 5
        $attributes = array('type' => 'button');
344 5
        foreach ($options as $key => $value) {
345 4
            if (!in_array($key, array('dropdown', 'dropup', 'active', 'disabled', 'pull'))) {
346 3
                $attributes[$key] = $value;
347 3
            }
348 5
        }
349 5
        $attributes['class'] = $this->prefixClasses('btn', array('block', 'xs', 'sm', 'lg', 'default', 'primary', 'success', 'info', 'warning', 'danger', 'link'), $class);
350 5
        if (isset($options['dropdown']) || isset($options['dropup'])) {
351 2
            $html = '';
352 2
            unset($attributes['href']);
353 2
            $class = (isset($options['dropup'])) ? 'btn-group dropup' : 'btn-group';
354 2
            $links = (isset($options['dropup'])) ? $options['dropup'] : $options['dropdown'];
355 2
            $html .= '<div class="'.$class.'">';
356 2
            list($dropdown, $id) = $this->dropdown($links, $options);
357 2
            if (is_array($name) && isset($name['split'])) {
358 2
                $html .= $this->page->tag('button', $attributes, $name['split']);
359 2
                $attributes['id'] = $id;
360 2
                $attributes['class'] .= ' dropdown-toggle';
361 2
                $attributes['data-toggle'] = 'dropdown';
362 2
                $attributes['aria-haspopup'] = 'true';
363 2
                $attributes['aria-expanded'] = 'false';
364 2
                $html .= $this->page->tag('button', $attributes, '<span class="caret"></span>', '<span class="sr-only">Toggle Dropdown</span>');
365 2
            } else {
366 1
                $attributes['id'] = $id;
367 1
                $attributes['class'] .= ' dropdown-toggle';
368 1
                $attributes['data-toggle'] = 'dropdown';
369 1
                $attributes['aria-haspopup'] = 'true';
370 1
                $attributes['aria-expanded'] = 'false';
371 1
                $html .= $this->page->tag('button', $attributes, $name, '<span class="caret"></span>');
372
            }
373 2
            $html .= $dropdown;
374 2
            $html .= '</div>';
375
376 2
            return $html;
377 5
        } elseif (isset($options['href'])) {
378 3
            unset($attributes['type']);
379
380 3
            return $this->page->tag('a', $attributes, $name);
381
        } else {
382 3
            return $this->page->tag('button', $attributes, $name);
383
        }
384
    }
385
386
    /**
387
     * This method will group together your buttons.
388
     * 
389
     * @param string $class    The classes: '**xs**', '**sm**', '**lg**', '**justified**', and '**vertical**' will all be prefixed with 'btn-group', and we include the 'btn-group' class too. When you size a group up, then don't size the individual buttons.
390
     * @param array  $buttons  An ``array($bp->button(), ...)`` of buttons.
391
     * @param string $form     This can be either '**checkbox**' or '**radio**' and your button group will act accordingly.
392
     * 
393
     * @return string
394
     * 
395
     * ```php
396
     * $bp->group('', array(
397
     *     $bp->button('primary', 'Btn'),
398
     *     $bp->button('primary', 'Group'),
399
     *     $bp->button('primary', array('split'=>'Split'), array(
400
     *         'dropdown' => array(
401
     *             'Works' => '#',
402
     *             'Here' => '#',
403
     *             'Too' => '#',
404
     *         ),
405
     *         'pull'=>'right'
406
     *     )),
407
     *     $bp->button('primary', 'Middle'),
408
     * ));
409
     * ```
410
     */
411 1
    public function group($class, array $buttons, $form = '')
412
    {
413 1
        $attributes = array('class' => $this->prefixClasses('btn-group', array('xs', 'sm', 'lg', 'justified', 'vertical'), $class));
414 1
        if ($form == 'checkbox' || $form == 'radio') {
415 1
            $attributes['data-toggle'] = 'buttons-'.$form;
416 1
        }
417 1
        if (strpos($class, 'justified') !== false) {
418 1
            $buttons = '<div class="btn-group" role="group">'.implode('</div><div class="btn-group" role="group">', $buttons).'</div>';
419 1
        } else {
420 1
            $buttons = implode('', $buttons);
421
        }
422 1
        $attributes['role'] = 'group';
423
424 1
        return $this->page->tag('div', $attributes, $buttons);
425
    }
426
427
    /**
428
     * This used to be a private method that we only used internally for tabs and pills and buttons and so forth, but it is just so useful. Now you can make your own dropdowns with regular ``<a>`` links as will.
429
     * 
430
     * @param string $tag      If this isn't 'li', then it will be an '**a**'. If you specify 'li' tags then you will need to surround this method's output with your own ``<ul>`` or ``<ol>`` tags. Otherwise you can just use the returned ``<a>`` $links (with dropdowns if any) as is. The ``<a>``'s with dropdowns will be surrounded by a ``<span class="dropdown">``. If one of those dropdown links are active then the ``<span>`` and ``<a>`` tags will receive an additional 'active' class as well. To add any other class(es) to the ``<a>`` or ``<li>`` tags just add them after the $tag here eg. '**a special-class**'.
431
     * @param array  $links    An ``array($name => $href, ...)`` of links. If $href is an array unto itself, then it will be turned into a dropdown menu with the same header and divider rules applied as with ``$bp->buttons()``.
432
     * @param array  $options  The available options are:
433
     * 
434
     * - '**active**' => $name, $href, 'url', 'urlquery', or number (starting from 1)
435
     * - '**disabled**' => $name or $href or number (starting from 1)
436
     * - '**align**' => '**left**' (default) or '**right**' - the direction you would like to pull them towards
437
     * 
438
     * @return string
439
     * 
440
     * ```php
441
     * echo $bp->links('a special-class', array(
442
     *     'Home' => BASE_URL,
443
     *     'Dropdown' => array(
444
     *         'Header',
445
     *         'Action' => '#',
446
     *         'Another Action' => '#',
447
     *     ),
448
     * ), array('active'=>'url'));
449
     * ```
450
     */
451 5
    public function links($tag, array $links, array $options = array())
452
    {
453 5
        $html = '';
454 5
        $class = null;
455 5
        if ($space = strpos($tag, ' ')) {
456 1
            $class = ' '.trim(substr($tag, $space));
457 1
            $tag = substr($tag, 0, $space);
458 1
        }
459 5
        if ($tag != 'li') {
460 1
            $tag = 'a';
461 1
        }
462 5
        $count = 1;
463 5
        if (isset($options['active'])) {
464 5
            if ($options['active'] == 'url') {
465 1
                $options['active'] = $this->page->url('delete', '', '?');
466 5
            } elseif ($options['active'] == 'urlquery') {
467 1
                $options['active'] = $this->page->url();
468 1
            }
469 5
        }
470 5
        foreach ($links as $name => $href) {
471 5
            if (is_array($href)) {
472 3
                list($dropdown, $id) = $this->dropdown($href, $options, $count);
473 3
                $active = (strpos($dropdown, 'class="active"') !== false) ? ' active' : null;
474 3
                $link = $this->page->tag('a', array(
475 3
                    'id' => $id,
476 3
                    'data-target' => '#',
477 3
                    'href' => '#',
478 3
                    'role' => 'button',
479 3
                    'data-toggle' => 'dropdown',
480 3
                    'aria-haspopup' => 'true',
481 3
                    'aria-expanded' => $active ? 'true' : 'false',
482 3
                ), $name, '<span class="caret"></span>');
483 3
                if ($tag == 'li') {
484 2
                    $html .= $this->page->tag('li', array('class' => 'dropdown'.$active.$class), $link, $dropdown);
485 2
                } else {
486 1
                    if ($active || $class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $active of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
487 1
                        $link = $this->addClass($link, array('a' => $active.$class));
488 1
                    }
489 1
                    $html .= $this->page->tag('span', array('class' => 'dropdown'.$active), $link, $dropdown);
490
                }
491 3
            } else {
492 5
                if (is_numeric($name)) {
493 1
                    $link = $href;
494 1
                } else {
495 5
                    $attributes = array('href' => $href);
496 5
                    if (isset($options['toggle'])) {
497 1
                        if ($href[0] == '#') {
498 1
                            $attributes['aria-controls'] = substr($href, 1);
499 1
                        }
500 1
                        $attributes['role'] = $options['toggle'];
501 1
                        $attributes['data-toggle'] = $options['toggle'];
502 1
                    }
503 5
                    $link = $this->page->tag('a', $attributes, $name);
504
                }
505 5
                $li = $this->listItem($link, $options, $name, $href, $count);
506 5
                if ($tag == 'li') {
507 5
                    if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
508 1
                        $li = $this->addClass($li, array('li' => $class));
509 1
                    }
510 5
                    $html .= $li;
511 5
                } else {
512 1
                    if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
513 1
                        $link = $this->addClass($link, array('a' => $class));
514 1
                    }
515 1
                    if (strpos($li, 'class="active"') !== false) {
516 1
                        $link = $this->addClass($link, array('a' => 'active'));
517 1
                    } elseif (strpos($li, 'class="disabled"') !== false) {
518 1
                        $link = $this->addClass($link, array('a' => 'disabled'));
519 1
                    }
520 1
                    $html .= $link;
521
                }
522 5
                ++$count;
523
            }
524 5
        }
525
526 5
        return $html;
527
    }
528
529
    /**
530
     * Creates a Bootstrap tabs nav menu.
531
     * 
532
     * @param array $links    An ``array($name => $href, ...)`` of links. If $href is an array unto itself, then it will be turned into a dropdown menu with the same header and divider rules applied as with ``$bp->buttons()``.
533
     * @param array $options  The available options are:
534
     * 
535
     * - '**active**' => $name, $href, 'url', 'urlquery', or number (starting from 1)
536
     * - '**disabled**' => $name or $href or number (starting from 1)
537
     * - '**align**' =>
538
     *   - '**justified**' - The tabs will horizontally extend the full width
539
     *   - '**left**' (default) or '**right**' - The direction you would like to pull them towards
540
     * 
541
     * @return string
542
     * 
543
     * ```php
544
     * echo $bp->tabs(array(
545
     *     'Nav' => '#',
546
     *     'Tabs' => '#',
547
     *     'Justified' => '#',
548
     * ), array('active'=>1, 'align'=>'justified'));
549
     * ```
550
     */
551 1
    public function tabs(array $links, array $options = array())
552
    {
553 1
        $class = 'nav nav-tabs';
554 1
        if (isset($options['align'])) {
555 1
            switch ($options['align']) {
556 1
                case 'justified':
557 1
                    $class .= ' nav-justified';
558 1
                    break;
559 1
                case 'left':
560 1
                case 'right':
561 1
                    $class .= ' pull-'.$options['align'];
562 1
                    break;
563 1
            }
564 1
        }
565
566 1
        return $this->page->tag('ul', array('class' => $class), $this->links('li', $links, $options));
567
    }
568
569
    /**
570
     * Creates a Bootstrap pills nav menu.
571
     * 
572
     * @param array $links    An ``array($name => $href, ...)`` of links. If $href is an array unto itself, then it will be turned into a dropdown menu with the same header and divider rules applied as with ``$bp->buttons()``.
573
     * @param array $options  The available options are:
574
     * 
575
     * - '**active**' => $name, $href, 'url', 'urlquery', or number (starting from 1)
576
     * - '**disabled**' => $name or $href or number (starting from 1)
577
     * - '**align**' =>
578
     *   - '**justified**' - The pills will horizontally extend the full width
579
     *   - '**vertical**' or '**stacked**' - Each pill will be stacked on top of the other
580
     *   - '**left**' (default) or '**right**' - The direction you would like to pull them towards
581
     * 
582
     * @return string
583
     * 
584
     * ```php
585
     * echo $bp->pills(array(
586
     *     'Home ' . $bp->badge(42) => '#',
587
     *     'Profile' . $bp->badge(0) => '#',
588
     *     'Messages' . $bp->badge(3) => array(
589
     *         'New! ' . $bp->badge(1) => '#',
590
     *         'Read ' => '#',
591
     *         'Trashed ' => '#',
592
     *         '',
593
     *         'Spam ' . $bp->badge(2) => '#'
594
     *     ),
595
     *     'Disabled link' => '#'
596
     * ), array('active'=>'Home', 'disabled'=>'Disabled link'));
597
     * 
598
     * echo $bp->pills(array(
599
     *     'Nav' => '#',
600
     *     'Pills' => '#',
601
     *     'Justified' => '#'
602
     * ), array('active'=>'Nav', 'align'=>'justified'));
603
     * ```
604
     */
605 1
    public function pills(array $links, array $options = array())
606
    {
607 1
        $class = 'nav nav-pills';
608 1
        if (isset($options['align'])) {
609 1
            switch ($options['align']) {
610 1
                case 'justified':
611 1
                    $class .= ' nav-justified';
612 1
                    break;
613 1
                case 'vertical':
614 1
                case 'stacked':
615 1
                    $class .= ' nav-stacked';
616 1
                    break;
617 1
                case 'left':
618 1
                case 'right':
619 1
                    $class .= ' pull-'.$options['align'];
620 1
                    break;
621 1
            }
622 1
        }
623
624 1
        return $this->page->tag('ul', array('class' => $class), $this->links('li', $links, $options));
625
    }
626
627
    /**
628
     * This creates a Bootstrap styled breadcrumb trail. The last link is automatically activated.
629
     * 
630
     * @param array $links An ``array($name => $href)`` of links to display. The ``$href`` may also be another ``array($name => $href)`` of dropdown links.
631
     * 
632
     * @return string
633
     * 
634
     * ```php
635
     * $bp->breadcrumbs(array(
636
     *     'Home' => '#',
637
     *     'Library' => '#',
638
     *     'Data' => '#'
639
     * )); // <ul class="breadcrumb"><li><a href="#">Home</a></li> <li><a href="#">Library</a></li> <li class="active">Data</li></ul>
640
     * ```
641
     */
642 12
    public function breadcrumbs(array $links)
643
    {
644 12
        if (empty($links)) {
645 1
            return '';
646
        }
647 12
        foreach ($links as $name => $href) {
648 12
            if (is_array($href)) {
649 1
                list($dropdown, $id) = $this->dropdown($href);
650 1
                $link = $this->page->tag('a', array('href' => '#', 'data-toggle' => 'dropdown', 'id' => $id), $name, '<b class="caret"></b>');
651 1
                $links[$name] = '<li class="dropdown">'.$link.$dropdown.'</li>';
652 1
            } else {
653 12
                $links[$name] = '<li><a href="'.$href.'">'.$name.'</a></li>';
654
            }
655 12
            if ($name === 0) {
656 1
                $name = $href; // this should only happen to the last breadcrumb
657 1
            }
658 12
        }
659 12
        array_pop($links);
660
661 12
        return '<ul class="breadcrumb">'.implode(' ', $links).' <li class="active">'.$name.'</li></ul>';
0 ignored issues
show
The variable $name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
662
    }
663
664
    /**
665
     * This makes a Bootstrap label, and saves you having to type label twice. Awesome, right? We didn't build the class for this, but it was simple enough to include so I did. I use it.
666
     * 
667
     * @param string $class  Either '**default**', '**primary**', '**success**', '**info**', '**warning**', or '**danger**'. The label class and prefix are automatically included. You can add more classes to it if you like.
668
     * @param string $text   The label's text.
669
     * 
670
     * @return string
671
     * 
672
     * ```php
673
     * echo $bp->label('primary', 'New'); // <span class="label label-primary">New</span>
674
     * ```
675
     */
676 2
    public function label($class, $text)
677
    {
678 2
        return $this->page->tag('span', array(
679 2
            'class' => $this->prefixClasses('label', array('default', 'primary', 'success', 'info', 'warning', 'danger'), $class),
680 2
        ), $text);
681
    }
682
683
    /**
684
     * This method is a little bit more useful than label, so I am not as embarrassed to present it. If ``$count`` equals 0, or if it's not numeric (null?), then it still includes the tag but leaves the value empty.
685
     * 
686
     * @param integer $count  The number you would like to display.
687
     * @param string  $align  This will pull your badge 'right' or 'left' or not (default). In a list group, badges are automatically positioned on the right.
688
     * 
689
     * @return string
690
     * 
691
     * ```php
692
     * $count = 13;
693
     * echo $bp->badge($count, 'right'); // <span class="badge pull-right">13</span>
694
     * ```
695
     */
696 3
    public function badge($count, $align = '')
697
    {
698 3
        return $this->page->tag('span', array(
699 3
            'class' => !empty($align) ? 'badge pull-'.$align : 'badge',
700 3
        ), (is_numeric($count) && $count == 0) ? '' : $count);
701
    }
702
703
    /**
704
     * Creates Bootstrap alert messages.
705
     * 
706
     * @param string $type         Either '**success**', '**info**', '**warning**', or '**danger**'.
707
     * @param string $alert        The status message. All ``<h1-6>`` headers and ``<a>`` links will be classed appropriately.
708
     * @param bool   $dismissable  If you set this to false, then the alert will not be dismissable.
709
     * 
710
     * @return string
711
     * 
712
     * ```php
713
     * echo $bp->row('sm', array(
714
     *     $bp->col(4, $bp->alert('danger', '<strong>Danger</strong> Change a few things up and try submitting again. ', false)),
715
     *     $bp->col(4,  $bp->alert('success', '<strong>Success</strong> You successfully read this <a href="#">important alert message</a>. ', false)),
716
     *     $bp->col(4, $bp->alert('info', '<strong>Info</strong> This alert needs your attention, but it\'s not super important. '))
717
     * ));
718
     * ```
719
     */
720 2
    public function alert($type, $alert, $dismissable = true)
721
    {
722 2
        $html = '';
723 2
        $class = 'alert alert-'.$type;
724 2
        if ($dismissable) {
725 2
            $class .= ' alert-dismissable';
726 2
        }
727 2
        $html .= '<div class="'.$class.'" role="alert">';
728 2
        if ($dismissable) {
729 2
            $html .= '<button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span></button>';
730 2
        }
731 2
        $html .= $this->addClass($alert, array('h([1-6]){1}' => 'alert-heading', 'a' => 'alert-link'));
732 2
        $html .= '</div>';
733
734 2
        return $html;
735
    }
736
737
    /**
738
     * This allows you to create any flavor of progress bar that Bootstrap has to offer.
739
     * 
740
     * @param integer $percent  The amount of progress from 0 to 100. In order to stack multiple values then turn this into an array.
741
     * @param string  $class    You can include one of the four contextual classes: '**success**', '**info**', '**warning**' or '**danger**'. Also '**striped**' and '**active**' if you like the looks of those. These will all be properly prefixed. If you are stacking multiple bars, then turn this into an array and make sure your classes correspond with your percentages.
742
     * @param mixed   $display  If $display !== false, then the percentage will be displayed in the progress bar.
743
     * 
744
     * @return string
745
     * 
746
     * ```php
747
     * echo $bp->progress(60, 'info', 'display');
748
     * 
749
     * echo $bp->progress(array(25, 25, 25, 25), array('', 'warning', 'success', 'danger'));
750
     * ```
751
     */
752 1
    public function progress($percent, $class = '', $display = false)
753
    {
754 1
        $html = '';
755 1
        $classes = (array) $class;
756 1
        foreach ((array) $percent as $key => $progress) {
757 1
            $class = (isset($classes[$key])) ? $classes[$key] : '';
758 1
            $class = $this->prefixClasses('progress-bar', array('success', 'info', 'warning', 'danger', 'striped'), $class);
759 1
            $html .= $this->page->tag('div', array(
760 1
                'class' => $class,
761 1
                'style' => 'width:'.$progress.'%;',
762 1
                'role' => 'progressbar',
763 1
                'aria-valuenow' => $progress,
764 1
                'aria-valuemin' => 0,
765 1
                'aria-valuemax' => 100,
766 1
            ), $display !== false ? $progress.'%' : '<span class="sr-only">'.$progress.'% Complete</span>');
767 1
        }
768
769 1
        return '<div class="progress">'.$html.'</div>';
770
    }
771
772
    /**
773
     * This is the easiest way I could devise of making Bootstrap media objects as manageable as possible. ``<h1-6>`` Headers and ``<img>`` images will automatically be classed appropriately.
774
     * 
775
     * @param array $list  A media array row that looks like this: ``array($left, $body, $right);``
776
     * 
777
     * - If you don't have an image or whatever for the left side, then set an empty value
778
     * - If you have nothing to right align then you can either leave it off, or set an empty value
779
     * - If you have a special class and / or id to assign, then you can include them in the array like so:
780
     *   - ``array('id' => $id, 'class' => 'custom', $left, $body, $right);``
781
     * - You can pack unlimited $list's (arguments) into this method, each $list being a sibling of the other:
782
     *   - ``$bp->media(array($left, $body, $right), array($left, $body, $right), array($left, $body, $right));``
783
     * - To nest media comments in a parent / child relationship, just add another media array row to the parent:
784
     *   - ``array($left, $body, $right, array($left, $body, $right, array($left, $body, $right)));`` - this would be a grandparent, parent, child setup
785
     *   - ``array($left, $body, $right, array($left, $body, $right), array($left, $body, $right));`` - a parent, child, child arrangement
786
     *   - ``array($left, $body, $right, array($left, $body, $right, array($left, $body, $right), array($left, $body, $right)), array($left, $body, $right)), array($left, $body, $right));`` - now I'm just messing with you, but I think you've got the picture (a parent, child, grandchild, grandchild, child, sibling)
787
     *   - This could go on ad infinitum, but soon your content will become pretty scrunched up if you take it too far.
788
     * 
789
     * @return string
790
     * 
791
     * ```php
792
     * $image = '<img alt="64x64" style="width: 64px; height: 64px;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAABWUlEQVR4nO2VMY7CMBBFuf9RfAMfwL17t67nCkM1aHBIASF5QvziSbtLGD0/T7Q3M/N/5kYL0CgALUCjALQAjQLQAjQKQAvQKAAtQKMAtACNAtACNApAC9AoAC1AowC0AI0C0AI0CkAL0HwlQCnlQe998/kYY/ezK2eeEqDW6q01NzNvrXkpxeecm2fekT1j5ikB5pxeSvExxu4zvfeNbHwvDtl7fxzy05lIgFjDkFnF82FW2bhZM3uKcWTm5QHi5kJiXddY5ZBeZfN7/q2ZyAbEDeXfX631KhuHy38/OvPSAOv7mmXzCmdi1eO78Vzc8JGZlwfIK5lvdC9Uvq1aq9daNz8fmYkEMHt+l9d/V69kY5XXW86HeXcmGuCXUQBagEYBaAEaBaAFaBSAFqBRAFqARgFoARoFoAVoFIAWoFEAWoBGAWgBGgWgBWgUgBagUQBagEYBaAGaO387LYipKEKVAAAAAElFTkSuQmCC">';
793
     * 
794
     * $content = '<h4>Media Heading</h4><p>Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis.</p>';
795
     * 
796
     * echo $bp->media(
797
     *     array('class'=>'special', $image, $content, $image, array($image, $content, array($image, $content, $image)), array('class'=>'content', $image, $content)), // parent, child, grandchild, child
798
     *     array($image, $content, $image) // sibling
799
     * );
800
     * ```
801
     */
802 1
    public function media(array $list, $parent = 0)
803
    {
804 1
        if (is_numeric($parent) && isset($list[0]) && is_array($list[0])) {
805 1
            $media = array();
806 1
            foreach ($list[$parent] as $child => $display) {
807 1
                if (isset($list[$child])) {
808 1
                    $display[] = $this->media($list, $child)[0];
809 1
                }
810 1
                $media[] = $display;
811 1
            }
812
813 1
            return ($parent === 0) ? call_user_func_array(array($this, 'media'), $media) : $media;
814
        } else {
815 1
            $html = '';
816 1
            $siblings = func_get_args();
817 1
            $parent = array_shift($siblings);
818 1
            $children = array();
819 1
            foreach ($parent as $key => $value) {
820 1
                if (is_array($value)) {
821 1
                    $children[] = $value;
822 1
                    unset($parent[$key]);
823 1
                }
824 1
            }
825 1
            $div = $parent;
826 1
            unset($parent['id'], $parent['class']);
827 1
            list($left, $body, $right) = array_pad($parent, 3, '');
828 1
            $media = '';
829 1
            if (!empty($left)) {
830 1
                $media .= '<div class="media-left">'.$this->addClass($left, array('img' => 'media-object')).'</div>';
831 1
            }
832 1
            $media .= '<div class="media-body">';
833 1
            if (!empty($body)) {
834 1
                $media .= $this->addClass($body, array('h([1-6]){1}' => 'media-heading'));
835 1
            }
836 1
            if (!empty($children)) {
837 1
                $media .= call_user_func_array(array($this, 'media'), $children);
838 1
            }
839 1
            $media .= '</div>';
840 1
            if (!empty($right)) {
841 1
                $media .= '<div class="media-right">'.$this->addClass($right, array('img' => 'media-object')).'</div>';
842 1
            }
843
844 1
            $html .= $this->page->tag('div', array(
845 1
                'id' => isset($div['id']) ? $div['id'] : '',
846 1
                'class' => isset($div['class']) ? 'media '.$div['class'] : 'media',
847 1
            ), $media);
848 1
            if (!empty($siblings)) {
849 1
                $html .= call_user_func_array(array($this, 'media'), $siblings);
850 1
            }
851
852 1
            return $html;
853
        }
854
    }
855
856
    /**
857
     * Displays a Bootstrap list group. ``<h1-6>`` Headers and ``<p>`` paragraphs will automatically be classed appropriately.
858
     * 
859
     * @param array $links   If you would like to create an unordered list, then this is just an array of values. Otherwise this will be an ``array($name => $href, ...)`` of links. $name badges will automatically be positioned on the right.
860
     * @param mixed $active  This value can be either the $name, $href (link), or number (starting from 1) that you would like to be selected as "active".
861
     * 
862
     * @return string
863
     * 
864
     * ```php
865
     * $bp->list_group(array(
866
     *     'Unordered',
867
     *     'List',
868
     *     $bp->badge(1) . 'Group'
869
     * ));
870
     * 
871
     * $bp->list_group(array(
872
     *     'Anchor' => '#',
873
     *     'List' => '#',
874
     *     'Group' . $bp->badge(2) => '#'
875
     * ), 'Anchor');
876
     * 
877
     * $bp->list_group(array(
878
     *     '<h4>Custom Content</h4><p>Donec id elit non mi porta gravida.</p>' => '#',
879
     *     $bp->badge(3) . '<h4>Linked List Group</h4><p>Donec id elit non mi porta gravida.</p>' => '#'
880
     * ), 1);
881
     * ```
882
     */
883 2
    public function listGroup(array $links, $active = false)
884
    {
885 2
        $html = '';
886 2
        $tag = 'a';
887 2
        $count = 1;
888 2
        foreach ($links as $name => $href) {
889 2
            if (empty($html) && empty($name)) {
890 2
                $tag = 'li';
891 2
            }
892 2
            $attributes = array('class' => 'list-group-item');
893 2
            if ($tag == 'li') {
894 2
                $name = $href;
895 2
            } else {
896 1
                $attributes['href'] = $href;
897 1
                if (in_array($active, array($count, $name, $href))) {
898 1
                    $attributes['class'] .= ' active';
899 1
                }
900
            }
901 2
            $html .= $this->page->tag($tag, $attributes, $this->addClass($name, array(
902 2
                'h([1-6]){1}' => 'list-group-item-heading',
903 2
                'p' => 'list-group-item-text',
904 2
            )));
905 2
            ++$count;
906 2
        }
907
908 2
        return $this->page->tag($tag == 'a' ? 'div' : 'ul', array('class' => 'list-group'), $html);
909
    }
910
911
    /**
912
     * Creates a Bootstrap panel component.
913
     * 
914
     * @param string $class     Either '**default**', '**primary**', '**success**', '**info**', '**warning**', or '**danger**'. The panel class and prefix are automatically included. You can add more classes to it if you like.
915
     * @param array  $sections  An ``array($panel => $content, ...)`` of sections. If $panel equals:
916
     * 
917
     * - '**head**', '**header**', or '**heading**' - The panel heading $content. All ``<h1-6>`` headers will be classed appropriately
918
     * - '**body**' - The panel body $content
919
     * - '**foot**', '**footer**', or '**footing**' - The panel footer $content
920
     * - Anything else will just be inserted as is. It could be a table, or list group, or ...
921
     * 
922
     * @return string
923
     * 
924
     * ```php
925
     * echo $bp->row('sm', array(
926
     *     $bp->col(2, $bp->panel('default', array('header'=>'Header', 'body'=>'Default', 'footer'=>'Footer'))),
927
     *     $bp->col(2, $bp->panel('primary', array('header'=>'Header', 'body'=>'Primary', 'footer'=>'Footer'))),
928
     *     $bp->col(2, $bp->panel('success', array('header'=>'Header', 'body'=>'Success', 'footer'=>'Footer'))),
929
     *     $bp->col(2, $bp->panel('info', array('header'=>'Header', 'body'=>'Info', 'footer'=>'Footer'))),
930
     *     $bp->col(2, $bp->panel('warning', array('header'=>'Header', 'body'=>'Warning', 'footer'=>'Footer'))),
931
     *     $bp->col(2, $bp->panel('danger', array('header'=>'Header', 'body'=>'Danger', 'footer'=>'Footer'))),
932
     * ));
933
     * ```
934
     */
935 2
    public function panel($class, $sections)
936
    {
937 2
        $html = '';
938 2
        foreach ($sections as $panel => $content) {
939 2
            if (!is_numeric($panel)) {
940 2
                $panel = substr($panel, 0, 4);
941 2
            }
942 2
            switch ((string) $panel) {
943 2
                case 'head':
944 2
                    $html .= '<div class="panel-heading">'.$this->addClass($content, array('h([1-6]){1}' => 'panel-title')).'</div>';
945 2
                    break;
946 1
                case 'body':
947 1
                    $html .= '<div class="panel-body">'.$content.'</div>';
948 1
                    break;
949 1
                case 'foot':
950 1
                    $html .= '<div class="panel-footer">'.$content.'</div>';
951 1
                    break;
952 1
                default:
953 1
                    $html .= $content;
954 1
                    break; // a table, or list group, or ...
955 1
            }
956 2
        }
957
958 2
        return $this->page->tag('div', array(
959 2
            'class' => $this->prefixClasses('panel', array('default', 'primary', 'success', 'info', 'warning', 'danger'), $class),
960 2
        ), $html);
961
    }
962
963
    /**
964
     * This is for creating toggleable tabs and pills for transitioning through panes of local content.
965
     * 
966
     * @param string $type     Specify either '**tabs**' or '**pills**'.
967
     * @param array  $links    An ``array($name => $html, ...)`` of content to toggle through. If $html is an array unto itself, then it will be turned into a dropdown menu with the same header and divider rules applied as with ``$bp->buttons()``.
968
     * @param array  $options  The available options are:
969
     * 
970
     * - '**fade**' - No key, just the value. Will give your panes a fade in effect while toggling.
971
     * - '**active**' and '**disabled**' => $name, $html (if you dare), or number (starting from 1)
972
     * - 'align' => '**justified**', '**left**', or '**right**'
973
     * 
974
     * @return string
975
     * 
976
     * ```php
977
     * echo $bp->toggle('tabs', array(
978
     *     'Toggle' => '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.',
979
     *     'Profile' => 'Food truck fixie locavore, accusamus mcsweeney\'s marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.',
980
     *     'Dropdown' => array(
981
     *         'This' => 'Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney\'s organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven\'t heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.',
982
     *         'That' => 'Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.'
983
     *     )
984
     * ), array('active'=>1, 'fade'));
985
     * ```
986
     */
987 1
    public function toggle($type, array $links, array $options = array())
988
    {
989 1
        $count = 1;
990 1
        $toggle = array();
991 1
        $content = '';
992 1
        $class = (in_array('fade', $options)) ? 'tab-pane fade' : 'tab-pane';
993 1
        $active = (isset($options['active'])) ? $options['active'] : '';
994 1
        $disabled = (isset($options['disabled'])) ? $options['disabled'] : '';
995 1
        foreach ($links as $name => $html) {
996 1
            if (is_array($html)) {
997 1
                foreach ($html as $drop => $down) { // cannot be an array, but can be disabled, active, or empty
998 1
                    if (is_numeric($drop)) { // then it is either a header or a divider
999 1
                        $toggle[$name][$drop] = $down;
1000 1
                    } else {
1001 1
                        $id = $this->page->id('tabs');
1002
                    }
1003 1
                    $toggle[$name][$drop] = '#'.$id;
0 ignored issues
show
The variable $id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1004 1
                    if ($active == $drop || $active == $count) {
1005 1
                        $options['active'] = '#'.$id;
1006 1
                        $content .= '<div role="tabpanel" class="'.$class.' in active" id="'.$id.'">'.$down.'</div>';
1007 1
                    } else {
1008 1
                        if ($disabled == $drop || $disabled == $count) {
1009 1
                            $options['disabled'] = '#'.$id;
1010 1
                        }
1011 1
                        $content .= '<div role="tabpanel" class="'.$class.'" id="'.$id.'">'.$down.'</div>';
1012
                    }
1013 1
                    ++$count;
1014 1
                }
1015 1
            } else { // $name (a tab) cannot be empty
1016 1
                if ($frag = strpos($name, '#')) { // if it is the first character then it doesn't count
1017 1
                    $id = substr($name, $frag + 1);
1018 1
                    $name = substr($name, 0, $frag);
1019 1
                } else {
1020 1
                    $id = $this->page->id('tabs');
1021
                }
1022 1
                $toggle[$name] = '#'.$id;
1023 1
                if ($active == $name || $active == $count) {
1024 1
                    $options['active'] = '#'.$id;
1025 1
                    $content .= '<div role="tabpanel" class="'.$class.' in active" id="'.$id.'">'.$html.'</div>';
1026 1
                } else {
1027 1
                    if ($disabled == $name || $disabled == $count) {
1028 1
                        $options['disabled'] = '#'.$id;
1029 1
                    }
1030 1
                    $content .= '<div role="tabpanel" class="'.$class.'" id="'.$id.'">'.$html.'</div>';
1031
                }
1032 1
                ++$count;
1033
            }
1034 1
        }
1035 1
        if (substr($type, 0, 4) == 'pill') {
1036 1
            $options['toggle'] = 'pill';
1037 1
            $class = 'nav nav-pills';
1038 1
        } else { // tabs
1039 1
            $options['toggle'] = 'tab';
1040 1
            $class = 'nav nav-tabs';
1041
        }
1042 1
        if (isset($options['align']) && $options['align'] == 'justified') {
1043 1
            $class .= ' nav-justified';
1044 1
        }
1045
1046 1
        return '<ul class="'.$class.'" role="tablist">'.$this->links('li', $toggle, $options).'</ul><div class="tab-content">'.$content.'</div>';
1047
    }
1048
1049
    /**
1050
     * Bootstrap accordions are basically collapsible panels, so keep that in mind. That is essentially what you are creating here.
1051
     * 
1052
     * @param string  $class     Either '**default**', '**primary**', '**success**', '**info**', '**warning**', or '**danger**'. These only apply to the head section, and are passed directly by us into ``$this->panel()``.
1053
     * @param array   $sections  An ``array($heading => $body, ...)`` of sections that will become your accordion. The ``<h1-6>`` headers in the $heading will be automatically classed appropriately. Accordions are definitely nestable, but we don't create them via nested arrays through this method. Just add a pre-made accordion to the $body you would like it to reside in ie. the $body should never be an array.
1054
     * @param integer $open      This is the panel number you would like be open from the get-go (starting at 1). If you don't want any to be open initially, then set this to 0.
1055
     * 
1056
     * @return string
1057
     * 
1058
     * ```php
1059
     * echo $bp->accordion('info', array(
1060
     *     '<h4>Collapsible Group Item #1</h4>' => 'Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven\'t heard of them accusamus labore sustainable VHS.',
1061
     *     '<h4>Collapsible Group Item #2</h4>' => 'Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven\'t heard of them accusamus labore sustainable VHS.',
1062
     *     '<h4>Collapsible Group Item #3</h4>' => 'Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven\'t heard of them accusamus labore sustainable VHS.'
1063
     * ), 0);
1064
     * ```
1065
     */
1066 1
    public function accordion($class, array $sections, $open = 1)
1067
    {
1068 1
        $html = '';
1069 1
        $count = 0;
1070 1
        $id = $this->page->id('accordion');
1071 1
        foreach ($sections as $head => $body) {
1072 1
            ++$count;
1073 1
            $heading = $this->page->id('heading');
1074 1
            $collapse = $this->page->id('collapse');
1075 1
            $in = ($open == $count) ? ' in' : '';
1076
            $attributes = array(
1077 1
                'role' => 'button',
1078 1
                'data-toggle' => 'collapse',
1079 1
                'data-parent' => '#'.$id,
1080 1
                'href' => '#'.$collapse,
1081 1
                'aria-expanded' => !empty($in) ? 'true' : 'false',
1082 1
                'aria-controls' => $collapse,
1083 1
            );
1084 1
            $begin = strpos($head, '>') + 1;
1085 1
            $end = strrpos($head, '</');
1086 1
            $head = substr($head, 0, $begin).$this->page->tag('a', $attributes, substr($head, $begin, $end - $begin)).substr($head, $end);
1087 1
            $head = substr($this->panel($class, array('head' => $head)), 0, -6); // </div>
1088 1
            $html .= substr_replace($head, ' role="tab" id="'.$heading.'"', strpos($head, 'class="panel-heading"') + 21, 0);
1089 1
            $html .= $this->page->tag('div', array(
1090 1
                    'id' => $collapse,
1091 1
                    'class' => 'panel-collapse collapse'.$in,
1092 1
                    'role' => 'tabpanel',
1093 1
                    'aria-labelledby' => $heading,
1094 1
                ), strpos($body, 'class="list-group"') ? $body : '<div class="panel-body">'.$body.'</div>');
1095 1
            $html .= '</div>'; // the one we removed from the $head up top
1096 1
        }
1097
1098 1
        return $this->page->tag('div', array(
1099 1
            'class' => 'panel-group',
1100 1
            'id' => $id,
1101 1
            'role' => 'tablist',
1102 1
            'aria-multiselectable' => 'true',
1103 1
        ), $html);
1104
    }
1105
1106
    /**
1107
     * Creates a Bootstrap carousel for cycling through elements. Those elements don't necessarily need to be images, but pretty much they always are.
1108
     * 
1109
     * @param array $images   An ``array($image, ...)`` of images to cycle through, starting with the first (logically). To get fancy and add captions, then make this an ``array($image => $caption, ...)`` of images with captions to cycle through. If you have some images with captions and others without, then you can merge these two concepts no problem. Remember, the $image is not just a location, it is the entire ``<img>`` tag src and all.
1110
     * @param array $options  The available option keys are:
1111
     * 
1112
     * - '**interval**' - The time delay in thousandths of a second between cycles (or frame changes). The default is 5000 ie. 5 seconds.
1113
     * - '**indicators**' - Those little circle things at the bottom to give you an idea of where you are at. If you don't want them, then set this to false. The default is true ie. include them.
1114
     * - '**controls**' - The clickable arrows on the side that you can click to get to what you are interested in. If you don't want them, then set this to false. The default is true ie. include them. Also by default we use ``array($bp->icon('chevron-left'), $bp->icon('chevron-right'))`` for the left and right arrows. If you would like something else, then you can make this an array of your preferences.
1115
     * 
1116
     * @return string
1117
     */
1118 1
    public function carousel(array $images, array $options = array())
1119
    {
1120 1
        $html = '';
1121 1
        $id = $this->page->id('carousel');
1122 1
        $options = array_merge(array(
1123 1
            'interval' => 5000, // ie. 5 seconds in between frame changes
1124 1
            'indicators' => true, // set to false if you don't want them
1125 1
            'controls' => true, // set to false if you don't want them
1126 1
        ), $options);
1127 1
        if ($options['indicators']) {
1128 1
            $indicators = array_keys(array_values($images));
1129 1
            $html .= '<ol class="carousel-indicators">';
1130 1
            $html .= '<li data-target="#'.$id.'" data-slide-to="'.array_shift($indicators).'" class="active"></li>';
1131 1
            foreach ($indicators as $num) {
1132 1
                $html .= '<li data-target="#'.$id.'" data-slide-to="'.$num.'"></li>';
1133 1
            }
1134 1
            $html .= '</ol>';
1135 1
        }
1136 1
        $html .= '<div class="carousel-inner" role="listbox">';
1137 1
        foreach ($images as $key => $value) {
1138 1
            $class = (isset($class)) ? 'item' : 'item active'; // ie. the first one is active
1139 1
            $img = (!is_numeric($key)) ? $key : $value;
1140 1
            $caption = (!is_numeric($key)) ? '<div class="carousel-caption">'.$value.'</div>' : '';
1141 1
            $html .= '<div class="'.$class.'">'.$img.$caption.'</div>';
1142 1
        }
1143 1
        $html .= '</div>';
1144 1
        if ($options['controls']) {
1145 1
            if (is_array($options['controls'])) {
1146 1
                list($left, $right) = $options['controls'];
1147 1
                if (strpos($left, '<') === false) {
1148 1
                    $left = $this->icon($left, 'glyphicon', 'span aria-hidden="true"');
1149 1
                }
1150 1
                if (strpos($right, '<') === false) {
1151 1
                    $right = $this->icon($right, 'glyphicon', 'span aria-hidden="true"');
1152 1
                }
1153 1
            } else {
1154 1
                $left = $this->icon('chevron-left', 'glyphicon', 'span aria-hidden="true"');
1155 1
                $right = $this->icon('chevron-right', 'glyphicon', 'span aria-hidden="true"');
1156
            }
1157 1
            $html .= $this->page->tag('a', array(
1158 1
                'class' => 'left carousel-control',
1159 1
                'href' => '#'.$id,
1160 1
                'role' => 'button',
1161 1
                'data-slide' => 'prev',
1162 1
            ), $left, '<span class="sr-only">Previous</span>');
1163 1
            $html .= $this->page->tag('a', array(
1164 1
                'class' => 'right carousel-control',
1165 1
                'href' => '#'.$id,
1166 1
                'role' => 'button',
1167 1
                'data-slide' => 'next',
1168 1
            ), $right, '<span class="sr-only">Next</span>');
1169 1
        }
1170
1171 1
        return $this->page->tag('div', array(
1172 1
            'id' => $id,
1173 1
            'class' => 'carousel slide',
1174 1
            'data-ride' => 'carousel',
1175 1
            'data-interval' => $options['interval'],
1176 1
        ), $html);
1177
    }
1178
1179 6
    protected function dropdown(array $links, array $options = array(), &$count = 1)
1180
    {
1181 6
        $html = '';
1182 6
        $toggle = (isset($options['toggle'])) ? ' data-toggle="'.$options['toggle'].'"' : '';
1183 6
        foreach ($links as $name => $href) {
1184 6
            if (empty($href)) {
1185 1
                $html .= '<li role="presentation" class="divider"></li>';
1186 6
            } elseif (is_numeric($name)) {
1187 4
                $html .= '<li role="presentation" class="dropdown-header">'.$href.'</li>';
1188 4
            } else {
1189 6
                $link = '<a role="menuitem" tabindex="-1" href="'.$href.'"'.$toggle.'>'.$name.'</a>';
1190 6
                $html .= $this->listItem($link, $options, $name, $href, $count);
1191 6
                ++$count;
1192
            }
1193 6
        }
1194 6
        $class = 'dropdown-menu';
1195 6
        if (isset($options['pull'])) {
1196 1
            $class .= ' dropdown-menu-'.$options['pull'];
1197 1
        }
1198 6
        $id = $this->page->id('dropdown');
1199 6
        $html = $this->page->tag('ul', array(
1200 6
            'class' => $class,
1201 6
            'aria-labelledby' => $id,
1202 6
        ), $html);
1203
1204 6
        return array($html, $id);
1205
    }
1206
1207 8
    protected function listItem($link, $options, $name, $href, $count)
1208
    {
1209 8
        $name = trim(preg_replace('/(\<[^\<]+\<\/[^\>]+\>)/i', '', $name)); // remove tags and their contents
1210 8
        $check = array_flip(array($name, $href, $count));
1211 8
        if (isset($options['active']) && isset($check[$options['active']])) {
1212 6
            return '<li role="presentation" class="active">'.$link.'</li>';
1213 8
        } elseif (isset($options['disabled']) && isset($check[$options['disabled']])) {
1214 4
            return '<li role="presentation" class="disabled">'.$link.'</li>';
1215
        } else {
1216 8
            return '<li role="presentation">'.$link.'</li>';
1217
        }
1218
    }
1219
}
1220