Initial commit and migration to Mercurial.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Apache/OTL.pm Fri Jul 24 07:39:57 2009 -0700
@@ -0,0 +1,232 @@
+#!/usr/bin/perl
+
+package Apache::OTL;
+use strict;
+use Apache2::Const qw/ DECLINED OK /;
+use Time::HiRes qw/ gettimeofday /;
+
+sub handler
+{
+ my $r = shift;
+ my $VERSION = '0.4';
+ my $t0 = Time::HiRes::gettimeofday;
+ my (
+ $file, # the absolute file path
+ $title, # the file's title
+ $uri, # the file uri
+ %re, # a hash of pre compiled regular expressions
+ $data, # file contents
+ %opt, # options from the otl file
+ @blocks, # todo groupings
+ $mtime, # last modification time of otl file
+ %get, # get arguments (sorting, etc)
+ );
+
+ return DECLINED unless $r->method() eq 'GET';
+ ($file, $uri) = ($r->filename, $r->uri);
+ return DECLINED unless -e $file;
+ $mtime = localtime( (stat(_))[9] );
+
+ %get = $r->args;
+
+ %re =
+ (
+ title => qr/(?:.+)?\/(.+).otl$/i,
+ percent => qr/(\[.\]) (\d+)%/,
+ todo => qr/(\[_\]) /,
+ done => qr/(\[X\]) /,
+ comment => qr/^(?:\t+)?:(.+)/,
+ time => qr/(\d{2}:\d{2}:\d{2})/,
+ date => qr/(\d{2,4}-\d{2}-\d{2})/,
+ subitem => qr/^\t(?!\t)/,
+ line_wo_tabs => qr/^(?:\t+)?(.+)/,
+ linetext => qr/^(?:\[.\] (?:\d+%)?)? (.+)/,
+ );
+
+ open OTL, "$file"
+ || ( $r->log_error("Unable to read $file: $!") && return DECLINED );
+ do {
+ local $/ = undef;
+ $data = <OTL>; # shlorp
+ };
+ close OTL;
+
+ # just spit out the plain otl if requested.
+ if ($get{show} eq 'source') {
+ $r->content_type('text/plain');
+ $r->print( $data );
+ return OK;
+ }
+
+ # divide each outline into groups
+ # skip blocks that start with a comment '#'
+ @blocks = grep { $_ !~ /^\#/ } split /\n\n+/, $data;
+
+ # get optional settings and otl title
+ {
+ my $settings = shift @blocks;
+ if ($settings =~ $re{comment}) {
+ %opt = map { split /=/ } split /\s?:/, $settings;
+ }
+
+ # if the first group wasn't a comment,
+ # we probably just aren't using a settings
+ # line. push the group back into place.
+ else {
+ unshift @blocks, $settings;
+ }
+ }
+
+ # GET args override settings
+ $opt{$_} = $get{$_} foreach keys %get;
+
+ # set title (fallback to file uri)
+ $title =
+ $opt{title}
+ ? $opt{title}
+ : $1 if $uri =~ $re{title};
+
+ $opt{style} ||= '/otl_style.css';
+
+ $r->content_type('text/html');
+ $r->print(<<EHTML);
+<html>
+ <!--
+ generated by otl_handler $VERSION
+ Mahlon E. Smith <mahlon\@spime.net>
+
+ http://www.vimoutliner.org/
+ -->
+ <head>
+ <title>$title</title>
+ <link href="$opt{style}" rel="stylesheet" media="screen" type="text/css">
+EHTML
+
+ if ($opt{js}) {
+ $r->print(
+ ' ' x 8,
+ "<script type=\"text/javascript\" language=\"JavaScript\" src=\"$opt{js}\"></script>\n",
+ ' ' x 4, "</head>\n",
+ "<body onLoad=\"init_page()\">\n",
+ );
+ } else {
+ $r->print(<<EHTML);
+ </head>
+ <body>
+EHTML
+ }
+
+ $r->print("<div class=\"header\">$opt{title}</div>\n") if $opt{title};
+ $r->print("<div class=\"last_mod\">Last modified: $mtime</div>\n") if $opt{last_mod};
+ if ($opt{legend}) {
+ $r->print(<<EHTML);
+<div class="legend">
+<span class="done"> </span> Item completed<br />
+<span class="todo"> </span> Item is incomplete<br />
+</div>
+EHTML
+ }
+ if ($opt{sort}) {
+ my %sorts = (
+ alpha => 'alphabetical',
+ percent => 'percentages',
+ );
+ $r->print("<div class=\"sort\">Sort: \n");
+ foreach (sort keys %sorts) {
+ if ($opt{sorttype} eq $_ && $opt{sortrev}) {
+ $r->print("<a href=\"$uri?sorttype=$_\">$sorts{$_}</a> ");
+ } elsif ($opt{sorttype} eq $_ && ! $opt{sortrev}) {
+ $r->print("<a href=\"$uri?sorttype=$_&sortrev=1\">$sorts{$_}</a> ");
+ } else {
+ $r->print("<a href=\"$uri?sorttype=$_\">$sorts{$_}</a> ");
+ }
+ }
+ $r->print("</div>\n");
+ }
+
+ my $bc = 0;
+ foreach my $block ( sort { sorter(\%opt, \%re) } @blocks ) {
+ # separate outline items
+ my @items = split /\n/, $block;
+ $r->print("<div class=\"group\">\n") if $opt{divs};
+ my $lc = 0;
+
+ # get item counts
+ my ($subs, $comments, $subsubs);
+ if ($opt{counts}) {
+ foreach (@items) {
+ if (/$re{comment}/) {
+ $comments++;
+ } elsif (/$re{subitem}/) {
+ $subs++;
+ }
+ }
+ $subsubs = (scalar @items - 1) - $subs - $comments;;
+ }
+
+ # parse
+ foreach (@items) {
+ next if /^\#/;
+ my $level = tr/\t/\t/ || 0;
+ next unless /\w/;
+
+ # append counts
+ if ($lc == 0 && $opt{counts} && $_ !~ $re{comment}) {
+ my $itmstr = $subs == 1 ? 'item' : 'items';
+ my $sitmstr = $subsubs == 1 ? 'subitem' : 'subitems';
+ $_ .= " <span class=\"counts\">$subs $itmstr, $subsubs $sitmstr</span>";
+ }
+ s/^:// if ! $level;
+
+ if ($opt{js}) {
+ s#(.+)#<span id=\"itemtoplevel_$bc\">$1</span># if $lc == 0;
+ $r->print("<span id=\"itemgroup_$bc\">\n") if $lc == 1;
+ }
+
+ s#$re{'time'}#<span class="time">$1</span>#g if /$re{'time'}/;
+ s#$re{date}#<span class="date">$1</span>#g if /$re{date}/;
+ s#$re{percent}#$1 <span class="percent">$2%</span># if /$re{percent}/;
+ s#$re{todo}#<span class="todo"> </span># if /$re{todo}/;
+ s#$re{done}#<span class="done"> </span># if /$re{done}/;
+ s#$re{comment}#<span class="comment">$1</span># if /$re{comment}/;
+ s#$re{line_wo_tabs}#<span class="level$level">$1</span>#;
+
+ $r->print("$_\n");
+ $lc++;
+ }
+ $r->print("</span>\n") if $opt{js};
+ $r->print("</div>\n") if $opt{divs};
+ $r->print("<br /><hr /><br />\n") if $opt{dividers};
+ $r->print("<br /><br />\n") unless $opt{divs} || $opt{dividers};
+ $bc++;
+ }
+
+ my $t1 = Time::HiRes::gettimeofday;
+ my $td = sprintf("%0.3f", $t1 - $t0);
+ $r->print("<div class=\"timer\">OTL parsed in $td secs</div>") if $opt{timer};
+ $r->print(<<EHTML);
+ </body>
+</html>
+EHTML
+
+ return OK;
+}
+
+sub sorter
+{
+ my ($opt, $re) = @_;
+ return 0 unless $opt->{sorttype};
+ my ($sa, $sb);
+ if ($opt->{sorttype} eq 'percent') {
+ $sa = $2 if $a =~ $re->{percent};
+ $sb = $2 if $b =~ $re->{percent};
+ return $opt->{sortrev} ? $sb <=> $sa : $sa <=> $sb;
+ }
+ else {
+ $sa = $1 if $a =~ $re->{linetext};
+ $sb = $1 if $b =~ $re->{linetext};
+ return $opt->{sortrev} ? $sb cmp $sa : $sa cmp $sb;
+ }
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/README Fri Jul 24 07:39:57 2009 -0700
@@ -0,0 +1,149 @@
+---------------------------------------------------------------------
+ WHAT IS THIS?
+---------------------------------------------------------------------
+
+Vimoutliner already comes with some otl to HTML converters that work
+quite well. I maintain a few different otl files, that are displayed
+on a internal intranet - the step of converting to HTML on every little
+change before upload was becoming mildly irritating, and countering my
+near legendary laziness.
+
+This mod_perl handler teaches apache how to pretty print otl natively.
+
+Now, I can just edit the otl files directly - skip the conversion step
+altogether, and let Apache make some delicious looking outlines.
+
+---------------------------------------------------------------------
+ INSTALLATION
+---------------------------------------------------------------------
+
+First off all, make sure you have a mod_perl enabled Apache2.
+
+1) Add the following lines in your httpd.conf, or in a
+ separate otl.conf in the apache Includes directory:
+
+ -------------------------
+ PerlSwitches -I/path/to/perl/libraries
+ PerlModule Apache::OTL
+
+ <FilesMatch ".*\.otl">
+ SetHandler perl-script
+ PerlResponseHandler Apache::OTL
+ </FilesMatch>
+ -------------------------
+
+2) Put the included css at /otl_style.css in your document root.
+
+
+That's it! Apache will now pretty-print all your otl files.
+
+---------------------------------------------------------------------
+ SETTINGS
+---------------------------------------------------------------------
+
+Settings for the otl_handler are stored on the first line of the otl
+files themselves, prefixed by a colon. See the sample.otl for an
+example settings line. All settings are entirely optional.
+
+title
+ Type: string
+ Default: filename
+
+ The title of the OTL. Used as a header, and the html title.
+ If this is not set, the html title is derived from the filename.
+
+
+style
+ Type: string
+ Default: /otl_style.css
+
+ A path to the css style.
+
+
+js
+ Type: string
+ Default: none
+
+ Use javascript? If set, loads an external javascript library,
+ and calls init_page() on body load.
+ See the example 'folding' javascript included.
+
+
+divs
+ Type: boolean
+ Default: 0
+
+ Wrap each outline group in a div class called "group"
+
+
+dividers
+ Type: boolean
+ Default: 0
+
+ Separate each outline group with a horizontal rule?
+
+
+legend
+ Type: boolean
+ Default: 0
+
+ Display small legend for todo and done items?
+
+
+sort
+ Type: boolean
+ Default: 0
+
+ Show sort links?
+
+
+sorttype
+ Type: string
+ Default: none
+
+ Default sorting method. Valid values are
+ percent
+ alpha
+
+
+sortrev
+ Type: boolean
+ Default: 0
+
+ Should we default to reverse sorting?
+
+
+counts
+ Type: boolean
+ Default: 0
+
+ Count and display sub items?
+
+
+timer
+ Type: boolean
+ Default: 0
+
+ Display how long the parser took to generate the html?
+
+
+---------------------------------------------------------------------
+ INCLUDED FILES
+---------------------------------------------------------------------
+
+otl_handler.pl
+ The mod_perl code itself.
+ Feel free to modify to taste.
+
+themes/*
+ Example css. Again, modify to taste!
+
+otl.js
+ Example (but functional!) javascript. If you use this
+ file, your top level items will be 'clickable' - expanding
+ the sub items underneath, but not initially showing them.
+
+sample.otl
+ An example vimoutliner file, with optional settings.
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/otl.js Fri Jul 24 07:39:57 2009 -0700
@@ -0,0 +1,109 @@
+
+// otl_handler javascript functions
+
+
+var scroll = new Array();
+var itemcount = 0;
+
+function init_page()
+{
+ if (! document.getElementById ) return false;
+
+ var spans = document.getElementsByTagName('span');
+ for (i = 0; i < spans.length; i++) {
+ var id = spans[i].getAttribute('id');
+ if (id == null || id == "") continue;
+ if (id.indexOf("itemtoplevel_") == -1) continue;
+
+ // ie doesn't support negative substr positions :\
+ // var num = id.substr(-1, 1);
+ var num = id.substr(13, 1);
+ var itemtoplevel = spans[i];
+ var itemgroup = document.getElementById("itemgroup_" + num);
+ if (! itemtoplevel || ! itemgroup) continue;
+
+ itemcount++;
+
+ itemgroup.style.display = 'none';
+ itemgroup.style.overflow = 'hidden';
+ itemtoplevel.onmouseover = function() { this.className = 'level0_over'; }
+ itemtoplevel.onmouseout = function() { this.className = 'level0'; }
+ itemtoplevel.onmouseup = function() { this.className = 'level0'; toggle(this); return false; }
+ itemtoplevel.onselectstart = function() { return false; }
+
+ }
+
+ return;
+}
+
+
+function toggle(i)
+{
+ var ig = document.getElementById( i.id.replace("toplevel", "group") );
+ if (! ig ) return;
+
+ var num = ig.id.substr(10,1);
+
+ // show
+ if (ig.style.display == "" ||
+ ig.style.display == "none") {
+
+ ig.style.height = "0pt";
+ ig.style.display = 'block';
+ grow(num);
+
+ // hide others
+ for (i = 0; i != itemcount; i++) {
+ if (i != num) shrink(i);
+ }
+
+ }
+ // hide
+ else {
+ shrink(num);
+ }
+
+ return;
+}
+
+function grow(num)
+{
+ var ig = document.getElementById( "itemgroup_" + num );
+ if (! ig ) return;
+ scroll[num] = 1;
+
+ var curheight = parseInt(ig.style.height.replace("pt", ""));
+ if (curheight >= 250) {
+ ig.style.overflow = 'auto';
+ scroll[num] = 0;
+ return;
+ }
+
+ var newheight = curheight + 25 + "pt";
+ ig.style.height = newheight;
+
+ setTimeout("grow(" + num + ")", 30);
+ return;
+}
+
+function shrink(num)
+{
+ var ig = document.getElementById( "itemgroup_" + num );
+ if (! ig ) return;
+ if (scroll[num] == 1) return;
+ ig.style.overflow = 'hidden';
+
+ var curheight = parseInt(ig.style.height.replace("pt", ""));
+ if (curheight == 0) {
+ ig.style.display = 'none';
+ return;
+ }
+
+ var newheight = curheight - 50 + "pt";
+ ig.style.height = newheight;
+
+ setTimeout("shrink(" + num + ")", 30);
+ return;
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sample.otl Fri Jul 24 07:39:57 2009 -0700
@@ -0,0 +1,37 @@
+:title=Sample OTL list :style=/otl_style.css :divs=1 :dividers=0 :legend=1 :js=/otl.js :last_mod=1
+
+[_] 58% Things to get for party
+ [_] 50% Food
+ [_] Chips
+ [X] Dips
+ [X] Honey roasted peanuts
+ [_] Sausage
+ [_] 66% Party favors
+ [X] Hats
+ [X] Whistles
+ [_] Beer bong
+
+[_] 18% House projects
+ [_] 12% Paint
+ [_] 25% Buy paint and supplies
+ [_] Paint
+ [X] Brushes
+ [_] Trays
+ [_] Overalls
+ [_] 0% Rooms done
+ [_] Bathroom
+ [_] Bedroom
+ : Red?
+ [_] 20% Upgrade electrical
+ [_] 2 circuits to computer room
+ [_] 40% Get equipment
+ [X] Romex wire
+ [_] 0% Entry feed wire
+ : How much of this do I really need?
+ : I should probably go out to the street
+ : and measure stuff.
+ [_] Service meter
+ [X] Grounding rods
+ [_] Breakers
+ [_] Learn about electricity
+ [_] Don't die
Binary file themes/bg.gif has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/themes/otl_style.css Fri Jul 24 07:39:57 2009 -0700
@@ -0,0 +1,153 @@
+body
+{
+ font-family: Verdana;
+ font-size: 12px;
+ color: black;
+ background-color: #efefef;
+ margin: 30px 30px 100px 30px;
+}
+
+.legend
+{
+ margin-bottom: 10px;
+}
+
+.timer
+{
+ position: absolute;
+ top: 5;
+ right: 10;
+ color: #ccc;
+}
+
+.group
+{
+ padding: 0px;
+ background-color: white;
+ width: 600px;
+ border: 1px solid #ccc;
+ margin-bottom: 5px;
+}
+
+.header
+{
+ font-size: 24px;
+ font-variant: small-caps;
+ font-weight: bold;
+}
+
+.last_mod
+{
+ display: block;
+ border-top: 1px solid #ccc;
+ font-style: italic;
+ color: #777;
+}
+
+.counts
+{
+ font-size: 11px;
+ display: block;
+ color: #777;
+ margin-left: 30px;
+ font-style: italic;
+}
+
+.sort
+{
+ margin-bottom: 30px;
+ border-bottom: 1px solid #ccc;
+ font-weight: bold;
+}
+
+.sort a
+{
+ font-weight: normal;
+ text-decoration: none;
+ color: #777;
+}
+
+.sort a:hover
+{
+ color: black;
+}
+
+.date, .time { }
+
+.level0
+{
+ background-color: #ddd;
+ font-size: 18px;
+ font-weight: bold;
+ display: block;
+ cursor: pointer;
+ -moz-user-select: none;
+}
+
+.level0_over
+{
+ display: block;
+ background-color: #ecebe2;
+ cursor: pointer;
+}
+
+.level1
+{
+ font-size: 14px;
+ font-weight: bold;
+ margin-left: 15px;
+ color: #333;
+ display: block;
+}
+
+.level2
+{
+ font-size: 12px;
+ margin-left: 30px;
+ color: #555;
+ display: block;
+}
+
+.level3
+{
+ font-size: 10px;
+ margin-left: 45px;
+ color: #777;
+ display: block;
+}
+
+.level4
+{
+ font-size: 10px;
+ margin-left: 60px;
+ color: #aaa;
+ display: block;
+}
+
+.percent
+{
+ font-weight: bold;
+ color: #7c8ee8;
+}
+
+.todo
+{
+ background-color: #ccc;
+ padding-right: 12px;
+ margin-right: 10px;
+}
+
+.done
+{
+ background-color: #7c8ee8;
+ margin-right: 10px;
+ padding-right: 12px;
+}
+
+.comment
+{
+ font-weight: normal;
+ font-style: italic;
+ display: block;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/themes/otl_style2.css Fri Jul 24 07:39:57 2009 -0700
@@ -0,0 +1,136 @@
+body
+{
+ font-family: Verdana;
+ font-size: 11px;
+ background-color: white;
+ margin: 30px 30px 100px 30px;
+}
+
+.header
+{
+ font-size: 18px;
+ font-weight: bold;
+}
+
+.last_mod
+{
+ display: block;
+ border-top: 1px solid #ccc;
+ font-style: italic;
+ color: #777;
+}
+
+.legend {}
+.date {}
+.time {}
+
+.group
+{
+ width: 50%;
+ min-width: 500px;
+ margin-bottom: 5px;
+ border-bottom: 1px solid #eee;
+}
+
+.timer
+{
+ position: absolute;
+ top: 5;
+ right: 10;
+ color: #999;
+}
+
+.counts
+{
+ color: #777;
+ margin-left: 10px;
+ font-weight: normal;
+ font-size: 10px;
+ font-style: italic;
+}
+
+.sort
+{
+ margin-bottom: 15px;
+ font-weight: bold;
+}
+
+.sort a
+{
+ font-weight: normal;
+ text-decoration: none;
+ color: #777;
+}
+
+.sort a:hover
+{
+ color: black;
+}
+
+.level0
+{
+ font-weight: bold;
+ cursor: pointer;
+ display: block;
+ -moz-user-select: none;
+}
+
+.level0_over
+{
+ background-color: #eee;
+ cursor: pointer;
+ display: block;
+}
+
+.level1
+{
+ display: block;
+ margin-left: 20px;
+}
+
+.level2
+{
+ display: block;
+ margin-left: 40px;
+}
+
+.level3
+{
+ display: block;
+ margin-left: 60px;
+}
+
+.level4
+{
+ display: block;
+ margin-left: 80px;
+}
+
+.percent
+{
+ color: #8193c8;
+ font-weight: bold;
+}
+
+.todo
+{
+ padding-left: 10px;
+ margin-right: 5px;
+ border: .5px solid #c88181;
+ background-color: #fbf5f5;
+}
+
+.done
+{
+ padding-left: 10px;
+ margin-right: 5px;
+ border: .5px solid #8193c8;
+ background-color: #f5f7fb;
+}
+
+.comment
+{
+ font-style: italic;
+ display: block;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/themes/otl_style3.css Fri Jul 24 07:39:57 2009 -0700
@@ -0,0 +1,158 @@
+body
+{
+ font-family: Verdana;
+ font-size: 12px;
+ color: black;
+ background-color: #efefef;
+ margin: 30px 30px 100px 30px;
+}
+
+.legend
+{
+ margin-bottom: 10px;
+}
+
+.timer
+{
+ position: absolute;
+ top: 5;
+ right: 10;
+ color: #ccc;
+}
+
+.group
+{
+ padding: 0px;
+ background-color: white;
+ border: 1px solid #ccc;
+ margin-bottom: 5px;
+ width: 600px;
+}
+
+.header
+{
+ font-size: 24px;
+ font-variant: small-caps;
+ font-weight: bold;
+}
+
+.last_mod
+{
+ display: block;
+ border-top: 1px solid #ccc;
+ font-style: italic;
+ color: #777;
+}
+
+.counts
+{
+ font-size: 11px;
+ display: block;
+ color: #777;
+ margin-left: 30px;
+ font-style: italic;
+}
+
+.sort
+{
+ margin-bottom: 30px;
+ border-bottom: 1px solid #ccc;
+ font-weight: bold;
+}
+
+.sort a
+{
+ font-weight: normal;
+ text-decoration: none;
+ color: #777;
+}
+
+.sort a:hover
+{
+ color: black;
+}
+
+.time, .date
+{
+ font-weight: normal;
+ font-size: 10px;
+ color: #777;
+}
+
+.level0
+{
+ background-color: #ddd;
+ font-size: 18px;
+ font-weight: bold;
+ display: block;
+ cursor: pointer;
+ -moz-user-select: none;
+}
+
+.level0_over
+{
+ display: block;
+ background-color: #ecebe2;
+ cursor: pointer;
+}
+
+.level1
+{
+ font-size: 14px;
+ font-weight: bold;
+ margin-left: 15px;
+ color: #333;
+ display: block;
+}
+
+.level2
+{
+ font-size: 12px;
+ margin-left: 30px;
+ color: #555;
+ display: block;
+}
+
+.level3
+{
+ font-size: 10px;
+ margin-left: 45px;
+ color: #777;
+ display: block;
+}
+
+.level4
+{
+ font-size: 10px;
+ margin-left: 60px;
+ color: #aaa;
+ display: block;
+}
+
+.percent
+{
+ font-weight: bold;
+ color: #7c8ee8;
+}
+
+.todo
+{
+ background-color: #ccc;
+ padding-right: 12px;
+ margin-right: 10px;
+}
+
+.done
+{
+ background-color: #7c8ee8;
+ margin-right: 10px;
+ padding-right: 12px;
+}
+
+.comment
+{
+ font-weight: normal;
+ font-style: italic;
+ display: block;
+}
+