Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
100 / 100
TwbBundleDropDown
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
5 / 5
58
100.00% covered (success)
100.00%
100 / 100
 __invoke(array $aDropdownOptions = null)
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 render(array $aDropdownOptions)
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
10 / 10
 renderToggle(array $aDropdownOptions)
100.00% covered (success)
100.00%
1 / 1
13
100.00% covered (success)
100.00%
21 / 21
 renderListItems(array $aDropdownOptions)
100.00% covered (success)
100.00%
1 / 1
17
100.00% covered (success)
100.00%
27 / 27
 renderItem($aItemOptions)
100.00% covered (success)
100.00%
1 / 1
21
100.00% covered (success)
100.00%
41 / 41
<?php
namespace TwbBundle\View\Helper;
class TwbBundleDropDown extends \Zend\Form\View\Helper\AbstractHelper{
    const TYPE_ITEM_HEADER = 'header';
    const TYPE_ITEM_DIVIDER = '---';
    const TYPE_ITEM_LINK = 'link';
    /**
     * @var string
     */
    private static $dropdownContainerFormat = '<div %s>%s</div>';
    /**
     * @var string
     */
    private static $dropdownToggleFormat = '<a %s>%s <b class="caret"></b></a>';
    /**
     * @var string
     */
    private static $dropdownListFormat = '<ul %s>%s</ul>';
    /**
     * @var string
     */
    private static $dropdownItemContainerFormat = '<li %s>%s</li>';
    /**
     * @var string
     */
    private static $dropdownItemFormats = array(
        self::TYPE_ITEM_LINK => '<a %s>%s</a>'
    );
    /**
     * @param array $aDropdownOptions
     * @return \TwbBundle\View\Helper\TwbBundleDropDown|string
     */
    public function __invoke(array $aDropdownOptions = null){
        return $aDropdownOptions?$this->render($aDropdownOptions):$this;
    }
    /**
     * Render dropdown markup
     * @param array $aDropdownOptions
     * @throws \LogicException
     * @return string
     */
    public function render(array $aDropdownOptions){
        // ### Dropdown container attributes ###
        if(empty($aDropdownOptions['attributes']))$aDropdownOptions['attributes'] = array('class' => 'dropdown');
        else{
            if(!is_array($aDropdownOptions['attributes']))throw new \LogicException('"attributes" option expects an array, "'.gettype($aDropdownOptions['attributes']).'" given');
            if(empty($aDropdownOptions['attributes']['class']))$aDropdownOptions['attributes']['class'] = 'dropdown';
            elseif(!preg_match('/(\s|^)dropdown(\s|$)/',$aDropdownOptions['attributes']['class']))$aDropdownOptions['attributes']['class'] .= ' dropdown';
        }
        // ### Render dropdown ###
        return sprintf(
            self::$dropdownContainerFormat,
            //Container attributes
            $this->createAttributesString($aDropdownOptions['attributes']),
            //Toggle
            $this->renderToggle($aDropdownOptions).
            //List items
            $this->renderListItems($aDropdownOptions)
        );
    }
    /**
     * Render dropdown toggle markup
     * @param array $aDropdownOptions
     * @throws \LogicException
     * @return string
     */
    public function renderToggle(array $aDropdownOptions){
        // ### Dropdown toggle ###
        if(empty($aDropdownOptions['label']))$aDropdownOptions['label'] = '';
        elseif(!is_scalar($aDropdownOptions['label']))throw new \InvalidArgumentException('"label" option expects a scalar value, "'.gettype($aDropdownOptions['label']).'" given');
        elseif(($oTranslator = $this->getTranslator()))$aDropdownOptions['label'] = $oTranslator->translate($aDropdownOptions['label'],$this->getTranslatorTextDomain());
        //Dropdown toggle attributes
        //Class
        if(empty($aDropdownOptions['toggle_attributes']))$aDropdownOptions['toggle_attributes'] = array('class' => 'sr-only dropdown-toggle');
        else{
            if(!is_array($aDropdownOptions['toggle_attributes']))throw new \InvalidArgumentException('"toggle_attributes" option expects an array, "'.gettype($aDropdownOptions['toggle_attributes']).'" given');
            if(empty($aDropdownOptions['toggle_attributes']['class']))$aDropdownOptions['toggle_attributes']['class'] = 'sr-only dropdown-toggle';
            else{
                if(!preg_match('/(\s|^)sr-only(\s|$)/',$aDropdownOptions['toggle_attributes']['class']))$aDropdownOptions['toggle_attributes']['class'] .= ' sr-only';
                if(!preg_match('/(\s|^)dropdown-toggle(\s|$)/',$aDropdownOptions['toggle_attributes']['class']))$aDropdownOptions['toggle_attributes']['class'] .= ' dropdown-toggle';
            }
        }
        //data-toggle
        if(empty($aDropdownOptions['toggle_attributes']['data-toggle']))$aDropdownOptions['toggle_attributes']['data-toggle'] = 'dropdown';
        //Role
        if(empty($aDropdownOptions['toggle_attributes']['role']))$aDropdownOptions['toggle_attributes']['role'] = 'button';
        //Href
        if(empty($aDropdownOptions['toggle_attributes']['href']))$aDropdownOptions['toggle_attributes']['href'] = '#';
        //Id
        if(!empty($aDropdownOptions['name']))$aDropdownOptions['toggle_attributes']['id'] = $aDropdownOptions['name'];
        $aValidTagAttributes = $this->validTagAttributes;
        $this->validTagAttributes = array('href' => true);
        $sAttributeString = $this->createAttributesString($aDropdownOptions['toggle_attributes']);
        $this->validTagAttributes = $aValidTagAttributes;
        return sprintf(
            self::$dropdownToggleFormat,
            //Toggle attributes
            $sAttributeString,
            //Toggle label
            $this->getEscapeHtmlHelper()->__invoke($aDropdownOptions['label'])
        );
    }
    /**
     * Render dropdown list items markup
     * @param array $aDropdownOptions
     * @throws \LogicException
     * @return string
     */
    public function renderListItems(array $aDropdownOptions){
        if(!isset($aDropdownOptions['items']))throw new \LogicException(__METHOD__.' expects "items" option');
        if(!is_array($aDropdownOptions['items']))throw new \LogicException('"items" option expects an array, "'.gettype($aDropdownOptions['items']).'" given');
        // ### Dropdown list attributes ###
        //Class
        if(empty($aDropdownOptions['list_attributes']))$aDropdownOptions['list_attributes'] = array('class' => 'dropdown-menu');
        else{
            if(!is_array($aDropdownOptions['list_attributes']))throw new \LogicException('"list_attributes" option expects an array, "'.gettype($aDropdownOptions['list_attributes']).'" given');
            if(empty($aDropdownOptions['list_attributes']['class']))$aDropdownOptions['list_attributes']['class'] = 'dropdown-menu';
            elseif(!preg_match('/(\s|^)dropdown-menu(\s|$)/',$aDropdownOptions['list_attributes']['class']))$aDropdownOptions['list_attributes']['class'] .= ' dropdown-menu';
        }
        //Role
        if(empty($aDropdownOptions['list_attributes']['role']))$aDropdownOptions['list_attributes']['role'] = 'menu';
        //Id
        if(!empty($aDropdownOptions['name']))$aDropdownOptions['list_attributes']['aria-labelledby'] = $aDropdownOptions['name'];
        // ### Items ###
        $sItems = '';
        foreach($aDropdownOptions['items'] as $sKey => $aItemOptions){
            if(!is_array($aItemOptions)){
                if(!is_scalar($aItemOptions))throw new \LogicException('item option expects an array or a scalar value, "'.gettype($aItemOptions).'" given');
                $aItemOptions = $aItemOptions === self::TYPE_ITEM_DIVIDER
                //Divider
                ?array('type' => self::TYPE_ITEM_DIVIDER)
                //Link
                :array(
                    'label' => $aItemOptions,
                    'type' => self::TYPE_ITEM_LINK,
                    'item_attributes' => array('href' => is_string($sKey)?$sKey:null)
                );
            }
            else{
                if(!isset($aItemOptions['label']))$aItemOptions['label'] = is_string($sKey)?$sKey:null;
                if(!isset($aItemOptions['type']))$aItemOptions['type'] = self::TYPE_ITEM_LINK;
            }
            $sItems .= $this->renderItem($aItemOptions).PHP_EOL;
        }
        return sprintf(
            self::$dropdownListFormat,
            //List attributes
            $this->createAttributesString($aDropdownOptions['list_attributes']),
            //Items
            $sItems
        );
    }
    /**
     * Render dropdown list item markup
     * @param array $aItemOptions
     * @throws \LogicException
     * @return string
     */
    protected function renderItem($aItemOptions){
        if(empty($aItemOptions['type']))throw new \LogicException(__METHOD__.' expects "type" option');
        //Item container attributes
        if(empty($aItemOptions['attributes']))$aItemOptions['attributes'] = array();
        elseif(!is_array($aItemOptions['attributes']))throw new \LogicException('"attributes" option expects an array, "'.gettype($aItemOptions['attributes']).'" given');
        //Role
        if(empty($aItemOptions['attributes']['role']))$aItemOptions['attributes']['role'] = 'presentation';
        $sItemContent = '';
        switch($aItemOptions['type']){
            case self::TYPE_ITEM_HEADER:
                //Define item container "header" class
                if(empty($aItemOptions['attributes']['class']))$aItemOptions['attributes']['class'] = 'dropdown-header';
                elseif(!preg_match('/(\s|^)dropdown-header(\s|$)/',$aItemOptions['attributes']['class']))$aItemOptions['attributes']['class'] .= ' dropdown-header';
                //Header label
                if(empty($aItemOptions['label']))throw new \LogicException('"'.$aItemOptions['type'].'" item expects "label" option');
                if(!is_scalar($aItemOptions['label']))throw new \LogicException('"label" option expect scalar value, "'.gettype($aItemOptions['label']).'" given');
                elseif(($oTranslator = $this->getTranslator()))$aItemOptions['label'] = $oTranslator->translate($aItemOptions['label'],$this->getTranslatorTextDomain());
                $sItemContent = $this->getEscapeHtmlHelper()->__invoke($aItemOptions['label']);
                break;
            case self::TYPE_ITEM_DIVIDER:
                //Define item container "divider" class
                if(empty($aItemOptions['attributes']['class']))$aItemOptions['attributes']['class'] = 'divider';
                elseif(!preg_match('/(\s|^)divider(\s|$)/',$aItemOptions['attributes']['class']))$aItemOptions['attributes']['class'] .= ' divider';
                $sItemContent = '';
                break;
            case self::TYPE_ITEM_LINK:
                if(empty($aItemOptions['label']))throw new \LogicException('"'.$aItemOptions['type'].'" item expects "label" option');
                if(!is_scalar($aItemOptions['label']))throw new \LogicException('"label" option expect scalar value, "'.gettype($aItemOptions['label']).'" given');
                elseif(($oTranslator = $this->getTranslator()))$aItemOptions['label'] = $oTranslator->translate($aItemOptions['label'],$this->getTranslatorTextDomain());
                //Item attributes
                //Role
                if(empty($aItemOptions['item_attributes']['role']))$aItemOptions['item_attributes']['role'] = 'menuitem';
                //Tab index
                if(!isset($aItemOptions['item_attributes']['tabindex']))$aItemOptions['item_attributes']['tabindex'] = '-1';
                //Href
                if(!isset($aItemOptions['item_attributes']['href']))$aItemOptions['item_attributes']['href'] = '#';
                $aValidTagAttributes = $this->validTagAttributes;
                $this->validTagAttributes = array('href' => true);
                $sAttributeString = $this->createAttributesString($aItemOptions['item_attributes']);
                $this->validTagAttributes = $aValidTagAttributes;
                $sItemContent = sprintf(
                    self::$dropdownItemFormats[self::TYPE_ITEM_LINK],
                    $sAttributeString,
                    $this->getEscapeHtmlHelper()->__invoke($aItemOptions['label'])
                );
                break;
        }
        return sprintf(
            self::$dropdownItemContainerFormat,
            $this->createAttributesString($aItemOptions['attributes']),
            $sItemContent
        );
    }
}