#!/usr/bin/perl
# Navbar plugin by Tobi Oetiker <tobi at oetiker dot ch>
# based on Sidebar Plugin by Tuomo Valkonen <tuomov at iki dot fi>
# Adapted to by own needs by Martin Quinson (not sure what the changes are over the time)

####
# The Navbar Plugin expects to find a page called "navbar" see http://ikiwiki.kitenet.net/plugins/sidebar.html
# for details
#
# This page must contain a itemized list of the form
#
# * [[Welcome|index]]
# * [[Management|mgmt]]
#   * [[Leadership|mgmt/lead]]
#   * [[Kidnapping|mgmt/kidnapping]]
# * [[Information_Technology|it]]
#   * [[Windows|it/windows]]
#   * [[Mobile_Communication|it/mobile]]
#
# This list will be turned into a folding menu structure
#
# Include this into your templates.
#
# <TMPL_IF NAVBAR>
# <div id="navbar">
# <TMPL_VAR NAVBAR>
# </div>
# </TMPL_IF>
#
# To make a nice menu, some css magic is required, but since this is required to make
# ikiwiki look good anyway, I won't go into details here ... 
#
# Tobi Oetiker 2006.12.30    
    

package IkiWiki::Plugin::navbar;

use warnings;
use strict;
use IkiWiki;
use POSIX qw(strftime);

sub import { #{{{
	hook(type => "pagetemplate", id => "navbar", call => \&pagetemplate);
} # }}}


sub pagetemplate (@) { #{{{
    my %params=@_;
    my $page=$params{page};
    my $template=$params{template};
    if ($template->query(name => "navbar")) {
	my $content=navbar_content($page);
	if (defined $content && length $content) {
	    $template->param(navbar => $content);
	}
    }
    $template->param(mypage => $page); # this is unrelated: I need to have that info from the template mechanism, and I didn't felt like having a specific plugin for one line only
} # }}}

my $debug = 0;

######
# Highlights the selected elements of the menu tree

sub menu_highlight_selected_entry($);
sub menu_highlight_selected_entry($){ # {{{
    # returns true iff we are on the path to selected (and should show sibilings)
    my $entry = shift;
    die "class undefined for entry ".$entry->{'title'}.""
       unless defined($entry->{'class'});
    # current entry is the selected element
    if ($entry->{'class'} eq "selected") {
	# show (ie, un-hide) childs (if any)
	for (my $i=0; $i<$entry->{'size'}; $i++) {
	    $entry->{"$i"}->{'class'} = "";
	}
	return 1;
    }
    # else, search in childrens
    for (my $i=0; $i<$entry->{'size'}; $i++) {
	if (menu_highlight_selected_entry($entry->{"$i"})) {
	    # That child is on path to selected!
	    # Remove "hidden" marker from all my childs
	    for (my $j=0; $j<$entry->{'size'}; $j++) {
		$entry->{"$j"}->{'class'} = "";
	    }

	    # Mark the golden child as selected
	    $entry->{"$i"}->{'class'} = "selected";

	    # Bail out to report the good news
	    return 1;
	}
    }
    # none of my childs are on the golden path. Too bad
    return 0;
} # }}}

#####
# Reads the menu from the navbar file, create the tree structure, and highlight the path to the page

sub parse_menu($$);
sub parse_menu($$) { #{{{
    my $txt = shift;
    my $rendered_page = shift;
    my $menu;
    $menu->{'size'} = 0;
    $menu->{'class'} = "ignored";
    $menu->{'title'} = "(root)";
    my $lvl =0;
    foreach my $line (split(/\n/,$txt)) {
	next unless ($line =~ m/\S/);
	unless ($line =~ m/^( *)o(.*)/) {
	    print STDERR "Ignoring unparsable line: $line\n";
	    next;
	}
	my $newlvl = length($1);
	my $entrytxt = $2;
#	$entrytxt =~ s|<span(.*?)</a>||;$entrytxt =~ s|</span>||;

	# if in a sub-entry, find the right one
	my $entry = $menu;
	for (my $i = 0 ; $i<$newlvl/2; $i++) {
	    $entry = $entry->{$entry->{'size'}-1};
#	    print STDERR "entry becomes ".$entry->{'title'}."\n";
	}
	
	# Add the new child to the parent sub-entry
	$entry->{$entry->{'size'}}->{'size'} = 0;
	$entry->{$entry->{'size'}}->{'class'} = "hidden";
	$entry->{$entry->{'size'}}->{'title'} = $entrytxt;
	$entry->{'size'}++;

	if ($entrytxt =~ m|<span class="selflink">|) {
	    $entry->{$entry->{'size'}-1}->{'class'} = "selected";
	}

    }

    # Search the selected entry
    unless (menu_highlight_selected_entry($menu)) {
	# if we are in a blog post, highlight what should be
	if ($rendered_page =~ m|^blog/|) {
	    for (my $i=0; $i<$menu->{"$i"}->{'size'}; $i++) {
		$menu->{"$i"}->{'class'} = ""; 
		if ($menu->{"$i"}->{'title'} =~ m/>Blog</i) {
		    $menu->{"$i"}->{'class'} = "selected";
 		    for (my $j=0; $j<$menu->{"$i"}->{"$j"}->{'size'}; $j++) {
			$menu->{"$i"}->{"$j"}->{'class'} = ""; 			
		    }
		} 
	    }
	    
	} else { # if none (known) entry is selected, show the level 1 childs
	    for (my $i=0; $i<$menu->{"$i"}->{'size'}; $i++) {
		$menu->{"$i"}->{'class'} = "";
	    }
	}
	
    }
    menu_dump($menu) if $debug;

    return menu_format_by_level($menu);
} # }}}

######
# Recursively format the menu, so that childs are displayed right after the ancestor

sub menu_format_rec($$);
sub menu_format_rec($$) { # {{{
    my $menu = shift;
    my $level = shift ||0;
    my $indent="";
    my $res = "";

    for (my $i=0;$i<$level;$i++) {
	$indent .= "  ";
    }
    if ($level==0) {
	$res .= "<ul>\n";
    }
    if ($level>0) { # Ignore (root)
	$res .= "$indent<li class=\"".$menu->{'class'}."\">";
	$res .= $menu->{'title'};
	if (($menu->{'size'}>0)) {
	    $res .= "<ul>\n";
	}
    }
    for (my $i=0;$i< ($menu->{'size'}||0);$i++) {
	$res .= menu_format($menu->{"$i"},$level+1);
    }
    if ($menu->{'size'}>0) {
	$res .= "$indent</ul>";
    }
    if ($level == 0) {
	$res .= "\n";
    } else {
	$res .= "</li>\n";
    }
    return $res;
} # }}}

#######
# Format the menu so that each level are displayed one after the other

sub menu_format_by_level($);
sub menu_format_by_level($) { # {{{
    my $selected_item=shift;
    my $menu;
    my $level=0;
    my $res = "";

    while (defined($selected_item)) {
	last unless $selected_item->{'size'} >0;
	$res .= "<!-- level $level entries -->\n";
	$res .= "<div class=\"tabs\">\n";
	$res .= " <ul>\n";
	$menu = $selected_item;
	$selected_item=undef;
	for (my $i=0;$i< ($menu->{'size'}||0);$i++) {
	    $res .= "  <li";
	    $res .= " class=\"".$menu->{"$i"}->{'class'}."\""
		unless (!defined($menu->{"$i"}->{'class'}) || 
			!length($menu->{"$i"}->{'class'}));
	    $res .= ">";
	    if ($menu->{"$i"}->{'title'} =~ m|(<a[^>]*>)(.*?)(</a>)|) {
		$res .= "$1<span>$2</span></a></li>\n";
	    } elsif ($menu->{"$i"}->{'title'} =~ m|<span class="selflink">(.*?)</span>|) {
		$res .= "<a href=\"#\"><span>$1</span></a></li>\n";
	    } else {
		die "Unable to parse title: ".$menu->{"$i"}->{'title'};
	    }
	    if ($menu->{"$i"}->{'class'} eq "selected") {
		$selected_item = $menu->{"$i"};
	    }
	}
	$res .= " </ul>\n</div>\n";
	$level++;
    }
    return $res;
} # }}}

sub navbar_content ($) { #{{{
    my $page=shift;
    my $navbar_page=bestlink($page, "navbar") || return;
    my $navbar_file=$pagesources{$navbar_page} || return;
    my $navbar_type=pagetype($navbar_file);
    
    if (defined $navbar_type) {
	# FIXME: This isn't quite right; it won't take into account
	# adding a new navbar page. So adding such a page
	# currently requires a wiki rebuild.
	add_depends($page, $navbar_page);
	
	print STDERR "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX page: $page (navbar: $navbar_page)\n" if $debug;
	
	my $content=readfile(srcfile($navbar_file));
	return unless length $content;
	
	my $filter = IkiWiki::filter($navbar_page, $page, $content);
	my $preproc =  IkiWiki::preprocess($navbar_page, $page, $filter);
	
	my $linkify = IkiWiki::linkify($navbar_page, $page, $preproc);
	print STDERR "linkify:\n$linkify\n" if $debug;
	
	my $html = parse_menu($linkify,$page);
	print STDERR "html:\n$html\n" if $debug;	

	my $menu = IkiWiki::htmlize($navbar_page, $page, $navbar_type,$html);

	$menu =~ s|<p>(.*)</p>|$1|s;
	print STDERR "menu:\n$menu\n" if $debug;
	
	
	print STDERR "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" if $debug;

	if ($menu =~ m|span class="createlink">(.*?)</span>|) {
	    my $page = $1;
	    $page =~ s/<[^>]*>//g;
	    $page =~ s/^\?//;
	    printf STDERR "Error: Non-existant page listed from the navbar: $page\n";
	    exit (1);
	}
	return "<!-- The navbar -->\n".
	    "<div class=\"navbar\">\n$menu</div>\n".
	    "<span class=\"endNavbar\"></span>\n".
	    "<!-- end of navbar -->\n";
    }
    
} # }}}



###############
# sub menu_dump
#  debugging purpose
sub menu_dump{
  my $menu=shift;
  my $level=shift||0;

  print STDERR "\n------ DUMP MENU (begin)\n" if $level == 0;
  for (my $i=0;$i<$level;$i++) {
      print STDERR "  ";
  }
  if (!defined($menu->{'size'})) {
      print STDERR "()\n <par*>";
  } else {
      print STDERR $menu->{'title'}.
	           " (size=".$menu->{'size'}.
		   "; class=".$menu->{'class'}.").\n";
      for (my $i=0;$i< ($menu->{'size'}||0);$i++) {
	  menu_dump($menu->{"$i"},$level+1);
      }
  }
  print STDERR "------ DUMP MENU (end)\n" if $level == 0;
}




1
