<?php
/**
 * Zend Framework (http://framework.zend.com/)
 *
 * @link      http://github.com/zendframework/zf2 for the canonical source repository
 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

namespace Zend\View\Helper\Navigation;

use 
Zend\Navigation\AbstractContainer;
use 
Zend\Navigation\Page\AbstractPage;
use 
Zend\View;
use 
Zend\View\Exception;

/**
 * Helper for printing breadcrumbs
 */
class Breadcrumbs extends AbstractHelper
{
    
/**
     * Breadcrumbs separator string
     *
     * @var string
     */
    
protected $separator ' &gt; ';

    
/**
     * The minimum depth a page must have to be included when rendering
     *
     * @var int
     */
    
protected $minDepth 1;

    
/**
     * Whether last page in breadcrumb should be hyperlinked
     *
     * @var bool
     */
    
protected $linkLast false;

    
/**
     * Partial view script to use for rendering menu
     *
     * @var string|array
     */
    
protected $partial;

    
/**
     * Helper entry point
     *
     * @param  string|AbstractContainer $container container to operate on
     * @return Breadcrumbs
     */
    
public function __invoke($container null)
    {
        if (
null !== $container) {
            
$this->setContainer($container);
        }

        return 
$this;
    }

    
/**
     * Sets breadcrumb separator
     *
     * @param  string $separator separator string
     * @return Breadcrumbs fluent interface, returns self
     */
    
public function setSeparator($separator)
    {
        if (
is_string($separator)) {
            
$this->separator $separator;
        }

        return 
$this;
    }

    
/**
     * Returns breadcrumb separator
     *
     * @return string  breadcrumb separator
     */
    
public function getSeparator()
    {
        return 
$this->separator;
    }

    
/**
     * Sets whether last page in breadcrumbs should be hyperlinked
     *
     * @param  bool $linkLast whether last page should be hyperlinked
     * @return Breadcrumbs fluent interface, returns self
     */
    
public function setLinkLast($linkLast)
    {
        
$this->linkLast = (bool) $linkLast;
        return 
$this;
    }

    
/**
     * Returns whether last page in breadcrumbs should be hyperlinked
     *
     * @return bool  whether last page in breadcrumbs should be hyperlinked
     */
    
public function getLinkLast()
    {
        return 
$this->linkLast;
    }

    
/**
     * Sets which partial view script to use for rendering menu
     *
     * @param  string|array $partial partial view script or null. If an array is
     *                               given, it is expected to contain two
     *                               values; the partial view script to use,
     *                               and the module where the script can be
     *                               found.
     * @return Breadcrumbs fluent interface, returns self
     */
    
public function setPartial($partial)
    {
        if (
null === $partial || is_string($partial) || is_array($partial)) {
            
$this->partial $partial;
        }

        return 
$this;
    }

    
/**
     * Returns partial view script to use for rendering menu
     *
     * @return string|array|null
     */
    
public function getPartial()
    {
        return 
$this->partial;
    }

    
// Render methods:

    /**
     * Renders breadcrumbs by chaining 'a' elements with the separator
     * registered in the helper
     *
     * @param  AbstractContainer $container [optional] container to render. Default is
     *                              to render the container registered in the helper.
     * @return string               helper output
     */
    
public function renderStraight($container null)
    {
        
$this->parseContainer($container);
        if (
null === $container) {
            
$container $this->getContainer();
        }

        
// find deepest active
        
if (!$active $this->findActive($container)) {
            return 
'';
        }

        
$active $active['page'];

        
// put the deepest active page last in breadcrumbs
        
if ($this->getLinkLast()) {
            
$html $this->htmlify($active);
        } else {
            
$html $active->getLabel();
            if (
null !== ($translator $this->getTranslator())) {
                
$html $translator->translate($html$this->getTranslatorTextDomain());
            }
            
$escaper $this->view->plugin('escapeHtml');
            
$html    $escaper($html);
        }

        
// walk back to root
        
while ($parent $active->getParent()) {
            if (
$parent instanceof AbstractPage) {
                
// prepend crumb to html
                
$html $this->htmlify($parent)
                      . 
$this->getSeparator()
                      . 
$html;
            }

            if (
$parent === $container) {
                
// at the root of the given container
                
break;
            }

            
$active $parent;
        }

        return 
strlen($html) ? $this->getIndent() . $html '';
    }

    
/**
     * Renders the given $container by invoking the partial view helper
     *
     * The container will simply be passed on as a model to the view script,
     * so in the script it will be available in <code>$this->container</code>.
     *
     * @param  AbstractContainer $container [optional] container to pass to view script.
     *                              Default is to use the container registered
     *                              in the helper.
     * @param  string|array $partial [optional] partial view script to use.
     *                               Default is to use the partial registered
     *                               in the helper.  If an array is given, it
     *                               is expected to contain two values; the
     *                               partial view script to use, and the module
     *                               where the script can be found.
     * @return string               helper output
     * @throws Exception\RuntimeException if no partial provided
     * @throws Exception\InvalidArgumentException if partial is invalid array
     */
    
public function renderPartial($container null$partial null)
    {
        
$this->parseContainer($container);
        if (
null === $container) {
            
$container $this->getContainer();
        }

        if (
null === $partial) {
            
$partial $this->getPartial();
        }

        if (empty(
$partial)) {
            throw new 
Exception\RuntimeException(
                
'Unable to render menu: No partial view script provided'
            
);
        }

        
// put breadcrumb pages in model
        
$model  = array('pages' => array());
        
$active $this->findActive($container);
        if (
$active) {
            
$active $active['page'];
            
$model['pages'][] = $active;
            while (
$parent $active->getParent()) {
                if (
$parent instanceof AbstractPage) {
                    
$model['pages'][] = $parent;
                } else {
                    break;
                }

                if (
$parent === $container) {
                    
// break if at the root of the given container
                    
break;
                }

                
$active $parent;
            }
            
$model['pages'] = array_reverse($model['pages']);
        }

        if (
is_array($partial)) {
            if (
count($partial) != 2) {
                throw new 
Exception\InvalidArgumentException(
                    
'Unable to render menu: A view partial supplied as '
                    
.  'an array must contain two values: partial view '
                    
.  'script and module where script can be found'
                
);
            }

            
$partialHelper $this->view->plugin('partial');
            return 
$partialHelper($partial[0], /*$partial[1], */$model);
        }

        
$partialHelper $this->view->plugin('partial');
        return 
$partialHelper($partial$model);
    }

    
// Zend\View\Helper\Navigation\Helper:

    /**
     * Renders helper
     *
     * Implements {@link HelperInterface::render()}.
     *
     * @param  AbstractContainer $container [optional] container to render. Default is
     *                              to render the container registered in the helper.
     * @return string               helper output
     */
    
public function render($container null)
    {
        
$partial $this->getPartial();
        if (
$partial) {
            return 
$this->renderPartial($container$partial);
        }

        return 
$this->renderStraight($container);
    }
}