Skip to content
Snippets Groups Projects
Select Git revision
  • 4604e92a0102383b8b87152852ba3fed74cf9600
  • master default protected
  • update-renderer-class
3 results

render-adminlte.class.php

Blame
  • render-adminlte.class.php 45.29 KiB
    <?php
    require_once 'htmlelements.class.php';
    /**
     * ======================================================================
     * 
     * RENDERER FOR ADNINLTE template https://adminlte.io
     * DOCS: https://adminlte.io/docs/3.2/
     *       https://adminlte.io/themes/v3/index3.html
     * 
     * ======================================================================
     *
     * @author Axel
     */
    class renderadminlte {
    
        var $aPresets=[
    
            'bgcolor'=>[
                'description'=>'background colors',
                'group'=>'styling',
                'values'=>[
                    // https://adminlte.io/themes/v3/pages/UI/general.html
                    ''=>'no value',
                    'indigo'=>'indigo',
                    'lightblue'=>'',
                    'navy'=>'',
                    'purple'=>'',
                    'fuchsia'=>'',
                    'pink'=>'',
                    'maroon'=>'',
                    'orange'=>'',
                    'lime'=>'',
                    'teal'=>'',
                    'olive'=>'',
            
                    'black'=>'black',
                    'dark'=>'dark gray',
                    'gray'=>'gray', 
                    'light'=>'light gray', 
                ]
            ],
        
            'type'=>[
                'description'=>'type or status like info/ warning/ danger to define a color',
                'group'=>'styling',
                'values'=>[
                    ''=>'no value',
                    'danger'=>'red',
                    'info'=>'aqua',
                    'primary'=>'blue',
                    'secondary'=>'gray',
                    'success'=>'green',
                    'warning'=>'yellow',
                    'dark'=>'dark gray',
                    'gray'=>'gray', 
                ]
            ],
            'shadow'=>[
                'description'=>'use a shadow',
                'group'=>'styling',
                'values'=>[
                    ''=>'no value', 
                    'none'=>'none',
                    'small'=>'small', 
                    'regular'=>'regular',
                    'large'=>'large'
                ]
            ],
            'size'=>[
                'description'=>'set a size',
                'group'=>'styling',
                'values'=>[
                    ''=>'no value',
                    'lg'=>'',
                    'sm'=>'',
                    'xs'=>'',
                    'flat'=>'',
                ]
            ],
            'variant'=>[
                'description'=>'coloring style',
                'group'=>'styling',
                'values'=>[
                    ''=>'no value',
                    'outline'=>'small stripe on top',
                    'solid'=>'full filled widget',
                    'gradient'=>'full filled with gradient',
                ]
            ],
            'visibility'=>[
                'description'=>'',
                'group'=>'customizing',
                'values'=>[
                    ''=>'no value', 
                    '0'=>'hide', 
                    '1'=>'show',
                ]
            ],
            // for keys: state
            'windowstate'=>[
                'description'=>'state of a resizable widget',
                'group'=>'customizing',
                'values'=>[
                    ''=>'no value', 
                    'collapsed'=>'header only', 
                    'maximized'=>'full window',
                ]
            ],
            // for keys: dismissable
            'yesno'=>[
                'description'=>'',
                'group'=>'customizing',
                'values'=>[
                    ''=>'no value', 
                    '0'=>'no', 
                    '1'=>'yes',
                ]
            ],
        ];
    
        var $_aValueMappings=[
            'shadow'=>[
                'default'  => '',
                'none'     => 'shadow-none',
                'small'    => 'shadow-small',
                'regular'  => 'shadow',
                'large'    => 'shadow-lg',
            ]
        ];
    
        var $_aElements=[];
        
        /**
         * instance of htmlelements
         * @var object
         */
        var $_oHtml=false;
        
        
        // ----------------------------------------------------------------------
        // 
        // CONSTRUCTOR
        // 
        // ----------------------------------------------------------------------
        public function __construct() {
            $this->_oHtml=new htmlelements();
            $this->_initElements();
            return true;
        }
    
        // ----------------------------------------------------------------------
        // 
        // PRIVATE FUNCTIONS 
        // 
        // ----------------------------------------------------------------------
        
        /**
         * verify if an item has a correct value
         * it returns false if a key is not defined to be checked
         * it returns true if it was validated successfully
         * it dies with an errror, if a value check failed
         * 
         * @param string  $sType      type; key in $_aValidItems; one of bgcolor|color|type|size
         * @param string  $sValue     value to check
         * @param string  $sReferrer  optional: method that called this function
         */
        protected function _DELETE_ME___checkValue($sType, $sValue, $sReferrer=false){
            if (!$sValue || !array_key_exists($sType, $this->_aValidItems)){
                return false;
            }
            if(array_search($sValue, $this->_aValidItems[$sType])===false){
                echo "ERROR: ".($sReferrer ? $sReferrer.' - ' : '')."value [$sValue] is not a valid for type [$sType]; it must be one of ".implode("|", $this->_aValidItems[$sType]).'<br>';
            }
            return true;
        }
    
        /**
         * used in cosntructor
         * initialize all element definitions
         */
        protected function _initElements(){
            $this->_aElements=[
    
                // ------------------------------------------------------------
                'alert'=>[
                    'label'=>'Alert',
                    'description'=>'Colored box with title and a text',
                    'method'=>'getAlert',
            
                    'params'=>[
                        'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'warning'],
                        'dismissible' => ['select'=>$this->aPresets['yesno'],    'example_value'=>''],
                        'title'       => [
                            'description'=>'Title in a bit bigger font', 
                            'group'=>'content',
                            'example_value'=>'Alert title'
                        ],
                        'text'        => [
                            'description'=>'Message text', 
                            'group'=>'content',
                            'example_value'=>'I am a message. Read me, please.'
                        ],
                    ]
                ],
                // ------------------------------------------------------------
                'badge'=>[
                    'label'=>'Badge',
                    'description'=>'Tiny additional info; mostly as counter',
                    'method'=>'getBadge',
            
                    'params'=>[
                        'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'danger'],
                        'bgcolor'     => ['select'=>$this->aPresets['bgcolor'],  'example_value'=>''],
                        'class'       => [
                            'group'=>'styling', 
                            'description'=>'optional: css classes', 
                            'example_value'=>''
                        ],
                        'id'          => [
                            'group'=>'customizing', 
                            'description'=>'optional: id attribute', 
                            'example_value'=>''
                        ],
                        'title'       => [
                            'group'=>'content', 
                            'description'=>'optional: title attribute for mouseover', 
                            'example_value'=>'Errors: 5'
                        ],
                        'text'        => [
                            'group'=>'content', 
                            'description'=>'Text or value in the badge', 
                            'example_value'=>'5'
                        ],
                    ]
                ],
                // ------------------------------------------------------------
                'button'=>[
                    'label'=>'Button',
                    'description'=>'Buttons<br>In this component you can add other parmeter keys too - these will be added as attributes in the button tag.',
                    'method'=>'getButton',
            
                    'params'=>[
                        'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'primary'],
                        'size'        => ['select'=>$this->aPresets['size'],     'example_value'=>''],
                        'class'       => [
                            'group'=>'styling', 
                            'description'=>'optional: css classes', 
                            'example_value'=>''
                        ],
                        'text'        => [
                            'group'=>'content', 
                            'description'=>'Text/ html code on the button', 
                            'example_value'=>'Click me'
                        ],
                    ]
                ],
                // ------------------------------------------------------------
                'callout'=>[
                    'label'=>'Callout',
                    'description'=>'Kind of infobox',
                    'method'=>'getCallout',
            
                    'params'=>[
                        'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'danger'],
                        'class'       => [
                            'group'=>'styling', 
                            'description'=>'optional: css classes', 
                            'example_value'=>''
                        ],
                        'title'       => [
                            'group'=>'content', 
                            'description'=>'Title in a bit bigger font', 
                            'example_value'=>'I am a callout'
                        ],
                        'text'        => [
                            'group'=>'content',
                            'description'=>'Message text', 
                            'example_value'=>'Here is some description to whatever.'
                        ],
                    ]
                ],
                // ------------------------------------------------------------
                'card'=>[
                    'label'=>'Card',
                    'description'=>'Content box with header, text, footer',
                    'method'=>'getCard',
            
                    'params'=>[
                        'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'primary'],
                        'variant'     => ['select'=>$this->aPresets['variant'],  'example_value'=>'outline'],
                        'class'       => [
                            'group'=>'styling', 
                            'description'=>'optional: css classes', 
                            'example_value'=>''
                        ],
                        'state'       => [
                            'group'=>'customizing', 
                            'select'=>$this->aPresets['windowstate'], 
                            'example_value'=>''
                        ],
            
                        'tb-collapse' => ['description'=>'show minus symbol as collapse button', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
                        'tb-expand'   => ['description'=>'show plus symbol to expand card', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
                        'tb-maximize' => ['description'=>'show maximize button for fullscreen', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
                        'tb-minimize' => ['description'=>'show minimize button to minimize', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
                        'tb-remove'   => ['description'=>'show cross symbol to remove card', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
            
                        'title'       => [
                            'group'=>'content', 
                            'description'=>'Title in the top row', 
                            'example_value'=>'I am a card'
                        ],
                        'tools'       => [
                            'group'=>'content', 
                            'description'=>'Html code for the top right', 
                            'example_value'=>''
                        ],
                        'text'        => [
                            'group'=>'content', 
                            'description'=>'Main content', 
                            'example_value'=>'Here is some beautiful content.'
                        ],
                        'footer'      => [
                            'group'=>'content', 
                            'description'=>'optional: footer content', 
                            'example_value'=>'Footer'
                        ],
                    ]
                ],
                // ------------------------------------------------------------
                'infobox'=>[
                    'label'=>'Info box',
                    'description'=>'Box with icon to highlight a single value; optional with a progress bar',
                    'method'=>'getInfobox',
            
                    'params'=>[
                        'type'=>['select'=>$this->aPresets['type'],     'example_value'=>''],
                        'iconbg'=>['select'=>$this->aPresets['type'],   'example_value'=>'info'],
                        'shadow'=>['select'=>$this->aPresets['shadow'], 'example_value'=>''],
                        'icon'=>[
                            'group'=>'content', 
                            'description'=>'css class for an icon', 
                            'example_value'=>'far fa-thumbs-up'
                        ],
                        'text'=>[
                            'group'=>'content', 
                            'description'=>'short information text', 
                            'example_value'=>'Likes'
                        ],
                        'number'=>[
                            'group'=>'content', 
                            'description'=>'a number to highlight', 
                            'example_value'=>"41,410"
                        ],
                        'progressvalue'=>[
                            'group'=>'content', 
                            'description'=>'optional: progress value 0..100 to draw a progress bar', 
                            'example_value'=>70
                        ],
                        'progresstext'=>[
                            'group'=>'content', 
                            'description'=>'optional: text below progress bar', 
                            'example_value'=>'70% Increase in 30 Days'
                        ]
                    ]
                ],
                // ------------------------------------------------------------
                'smallbox'=>[
                    'label'=>'Small box',
                    'description'=>'Solid colored box to highlight a single value; optional with a link',
                    'method'=>'getSmallbox',
            
                    'params'=>[
                        'type'=>['select'=>$this->aPresets['type'],     'example_value'=>'info'],
                        'shadow'=>['select'=>$this->aPresets['shadow'], 'example_value'=>''],
            
                        'icon'=>['group'=>'content', 'description'=>'css class for an icon', 'example_value'=>'fas fa-shopping-cart'],
            
                        'text'=>['group'=>'content', 'description'=>'short information text', 'example_value'=>'New orders'],
                        'number'=>['group'=>'content', 'description'=>'a number to highlight', 'example_value'=>"150"],
                        'url'=>['group'=>'content', 'description'=>'optional: url to set a link on the bottom', 'example_value'=>'#'],
                        'linktext'=>['group'=>'content', 'optional: description'=>'linktext', 'example_value'=>'More info']
                    ]
                ],
            ];
        }
        /**
         * helper function: a shortcut for $this->_oHtml->getTag
         * @param  string  $sTag         name of html tag
         * @param  array   $aAttributes  array of its attributes
         */
        protected function _tag($sTag, $aAttributes, $sContent=false){
            if ($sContent){
                $aAttributes['label']=(isset($aAttributes['label']) ? $aAttributes['label'] : '') . $sContent;
            }
            return $this->_oHtml->getTag($sTag, $aAttributes);
        }
        // ----------------------------------------------------------------------
        // 
        // PUBLIC FUNCTIONS AdminLTE 3.2
        // 
        // ----------------------------------------------------------------------
    
        /**
         * render a page by using template 
         * @param  string  $stemplate  html template with placeholders
         * @param  array   $aReplace   key = what to replace .. value = new value
         */
        public function render($sTemplate, $aReplace){
            return str_replace(
                array_keys($aReplace),
                array_values($aReplace),
                $sTemplate
            );
        }
        
    
        /**
         * add a wrapper: wrap some content into a tag
         * 
         * @param  string  $sContent  html content inside
         * @return string
         */
        public function addWrapper($sTag, $aOptions, $sContent){
            $aOptions['label']=$sContent;
            return $this->_tag($sTag, $aOptions)."\n";
        }
    
        // ----------------------------------------------------------------------
        // 
        // PUBLIC FUNCTIONS :: NAVIGATION
        // 
        // ----------------------------------------------------------------------
    
    
        /** 
         * get a single navigation item on top bar
         * 
         * @param  array    $aLink   Array of html attributes; key children can contain subitems
         * @param  integer  $iLevel  Menu level; 1=top bar; 2=pupup menu with subitems
         * @return string
         */
        public function getNavItem($aLink, $iLevel=1){
            static $iCounter;
            if(!isset($iCounter)){
                $iCounter=0;
            }
            
            // special menu entry: horizontal bar (label is "-")
            if($aLink['label']=='-'){
                return '<div class="dropdown-divider"></div>';
            }
    
            $aChildren=isset($aLink['children']) && is_array($aLink['children']) && count($aLink['children']) ? $aLink['children'] : false;
    
            $aLink['class']='nav-link'
                . (isset($aLink['class']) ? ' '.$aLink['class'] : '')
                . ($aChildren ? ' dropdown-toggle' : '')
                ;
            if($aChildren){
                $iCounter++;
                $sNavId="navbarDropdown".$iCounter;
                $aLink=array_merge($aLink,[
                    'id'=>$sNavId,
                    'role'=>"button",
                    'data-toggle'=>"dropdown",
                    'aria-haspopup'=>"true",
                    'aria-expanded'=>"false",
                ]);
                unset($aLink['children']); // remove from html attributes to draw
            }
            $sReturn=$this->_tag('a', $aLink)."\n";
            if($aChildren){ 
                $iLevel++;
                $sReturn.=$this->addWrapper(
                        'div', 
                        [ 
                            'aria-labelledby'=> $sNavId,
                            'class'=>'dropdown-menu'
                        ], 
                        $this->getNavItems($aChildren, $iLevel)
                    )."\n";
                $iLevel--;
            }
       
            if($iLevel==1){
                $sLiClass='nav-item'.($aChildren ? ' dropdown' : '');
                $sReturn=$this->addWrapper(
                    'li', 
                    ['class'=>$sLiClass],
                    $sReturn 
                );
            }
            return $sReturn;
        }
        /** 
         * get a navigation item for top bar
         * 
         */
        public function getNavItems($aLinks, $iLevel=1){
            $sReturn='';
            if (!$aLinks || !is_array($aLinks) || !count($aLinks)){
                return false;
            }
            foreach($aLinks as $aLink){
                $sReturn.=$this->getNavItem($aLink, $iLevel);
            }
            return $sReturn;
        }
        /**
         * get a top left navigation for a top navigation bar
         * 
         * @example
         * <code>
         * $aTopnav=[
         *     ['href'=>'/index.php', 'label'=>'MyHome' , 'class'=>'bg-gray'],
         *     ['href'=>'#',          'label'=>'Contact'],
         *     ['href'=>'#',          'label'=>'Help',
         *         'children'=>[
         *             ['href'=>'#',      'label'=>'FAQ'],
         *             ['label'=>'-'],
         *             ['href'=>'#',      'label'=>'Support'],
         *         ]
         *     ]
         * ];
         *  echo '<nav class="main-header navbar navbar-expand navbar-white navbar-light">'
         *     .$renderAdminLTE->getTopNavigation($aTopnav)
         *     .'</nav>'
         * </code>
         * 
         * @param  array  $aNavitems   array of navigation items/ tree
         * @param  array  $aUlOptions  array of html attrubutes for wrapping UL tag
         * @return string
         */
        public function getTopNavigation($aNavItems, $aUlOptions=['class'=>'navbar-nav']){
            array_unshift($aNavItems, ['class'=>'nav-link', 'data-widget'=>'pushmenu', 'href'=>'#', 'role'=>'button', 'label'=>'<i class="fas fa-bars"></i>']);
            return $this->addWrapper('ul', $aUlOptions, $this->getNavItems($aNavItems));
        }
    
        // ----------------------------------------------------------------------
    
    
        /** 
         * get a navigation items for sidebar
         * @param  array  $aLinks  list of link items
         */
        public function getNavi2Items($aLinks){
            $sReturn='';
            if (!$aLinks || !is_array($aLinks) || !count($aLinks)){
                return false;
            }
            foreach($aLinks as $aLink){
                // to render active or open links: 
                $aLink['class']='nav-link' . (isset($aLink['class']) ? ' '.$aLink['class'] : '');
    
                $aChildren=isset($aLink['children']) ? $aLink['children'] : false;
                $aLiClass='nav-item' . ($aChildren && strstr($aLink['class'], 'active') ? ' menu-open' : '');
                $sSubmenu='';
                if($aChildren){
                    unset($aLink['children']);                
                    $aLink['label'].='<i class="right fas fa-angle-left"></i>';
                    $sSubmenu.=$this->getSidebarNavigation($aChildren, ['class'=>'nav nav-treeview']);
                }
                $aLink['label']=$this->addWrapper('p', [], $aLink['label']);
                $sReturn.=$this->addWrapper(
                    'li', ['class'=>$aLiClass],
                    $this->_tag('a', $aLink).$sSubmenu
                )."\n";
            }
            return $sReturn;
        }
    
        /**
         * 
         */
        public function getSidebarNavigation($aNavItems, $aUlOptions=[
                'class'=>'nav nav-pills nav-sidebar flex-column nav-flat_ nav-child-indent',
                'data-widget'=>'treeview',
                'role'=>'menu',
                'data-accordion'=>'false'
                ])
        {
            return $this->addWrapper(
                    'ul', $aUlOptions, 
                    $this->getNavi2Items($aNavItems)
                )."\n";
        }
    
        // ----------------------------------------------------------------------
        // 
        // PUBLIC FUNCTIONS :: CONTENT - BASIC FUNCTIONS
        // 
        // ----------------------------------------------------------------------
        
        /**
         * add page row
         * 
         * @param  string  $sContent  html content inside
         * @return string
         */
        public function addRow($sContent){
            return $this->addWrapper('div', ['class'=>'row'], $sContent);
        }
    
        /**
         * add page column
         * 
         * @param  string   $sContent  html content inside
         * @param  integer  $iCols     column width; 12 = full width
         * @param  string   $sFloat    css value for float attribute; default=false
         * @return string
         */
        public function addCol($sContent, $iCols=6, $sFloat=false){
            return $this->addWrapper('div', ['class'=>'col-md-'.$iCols, 'style'=>'float:'.$sFloat], $sContent);
        }
    
        // ----------------------------------------------------------------------
        // 
        // PUBLIC FUNCTIONS :: CONTENT - WIDGET HELPERS
        // 
        // ----------------------------------------------------------------------
    
    
        /**
         * get a list of all defined components that can be rendered
         * @param  {bool}  $bSendData  flag: send including subkeys of the hash; default: false (keys only)
         * @return {array}
         */
        public function getComponents($bSendData=false){
            return $bSendData
                ? $this->_aElements
                : array_keys($this->_aElements)
            ;
        }
        /**
         * get data of a component
         * @param  {string}  $sComponent  id of the component
         * @return {array}
         */
        public function getComponent($sComponent){
            if(!isset($this->_aElements[$sComponent])){
                return false;
            }
            $aReturn=array_merge(['id'=>$sComponent], $this->_aElements[$sComponent]);
            unset($aReturn['params']);
            return $aReturn;
        }
    
        /**
         * get parameter keys of a component
         * @param  {string}  $sComponent  id of the component
         * @param  {bool}    $bSendData  flag: send including subkeys of the hash; default: false (keys only)
         * @return {array}
         */
        public function getComponentParamkeys($sComponent, $bSendData=false){
            if(!isset($this->_aElements[$sComponent])){
                return false;
            }
            $aKeys=array_keys($this->_aElements[$sComponent]['params']);
            if(!$bSendData){
                return $aKeys;
            }
            $aReturn=[];
            foreach($aKeys as $sKey){
                $aReturn[$sKey]=$this->getComponentParamkey($sComponent, $sKey);
            }
            // $aReturn=$this->_aElements[$sComponent]['params'];
            return $aReturn;
        }
    
        /**
         * get information a parameter keys of a component
         * @param  {string}  $sComponent  id of the component
         * @param  {string}  $sKey        key in the options array
         * @return {array}
         */
        public function getComponentParamkey($sComponent, $sKey){
            if(!isset($this->_aElements[$sComponent]['params'][$sKey])){
                return false;
            }
            $aReturn=$this->_aElements[$sComponent]['params'][$sKey];
            // get description from a preset
            if(!isset($aReturn['description']) && isset($aReturn['select']['description'])){
                $aReturn['description']=$aReturn['select']['description'];
            }
            if(!isset($aReturn['group']) && isset($aReturn['select']['group'])){
                $aReturn['group']=$aReturn['select']['group'];
            }
            return $aReturn;
        }
    
        /**
         * get a flat list of valid parameters for a key in a component
         * @param  {string}  $sComponent  id of the component
         * @param  {string}  $sKey        key in the options array
         * @return {array}
         */
        public function getValidParamValues($sComponent, $sKey){
            $aOptionkey=$this->getComponentParamkey($sComponent, $sKey);
            if(!$aOptionkey || !isset($aOptionkey['select']['values'])){
                return false;
            }
            return array_keys($aOptionkey['select']['values']);
        }
    
    
        // ----------------------------------------------------------------------
    
        /**
         * helper: add a css value with prefix
         * this handles option keys in get[COMPONENT] methods
         * if a value is set then this function returns a space + prefix (param 2) + value
         * @param  {string}  $sValue   option value
         * @param  {string}  $sPrefix  prefix in front of css value
         * @return {string}
         */
        protected function _addClassValue($sValue, $sPrefix=''){
            return $sValue ? ' '.$sPrefix.$sValue : '';
        }
    
        /**
         * helper function for get[COMPONENTNAME] methods:
         * ensure that all wanted keys exist in an array. Non existing keys will be added with value false
         * @return array
         */
        protected function _ensureOptions($sComponent, $aOptions=[]){
    
            $aParams=$this->getComponentParamkeys($sComponent, 0);
            if(!$aParams){
                $aOptions['_infos'][]="Warning: no definition was found for component $sComponent.";
                return $aOptions;
            }
            foreach ($aParams as $sKey){
                if(!isset($aOptions) || !isset($aOptions[$sKey])){
                    $aOptions[$sKey]=false;
                    if(!isset($aOptions['_infos'])){
                        $aOptions['_infos']=[];
                    }
                    $aOptions['_infos'][]="added missing key: $sKey";
                }
    
                // $aParamdata
                $aValidvalues=$this->getValidParamValues($sComponent, $sKey);
                if($aValidvalues){
                    if(array_search($aOptions[$sKey], $aValidvalues)===false){
                        echo "ERROR: [".$sComponent."] value &quot;".$aOptions[$sKey]."&quot; is not a valid for param key [".$sKey."]; it must be one of ".implode("|", $aValidvalues).'<br>';
                    }
                }
        
                // $this->_checkValue($sKey, $aOptions[$sKey], __METHOD__);
            }
            // echo '<pre>' . print_r($aOptions, 1) . '</pre>';
            return $aOptions;
        }
        // ----------------------------------------------------------------------
        // 
        // PUBLIC FUNCTIONS :: CONTENT - WIDGETS
        // 
        // ----------------------------------------------------------------------
    
        /**
         * return a alert box      
         * https://adminlte.io/themes/v3/pages/UI/general.html
         * 
         * @param type $aOptions  hash with keys for all options
         *                          - type - one of [none]|danger|info|primary|success|warning
         *                          - dismissible - if dismissible - one of true|false; default: false
         *                          - title
         *                          - text
         * @return string
         */
        public function getAlert($aOptions){
            $aOptions=$this->_ensureOptions('alert', $aOptions);
            $aAlertIcons=[
                'danger'=>'icon fas fa-ban',
                'info'=>'icon fas fa-info',
                'warning'=>'icon fas fa-exclamation-triangle',
                'success'=>'icon fas fa-check',
            ];
    
            $aElement=[
                'class'=>'alert'
                    . $this->_addClassValue($aOptions['type'], 'alert-')
                    . $this->_addClassValue($aOptions['dismissible'], 'alert-')
                    ,
                'label'=>''
                    . ($aOptions['dismissible'] ? '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' : '')
                    . $this->_tag('h5', [
                        'label'=> ''
                            .(isset($aAlertIcons[$aOptions['type']]) ? '<i class="'.$aAlertIcons[$aOptions['type']].'"></i> ' : '')
                            .$aOptions['title']
                        ])
                    .$aOptions['text']
            ];
    
            return $this->_tag('div', $aElement);
        }
    
        /**
         * get html code for a badge
         * 
         * Examples
         * <span class="badge badge-danger navbar-badge">3</span>
         * <span title="3 New Messages" class="badge badge-warning">3</span>
         * <span class="right badge badge-danger">New</span>
         * <span class="badge badge-danger float-right">$350</span>
         * 
         * @param array $aOptions  hash with keys for all options
         *                          - bgcolor - background color (without prefix "bg")
         *                          - class   - css class
         *                          - id      - optional: id attribute
         *                          - text    - visible text
         *                          - title   - optional: title attribute
         *                          - type    - one of [none]|danger|dark|info|primary|secondary|success|warning
         */
        public function getBadge($aOptions){
            $aOptions=$this->_ensureOptions('badge', $aOptions);
            $aElement=[];
            $aElement['class']='badge'
                . $this->_addClassValue($aOptions['class'],   '')
                . $this->_addClassValue($aOptions['type'],    'badge-')
                . $this->_addClassValue($aOptions['bgcolor'], 'bg-')
                ;
            if ($aOptions['id']){
                $aElement['id']=$aOptions['id'];
            }
            $aElement['title']=$aOptions['title'];
            $aElement['label']=$aOptions['text'];
    
            return $this->_tag('span', $aElement);
        }
    
        /**
         * Get a button.
         * You can use any other key that are not named here. Those keys will be rendered 
         * as additional html attributes without modification.
         * https://adminlte.io/themes/v3/pages/UI/buttons.html
         * 
         * <button type="button" class="btn btn-block btn-default">Default</button>
         * @param type $aOptions  hash with keys for all options
         *                          - type  - one of [none]|danger|dark|info|primary|secondary|success|warning
         *                          - size  - one of [none]|lg|sm|xs|flat
         *                          - class - any css class for customizing, eg. "disabled"
         *                          - icon  - not supported yet
         *                          - text  - text on button
         * @return string
         */
        public function getButton($aOptions){
            $aOptions=$this->_ensureOptions('button', $aOptions);
            $aElement=$aOptions;
            $aElement['class']='btn'
                . $this->_addClassValue($aOptions['type'],    'btn-')
                . $this->_addClassValue($aOptions['size'],    'btn-')
                . $this->_addClassValue($aOptions['class'],   '')
                ;
            $aElement['label']=$aOptions['text'] ? $aOptions['text'] : '&nbsp;';
            foreach(['_infos', 'type', 'size', 'icon', 'text'] as $sDeleteKey){
                unset($aElement[$sDeleteKey]);
            }
            return $this->_tag('button', $aElement);
        }
    
        /**
         * get a callout (box with coloered left border; has type, title + text)
         * https://adminlte.io/themes/v3/pages/UI/general.html
         * 
         * @param type $aOptions  hash with keys for all options
         *                        >> styling
         *                          - type    - one of [none]|danger|dark|info|primary|secondary|success|warning
         *                          - class   - optional css class
         * 
         *                        >> texts/ html content
         *                          - title   - text: title of the card
         *                          - text    - text: content of the card
         * @return string
         */
        public function getCallout($aOptions){
            $aOptions=$this->_ensureOptions('callout', $aOptions);
            $sClass='callout'
                . $this->_addClassValue($aOptions['type'],    'callout-')
                . $this->_addClassValue($aOptions['class'],   '')
                ;
    
            return $this->addWrapper(
                'div', ['class'=>$sClass],
                ($aOptions['title'] ? $this->_tag('h5', ['label'=>$aOptions['title']]) : '')
                .($aOptions['text'] ? $this->_tag('p', ['label'=>$aOptions['text']]) : '')
            );
        }
    
        /**
         * get a card
         * https://adminlte.io/docs/3.2/components/cards.html
         * https://adminlte.io/docs/3.2/javascript/card-widget.html
         * 
         * @param type $aOptions  hash with keys for all options
         *                        >> styling
         *                          - variant: "default"  - titlebar is colored
         *                                     "outline"  - a small stripe on top border is colored
         *                                     "solid"    - whole card is colored
         *                                     "gradient" - whole card is colored with a gradient
         *                          - type    - one of [none]|danger|dark|info|primary|secondary|success|warning
         *                          - class   - any css class for customizing, eg. "disabled"
         *                          - state   - one of [none]|collapsed|maximized
         * 
         *                        >> toolbar icons - set to true to show it; default: none of it is visible
         *                         - tb-collapse
         *                         - tb-expand    it is added automatically if you set 'state'=>'collapsed'
         *                         - tb-maximize 
         *                         - tb-minimize  it is added automatically if you set 'state'=>'maximized'
         *                         - tb-remove
         * 
         *                        >> texts/ html content
         *                          - title   - text: title of the card
         *                          - tools   - text: titlebar top right elements
         *                          - text    - text: content of the card
         *                          - footer  - text: footer of the card
         * @return string
         */
        public function getCard($aOptions){
            $aOptions=$this->_ensureOptions('card', $aOptions);
            // css class prefixes based on "variant" value
            $aVariants=[
                'default'  => 'card-',
                'outline'  => 'card-outline card-',
                'solid'    => 'bg-',
                'gradient' => 'bg-gradient-',
            ];
    
            // window states: css class and toolbar buttons to add
            $aStates=[
                'collapsed'=>[ 'class'=>'collapsed-card', 'tool'=>'tb-expand'],
                'maximized'=>[ 'class'=>'maximized-card', 'tool'=>'tb-minimize'],
            ];
            $aTools=[
                'tb-collapse'=>'<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>',
                'tb-expand'=>'<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-plus"></i></button>',
    
                'tb-maximize'=>'<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fas fa-expand"></i></button>',
                'tb-minimize'=>'<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fas fa-compress"></i></button>',
    
                'tb-remove'=>'<button type="button" class="btn btn-tool" data-card-widget="remove"><i class="fas fa-times"></i></button>',
            ];
    
            // print_r($aOptions);
    
            $sVariantPrefix=$aVariants[$aOptions['variant']] ? $aVariants[$aOptions['variant']] : $aVariants['default'];
            $sClass='card'
                    . $this->_addClassValue($aOptions['type'],    $sVariantPrefix)
                    . $this->_addClassValue($aOptions['class'],   '')
                    ;
    
            // check window state
            foreach($aStates as $sStatus=>$aStatus){
                if($aOptions['state']===$sStatus){
                    $sClass.=' '.$aStatus['class'];
                    $aOptions[$aStatus['tool']]=1;
                }
            }
    
            // add toolbar buttons - from given options or by window state
            foreach($aTools as $sTool=>$sHtml){
                $aOptions['tools'].=($aOptions[$sTool] ? $sHtml : '');
            }
            // build parts of the card
            $sCardHeader=$this->addWrapper('div', ['class'=>'card-header'],
                $this->_tag('h3', ['class'=>'card-title', 'label'=>$aOptions['title']])
                . ($aOptions['tools'] ? $this->_tag('div', ['class'=>'card-tools', 'label'=>$aOptions['tools']]) : '')
            );
    
            $sCardBody=$this->_tag('div', ['class'=>'card-body', 'label'=>$aOptions['text']]);
            $sCardFooter=$aOptions['footer'] ? $this->_tag('div', ['class'=>'card-footer', 'label'=>$aOptions['footer']]) : '';
    
            // merge all
            return $this->addWrapper('div', ['class'=>$sClass], $sCardHeader.$sCardBody.$sCardFooter);
        }
    
    
        /**
         * return an info-box:
         * A colored box with large icon, text and a value.
         * https://adminlte.io/docs/3.2/components/boxes.html
         * 
         * @param type $aOptions  hash with keys for all options
         *                        styling:
         *                          - type    - color of the box; one of [none]|danger|dark|info|primary|secondary|success|warning
         *                          - iconbg  - background color or type of icon; use it for default type (type="")
         *                          - shadow  - size of shadow; one of [none] (=default: between small and regular)|none|small|regular|large 
         * 
         *                        content
         *                          - icon    - icon class
         *                          - text    - information text
         *                          - number  - value (comes in bold text)
         *                          - progressvalue - integer: progress bar; range: 0..100
         *                          - progresstext  - text below progress bar
         * @return string
         */
        public function getInfobox($aOptions){
            $aOptions=$this->_ensureOptions('infobox', $aOptions);
    
            // print_r($aOptions);
            $sClass='info-box'
                    . $this->_addClassValue($aOptions['type'],    'bg-')
                    . $this->_addClassValue($aOptions['class'],   '')
                    .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) 
                        ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
                    ;
            
            // build parts
            $sIcon=$aOptions['icon'] 
                ? $this->addWrapper("span", [
                        'class'=>'info-box-icon'.($aOptions['iconbg'] ? ' bg-'.$aOptions['iconbg'] : '')
                    ], $this->_tag('i',['class'=>$aOptions['icon']])) 
                : ''
                ;
            $sContent=$this->addWrapper("div", ['class'=>'info-box-content'],
                ''
                . ($aOptions['text']   ? $this->_tag('span', ['class'=>'info-box-text',   'label'=>$aOptions['text']])   : '')
                . ($aOptions['number'] ? $this->_tag('span', ['class'=>'info-box-number', 'label'=>$aOptions['number']]) : '')
                . ($aOptions['progressvalue']!==false && $aOptions['progressvalue']!=='' 
                    ? $this->addWrapper('div', ['class'=>'progress'],
                            $this->_tag('div', ['class'=>'progress-bar'. ($aOptions['iconbg'] ? ' bg-'.$aOptions['iconbg'] : ''), 'style'=>'width: '.(int)$aOptions['progressvalue'].'%' ]) 
                        )
                        . ($aOptions['progresstext'] ? $this->_tag('span', ['class'=>'progress-description', 'label'=>$aOptions['progresstext']]) : '' )
                    : ''    
                    ) 
            );
    
            // merge all
            return $this->_tag('div', ['class'=>$sClass], $sIcon.$sContent);
        }
    
    
        /**
         * return an info-box:
         * A colored box with large icon, text and a value.
         * https://adminlte.io/docs/3.2/components/boxes.html
         * https://adminlte.io/themes/v3/pages/widgets.html
         * 
         * @param type $aOptions  hash with keys for all options
         *                        styling:
         *                          - type    - color of the box; one of [none]|danger|dark|info|primary|secondary|success|warning
    
         *                        content
         *                          - icon    - icon class for icon on the right
         *                          - text    - information text
         *                          - number  - value (comes in bold text)
         *                          - url     - integer: progress bar; range: 0..100
         *                          - linktext- text below progress bar
         * @return string
         */
        public function getSmallbox($aOptions){
            $aOptions=$this->_ensureOptions('smallbox', $aOptions);
            $aShadows=[
                'default'  => '',
                'none'     => 'shadow-none',
                'small'    => 'shadow-small',
                'regular'  => 'shadow',
                'large'    => 'shadow-lg',
            ];
            // print_r($aOptions);
            $sClass='small-box'
                    . $this->_addClassValue($aOptions['type'],    'bg-')
                    .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) 
                        ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
                    ;
            
            // build parts
            $sContent=$this->addWrapper("div", ['class'=>'inner'],
                ''
                . ($aOptions['number'] ? $this->_tag('h3', ['label'=>$aOptions['number']]) : '')
                . ($aOptions['text']   ? $this->_tag('p', ['class'=>'info-box-text',   'label'=>$aOptions['text']])   : '')
            );
            $sIcon=$aOptions['icon'] 
                ? $this->addWrapper("div", ['class'=>'icon'], 
                    $this->_tag('i',['class'=>$aOptions['icon']])) 
                : ''
                ;
            $sFooter=($aOptions['url']
                ? $this->addWrapper("a", [
                    'class'=>'small-box-footer',
                    'href'=>$aOptions['url'],
                    ],
                    ''
                    . ($aOptions['linktext'] ? $aOptions['linktext'] : $aOptions['url'])
                    . ' '
                    . $this->_tag('i',['class'=>'fas fa-arrow-circle-right'])
                )
                : ''
            );
    
            // merge all
            return $this->_tag('div', ['class'=>$sClass], $sContent.$sIcon.$sFooter);
        }
    
    
    /*
    
    <div class="info-box">
      <span class="info-box-icon bg-info"><i class="far fa-bookmark"></i></span>
      <div class="info-box-content">
        <span class="info-box-text">Bookmarks</span>
        <span class="info-box-number">41,410</span>
        <div class="progress">
          <div class="progress-bar bg-info" style="width: 70%"></div>
        </div>
        <span class="progress-description">
          70% Increase in 30 Days
        </span>
      </div>
    </div>
    
    
    
      <div class="info-box bg-success">
        <span class="info-box-icon"><i class="far fa-thumbs-up"></i></span>
        <div class="info-box-content">
          <span class="info-box-text">Likes</span>
          <span class="info-box-number">41,410</span>
          <div class="progress">
            <div class="progress-bar" style="width: 70%"></div>
          </div>
          <span class="progress-description">
            70% Increase in 30 Days
          </span>
        </div>
      </div>
    */
      
    
    
    
    
    
    
    
    
    }