Helper function to load the menu items to the html element.

Allows overloading by sub classes.

@param \MUtil_Html_ListElement $ul
@param array $items
@param boolean $cascade render nested items
167366
protected function renderItems(\MUtil_Html_ListElement $ul, array $items, $cascade)
    {
        foreach ($items as $item) {
            if (isset($item['visible'], $item['label']) && $item['visible'] && $item['label']) {
                $url = $item['params'];
                $url['controller'] = $item['controller'];
                $url['action']     = $item['action'];
                $url['RouteReset'] = true;
                $li = $ul->li();
                if (isset($item['active']) && $item['active']) {
                    $li->class = 'active';
                }
                if (isset($item['liClass']) && $item['liClass']) {
                    $li->appendAttrib('class', $item['liClass']);
                }

                $a = $li->a($url, $item['label']);
                if (isset($item['class'])) {
                    $a->class = $item['class'];
                }
                if (isset($item['target'])) {
                    $a->target = $item['target'];
                }

                if ($cascade && isset($item['pages']) && is_array($item['pages'])) {
                    $this->renderItems($li->ul(array('class' => 'subnav '. $this->_menuUlClass)), $item['pages'], true);
                }
            }
        }
    }
1356601
function UnorderedList(items) {
  if (typeof items == 'string') {
    items = items.split(' ');
  }
  this._items = items || [];
  this.__defineGetter__('length', this._length);
}
303982
private function _renderItemsTogether ($actualGroupName = '', $items = [], $indent = 0, $minify = FALSE) {
		// some configurations is not possible to render together and minimized
		list($itemsToRenderMinimized, $itemsToRenderSeparately) = $this->filterItemsForNotPossibleMinifiedAndPossibleMinifiedItems($items);

		$indentStr = $this->getIndentString($indent);
		$resultItems = [];
		if (self::$fileRendering) $resultItems[] = '<!-- css group begin: ' . $actualGroupName . ' -->';

		// process array with groups, which are not possible to minimize
		foreach ($itemsToRenderSeparately as & $itemsToRender) {
			$resultItems[] = $this->_renderItemsTogetherAsGroup($itemsToRender, $minify);
		}

		// process array with groups to minimize
		foreach ($itemsToRenderMinimized as & $itemsToRender) {
			$resultItems[] = $this->_renderItemsTogetherAsGroup($itemsToRender, $minify);
		}

		if (self::$fileRendering) $resultItems[] = '<!-- css group end: ' . $actualGroupName . ' -->';

		return $indentStr . implode(PHP_EOL . $indentStr, $resultItems);
	}
560926
public function current($item, $level = null)
    {
        if (is_null($item)) {
            return null;
        }

        if (!$item instanceof MenuItem) {
            throw new \UnexpectedValueException(sprintf('$ITEM must be \Knp\Menu\MenuItem not "%s"', get_class($item)));
        }

        /** @var MenuItem $child */
        foreach ($item->getChildren() as $child) {
            if (
                (null !== $this->matcher && $this->matcher->isAncestor($child))
             || (null === $this->matcher && $child->isCurrentAncestor())
            ) {
                if ($level !== null and $level == $child->getLevel()) {
                    return $child;
                }

                return $this->current($child);
            }

            if (
                (null !== $this->matcher && $this->matcher->isCurrent($child))
             || (null === $this->matcher && $child->isCurrent())
            ) {
                return $child;
            }
        }

        return null;
    }
617243
private function parentMatches(MenuItem $parent = null, $item)
    {
        if (null === $parent) {
            return null === $item['parent'];
        }

        return $item['parent'] === $parent->getRoute();
    }
1301896
public static function unorderedList( $items, $level = 0, $attributes = array() )
	{
		$depth1		= 2 * abs( (int) $level );
		$depth2		= $level ? 2 * abs( (int) $level - 1 ) + 1 : 0;
		$indent1	= str_repeat( "  ", $depth1 );
		$indent2	= str_repeat( "  ", $depth2 );
		$content	= "\n".implode( "\n", $items )."\n".$indent1;
		$tag		= UI_HTML_Tag::create( "ul", $content, $attributes );
		$code		= $indent1.$tag."\n".$indent2;
		return $code;
	}
1360461
protected function navigation_node($items, $attrs=array(), $expansionlimit=null, array $options = array(), $depth=1) {
        // Exit if empty, we don't want an empty ul element.
        if (count($items) === 0) {
            return '';
        }

        // Turn our navigation items into list items.
        $lis = array();
        // Set the number to be static for unique id's.
        static $number = 0;
        foreach ($items as $item) {
            $number++;
            if (!$item->display && !$item->contains_active_node()) {
                continue;
            }

            $isexpandable = (empty($expansionlimit) || ($item->type > navigation_node::TYPE_ACTIVITY || $item->type < $expansionlimit) || ($item->contains_active_node() && $item->children->count() > 0));

            // Skip elements which have no content and no action - no point in showing them
            if (!$isexpandable && empty($item->action)) {
                continue;
            }

            $id = $item->id ? $item->id : html_writer::random_id();
            $content = $item->get_content();
            $title = $item->get_title();
            $ulattr = ['id' => $id . '_group', 'role' => 'group'];
            $liattr = ['class' => [$item->get_css_type(), 'depth_'.$depth]];
            $pattr = ['class' => ['tree_item'], 'role' => 'treeitem'];
            $pattr += !empty($item->id) ? ['id' => $item->id] : [];
            $isbranch = $isexpandable && ($item->children->count() > 0 || ($item->has_children() && (isloggedin() || $item->type <= navigation_node::TYPE_CATEGORY)));
            $hasicon = ((!$isbranch || $item->type == navigation_node::TYPE_ACTIVITY || $item->type == navigation_node::TYPE_RESOURCE) && $item->icon instanceof renderable);
            $icon = '';

            if ($hasicon) {
                $liattr['class'][] = 'item_with_icon';
                $pattr['class'][] = 'hasicon';
                $icon = $this->output->render($item->icon);
                // Because an icon is being used we're going to wrap the actual content in a span.
                // This will allow designers to create columns for the content, as we've done in styles.css.
                $content = $icon . html_writer::span($content, 'item-content-wrap');
            }
            if ($item->helpbutton !== null) {
                $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton'));
            }
            if (empty($content)) {
                continue;
            }

            $nodetextid = 'label_' . $depth . '_' . $number;
            $attributes = array('tabindex' => '-1', 'id' => $nodetextid);
            if ($title !== '') {
                $attributes['title'] = $title;
            }
            if ($item->hidden) {
                $attributes['class'] = 'dimmed_text';
            }
            if (is_string($item->action) || empty($item->action) ||
                    (($item->type === navigation_node::TYPE_CATEGORY || $item->type === navigation_node::TYPE_MY_CATEGORY) &&
                    empty($options['linkcategories']))) {
                $content = html_writer::tag('span', $content, $attributes);
            } else if ($item->action instanceof action_link) {
                //TODO: to be replaced with something else
                $link = $item->action;
                $link->text = $icon.html_writer::span($link->text, 'item-content-wrap');
                $link->attributes = array_merge($link->attributes, $attributes);
                $content = $this->output->render($link);
            } else if ($item->action instanceof moodle_url) {
                $content = html_writer::link($item->action, $content, $attributes);
            }

            if ($isbranch) {
                $pattr['class'][] = 'branch';
                $liattr['class'][] = 'contains_branch';
                $pattr += ['aria-expanded' => ($item->has_children() && (!$item->forceopen || $item->collapse)) ? "false" : "true"];
                if ($item->requiresajaxloading) {
                    $pattr += [
                        'data-requires-ajax' => 'true',
                        'data-loaded' => 'false',
                        'data-node-id' => $item->id,
                        'data-node-key' => $item->key,
                        'data-node-type' => $item->type
                    ];
                } else {
                    $pattr += ['aria-owns' => $id . '_group'];
                }
            }

            if ($item->isactive === true) {
                $liattr['class'][] = 'current_branch';
            }
            if (!empty($item->classes) && count($item->classes)>0) {
                $pattr['class'] = array_merge($pattr['class'], $item->classes);
            }

            $liattr['class'] = join(' ', $liattr['class']);
            $pattr['class'] = join(' ', $pattr['class']);

            $pattr += $depth == 1 ? ['data-collapsible' => 'false'] : [];
            if (isset($pattr['aria-expanded']) && $pattr['aria-expanded'] === 'false') {
                $ulattr += ['aria-hidden' => 'true'];
            }

            // Create the structure.
            $content = html_writer::tag('p', $content, $pattr);
            if ($isexpandable) {
                $content .= $this->navigation_node($item->children, $ulattr, $expansionlimit, $options, $depth + 1);
            }
            if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) {
                $content = html_writer::empty_tag('hr') . $content;
            }

            $liattr['aria-labelledby'] = $nodetextid;
            $content = html_writer::tag('li', $content, $liattr);
            $lis[] = $content;
        }

        if (count($lis) === 0) {
            // There is still a chance, despite having items, that nothing had content and no list items were created.
            return '';
        }

        // We used to separate using new lines, however we don't do that now, instead we'll save a few chars.
        // The source is complex already anyway.
        return html_writer::tag('ul', implode('', $lis), $attrs);
    }
101207
public static function alisty($items, $attribs = array(), $type = 'ul', $active_title = null)
    {
        if (!Arr::iterable($items)) {
            return '';
        }
        
        $htmls = array();
        $htmls[] = '<' . $type . self::attribs($attribs) . '>';
        foreach ($items as $title => $href) {
            $body = (is_array($href)) ? Html::a($title, $href) : Html::link($href, $title);
            
            $classes = array(Str::machine($title));
            if ($active_title === $title) {
                $classes[] = 'active';
            }
            
            $htmls[] = sprintf('<li class="%s">', join(' ', $classes)) . $body . '</li>';
        }
        $htmls[] = '</' . $type . '>';
        return join("\n", $htmls);
    }
659256
protected function renderItems($items, $options = [])
    {
        $lines = [];
        foreach ($items as $item) {
            if (is_string($item)) {
                $lines[] = $item;
                continue;
            }
            if (isset($item['visible']) && !$item['visible']) {
                continue;
            }
            if (!array_key_exists('label', $item)) {
                throw new InvalidConfigException("The 'label' option is required.");
            }
            $encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
            $label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
            $itemOptions = ArrayHelper::getValue($item, 'options', []);
            $linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
            $linkOptions['tabindex'] = '-1';
            $url = array_key_exists('url', $item) ? $item['url'] : null;
            if (empty($item['items'])) {
                if ($url === null) {
                    $content = $label;
                    Html::addCssClass($itemOptions, ['widget' => 'dropdown-header']);
                } else {
                    $content = Html::a($label, $url, $linkOptions);
                }
            } else {
                $submenuOptions = $this->submenuOptions;
                if (isset($item['submenuOptions'])) {
                    $submenuOptions = array_merge($submenuOptions, $item['submenuOptions']);
                }
                $content = Html::a($label, $url === null ? '#' : $url, $linkOptions)
                    . $this->renderItems($item['items'], $submenuOptions);
                Html::addCssClass($itemOptions, ['widget' => 'dropdown-submenu']);
            }

            $lines[] = Html::tag('li', $content, $itemOptions);
        }

        return Html::tag('ul', implode("\n", $lines), $options);
    }
786970
public function render($key, array $items = [], array $options = [], $level = 1)
    {
        $i = 0;
        $output    = [];
        $options   = Hash::merge($this->_default, $options);
        $sorted    = Hash::sort($items, '{s}.weight', 'ASC');
        $itemCount = count($sorted);

        foreach ($sorted as $item) {
            $i++;
            $item = $this->_setFirstLast($i, $itemCount, $item);

            $children = false;
            if (count($item['children']) > 0) {
                $children = $this->render($key, $item['children'], $options, $level + 1);
            }

            $itemParams = [
                'options'  => $options,
                'item'     => $item,
                'count'    => $i,
                'children' => $children,
                'level'    => $level,
            ];

            $this->_setItemParams($itemParams);

            if (isset($item['callable']) && is_callable($item['callable'])) {
                $output[] = call_user_func_array($item['callable'], array_merge(['view' => $this->_View], $itemParams));
            } else {
                $output[] = $this->_View->element($options['itemElement'], $itemParams);
            }
        }

        $element = $this->_getCurrentMenuElement($options, $level);

        return $this->_View->element($element, [
            'content' => $output,
            'options' => $options,
        ]);
    }
164443
protected function buildList(array $items, string $marker, int $nestingLevel = 0, int $parentWidth = 0): string
	{
		$width  = $this->calculateWidth($items, $marker);
		$number = 0;
		$list   = '';

		foreach($items as $item)
		{
			if(is_array($item))
			{
				$list .= $this->buildList($item, $marker, ($nestingLevel + 1), ($width['marker'] - 1 + $parentWidth));
			}
			else
			{
				$list .= $this->buildListItem($item, $marker, $width['number'], ++$number, $nestingLevel, $parentWidth);
			}
		}

		return $list;
	}
797861
protected function registerBreadcrumbs($container, array $item = [])
    {
        $this->setBreadcrumbsContainer($container);

        breadcrumbs()->register('main', function(Builder $bc) use ($item) {
            if (empty($item)) {
                $item = $this->getBreadcrumbsHomeItem();
            }

            $bc->push($item['title'], $item['url'], $item['data'] ?? []);
        });
    }
222589
public function ul($list, $attr = null)
    {
        $out = $this->tag('ul', $attr);

        if (! empty($list) && is_array($list)) {
            $out .= $this->processList($list);
        }

        $out .= $this->tag('/ul');

        return $out;
    }
1023029
protected function ul(array $items, string $marker = '<yellow>*</yellow>', int $writer = Output::STANDARD): void
	{
		(new UnorderedList($this->output))->draw($items, $marker, $writer);
	}
696408
public static function pagination(array $items, $htmlOptions = array())
    {
        if (!empty($items)) {
            self::addCssClass('pagination', $htmlOptions);
            $size = TbArray::popValue('size', $htmlOptions);
            if (!empty($size)) {
                self::addCssClass('pagination-' . $size, $htmlOptions);
            }
            $align = TbArray::popValue('align', $htmlOptions);
            if (!empty($align)) {
                self::addCssClass('pagination-' . $align, $htmlOptions);
            }
            $listOptions = TbArray::popValue('listOptions', $htmlOptions, array());
            $output = self::openTag('div', $htmlOptions);
            $output .= self::openTag('ul', $listOptions);
            foreach ($items as $itemOptions) {
                // todo: consider removing the support for htmlOptions.
                $options = TbArray::popValue('htmlOptions', $itemOptions, array());
                if (!empty($options)) {
                    $itemOptions = TbArray::merge($options, $itemOptions);
                }
                $label = TbArray::popValue('label', $itemOptions, '');
                $url = TbArray::popValue('url', $itemOptions, false);
                $output .= self::paginationLink($label, $url, $itemOptions);
            }
            $output .= '</ul></div>';
            return $output;
        }
        return '';
    }
72303
public function nestedList(array $list, array $options = [], array $itemOptions = [])
    {
        $options = optionsParser($options);
        $itemOptions = optionsParser($itemOptions);

        if ($options->exists('icon')) {
            $itemOptions->add('icon', $options->get('icon'));
        }

        if ($itemOptions->exists('icon')) {
            $options->append('class', 'fa-ul');
            $itemOptions->append('icon', 'li');

            $list = array_map(function ($element) use ($itemOptions) {
                return collection($this->addIconToText($element, clone $itemOptions))->first();
            }, $list);
        }

        $options->delete('icon', 'icon-align');
        $itemOptions->delete('icon', 'icon-align');

        return parent::nestedList($list, $options->toArray(), $itemOptions->toArray());
    }
838679
public function addSelectList(string $name, array $items = []): void
    {
        $options = $this->formOptions['fields'][$name];
        $wrapper = $this->createWrapper();
        $label = '';

        if (isset($options['label']['text'])) {
            $label = $this->createLabel($options['label']['text'], $name);

            unset($options['label']['text']);
        }

        $list = HtmlTag::createElement('select')
            ->set('id', $name)
            ->set('name', $name);

        $caption = '';

        if (isset($options['caption']['text'])) {
            $list->set('aria-describedby', $name . '_caption');
            $caption = $this->createCaption($options['caption']['text'], $name);

            unset($options['caption']['text']);
        }

        foreach ($items as $value => $text) {
            $item[] = $list->addElement('option')
                ->set('value', $value)
                ->text($text);
        }

        $elements = [
            'wrapper' => $wrapper,
            'label'   => $label,
            'list'    => $list,
            'item'    => $item ?? [],
            'caption' => $caption
        ];

        foreach ($elements as $name => $element) {
            $$name = $this->assignOptionsToElement($options, $name, $element);
        }

        $wrapper->addElement($label);
        $wrapper->addElement($list);
        $wrapper->addElement($caption);

        echo $wrapper;
    }