Latest News: (loading..)
gadlol

tep_draw_categories_tree finally no recursion just one mysql_query

59 posts in this topic

I did not and don't like the recursive function tep_get_categories() and the how the categories get drawn into the categories box.
 
So I created a function that draws the categories tree (expanded or not). It uses only one query for the categories.

So its faster than the default.
 
It uses <ul> list tags.
It produces full tree code even if $drawExpanded=false, that way you can use css3 or jquery menu so to popup on hover-click the hidden subcategories, any length of subcategories.
 

Things to be done replace that recursive function tep_count_products_in_category when displaying the number of products under each category.

 

Hope you like it.

Check images:

Tree fully expanded
 
Tree expanded only if page in categories
 
Produced Code
 

 

Code : http://pastebin.com/puCzrASC

 

or:

//tep_draw_categories_tree

  function tep_draw_categories_tree($drawExpanded=false,$root_id = 0,$mainUlClass='',$submenuUlClass='submenu'){
    global $languages_id,$cPath_array;
        
    //GET ALL CATEGORIES
    $categories_query = tep_db_query("select c.categories_id, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' order by sort_order, cd.categories_name");
    
    $items = array();
    
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    
    $citems=count($items);
    
    if($citems<=0) return '';
    elseif($citems==1) $children[] = $items; //in case we have one category item without subcategories, rare but possible
    else foreach( $items as $item ) $children[$item['parent_id']][] = $item;
        
		// loop will be false if the root has no children (i.e., an empty categories!)
		$loop = !empty( $children[$root_id] );

		$parent = $root_id;
		$parent_stack = array();
    $html=array();//store html code
		$stack=array();//helper array so to know the current level
    
    $pic=''; //products_in_category string
    
		$html[]='<ul class="'.$mainUlClass.'">';
		while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ){

			if ( $option === false ){

				$parent = array_pop( $parent_stack );
				
				$html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 ) . '</ul>';
				$html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ) . '</li>';
				
				array_pop( $stack );
				
			}elseif ( !empty( $children[$option['value']['id']] ) ){
			
				$tab = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 );
				$stack[]=$option['value']['id'];
				
  				$rt=$root_id>0 ? $root_id.'_' : '';

	  			$cpath_new=count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack);        
                    
          $html[]=$tab.'<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'">';
        
          if (SHOW_COUNTS == 'true') { //THIS SHOULD BE CHANGED SO NOT TO USE tep_count_products_in_category WHICH IS RECURSIVE
            $products_in_category = tep_count_products_in_category($option['value']['id']);
            if ($products_in_category > 0) {
              $pic=' (' . $products_in_category . ')';
            }
          }
          
          $sm=0;        
          
          if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
                    
            $sm=1;
          
            $html[]='<strong>'.stripslashes($option['value']['name']).'->'.$pic.'</strong>';
            
          }else{
          
            $html[]=stripslashes($option['value']['name']).'->'.$pic;
            
          }
      
          $html[]='</a>';

				  $html[] = $tab . "\t" . '<ul class="'.$submenuUlClass.'" style="'.($sm!==1 && !$drawExpanded ?'display:none;':'').'">';

				$parent_stack[]=$option['value']['parent_id'];
				$parent = $option['value']['id'];

			}else{
				
        $rt=$root_id>0 ? $root_id.'_' : '';

				$cpath_new= count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack).'_'.$option['value']['id'];
  			
				$html[]=str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ).'<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'" >';
				
        if (SHOW_COUNTS == 'true') { //THIS SHOULD BE CHANGED SO NOT TO USE tep_count_products_in_category WHICH IS RECURSIVE
          $products_in_category = tep_count_products_in_category($option['value']['id']);
          if ($products_in_category > 0) {
            $pic=' (' . $products_in_category . ')';
          }
        }
				
				if (isset($cPath_array) && in_array($option['value']['id'], $cPath_array)) {

          $html[]='<strong>'.stripslashes($option['value']['name']).$pic.'</strong>';

        }else{

          $html[]=stripslashes($option['value']['name']).$pic;

        }
				
				$html[]='</a></li>';
				
			}
				
		}
    $html[]='</ul>';
		echo implode( "\r\n", $html );

  }

//tep_draw_categories_tree
joli1811, GLWalker, De Dokta and 1 other like this

Share this post


Link to post
Share on other sites

I like it. Its looks like as category_tree class conception but only one function with intresting features. (category_class is not count subcategories)

My favorite code detail is

echo implode( "\r\n", $html );

Grat!

gadlol likes this

Share this post


Link to post
Share on other sites

By using that function with some modifications, I managed to create a catalog treeview list.

You have all the categories and subcategories and the products that are under each category.

The products are hidden by default but you can reveal them by clicking on the plus icon.

 

That Catalog treeview will be a module for Alternative Administration System version 0.3 (when it will be released).

 

Question is "Catalog treeview" a good name for that module?

 

Please have a look at the screenshot bellow so you can understand what I mean.

 

http://www.alternative-administration-system.com/photos/various/aas_module_catalog_treeview.png

 

Share this post


Link to post
Share on other sites

I have a question regarding this categories code.

For the past 3 hours i am trying to figure out how to put this great code inside the categories box.

But for the life of me i can't figure it out... :wacko:

I looked at De Doktas category code and how he put it inside the header. And his version works great inside the header.

If i follow the same steps as the De Dokta did i get the menu showing but it is always showing top-left of the screen no matter what.

I tried BS and non-BS shops with same results. Always top-left but never inside the categories box itself.

Tried to give it all kinds of css definitions but no luck.

I would really appreciate if someone could give me a hint as to where to put what to make it work.

Is there a working example (for 2.3.3.4BS or 2.3.4BS) ?

Edited by Tsimi

Share this post


Link to post
Share on other sites

Hi Lambros,

 

I didn't try it but I'm sure it can't be done with the tags I used. I took the dropdown-menu tags which are predefined in the Bootstrap css und show a certain behavior.

What you need for the categories box is a structure like this:

 

<div class="panel panel-default">  <
   div class="panel-heading">Categories</div>  
    <div class="panel-body">                                                                   ///bm_categories.php
   

    <ul class="nav nav-list">
      <li><a href="http://your_path/index.php?cPath=1">Cat_1</a></li>
      <li><a href="http://your_path/index.php?cPath=2">Cat_2</a></li>
      <li><a href="http://your_path/index.php?cPath=3">Cat_3</a></li>
      <li><a href="http://your_path/index.php?cPath=4">Cat_4</a></li>
      <li><a href="http://your_path/index.php?cPath=5">Cat_5</a></li>   
////cats without subs
      
      <li><a href="http://your_path/index.php?cPath=5"><strong>Cat_6</strong></a>
//cat with subs
         <ul class="nav nav-list">
           <li><a href="http://your_path/index.php?cPath=5_1">  Subcat_1</a></li>
           <li><a href="http://your_path/index.php?cPath=5_2">  Subcat_1</a></li>
           <li><a href="http://your_path/index.php?cPath=5_3">  Subcat_1</a></li>
         </ul>                                                                                                                  
///end subcats
      </li>                                                                                                                       ///end Cat_6
           
      <li><a href="http://your_path/index.php?cPath=7">Cat_6</a></li>
      ..................
    </ul>

 

</div>                                                                                                    ///bm_categories.php          
</div>  
 

Should be possible to integrate this with gadlols great code. :rolleyes: I'm really looking forward to your results.

 

Gruß

J.J.

Share this post


Link to post
Share on other sites
That interested me now! :-
 
Just quick and dirty:
html_output.php
/* Menu cats box*/

function tep_show_tree_box($root_id = 0,$mainUlClass='nav nav-list',$submenuUlClass='nav nav-list submenu'){
    global $languages_id,$cPath_array, $datas;
    $categories_query = tep_db_query("select c.categories_id, cd.categories_name, c.categories_status, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' and c.categories_status = '1' order by sort_order, cd.categories_name");
    $items = array();
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    $citems=count($items);
    
    if($citems<=0) return '';
    elseif($citems==1) $children[] = $items; //in case we have one category item without subcategories, rare but possible
    else foreach( $items as $item ) $children[$item['parent_id']][] = $item;
        $loop = !empty( $children[$root_id] );
        $parent = $root_id;
        $parent_stack = array();
        $stack=array();//helper array so to know the current level
    $pic=''; //products_in_category string
        $datas .='<ul class="'.$mainUlClass.'">';
        while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ){
            if ( $option === false ){
                $parent = array_pop( $parent_stack );
                $datas .= '</ul>';
                $datas .= '</li>';
                array_pop( $stack );
            }elseif ( !empty( $children[$option['value']['id']] ) ){
                $stack[]=$option['value']['id'];
                  $rt=$root_id>0 ? $root_id.'_' : '';
                  $cpath_new=count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack);
                $datas .= '<li><a class="trigger right-caret" href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'">';
          $sm=0;        
          if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
            $sm=1;
            $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
          }else{
            $datas .=stripslashes($option['value']['name']);
          }
            $datas .='</a>';
            $datas .= '<ul class="'.$submenuUlClass.'">';
                $parent_stack[]=$option['value']['parent_id'];
                $parent = $option['value']['id'];
          }else{
        $rt=$root_id>0 ? $root_id.'_' : '';
                $cpath_new= count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack).'_'.$option['value']['id'];
                $datas .= '<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'" >';

                if (isset($cPath_array) && in_array($option['value']['id'], $cPath_array)) {
          $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
        }else{
          $datas .=stripslashes($option['value']['name']);
        }
        $datas .='</a></li>';
        }
       }
        $datas .='</ul>';

      return $datas;
  } 

footer.php:

<script type="text/javascript">
$(".nav-list > li > a.trigger").one("click",function(e){e.preventDefault();var current=$(this).next();var grandparent=$(this).parent().parent();if($(this).hasClass('left-caret')||$(this).hasClass('right-caret'))$(this).toggleClass('right-caret left-caret');grandparent.find('.left-caret').not(this).toggleClass('right-caret left-caret');grandparent.find(".submenu:visible").not(current).hide();current.toggle();e.stopPropagation();});$(".nav-list > li > a:not(.trigger)").one("click",function(){var root=$(this).closest('.nav-list');root.find('.left-caret').toggleClass('right-caret left-caret');root.find('.submenu:visible').hide();});(jQuery);
</script>

user.css:

.nav-list>li /* To prevent selection of text */
{   position:relative;
    padding-left: 5px;
    -webkit-user-select: none; /* Chrome/Safari */        
    -moz-user-select: none; /* Firefox */
    -ms-user-select: none; /* IE10+ */
    /* Rules below not implemented in browsers yet */
    -o-user-select: none;
    user-select: none;
    cursor:pointer;
}
.submenu
{
    display:none;
}
.right-caret:after,.left-caret:after
 {  content:"";
    border: 5px solid transparent;
    border-top: 5px solid transparent;
    display: inline-block;
    height: 0;
    vertical-align: middle;
    width: 0;
    margin-left:15px;
}
.right-caret:after {   
    border-left: 5px solid #ffaf46;
}
.left-caret:after {
    border-right: 5px solid #ffaf46;
}

and bm_categories.php

    function execute() {
      global $oscTemplate, $cPath;


      $data = '<div class="panel panel-default no-border hidden-xs hidden-sm">' .
              '  <div class="panel-heading gradient">' . MODULE_BOXES_CATEGORIES_BOX_TITLE . '</div>' .
              '  <div class="panel-body ContentBody">' . tep_show_tree_box() . '</div>' .
              '</div>';

      $oscTemplate->addBlock($data, $this->group);
    }

May need some fine tuning (e.g.hover effects) but in principle it works. (w00t)

 

J.J.

 

Tsimi likes this

Share this post


Link to post
Share on other sites

@@De Dokta

 

Hi J.J.

 

Thanks very much for that!

It does work and as you said and it needs a tiny bit of fine tuning. Example when you browse "DVD Movies" the 1st subcategories expand and then when you choose "Action" the correct products are loaded but the tree collapses. I guess it has something to do with the javascript code? *justguessing*

But other than that it does work very nicely the hover is there too thanks to Bootstrap.

Right now i use your other header categories code for sm and xs view and this code for md and lg view.

 

I would never have guessed to add any javascript code...I always thought this would be not necessary.

 

Kind regards

Lambros

Edited by Tsimi

Share this post


Link to post
Share on other sites

Right now i use your other header categories code for sm and xs view and this code for md and lg view.

Hi Lambros,

 

exactly this was my idea too. Currently I use the header solution for the collapsed view, because in that case I want the categories navigation on the top and not somewhere "im Nirvana" - for the full view I have so far the default navigation (which makes me not really happy), but so far I have not found the time to deal intensively with. As I said "quick and dirty".............. :blush:

 

J.J.

Share this post


Link to post
Share on other sites

Ok, i  think I am almost there....

Did some more digging and there seems to be a way to keep the tree open after clicking on a parent or child category.

Cookies, jquery cookies to be exact. If you search for "jQuery vertical accordion menu plugin" or "jQuery Simple Vertical Accordion Menu with Cookies"

you should find what i am talking about.

It works "almost" perfect. The only thing that i feel to be a bit weird is the transition of the menu on page reload when category is clicked. The menu has a very nice

transition effect which gets kinda hacked off because of the page load. I wish there would be something like Ajax for menus (haven't looked for it).

Other than that it works!

The tree stays open, Bootstrap hover and active style is applied.

I am not really sure if I am allowed to post any code from it or link to that page that is why i am holding back for now...or just PM me for more details on the current code.

Edited by Tsimi

Share this post


Link to post
Share on other sites

Hi Lambros

 

Not so shy - I see no reason why you are not allowed to post some code here!? Others might also be interested in that issue.
 
I think some additional JS is necessary to keep active parents and subs open. Since I've some trouble getting along with JS :blush: a solution from my side may last a while. Maybe someone else have a flash of genius........
 
J.J.
Edited by De Dokta

Share this post


Link to post
Share on other sites

Here is the html_output.php code

function tep_show_tree_box($root_id = 0,$mainUlClass='nav nav-list',$submenuUlClass='nav nav-list submenu',$mainUlId='accordion',$submenuUlId='accordion'){
    global $languages_id,$cPath_array, $datas;
    $categories_query = tep_db_query("select c.categories_id, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' order by sort_order, cd.categories_name");
    $items = array();
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    $citems=count($items);
    
    if($citems<=0) return '';
    elseif($citems==1) $children[] = $items; //in case we have one category item without subcategories, rare but possible
    else foreach( $items as $item ) $children[$item['parent_id']][] = $item;
        $loop = !empty( $children[$root_id] );
        $parent = $root_id;
        $parent_stack = array();
        $stack=array();//helper array so to know the current level
    $pic=''; //products_in_category string
		$datas .='<ul class="'.$mainUlClass.'" id="' . $mainUlId . '">';
        while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ){
            if ( $option === false ){
                $parent = array_pop( $parent_stack );
                $datas .= '</ul>';
                $datas .= '</li>';
                array_pop( $stack );
            }elseif ( !empty( $children[$option['value']['id']] ) ){
                $stack[]=$option['value']['id'];
                  $rt=$root_id>0 ? $root_id.'_' : '';
                  $cpath_new=count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack);
                $datas .= '<li><a class="trigger right-caret" href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'">';
          $sm=0;        
          if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
            $sm=1;
            $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
          }else{
            $datas .=stripslashes($option['value']['name']);
          }
            $datas .='</a>';
            $datas .= '<ul class="'.$submenuUlClass.'" id="' . $submenuUlId . '">';
                $parent_stack[]=$option['value']['parent_id'];
                $parent = $option['value']['id'];
          }else{
        $rt=$root_id>0 ? $root_id.'_' : '';
                $cpath_new= count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack).'_'.$option['value']['id'];
                $datas .= '<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'" >';

                if (isset($cPath_array) && in_array($option['value']['id'], $cPath_array)) {
          $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
        }else{
          $datas .=stripslashes($option['value']['name']);
        }
        $datas .='</a></li>';
        }
       }
        $datas .='</ul>';
        $datas .='</div>';

      return $datas;
  }

here is the footer.php javascript code

<script type="text/javascript">
$('#accordion').dcAccordion({
    eventType    : 'click',    // Event for activating menu - options are "click" or "hover"
    hoverDelay   : 300,    // Hover delay for hoverIntent plugin
    menuClose   : true,    // If set "true" with event "hover" menu will close fully when mouseout
    autoClose    : true,    // If set to "true" only one sub-menu open at any time
    autoExpand   : false,    // If set to "true" all sub-menus of parent tags with class 'classExpand' will expand on page load
    speed         : 'slow',    // Speed of animation
    saveState    : true,    // Save menu state using cookies
    disableLink  : false,    // Disable all links of parent items
    showCount : false,    // If "true" will add a count of the number of links under each parent menu item
    cookie  : 'dcjq-accordion'    // Sets the cookie name for saving menu state - each menu instance on a single page requires a unique cookie name.
});
</script>

two new js files need to be added, in my case i added a js folder under the root folder and define the paths inside the template_top.php

<script type="text/javascript" src="js/jquery.cookie.js"></script>
<script type='text/javascript' src='js/jquery.dcjqaccordion.2.7.min.js'></script>

here are the two js files (zipped)

 

 

 

as you can see there are a lot options available and there are few more that i haven't posted here.

I did more digging and there seems to be a way to prevent the page from loading when a link is clicked. "Dynamic Page"

I will do more testing regarding this matter and report back.

 

 

De Dokta likes this

Share this post


Link to post
Share on other sites

small typo in the html_output.php code above. forgot to remove a closing div tag.

 

remove this

 

$datas .='</div>';

Share this post


Link to post
Share on other sites

Did some more work and still can't get the desired result, frustrating.
If you search for "Rethinking Dynamic Page Replacing Content" you will see that this is a solution to create dynamic page contents.
I implemented that into osC 2.3.4BS and now i have the following issues.

 - When in current category the category name doesn't show "bold"
 - Back and Forward buttons from browser or mouse itself doesn't work. Always going back to start page.
 - Interfering with the grid/list switch of bootstrap
 
 the good thing is the transition and navigation works fine and the URLs also show just fine no hashtags or anything else that could interfere with SEO.
 the bad thing this this is getting more and more over engineered just to receive a simple function.
 
 Anyway here is a small manual to reach the current state of this categories menu. Hope someone who has more skills in jquery/javascript
 can take a look at it and tell what is going wrong or what need to be changed.
 
 html_output.php file

function tep_show_tree_box($root_id = 0,$mainUlClass='nav nav-list group',$submenuUlClass='nav nav-list submenu group',$mainUlId='accordion',$submenuUlId='accordion'){
    global $languages_id,$cPath_array, $datas;
    $categories_query = tep_db_query("select c.categories_id, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' order by sort_order, cd.categories_name");
    $items = array();
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    $citems=count($items);
    
    if($citems<=0) return '';
    elseif($citems==1) $children[] = $items; //in case we have one category item without subcategories, rare but possible
    else foreach( $items as $item ) $children[$item['parent_id']][] = $item;
        $loop = !empty( $children[$root_id] );
        $parent = $root_id;
        $parent_stack = array();
        $stack=array();//helper array so to know the current level
    $pic=''; //products_in_category string
		$datas .='<navWrapper><ul class="'.$mainUlClass.'" id="' . $mainUlId . '">';
        while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ){
            if ( $option === false ){
                $parent = array_pop( $parent_stack );
                $datas .= '</ul></navWrapper>';
                $datas .= '</li>';
                array_pop( $stack );
            }elseif ( !empty( $children[$option['value']['id']] ) ){
                $stack[]=$option['value']['id'];
                  $rt=$root_id>0 ? $root_id.'_' : '';
                  $cpath_new=count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack);
                $datas .= '<li><a class="trigger right-caret" href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'">';
          $sm=0;        
          if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
            $sm=1;
            $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
          }else{
            $datas .=stripslashes($option['value']['name']);
          }
            $datas .='</a>';
            $datas .= '<ul class="'.$submenuUlClass.'" id="' . $submenuUlId . '">';
                $parent_stack[]=$option['value']['parent_id'];
                $parent = $option['value']['id'];
          }else{
        $rt=$root_id>0 ? $root_id.'_' : '';
                $cpath_new= count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack).'_'.$option['value']['id'];
                $datas .= '<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'" >';

                if (isset($cPath_array) && in_array($option['value']['id'], $cPath_array)) {
          $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
        }else{
          $datas .=stripslashes($option['value']['name']);
        }
        $datas .='</a></li>';
        }
       }
        $datas .='</ul>';
       
      return $datas;
  }

footer.php

<script type="text/javascript">
$('#accordion').dcAccordion({
    eventType    : 'click',    // Event for activating menu - options are "click" or "hover"
    hoverDelay   : 300,    // Hover delay for hoverIntent plugin
    menuClose   : true,    // If set "true" with event "hover" menu will close fully when mouseout
    autoClose    : true,    // If set to "true" only one sub-menu open at any time
    autoExpand   : false,    // If set to "true" all sub-menus of parent tags with class 'classExpand' will expand on page load
    speed         : 'slow',    // Speed of animation
    saveState    : true,    // Save menu state using cookies
    disableLink  : false,    // Disable all links of parent items
    showCount : false,    // If "true" will add a count of the number of links under each parent menu item
    cookie  : 'dcjq-accordion',    // Sets the cookie name for saving menu state - each menu instance on a single page requires a unique cookie name.
	saveState    : true    // Save menu state using cookies
});
</script>

template_top.php add the following js paths before the </head> tag (that is if you have a js folder under the catalog file otherwise adjust the paths)

<script type="text/javascript" src="js/jquery.cookie.js"></script>
<script type='text/javascript' src="js/jquery.hoverIntent.minified.js"></script>
<script type="text/javascript" src="js/jquery.dcjqaccordion.2.7.min.js"></script>
<script type="text/javascript" src="js/modernizr.js"></script>
<script type="text/javascript" src="js/dynamicpage.js"></script>

look for this code

<div id="bodyContent" class="col-md-<?php echo $oscTemplate->getGridContentWidth(); ?> <?php echo ($oscTemplate->hasBlocks('boxes_column_left') ? 'col-md-push-' . $oscTemplate->getGridColumnWidth() : ''); ?>">

add after it

	  <section id="main-content">
		<div id="guts">

template_bottom.php find this code

 </div> <!-- #bodyContent //-->

replace with

      </div> <!-- #guts //-->
        </section> <!-- #main-content //-->
      </div> <!-- #bodyContent //-->

create a js folder under the catalog root folder and add the following javascript files into it

 

 

 

and that's it i think. If i forgot anything let me know...

any help on this matter is very welcome, thanks.

Edited by Tsimi

Share this post


Link to post
Share on other sites

Sorry, forgot the css definitions

/* BOF CATEGORIES BOX */
.nav-list>li /* To prevent selection of text */
{   position:relative;
    padding-left: 5px;
    -webkit-user-select: none; /* Chrome/Safari */        
    -moz-user-select: none; /* Firefox */
    -ms-user-select: none; /* IE10+ */
    /* Rules below not implemented in browsers yet */
    -o-user-select: none;
    user-select: none;
    cursor:pointer;
}
.submenu
{
    display:none;
}
.right-caret:after,.left-caret:after
 {  content:"";
    border: 5px solid transparent;
    border-top: 5px solid transparent;
    display: inline-block;
    height: 0;
    vertical-align: middle;
    width: 0;
    margin-left:15px;
}
.right-caret:after {   
    border-left: 5px solid #ffaf46;
}
.left-caret:after {
    border-right: 5px solid #ffaf46;
}
/* EOF CATEGORIES BOX */

Share this post


Link to post
Share on other sites

Hi Lambros

 

Sorry for the late reply, but after a week of working until 2-3 clock in the morning I was (I am) very, very tired.

I just tested your accordion solution on the fly - without your last addings to template_top, template_bottom and without jquery.hoverIntent.minified.js, modernizr.js, dynamicpage.js (don't know what you added them for).

 

Hey looks good!!!!!! (w00t)  And in this state I can find none of the issues you mentioned!

- current categories are bold

- browser back button works

- no interference with grid/list function

 

The only issue I found is that a click on main category opens the accordion but also the category itself. The other thing is that you lost the caret-left caret-right toggle which seems a minor problem.

BTW: Why did you add jquery.dcjqaccordion.2.7.min.js? Accordion functionality should be part of the jquery core...

 

Greetz

J.J.

Share this post


Link to post
Share on other sites
Hey looks good!!!!!! (w00t)  And in this state I can find none of the issues you mentioned!

- current categories are bold

- browser back button works

- no interference with grid/list function

 

:huh:  it does? i just tried again...somehow the transition doesn't look good to me. No matter if FF, Chrome or IE.

Normally when you open or close an accordion menu there is some sort of smooth slideUp or slideDown transition right? That transition gets hacked off a bit at the end because of the page load. If you just try to install the latest codes that i created then you'll see what i mean or you could just look at any accordion demo in the web and see how nice and smooth they open and close.

And yes all other issues are not there which is great! (w00t)  Except the caret-left and caret toggle and that the first parent category stays open for most the time and can't be closed. Minor bugs but hey we're getting closer. I am sure there is a possibility without all these schnick schnack to keep the tree open when browsing the category but no one here to share...@@gadlol? @@Dr. Rolex?

 

Why i don't use the Bootstrap accordion? ...3 reasons...1st i need the cookies function inside the javascript.

If you look at the jquery.dcjqaccordion.2.7.min.js file you see there is a lot cookie code stuff inside it which again needs the jquery.cookies.js file.

I couldn't find any other solution to keep the tree open when in current category.

2nd reason i thought it would be great to have a solution that works for BS shops and non-BS shops.

3rd and most shameful reason is, i don't know how to use the bootstrap accordion function with javascript. :blush:  need probably more time to check it out...

 

Is there anyone else that can confirm De Doktas result? Is the transition OK? Or is it just me?

Edited by Tsimi

Share this post


Link to post
Share on other sites

Ok once again.

 

Case 1: Click function (which I prefer)

 

general.php:

function tep_show_tree_box($root_id = 0,$mainUlClass='nav nav-list group',$submenuUlClass='nav nav-list submenu group',$mainUlId='accordion'){
    global $languages_id,$cPath_array, $datas;
    $categories_query = tep_db_query("select c.categories_id, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' order by sort_order, cd.categories_name");
    $items = array();
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    $citems=count($items);
    
    if($citems<=0) return '';
    elseif($citems==1) $children[] = $items; //in case we have one category item without subcategories, rare but possible
    else foreach( $items as $item ) $children[$item['parent_id']][] = $item;
        $loop = !empty( $children[$root_id] );
        $parent = $root_id;
        $parent_stack = array();
        $stack=array();//helper array so to know the current level
    $pic=''; //products_in_category string
        $datas .='<ul class="'.$mainUlClass.'" id="' . $mainUlId . '">';
        while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ){
            if ( $option === false ){
                $parent = array_pop( $parent_stack );
                $datas .= '</ul>';
                $datas .= '</li>';
                array_pop( $stack );
            }elseif ( !empty( $children[$option['value']['id']] ) ){
                $stack[]=$option['value']['id'];
                  $rt=$root_id>0 ? $root_id.'_' : '';
                  $cpath_new=count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack);
                $datas .= '<li><a class="trigger right-caret" href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'">';
          $sm=0;        
          if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
            $sm=1;
            $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
          }else{
            $datas .=stripslashes($option['value']['name']);
          }
            $datas .='</a>';
            $datas .= '<ul class="'.$submenuUlClass.'">';
                $parent_stack[]=$option['value']['parent_id'];
                $parent = $option['value']['id'];
          }else{
        $rt=$root_id>0 ? $root_id.'_' : '';
                $cpath_new= count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack).'_'.$option['value']['id'];
                $datas .= '<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'" >';

                if (isset($cPath_array) && in_array($option['value']['id'], $cPath_array)) {
          $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
        }else{
          $datas .=stripslashes($option['value']['name']);
        }
        $datas .='</a></li>';
        }
       }
        $datas .='</ul>';
               $datas .='</div>';

      return $datas;
  }

You don't need a $submenuUlId='accordion' - once the id is set, it's set for the whole menu! I also removed the navWrapper - no idea what it was good for.

 

footer.php:

<script type="text/javascript">
$('#accordion').dcAccordion({
    eventType    : 'click',    // Event for activating menu - options are "click" or "hover"
    hoverDelay   : 0,    // Hover delay for hoverIntent plugin
    menuClose   : false,    // If set "true" with event "hover" menu will close fully when mouseout
    autoClose    : true,    // If set to "true" only one sub-menu open at any time
    autoExpand   : false,    // If set to "true" all sub-menus of parent tags with class 'classExpand' will expand on page load
    speed         : 'slow',    // Speed of animation
    saveState    : true,    // Save menu state using cookies
    disableLink  : false,    // Disable all links of parent items
    showCount : false,    // If "true" will add a count of the number of links under each parent menu item
    cookie  : 'dcjq-accordion',    // Sets the cookie name for saving menu state - each menu instance on a single page requires a unique cookie name.
    saveState    : true    // Save menu state using cookies
});
$(".nav-list > li > a.trigger").one("click",function(e){e.preventDefault();
var current=$(this).next();
var grandparent=$(this).parent().parent();
if($(this).hasClass('left-caret')||$(this).hasClass('right-caret'))$(this).toggleClass('right-caret left-caret');
grandparent.find('.left-caret').not(this).toggleClass('right-caret left-caret');
});$(".nav-list > li > a:not(.trigger)").one("click",function(){var root=$(this).closest('.nav-list');root.find('.left-caret').toggleClass('right-caret left-caret');});(jQuery);
</script>

jquery.dcjqaccordion.2.7.min.js:

(function($){
    $.fn.dcAccordion = function(options) {
        //set default options
        var defaults = {
            classParent     : 'dcjq-parent',
            classActive     : 'active',            classArrow     : 'dcjq-icon',            classCount     : 'dcjq-count',
            classExpand     : 'dcjq-current-parent',
            eventType     : 'click',
            hoverDelay     : 300,
            menuClose     : true,
            autoClose    : true,
            autoExpand     : false,
            speed        : 'slow',
            saveState     : true,
            disableLink     : true,            showCount : false,
            cookie    : 'dcjq-accordion'
        };
        //call in the default otions
        var options = $.extend(defaults, options);
        this.each(function(options){
            var obj = this;
            setUpAccordion();
            if(defaults.saveState == true){
                checkCookie(defaults.cookie, obj);
            }
            if(defaults.autoExpand == true){
                $('li.'+defaults.classExpand+' > a').addClass(defaults.classActive);
            }
            resetAccordion();
            if(defaults.eventType == 'hover'){
                var config = {
                    sensitivity: 2, // number = sensitivity threshold (must be 1 or higher)
                    interval: defaults.hoverDelay, // number = milliseconds for onMouseOver polling interval
                    over: linkOver, // function = onMouseOver callback (REQUIRED)
                    timeout: defaults.hoverDelay, // number = milliseconds delay before onMouseOut
                    out: linkOut // function = onMouseOut callback (REQUIRED)
                };
                $('li a',obj).hoverIntent(config);
                var configMenu = {
                    sensitivity: 2, // number = sensitivity threshold (must be 1 or higher)
                    interval: 1000, // number = milliseconds for onMouseOver polling interval
                    over: menuOver, // function = onMouseOver callback (REQUIRED)
                    timeout: 1000, // number = milliseconds delay before onMouseOut
                    out: menuOut // function = onMouseOut callback (REQUIRED)
                };
                $(obj).hoverIntent(configMenu);
                // Disable parent links
                if(defaults.disableLink == true){
                    $('li a',obj).one('click',function(e){
                        if($(this).siblings('ul').length >0){
                            e.preventDefault();
                        }
                    });
                }
            } else {            
                $('li a',obj).one('click',function(e){
                    $activeLi = $(this).parent('li');
                    $parentsLi = $activeLi.parents('li');
                    $parentsUl = $activeLi.parents('ul');
                    // Prevent browsing to link if has child links
                    if(defaults.disableLink == true){
                        if($(this).siblings('ul').length >0){
                            e.preventDefault();
                        }
                    }
                    // Auto close sibling menus
                    if(defaults.autoClose == true){
                        autoCloseAccordion($parentsLi, $parentsUl);
                    }
                    if ($('> ul',$activeLi).is(':visible')){
                        $('ul',$activeLi).slideUp(defaults.speed);
                        $('a',$activeLi).removeClass(defaults.classActive);
                    } else {
                        $(this).siblings('ul').slideToggle(defaults.speed);
                        $('> a',$activeLi).addClass(defaults.classActive);
                    }                    
                    // Write cookie if save state is on
                    if(defaults.saveState == true){
                        createCookie(defaults.cookie, obj);
                    }
                });
            }
            // Set up accordion
            function setUpAccordion(){
                $arrow = '<span class="'+defaults.classArrow+'"></span>';
                var classParentLi = defaults.classParent+'-li';
                $('> ul',obj).show();
                $('li',obj).each(function(){
                    if($('> ul',this).length > 0){                        $(this).addClass(classParentLi);
                        $('> a',this).addClass(defaults.classParent).append($arrow);
                    }
                });
                $('> ul',obj).hide();
                if(defaults.showCount == true){
                    $('li.'+classParentLi,obj).each(function(){
                        if(defaults.disableLink == true){
                            var getCount = parseInt($('ul a:not(.'+defaults.classParent+')',this).length);
                        } else {
                            var getCount = parseInt($('ul a',this).length);
                        }
                        $('> a',this).append(' <span class="'+defaults.classCount+'">('+getCount+')</span>');
                    });
                }
            }
            
            function linkOver(){

            $activeLi = $(this).parent('li');
            $parentsLi = $activeLi.parents('li');
            $parentsUl = $activeLi.parents('ul');

            // Auto close sibling menus
            if(defaults.autoClose == true){
                autoCloseAccordion($parentsLi, $parentsUl);

            }

            if ($('> ul',$activeLi).is(':visible')){
                $('ul',$activeLi).slideUp(defaults.speed);
                $('a',$activeLi).removeClass(defaults.classActive);
            } else {
                $(this).siblings('ul').slideToggle(defaults.speed);
                $('> a',$activeLi).addClass(defaults.classActive);
            }

            // Write cookie if save state is on
            if(defaults.saveState == true){
                createCookie(defaults.cookie, obj);
            }
        }

        function linkOut(){
        }

        function menuOver(){
        }

        function menuOut(){

            if(defaults.menuClose == true){
                $('ul',obj).slideUp(defaults.speed);
                // Reset active links
                $('a',obj).removeClass(defaults.classActive);
                createCookie(defaults.cookie, obj);
            }
        }

        // Auto-Close Open Menu Items
        function autoCloseAccordion($parentsLi, $parentsUl){
            $('ul',obj).not($parentsUl).slideUp(defaults.speed);
            // Reset active links
            $('a',obj).removeClass(defaults.classActive);
            $('> a',$parentsLi).addClass(defaults.classActive);
        }
        // Reset accordion using active links
        function resetAccordion(){
            $('ul',obj).hide();
            $allActiveLi = $('a.'+defaults.classActive,obj);
            $allActiveLi.siblings('ul').show();
        }
        });
        // Retrieve cookie value and set active items
        function checkCookie(cookieId, obj){
            var cookieVal = $.cookie(cookieId);
            if(cookieVal != null){
                // create array from cookie string
                var activeArray = cookieVal.split(',');
                $.each(activeArray, function(index,value){
                    var $cookieLi = $('li:eq('+value+')',obj);
                    $('> a',$cookieLi).addClass(defaults.classActive);
                    var $parentsLi = $cookieLi.parents('li');
                    $('> a',$parentsLi).addClass(defaults.classActive);
                });
            }
        }
        // Write cookie
        function createCookie(cookieId, obj){
            var activeIndex = [];
            // Create array of active items index value
            $('li a.'+defaults.classActive,obj).each(function(i){
                var $arrayItem = $(this).parent('li');
                var itemIndex = $('li',obj).index($arrayItem);
                    activeIndex.push(itemIndex);
                });
            // Store in cookie
            $.cookie(cookieId, activeIndex, { path: '/' });
        }
    };
})(jQuery);

I changed it a little bit so that a click on a main category only opens the submenus but doesn't trigger a site reload.

 

bm_categories.php:

function execute() {
      global $oscTemplate, $cPath;


      $data = '<div class="panel panel-default no-border hidden-xs hidden-sm">' .
              '  <div class="panel-heading gradient">' . MODULE_BOXES_CATEGORIES_BOX_TITLE . '</div>' .
              '  <div class="panel-body ContentBody">' . tep_show_tree_box() . '</div>' .
              '</div>';

      $oscTemplate->addBlock($data, $this->group);
    }

No changes!

 

user.css:

.nav-list>li /* To prevent selection of text */
{   position:relative;
    padding-left: 5px;
    -webkit-user-select: none; /* Chrome/Safari */        
    -moz-user-select: none; /* Firefox */
    -ms-user-select: none; /* IE10+ */
    /* Rules below not implemented in browsers yet */
    -o-user-select: none;
    user-select: none;
    cursor:pointer;
}
.submenu
{
    display:none;
}
.right-caret:after,.left-caret:after
 {  content:"";
    border: 5px solid transparent;
    border-top: 5px solid transparent;
    display: inline-block;
    height: 0;
    vertical-align: middle;
    width: 0;
    margin-left:15px;
}
.right-caret:after {   
    border-left: 5px solid #ffaf46;
}
.left-caret:after {
    border-right: 5px solid #ffaf46;
}
/* EOF CATEGORIES BOX */

No changes!

 

template_top.php after:

<script src="ext/jquery/jquery-1.11.0.min.js"></script>

add:

 <script src="ext/jquery/jquery.cookie.js"></script>
 <script src="ext/jquery/jquery.dcjqaccordion.2.7.min.js"></script>

or any other path where you put these js files to.........

 

 

Case 2: Hover function

In that case you need the additional jquery.hoverIntent.js which you also add to template_top.php.

 

In footer.php change the script to:

<script type="text/javascript">
$('#accordion').dcAccordion({
    eventType    : 'hover',    // Event for activating menu - options are "click" or "hover"
    hoverDelay   : 50,    // Hover delay for hoverIntent plugin
    menuClose   : false,    // If set "true" with event "hover" menu will close fully when mouseout
    autoClose    : true,    // If set to "true" only one sub-menu open at any time
    autoExpand   : false,    // If set to "true" all sub-menus of parent tags with class 'classExpand' will expand on page load
    speed         : 'slow',    // Speed of animation
    saveState    : true,    // Save menu state using cookies
    disableLink  : false,    // Disable all links of parent items
    showCount : false,    // If "true" will add a count of the number of links under each parent menu item
    cookie  : 'dcjq-accordion',    // Sets the cookie name for saving menu state - each menu instance on a single page requires a unique cookie name.
    saveState    : true    // Save menu state using cookies
});
$(".nav-list > li > a.trigger").on("mouseover",function(e){e.preventDefault();
var current=$(this).next();
var grandparent=$(this).parent().parent();
if($(this).hasClass('left-caret')||$(this).hasClass('right-caret'))$(this).toggleClass('right-caret left-caret');
grandparent.find('.left-caret').not(this).toggleClass('right-caret left-caret');
});
$(".nav-list > li > a:not(.trigger)").on("mouseover",function(){var root=$(this).closest('.nav-list');root.find('.left-caret').toggleClass('right-caret left-caret');});(jQuery);
</script>

The only thing, which I'm now searching for is a way to reset the jquery cookie in case I click on 'Home' or any other outside the categories nav. In that case the navigation should reset to default. :unsure:

 

I tested it on the latest BS version (osc 2.3.4) - for me it works fine!

 

J.J.

Tsimi likes this

Share this post


Link to post
Share on other sites

@@De Dokta

 

Thank you for finding/taking the time to test and improve this.

Yeah, navWrapper and the accordion part were there for testing things...forgot to remove. And don't forget to remove the $datas .='</div>'; part at the bottom of the code. ;)

Me on the other hand gave up after searching and searching...so i ask an expert in jquery/javascript for help and asked if there is a better way than
this over engineered way.
So we kicked in TeamViewer and I could be witness of something astonishing, live javascript coding. Never seen something like this,
The logic you need to have and the combinations....crazy!
Anyway we/HE came up with a tiny bit of js code and minor changes to the html_output.php code and a bit of css.
This javascript code adds a trigger point a glyphicon plus/minus icon next to the category name if sub categories are there to open or close the menu tree.
Of course you can also just click the category name to open or close the tree and it stays open when browsing the current category.
There are two ways to browse the category tree.
1. you click on the plus icons and you can open all sub trees and keep them open while opening an other tree
2. or you uncomment two lines of js code and the tree will close/collapse when clicking on the next category.

This way requires only changes to 3 files. This is only osC 2.3.3.4BS and 2.3.4BS compatible!

 

here is the html_output.php file

// BOF Categories Tree for Categories Box by De Dokta
function tep_show_tree_box($root_id = 0,$mainUlClass='navMain nav-list',$submenuUlClass='navMain nav-list collapse'){
    global $languages_id,$cPath_array, $datas;
    $categories_query = tep_db_query("select c.categories_id, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' order by sort_order, cd.categories_name");
    $items = array();
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    $citems=count($items);
    
    if($citems<=0) return '';
    elseif($citems==1) $children[] = $items; //in case we have one category item without subcategories, rare but possible
    else foreach( $items as $item ) $children[$item['parent_id']][] = $item;
        $loop = !empty( $children[$root_id] );
        $parent = $root_id;
        $parent_stack = array();
        $stack=array();//helper array so to know the current level
    $pic=''; //products_in_category string
	    
        // MAINMENU
		$datas .='<ul class="'.$mainUlClass.'">';
        while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ){
            if ( $option === false ){
                $parent = array_pop( $parent_stack );
                $datas .= '</ul>';
                $datas .= '</li>';
                array_pop( $stack );
            }elseif ( !empty( $children[$option['value']['id']] ) ){
				$stack[]=$option['value']['id'];
				$rt=$root_id>0 ? $root_id.'_' : '';
				$cpath_new=count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack);
				$datas .= '<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'">';
				$sm=0;        
				if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
					$sm=1;
					$datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
				}else{
					$datas .=stripslashes($option['value']['name']);
				}
				$datas .='</a><a href="#" class="switch"><span class="glyphicon glyphicon-plus-sign '.($sm==1?'glyphicon-minus-sign':'').' test"></span></a>';
				// SUBMENU
				$datas .= '<ul class="'.$submenuUlClass.' '.($sm==1?'in':'').' ">';
                $parent_stack[]=$option['value']['parent_id'];
                $parent = $option['value']['id'];
          }else{
				$rt=$root_id>0 ? $root_id.'_' : '';
                $cpath_new= count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack).'_'.$option['value']['id'];
                $datas .= '<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'" >';

                if (isset($cPath_array) && in_array($option['value']['id'], $cPath_array)) {
					$datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
				}else{
					$datas .=stripslashes($option['value']['name']);
				}
				$datas .='</a></li>';  //BACK ! it works like a charm
				//cool if you click the category name the other open tree collapses if you only click on the + sign the other tree stays open so two or even tree open trees at the same time
				// is this sometihng you want? i am not really sure. looks cool though. usually other tree closes if you click the next one what you think?
				//you can do some javascript to hide
        }
       }
        $datas .='</ul>';
        
      return $datas;
  }   
// BOF Categories Tree for Categories Box by De Dokta

here the js code inside the footer.php (please leave the credit inside it to honor the creater, thank you.)

<script type="text/javascript">
/*javascript coded by John Barounis, www.johnbarounis.com
Copyright © 2014 John Barounis*/
$(function(){

	$(".nav-list > li > a.switch, .nav-list > li > a.switch span").on("click",function(e){
	
		var jthis=$(this),mainUl=jthis.closest('div').find('ul:first'),sibs=jthis.closest('li').siblings();
	    //uncomment the two lines below to collapse the siblings (sub-sub-xxx categories)
		//sibs.find('ul.in').collapse('hide');
		//sibs.find('a.switch span').removeClass('glyphicon-minus-sign');
		
		jthis.closest('li').find('ul:first').collapse('toggle'); //here we toggle the clicked
		if(jthis.hasClass('glyphicon')) jthis.toggleClass('glyphicon-minus-sign');
		else $(this).find('span').toggleClass('glyphicon-minus-sign');
		
		return false;
	});

});
</script>

and the css definitions to not conflict with the real nav class from bootstrap i changed the name of the class

/* BOF CATEGORIES BOX */
.navMain {
  padding-left: 0;
  margin-bottom: 0;
  list-style: none;
}
.navMain > li {
  position: relative;
  display: block;
}
.navMain > li > a {
position: relative;
display: inline-block;
padding: 10px 15px;
}
.navMain > li > a:hover,
.navMain > li > a:focus {
  text-decoration: none;
  background-color: #eee;
}
.navMain > li.disabled > a {
  color: #999;
}
.navMain > li.disabled > a:hover,
.navMain > li.disabled > a:focus {
  color: #999;
  text-decoration: none;
  cursor: not-allowed;
  background-color: transparent;
}
.navMain .open > a,
.navMain .open > a:hover,
.navMain .open > a:focus {
  background-color: #eee;
  border-color: #428bca;
}
.navMain .nav-divider {
  height: 1px;
  margin: 9px 0;
  overflow: hidden;
  background-color: #e5e5e5;
}
/* EOF CATEGORIES BOX */

I take no credit for the js code! All credit for it goes to @@gadlol he did an amazing job! And amazing is understated.

Edited by Tsimi

Share this post


Link to post
Share on other sites

Wow! Where can I give John (gadlol) the deserved 'Like it' for that?

Edited by De Dokta
Denzel likes this

Share this post


Link to post
Share on other sites

Improved js

<script type="text/javascript">
/*javascript coded by John Barounis, www.johnbarounis.com
Copyright © 2014 John Barounis*/
$(function(){

    $(".nav-list > li > a.switch, .nav-list > li > a.switch span").on("click",function(e){
    
        var jthis=$(this);
        
        //uncomment the three lines below to collapse the siblings (sub-sub-xxx categories)
        //var sibs=jthis.closest('li').siblings();
        //sibs.find('ul.in').collapse('hide');
        //sibs.find('a.switch span').removeClass('glyphicon-minus-sign');
        
        jthis.closest('li').find('ul:first').collapse('toggle'); //here we toggle the clicked
        if(jthis.hasClass('glyphicon')) jthis.toggleClass('glyphicon-minus-sign');
        else $(this).find('span').toggleClass('glyphicon-minus-sign');
        
        return false;
    });

});
</script>
Tsimi, GLWalker, ArtcoInc and 1 other like this

Share this post


Link to post
Share on other sites

Hi

 

gadlols JS works perfectly, but the look of the categories menu needs some further cosmetics :D .

To align the +/- buttons and to get a nice hover bar I added another <div class="light"> to the function in html_output.php:

  function tep_show_tree_box($root_id = 0,$mainUlClass='navMain nav-list',$submenuUlClass='navMain nav-list collapse'){
    global $languages_id,$cPath_array, $datas;
    $categories_query = tep_db_query("select c.categories_id, c.categories_status, c.sort_order, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_status = '1' and c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' order by c.sort_order, cd.categories_name");
    $items = array();
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    $citems=count($items);
    
    if($citems<=0) return '';
    elseif($citems==1) $children[] = $items; //in case we have one category item without subcategories, rare but possible
    else foreach( $items as $item ) $children[$item['parent_id']][] = $item;
        $loop = !empty( $children[$root_id] );
        $parent = $root_id;
        $parent_stack = array();
        $stack=array();//helper array so to know the current level
    $pic=''; //products_in_category string
        
        // MAINMENU
        $datas .='<ul class="'.$mainUlClass.'">';
        while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ){
            if ( $option === false ){
                $parent = array_pop( $parent_stack );
                $datas .= '</ul>';
                $datas .= '</li>';
                array_pop( $stack );
            }elseif ( !empty( $children[$option['value']['id']] ) ){
                $stack[]=$option['value']['id'];
                $rt=$root_id>0 ? $root_id.'_' : '';
                $cpath_new=count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack);
                $datas .= '<li><div class="light"><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'">';
                $sm=0;        
                if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
                    $sm=1;
                    $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
                }else{
                    $datas .=stripslashes($option['value']['name']);
                }
                $datas .='<a href="" class="switch"><span class="glyphicon glyphicon-plus-sign '.($sm==1?'glyphicon-minus-sign':'').'"></span></a></div>';
                // SUBMENU
                $datas .= '<ul class="'.$submenuUlClass.' '.($sm==1?'in':'').' ">';
                $parent_stack[]=$option['value']['parent_id'];
                $parent = $option['value']['id'];
          }else{
                $rt=$root_id>0 ? $root_id.'_' : '';
                $cpath_new= count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack).'_'.$option['value']['id'];
                $datas .= '<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'" >';

                if (isset($cPath_array) && in_array($option['value']['id'], $cPath_array)) {
                    $datas .='<strong>'.stripslashes($option['value']['name']).'</strong>';
                }else{
                    $datas .=stripslashes($option['value']['name']);
                }
                $datas .='</a></li>';
        }
       }
        $datas .='</ul>';
        
      return $datas;
  }

Then I added this <div> also to the JS function in footer.php:

$(function(){$(".nav-list > li > div >a.switch, .nav-list > li > div >a.switch span").on("click",function(e){var sibs=$(this).closest('li').siblings();sibs.find('ul.in').collapse('hide');sibs.find('a.switch span').removeClass('glyphicon-minus-sign');$(this).closest('li').find('ul:first').collapse('toggle');if($(this).hasClass('glyphicon')) $(this).toggleClass('glyphicon-minus-sign');return false;});});

And at last some css in the user.css:

/* BOF CATEGORIES BOX */
.navMain {
  padding-left: 0;
  margin-bottom: 0;
  list-style: none;
}
.navMain > li {
  position: relative;
  display: block;
}
.navMain > li > a {
position: relative;
display: inline-block;
padding: 5px;
}
.navMain > li > a:hover,
.navMain > li > a:focus {
    background-color: #F9CFD2;
    color: #B70F43;
    border-radius: 5px;
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    -opera-border-radius: 5px;
    -khtml-border-radius: 5px;
  width: 100%;
}
.navMain > li.disabled > a {
  color: #999;
}
.navMain > li.disabled > a:hover,
.navMain > li.disabled > a:focus {
  color: #999;
  text-decoration: none;
  cursor: not-allowed;
  background-color: transparent;
}
.navMain .open > a,
.navMain .open > a:hover,
.navMain .open > a:focus {
  background-color: #eee;
  border-color: #428bca;
}
a.switch {
  float:right;
  margin-right:20px;
}
.light {
padding: 5px
}
.light:hover {
    background-color: #F9CFD2;
    color: #B70F43;
    border-radius: 5px;
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    -opera-border-radius: 5px;
    -khtml-border-radius: 5px;
}
.nav-list.collapse{
padding-left:20px;
}
 /* EOF CATEGORIES BOX */

If you like a smooth transition, the last entry in user.css should read:

.nav-list.collapse{
 padding-left:20px;
 transition: all 0.3s ease-out 0s;
  -webkit-transition: all 0.3s ease-out 0s;
  -moz-transition: all 0.3s ease-out 0s;
  -ms-transition: all 0.3s ease-out 0s;
  -o-transition: all 0.3s ease-out 0s;
}

(Thanks to Tsimi for that :) )

And the result looks like this:

 

post-315569-0-52858000-1413195257_thumb.jpg

 

J.J.
 

 

joli1811, burt and Tsimi like this

Share this post


Link to post
Share on other sites

@@De Dokta

 

I've just tried this in osC234bs, and received an error that there was no category_status field. I looked into the categories table in both 2334bs and 234bs, and there is no such field.

function tep_show_tree_box($root_id = 0,$mainUlClass='navMain nav-list',$submenuUlClass='navMain nav-list collapse'){
    global $languages_id,$cPath_array, $datas;
    $categories_query = tep_db_query("select c.categories_id, c.categories_status, c.sort_order, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_status = '1' and c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' order by c.sort_order, cd.categories_name");
    $items = array();
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    $citems=count($items);

...

So, I deleted it from the query (both places), and everything now works.

 

Was that field a part of the standard 2334 or 234 install, or was it added by another add-on? I don't see that field in @@gadlol 's original code.

 

Malcolm

Share this post


Link to post
Share on other sites

@@ArtcoInc

 

That field is inside the query because De Dokta uses sort of a Enable/Disable Categories function.

It is not a standard field and can be removed if not needed.

Share this post


Link to post
Share on other sites

For anyone else trying this code, I ran into an issue with the Categories box disappearing when the screen shrunk down, or was displayed on, say, a smart phone. After talking with @@Tsimi , it was pointed out that code used within catalog/includes/modules/boxes/bm_categories.php specifically hides the Categories box for the x-small and small displays:

function execute() {
global $oscTemplate, $cPath;

$data = '<div class="panel panel-default no-border hidden-xs hidden-sm">' .
' <div class="panel-heading gradient">' . MODULE_BOXES_CATEGORIES_BOX_TITLE . '</div>' .
' <div class="panel-body ContentBody">' . tep_show_tree_box() . '</div>' .
'</div>';

$oscTemplate->addBlock($data, $this->group);
}

To solve this, remove "hidden-xs" and "hidden-sm" :

function execute() {
global $oscTemplate, $cPath;

$data = '<div class="panel panel-default no-border">' .
' <div class="panel-heading gradient">' . MODULE_BOXES_CATEGORIES_BOX_TITLE . '</div>' .
' <div class="panel-body ContentBody">' . tep_show_tree_box() . '</div>' .
'</div>';

$oscTemplate->addBlock($data, $this->group);
}

HTH

 

Malcolm

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now