aboutsummaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/sparkline.pm
diff options
context:
space:
mode:
Diffstat (limited to 'IkiWiki/Plugin/sparkline.pm')
-rw-r--r--IkiWiki/Plugin/sparkline.pm173
1 files changed, 173 insertions, 0 deletions
diff --git a/IkiWiki/Plugin/sparkline.pm b/IkiWiki/Plugin/sparkline.pm
new file mode 100644
index 000000000..e28d2605a
--- /dev/null
+++ b/IkiWiki/Plugin/sparkline.pm
@@ -0,0 +1,173 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::sparkline;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+use IPC::Open2;
+
+my $match_num=qr/[-+]?[0-9]+(?:\.[0-9]+)?/;
+my %locmap=(
+ top => 'TEXT_TOP',
+ right => 'TEXT_RIGHT',
+ bottom => 'TEXT_BOTTOM',
+ left => 'TEXT_LEFT',
+);
+
+sub import {
+ hook(type => "getsetup", id => "sparkline", call => \&getsetup);
+ hook(type => "preprocess", id => "sparkline", call => \&preprocess);
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => undef,
+ section => "widget",
+ },
+}
+
+sub preprocess (@) {
+ my %params=@_;
+
+ my $php;
+
+ my $style=(exists $params{style} && $params{style} eq "bar") ? "Bar" : "Line";
+ $php=qq{<?php
+ require_once('sparkline/Sparkline_$style.php');
+ \$sparkline = new Sparkline_$style();
+ \$sparkline->SetDebugLevel(DEBUG_NONE);
+ };
+
+ foreach my $param (qw{BarWidth BarSpacing YMin YMaz}) {
+ if (exists $params{lc($param)}) {
+ $php.=qq{\$sparkline->Set$param(}.int($params{lc($param)}).qq{);\n};
+ }
+ }
+
+ my $c=0;
+ while (@_) {
+ my $key=shift;
+ my $value=shift;
+
+ if ($key=~/^($match_num)(?:,($match_num))?(?:\(([a-z]+)\))?$/) {
+ $c++;
+ my ($x, $y);
+ if (defined $2) {
+ $x=$1;
+ $y=$2;
+ }
+ else {
+ $x=$c;
+ $y=$1;
+ }
+ if ($style eq "Bar" && defined $3) {
+ $php.=qq{\$sparkline->SetData($x, $y, '$3');\n};
+ }
+ else {
+ $php.=qq{\$sparkline->SetData($x, $y);\n};
+ }
+ }
+ elsif (! length $value) {
+ error gettext("parse error")." \"$key\"";
+ }
+ elsif ($key eq 'featurepoint') {
+ my ($x, $y, $color, $diameter, $text, $location)=
+ split(/\s*,\s*/, $value);
+ if (! defined $diameter || $diameter < 0) {
+ error gettext("invalid featurepoint diameter");
+ }
+ $x=int($x);
+ $y=int($y);
+ $color=~s/[^a-z]+//g;
+ $diameter=int($diameter);
+ $text=~s/[^-a-zA-Z0-9]+//g if defined $text;
+ if (defined $location) {
+ $location=$locmap{$location};
+ if (! defined $location) {
+ error gettext("invalid featurepoint location");
+ }
+ }
+ $php.=qq{\$sparkline->SetFeaturePoint($x, $y, '$color', $diameter};
+ $php.=qq{, '$text'} if defined $text;
+ $php.=qq{, $location} if defined $location;
+ $php.=qq{);\n};
+ }
+ }
+
+ if ($c eq 0) {
+ error gettext("missing values");
+ }
+
+ my $height=int($params{height} || 20);
+ if ($height < 2 || $height > 100) {
+ error gettext("invalid height value");
+ }
+ if ($style eq "Bar") {
+ $php.=qq{\$sparkline->Render($height);\n};
+ }
+ else {
+ if (! exists $params{width}) {
+ error gettext("missing width parameter");
+ }
+ my $width=int($params{width});
+ if ($width < 2 || $width > 1024) {
+ error gettext("invalid width value");
+ }
+ $php.=qq{\$sparkline->RenderResampled($width, $height);\n};
+ }
+
+ $php.=qq{\$sparkline->Output();\n?>\n};
+
+ # Use the sha1 of the php code that generates the sparkline as
+ # the base for its filename.
+ eval q{use Digest::SHA};
+ error($@) if $@;
+ my $fn=$params{page}."/sparkline-".
+ IkiWiki::possibly_foolish_untaint(Digest::SHA::sha1_hex($php)).
+ ".png";
+ will_render($params{page}, $fn);
+
+ if (! -e "$config{destdir}/$fn") {
+ my $pid;
+ my $sigpipe=0;
+ $SIG{PIPE}=sub { $sigpipe=1 };
+ $pid=open2(*IN, *OUT, "php");
+
+ # open2 doesn't respect "use open ':utf8'"
+ binmode (OUT, ':utf8');
+
+ print OUT $php;
+ close OUT;
+
+ my $png;
+ {
+ local $/=undef;
+ $png=<IN>;
+ }
+ close IN;
+
+ waitpid $pid, 0;
+ $SIG{PIPE}="DEFAULT";
+ if ($sigpipe || ! defined $png) {
+ error gettext("failed to run php");
+ }
+
+ if (! $params{preview}) {
+ writefile($fn, $config{destdir}, $png, 1);
+ }
+ else {
+ # in preview mode, embed the image in a data uri
+ # to avoid temp file clutter
+ eval q{use MIME::Base64};
+ error($@) if $@;
+ return "<img src=\"data:image/png;base64,".
+ encode_base64($png)."\" />";
+ }
+ }
+
+ return '<img src="'.urlto($fn, $params{destpage}).'" alt="graph" />';
+}
+
+1