aboutsummaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/table.pm
diff options
context:
space:
mode:
Diffstat (limited to 'IkiWiki/Plugin/table.pm')
-rw-r--r--IkiWiki/Plugin/table.pm200
1 files changed, 200 insertions, 0 deletions
diff --git a/IkiWiki/Plugin/table.pm b/IkiWiki/Plugin/table.pm
new file mode 100644
index 000000000..e4d987dd3
--- /dev/null
+++ b/IkiWiki/Plugin/table.pm
@@ -0,0 +1,200 @@
+package IkiWiki::Plugin::table;
+# by Victor Moral <victor@taquiones.net>
+
+use warnings;
+use strict;
+
+use IkiWiki;
+use IkiWiki::Plugin::mdwn;
+
+my %defaults = (
+ data => undef,
+ file => undef,
+ format => 'auto',
+ sep_char => {
+ 'csv' => ',',
+ 'dsv' => '\|',
+ },
+ class => undef,
+ header => 1,
+);
+
+sub import { #{{{
+ hook(type => "preprocess", id => "table", call => \&preprocess);
+} # }}}
+
+sub preprocess (@) { #{{{
+ my %params = (%defaults, @_);
+
+ if (defined $params{delimiter}) {
+ $params{sep_char}->{$params{format}} = $params{delimiter};
+ }
+ if (defined $params{file}) {
+ if (! $pagesources{$params{file}}) {
+ return "[[table cannot find file]]";
+ }
+ $params{data} = readfile(srcfile($params{file}));
+ }
+
+ if (lc $params{format} eq 'auto') {
+ # first try the more simple format
+ if (is_dsv_data($params{data})) {
+ $params{format} = 'dsv';
+ $params{sep_char}->{dsv} = '\|';
+ }
+ else {
+ $params{format} = 'csv';
+ $params{sep_char}->{csv} = ',';
+ }
+ }
+
+ my @data;
+ if (lc $params{format} eq 'csv') {
+ @data=read_csv(\%params);
+ }
+ elsif (lc $params{format} eq 'dsv') {
+ @data=read_dsv(\%params);
+ }
+ else {
+ return "[[table unknown data format]]";
+ }
+
+ my $header;
+ if ($params{header} != 1) {
+ $header=shift @data;
+ }
+ if (! @data) {
+ return "[[table has empty data]]";
+ }
+
+ my $html = tidy_up(open_table(\%params, $header),
+ build_rows(\%params, @data),
+ close_table(\%params, $header));
+
+ if (defined $params{file}) {
+ return $html."\n\n".
+ htmllink($params{page}, $params{destpage}, $params{file},
+ linktext => gettext('Direct data download'));
+ }
+ else {
+ return $html;
+ }
+} #}}}
+
+sub tidy_up (@) { #{{{
+ my $html="";
+
+ foreach my $text (@_) {
+ my $indentation = $text =~ m{thead>|tbody>} ? 0 :
+ $text =~ m{tr>} ? 4 :
+ $text =~ m{td>|th>} ? 8 :
+ 0;
+ $html .= (' ' x $indentation)."$text\n";
+ }
+
+ return $html;
+} #}}}
+
+sub is_dsv_data ($) { #{{{
+ my $text = shift;
+
+ my ($line) = split(/\n/, $text);
+ return $line =~ m{.+\|};
+}
+
+sub read_csv ($) { #{{{
+ my $params=shift;
+ my @text_lines = split(/\n/, $params->{data});
+
+ eval q{use Text::CSV};
+ error($@) if $@;
+ my $csv = Text::CSV->new({
+ sep_char => $params->{sep_char}->{csv},
+ binary => 1,
+ }) || error("could not create a Text::CSV object");
+
+ my $l=0;
+ my @data;
+ foreach my $line (@text_lines) {
+ $l++;
+ if ($csv->parse($line)) {
+ push(@data, [ $csv->fields() ]);
+ }
+ else {
+ debug(sprintf(gettext('parse fail at line %d: %s'),
+ $l, $csv->error_input()));
+ }
+ }
+
+ return @data;
+} #}}}
+
+sub read_dsv ($) { #{{{
+ my $params = shift;
+ my @text_lines = split(/\n/, $params->{data});
+
+ my @data;
+ my $splitter = qr{$params->{sep_char}->{dsv}};
+ foreach my $line (@text_lines) {
+ push @data, [ split($splitter, $line) ];
+ }
+
+ return @data;
+} #}}}
+
+sub open_table ($$) { #{{{
+ my $params = shift;
+ my $header = shift;
+
+ my @items;
+ push @items, defined $params->{class}
+ ? "<table class=\"".$params->{class}.'">'
+ : '<table>';
+ push @items, '<thead>','<tr>',
+ (map { "<th>".htmlize($params, $_)."</th>" } @$header),
+ '</tr>','</thead>' if defined $header;
+ push @items, '<tbody>';
+
+ return @items;
+}
+
+sub build_rows ($@) { #{{{
+ my $params = shift;
+
+ my @items;
+ foreach my $record (@_) {
+ push @items, '<tr>',
+ (map { "<td>".htmlize($params, $_)."</td>" } @$record),
+ '</tr>';
+ }
+ return @items;
+} #}}}
+
+sub close_table ($$) { #{{{
+ my $params = shift;
+ my $header = shift;
+
+ my @items;
+ push @items, '</tbody>' if defined $header;
+ push @items, '</table>';
+ return @items;
+} #}}}
+
+sub htmlize { #{{{
+ my $params = shift;
+ my $text = shift;
+
+ $text=IkiWiki::preprocess($params->{page},
+ $params->{destpage}, $text);
+ $text=IkiWiki::htmlize($params->{page},
+ pagetype($pagesources{$params->{page}}), $text);
+
+ # hack to get rid of enclosing junk added by markdown
+ $text=~s!^<p>!!;
+ $text=~s!</p>$!!;
+ chomp $text;
+
+ return $text;
+}
+
+1