1package Pisg::HTMLGenerator;
2
3# $Id: HTMLGenerator.pm,v 1.189 2007/09/07 23:17:23 df7cb Exp $
4#
5# Copyright and license, as well as documentation(POD) for this module is
6# found at the end of the file.
7
8use strict;
9$^W = 1;
10
11# test for Text::Iconv
12my $have_iconv = 1;
13eval 'use Text::Iconv';
14$have_iconv = 0 if $@;
15
16sub new
17{
18    my $type = shift;
19    my %args = @_;
20    my $self = {
21        cfg => $args{cfg},
22        stats => $args{stats},
23        users => $args{users},
24        tmps => $args{tmps},
25        topactive => {},
26    };
27
28    # Import common functions in Pisg::Common
29    require Pisg::Common;
30    Pisg::Common->import();
31
32    bless($self, $type);
33    return $self;
34}
35
36sub create_output
37{
38    # This subroutine calls all the subroutines which create their
39    # individual stats. The name of the functions is somewhat saying - if
40    # you don't understand it, most subs have a better explanation in the
41    # sub itself.
42
43    my $self = shift;
44    $self->{cfg}->{lang} = shift;
45
46    # save table width as multiplie files would just increase tablewidth
47    my $tablewidth_original = $self->{cfg}->{tablewidth};
48
49    # remove old iconv if it exist as it could mess up recode.
50    delete $self->{iconv} if $self->{iconv};
51
52    my $lang_charset = $self->{tmps}->{$self->{cfg}->{lang}}{lang_charset};
53    if($lang_charset and $lang_charset ne $self->{cfg}->{charset}) {
54        if($have_iconv) {
55            # convert from template charset to our
56            $self->{iconv} = Text::Iconv->new($lang_charset, $self->{cfg}->{charset});
57        } else {
58            print "Text::Iconv is not installed, skipping charset conversion of language templates\n"
59                unless ($self->{cfg}->{silent});
60        }
61    }
62
63    $self->_topactive();
64
65    if($self->{cfg}->{bignumbersthreshold} =~ /^sqrt/) {
66        $self->{cfg}->{bignumbersthreshold} = int(sqrt($self->{stats}->{topactive_lines}));
67    }
68
69    my $fname = $self->{cfg}->{outputfile};
70    $fname =~ s/\%t/$self->{cfg}->{outputtag}/g;
71    $fname =~ s/\%l/$self->{cfg}->{lang}/g;
72    print "Now generating HTML ($self->{cfg}->{lang}) in $fname...\n"
73        unless ($self->{cfg}->{silent});
74
75    open (OUTPUT, "> $fname") or
76        die("$0: Unable to open outputfile($fname): $!\n");
77
78    if ($self->{cfg}->{showlines}) {
79        $self->{cfg}->{tablewidth} += 40;
80    }
81    if ($self->{cfg}->{showtime}) {
82        $self->{cfg}->{tablewidth} += 40;
83    }
84    if ($self->{cfg}->{showlinetime}) {
85        $self->{cfg}->{tablewidth} += 100;
86    }
87    if ($self->{cfg}->{showwordtime}) {
88        $self->{cfg}->{tablewidth} += 100;
89    }
90    if ($self->{cfg}->{showwords}) {
91        $self->{cfg}->{tablewidth} += 40;
92    }
93    if ($self->{cfg}->{showwpl}) {
94        $self->{cfg}->{tablewidth} += 40;
95    }
96    if ($self->{cfg}->{showcpl}) {
97        $self->{cfg}->{tablewidth} += 40;
98    }
99    if ($self->{cfg}->{userpics}) {
100        $self->{cfg}->{tablewidth} += $self->{cfg}->{userpics} * ($self->{cfg}->{picwidth} || 60);
101    }
102    $self->{cfg}->{headwidth} = $self->{cfg}->{tablewidth} - 4;
103    $self->_htmlheader();
104    $self->_pageheader()
105        if ($self->{cfg}->{pagehead} ne 'none');
106
107    if ($self->{cfg}->{dailyactivity}) {
108        $self->_activedays();
109    }
110
111    if ($self->{cfg}->{showactivetimes}) {
112        $self->_activetimes();
113    }
114
115    if ($self->{cfg}->{showactivenicks}) {
116        $self->_activenicks();
117    }
118
119    if ($self->{cfg}->{showmostactivebyhour}) {
120        $self->_mostactivebyhour();
121    }
122
123    if ($self->{cfg}->{showbignumbers}) {
124        $self->_headline($self->_template_text('bignumtopic'));
125        _html("<table width=\"$self->{cfg}->{tablewidth}\">"); # Needed for sections
126        $self->_questions();
127        $self->_shoutpeople();
128        $self->_capspeople();
129        $self->_violent();
130        $self->_mostsmiles();
131        $self->_mostsad();
132        $self->_linelengths();
133        $self->_mostwords();
134        $self->_mostwordsperline();
135        _html("</table>"); # Needed for sections
136    }
137
138    if ($self->{cfg}->{showmostnicks}) {
139        $self->_mostnicks();
140    }
141
142    if ($self->{cfg}->{showactivegenders}) {
143        $self->_activegenders();
144    }
145
146    if ($self->{cfg}->{showmuw}) {
147        $self->_mostusedword();
148    }
149
150    if ($self->{cfg}->{showmrn}) {
151        $self->_mostreferencednicks();
152    }
153
154    if ($self->{cfg}->{showsmileys}) {
155        $self->_smileys();
156    }
157
158    if ($self->{cfg}->{showkarma}) {
159        $self->_karma();
160    }
161
162    if ($self->{cfg}->{showmru}) {
163        $self->_mosturls();
164    }
165
166    if ($self->{cfg}->{showcharts}) {
167        $self->_charts();
168    }
169
170    if ($self->{cfg}->{showbignumbers}) {
171        $self->_headline($self->_template_text('othernumtopic'));
172        _html("<table width=\"$self->{cfg}->{tablewidth}\">"); # Needed for sections
173        $self->_gotkicks();
174        $self->_mostkicks();
175        $self->_mostop() if $self->{cfg}->{showops};
176        $self->_mosthalfop() if $self->{cfg}->{showhalfops};
177        $self->_mostvoice() if $self->{cfg}->{showvoices};
178        $self->_mostactions();
179        $self->_mostmonologues();
180        $self->_mostjoins();
181        $self->_mostfoul();
182        _html("</table>"); # Needed for sections
183    }
184
185    if ($self->{cfg}->{showtopics}) {
186        $self->_headline($self->_template_text('latesttopic'));
187        _html("<table width=\"$self->{cfg}->{tablewidth}\">"); # Needed for sections
188
189        $self->_lasttopics();
190
191        _html("</table>"); # Needed for sections
192    }
193
194    my %hash = ( lines => $self->{stats}->{parsedlines} );
195    _html($self->_template_text('totallines', %hash) . "<br /><br />");
196
197    $self->_pagefooter()
198        if ($self->{cfg}->{pagefoot} ne 'none');
199
200    $self->_htmlfooter();
201
202    close(OUTPUT);
203
204    # restore tablewidth
205    $self->{cfg}->{tablewidth} = $tablewidth_original;
206}
207
208sub _htmlheader
209{
210    my $self = shift;
211    my %hash = (
212        network    => $self->{cfg}->{network},
213        maintainer => $self->{cfg}->{maintainer},
214        days       => $self->{stats}->{days},
215        nicks      => scalar keys %{ $self->{stats}->{lines} }
216    );
217
218    my $CSS;
219    if($self->{cfg}->{colorscheme} =~ /([^\/.]+)\.[^\/]+$/) { # use external CSS file
220        $CSS = "<link rel=\"stylesheet\" type=\"text/css\" title=\"$1\" href=\"$self->{cfg}->{colorscheme}\" />";
221    } elsif($self->{cfg}->{colorscheme} ne "none") { # read the chosen CSS file
222        my $css_file = $self->{cfg}->{cssdir} . $self->{cfg}->{colorscheme} . ".css";
223        open(FILE, $css_file) or open (FILE, $self->{cfg}->{search_path} . "/$css_file") or die("$0: Unable to open stylesheet $css_file: $!\n");
224        {
225            local $/; # enable "slurp" mode
226            $CSS = "<style type=\"text/css\" title=\"$self->{cfg}->{colorscheme}\">\n". <FILE>. "</style>";
227			$CSS =~ s/\/\*/\/\* <!--/g;
228			$CSS =~ s/\*\//--> \*\//g;
229        }
230        close FILE;
231    }
232
233    # use alternate CSS file
234    if($self->{cfg}->{altcolorscheme} ne "none" and $self->{cfg}->{altcolorscheme} =~ /[^\w]/) {
235        foreach (split /\s+/, $self->{cfg}->{altcolorscheme}) {
236            /([^\/.]+)\.[^\/]+$/;
237            $CSS .= "\n<link rel=\"alternate stylesheet\" type=\"text/css\" title=\"$1\" href=\"$_\" />";
238        }
239    }
240
241    my $title = $self->_template_text('pagetitle1', %hash);
242    if($self->{cfg}->{colorscheme} ne "none") {
243        _html( <<HTML );
244<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
245<html>
246<head>
247<meta http-equiv="Content-Type" content="text/html; charset=$self->{cfg}->{charset}" />
248<title>$title</title>
249$CSS
250</head>
251<body>
252<div align="center">
253HTML
254    }
255    _html("<span class=\"title\">$title</span><br />");
256    _html("<br />");
257    _html($self->_template_text('pagetitle2', %hash) . " " . $self->get_time());
258
259    _html("<br />" . $self->_template_text('pagetitle3', %hash) . "<br /><br />");
260
261}
262
263sub get_time
264{
265    my $self = shift;
266    my ($tday, %hash);
267
268    my $month = $self->_template_text('month', %hash);
269    my $day = $self->_template_text('day', %hash);
270
271    my @month = split(" ", $month);
272    my @day = split(" ", $day);
273
274    # Get the Date from the users computer
275    my $timezone = $self->{cfg}->{timeoffset} * 3600;
276    my ($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime(time+$timezone);
277
278    $year += 1900;                    # Y2K Patch
279
280    $min =~ s/^(.)$/0$1/;             # Fixes the display of mins/secs below
281    $sec =~ s/^(.)$/0$1/;             # it displays 03 instead of 3
282
283    if ($hour > '23') {               # Checks to see if it Midnight
284        $hour = 12;                   # Makes it display the hour 12
285        $tday = "AM";                 # Display AM
286    } elsif($hour > '12') {           # Get rid of the Military time and
287        $hour -= 12;                  # put it into normal time
288        $tday = "PM";                 # If past Noon and before Midnight set
289    } else {
290        $tday = "AM";                 # If it's past Midnight and before Noon
291    }                                 # set the time as AM
292
293    # Use 24 hours pr. day
294    if ($tday eq "PM" && $hour < '12') {
295        $hour += 12;
296    }
297
298    return "$day[$wday] $mday $month[$mon] $year - $hour:$min:$sec";
299}
300
301
302sub _htmlfooter
303{
304    my $self = shift;
305
306    my %hash;
307
308    my $pisg_hp = $self->_template_text('pisghomepage');
309    $hash{pisg_url} = "<a href=\"http://pisg.sourceforge.net/\" title=\"$pisg_hp\" class=\"background\">pisg</a>";
310
311    my $author_hp = $self->_template_text('authorhomepage');
312    $hash{author_url} = "<a href=\"http://mbrix.dk/\" title=\"$author_hp\" class=\"background\">Morten Brix Pedersen</a>";
313
314    $hash{version} = $self->{cfg}->{version};
315
316    my $hours = $self->_template_text('hours');
317    my $mins = $self->_template_text('minutes');
318    my $secs = $self->_template_text('seconds');
319    my $and = $self->_template_text('and');
320
321    my $h = $self->{stats}->{processtime}{hours};
322    my $m = $self->{stats}->{processtime}{mins};
323    my $s = $self->{stats}->{processtime}{secs};
324    $hash{time} = "$h $hours $m $mins $and $s $secs";
325
326    my $stats_gen = $self->_template_text('stats_gen_by', %hash);
327    my $author_text = $self->_template_text('author', %hash);
328    my $stats_text = $self->_template_text('stats_gen_in', %hash);
329
330    _html( <<HTML );
331<span class="small">
332$stats_gen<br />
333$author_text<br />
334$stats_text
335</span>
336HTML
337
338    _html( sprintf( qq(<!-- NFiles = "%s"; Format = "%s"; Lang = "%s"; LangFile = "%s"; Charset = "%s"; LogCharset = "%s"; LogCharsetFallback = "%s"; LogPrefix = "%s"; LogSuffix = "%s"; NickTracking = "%s"; TimeOffset = "%s" -->),
339        $self->{cfg}->{nfiles},
340        $self->{cfg}->{format},
341        $self->{cfg}->{lang},
342        $self->{cfg}->{langfile},
343        $self->{cfg}->{charset},
344        $self->{cfg}->{logcharset},
345        $self->{cfg}->{logcharsetfallback},
346        $self->{cfg}->{logprefix},
347        $self->{cfg}->{logsuffix},
348        $self->{cfg}->{nicktracking},
349        $self->{cfg}->{timeoffset}
350    ));
351
352    if($self->{cfg}->{colorscheme} ne "none") {
353        _html( <<HTML );
354</div>
355</body>
356</html>
357HTML
358    }
359}
360
361sub _headline
362{
363    my $self = shift;
364    my ($title) = (@_);
365    _html( <<HTML );
366   <br />
367   <table width="$self->{cfg}->{headwidth}" cellpadding="1" cellspacing="0" border="0">
368    <tr>
369     <td class="headlinebg">
370      <table width="100%" cellpadding="2" cellspacing="0" border="0">
371       <tr>
372        <td class="headtext">$title</td>
373       </tr>
374      </table>
375     </td>
376    </tr>
377   </table>
378HTML
379}
380
381sub _pageheader
382{
383    my $self = shift;
384    unless(open(PAGEHEAD, $self->{cfg}->{pagehead})) {
385        warn("$0: Unable to open $self->{cfg}->{pagehead} for reading: $!\n");
386        return;
387    }
388    while (<PAGEHEAD>) {
389        chomp;
390        _html($_);
391    }
392    close(PAGEHEAD);
393}
394
395sub _pagefooter
396{
397    my $self = shift;
398    unless(open(PAGEFOOT, $self->{cfg}->{pagefoot})) {
399        warn("$0: Unable to open $self->{cfg}->{pagefoot} for reading: $!\n");
400        return;
401    }
402    while (<PAGEFOOT>) {
403        chomp;
404        _html($_);
405    }
406    close(PAGEFOOT);
407}
408
409sub _activedays
410{
411    # The most actives days on the channel
412    my $self = shift;
413    my $days = $self->{stats}->{days};
414    my $ndays = $self->{cfg}->{dailyactivity};
415
416    my $highest_value = 1;
417    for (my $day = $days; $day > $days - $ndays ; $day--) {
418        if (defined($self->{stats}->{day_lines}->[$day])) {
419            if ($self->{stats}->{day_lines}->[$day] > $highest_value) {
420                $highest_value = $self->{stats}->{day_lines}->[$day];
421            }
422        } else {
423            #there are only $days - $day days :)
424            $ndays = $days - $day;
425            last;
426        }
427    }
428
429    my %hash = (
430        n => $ndays
431    );
432    $self->_headline($self->_template_text('dailyactivitytopic', %hash));
433
434    _html("<table border=\"0\"><tr>");
435
436    for (my $day = $days - $ndays + 1; $day <= $days ; $day++) {
437        my $lines = $self->{stats}->{day_lines}[$day];
438        _html("<td align=\"center\" valign=\"bottom\" class=\"asmall\">$lines<br />");
439        for (my $time = 4; $time >= 0; $time--) {
440            if (defined($self->{stats}->{day_times}[$day][$time])) {
441                my $size = int(($self->{stats}->{day_times}[$day][$time] / $highest_value) * 100);
442
443                my $image = "pic_v_".$time*6;
444                $image = $self->{cfg}->{$image};
445                _html("<img src=\"$self->{cfg}->{piclocation}/$image\" width=\"15\" height=\"$size\" alt=\"$size\" title=\"$size\" /><br />") if $size;
446
447            }
448        }
449        _html("</td>");
450    }
451
452    _html("</tr><tr>");
453
454    for (my $day = $ndays - 1; $day >= 0 ; $day--) {
455        _html("<td class=\"rankc10center\" align=\"center\">$day</td>");
456    }
457
458    _html("</tr></table>");
459
460    if($self->{cfg}->{showlegend} == 1) {
461        $self->_legend();
462    }
463}
464
465sub _activetimes
466{
467    # The most actives times on the channel
468    my $self = shift;
469
470    my (%output);
471
472    $self->_headline($self->_template_text('activetimestopic'));
473
474    my @toptime = sort { $self->{stats}->{times}{$b} <=> $self->{stats}->{times}{$a} } keys %{ $self->{stats}->{times} };
475
476    my $highest_value = $self->{stats}->{times}{$toptime[0]};
477
478    for my $hour (sort keys %{ $self->{stats}->{times} }) {
479
480        my $size = int(($self->{stats}->{times}{$hour} / $highest_value) * 100);
481        my $percent = sprintf("%.1f", ($self->{stats}->{times}{$hour} / $self->{stats}->{parsedlines}) * 100);
482        my $lines_per_hour = $self->{stats}->{times}{$hour};
483
484        my $image = "pic_v_".(int($hour/6)*6);
485        $image = $self->{cfg}->{$image};
486
487        $output{$hour} = "<td align=\"center\" valign=\"bottom\" class=\"asmall\">$percent%<br /><img src=\"$self->{cfg}->{piclocation}/$image\" width=\"15\" height=\"$size\" alt=\"$lines_per_hour\" title=\"$lines_per_hour\"/></td>" if $size;
488    }
489
490    _html("<table border=\"0\"><tr>");
491
492    for ($b = 0; $b < 24; $b++) {
493        $a = sprintf("%02d", $b);
494
495        if (!defined($output{$a})) {
496            _html("<td align=\"center\" valign=\"bottom\" class=\"asmall\">0%</td>");
497        } else {
498            _html($output{$a});
499        }
500    }
501
502    _html("</tr><tr>");
503
504    # Remove leading zero
505    $toptime[0] =~ s/^0//;
506
507    for ($b = 0; $b < 24; $b++) {
508        # Highlight the top time
509        my $class = $toptime[0] == $b ? 'hirankc10center' : 'rankc10center';
510        _html("<td class=\"$class\" align=\"center\">$b</td>");
511    }
512
513    _html("</tr></table>");
514
515    if($self->{cfg}->{showlegend} == 1) {
516        $self->_legend();
517    }
518}
519
520sub _activenicks
521{
522    # The most active nicks (those who wrote most lines)
523    my $self = shift;
524
525    $self->_headline($self->_template_text('activenickstopic'));
526
527    my $output = "";
528    $output .= "<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>";
529    $output .= "<td>&nbsp;</td>";
530    $output .= "<td class=\"tdtop\"><b>" . $self->_template_text('nick') . "</b></td>";
531
532    $output .= "<td class=\"tdtop\"><b>" . $self->_template_text('numberlines') . "</b></td>"   if ($self->{cfg}->{showlines});
533    $output .= "<td class=\"tdtop\"><b>" . $self->_template_text('show_time') . "</b></td>"     if ($self->{cfg}->{showtime});
534    $output .= "<td class=\"tdtop\"><b>" . $self->_template_text('show_words') . "</b></td>"    if ($self->{cfg}->{showwords});
535    $output .= "<td class=\"tdtop\"><b>" . $self->_template_text('show_wpl') . "</b></td>"      if ($self->{cfg}->{showwpl});
536    $output .= "<td class=\"tdtop\"><b>" . $self->_template_text('show_cpl') . "</b></td>"      if ($self->{cfg}->{showcpl});
537    $output .= "<td class=\"tdtop\"><b>" . $self->_template_text('show_lastseen') . "</b></td>" if ($self->{cfg}->{showlastseen});
538    $output .= "<td class=\"tdtop\"><b>" . $self->_template_text('randquote') . "</b></td>"     if ($self->{cfg}->{showrandquote});
539
540    $output .= "\n";
541
542    my @active;
543    my $nicks;
544    if ($self->{cfg}->{sortbywords}) {
545        @active = sort { $self->{stats}->{words}{$b} <=> $self->{stats}->{words}{$a} } keys %{ $self->{stats}->{words} };
546        $nicks = scalar keys %{ $self->{stats}->{words} };
547    } else {
548        @active = sort { $self->{stats}->{lines}{$b} <=> $self->{stats}->{lines}{$a} } keys %{ $self->{stats}->{lines} };
549        $nicks = scalar keys %{ $self->{stats}->{lines} };
550    }
551
552    if ($self->{cfg}->{activenicks} > $nicks) { $self->{cfg}->{activenicks} = $nicks; }
553
554    for (my $c = 0; $c < $self->{cfg}->{activenicks}; $c++) {
555        last unless $self->{cfg}->{userpics};
556        my $nick = $active[$c];
557        if ($self->{users}->{userpics}{$nick} or $self->{cfg}->{defaultpic}) {
558            $output .= "<td class=\"tdtop\"";
559            $output .= " colspan=\"$self->{cfg}->{userpics}\""  if ($self->{cfg}->{userpics} > 1);
560            $output .= "><b>" . $self->_template_text('userpic') ."</b></td>";
561            last;
562        }
563    }
564
565    $output .= "\n</tr>";
566    _html($output);
567    undef $output;
568
569    for (my $i = 0; $i < $self->{cfg}->{activenicks}; $i++) {
570        my $c = $i + 1;
571        my $nick = $active[$i];
572        my $visiblenick;
573
574        my $randomline;
575        if (not defined $self->{stats}->{sayings}{$nick}) {
576            if ($self->{stats}->{actions}{$nick}) {
577                $randomline = $self->{stats}->{actionlines}{$nick};
578            } else {
579                $randomline = "";
580            }
581        } else {
582            $randomline = $self->{stats}->{sayings}{$nick};
583        }
584
585        if ($randomline) {
586            $randomline = $self->_format_line($randomline);
587        }
588
589        # Add a link to the nick if there is any and quote nick
590        if ($self->{users}->{userlinks}{$nick}) {
591            $visiblenick = $self->_format_word($self->{users}->{userlinks}{$nick}, $nick);
592        } else {
593            $visiblenick = $self->_format_word($nick);
594        }
595
596        my $style = $self->generate_colors($c);
597
598        my $class = 'rankc';
599        if ($c == 1) {
600            $class = 'hirankc';
601        }
602
603        my $lastseen;
604
605        if ($self->{cfg}->{showlastseen}) {
606            $lastseen = $self->{stats}->{days} - $self->{stats}->{lastvisited}{$nick};
607            my %hash = ( days => $lastseen );
608            if ($lastseen == 0) {
609                $lastseen = $self->_template_text('today');
610            } elsif ($lastseen == 1) {
611                $lastseen = $self->_template_text('lastseen1', %hash);
612            } else {
613                $lastseen = $self->_template_text('lastseen2', %hash);
614            }
615        }
616
617        _html("<tr><td class=\"$class\" align=\"left\">$c</td>");
618
619        my $line = $self->{stats}->{lines}{$nick};
620        my $w = $self->{stats}->{words}{$nick} ? $self->{stats}->{words}{$nick} : 0;
621        my $ch   = $self->{stats}->{lengths}{$nick};
622        my $sex = $self->{users}->{sex}{$nick};
623
624        my $output = "";
625        $output .= "<td $style>";
626
627        # Hilight nick with gendercolors
628        if ($sex and $sex eq 'm') {
629            $output .= "<span class=\"male\">";
630        } elsif ($sex and $sex eq 'f') {
631            $output .= "<span class=\"female\">";
632        } elsif ($sex and $sex eq 'b') {
633            $output .= "<span class=\"bot\">";
634        } else {
635            $output .= "<span>";
636        }
637        $output .= $visiblenick;
638        $output .= "</span></td>";
639
640        if ($self->{cfg}->{showlines}) {
641            if ($self->{cfg}->{showlinetime}) {
642                $output .= "<td $style nowrap=\"nowrap\">" . $self->_user_linetimes($nick,$active[0]) . "</td>";
643            } else {
644                $output .= "<td $style>$line</td>";
645            }
646        }
647
648        $output .= "<td $style>" . $self->_user_times($nick) . "</td>" if ($self->{cfg}->{showtime});
649
650        if ($self->{cfg}->{showwords}) {
651            if ($self->{cfg}->{showwordtime}) {
652                $output .= "<td $style nowrap=\"nowrap\">" . $self->_user_wordtimes($nick,$active[0]) . "</td>";
653            } else {
654                $output .= "<td $style>$w</td>";
655            }
656        }
657
658        $output .= "<td $style>" . sprintf("%.1f", $w/$line) . "</td>"  if ($self->{cfg}->{showwpl});
659        $output .= "<td $style>" . sprintf("%.1f", $ch/$line) . "</td>" if ($self->{cfg}->{showcpl});
660        $output .= "<td $style>$lastseen</td>"                          if ($self->{cfg}->{showlastseen});
661        $output .= "<td $style>\"$randomline\"</td>"                    if ($self->{cfg}->{showrandquote});
662
663        _html($output);
664        undef $output;
665
666
667        if ($self->{cfg}->{userpics} && $i % $self->{cfg}->{userpics} == 0) {
668            for my $ii (0 .. $self->{cfg}->{userpics} - 1) {
669                last if $i + $ii >= $self->{cfg}->{activenicks};
670                $self->_user_pic($active[$i + $ii], $style);
671            }
672        }
673        _html("</tr>");
674    }
675
676    _html("</table><br />");
677
678    # Almost as active nicks ('These didn't make it to the top..')
679
680    my $toshow = $self->{cfg}->{activenicks2} - $self->{cfg}->{activenicks};
681    my $remain = $self->{cfg}->{activenicks} + $toshow;
682
683    unless ($toshow > $nicks) {
684        $remain = $self->{cfg}->{activenicks} + $self->{cfg}->{activenicks2};
685        if ($remain > $nicks) {
686            $remain = $nicks;
687        }
688
689        if ($self->{cfg}->{activenicks} <  $remain) {
690            _html("<br /><b><i>" . $self->_template_text('nottop') . "</i></b><table><tr>");
691            for (my $i = $self->{cfg}->{activenicks}; $i < $remain; $i++) {
692                my $visiblenick;
693                my $nick = $active[$i];
694                if ($i != $self->{cfg}->{activenicks} and ($i - $self->{cfg}->{activenicks}) % 5 == 0) {
695                    _html("</tr><tr>");
696                }
697                my $items;
698                if ($self->{users}->{userlinks}{$nick}) {
699                    $visiblenick = $self->_format_word($self->{users}->{userlinks}{$nick}, $nick);
700                } else {
701                    $visiblenick = $self->_format_word($nick);
702                }
703                if ($self->{cfg}->{sortbywords}) {
704                    $items = $self->{stats}->{words}{$active[$i]};
705                } else {
706                    $items = $self->{stats}->{lines}{$active[$i]};
707                }
708                my $sex = $self->{users}->{sex}{$active[$i]};
709
710                my $output = "";
711                $output .= "<td class=\"rankc10\">";
712
713                if ($sex and $sex eq 'm') {
714                    $output .= "<span class=\"male\">";
715                } elsif ($sex and $sex eq 'f') {
716                    $output .= "<span class=\"female\">";
717                } elsif ($sex and $sex eq 'b') {
718                    $output .= "<span class=\"bot\">";
719                } else {
720                    $output .= "<span>";
721                }
722                $output .= "$visiblenick ($items)";
723                $output .= "</span></td>";
724
725                _html($output);
726                undef $output;
727
728            }
729            _html("</tr></table>");
730        }
731    }
732
733    my %hash;
734    $hash{totalnicks} = $nicks - $remain;
735    if ($hash{totalnicks} > 0) {
736        _html("<br /><b>" . $self->_template_text('totalnicks', %hash) . "</b><br />");
737    }
738}
739
740sub generate_colors
741{
742    my $self = shift;
743    my $c = shift;
744
745    # if hicell or hicell2 is "", do not print the class as it could mess up the gendercode
746    return "" if not (length $self->{cfg}->{hicell} and length $self->{cfg}->{hicell2});
747
748    my $h = $self->{cfg}->{hicell} or return "class=\"hicell\"";
749    $h =~ s/^#//;
750    $h = hex $h;
751    my $h2 = $self->{cfg}->{hicell2} or return "class=\"hicell\"";
752    $h2 =~ s/^#//;
753    $h2 = hex $h2;
754    my $f_b = $h & 0xff;
755    my $f_g = ($h & 0xff00) >> 8;
756    my $f_r = ($h & 0xff0000) >> 16;
757    my $t_b = $h2 & 0xff;
758    my $t_g = ($h2 & 0xff00) >> 8;
759    my $t_r = ($h2 & 0xff0000) >> 16;
760    my $blue  = sprintf "%0.2x", abs int(((($t_b - $f_b) / $self->{cfg}->{activenicks}) * +$c) + $f_b);
761    my $green  = sprintf "%0.2x", abs int(((($t_g - $f_g) / $self->{cfg}->{activenicks}) * +$c) + $f_g);
762    my $red  = sprintf "%0.2x", abs int(((($t_r - $f_r) / $self->{cfg}->{activenicks}) * +$c) + $f_r);
763
764    return "style=\"background-color: #$red$green$blue\"";
765}
766
767sub _html
768{
769    my $html = shift;
770    print OUTPUT "$html\n" or die "Could not write to disk: $!\n";
771}
772
773sub _questions
774{
775    # Persons who asked the most questions
776    my $self = shift;
777
778    my %qpercent;
779
780    foreach my $nick (sort keys %{ $self->{stats}->{questions} }) {
781        if ($self->{topactive}{$nick} || !$self->{cfg}->{showonlytop}) {
782          if ($self->{stats}->{lines}{$nick} > $self->{cfg}->{bignumbersthreshold}) {
783              $qpercent{$nick} = sprintf("%.1f", ($self->{stats}->{questions}{$nick} / $self->{stats}->{lines}{$nick}) * 100);
784          }
785        }
786    }
787
788    my @question = sort { $qpercent{$b} <=> $qpercent{$a} } keys %qpercent;
789
790    if (@question) {
791        my %hash = (
792            nick => $question[0],
793            per  => $qpercent{$question[0]}
794        );
795
796        my $text = $self->_template_text('question1', %hash);
797        _html("<tr><td class=\"hicell\">$text");
798        if (@question >= 2) {
799            my %hash = (
800                nick => $question[1],
801                per => $qpercent{$question[1]}
802            );
803
804            my $text = $self->_template_text('question2', %hash);
805            _html("<br /><span class=\"small\">$text</span>");
806        }
807        _html("</td></tr>");
808
809    } else {
810        _html("<tr><td class=\"hicell\">" . $self->_template_text('question3') . "</td></tr>");
811    }
812}
813
814sub _shoutpeople
815{
816    # The ones who speak with exclamation marks!
817    my $self = shift;
818
819    my %spercent;
820
821    foreach my $nick (sort keys %{ $self->{stats}->{shouts} }) {
822        if ($self->{topactive}{$nick} || !$self->{cfg}->{showonlytop}) {
823          if ($self->{stats}->{lines}{$nick} > $self->{cfg}->{bignumbersthreshold}) {
824              $spercent{$nick} = sprintf("%.1f", ($self->{stats}->{shouts}{$nick} / $self->{stats}->{lines}{$nick}) * 100);
825          }
826        }
827    }
828
829    my @shout = sort { $spercent{$b} <=> $spercent{$a} } keys %spercent;
830
831    if (@shout) {
832        my %hash = (
833            nick => $shout[0],
834            per  => $spercent{$shout[0]}
835        );
836
837        my $text = $self->_template_text('shout1', %hash);
838        _html("<tr><td class=\"hicell\">$text");
839        if (@shout >= 2) {
840            my %hash = (
841                nick => $shout[1],
842                per  => $spercent{$shout[1]}
843            );
844
845            my $text = $self->_template_text('shout2', %hash);
846            _html("<br /><span class=\"small\">$text</span>");
847        }
848        _html("</td></tr>");
849
850    } else {
851        my $text = $self->_template_text('shout3');
852        _html("<tr><td class=\"hicell\">$text</td></tr>");
853    }
854
855}
856
857sub _capspeople
858{
859    # The ones who speak ALL CAPS.
860    my $self = shift;
861
862    my %cpercent;
863
864    foreach my $nick (sort keys %{ $self->{stats}->{allcaps} }) {
865        if ($self->{topactive}{$nick} || !$self->{cfg}->{showonlytop}) {
866          if ($self->{stats}->{lines}{$nick} > $self->{cfg}->{bignumbersthreshold}) {
867              $cpercent{$nick} = sprintf("%.1f", $self->{stats}->{allcaps}{$nick} / $self->{stats}->{lines}{$nick} * 100);
868          }
869        }
870    }
871
872    my @caps = sort { $cpercent{$b} <=> $cpercent{$a} } keys %cpercent;
873
874    if (@caps) {
875        my %hash = (
876            nick => $caps[0],
877            per  => $cpercent{$caps[0]},
878            line => $self->_format_line($self->{stats}->{allcaplines}{$caps[0]})
879        );
880
881        my $text = $self->_template_text('allcaps1', %hash);
882        if($self->{cfg}->{showshoutline}) {
883            my $exttext = $self->_template_text('allcapstext', %hash);
884            _html("<tr><td class=\"hicell\">$text<br /><span class=\"small\">$exttext</span>");
885        } else {
886            _html("<tr><td class=\"hicell\">$text");
887        }
888        if (@caps >= 2) {
889            my %hash = (
890                nick => $caps[1],
891                per  => $cpercent{$caps[1]}
892            );
893
894            my $text = $self->_template_text('allcaps2', %hash);
895            _html("<br /><span class=\"small\">$text</span>");
896        }
897        _html("</td></tr>");
898
899    } else {
900        my $text = $self->_template_text('allcaps3');
901        _html("<tr><td class=\"hicell\">$text</td></tr>");
902    }
903}
904
905sub _violent
906{
907    # They attacked others (words defined by $self->{cfg}->{violent})
908    my $self = shift;
909
910    my @aggressors = sort { $self->{stats}->{violence}{$b} <=> $self->{stats}->{violence}{$a} }
911                         keys %{ $self->{stats}->{violence} };
912
913    @aggressors = $self->_istoponly(@aggressors);
914
915    if(@aggressors) {
916        my %hash = (
917            nick    => $aggressors[0],
918            attacks => $self->{stats}->{violence}{$aggressors[0]},
919            line    => $self->_format_line($self->{stats}->{violencelines}{$aggressors[0]})
920        );
921        my $text = $self->_template_text('violent1', %hash);
922        if($self->{cfg}->{showviolentlines}) {
923            my $exttext = $self->_template_text('violenttext', %hash);
924            _html("<tr><td class=\"hicell\">$text<br /><span class=\"small\">$exttext</span>");
925        } else {
926            _html("<tr><td class=\"hicell\">$text");
927        }
928        if (@aggressors >= 2) {
929            my %hash = (
930                nick    => $aggressors[1],
931                attacks => $self->{stats}->{violence}{$aggressors[1]}
932            );
933
934            my $text = $self->_template_text('violent2', %hash);
935            _html("<br /><span class=\"small\">$text</span>");
936        }
937        _html("</td></tr>");
938    } else {
939        my $text = $self->_template_text('violent3');
940        _html("<tr><td class=\"hicell\">$text</td></tr>");
941    }
942
943
944    # They got attacked
945    my @victims = sort { $self->{stats}->{attacked}{$b} <=> $self->{stats}->{attacked}{$a} }
946                    keys %{ $self->{stats}->{attacked} };
947
948    @victims = $self->_istoponly(@victims);
949
950    if(@victims) {
951        my %hash = (
952            nick    => $victims[0],
953            attacks => $self->{stats}->{attacked}{$victims[0]},
954            line    => $self->_format_line($self->{stats}->{attackedlines}{$victims[0]})
955        );
956        my $text = $self->_template_text('attacked1', %hash);
957        if($self->{cfg}->{showviolentlines}) {
958            my $exttext = $self->_template_text('attackedtext', %hash);
959            _html("<tr><td class=\"hicell\">$text<br /><span class=\"small\">$exttext</span>");
960        } else {
961            _html("<tr><td class=\"hicell\">$text");
962        }
963        if (@victims >= 2) {
964            my %hash = (
965                nick    => $victims[1],
966                attacks => $self->{stats}->{attacked}{$victims[1]}
967            );
968
969            my $text = $self->_template_text('attacked2', %hash);
970            _html("<br /><span class=\"small\">$text</span>");
971        }
972        _html("</td></tr>");
973    }
974}
975
976sub _gotkicks
977{
978    # The persons who got kicked the most
979    my $self = shift;
980
981    my @gotkick = sort { $self->{stats}->{gotkicked}{$b} <=> $self->{stats}->{gotkicked}{$a} }
982                       keys %{ $self->{stats}->{gotkicked} };
983
984    @gotkick = $self->_istoponly(@gotkick);
985
986    if (@gotkick) {
987        my %hash = (
988            nick  => $gotkick[0],
989            kicks => $self->{stats}->{gotkicked}{$gotkick[0]},
990            line  => $self->_format_line($self->{stats}->{kicklines}{$gotkick[0]})
991        );
992
993        my $text = $self->_template_text('gotkick1', %hash);
994
995        if ($self->{cfg}->{showkickline}) {
996            my $exttext = $self->_template_text('kicktext', %hash);
997            _html("<tr><td class=\"hicell\">$text<br /><span class=\"small\">$exttext</span>");
998        } else {
999            _html("<tr><td class=\"hicell\">$text");
1000        }
1001
1002        if (@gotkick >= 2) {
1003            my %hash = (
1004                nick  => $gotkick[1],
1005                kicks => $self->{stats}->{gotkicked}{$gotkick[1]}
1006            );
1007
1008            my $text = $self->_template_text('gotkick2', %hash);
1009            _html("<br /><span class=\"small\">$text</span>");
1010        }
1011        _html("</td></tr>");
1012    }
1013}
1014
1015sub _mostjoins
1016{
1017    my $self = shift;
1018
1019    my @joins = sort { $self->{stats}->{joins}{$b} <=> $self->{stats}->{joins}{$a} }
1020                     keys %{ $self->{stats}->{joins} };
1021
1022    @joins = $self->_istoponly(@joins);
1023
1024    if (@joins) {
1025        my %hash = (
1026            nick  => $joins[0],
1027            joins => $self->{stats}->{joins}{$joins[0]}
1028        );
1029
1030        my $text = $self->_template_text('joins', %hash);
1031
1032        _html("<tr><td class=\"hicell\">$text</td></tr>");
1033    }
1034}
1035
1036sub _mostwords
1037{
1038    # The person who got words the most
1039    my $self = shift;
1040
1041    my @words = sort { $self->{stats}->{words}{$b} <=> $self->{stats}->{words}{$a} }
1042                      keys %{ $self->{stats}->{words} };
1043
1044    @words = $self->_istoponly(@words);
1045
1046    if (@words) {
1047        my %hash = (
1048            nick  => $words[0],
1049            words => $self->{stats}->{words}{$words[0]}
1050        );
1051
1052        my $text = $self->_template_text('words1', %hash);
1053        _html("<tr><td class=\"hicell\">$text");
1054
1055        if (@words >= 2) {
1056            my %hash = (
1057                oldnick => $words[0],
1058                nick    => $words[1],
1059                words   => $self->{stats}->{words}{$words[1]}
1060            );
1061
1062            my $text = $self->_template_text('words2', %hash);
1063            _html("<br /><span class=\"small\">$text</span>");
1064        }
1065        _html("</td></tr>");
1066    } else {
1067        my $text = $self->_template_text('words3');
1068        _html("<tr><td class=\"hicell\">$text</td></tr>");
1069    }
1070}
1071
1072sub _mostkicks
1073{
1074    # The person who kicked the most
1075    my $self = shift;
1076
1077    my @kicked = sort { $self->{stats}->{kicked}{$b} <=> $self->{stats}->{kicked}{$a} }
1078                        keys %{ $self->{stats}->{kicked} };
1079
1080    @kicked = $self->_istoponly(@kicked);
1081
1082    if (@kicked) {
1083        my %hash = (
1084            nick   => $kicked[0],
1085            kicked => $self->{stats}->{kicked}{$kicked[0]}
1086        );
1087
1088        my $text = $self->_template_text('kick1', %hash);
1089        _html("<tr><td class=\"hicell\">$text");
1090
1091        if (@kicked >= 2) {
1092            my %hash = (
1093                oldnick => $kicked[0],
1094                nick    => $kicked[1],
1095                kicked  => $self->{stats}->{kicked}{$kicked[1]}
1096            );
1097
1098            my $text = $self->_template_text('kick2', %hash);
1099            _html("<br /><span class=\"small\">$text</span>");
1100        }
1101        _html("</td></tr>");
1102    } else {
1103        my $text = $self->_template_text('kick3');
1104        _html("<tr><td class=\"hicell\">$text</td></tr>");
1105    }
1106}
1107
1108sub _mostmonologues
1109{
1110    # The person who had the most monologues (speaking to himself)
1111    my $self = shift;
1112
1113    my @monologue = sort { $self->{stats}->{monologues}{$b} <=> $self->{stats}->{monologues}{$a} }
1114                           keys %{ $self->{stats}->{monologues} };
1115
1116    @monologue = $self->_istoponly(@monologue);
1117
1118    if (@monologue) {
1119        my %hash = (
1120            nick  => $monologue[0],
1121            monos => $self->{stats}->{monologues}{$monologue[0]}
1122        );
1123
1124        my $text = $self->_template_text('mono1', %hash);
1125
1126        _html("<tr><td class=\"hicell\">$text");
1127        if (@monologue >= 2) {
1128            my %hash = (
1129                nick  => $monologue[1],
1130                monos => $self->{stats}->{monologues}{$monologue[1]}
1131            );
1132
1133            my $text = $self->_template_text('mono2', %hash);
1134            _html("<br /><span class=\"small\">$text</span>");
1135        }
1136        _html("</td></tr>");
1137    }
1138}
1139
1140sub _linelengths
1141{
1142    # The person(s) who wrote the longest lines
1143    my $self = shift;
1144
1145    my %len;
1146
1147    foreach my $nick (sort keys %{ $self->{stats}->{lengths} }) {
1148        if ($self->{topactive}{$nick} || !$self->{cfg}->{showonlytop}) {
1149          if ($self->{stats}->{lines}{$nick} > $self->{cfg}->{bignumbersthreshold}) {
1150              $len{$nick} = sprintf("%.1f", $self->{stats}->{lengths}{$nick} / $self->{stats}->{lines}{$nick});
1151          }
1152        }
1153    }
1154
1155    my @len = sort { $len{$b} <=> $len{$a} } keys %len;
1156
1157    my $all_lines = 0;
1158    my $totallength;
1159    foreach my $nick (keys %{ $self->{stats}->{lines} }) {
1160        $all_lines   += $self->{stats}->{lines}{$nick};
1161        $totallength += $self->{stats}->{lengths}{$nick};
1162    }
1163
1164    my $totalaverage;
1165
1166    if ($all_lines > 0) {
1167        $totalaverage = sprintf("%.1f", $totallength / $all_lines);
1168    }
1169
1170    if (@len) {
1171        my %hash = (
1172            nick    => $len[0],
1173            letters => $len{$len[0]}
1174        );
1175
1176        my $text = $self->_template_text('long1', %hash);
1177        _html("<tr><td class=\"hicell\">$text<br />");
1178
1179        if (@len >= 2) {
1180            %hash = (
1181                avg => $totalaverage
1182            );
1183
1184            $text = $self->_template_text('long2', %hash);
1185            _html("<span class=\"small\">$text</span></td></tr>");
1186        } else {
1187            _html("</td></tr>");
1188        }
1189    }
1190
1191    # The person(s) who wrote the shortest lines
1192
1193    if (@len) {
1194        my %hash = (
1195            nick => $len[$#len],
1196            letters => $len{$len[$#len]}
1197        );
1198
1199        my $text = $self->_template_text('short1', %hash);
1200        _html("<tr><td class=\"hicell\">$text<br />");
1201
1202        if (@len >= 2) {
1203            %hash = (
1204                nick => $len[$#len - 1],
1205                letters => $len{$len[$#len - 1]}
1206            );
1207
1208            $text = $self->_template_text('short2', %hash);
1209            _html("<span class=\"small\">$text</span></td></tr>");
1210        } else {
1211            _html("</td></tr>");
1212        }
1213    }
1214}
1215
1216sub _mostfoul
1217{
1218    my $self = shift;
1219
1220    my %spercent;
1221
1222    foreach my $nick (sort keys %{ $self->{stats}->{foul} }) {
1223        if ($self->{topactive}{$nick} || !$self->{cfg}->{showonlytop}) {
1224          if ($self->{stats}->{lines}{$nick} > 15) {
1225              my $dec = $self->{cfg}->{showfouldecimals};
1226              $dec = 1 if($dec < 0); # default to 1
1227              $spercent{$nick} = sprintf("%.${dec}f", $self->{stats}->{foul}{$nick} / $self->{stats}->{words}{$nick} * 100);
1228          }
1229        }
1230    }
1231
1232    my @foul = sort { $spercent{$b} <=> $spercent{$a} } keys %spercent;
1233
1234    if (@foul) {
1235
1236        my %hash = (
1237            nick => $foul[0],
1238            per  => $spercent{$foul[0]},
1239            line => $self->_format_line($self->{stats}{foullines}{$foul[0]}),
1240        );
1241
1242        my $text = $self->_template_text('foul1', %hash);
1243
1244        if($self->{cfg}->{showfoulline}) {
1245            my $exttext = $self->_template_text('foultext', %hash);
1246            _html("<tr><td class=\"hicell\">$text<br /><span class=\"small\">$exttext</span>");
1247        } else {
1248            _html("<tr><td class=\"hicell\">$text");
1249        }
1250
1251        if (@foul >= 2) {
1252            my %hash = (
1253                nick => $foul[1],
1254                per  => $spercent{$foul[1]}
1255            );
1256
1257            my $text = $self->_template_text('foul2', %hash);
1258            _html("<br /><span class=\"small\">$text</span>");
1259        }
1260
1261        _html("</td></tr>");
1262    } else {
1263        my $text = $self->_template_text('foul3');
1264
1265        _html("<tr><td class=\"hicell\">$text</td></tr>");
1266    }
1267}
1268
1269
1270sub _mostsad
1271{
1272    my $self = shift;
1273
1274    my %spercent;
1275
1276    foreach my $nick (sort keys %{ $self->{stats}->{frowns} }) {
1277        if ($self->{topactive}{$nick} || !$self->{cfg}->{showonlytop}) {
1278          if ($self->{stats}->{lines}{$nick} > $self->{cfg}->{bignumbersthreshold}) {
1279              $spercent{$nick} = sprintf("%.1f", $self->{stats}->{frowns}{$nick} / $self->{stats}->{lines}{$nick} * 100);
1280          }
1281        }
1282    }
1283
1284    my @sadface = sort { $spercent{$b} <=> $spercent{$a} } keys %spercent;
1285
1286
1287    if (@sadface) {
1288        my %hash = (
1289            nick => $sadface[0],
1290            per  => $spercent{$sadface[0]}
1291        );
1292
1293        my $text = $self->_template_text('sad1', %hash);
1294        _html("<tr><td class=\"hicell\">$text");
1295
1296        if (@sadface >= 2) {
1297            my %hash = (
1298                nick => $sadface[1],
1299                per  => $spercent{$sadface[1]}
1300            );
1301
1302            my $text = $self->_template_text('sad2', %hash);
1303
1304            _html("<br /><span class=\"small\">$text</span>");
1305        }
1306        _html("</td></tr>");
1307    } else {
1308        my $text = $self->_template_text('sad3');
1309        _html("<tr><td class=\"hicell\">$text</td></tr>");
1310    }
1311}
1312
1313
1314sub _mostop
1315{
1316    my $self = shift;
1317
1318    my @ops   = sort { $self->{stats}->{gaveops}{$b} <=> $self->{stats}->{gaveops}{$a} }
1319                     keys %{ $self->{stats}->{gaveops} };
1320
1321    @ops = $self->_istoponly(@ops);
1322
1323    my @deops = sort { $self->{stats}->{tookops}{$b} <=> $self->{stats}->{tookops}{$a} }
1324                     keys %{ $self->{stats}->{tookops} };
1325
1326    @deops = $self->_istoponly(@deops);
1327
1328    if (@ops) {
1329        my %hash = (
1330            nick => $ops[0],
1331            ops  => $self->{stats}->{gaveops}{$ops[0]}
1332        );
1333
1334        my $text = $self->_template_text('mostop1', %hash);
1335
1336        _html("<tr><td class=\"hicell\">$text");
1337
1338        if (@ops >= 2) {
1339            my %hash = (
1340                nick => $ops[1],
1341                ops  => $self->{stats}->{gaveops}{$ops[1]}
1342            );
1343
1344            my $text = $self->_template_text('mostop2', %hash);
1345            _html("<br /><span class=\"small\">$text</span>");
1346        }
1347        _html("</td></tr>");
1348    } else {
1349        my $text = $self->_template_text('mostop3');
1350        _html("<tr><td class=\"hicell\">$text</td></tr>");
1351    }
1352
1353    if (@deops) {
1354        my %hash = (
1355            nick  => $deops[0],
1356            deops => $self->{stats}->{tookops}{$deops[0]}
1357        );
1358        my $text = $self->_template_text('mostdeop1', %hash);
1359
1360        _html("<tr><td class=\"hicell\">$text");
1361
1362        if (@deops >= 2) {
1363            my %hash = (
1364                nick  => $deops[1],
1365                deops => $self->{stats}->{tookops}{$deops[1]}
1366            );
1367            my $text = $self->_template_text('mostdeop2', %hash);
1368
1369            _html("<br /><span class=\"small\">$text</span>");
1370        }
1371        _html("</td></tr>");
1372    } else {
1373        my $text = $self->_template_text('mostdeop3');
1374        _html("<tr><td class=\"hicell\">$text</td></tr>");
1375    }
1376}
1377
1378sub _mostvoice
1379{
1380    my $self = shift;
1381
1382    my @voice   = sort { $self->{stats}->{gavevoice}{$b} <=> $self->{stats}->{gavevoice}{$a} }
1383                     keys %{ $self->{stats}->{gavevoice} };
1384
1385    @voice = $self->_istoponly(@voice);
1386
1387    my @devoice = sort { $self->{stats}->{tookvoice}{$b} <=> $self->{stats}->{tookvoice}{$a} }
1388                     keys %{ $self->{stats}->{tookvoice} };
1389
1390    @devoice = $self->_istoponly(@devoice);
1391
1392    if (@voice) {
1393        my %hash = (
1394            nick => $voice[0],
1395            voices  => $self->{stats}->{gavevoice}{$voice[0]}
1396        );
1397
1398        my $text = $self->_template_text('mostvoice1', %hash);
1399
1400        _html("<tr><td class=\"hicell\">$text");
1401
1402        if (@voice >= 2) {
1403            my %hash = (
1404                nick => $voice[1],
1405                voices  => $self->{stats}->{gavevoice}{$voice[1]}
1406            );
1407
1408            my $text = $self->_template_text('mostvoice2', %hash);
1409            _html("<br /><span class=\"small\">$text</span>");
1410        }
1411        _html("</td></tr>");
1412    } else {
1413        my $text = $self->_template_text('mostvoice3');
1414        _html("<tr><td class=\"hicell\">$text</td></tr>");
1415    }
1416
1417    if (@devoice) {
1418        my %hash = (
1419            nick  => $devoice[0],
1420            devoices => $self->{stats}->{tookvoice}{$devoice[0]}
1421        );
1422        my $text = $self->_template_text('mostdevoice1', %hash);
1423
1424        _html("<tr><td class=\"hicell\">$text");
1425
1426        if (@devoice >= 2) {
1427            my %hash = (
1428                nick  => $devoice[1],
1429                devoices => $self->{stats}->{tookvoice}{$devoice[1]}
1430            );
1431            my $text = $self->_template_text('mostdevoice2', %hash);
1432
1433            _html("<br /><span class=\"small\">$text</span>");
1434        }
1435        _html("</td></tr>");
1436    } else {
1437        my $text = $self->_template_text('mostdevoice3');
1438        _html("<tr><td class=\"hicell\">$text</td></tr>");
1439    }
1440
1441}
1442
1443sub _mosthalfop
1444{
1445    my $self = shift;
1446
1447    my @halfops   = sort { $self->{stats}->{gavehalfops}{$b} <=> $self->{stats}->{gavehalfops}{$a} }
1448                     keys %{ $self->{stats}->{gavehalfops} };
1449
1450    @halfops = $self->_istoponly(@halfops);
1451
1452    my @dehalfops = sort { $self->{stats}->{tookhalfops}{$b} <=> $self->{stats}->{tookhalfops}{$a} }
1453                     keys %{ $self->{stats}->{tookhalfops} };
1454
1455    @dehalfops = $self->_istoponly(@dehalfops);
1456
1457    if (@halfops) {
1458        my %hash = (
1459            nick => $halfops[0],
1460            halfops  => $self->{stats}->{gavehalfops}{$halfops[0]}
1461        );
1462
1463        my $text = $self->_template_text('mosthalfop1', %hash);
1464
1465        _html("<tr><td class=\"hicell\">$text");
1466
1467        if (@halfops >= 2) {
1468            my %hash = (
1469                nick => $halfops[1],
1470                halfops  => $self->{stats}->{gavehalfops}{$halfops[1]}
1471            );
1472
1473            my $text = $self->_template_text('mosthalfop2', %hash);
1474            _html("<br /><span class=\"small\">$text</span>");
1475        }
1476        _html("</td></tr>");
1477    } else {
1478        my $text = $self->_template_text('mosthalfop3');
1479        _html("<tr><td class=\"hicell\">$text</td></tr>");
1480    }
1481
1482    if (@dehalfops) {
1483        my %hash = (
1484            nick  => $dehalfops[0],
1485            dehalfops => $self->{stats}->{tookhalfops}{$dehalfops[0]}
1486        );
1487        my $text = $self->_template_text('mostdehalfop1', %hash);
1488
1489        _html("<tr><td class=\"hicell\">$text");
1490
1491        if (@dehalfops >= 2) {
1492            my %hash = (
1493                nick  => $dehalfops[1],
1494                dehalfops => $self->{stats}->{tookhalfops}{$dehalfops[1]}
1495            );
1496            my $text = $self->_template_text('mostdehalfop2', %hash);
1497
1498            _html("<br /><span class=\"small\">$text</span>");
1499        }
1500        _html("</td></tr>");
1501    } else {
1502        my $text = $self->_template_text('mostdehalfop3');
1503        _html("<tr><td class=\"hicell\">$text</td></tr>");
1504    }
1505}
1506
1507sub _mostactions
1508{
1509    # The person who did the most /me's
1510    my $self = shift;
1511
1512    my @actions = sort { $self->{stats}->{actions}{$b} <=> $self->{stats}->{actions}{$a} }
1513                        keys %{ $self->{stats}->{actions} };
1514
1515    @actions = $self->_istoponly(@actions);
1516
1517    if (@actions) {
1518        my %linehash =
1519        my %hash = (
1520            nick    => $actions[0],
1521            actions => $self->{stats}->{actions}{$actions[0]},
1522            line    => $self->_format_line($self->{stats}->{actionlines}{$actions[0]})
1523        );
1524        my $text = $self->_template_text('action1', %hash);
1525        if($self->{cfg}->{showactionline}) {
1526            my $exttext = $self->_template_text('actiontext', %hash);
1527            _html("<tr><td class=\"hicell\">$text<br /><span class=\"small\">$exttext</span>");
1528        } else {
1529            _html("<tr><td class=\"hicell\">$text");
1530        }
1531
1532        if (@actions >= 2) {
1533            my %hash = (
1534                nick    => $actions[1],
1535                actions => $self->{stats}->{actions}{$actions[1]}
1536            );
1537
1538            my $text = $self->_template_text('action2', %hash);
1539            _html("<br /><span class=\"small\">$text</span>");
1540        }
1541        _html("</td></tr>");
1542    } else {
1543        my $text = $self->_template_text('action3');
1544        _html("<tr><td class=\"hicell\">$text</td></tr>");
1545    }
1546}
1547
1548
1549sub _mostsmiles
1550{
1551    # The person(s) who smiled the most :-)
1552    my $self = shift;
1553
1554    my %spercent;
1555
1556    foreach my $nick (sort keys %{ $self->{stats}->{smiles} }) {
1557        if ($self->{topactive}{$nick} || !$self->{cfg}->{showonlytop}) {
1558          if ($self->{stats}->{lines}{$nick} > $self->{cfg}->{bignumbersthreshold}) {
1559              $spercent{$nick} = sprintf("%.1f", $self->{stats}->{smiles}{$nick} / $self->{stats}->{lines}{$nick} * 100);
1560          }
1561        }
1562    }
1563
1564    my @smiles = sort { $spercent{$b} <=> $spercent{$a} } keys %spercent;
1565
1566
1567    if (@smiles) {
1568        my %hash = (
1569            nick => $smiles[0],
1570            per  => $spercent{$smiles[0]}
1571        );
1572
1573        my $text = $self->_template_text('smiles1', %hash);
1574
1575        _html("<tr><td class=\"hicell\">$text");
1576        if (@smiles >= 2) {
1577            my %hash = (
1578                nick => $smiles[1],
1579                per  => $spercent{$smiles[1]}
1580            );
1581
1582            my $text = $self->_template_text('smiles2', %hash);
1583            _html("<br /><span class=\"small\">$text</span>");
1584        }
1585        _html("</td></tr>");
1586
1587    } else {
1588
1589        my $text = $self->_template_text('smiles3');
1590        _html("<tr><td class=\"hicell\">$text</td></tr>");
1591    }
1592}
1593
1594sub _lasttopics
1595{
1596    my $self = shift;
1597
1598    if ($self->{stats}->{topics}) {
1599
1600        my %topic_seen;
1601
1602        my %hash = (
1603            total => scalar @{ $self->{stats}->{topics} }
1604        );
1605
1606        my $ltopic = $#{ $self->{stats}->{topics} };
1607        my $tlimit = 0;
1608
1609        $self->{cfg}->{topichistory} -= 1;
1610
1611        if ($ltopic > $self->{cfg}->{topichistory}) {
1612            $tlimit = $ltopic - $self->{cfg}->{topichistory};
1613        }
1614
1615        for (my $i = $ltopic; $i >= $tlimit; $i--) {
1616            my $topic = $self->{stats}->{topics}[$i]{topic};
1617            # This code makes sure that we don't see the same topic twice
1618            next if ($topic_seen{$topic});
1619            $topic_seen{$topic} = 1;
1620
1621            # Strip off the quotes (')
1622            $topic =~ s/^\'(.*)\'$/$1/;
1623
1624            my $nick = $self->{stats}->{topics}[$i]{nick};
1625            my $hour = $self->{stats}->{topics}[$i]{hour};
1626            my $min  = $self->{stats}->{topics}[$i]{min};
1627
1628            $hash{nick} = $nick;
1629            $hash{time} = "$hour:$min";
1630            $hash{days} = $self->{stats}->{days} - $self->{stats}->{topics}[$i]{days};
1631            if ($hash{days} == 0) {
1632                $hash{date} = $self->_template_text('today');
1633            } elsif ($hash{days} == 1) {
1634                $hash{date} = $self->_template_text('lastseen1', %hash);
1635            } else {
1636                $hash{date} = $self->_template_text('lastseen2', %hash);
1637            }
1638
1639            _html('<tr><td class="hicell"><i>' . $self->_format_line($topic) . '</i></td>');
1640            _html('<td class="hicell"><b>' . $self->_template_text('bylinetopic', %hash) . '</b></td></tr>');
1641        }
1642        _html("<tr><td align=\"center\" colspan=\"2\" class=\"asmall\">" . $self->_template_text('totaltopic', %hash) . "</td></tr>");
1643    } else {
1644        _html("<tr><td class=\"hicell\">" . $self->_template_text('notopic') ."</td></tr>");
1645    }
1646}
1647
1648sub _template_text
1649{
1650    # This function is for the homemade template system. It receives a name
1651    # of a template and a hash with the fields in the template to update to
1652    # its corresponding value
1653    my $self = shift;
1654    my $template = shift;
1655    my %hash = @_;
1656
1657    my $text;
1658
1659    unless ($text = $self->{tmps}->{$self->{cfg}->{lang}}{$template}) {
1660        # Fall back to English if the language template doesn't exist
1661
1662        if ($text = $self->{tmps}->{EN}{$template}) {
1663            print "Note: No translation in '$self->{cfg}->{lang}' for '$template' - falling back to English.\n"
1664                unless ($self->{cfg}->{silent});
1665        } else {
1666            die("No template for '$template' in language file.\n");
1667        }
1668    }
1669    if($self->{iconv}) {
1670        $text = $self->{iconv}->convert($text);
1671        die("Could not convert charset for template '$template'.\n") unless $text;
1672    }
1673
1674    $hash{channel} = $self->{cfg}->{channel};
1675    # the nick is sanitized here, everything else outside of _template_text
1676    $hash{nick} = $self->_format_word($hash{nick}) if $hash{nick};
1677
1678    foreach my $key (sort keys %hash) {
1679        $text =~ s/\[:$key\]/$hash{$key}/;
1680    }
1681
1682    if ($text =~ /\[:[^:]*?:[^:]*?:[^:]*?:\]/o) {
1683        $text =~ s/\[:([^:]*?):([^:]*?):([^:]*?):\]/$self->_get_subst($1,$2,$3,\%hash)/geo;
1684    }
1685    if ($text =~ /\[:[^:]*?:[^:]*?:]/o) {
1686        $text =~ s/\[:([^:]*?):([^:]*?):]/$self->_get_subst($1,$2,undef,\%hash)/geo;
1687    }
1688
1689    return $text;
1690}
1691
1692sub _format_word
1693{
1694    # This function formats a word -- should ONLY be called on words used alone (EG: not whole line printed)
1695    my ($self, $word, $nick) = @_; # nick is only defined for user links in top table
1696
1697    $word = htmlentities($word, $self->{cfg}->{charset});
1698    $word = $self->_replace_links($word, $nick);
1699    return $word;
1700}
1701
1702sub _format_line
1703{
1704    # This function formats a action/normal line to be more readable, and calls any other function
1705    # that should be executed on every line.
1706    my ($self, $line) = @_;
1707    my $hashref;
1708    if ($hashref = $self->{cfg}->{analyzer}->{parser}->normalline($line)) {
1709        $line = '<' . $hashref->{nick} . '> ' . $hashref->{saying};
1710    } elsif ($hashref = $self->{cfg}->{analyzer}->{parser}->actionline($line)) {
1711        $line = '* ' . $hashref->{nick} . ' ' . $hashref->{saying};
1712    } elsif ($hashref = $self->{cfg}->{analyzer}->{parser}->thirdline($line)) {
1713        if (defined($hashref->{kicker})) {
1714            $line = '*** ' . $hashref->{nick} . ' was kicked by ' . $hashref->{kicker};
1715            $line .= ' (' . $hashref->{kicktext} . ')'
1716                if (defined($hashref->{kicktext}));
1717        } elsif (defined($hashref->{newtopic})) {
1718            $line = '*** ' . $hashref->{nick} . ' changes topic to \'' . $hashref->{newtopic} . '\'';
1719        } elsif (defined($hashref->{newmode})) {
1720            $line = '*** ' . $hashref->{nick} . ' sets mode ' . $hashref->{newmode};
1721            $line .= ' ' . $hashref->{modechanges}
1722                if (defined($hashref->{kicktext}));
1723        } elsif (defined($hashref->{newjoin})) {
1724            $line = '*** Joins: ' . $hashref->{nick};
1725        } elsif (defined($hashref->{newnick})) {
1726            $line = '*** ' . $hashref->{nick} . ' is now known as ' . $hashref->{newnick};
1727        } elsif (defined($hashref->{newtopic})) {
1728            $line = '*** ' . $hashref->{nick} . ' changes topic to: ' . $hashref->{newtopic};
1729        }
1730    }
1731    $line = htmlentities($line, $self->{cfg}->{charset});
1732    $line = $self->_replace_links($line);
1733    return $line;
1734}
1735
1736sub _get_subst
1737{
1738    # This function looks at the user definition and see if there is sex
1739    # defined. If yes, return the appropriate value. If no, just return the
1740    # default he/she value.
1741    my $self = shift;
1742    my ($m,$f,$d,$hash) = @_;
1743    if ($hash->{nick} && $self->{users}->{sex}{$hash->{nick}}) {
1744        if ($self->{users}->{sex}{$hash->{nick}} eq 'm') {
1745            return $m;
1746        } elsif ($self->{users}->{sex}{$hash->{nick}} eq 'f') {
1747            return $f;
1748        }
1749    }
1750    return defined($d) ? $d : "$m/$f";
1751}
1752
1753sub _mostusedword
1754{
1755    # Word usage statistics
1756    my $self = shift;
1757
1758    my %usages;
1759
1760    foreach my $word (keys %{ $self->{stats}->{wordcounts} }) {
1761        # Skip people's nicks.
1762        next if is_nick($word, $self->{cfg}->{cachedir});
1763        next if (length($word) < $self->{cfg}->{wordlength});
1764        $usages{$word} = $self->{stats}->{wordcounts}{$word};
1765    }
1766
1767
1768    my @popular = sort { $usages{$b} <=> $usages{$a} } keys %usages;
1769
1770    if (@popular) {
1771        $self->_headline($self->_template_text('mostwordstopic'));
1772
1773        _html("<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>");
1774        _html("<td>&nbsp;</td><td class=\"tdtop\"><b>" . $self->_template_text('word') . "</b></td>");
1775        _html("<td class=\"tdtop\"><b>" . $self->_template_text('numberuses') . "</b></td>");
1776        _html("<td class=\"tdtop\"><b>" . $self->_template_text('lastused') . "</b></td></tr>");
1777
1778
1779        my $count = 0;
1780        for(my $i = 0; $count < $self->{cfg}->{wordhistory}; $i++) {
1781            last unless $i < $#popular;
1782            # Skip nicks.  It's far more efficient to do this here than when
1783            # @popular is created.
1784            next if is_ignored($popular[$i]);
1785
1786            my $a = $count + 1;
1787            my $popular = $self->_format_word($self->{stats}->{word_upcase}{$popular[$i]});
1788            my $wordcount = $self->{stats}->{wordcounts}{$popular[$i]};
1789            my $lastused = $self->_format_word($self->{stats}->{wordnicks}{$popular[$i]});
1790            my $class;
1791            if ($a == 1) {
1792                $class = 'hirankc';
1793            } else {
1794                $class = 'rankc';
1795            }
1796            _html("<tr><td class=\"$class\">$a</td>");
1797            _html("<td class=\"hicell\">$popular</td>");
1798            _html("<td class=\"hicell\">$wordcount</td>");
1799            _html("<td class=\"hicell\">$lastused</td>");
1800            _html("</tr>");
1801            $count++;
1802        }
1803
1804        _html("</table>");
1805    }
1806}
1807
1808sub _mostwordsperline
1809{
1810    # The person who got the most words per line
1811    my $self = shift;
1812
1813    my %wpl = ();
1814    my $numlines = 0;
1815    my ($avg, $numwords);
1816    foreach my $nick (keys %{ $self->{stats}->{words} }) {
1817        if ($self->{topactive}{$nick} || !$self->{cfg}->{showonlytop}) {
1818          $wpl{$nick} = sprintf("%.2f", $self->{stats}->{words}{$nick}/$self->{stats}->{lines}{$nick});
1819          $numlines += $self->{stats}->{lines}{$nick};
1820          $numwords += $self->{stats}->{words}{$nick};
1821        }
1822    }
1823    if ($numlines > 0) {
1824        $avg = sprintf("%.2f", $numwords/$numlines);
1825    }
1826
1827    my @wpl = sort { $wpl{$b} <=> $wpl{$a} } keys %wpl;
1828
1829    if (@wpl) {
1830        my %hash = (
1831            nick => $wpl[0],
1832            wpl  => $wpl{$wpl[0]}
1833        );
1834
1835        my $text = $self->_template_text('wpl1', %hash);
1836        _html("<tr><td class=\"hicell\">$text");
1837
1838        $hash{avg} = $avg;
1839
1840        $text = $self->_template_text('wpl2', %hash);
1841        _html("<br /><span class=\"small\">$text</span>");
1842        _html("</td></tr>");
1843    }
1844}
1845
1846sub _mostreferencednicks
1847{
1848    # List showing the most referenced nicks
1849    my $self = shift;
1850
1851    my %usages;
1852
1853    foreach my $word (sort keys %{ $self->{stats}->{wordcounts} }) {
1854        next if is_ignored($word);
1855        my $nick = is_nick($word) or next;
1856        next if !exists $self->{stats}->{lines}{$nick};
1857        $usages{$word} = $self->{stats}->{wordcounts}{$word};
1858    }
1859
1860    my @popular = sort { $usages{$b} <=> $usages{$a} } keys %usages;
1861
1862    if (@popular) {
1863
1864        $self->_headline($self->_template_text('referencetopic'));
1865
1866        _html("<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>");
1867        _html("<td>&nbsp;</td><td class=\"tdtop\"><b>" . $self->_template_text('nick') . "</b></td>");
1868        _html("<td class=\"tdtop\"><b>" . $self->_template_text('numberuses') . "</b></td>");
1869        _html("<td class=\"tdtop\"><b>" . $self->_template_text('lastused') . "</b></td></tr>");
1870
1871        for(my $i = 0; $i < $self->{cfg}->{nickhistory}; $i++) {
1872            last if $i >= @popular;
1873            my $a = $i + 1;
1874            my $popular   = $self->_format_word(is_nick($popular[$i]));
1875            my $wordcount = $self->{stats}->{wordcounts}{$popular[$i]};
1876            my $lastused  = $self->_format_word($self->{stats}->{wordnicks}{$popular[$i]} || "");
1877            # this is undefined when a nick is referenced before being used
1878            # first (this is a minor bug we ignore here, see test/01.cfg)
1879
1880            my $class;
1881            if ($a == 1) {
1882                $class = 'hirankc';
1883            } else {
1884                $class = 'rankc';
1885            }
1886            _html("<tr><td class=\"$class\">$a</td>");
1887            _html("<td class=\"hicell\">$popular</td>");
1888            _html("<td class=\"hicell\">$wordcount</td>");
1889            _html("<td class=\"hicell\">$lastused</td>");
1890            _html("</tr>");
1891        }
1892        _html("</table>");
1893    }
1894}
1895
1896sub _smileys
1897{
1898    my $self = shift;
1899
1900    my %usages;
1901    foreach my $smiley (sort keys %{ $self->{stats}->{smileys} }) {
1902        $usages{$smiley} = $self->{stats}->{smileys}{$smiley};
1903    }
1904    my @popular = sort { $usages{$b} <=> $usages{$a} } keys %usages;
1905    return unless @popular;
1906
1907    $self->_headline($self->_template_text('smileytopic'));
1908
1909    _html("<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>");
1910    _html("<td>&nbsp;</td><td class=\"tdtop\"><b>" . $self->_template_text('smiley') . "</b></td>");
1911    _html("<td class=\"tdtop\"><b>" . $self->_template_text('numberuses') . "</b></td>");
1912    _html("<td class=\"tdtop\"><b>" . $self->_template_text('lastused') . "</b></td></tr>");
1913
1914    for(my $i = 0; $i < $self->{cfg}->{smileyhistory}; $i++) {
1915        last if $i >= @popular;
1916        my $a = $i + 1;
1917        my $popular   = $self->_format_word($popular[$i]);
1918        my $count     = $self->{stats}->{smileys}{$popular[$i]};
1919        my $lastused  = $self->_format_word($self->{stats}->{smileynicks}{$popular[$i]} || "");
1920
1921        my $class = ($a == 1) ? 'hirankc' : 'rankc';
1922        _html("<tr><td class=\"$class\">$a</td>");
1923        _html("<td class=\"hicell\">$popular</td>");
1924        _html("<td class=\"hicell\">$count</td>");
1925        _html("<td class=\"hicell\">$lastused</td>");
1926        _html("</tr>");
1927    }
1928    _html("</table>");
1929}
1930
1931sub _karma
1932{
1933    # List showing the most referenced nicks
1934    my $self = shift;
1935
1936    my %karma;
1937
1938    foreach my $thing (sort keys %{ $self->{stats}->{karma} }) {
1939        my $Thing = lc(is_nick($thing) || $thing); # FIXME: this is ugly
1940        foreach my $nick (keys %{ $self->{stats}->{karma}{$thing} }) {
1941            $karma{$Thing} += $self->{stats}->{karma}{$thing}{$nick};
1942        }
1943    }
1944
1945    my @popular = sort { $karma{$b} <=> $karma{$a} } keys %karma;
1946    return unless @popular;
1947
1948    $self->_headline($self->_template_text('karmatopic'));
1949
1950    _html("<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>");
1951    _html("<td>&nbsp;</td><td class=\"tdtop\"><b>" . $self->_template_text('nick') . "</b></td>");
1952    _html("<td class=\"tdtop\"><b>" . $self->_template_text('karma') . "</b></td>");
1953    _html("<td class=\"tdtop\"><b>" . $self->_template_text('goodkarma') . "</b></td>");
1954    _html("<td class=\"tdtop\"><b>" . $self->_template_text('badkarma') . "</b></td></tr>");
1955
1956    my @goodpos = grep { $karma{$_} > 0 } @popular;
1957    splice @goodpos, $self->{cfg}->{karmahistory}, @goodpos
1958        if @goodpos > $self->{cfg}->{karmahistory};
1959    my @badpos = grep { $karma{$_} < 0 } @popular;
1960    splice @badpos, 0, @badpos - $self->{cfg}->{karmahistory}
1961        if @badpos > $self->{cfg}->{karmahistory};
1962
1963    my $pos = 0;
1964    foreach my $thing (@goodpos) {
1965        my $class = ($pos++ == 0) ? 'hirankc' : 'rankc';
1966        my $Thing = $self->_format_word(is_nick($thing) || $thing); # ugliness #2
1967        _html("<tr><td class=\"$class\">$pos</td>");
1968        _html("<td class=\"hicell\">$Thing</td>");
1969        _html("<td class=\"hicell\">$karma{$thing}</td>");
1970
1971        my @k = grep { $self->{stats}->{karma}{$thing}{$_} > 0 }
1972            (keys %{ $self->{stats}->{karma}{$thing} });
1973        @k = $self->_trimlist(@k);
1974        my $n = join ', ', map { $self->_format_word($_) } @k;
1975        _html("<td class=\"hicell\">$n</td>");
1976
1977        @k = grep { $self->{stats}->{karma}{$thing}{$_} < 0 }
1978            (keys %{ $self->{stats}->{karma}{$thing} });
1979        @k = $self->_trimlist(@k);
1980        $n = join ', ', map { $self->_format_word($_) } @k;
1981        _html("<td class=\"hicell\">$n</td>");
1982        _html("</tr>");
1983    }
1984
1985    if (@goodpos and @badpos) {
1986            _html("<tr><td class=\"rankc\"></td>");
1987            _html("<td class=\"hicell\" colspan=\"4\"></td>");
1988        _html("</tr>");
1989    }
1990
1991    $pos = @badpos;
1992    foreach my $thing (@badpos) {
1993        my $class = ($pos == 1) ? 'hirankc' : 'rankc';
1994        my $Thing = $self->_format_word(is_nick($thing) || $thing);
1995        _html("<tr><td class=\"$class\">". ($pos--) ."</td>");
1996        _html("<td class=\"hicell\">$Thing</td>");
1997        _html("<td class=\"hicell\">$karma{$thing}</td>");
1998
1999        my @k = grep { $self->{stats}->{karma}{$thing}{$_} > 0 }
2000            (keys %{ $self->{stats}->{karma}{$thing} });
2001        @k = $self->_trimlist(@k);
2002        my $n = join ', ', map { $self->_format_word($_) } @k;
2003        _html("<td class=\"hicell\">$n</td>");
2004
2005        @k = grep { $self->{stats}->{karma}{$thing}{$_} < 0 }
2006            (keys %{ $self->{stats}->{karma}{$thing} });
2007        @k = $self->_trimlist(@k);
2008        $n = join ', ', map { $self->_format_word($_) } @k;
2009        _html("<td class=\"hicell\">$n</td>");
2010        _html("</tr>");
2011    }
2012    _html("</table>");
2013}
2014
2015sub _mosturls
2016{
2017    # List showing the most referenced URLs
2018    my $self = shift;
2019
2020    my @sorturls = sort { $self->{stats}->{urlcounts}{$b} <=> $self->{stats}->{urlcounts}{$a} }
2021                        keys %{ $self->{stats}->{urlcounts} };
2022
2023    if (@sorturls) {
2024
2025        $self->_headline($self->_template_text('urlstopic'));
2026
2027        _html("<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>");
2028        _html("<td>&nbsp;</td><td class=\"tdtop\"><b>" . $self->_template_text('url') . "</b></td>");
2029        _html("<td class=\"tdtop\"><b>" . $self->_template_text('numberuses') . "</b></td>");
2030        _html("<td class=\"tdtop\"><b>" . $self->_template_text('lastused') . "</b></td></tr>");
2031
2032        for(my $i = 0; $i < $self->{cfg}->{urlhistory}; $i++) {
2033            last unless $i < @sorturls;
2034            my $a = $i + 1;
2035            my $urlcount = $self->{stats}->{urlcounts}{$sorturls[$i]};
2036            my $lastused = $self->{stats}->{urlnicks}{$sorturls[$i]};
2037            my $printurl = $sorturls[$i];
2038            if ($printurl and length($printurl) > 60) {
2039                $printurl = substr($printurl, 0, 60);
2040            }
2041            $printurl = htmlentities($printurl, $self->{cfg}->{charset});
2042            my $linkurl = urlencode($sorturls[$i]);
2043            my $class = ($a == 1) ? 'hirankc' : 'rankc';
2044            _html("<tr><td class=\"$class\">$a</td>");
2045            _html("<td class=\"hicell\"><a href=\"$linkurl\">$printurl</a></td>");
2046            _html("<td class=\"hicell\">$urlcount</td>");
2047            _html("<td class=\"hicell\">$lastused</td>");
2048            _html("</tr>");
2049        }
2050        _html("</table>");
2051    }
2052}
2053
2054sub _charts
2055{
2056    # List showing the most played songs
2057    my $self = shift;
2058
2059    my @sortcharts = sort { $self->{stats}->{chartcounts}{$b} <=> $self->{stats}->{chartcounts}{$a} }
2060                        keys %{ $self->{stats}->{chartcounts} };
2061
2062    if (@sortcharts) {
2063
2064        $self->_headline($self->_template_text('chartstopic'));
2065
2066        _html("<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>");
2067        _html("<td>&nbsp;</td><td class=\"tdtop\"><b>" . $self->_template_text('song') . "</b></td>");
2068        _html("<td class=\"tdtop\"><b>" . $self->_template_text('numberplayed') . "</b></td>");
2069        _html("<td class=\"tdtop\"><b>" . $self->_template_text('playedby') . "</b></td></tr>");
2070
2071        for(my $i = 0; $i < $self->{cfg}->{chartshistory}; $i++) {
2072            last unless $i < @sortcharts;
2073            my $a = $i + 1;
2074            my $song = $sortcharts[$i];
2075            my $chartcount = $self->{stats}->{chartcounts}{$song};
2076            my $lastused = $self->{stats}->{chartnicks}{$song};
2077            $song = $self->{stats}->{word_upcase}{$song};
2078            $song = substr($song, 0, 60) if (length($song) > 60);
2079            $song = $self->_format_word($song);
2080            my $class = ($a == 1) ? 'hirankc' : 'rankc';
2081            _html("<tr><td class=\"$class\">$a</td>");
2082            _html("<td class=\"hicell\">$song</td>");
2083            _html("<td class=\"hicell\">$chartcount</td>");
2084            _html("<td class=\"hicell\">$lastused</td>");
2085            _html("</tr>");
2086        }
2087        _html("</table>");
2088    }
2089}
2090
2091sub _legend
2092{
2093    # A legend showing the timebars and their associated time.
2094    my $self = shift;
2095    _html("<table align=\"center\" border=\"0\" width=\"520\"><tr>");
2096    _html("<td align=\"center\" class=\"asmall\"><img src=\"$self->{cfg}->{piclocation}/$self->{cfg}->{pic_h_0}\" width=\"40\" height=\"15\" align=\"middle\" alt=\"0-5\" /> = 0-5</td>");
2097    _html("<td align=\"center\" class=\"asmall\"><img src=\"$self->{cfg}->{piclocation}/$self->{cfg}->{pic_h_6}\" width=\"40\" height=\"15\" align=\"middle\" alt=\"6-11\" /> = 6-11</td>");
2098    _html("<td align=\"center\" class=\"asmall\"><img src=\"$self->{cfg}->{piclocation}/$self->{cfg}->{pic_h_12}\" width=\"40\" height=\"15\" align=\"middle\" alt=\"12-17\" /> = 12-17</td>");
2099    _html("<td align=\"center\" class=\"asmall\"><img src=\"$self->{cfg}->{piclocation}/$self->{cfg}->{pic_h_18}\" width=\"40\" height=\"15\" align=\"middle\" alt=\"18-23\" /> = 18-23</td>");
2100    _html("</tr></table>");
2101}
2102
2103
2104sub _replace_url # used internally by _replace_links
2105{
2106    my ($self, $url, $www, $ftp, $text) = @_;
2107    $url = "http://$url" if $www;
2108    $url = "ftp://$url" if $ftp;
2109    my $texturl = $self->_template_text("newwindow");
2110    return "<a href=\"$url\" target=\"_blank\" title=\"$texturl $url\">" . $self->_split_long_text($text) . '</a>';
2111}
2112
2113
2114sub _replace_email # used internally by _replace_links
2115{
2116    my ($self, $mailto, $user, $domain, $nick) = @_;
2117    $mailto = '' if $nick or not $mailto;
2118    $nick ||= "$user&#64;$domain"; # obfuscate mail address
2119    my $textmail = $self->_template_text("mailto");
2120    return "<a href=\"mailto:$user&#64;$domain\" title=\"$textmail $nick\">" . $self->_split_long_text($mailto . $nick) . "<\/a>";
2121}
2122
2123
2124sub _replace_links
2125{
2126    # replace URLs and email addresses by links
2127    my ($self, $str, $nick) = @_; # nick is only defined for user links in top table
2128
2129    # Regular expressions are taken from match_urls() and match_email() in
2130    # Common.pm
2131
2132    my (@str) = split(/ /,$str);
2133
2134    foreach (@str) {
2135        s/((?:(?:https?|ftp|telnet|news):\/\/|(?:(?:(www)|(ftp))[\w-]*\.))[-\w\/~\@:]+\.\S+[\w\/])/$self->_replace_url($1, $2, $3, $nick || $1)/egio
2136            or s/(mailto:)?([-\w.]+)@([-\w]+\.[-\w.]+)/$self->_replace_email($1, $2, $3, $nick)/egio
2137            or $_ = $self->_split_long_text($_);
2138     }
2139
2140    return join(' ', @str);
2141}
2142
2143sub _split_long_text
2144{
2145    my ($self, $str) = @_;
2146    $str =~ s/(\S{$self->{cfg}->{quotewidth}})(?!\s)/$1-<br \/>/og;
2147
2148    return($str);
2149}
2150
2151sub _user_linetimes
2152{
2153    my $self = shift;
2154    my $nick  = shift;
2155    my $top   = shift;
2156
2157    my $bar      = "";
2158    my $len      = ($self->{stats}->{lines}{$nick} / $self->{stats}->{lines}{$top}) * 100;
2159
2160    for (my $i = 0; $i <= 3; $i++) {
2161        my $l = $self->{stats}->{line_times}{$nick}[$i];
2162        next if not defined $l;
2163        my $w = int(($l / $self->{stats}->{lines}{$nick}) * $len);
2164        if ($w) {
2165            my $pic = 'pic_h_'.(6*$i);
2166            $bar .= "<img src=\"$self->{cfg}->{piclocation}/$self->{cfg}->{$pic}\" border=\"0\" width=\"$w\" height=\"15\" align=\"middle\" alt=\"$l\" title=\"$l\" />";
2167        }
2168    }
2169    return "$bar&nbsp;$self->{stats}->{lines}{$nick}";
2170}
2171
2172sub _user_wordtimes
2173{
2174    my $self = shift;
2175    my $nick  = shift;
2176    my $top   = shift;
2177
2178    $self->{stats}->{words}{$nick} ||= 0;
2179    my $len = ($self->{stats}->{words}{$nick} / $self->{stats}->{words}{$top}) * 100;
2180
2181    my $bar = "";
2182    for (my $i = 0; $i <= 3; $i++) {
2183        next if not defined $self->{stats}->{word_times}{$nick}[$i];
2184        my $w = int(($self->{stats}->{word_times}{$nick}[$i] / $self->{stats}->{words}{$nick}) * $len);
2185        if ($w) {
2186            my $pic = 'pic_h_'.(6*$i);
2187            $bar .= "<img src=\"$self->{cfg}->{piclocation}/$self->{cfg}->{$pic}\" border=\"0\" width=\"$w\" height=\"15\" align=\"middle\" alt=\"\" />";
2188        }
2189    }
2190    return "$bar&nbsp;$self->{stats}->{words}{$nick}";
2191}
2192
2193sub _user_times
2194{
2195    my $self = shift;
2196    my ($nick) = @_;
2197
2198    my $bar = "";
2199
2200    my $itemstat = ($self->{cfg}->{sortbywords} ? 'words' : 'lines');
2201    my $timestat = ($self->{cfg}->{sortbywords} ? 'word_times' : 'line_times');
2202
2203    for (my $i = 0; $i <= 3; $i++) {
2204        my $l = $self->{stats}->{$timestat}{$nick}[$i];
2205        next if not defined $l;
2206        my $w = int(($l / $self->{stats}->{$itemstat}{$nick}) * 40);
2207        if ($w) {
2208            my $pic = 'pic_h_'.(6*$i);
2209            $bar .= "<img src=\"$self->{cfg}->{piclocation}/$self->{cfg}->{$pic}\" border=\"0\" width=\"$w\" height=\"15\" alt=\"$l\" title=\"$l\" />";
2210        }
2211    }
2212    return $bar;
2213}
2214
2215sub _user_pic
2216{
2217    my $self = shift;
2218    my $nick  = shift;
2219    my $style  = shift;
2220
2221    return unless $self->{users}->{userpics}{$nick} or $self->{cfg}->{defaultpic};
2222
2223    my $rowspan = $self->{cfg}->{userpics} ? " rowspan=\"$self->{cfg}->{userpics}\"" : "";
2224    my $output = "<td $style align=\"center\" valign=\"middle\"$rowspan>";
2225
2226    my $biguserpic = $self->{users}->{biguserpics}{$nick};
2227    if ($biguserpic) {
2228        $biguserpic = $self->{cfg}->{imagepath} .
2229            randomglob($biguserpic, $self->{cfg}->{imageglobpath}, $nick)
2230                if $biguserpic !~ /^http:\/\//i;
2231        $output .= "<a href=\"$biguserpic\">";
2232    }
2233
2234    my $pic = $self->{users}->{userpics}{$nick} || $self->{cfg}->{defaultpic};
2235    $pic = $self->{cfg}->{imagepath} .
2236        randomglob($pic, $self->{cfg}->{imageglobpath}, $nick)
2237        unless $pic =~ /^http:\/\//i;
2238    my $height = $self->{cfg}->{picheight} ? " height=\"$self->{cfg}->{picheight}\"" : "";
2239    my $width = $self->{cfg}->{picwidth} ? " width=\"$self->{cfg}->{picwidth}\"" : "";
2240    my $alt = $self->{users}->{userpics}{$nick} ? " alt=\"$nick\" title=\"$nick\"" : "";
2241    my $border = $biguserpic ? ' border="0"' : '';
2242    $output .= "<img src=\"$pic\"$width$height$alt$border />";
2243
2244    $output .= "</a>" if $biguserpic;
2245    _html("$output</td>");
2246}
2247
2248sub _mostnicks
2249{
2250    # List showing the user with most used nicks
2251    my $self = shift;
2252
2253    my @sortnicks = sort { keys %{ $self->{stats}->{nicks}->{$b} } <=> keys %{ $self->{stats}->{nicks}->{$a} } }
2254                                keys %{ $self->{stats}->{nicks} };
2255
2256    if (keys %{ $self->{stats}->{nicks}->{$sortnicks[0]} } > 1) {
2257
2258        $self->_headline($self->_template_text('mostnickstopic'));
2259
2260        my $names1 = $self->_template_text('names1');
2261        my $names2 = $self->_template_text('names2');
2262        my $nick_txt = $self->_template_text('nick');
2263        my $names_txt = $self->_template_text('names');
2264        _html("<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>");
2265        _html("<td>&nbsp;</td><td class=\"tdtop\"><b>$nick_txt</b></td>");
2266        _html("<td class=\"tdtop\"><b>$names_txt</b></td></tr>");
2267
2268        my $a = 0;
2269        foreach my $nick (@sortnicks) {
2270            next if is_ignored($nick);
2271            my $nickcount = keys %{ $self->{stats}->{nicks}->{$nick} };
2272            my @nicks = values %{ $self->{stats}->{nicks}->{$nick} };
2273            my $nickused = join(", ", $self->_trimlist(@nicks));
2274
2275            last unless ($nickcount > 1);
2276
2277            $a++;
2278            my $class = $a == 1 ? 'hirankc' : 'rankc';
2279            my $n = $nickcount > 1 ? $names1 : $names2;
2280
2281            _html("<tr><td class=\"$class\">$a</td>");
2282            if ($self->{cfg}->{mostnicksverbose}) {
2283                _html("<td class=\"hicell\">$nick ($nickcount $n)</td>");
2284                _html("<td class=\"hicell\" valign='top'>$nickused</td>");
2285            } else {
2286                _html("<td class=\"hicell\">$nick</td>");
2287                _html("<td class=\"hicell\" valign='top'>$nickcount $n</td>");
2288            }
2289            _html("</tr>");
2290            last if $a >= $self->{cfg}->{mostnickshistory};
2291        }
2292        _html("</table>");
2293    }
2294}
2295
2296sub _mostactivebyhour
2297{
2298    # Charts for most active nicks by hour (0-5, 6-11, 12-17, 18-23)
2299    my $self = shift;
2300
2301    my $sortnicks;
2302
2303    my $lastline=-1;
2304    my $maxlines=0;
2305
2306    foreach my $period (0,1,2,3) {
2307        my @sortnicks =
2308        sort
2309        {
2310              (defined $self->{stats}->{line_times}{$b}[$period]?$self->{stats}->{line_times}{$b}[$period]:0)
2311              <=>
2312              (defined $self->{stats}->{line_times}{$a}[$period]?$self->{stats}->{line_times}{$a}[$period]:0)
2313        }
2314        keys %{ $self->{stats}->{line_times} } ;
2315
2316        for(my $i = 0; $i < $self->{cfg}->{activenicksbyhour}; $i++) {
2317            next if ! $sortnicks[$i];
2318            next if is_ignored($sortnicks[$i]);
2319            last unless $i < @sortnicks;
2320
2321            my $nick=$sortnicks[$i];
2322            my $count=$self->{stats}->{line_times}{$nick}[$period] || 0;
2323
2324            last unless $nick;
2325            last unless $count;
2326
2327            $sortnicks->[$period][$i]=$nick;
2328
2329            if ($lastline<$i) {
2330              $lastline=$i;
2331            }
2332
2333
2334            if ($maxlines<$count) {
2335              $maxlines=$count;
2336            }
2337        }
2338    }
2339
2340    if ($lastline>=0) {
2341
2342        $self->_headline($self->_template_text('activenickbyhourtopic'));
2343
2344        _html("<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>");
2345        _html("<td>&nbsp;</td>");
2346        _html("<td class=\"tdtop\"><b>0-5</b></td>");
2347        _html("<td class=\"tdtop\"><b>6-11</b></td>");
2348        _html("<td class=\"tdtop\"><b>12-17</b></td>");
2349        _html("<td class=\"tdtop\"><b>18-23</b></td>");
2350        _html("</tr>");
2351
2352        for(my $i = 0; $i <= $lastline; $i++) {
2353
2354            my $a = $i + 1;
2355            my $class = $a == 1 ? 'hirankc' : 'rankc';
2356
2357            _html("<tr><td class=\"$class\">$a</td>");
2358            foreach my $period (0,1,2,3) {
2359                my $nick=$sortnicks->[$period][$i];
2360                if ($nick) {
2361                    my $count=$self->{stats}->{line_times}{$nick}[$period];
2362                    if ($count) {
2363                        _html("<td class=\"hicell\">");
2364                        if ($self->{cfg}->{showmostactivebyhourgraph}) {
2365                            my $pic = 'pic_h_'.(6*$period);
2366                            my $w = int(($count / $maxlines) * 100) || 1;
2367                            _html("<img src=\"$self->{cfg}->{piclocation}/$self->{cfg}->{$pic}\" border=\"0\" width=\"$w\" height=\"15\" align=\"middle\" alt=\"\" />");
2368                        }
2369                        _html($self->_format_word($nick)." - ".$count);
2370                        _html("</td>");
2371                    } else {
2372                        _html("<td class=\"hicell\">&nbsp;</td>");
2373                    }
2374                } else {
2375                  _html("<td class=\"hicell\">&nbsp;</td>");
2376                }
2377            }
2378            _html("</tr>");
2379        }
2380        _html("</table>");
2381    }
2382}
2383
2384sub _topactive {
2385    my $self = shift;
2386    my @top_active;
2387    my $top_nicks;
2388
2389    if ($self->{cfg}->{sortbywords}) {
2390        @top_active = sort { $self->{stats}->{words}{$b} <=> $self->{stats}->{words}{$a} }
2391                             keys %{ $self->{stats}->{words} };
2392        $top_nicks = scalar keys %{ $self->{stats}->{words} };
2393    } else {
2394        @top_active = sort { $self->{stats}->{lines}{$b} <=> $self->{stats}->{lines}{$a} }
2395                             keys %{ $self->{stats}->{lines} };
2396        $top_nicks = scalar keys %{ $self->{stats}->{lines} };
2397    }
2398
2399    if ($self->{cfg}->{activenicks} > $top_nicks) {
2400        $self->{cfg}->{activenicks} = $top_nicks;
2401    }
2402    if (($self->{cfg}->{activenicks}+$self->{cfg}->{activenicks2}) > $top_nicks) {
2403        $self->{cfg}->{activenicks2} = $top_nicks-$self->{cfg}->{activenicks};
2404    }
2405
2406    (@top_active) = @top_active[0..($self->{cfg}->{activenicks}-1)];
2407    $self->{stats}->{topactive_lines} = @top_active ? $self->{stats}->{lines}{$top_active[0]} : 1;
2408
2409    foreach (@top_active) {
2410        $self->{topactive}{$_} = 1;
2411    }
2412}
2413
2414
2415sub _istoponly {
2416    my $self = shift;
2417    my (@nicks_tmp) = @_;
2418    my @nicks;
2419    my $cnt=0;
2420
2421    if ($self->{cfg}->{showonlytop}) {
2422        foreach my $nick (@nicks_tmp) {
2423            if ($self->{topactive}{$nick}) {
2424                push(@nicks, $nick);
2425            }
2426        }
2427        return(@nicks);
2428    } else {
2429        return(@nicks_tmp);
2430    }
2431}
2432
2433sub _trimlist {
2434    my $self = shift;
2435    return @_ unless $self->{cfg}->{nicklimit};
2436    splice @_, $self->{cfg}->{nicklimit}, @_, qw/.../ if @_ > $self->{cfg}->{nicklimit};
2437    return @_;
2438}
2439
2440sub _activegenders {
2441    # The most active gender in the channel
2442    my $self = shift;
2443    my @topgender = sort {$self->{stats}->{sex_lines}{$b} <=> $self->{stats}->{sex_lines}{$a}} keys %{$self->{stats}->{sex_lines}};
2444
2445    return unless @topgender;
2446
2447    $self->_headline($self->_template_text('activegenderstopic'));
2448    _html("<table border=\"0\" width=\"$self->{cfg}->{tablewidth}\"><tr>");
2449    _html(" <td>&nbsp;</td>"
2450    . "<td class=\"tdtop\"><b>" . $self->_template_text('gender') . "</b></td>"
2451    . "<td class=\"tdtop\"><b>" . $self->_template_text('numberlines') . "</b></td>"
2452    . "<td class=\"tdtop\"><b>" . $self->_template_text('nick') . "</b></td>"
2453    );
2454
2455    my $i = 1;
2456    for my $gender (@topgender) {
2457        my $bar = "";
2458        my $len = ($self->{stats}->{sex_lines}{$gender} / $self->{stats}->{sex_lines}{$topgender[0]}) * 100;
2459
2460        for (0 .. 3) {
2461            next if not defined $self->{stats}->{sex_line_times}{$gender}[$_];
2462            my $w = int(($self->{stats}->{sex_line_times}{$gender}[$_] / $self->{stats}->{sex_lines}{$gender}) * $len);
2463            if ($w) {
2464                my $pic = 'pic_h_'.(6*$_);
2465                $bar .= "<img src=\"$self->{cfg}->{piclocation}/$self->{cfg}->{$pic}\" border=\"0\" width=\"$w\" height=\"15\" align=\"middle\" alt=\"$self->{stats}->{sex_line_times}{$gender}[$_]\" />";
2466            }
2467        }
2468
2469        my @top_active = sort { $self->{stats}->{lines}{$b} <=> $self->{stats}->{lines}{$a} }
2470            grep { $self->{users}->{sex}{$_} and $self->{users}->{sex}{$_} eq $gender }
2471            keys %{ $self->{stats}->{lines} };
2472        my $nicklist = join ", ",
2473            map { $_ . ($self->{stats}->{lines}{$_} ? " ($self->{stats}->{lines}{$_})" : "") }
2474            $self->_trimlist(@top_active);
2475
2476        my $class = ($i == 1 ? "hirankc" : "rankc");
2477        my $span_class = $gender eq 'm' ? "male" : ($gender eq 'f' ? "female" : "bot");
2478        _html("</tr><tr>");
2479        _html(" <td class=\"$class\" align=\"left\">$i</td>");
2480        _html(" <td class=\"hicell\"><span class=\"$span_class\">" . $self->_template_text("gender_$gender") . "</span></td>");
2481        _html(" <td class=\"hicell\"><span style=\"white-space:nowrap;\">$bar</span> $self->{stats}->{sex_lines}{$gender}</td>");
2482        _html(" <td class=\"hicell\">$nicklist</td>");
2483        $i++;
2484    }
2485
2486    _html("</tr></table>");
2487}
2488
24891;
2490
2491__END__
2492
2493=head1 NAME
2494
2495Pisg::HTMLGenerator - class to create a static HTML page out of data parsed
2496
2497=head1 DESCRIPTION
2498
2499C<Pisg::HTMLGenerator> uses the hash returned by Pisg::Parser::Logfile and turns it into a static HTML page.
2500
2501=head1 SYNOPSIS
2502
2503    use Pisg::HTMLGenerator;
2504
2505    $generator = new Pisg::HTMLGenerator(
2506        cfg => $cfg,
2507        stats => $stats,
2508        users => $users,
2509        tmps => $tmps
2510    );
2511
2512=head1 CONSTRUCTOR
2513
2514=over 4
2515
2516=item new ( [ OPTIONS ] )
2517
2518This is the constructor for a new Pisg::HTMLGenerator object. C<OPTIONS> are passed in a hash like fashion using key and value pairs.
2519
2520Possible options are:
2521
2522B<cfg> - hashref containing configuration variables, created by the Pisg module.
2523
2524B<stats> - reference to the hash returned by Pisg::Parser::Logfile containing all stats.
2525
2526B<users> - reference to a hash containg user information
2527
2528B<tmps> - reference to a hash containing the language templates.
2529
2530=back
2531
2532=head1 AUTHOR
2533
2534Morten Brix Pedersen <morten@wtf.dk>
2535
2536=head1 COPYRIGHT
2537
2538Copyright (C) 2001-2005 Morten Brix Pedersen. All rights reserved.
2539Copyright (C) 2003-2005 Christoph Berg <cb@df7cb.de>.
2540This program is free software; you can redistribute it and/or modify it
2541under the terms of the GPL, license is included with the distribution of
2542this file.
2543
2544=cut
2545