summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/smiley.pm
blob: 6f4f49d18f4b3fb08d0d73a8ba877987fb12a94d (plain)
  1. #!/usr/bin/perl
  2. package IkiWiki::Plugin::smiley;
  3. use warnings;
  4. use strict;
  5. use IkiWiki 3.00;
  6. my %smileys;
  7. my $smiley_regexp;
  8. sub import {
  9. add_underlay("smiley");
  10. hook(type => "getsetup", id => "smiley", call => \&getsetup);
  11. hook(type => "sanitize", id => "smiley", call => \&sanitize);
  12. }
  13. sub getsetup () {
  14. return
  15. plugin => {
  16. safe => 1,
  17. # force a rebuild because turning it off
  18. # removes the smileys, which would break links
  19. rebuild => 1,
  20. },
  21. }
  22. sub build_regexp () {
  23. my $srcfile = srcfile("smileys.mdwn", 1);
  24. if (! defined $srcfile) {
  25. print STDERR sprintf(gettext("smiley plugin will not work without %s"),
  26. "smileys.mdwn")."\n";
  27. $smiley_regexp='';
  28. return;
  29. }
  30. my $list=readfile($srcfile);
  31. while ($list =~ m/^\s*\*\s+\\\\([^\s]+)\s+\[\[([^]]+)\]\]/mg) {
  32. my $smiley=$1;
  33. my $file=$2;
  34. $smileys{$smiley}=$file;
  35. # Add a version with < and > escaped, since they probably
  36. # will be (by markdown) by the time the sanitize hook runs.
  37. $smiley=~s/</&lt;/g;
  38. $smiley=~s/>/&gt;/g;
  39. $smileys{$smiley}=$file;
  40. }
  41. if (! %smileys) {
  42. debug(gettext("failed to parse any smileys"));
  43. $smiley_regexp='';
  44. return;
  45. }
  46. # sort and reverse so that substrings come after longer strings
  47. # that contain them, in most cases.
  48. $smiley_regexp='('.join('|', map { quotemeta }
  49. reverse sort keys %smileys).')';
  50. #debug($smiley_regexp);
  51. }
  52. sub sanitize (@) {
  53. my %params=@_;
  54. build_regexp() unless defined $smiley_regexp;
  55. $_=$params{content};
  56. return $_ unless length $smiley_regexp;
  57. MATCH: while (m{(?:^|(?<=\s|>))(\\?)$smiley_regexp(?:(?=\s|<)|$)}g) {
  58. my $escape=$1;
  59. my $smiley=$2;
  60. my $epos=$-[1];
  61. my $spos=$-[2];
  62. # Smilies are not allowed inside <pre> or <code>.
  63. # For each tag in turn, match forward to find the next <tag>
  64. # or </tag> after the smiley.
  65. my $pos=pos;
  66. foreach my $tag ("pre", "code") {
  67. if (m/<(\/)?\s*$tag\s*>/isg && defined $1) {
  68. # </tag> found first, so the smiley is
  69. # inside the tag, so do not expand it.
  70. next MATCH;
  71. }
  72. # Reset pos back to where it was before this test.
  73. pos=$pos;
  74. }
  75. if ($escape) {
  76. # Remove escape.
  77. substr($_, $epos, 1)="";
  78. pos=$epos+1;
  79. }
  80. else {
  81. # Replace the smiley with its expanded value.
  82. my $link=htmllink($params{page}, $params{destpage},
  83. $smileys{$smiley}, linktext => $smiley);
  84. substr($_, $spos, length($smiley))=$link;
  85. pos=$epos+length($link);
  86. }
  87. }
  88. return $_;
  89. }
  90. 1