Jump to content



Photo
* * * * * 1 votes

tep_draw_categories_tree finally no recursion just one mysql_query

tep_draw_categories_tree function no recursion one mysql query

  • Please log in to reply
58 replies to this topic

#1   gadlol

gadlol
  • Members
  • 224 posts
  • Real Name:John Barounis
  • Gender:Male
  • Location:Greece

Posted 14 July 2014 - 17:22

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='&nbsp;(' . $products_in_category . ')';
            }
          }
          
          $sm=0;        
          
          if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
                    
            $sm=1;
          
            $html[]='<strong>'.stripslashes($option['value']['name']).'-&gt;'.$pic.'</strong>';
            
          }else{
          
            $html[]=stripslashes($option['value']['name']).'-&gt;'.$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='&nbsp;(' . $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

Check out the great Alternative Administration System addon for osCommerce!


#2   Gergely

Gergely

    Json Juggler

  • Community Team
  • 2,112 posts
  • Real Name:Gergely Tóth
  • Gender:Male
  • Location:Budapest

Posted 14 July 2014 - 19:32

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!


Next PHP changes will kill the current codes on the following years. We should do programing for the future and never stick in the present.

My addons: Conversion Tools::Hungarian Translation::Email Templates::URL redirection
 
Development Works: Setup Languages::Email Templates::Languages from ini files::Parcel Shops::Facebook App
 
What core codes have been complained?

In orders table payment_methods value would be better if payment class name used than payment's language name.
In the orders class we found order status does not contains $order->info['orders_status'] but instead there is $order->info['orders_status_name'], and that property is language dependant.
We can not identify in order the customer language.


#3   gadlol

gadlol
  • Members
  • 224 posts
  • Real Name:John Barounis
  • Gender:Male
  • Location:Greece

Posted 18 July 2014 - 16:41

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.alternati...og_treeview.png

 


Check out the great Alternative Administration System addon for osCommerce!


#4   Tsimi

Tsimi
  • Members
  • 1,752 posts
  • Real Name:Lambros
  • Gender:Male
  • Location:Japan

Posted 14 September 2014 - 09:57

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, 14 September 2014 - 09:59.


#5   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 14 September 2014 - 11:22

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/ind...>Cat_1</a></li>
      <li><a href="http://your_path/ind...>Cat_2</a></li>
      <li><a href="http://your_path/ind...>Cat_3</a></li>
      <li><a href="http://your_path/ind...>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/ind..._6</strong></a>
//cat with subs
         <ul class="nav nav-list">
           <li><a href="http://your_path/ind...bcat_1</a></li>
           <li><a href="http://your_path/ind...bcat_1</a></li>
           <li><a href="http://your_path/ind...bcat_1</a></li>
         </ul>                                                                                                                  
///end subcats
      </li>                                                                                                                       ///end Cat_6
           
      <li><a href="http://your_path/ind...>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.



#6   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 14 September 2014 - 12:48

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.

 



#7   Tsimi

Tsimi
  • Members
  • 1,752 posts
  • Real Name:Lambros
  • Gender:Male
  • Location:Japan

Posted 14 September 2014 - 16:26

@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, 14 September 2014 - 16:26.


#8   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 14 September 2014 - 17:31

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.



#9   Tsimi

Tsimi
  • Members
  • 1,752 posts
  • Real Name:Lambros
  • Gender:Male
  • Location:Japan

Posted 15 September 2014 - 15:46

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, 15 September 2014 - 15:46.


#10   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 15 September 2014 - 21:59

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, 15 September 2014 - 22:00.


#11   Tsimi

Tsimi
  • Members
  • 1,752 posts
  • Real Name:Lambros
  • Gender:Male
  • Location:Japan

Posted 16 September 2014 - 11:16

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.

 

 



#12   Tsimi

Tsimi
  • Members
  • 1,752 posts
  • Real Name:Lambros
  • Gender:Male
  • Location:Japan

Posted 16 September 2014 - 12:26

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

 

remove this

 

$datas .='</div>';



#13   Tsimi

Tsimi
  • Members
  • 1,752 posts
  • Real Name:Lambros
  • Gender:Male
  • Location:Japan

Posted 17 September 2014 - 06:02

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, 17 September 2014 - 06:12.


#14   Tsimi

Tsimi
  • Members
  • 1,752 posts
  • Real Name:Lambros
  • Gender:Male
  • Location:Japan

Posted 17 September 2014 - 06:18

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 */


#15   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 17 September 2014 - 08:56

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.



#16   Tsimi

Tsimi
  • Members
  • 1,752 posts
  • Real Name:Lambros
  • Gender:Male
  • Location:Japan

Posted 17 September 2014 - 10:32

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, 17 September 2014 - 10:34.


#17   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 17 September 2014 - 14:09

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.



#18   Tsimi

Tsimi
  • Members
  • 1,752 posts
  • Real Name:Lambros
  • Gender:Male
  • Location:Japan

Posted 17 September 2014 - 15:32

@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, 17 September 2014 - 15:41.


#19   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 17 September 2014 - 20:25

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


Edited by De Dokta, 17 September 2014 - 20:25.


#20   gadlol

gadlol
  • Members
  • 224 posts
  • Real Name:John Barounis
  • Gender:Male
  • Location:Greece

Posted 17 September 2014 - 21:01

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>

Check out the great Alternative Administration System addon for osCommerce!