Fixed Bignumbers Plugin
[onis.git] / lib / Onis / Plugins / Core.pm
1 package Onis::Plugins::Core;
2
3 use strict;
4 use warnings;
5
6 use Carp (qw(confess));
7 use Exporter;
8
9 =head1 NAME
10
11 Onis::Plugins::Core
12
13 =head1 DESCRIPTION
14
15 Plugin for the main table and the hourly-statistics. This is the most
16 complicated plugin so far.
17
18 =cut
19
20 use Onis::Config qw/get_config/;
21 use Onis::Html qw/html_escape get_filehandle/;
22 use Onis::Language qw/translate/;
23 use Onis::Users (qw(get_realname get_link get_image ident_to_name));
24 use Onis::Data::Core qw#get_all_nicks nick_to_ident ident_to_nick get_main_nick register_plugin#;
25 use Onis::Data::Persistent;
26
27 @Onis::Plugins::Core::EXPORT_OK = (qw(get_core_nick_counters get_sorted_nicklist));
28 @Onis::Plugins::Core::ISA = ('Exporter');
29
30 our $NickLinesCounter = Onis::Data::Persistent->new ('NickLinesCounter', 'nick',
31         qw(
32                 lines00 lines01 lines02 lines03 lines04 lines05 lines06 lines07 lines08 lines09 lines10 lines11
33                 lines12 lines13 lines14 lines15 lines16 lines17 lines18 lines19 lines20 lines21 lines22 lines23
34         )
35 );
36 our $NickWordsCounter = Onis::Data::Persistent->new ('NickWordsCounter', 'nick',
37         qw(
38                 words00 words01 words02 words03 words04 words05 words06 words07 words08 words09 words10 words11
39                 words12 words13 words14 words15 words16 words17 words18 words19 words20 words21 words22 words23
40         )
41 );
42 our $NickCharsCounter = Onis::Data::Persistent->new ('NickCharsCounter', 'nick',
43         qw(
44                 chars00 chars01 chars02 chars03 chars04 chars05 chars06 chars07 chars08 chars09 chars10 chars11
45                 chars12 chars13 chars14 chars15 chars16 chars17 chars18 chars19 chars20 chars21 chars22 chars23
46         )
47 );
48
49 our $QuoteCache = Onis::Data::Persistent->new ('QuoteCache', 'key', qw(epoch text));
50 our $QuotePtr = Onis::Data::Persistent->new ('QuotePtr', 'nick', qw(pointer));
51
52 our $QuoteData = {};  # Is generated before output. Nicks are merged according to Data::Core.
53 our $NickData = {};  # Same as above, but for nicks rather than quotes.
54 our $SortedNicklist = [];
55
56 our @H_IMAGES = qw#dark-theme/h-red.png dark-theme/h-blue.png dark-theme/h-yellow.png dark-theme/h-green.png#;
57 our $QuoteCacheSize = 10;
58 our $QuoteMin = 30;
59 our $QuoteMax = 80;
60 our $WORD_LENGTH = 5;
61 our $SORT_BY = 'LINES';
62 our $DISPLAY_LINES = 'BOTH';
63 our $DISPLAY_WORDS = 'NONE';
64 our $DISPLAY_CHARS = 'NONE';
65 our $DISPLAY_TIMES = 0;
66 our $DISPLAY_IMAGES = 0;
67 our $DEFAULT_IMAGE = '';
68 our $BAR_HEIGHT = 130;
69 our $BAR_WIDTH  = 100;
70 our $LongLines  = 50;
71 our $ShortLines = 10;
72
73 if (get_config ('quote_cache_size'))
74 {
75         my $tmp = get_config ('quote_cache_size');
76         $tmp =~ s/\D//g;
77         $QuoteCacheSize = $tmp if ($tmp);
78 }
79 if (get_config ('quote_min'))
80 {
81         my $tmp = get_config ('quote_min');
82         $tmp =~ s/\D//g;
83         $QuoteMin = $tmp if ($tmp);
84 }
85 if (get_config ('quote_max'))
86 {
87         my $tmp = get_config ('quote_max');
88         $tmp =~ s/\D//g;
89         $QuoteMax = $tmp if ($tmp);
90 }
91 if (get_config ('min_word_length'))
92 {
93         my $tmp = get_config ('min_word_length');
94         $tmp =~ s/\D//g;
95         $WORD_LENGTH = $tmp if ($tmp);
96 }
97 if (get_config ('display_lines'))
98 {
99         my $tmp = get_config ('display_lines');
100         $tmp = uc ($tmp);
101
102         if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
103         {
104                 $DISPLAY_LINES = $tmp;
105         }
106         else
107         {
108                 $tmp = get_config ('display_lines');
109                 print STDERR $/, __FILE__, ": ``display_lines'' has been set to the invalid value ``$tmp''. ",
110                 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``both''.";
111         }
112 }
113 if (get_config ('display_words'))
114 {
115         my $tmp = get_config ('display_words');
116         $tmp = uc ($tmp);
117
118         if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
119         {
120                 $DISPLAY_WORDS = $tmp;
121         }
122         else
123         {
124                 $tmp = get_config ('display_words');
125                 print STDERR $/, __FILE__, ": ``display_words'' has been set to the invalid value ``$tmp''. ",
126                 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``none''.";
127         }
128 }
129 if (get_config ('display_chars'))
130 {
131         my $tmp = get_config ('display_chars');
132         $tmp = uc ($tmp);
133
134         if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
135         {
136                 $DISPLAY_CHARS = $tmp;
137         }
138         else
139         {
140                 $tmp = get_config ('display_chars');
141                 print STDERR $/, __FILE__, ": ``display_chars'' has been set to the invalid value ``$tmp''. ",
142                 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``none''.";
143         }
144 }
145 if (get_config ('display_times'))
146 {
147         my $tmp = get_config ('display_times');
148
149         if ($tmp =~ m/true|on|yes/i)
150         {
151                 $DISPLAY_TIMES = 1;
152         }
153         elsif ($tmp =~ m/false|off|no/i)
154         {
155                 $DISPLAY_TIMES = 0;
156         }
157         else
158         {
159                 print STDERR $/, __FILE__, ": ``display_times'' has been set to the invalid value ``$tmp''. ",
160                 $/, __FILE__, ": Valid values are ``true'' and ``false''. Using default value ``false''.";
161         }
162 }
163 if (get_config ('display_images'))
164 {
165         my $tmp = get_config ('display_images');
166
167         if ($tmp =~ m/true|on|yes/i)
168         {
169                 $DISPLAY_IMAGES = 1;
170         }
171         elsif ($tmp =~ m/false|off|no/i)
172         {
173                 $DISPLAY_IMAGES = 0;
174         }
175         else
176         {
177                 print STDERR $/, __FILE__, ": ``display_times'' has been set to the invalid value ``$tmp''. ",
178                 $/, __FILE__, ": Valid values are ``true'' and ``false''. Using default value ``false''.";
179         }
180 }
181 if (get_config ('default_image'))
182 {
183         $DEFAULT_IMAGE = get_config ('default_image');
184 }
185 if (get_config ('sort_by'))
186 {
187         my $tmp = get_config ('sort_by');
188         $tmp = uc ($tmp);
189
190         if (($tmp eq 'LINES') or ($tmp eq 'WORDS') or ($tmp eq 'CHARS'))
191         {
192                 $SORT_BY = $tmp;
193         }
194         else
195         {
196                 $tmp = get_config ('sort_by');
197                 print STDERR $/, __FILE__, ": ``sort_by'' has been set to the invalid value ``$tmp''. ",
198                 $/, __FILE__, ": Valid values are ``lines'' and ``words''. Using default value ``lines''.";
199         }
200 }
201 if (get_config ('horizontal_images'))
202 {
203         my @tmp = get_config ('horizontal_images');
204         my $i;
205         
206         if (scalar (@tmp) != 4)
207         {
208                 print STDERR $/, __FILE__, ": The number of horizontal images is not four. The output might look weird.", $/;
209         }
210
211         for ($i = 0; $i < 4; $i++)
212         {
213                 if (!defined ($tmp[$i]))
214                 {
215                         next;
216                 }
217
218                 $H_IMAGES[$i] = $tmp[$i];
219         }
220 }
221 if (get_config ('bar_height'))
222 {
223         my $tmp = get_config ('bar_height');
224         $tmp =~ s/\D//g;
225         $BAR_HEIGHT = $tmp if ($tmp >= 10);
226 }
227 if (get_config ('bar_width'))
228 {
229         my $tmp = get_config ('bar_width');
230         $tmp =~ s/\D//g;
231         $BAR_WIDTH = $tmp if ($tmp >= 10);
232 }
233 if (get_config ('longlines'))
234 {
235         my $tmp = get_config ('longlines');
236         $tmp =~ s/\D//g;
237         $LongLines = $tmp if ($tmp);
238 }
239 if (get_config ('shortlines'))
240 {
241         my $tmp = get_config ('shortlines');
242         $tmp =~ s/\D//g;
243         if ($tmp or ($tmp == 0))
244         {
245                 $ShortLines = $tmp;
246         }
247 }
248
249 register_plugin ('TEXT', \&add);
250 register_plugin ('ACTION', \&add);
251 register_plugin ('OUTPUT', \&output);
252
253 my $VERSION = '$Id$';
254 print STDERR $/, __FILE__, ": $VERSION" if ($::DEBUG);
255
256 return (1);
257
258 sub add
259 {
260         my $data = shift;
261
262         my $nick = $data->{'nick'};
263         my $ident = $data->{'ident'};
264         my $hour = int ($data->{'hour'});
265         my $host = $data->{'host'};
266         my $text = $data->{'text'};
267         my $type = $data->{'type'};
268         my $time = $data->{'epoch'};
269
270         my $words = scalar (@{$data->{'words'}});
271         my $chars = length ($text);
272
273         if ($type eq 'ACTION')
274         {
275                 $chars -= (length ($nick) + 3);
276         }
277
278         my @counter = $NickLinesCounter->get ($nick);
279         if (!@counter)
280         {
281                 @counter = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
282         }
283         $counter[$hour]++;
284         $NickLinesCounter->put ($nick, @counter);
285
286         @counter = $NickWordsCounter->get ($nick);
287         if (!@counter)
288         {
289                 @counter = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
290         }
291         $counter[$hour] += $words;
292         $NickWordsCounter->put ($nick, @counter);
293
294         @counter = $NickCharsCounter->get ($nick);
295         if (!@counter)
296         {
297                 @counter = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
298         }
299         $counter[$hour] += $chars;
300         $NickCharsCounter->put ($nick, @counter);
301
302         if ((length ($text) >= $QuoteMin)
303                                 and (length ($text) <= $QuoteMax))
304         {
305                 my ($pointer) = $QuotePtr->get ($nick);
306                 $pointer ||= 0;
307
308                 my $key = sprintf ("%s:%02i", $nick, $pointer);
309
310                 $QuoteCache->put ($key, $time, $text);
311
312                 $pointer = ($pointer + 1) % $QuoteCacheSize;
313                 $QuotePtr->put ($nick, $pointer);
314         }
315         return (1);
316 }
317
318 sub calculate
319 {
320         for (get_all_nicks ())
321         {
322                 my $nick = $_;
323                 my $main = get_main_nick ($nick);
324
325                 if (!defined ($NickData->{$main}))
326                 {
327                         $NickData->{$main} =
328                         {
329                                 lines => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
330                                 words => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
331                                 chars => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
332                                 lines_total => 0,
333                                 words_total => 0,
334                                 chars_total => 0
335                         };
336                 }
337
338                 my @counter = $NickLinesCounter->get ($nick);
339                 if (@counter)
340                 {
341                         my $sum = 0;
342                         for (my $i = 0; $i < 24; $i++)
343                         {
344                                 $NickData->{$main}{'lines'}[$i] += $counter[$i];
345                                 $sum += $counter[$i];
346                         }
347                         $NickData->{$main}{'lines_total'} = $sum;
348                 }
349
350                 @counter = $NickWordsCounter->get ($nick);
351                 if (@counter)
352                 {
353                         my $sum = 0;
354                         for (my $i = 0; $i < 24; $i++)
355                         {
356                                 $NickData->{$main}{'words'}[$i] += $counter[$i];
357                                 $sum += $counter[$i];
358                         }
359                         $NickData->{$main}{'words_total'} = $sum;
360                 }
361
362                 @counter = $NickCharsCounter->get ($nick);
363                 if (@counter)
364                 {
365                         my $sum = 0;
366                         for (my $i = 0; $i < 24; $i++)
367                         {
368                                 $NickData->{$main}{'chars'}[$i] += $counter[$i];
369                                 $sum += $counter[$i];
370                         }
371                         $NickData->{$main}{'chars_total'} = $sum;
372                 }
373
374                 if (!defined ($QuoteData->{$main}))
375                 {
376                         $QuoteData->{$main} = [];
377                 }
378         }
379
380         for ($QuoteCache->keys ())
381         {
382                 my $key = $_;
383                 my ($nick, $num) = split (m/:/, $key);
384                 my $main = get_main_nick ($nick);
385
386                 my ($epoch, $text) = $QuoteCache->get ($key);
387                 die unless (defined ($text));
388
389                 if (!defined ($QuoteData->{$main}))
390                 {
391                         die;
392                 }
393                 elsif (scalar (@{$QuoteData->{$main}}) < $QuoteCacheSize)
394                 {
395                         push (@{$QuoteData->{$main}}, [$epoch, $text]);
396                 }
397                 else
398                 {
399                         my $insert = -1;
400                         my $min = $epoch;
401
402                         for (my $i = 0; $i < $QuoteCacheSize; $i++)
403                         {
404                                 if ($QuoteData->{$main}[$i][0] < $min)
405                                 {
406                                         $insert = $i;
407                                         $min = $QuoteData->{$main}[$i][0];
408                                 }
409                         }
410
411                         if ($insert != -1)
412                         {
413                                 $QuoteData->{$main}[$insert] = [$epoch, $text];
414                         }
415                 }
416         }
417 }
418
419 sub output
420 {
421         calculate ();
422         activetimes ();
423         ranking ();
424 }
425         
426 sub activetimes
427 {
428         my $max = 0;            # the most lines that were written in one hour..
429         my $total = 0;          # the total amount of lines we wrote..
430         my $factor = 0;         # used to find a bar's height
431
432         my @data = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
433
434         my @img_urls = get_config ('vertical_images');
435         if (!@img_urls)
436         {
437                 @img_urls = qw#images/ver0n.png images/ver1n.png images/ver2n.png images/ver3n.png#;
438         }
439
440         my $fh = get_filehandle () or die;
441         
442 # this for loop looks for the most amount of lines in one hour and sets
443 # $most_lines
444         for (keys %$NickData)
445         {
446                 my $nick = $_;
447
448                 for (my $i = 0; $i < 24; $i++)
449                 {
450                         $data[$i] += $NickData->{$nick}{'chars'}[$i];
451                 }
452         }
453
454         for (my $i = 0; $i < 24; $i++)
455         {
456                 $max = $data[$i] if ($max < $data[$i]);
457                 $total += $data[$i];
458         }
459
460         if (!$total)
461         {
462                 $total = 1;
463                 $max = 1;
464         }
465
466         $factor = (($BAR_HEIGHT - 1) / $max);
467
468         my $header = translate ('When do we actually talk here?');
469         print $fh "<h2>$header</h2>\n",
470         qq#<table class="hours_of_day">\n#,
471         qq#  <tr>\n#;
472
473 # this for circles through the four colors. Each color represents six hours.
474 # (4 * 6 hours = 24 hours)
475         for (my $i = 0; $i <= 3; $i++)
476         {
477                 for (my $j = 0; $j <= 5; $j++)
478                 {
479                         my $hour = (($i * 6) + $j);
480                         if (!defined ($data[$hour]))
481                         {
482                                 $data[$hour] = 0;
483                         }
484
485                         my $percent = 100 * ($data[$hour] / $total);
486                         my $height = int ($data[$hour] * $factor) + 1;
487                         my $img_url = $img_urls[$i];
488                         
489                         print $fh '    <td>', sprintf ("%2.1f", $percent),
490                         qq#%<br /><img src="$img_url" style="height: $height#,
491                         qq#px;" alt="" /></td>\n#;
492                 }
493         }
494
495         print $fh "  </tr>\n",
496         qq#  <tr class="hour_row">\n#;
497         print $fh map { "    <td>$_</td>\n" } (0 .. 23);
498         print $fh "  </tr>\n",
499         "</table>\n\n";
500 }
501
502 sub ranking
503 {
504         my $count = 0;
505
506         my @nicks = keys (%$NickData);
507
508         return unless (@nicks);
509         
510         my $max_lines = 1;
511         my $max_words = 1;
512         my $max_chars = 1;
513         
514         my $linescount = 0;
515
516         my $fh = get_filehandle () or die;
517
518         my $sort_field = lc ($SORT_BY);
519
520         my $trans;
521
522         my $tmp;
523         ($tmp) = sort { $NickData->{$b}{'lines_total'} <=> $NickData->{$a}{'lines_total'} } (@nicks);
524         $max_lines = $NickData->{$tmp}{'lines_total'} || 0;
525         
526         ($tmp) = sort { $NickData->{$b}{'words_total'} <=> $NickData->{$a}{'words_total'} } (@nicks);
527         $max_words = $NickData->{$tmp}{'words_total'} || 0;
528         
529         ($tmp) = sort { $NickData->{$b}{'chars_total'} <=> $NickData->{$a}{'chars_total'} } (@nicks);
530         $max_chars = $NickData->{$tmp}{'chars_total'} || 0;
531         
532         $trans = translate ('Most active nicks');
533         
534         print $fh "<h2>$trans</h2>\n";
535         if ($SORT_BY eq 'LINES')
536         {
537                 $trans = translate ('Nicks sorted by numbers of lines written');
538         }
539         elsif ($SORT_BY eq 'WORDS')
540         {
541                 $trans = translate ('Nicks sorted by numbers of words written');
542         }
543         else # ($SORT_BY eq 'CHARS')
544         {
545                 $trans = translate ('Nicks sorted by numbers of characters written');
546         }
547         print $fh "<p>($trans)</p>\n";
548
549         print $fh <<EOF;
550
551 <table class="big_ranking">
552   <tr>
553     <td class="invis">&nbsp;</td>
554 EOF
555         if ($DISPLAY_IMAGES)
556         {
557                 $trans = translate ('Image');
558                 print $fh "    <th>$trans</th>\n";
559         }
560         #if (true)
561         {
562                 $trans = translate ('Nick');
563                 print $fh "    <th>$trans</th>\n";
564         }
565         if ($DISPLAY_LINES ne 'NONE')
566         {
567                 $trans = translate ('Number of Lines');
568                 print $fh "    <th>$trans</th>\n";
569         }
570         if ($DISPLAY_WORDS ne 'NONE')
571         {
572                 $trans = translate ('Number of Words');
573                 print $fh "    <th>$trans</th>\n";
574         }
575         if ($DISPLAY_CHARS ne 'NONE')
576         {
577                 $trans = translate ('Number of Characters');
578                 print $fh "    <th>$trans</th>\n";
579         }
580         if ($DISPLAY_TIMES)
581         {
582                 $trans = translate ('When?');
583                 print $fh "    <th>$trans</th>\n";
584         }
585         
586         $trans = translate ('Random Quote');
587         print $fh "    <th>$trans</th>\n",
588         "  </tr>\n";
589
590         @$SortedNicklist = sort
591         {
592                 $NickData->{$b}{"${sort_field}_total"} <=> $NickData->{$a}{"${sort_field}_total"}
593         } (@nicks);
594
595         @nicks = ();
596
597         for (@$SortedNicklist)
598         {
599                 my $nick = $_;
600                 my $ident = nick_to_ident ($nick);
601                 my $name  = ident_to_name ($ident);
602                 my $print = $name || $nick;
603
604                 $linescount++;
605
606                 # As long as we didn't hit the 
607                 # $LongLines-limit we continue
608                 # our table..
609                 if ($linescount <= $LongLines)
610                 {
611                         my $quote = translate ('-- no quote available --');
612
613                         if (@{$QuoteData->{$nick}})
614                         {
615                                 my $num = scalar (@{$QuoteData->{$nick}});
616                                 my $rand = int (rand ($num));
617
618                                 $quote = html_escape ($QuoteData->{$nick}[$rand][1]);
619                         }
620
621                         my $link = '';
622                         my $image = '';
623                         my $realname = '';
624                         if ($name)
625                         {
626                                 $link     = get_link ($name);
627                                 $image    = get_image ($name);
628                                 $realname = get_realname ($name);
629                         }
630                         
631                         print $fh "  <tr>\n",
632                         qq#    <td class="numeration"># . $linescount . "</td>\n";
633
634                         if ($DISPLAY_IMAGES)
635                         {
636                                 if ($DEFAULT_IMAGE and !$image)
637                                 {
638                                         $image = $DEFAULT_IMAGE;
639                                 }
640                                 
641                                 print $fh qq#    <td class="image">#;
642                                 if ($image)
643                                 {
644                                         if ($link)
645                                         {
646                                                 print $fh qq#<a href="$link">#;
647                                         }
648                                         print $fh qq#<img src="$image" alt="$name" />#;
649                                         if ($link)
650                                         {
651                                                 print $fh "</a>";
652                                         }
653                                 }
654                                 else
655                                 {
656                                         print $fh '&nbsp;';
657                                 }
658                                 print $fh "</td>\n";
659                         }
660                         
661                         my $title = $realname;
662                         if (!$title)
663                         {
664                                 $title = "User: $name; " if ($name);
665                                 $title .= "Ident: $ident";
666                         }
667                         print $fh qq#    <td class="nick" title="$title">#;
668
669                         if ($link)
670                         {
671                                 print $fh qq#<a href="$link">$print</a></td>\n#
672                         }
673                         else
674                         {
675                                 print $fh qq#$print</td>\n#;
676                         }
677                 
678                         if ($DISPLAY_LINES ne 'NONE')
679                         {
680                                 print $fh qq#    <td class="bar">#;
681                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'BAR'))
682                                 {
683                                         my $code = bar ($max_lines, $NickData->{$nick}{'lines'});
684                                         print $fh $code;
685                                 }
686                                 print $fh '&nbsp;' if ($DISPLAY_LINES eq 'BOTH');
687                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'NUMBER'))
688                                 {
689                                         print $fh $NickData->{$nick}{'lines_total'};
690                                 }
691                                 print $fh "</td>\n";
692                         }
693
694                         if ($DISPLAY_WORDS ne 'NONE')
695                         {
696                                 print $fh qq#    <td class="bar">#;
697                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'BAR'))
698                                 {
699                                         my $code = bar ($max_words, $NickData->{$nick}{'words'});
700                                         print $fh $code;
701                                 }
702                                 print $fh '&nbsp;' if ($DISPLAY_WORDS eq 'BOTH');
703                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'NUMBER'))
704                                 {
705                                         print $fh $NickData->{$nick}{'words_total'};
706                                 }
707                                 print $fh "</td>\n";
708                         }
709
710                         if ($DISPLAY_CHARS ne 'NONE')
711                         {
712                                 print $fh qq#    <td class="bar">#;
713                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'BAR'))
714                                 {
715                                         my $code = bar ($max_chars, $NickData->{$nick}{'chars'});
716                                         print $fh $code;
717                                 }
718                                 print $fh '&nbsp;' if ($DISPLAY_CHARS eq 'BOTH');
719                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'NUMBER'))
720                                 {
721                                         print $fh $NickData->{$nick}{'chars_total'};
722                                 }
723                                 print $fh "</td>\n";
724                         }
725
726                         if ($DISPLAY_TIMES)
727                         {
728                                 my $code = bar ($NickData->{$nick}{'chars_total'}, $NickData->{$nick}{'chars'});
729                                 print $fh qq#    <td class="bar">$code</td>\n#;
730                         }
731
732                         print $fh qq#    <td class="quote">$quote</td>\n#,
733                         qq#  </tr>\n#;
734                         
735                         if ($linescount == $LongLines)
736                         {
737                                 print $fh "</table>\n\n";
738                         }
739                 }
740
741                 # Ok, we have too many people to
742                 # list them all so we start a
743                 # smaller table and just list the
744                 # names.. (Six names per line..)
745                 elsif ($linescount <= ($LongLines + 6 * $ShortLines))
746                 {
747                         my $row_in_this_table = int (($linescount - $LongLines - 1) / 6);
748                         my $col_in_this_table = ($linescount - $LongLines - 1) % 6;
749
750                         my $total = 0;
751                         if ($SORT_BY eq 'LINES')
752                         {
753                                 $total = $NickData->{$nick}{'lines_total'};
754                         }
755                         elsif ($SORT_BY eq 'WORDS')
756                         {
757                                 $total = $NickData->{$nick}{'words_total'};
758                         }
759                         else # ($SORT_BY eq 'CHARS')
760                         {
761                                 $total = $NickData->{$nick}{'chars_total'};
762                         }
763
764                         my $title = $name ? get_realname ($name) : '';
765                         if (!$title)
766                         {
767                                 $title = "User: $name; " if ($name);
768                                 $title .= "Ident: $ident";
769                         }
770                         
771                         if ($row_in_this_table == 0 and $col_in_this_table == 0)
772                         {
773                                 $trans = translate ("They didn't write so much");
774                                 print $fh "<h2>$trans</h2>\n",
775                                 qq#<table class="small_ranking">\n#,
776                                 qq#  <tr>\n#;
777                         }
778                         
779                         if ($col_in_this_table == 0 and $row_in_this_table != 0)
780                         {
781                                 print $fh "  </tr>\n",
782                                 qq#  <tr>\n#;
783                         }
784                         
785                         print $fh qq#    <td title="$title">$print ($total)</td>\n#;
786                         
787                         if ($row_in_this_table == $ShortLines and $col_in_this_table == 5)
788                         {
789                                 print $fh "  </tr>\n",
790                                 qq#</table>\n\n#;
791                         }
792                 }
793
794                 # There is no else. There are
795                 # just too many people around.
796                 # I might add a "There are xyz
797                 # unmentioned nicks"-line..
798         }
799
800         if (($linescount > $LongLines)
801                         and ($linescount <= ($LongLines + 6 * $ShortLines)))
802         {
803                 my $col = ($linescount - $LongLines - 1) % 6;
804
805                 while ($col < 5)
806                 {
807                         print $fh qq#    <td>&nbsp;</td>\n#;
808                         $col++;
809                 }
810
811                 print $fh "  </tr>\n";
812         }
813
814         if ($linescount != $LongLines)
815         {
816                 print $fh "</table>\n\n";
817         }
818 }
819
820 # this is called by "&ranking ();" and prints the horizontal usage-bar in the
821 # detailed nick-table
822 sub bar
823 {
824         my $max_num = shift;
825         my $source = shift;
826
827         confess () unless (ref ($source) eq 'ARRAY');
828
829         # BAR_WIDTH is a least 10
830         my $max_width = $BAR_WIDTH - 4;
831         my $factor = 1;
832         my $retval = '';
833
834         my $i;
835         my $j;
836
837         if (!$max_num) { return ($retval); }
838         $factor = $max_width / $max_num;
839
840         for ($i = 0; $i < 4; $i++)
841         {
842                 my $sum = 0;
843                 my $width = 1;
844                 my $img = $H_IMAGES[$i];
845
846                 for ($j = 0; $j < 6; $j++)
847                 {
848                         my $hour = ($i * 6) + $j;
849                         $sum += $source->[$hour];
850                 }
851
852                 $width += int (0.5 + ($sum * $factor));
853                 
854                 $retval .= qq#<img src="$img" style="width: # . $width . q#px"#;
855                 if ($i == 0) { $retval .= qq# class="first"#; }
856                 elsif ($i == 3) { $retval .= qq# class="last"#; }
857                 $retval .= ' alt="" />';
858         }
859
860         return ($retval);
861 }
862
863 =head1 EXPORTED FUNCTIONS
864
865 =over 4
866
867 =item B<get_core_nick_counters> (I<$nick>)
868
869 Returns a hash-ref that containes all the nick-counters available. It looks
870 like this:
871
872     {
873         lines => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
874         words => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
875         chars => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
876         lines_total => 0,
877         words_total => 0,
878         chars_total => 0
879     }
880
881 =cut
882
883 sub get_core_nick_counters
884 {
885         my $nick = shift;
886
887         if (!defined ($NickData->{$nick}))
888         {
889                 return ({});
890         }
891
892         return ($NickData->{$nick});
893 }
894
895 =item B<get_sorted_nicklist> ()
896
897 Returns an array-ref that containes all nicks, sorted by the field given in the
898 config-file.
899
900 =cut
901
902 sub get_sorted_nicklist
903 {
904         return ($SortedNicklist);
905 }
906
907 =back
908
909 =head1 AUTHOR
910
911 Florian octo Forster, E<lt>octo at verplant.orgE<gt>
912
913 =cut