usemod.pl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #!/usr/bin/env perl
  2. # ====================[ usemod.pl ]====================
  3. use strict;
  4. use v5.10;
  5. AddModuleDescription('usemod.pl', 'Usemod Markup Extension');
  6. our ($q, $bol, %RuleOrder, @MyRules, @MyInitVariables, $PortraitSupportColor, $PortraitSupportColorDiv);
  7. our ($RFCPattern, $ISBNPattern, @HtmlTags, $HtmlTags, $HtmlLinks, $RawHtml,
  8. $UseModSpaceRequired, $UseModExtraSpaceRequired, $UseModMarkupInTitles);
  9. push(@MyRules, \&UsemodRule);
  10. # The ---- rule conflicts with the --- rule in markup.pl and portrait-support.pl
  11. # The == heading rule conflicts with the same rule in portrait-support.pl
  12. # The : indentation rule conflicts with a similar rule in portrait-support.pl
  13. $RuleOrder{\&UsemodRule} = 100;
  14. $RFCPattern = 'RFC\\s?(\\d+)';
  15. $ISBNPattern = 'ISBN:?([0-9- xX]{10,14})';
  16. $HtmlLinks = 0; # 1 = <a href="foo">desc</a> is a link
  17. $RawHtml = 0; # 1 = allow <HTML> environment for raw HTML inclusion
  18. @HtmlTags = (); # List of HTML tags. If not set, determined by $HtmlTags
  19. $HtmlTags = 0; # 1 = allow some 'unsafe' HTML tags
  20. $UseModSpaceRequired = 1; # 1 = require space after * # : ; for lists.
  21. $UseModMarkupInTitles = 0; # 1 = may use links and other markup in ==titles==
  22. $UseModExtraSpaceRequired = 0; # 1 = require space before : in definition lists
  23. # do this later so that the user can customize some vars
  24. push(@MyInitVariables, \&UsemodInit);
  25. sub UsemodInit {
  26. if (not @HtmlTags) { # do not override settings in the config file
  27. if ($HtmlTags) { # allow many tags
  28. @HtmlTags = qw(b i u font big small sub sup h1 h2 h3 h4 h5 h6 cite code
  29. em s strike strong tt var div center blockquote ol ul dl
  30. table caption br p hr li dt dd tr td th);
  31. } else { # only allow a very small subset
  32. @HtmlTags = qw(b i u em strong tt);
  33. }
  34. }
  35. }
  36. my $UsemodHtmlRegExp;
  37. my $rowcount;
  38. sub UsemodRule {
  39. $UsemodHtmlRegExp = join('|',(@HtmlTags)) unless $UsemodHtmlRegExp;
  40. # <pre> for monospaced, preformatted and escaped
  41. if ($bol && m/\G&lt;pre&gt;\n?(.*?\n)&lt;\/pre&gt;[ \t]*\n?/cgs) {
  42. return CloseHtmlEnvironments() . $q->pre({-class=>'real'}, $1) . AddHtmlEnvironment('p');
  43. }
  44. # <code> for monospaced and escaped
  45. elsif (m/\G\&lt;code\&gt;(.*?)\&lt;\/code\&gt;/cgis) { return $q->code($1); }
  46. # <nowiki> for escaped
  47. elsif (m/\G\&lt;nowiki\&gt;(.*?)\&lt;\/nowiki\&gt;/cgis) { return $1; }
  48. # whitespace for monospaced, preformatted and escaped, all clean
  49. # note that ([ \t]+(.+\n)*.*) seems to crash very long blocks (2000 lines and more)
  50. elsif ($bol && m/\G(\s*\n)*([ \t]+.+)\n?/cg) {
  51. my $str = $2;
  52. while (m/\G([ \t]+.*)\n?/cg) {
  53. $str .= "\n" . $1;
  54. }
  55. return OpenHtmlEnvironment('pre',1) . $str; # always level 1
  56. }
  57. # unumbered lists using *
  58. elsif ($bol && m/\G(\s*\n)*(\*+)[ \t]{$UseModSpaceRequired,}/cg
  59. or InElement('li') && m/\G(\s*\n)+(\*+)[ \t]{$UseModSpaceRequired,}/cg) {
  60. return CloseHtmlEnvironmentUntil('li') . OpenHtmlEnvironment('ul',length($2))
  61. . AddHtmlEnvironment('li');
  62. }
  63. # numbered lists using #
  64. elsif ($bol && m/\G(\s*\n)*(\#+)[ \t]{$UseModSpaceRequired,}/cg
  65. or InElement('li') && m/\G(\s*\n)+(\#+)[ \t]{$UseModSpaceRequired,}/cg) {
  66. return CloseHtmlEnvironmentUntil('li') . OpenHtmlEnvironment('ol',length($2))
  67. . AddHtmlEnvironment('li');
  68. }
  69. # indented text using : (use blockquote instead?)
  70. elsif ($bol && m/\G(\s*\n)*(\:+)[ \t]{$UseModSpaceRequired,}/cg
  71. or InElement('dd') && m/\G(\s*\n)+(\:+)[ \t]{$UseModSpaceRequired,}/cg) {
  72. return CloseHtmlEnvironmentUntil('dd') . OpenHtmlEnvironment('dl',length($2), 'quote')
  73. . $q->dt() . AddHtmlEnvironment('dd');
  74. }
  75. # definition lists using ;
  76. elsif (($bol and m/\G(\s*\n)*(\;+)[ \t]{$UseModSpaceRequired,}(?=.*[ \t]{$UseModExtraSpaceRequired,}\:)/cg) or
  77. (InElement('dd') and m/\G(\s*\n)+(\;+)[ \t]{$UseModSpaceRequired,}(?=.*[ \t]{$UseModExtraSpaceRequired,}\:)/cg)) {
  78. return CloseHtmlEnvironmentUntil('dd')
  79. .OpenHtmlEnvironment('dl', length($2))
  80. .AddHtmlEnvironment('dt'); # `:' needs special treatment, later
  81. }
  82. elsif (InElement('dt') and m/\G(?<=[ \t]){$UseModExtraSpaceRequired,}:[ \t]*/cg) {
  83. return CloseHtmlEnvironmentUntil('dt')
  84. .CloseHtmlEnvironment()
  85. .AddHtmlEnvironment('dd');
  86. }
  87. # headings using = (with lookahead)
  88. elsif ($bol && $UseModMarkupInTitles
  89. && m/\G(\s*\n)*(\=+)[ \t]*(?=[^=\n]+=)/cg) {
  90. my $depth = length($2);
  91. $depth = 6 if $depth > 6;
  92. $depth = 2 if $depth < 2;
  93. my $html = CloseHtmlEnvironments() . ($PortraitSupportColorDiv ? '</div>' : '')
  94. . AddHtmlEnvironment('h' . $depth);
  95. $PortraitSupportColorDiv = 0; # after the HTML has been determined.
  96. $PortraitSupportColor = 0;
  97. return $html;
  98. } elsif ($UseModMarkupInTitles
  99. && (InElement('h1') || InElement('h2') || InElement('h3')
  100. || InElement('h4') || InElement('h5') || InElement('h6'))
  101. && m/\G[ \t]*=+\n?/cg) {
  102. return CloseHtmlEnvironments() . AddHtmlEnvironment('p');
  103. } elsif ($bol && !$UseModMarkupInTitles
  104. && m/\G(\s*\n)*(\=+)[ \t]*(.+?)[ \t]*(=+)[ \t]*\n?/cg) {
  105. my $html = CloseHtmlEnvironments() . ($PortraitSupportColorDiv ? '</div>' : '')
  106. . WikiHeading($2, $3) . AddHtmlEnvironment('p');
  107. $PortraitSupportColorDiv = 0; # after the HTML has been determined.
  108. $PortraitSupportColor = 0;
  109. return $html;
  110. }
  111. # horizontal lines using ----
  112. elsif ($bol && m/\G(\s*\n)*----+[ \t]*\n?/cg) {
  113. my $html = CloseHtmlEnvironments() . ($PortraitSupportColorDiv ? '</div>' : '')
  114. . $q->hr() . AddHtmlEnvironment('p');
  115. $PortraitSupportColorDiv = 0;
  116. $PortraitSupportColor = 0;
  117. return $html;
  118. }
  119. # tables using || -- the first row of a table
  120. elsif ($bol && m/\G(\s*\n)*((\|\|)+)([ \t])*(?=.*\|\|[ \t]*(\n|$))/cg) {
  121. $rowcount = 1;
  122. return OpenHtmlEnvironment('table',1,'user')
  123. . AddHtmlEnvironment('tr', 'class="odd first"')
  124. . AddHtmlEnvironment('td', UsemodTableAttributes(length($2)/2, $4));
  125. }
  126. # tables using || -- end of the row and beginning of the next row
  127. elsif (InElement('td') && m/\G[ \t]*((\|\|)+)[ \t]*\n((\|\|)+)([ \t]*)/cg) {
  128. my $attr = UsemodTableAttributes(length($3)/2, $5);
  129. my $type = ++$rowcount % 2 ? 'odd' : 'even';
  130. $attr = " " . $attr if $attr;
  131. return qq{</td></tr><tr class="$type"><td$attr>};
  132. }
  133. # tables using || -- an ordinary table cell
  134. elsif (InElement('td') && m/\G[ \t]*((\|\|)+)([ \t]*)(?!(\n|$))/cg) {
  135. my $attr = UsemodTableAttributes(length($1)/2, $3);
  136. $attr = " " . $attr if $attr;
  137. return "</td><td$attr>";
  138. }
  139. # tables using || -- since "next row" was taken care of above, this must be the last row
  140. elsif (InElement('td') && m/\G[ \t]*((\|\|)+)[ \t]*/cg) {
  141. return CloseHtmlEnvironments() . AddHtmlEnvironment('p');
  142. }
  143. # RFC
  144. elsif (m/\G$RFCPattern/cg) { return &RFC($1); }
  145. # ISBN -- dirty because the URL translations will change
  146. elsif (m/\G($ISBNPattern)/cg) { Dirty($1); print ISBN($2); return ''; }
  147. # traditional wiki syntax closure for bold italic'''''
  148. elsif (InElement('strong') and InElement('em') and m/\G'''''/cg) { # close both
  149. return CloseHtmlEnvironment('strong').CloseHtmlEnvironment('em');
  150. }
  151. # traditional wiki syntax for '''bold'''
  152. elsif (m/\G'''/cg) { return AddOrCloseHtmlEnvironment('strong'); }
  153. # traditional wiki syntax for ''italic''
  154. elsif (m/\G''/cg ) { return AddOrCloseHtmlEnvironment('em'); }
  155. # <html> for raw html
  156. elsif ($RawHtml && m/\G\&lt;html\&gt;(.*?)\&lt;\/html\&gt;/cgis) {
  157. return UnquoteHtml($1);
  158. }
  159. # miscellaneous html tags
  160. elsif (m/\G\&lt;($UsemodHtmlRegExp)(\s+[^<>]*?)?\&gt;/cgi) {
  161. return AddHtmlEnvironment($1, $2); }
  162. elsif (m/\G\&lt;\/($UsemodHtmlRegExp)\&gt;/cgi) {
  163. return CloseHtmlEnvironment($1); }
  164. elsif (m/\G\&lt;($UsemodHtmlRegExp) *\/\&gt;/cgi) {
  165. return "<$1 />"; }
  166. # <a ...>text</a> for html links
  167. elsif ($HtmlLinks && m/\G\&lt;a(\s+href="\S+")\&gt;(.*?)\&lt;\/a\&gt;/cgi) {
  168. return "<a$1>$2</a>";
  169. }
  170. return;
  171. }
  172. sub UsemodTableAttributes {
  173. my ($span, $left, $right) = @_;
  174. my $attr = '';
  175. $attr = "colspan=\"$span\"" if ($span != 1);
  176. m/\G(?=.*?([ \t]*)\|\|)/;
  177. $right = $1;
  178. $attr .= ' ' if ($attr and ($left or $right));
  179. if ($left and $right) { $attr .= 'align="center"' }
  180. elsif ($left ) { $attr .= 'align="right"' }
  181. elsif ($right) { $attr .= 'align="left"' }
  182. return $attr;
  183. }
  184. sub WikiHeading {
  185. my ($depth, $text) = @_;
  186. $depth = length($depth);
  187. $depth = 6 if $depth > 6;
  188. $depth = 2 if $depth < 2;
  189. return "<h$depth>$text</h$depth>";
  190. }
  191. sub RFC {
  192. my $num = shift;
  193. return $q->a({-href=>"http://tools.ietf.org/html/rfc${num}"}, "RFC $num");
  194. }
  195. sub ISBN {
  196. my $rawnum = shift;
  197. my $num = $rawnum;
  198. my $rawprint = $rawnum;
  199. $rawprint =~ s/ +$//;
  200. $num =~ s/[- ]//g;
  201. my $len = length($num);
  202. return "ISBN $rawnum" unless $len == 10 or $len == 13 or $len = 14; # be prepared for 2007-01-01
  203. my $first = $q->a({-href => Ts('http://search.barnesandnoble.com/booksearch/isbninquiry.asp?ISBN=%s', $num)},
  204. "ISBN " . $rawprint);
  205. my $second = $q->a({-href => Ts('http://www.amazon.com/exec/obidos/ISBN=%s', $num)},
  206. T('alternate'));
  207. my $third = $q->a({-href => Ts('http://www.pricescan.com/books/BookDetail.asp?isbn=%s', $num)},
  208. T('search'));
  209. my $html = "$first ($second, $third)";
  210. $html .= ' ' if ($rawnum =~ / $/); # Add space if old ISBN had space.
  211. return $html;
  212. }
  213. =head1 COPYRIGHT AND LICENSE
  214. The information below applies to everything in this distribution,
  215. except where noted.
  216. Copyright 2008, 2009, 2010 by Alex Schroeder <alex@gnu.org>.
  217. Copyleft 2008 by Brian Curry <http://raiazome.com>.
  218. Copyright 2008 by Weakish Jiang <weakish@gmail.com>.
  219. Copyright 2004, 2005, 2006, 2007 by Alex Schroeder <alex@gnu.org>.
  220. This program is free software; you can redistribute it and/or modify
  221. it under the terms of the GNU General Public License as published by
  222. the Free Software Foundation; either version 3 of the License, or
  223. (at your option) any later version.
  224. This program is distributed in the hope that it will be useful,
  225. but WITHOUT ANY WARRANTY; without even the implied warranty of
  226. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  227. GNU General Public License for more details.
  228. You should have received a copy of the GNU General Public License
  229. along with this program. If not, see L<http://www.gnu.org/licenses/>.
  230. =cut