| blib/lib/Text/Slidez.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 12 | 12 | 100.0 |
| branch | n/a | ||
| condition | n/a | ||
| subroutine | 4 | 4 | 100.0 |
| pod | n/a | ||
| total | 16 | 16 | 100.0 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Text::Slidez; | ||||||
| 2 | $VERSION = v0.0.1; | ||||||
| 3 | |||||||
| 4 | 1 | 1 | 1045 | use warnings; | |||
| 1 | 3 | ||||||
| 1 | 43 | ||||||
| 5 | 1 | 1 | 7 | use strict; | |||
| 1 | 2 | ||||||
| 1 | 36 | ||||||
| 6 | 1 | 1 | 17 | use Carp; | |||
| 1 | 2 | ||||||
| 1 | 87 | ||||||
| 7 | |||||||
| 8 | 1 | 1 | 6 | use base 'Shebangml'; | |||
| 1 | 1 | ||||||
| 1 | 892 | ||||||
| 9 | use Class::Accessor::Classy; | ||||||
| 10 | lw 'slides'; | ||||||
| 11 | no Class::Accessor::Classy; | ||||||
| 12 | |||||||
| 13 | use XML::Bits qw(T); | ||||||
| 14 | |||||||
| 15 | =head1 NAME | ||||||
| 16 | |||||||
| 17 | Text::Slidez - format slideshows into XHTML | ||||||
| 18 | |||||||
| 19 | =head1 SYNOPSIS | ||||||
| 20 | |||||||
| 21 | See L |
||||||
| 22 | |||||||
| 23 | use Text::Slidez; | ||||||
| 24 | |||||||
| 25 | my $slidez = Text::Slidez->new; | ||||||
| 26 | $slidez->load('my_slides.hbml'); | ||||||
| 27 | foreach my $slide ($slidez->slides) { | ||||||
| 28 | ... | ||||||
| 29 | } | ||||||
| 30 | |||||||
| 31 | =cut | ||||||
| 32 | |||||||
| 33 | |||||||
| 34 | =head2 load | ||||||
| 35 | |||||||
| 36 | $slidez->load('my_slides.hbml'); | ||||||
| 37 | |||||||
| 38 | =cut | ||||||
| 39 | |||||||
| 40 | sub load { | ||||||
| 41 | my $self = shift; | ||||||
| 42 | my $input = shift; | ||||||
| 43 | |||||||
| 44 | local $self->{ctx}; | ||||||
| 45 | local $self->{started}; | ||||||
| 46 | |||||||
| 47 | $self->process($input); | ||||||
| 48 | |||||||
| 49 | # bit of cleanup on the innards: | ||||||
| 50 | foreach my $slide ($self->slides) { | ||||||
| 51 | my @kids = | ||||||
| 52 | grep({not ($_->tag eq '' and "$_" eq '')} $slide->children); | ||||||
| 53 | shift(@kids) while($kids[0] =~ m/^\s+$/); | ||||||
| 54 | pop(@kids) if($kids[-1] =~ m/^\n\s*$/); | ||||||
| 55 | $slide->{children} = [@kids]; | ||||||
| 56 | } | ||||||
| 57 | |||||||
| 58 | #warn join("\n---\n", @{$self->{slides}}); | ||||||
| 59 | return($self); | ||||||
| 60 | } # load ############################################################### | ||||||
| 61 | |||||||
| 62 | =head2 dump | ||||||
| 63 | |||||||
| 64 | Dump a marked-up version of the raw data. | ||||||
| 65 | |||||||
| 66 | warn $slidez->dump; | ||||||
| 67 | |||||||
| 68 | =cut | ||||||
| 69 | |||||||
| 70 | sub dump { | ||||||
| 71 | my $self = shift; | ||||||
| 72 | return join("\n---\n", | ||||||
| 73 | map({join("|", map({"($_)=" . $_->tag} $_->children))} | ||||||
| 74 | $self->slides) | ||||||
| 75 | ), "\n"; | ||||||
| 76 | } # dump ############################################################### | ||||||
| 77 | |||||||
| 78 | =head2 format_slide | ||||||
| 79 | |||||||
| 80 | Format a single slide for output. | ||||||
| 81 | |||||||
| 82 | my $xhtml = $slidez->format_slide($slide, %opts); | ||||||
| 83 | |||||||
| 84 | =cut | ||||||
| 85 | |||||||
| 86 | sub format_slide { | ||||||
| 87 | my $self = shift; | ||||||
| 88 | my ($slide, %opts) = @_; | ||||||
| 89 | |||||||
| 90 | my @parts = $self->_part_slide($slide); | ||||||
| 91 | |||||||
| 92 | # see if we can deduce a title from the first time we see one | ||||||
| 93 | unless($opts{title} or $self->{title}) { | ||||||
| 94 | if($parts[2] and @{$parts[1]} == 0) { | ||||||
| 95 | my $text = join('', @{$parts[0]}); | ||||||
| 96 | ($text) = split(/\n/, $text); | ||||||
| 97 | $text =~ s/<[^>]+>//g; | ||||||
| 98 | $self->{title} = $text; | ||||||
| 99 | } | ||||||
| 100 | } | ||||||
| 101 | |||||||
| 102 | my $page = T{html => | ||||||
| 103 | T{head => | ||||||
| 104 | T{title => $opts{title}||$self->{title}||'slidez'}, | ||||||
| 105 | T{meta => | ||||||
| 106 | ['http-equiv' => "Content-Type", | ||||||
| 107 | content => "text/html;charset=utf-8"]}, | ||||||
| 108 | T{meta => | ||||||
| 109 | ['http-equiv'=>"Content-Style-Type", | ||||||
| 110 | content => "text/css"]}, | ||||||
| 111 | T{link => | ||||||
| 112 | [rel=> 'stylesheet', href => 'style.css', type => 'text/css']}, | ||||||
| 113 | T{script => [type => 'text/javascript'], | ||||||
| 114 | $self->_mk_script(%opts); | ||||||
| 115 | }, | ||||||
| 116 | }, | ||||||
| 117 | T{body =>} | ||||||
| 118 | }; | ||||||
| 119 | $page->set_doctype('html PUBLIC "-//W3C//DTD XHTML 1.1//EN" | ||||||
| 120 | "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"'); | ||||||
| 121 | |||||||
| 122 | my $div = $page->child(-1)->create_child(div => [class => 'slide']); | ||||||
| 123 | |||||||
| 124 | $self->_handle_parts($div, \@parts, | ||||||
| 125 | calc_width => sub { | ||||||
| 126 | my $n = $self->_calc_width(shift); | ||||||
| 127 | $n > 20 ? '900px' : $n . 'em'; | ||||||
| 128 | }, | ||||||
| 129 | ); | ||||||
| 130 | |||||||
| 131 | return($page); | ||||||
| 132 | |||||||
| 133 | } # format_slide ####################################################### | ||||||
| 134 | |||||||
| 135 | =head2 as_single_page | ||||||
| 136 | |||||||
| 137 | $slidez->as_single_page; | ||||||
| 138 | |||||||
| 139 | =cut | ||||||
| 140 | |||||||
| 141 | sub as_single_page { | ||||||
| 142 | my $self = shift; | ||||||
| 143 | |||||||
| 144 | my @slides = $self->slides; | ||||||
| 145 | |||||||
| 146 | my $page = T{html => | ||||||
| 147 | T{head => | ||||||
| 148 | T{title => }, | ||||||
| 149 | T{meta => | ||||||
| 150 | ['http-equiv' => "Content-Type", | ||||||
| 151 | content => "text/html;charset=utf-8"]}, | ||||||
| 152 | T{meta => | ||||||
| 153 | ['http-equiv'=>"Content-Style-Type", | ||||||
| 154 | content => "text/css"]}, | ||||||
| 155 | T{link => | ||||||
| 156 | [rel=> 'stylesheet', href => 'style-flat.css', type => 'text/css']}, | ||||||
| 157 | }, | ||||||
| 158 | T{body =>} | ||||||
| 159 | }; | ||||||
| 160 | $page->set_doctype('html PUBLIC "-//W3C//DTD XHTML 1.1//EN" | ||||||
| 161 | "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"'); | ||||||
| 162 | |||||||
| 163 | my $title = $page->child(0)->child(0); | ||||||
| 164 | my $body = $page->child(-1); | ||||||
| 165 | my $outer = $body->create_child(div => [style=> "width: 600px"]); | ||||||
| 166 | |||||||
| 167 | for my $i (0..$#slides) { | ||||||
| 168 | my $div = $outer->create_child(div => [class => 'slide']); | ||||||
| 169 | |||||||
| 170 | my @parts = $self->_part_slide($slides[$i]); | ||||||
| 171 | unless($i) { # look for title on the first slide | ||||||
| 172 | if($parts[2] and @{$parts[1]} == 0) { | ||||||
| 173 | my $text = join('', @{$parts[0]}); | ||||||
| 174 | ($text) = split(/\n/, $text); | ||||||
| 175 | $text =~ s/^\s+//; | ||||||
| 176 | $text =~ s/<[^>]+>//g; | ||||||
| 177 | $title->create_child(''=> $text); | ||||||
| 178 | } | ||||||
| 179 | } | ||||||
| 180 | |||||||
| 181 | #warn "\n\nhandle $i\n\n\n"; | ||||||
| 182 | $self->_handle_parts($div, \@parts, | ||||||
| 183 | calc_width => sub { | ||||||
| 184 | my $n = $self->_calc_width(shift); | ||||||
| 185 | $n > 20 ? '500px' : $n . 'em'; | ||||||
| 186 | }, | ||||||
| 187 | ); | ||||||
| 188 | |||||||
| 189 | $outer->create_child(div => | ||||||
| 190 | [class => 'wee', style => "width:100%; text-align: right"], | ||||||
| 191 | )->create_child('' => | ||||||
| 192 | '' => $i+1 . ' / ' . scalar(@slides)); | ||||||
| 193 | $outer->create_child(hr =>); | ||||||
| 194 | } | ||||||
| 195 | |||||||
| 196 | return($page); | ||||||
| 197 | } # as_single_page ##################################################### | ||||||
| 198 | |||||||
| 199 | my %span_map = ( | ||||||
| 200 | L => 'large', | ||||||
| 201 | M => 'medium', | ||||||
| 202 | S => 'small', | ||||||
| 203 | ); | ||||||
| 204 | |||||||
| 205 | sub _atag { | ||||||
| 206 | my $self = shift; | ||||||
| 207 | my ($tag, $atts) = @_; | ||||||
| 208 | |||||||
| 209 | my @attr = $atts ? $atts->atts : (); | ||||||
| 210 | if(my $class = $span_map{$tag}) { | ||||||
| 211 | $tag = 'span'; | ||||||
| 212 | push(@attr, class => $class); | ||||||
| 213 | } | ||||||
| 214 | |||||||
| 215 | my $el = XML::Bits->new($tag, @attr ? \@attr : ()); | ||||||
| 216 | |||||||
| 217 | if($self->{ctx}) { | ||||||
| 218 | croak("no nested slides") if($tag eq 'slide'); | ||||||
| 219 | $self->{ctx}->add_child($el); | ||||||
| 220 | $self->{ctx} = $el; | ||||||
| 221 | } | ||||||
| 222 | else { | ||||||
| 223 | if($tag eq 'slide') { | ||||||
| 224 | croak("no start element") unless($self->{started}); | ||||||
| 225 | my $sl = $self->{slides} ||= []; | ||||||
| 226 | $self->{ctx} = $el; | ||||||
| 227 | push(@$sl, $el); | ||||||
| 228 | } | ||||||
| 229 | elsif($tag eq 'slides') { | ||||||
| 230 | $self->{started} = 1; | ||||||
| 231 | } | ||||||
| 232 | else { | ||||||
| 233 | croak("content '$tag' outside of slide!"); | ||||||
| 234 | } | ||||||
| 235 | } | ||||||
| 236 | |||||||
| 237 | return($el); | ||||||
| 238 | } | ||||||
| 239 | |||||||
| 240 | =head2 do_code | ||||||
| 241 | |||||||
| 242 | $slidez->do_code($tag, $atts, $string); | ||||||
| 243 | |||||||
| 244 | =cut | ||||||
| 245 | |||||||
| 246 | sub do_code { | ||||||
| 247 | my $self = shift; | ||||||
| 248 | my ($tag, $atts, $string) = @_; | ||||||
| 249 | |||||||
| 250 | my %atts = $atts ? $atts->atts : (); | ||||||
| 251 | |||||||
| 252 | my $make = sub { | ||||||
| 253 | my $pre = $self->{ctx}->create_child(pre => [%atts]); | ||||||
| 254 | $pre->create_child('' => $_) for(@_); | ||||||
| 255 | }; | ||||||
| 256 | |||||||
| 257 | my $ft = delete($atts{type}); | ||||||
| 258 | |||||||
| 259 | require Text::VimColor; | ||||||
| 260 | my $cache; | ||||||
| 261 | if($string) { | ||||||
| 262 | # XXX how to do the caching? | ||||||
| 263 | # warn "string code is slow: $string\n"; | ||||||
| 264 | } | ||||||
| 265 | else { | ||||||
| 266 | my $src = delete $atts{src} or croak("must have src"); | ||||||
| 267 | my $input = File::Fu->file($src); | ||||||
| 268 | my $cache_dir = File::Fu->dir('.cache'); | ||||||
| 269 | if($cache_dir->d) { | ||||||
| 270 | $cache = $cache_dir + $input->file; | ||||||
| 271 | if($cache->e and $cache->stat->mtime >= $input->stat->mtime) { | ||||||
| 272 | warn "load $input from cache\n"; | ||||||
| 273 | return($make->(scalar $cache->read)); | ||||||
| 274 | } | ||||||
| 275 | } | ||||||
| 276 | my %ftmap = ( | ||||||
| 277 | html => 'html', | ||||||
| 278 | hbml => 'hbml', | ||||||
| 279 | pl => 'perl', | ||||||
| 280 | pm => 'perl', | ||||||
| 281 | ); | ||||||
| 282 | unless($ft) { | ||||||
| 283 | my ($ext) = $input =~ m/\.([^\.]+)$/; | ||||||
| 284 | $ft = $ftmap{$ext} if($ftmap{$ext}); | ||||||
| 285 | } | ||||||
| 286 | $string = $input->read; | ||||||
| 287 | } | ||||||
| 288 | my $html = Text::VimColor->new( | ||||||
| 289 | string => $string, | ||||||
| 290 | $ft ? (filetype => $ft) : (), | ||||||
| 291 | )->html; | ||||||
| 292 | |||||||
| 293 | # leading whitespace cleanup | ||||||
| 294 | $html =~ s{]*>(\s+)}{$1}g; | ||||||
| 295 | # pull whitespace out front | ||||||
| 296 | $html =~ s{^(]+>)(\s+)}{$2$1}mg; | ||||||
| 297 | $html =~ s/\n+$//; | ||||||
| 298 | $make->($html); | ||||||
| 299 | $cache->write($html) if($cache); | ||||||
| 300 | |||||||
| 301 | } # do_code ############################################################ | ||||||
| 302 | |||||||
| 303 | =head1 Shebangml Callbacks | ||||||
| 304 | |||||||
| 305 | These are really part of the parser class and not the API. | ||||||
| 306 | |||||||
| 307 | =head2 put_tag | ||||||
| 308 | |||||||
| 309 | $slidez->put_tag($tag, $atts, $string); | ||||||
| 310 | |||||||
| 311 | =cut | ||||||
| 312 | |||||||
| 313 | sub put_tag { | ||||||
| 314 | my $self = shift; | ||||||
| 315 | my ($tag, $atts, $string) = @_; | ||||||
| 316 | |||||||
| 317 | return $self->do_code(@_) if($tag eq 'code'); | ||||||
| 318 | return $self->do_include($atts) if($tag eq '.include'); | ||||||
| 319 | |||||||
| 320 | my $el = $self->_atag($tag, $atts); | ||||||
| 321 | |||||||
| 322 | $el->create_child('' => $self->escape_text($string)) | ||||||
| 323 | if(defined($string)); | ||||||
| 324 | |||||||
| 325 | $self->{ctx} = $el->parent; | ||||||
| 326 | |||||||
| 327 | } # put_tag ############################################################ | ||||||
| 328 | |||||||
| 329 | =head2 put_tag_start | ||||||
| 330 | |||||||
| 331 | $slidez->put_tag_start($tag, $atts); | ||||||
| 332 | |||||||
| 333 | =cut | ||||||
| 334 | |||||||
| 335 | sub put_tag_start { | ||||||
| 336 | my $self = shift; | ||||||
| 337 | my ($tag, $atts) = @_; | ||||||
| 338 | |||||||
| 339 | my $el = $self->_atag($tag, $atts); | ||||||
| 340 | |||||||
| 341 | } # put_tag_start ###################################################### | ||||||
| 342 | |||||||
| 343 | =head2 put_tag_end | ||||||
| 344 | |||||||
| 345 | $slidez->put_tag_end($tag); | ||||||
| 346 | |||||||
| 347 | =cut | ||||||
| 348 | |||||||
| 349 | sub put_tag_end { | ||||||
| 350 | my $self = shift; | ||||||
| 351 | my ($tag) = @_; | ||||||
| 352 | |||||||
| 353 | $tag = 'span' if($span_map{$tag}); | ||||||
| 354 | |||||||
| 355 | my $ctx = delete($self->{ctx}); | ||||||
| 356 | return() if($tag eq 'slides'); | ||||||
| 357 | ($ctx->tag eq $tag) or croak($ctx->tag, " is not a $tag!"); | ||||||
| 358 | croak("context fail $tag") | ||||||
| 359 | unless($self->{ctx} = $ctx->parent or $tag eq 'slide'); | ||||||
| 360 | |||||||
| 361 | } # put_tag_end ######################################################## | ||||||
| 362 | |||||||
| 363 | =head2 put_text | ||||||
| 364 | |||||||
| 365 | $slidez->put_text($text); | ||||||
| 366 | |||||||
| 367 | =cut | ||||||
| 368 | |||||||
| 369 | sub put_text { | ||||||
| 370 | my $self = shift; | ||||||
| 371 | my ($text) = @_; | ||||||
| 372 | |||||||
| 373 | my $ctx = $self->{ctx} or return; | ||||||
| 374 | $ctx->create_child('', | ||||||
| 375 | length($text) ? $self->escape_text($text) : ''); | ||||||
| 376 | # TODO escaped text might actually contain some certain tags :-/ | ||||||
| 377 | |||||||
| 378 | } # put_text ########################################################### | ||||||
| 379 | |||||||
| 380 | =head2 _part_slide | ||||||
| 381 | |||||||
| 382 | my @parts = $self->_part_slide($slide); | ||||||
| 383 | |||||||
| 384 | =cut | ||||||
| 385 | |||||||
| 386 | sub _part_slide { | ||||||
| 387 | my $self = shift; | ||||||
| 388 | my ($slide) = @_; | ||||||
| 389 | |||||||
| 390 | my @children = $slide->children; | ||||||
| 391 | pop(@children) if($children[-1] =~ m/^\s*$/); | ||||||
| 392 | my @parts = ([]); | ||||||
| 393 | my $sp; | ||||||
| 394 | # warn join(",", map({$_->type} @children)); | ||||||
| 395 | # if($children[0]->is_text) { # undenting :-/ | ||||||
| 396 | # $children[0]->{content} =~ s/^(\s+)//; | ||||||
| 397 | # $sp = $1; | ||||||
| 398 | # } | ||||||
| 399 | # warn "sp is >$sp<\n"; | ||||||
| 400 | while(@children) { | ||||||
| 401 | my $bit = shift(@children); | ||||||
| 402 | if($bit->is_text and $bit->{content} =~ s/\n$//) { | ||||||
| 403 | #$bit->{content} =~ s/^$sp// if(defined($sp)); | ||||||
| 404 | push(@{$parts[-1]}, $bit) if(length($bit)); | ||||||
| 405 | push(@parts, []); # start a new group | ||||||
| 406 | } | ||||||
| 407 | else { | ||||||
| 408 | #if($bit->is_text) { $bit->{content} =~ s/^$sp// if(defined($sp)); } | ||||||
| 409 | push(@{$parts[-1]}, $bit); | ||||||
| 410 | } | ||||||
| 411 | } | ||||||
| 412 | |||||||
| 413 | foreach my $part (@parts) { | ||||||
| 414 | next unless(@$part); | ||||||
| 415 | shift(@$part) | ||||||
| 416 | while($part->[0]->is_text and $part->[0] =~ m/^\s+$/); | ||||||
| 417 | } | ||||||
| 418 | |||||||
| 419 | # drop the trailing chunk | ||||||
| 420 | pop(@parts) if(@{$parts[-1]} == 0); | ||||||
| 421 | |||||||
| 422 | if(0) { | ||||||
| 423 | warn "slide:\n"; | ||||||
| 424 | warn join("\n---\n", map({join('|', @$_)} @parts)), "\n"; | ||||||
| 425 | warn "\n\n\n"; | ||||||
| 426 | } | ||||||
| 427 | |||||||
| 428 | return(@parts); | ||||||
| 429 | } # _part_slide ######################################################## | ||||||
| 430 | |||||||
| 431 | =head2 _calc_width | ||||||
| 432 | |||||||
| 433 | my $n = $self->_calc_width($text); | ||||||
| 434 | |||||||
| 435 | =cut | ||||||
| 436 | |||||||
| 437 | sub _calc_width { | ||||||
| 438 | my $self = shift; | ||||||
| 439 | my $text = shift; | ||||||
| 440 | |||||||
| 441 | my @lines = split(/\n| /, $text); |
||||||
| 442 | my ($width) = sort({$b <=> $a} | ||||||
| 443 | map({s/<[^>]+>//g; s/&[^;]+;/./g; length($_)} @lines)); | ||||||
| 444 | $width *= 0.625; # emperical em-width adjustment | ||||||
| 445 | } # _calc_width ######################################################## | ||||||
| 446 | |||||||
| 447 | =head2 _handle_parts | ||||||
| 448 | |||||||
| 449 | $self->_handle_parts($ctx, \@parts, %opts); | ||||||
| 450 | |||||||
| 451 | =cut | ||||||
| 452 | |||||||
| 453 | sub _handle_parts { | ||||||
| 454 | my $self = shift; | ||||||
| 455 | my ($ctx, $parts, %opts) = @_; | ||||||
| 456 | |||||||
| 457 | my @parts = @$parts; | ||||||
| 458 | my $calc_width = $opts{calc_width}; | ||||||
| 459 | |||||||
| 460 | if($parts[2] and @{$parts[1]} == 0) { | ||||||
| 461 | my $title_chunk = shift(@parts); | ||||||
| 462 | shift(@parts); # scrap | ||||||
| 463 | $ctx->create_child(div => [class => 'title'], @$title_chunk); | ||||||
| 464 | $ctx->create_child('br'); | ||||||
| 465 | } | ||||||
| 466 | else { | ||||||
| 467 | # center the whole thing vertically | ||||||
| 468 | $ctx = $ctx->create_child(div => [class => 'cell']); | ||||||
| 469 | } | ||||||
| 470 | |||||||
| 471 | while(@parts) { | ||||||
| 472 | my $part = shift(@parts); | ||||||
| 473 | next unless(@$part); | ||||||
| 474 | if(@$part == 1 and $part->[0] =~ m/^[^<]* [^<]*$/) { |
||||||
| 475 | $ctx->add_child($part->[0]); | ||||||
| 476 | next; | ||||||
| 477 | } | ||||||
| 478 | # pre fixup | ||||||
| 479 | if(@$part == 1 and $part->[0]->tag eq 'pre') { | ||||||
| 480 | my ($pre) = @$part; | ||||||
| 481 | my $text = join('', $pre->children); | ||||||
| 482 | $text =~ s/^\n//; | ||||||
| 483 | if($text =~ s/^(\s+)//) { | ||||||
| 484 | my $sp = $1; | ||||||
| 485 | $text =~ s/^$sp//mg; | ||||||
| 486 | } | ||||||
| 487 | my %atts = $pre->atts; | ||||||
| 488 | my $class = $atts{class} || ''; | ||||||
| 489 | $pre->{children} = []; | ||||||
| 490 | $pre->create_child('' => $text); | ||||||
| 491 | my $width = $calc_width->($text); | ||||||
| 492 | my $inner = $ctx->create_child( | ||||||
| 493 | div => [class => "auto left $class", | ||||||
| 494 | style => "width: $width"]); | ||||||
| 495 | $inner->add_child($pre); | ||||||
| 496 | next; | ||||||
| 497 | } | ||||||
| 498 | # bullet points | ||||||
| 499 | if($part->[0] =~ m/^(\s*)\* /) { | ||||||
| 500 | my $sp = $1; | ||||||
| 501 | my @points = $part; | ||||||
| 502 | # then go back to the well: | ||||||
| 503 | while(@parts and $parts[0][0] =~ m/^\s*\* /) { | ||||||
| 504 | push(@points, shift(@parts)); | ||||||
| 505 | } | ||||||
| 506 | foreach my $point (@points) { | ||||||
| 507 | $point->[0]->is_text or die; | ||||||
| 508 | $point->[0]->{content} =~ s/^$sp//; | ||||||
| 509 | } | ||||||
| 510 | |||||||
| 511 | my $width = $calc_width->(join("\n", map({@$_} @points))); | ||||||
| 512 | my $inner = $ctx->create_child( | ||||||
| 513 | div => [class => "auto left", style => "width: $width"]); | ||||||
| 514 | my $top = $inner->create_child(ul =>); | ||||||
| 515 | my @d = ($top); | ||||||
| 516 | foreach my $point (@points) { | ||||||
| 517 | $point->[0]->{content} =~ s/(\s*)\*\s+//; | ||||||
| 518 | my $ws = length($1)/2; | ||||||
| 519 | # warn "ws: $ws ($point->[0]->{content})\n"; | ||||||
| 520 | if($ws) { | ||||||
| 521 | $d[$ws] ||= $d[$ws-1]->child(-1)->create_child(ul =>); | ||||||
| 522 | } | ||||||
| 523 | else { | ||||||
| 524 | @d = ($top); | ||||||
| 525 | } | ||||||
| 526 | $d[$ws]->create_child(li => @$point); | ||||||
| 527 | } | ||||||
| 528 | # warn "yay: $top\n"; | ||||||
| 529 | next; | ||||||
| 530 | } | ||||||
| 531 | my $inner = $ctx->create_child(div =>); | ||||||
| 532 | $inner->add_child($_) for(@$part); | ||||||
| 533 | } | ||||||
| 534 | |||||||
| 535 | } # _handle_parts ###################################################### | ||||||
| 536 | |||||||
| 537 | =head2 _mk_script | ||||||
| 538 | |||||||
| 539 | $self->_mk_script(%opts); | ||||||
| 540 | |||||||
| 541 | =cut | ||||||
| 542 | |||||||
| 543 | sub _mk_script { | ||||||
| 544 | my $self = shift; | ||||||
| 545 | my (%opts) = @_; | ||||||
| 546 | |||||||
| 547 | my $script = | ||||||
| 548 | ($opts{next} ? qq(var next="$opts{next}"\n) . | ||||||
| 549 | "var down=0; document.onmousedown=function(e) { down=1 }\n". | ||||||
| 550 | " document.onmousemove=function(e) { down=0; }\n". | ||||||
| 551 | "document.onmouseup=function(e) {\n" . | ||||||
| 552 | "if(down == 1) {window.location = next;}; }\n" : '' | ||||||
| 553 | ) . | ||||||
| 554 | ($opts{prev} ? qq(var prev="$opts{prev}"\n) : '') . | ||||||
| 555 | ($opts{first} ? qq(var first="$opts{first}"\n) : '') . | ||||||
| 556 | ($opts{last} ? qq(var last="$opts{last}"\n) : ''); | ||||||
| 557 | my $func = <<' ---'; | ||||||
| 558 | document.onkeypress=function(e) { | ||||||
| 559 | var e=window.event || e | ||||||
| 560 | var n=e.keyCode || e.which | ||||||
| 561 | switch (n) { | ||||||
| 562 | -SWITCH- | ||||||
| 563 | } | ||||||
| 564 | } | ||||||
| 565 | --- | ||||||
| 566 | my $switch = join("\n", map({$_ . ' break;'} | ||||||
| 567 | ($opts{next} ? 'case 32 : window.location = next;' : ()), | ||||||
| 568 | ($opts{prev} ? 'case 8 : window.location = prev;' : ()), | ||||||
| 569 | ($opts{first} ? 'case 36 : window.location = first;' : ()), | ||||||
| 570 | ($opts{last} ? 'case 35 : window.location = last;' : ()), | ||||||
| 571 | )); | ||||||
| 572 | $func =~ s/-SWITCH-/$switch/; | ||||||
| 573 | |||||||
| 574 | return($script . $func); | ||||||
| 575 | } # _mk_script ######################################################### | ||||||
| 576 | |||||||
| 577 | =head1 AUTHOR | ||||||
| 578 | |||||||
| 579 | Eric Wilhelm @ |
||||||
| 580 | |||||||
| 581 | http://scratchcomputing.com/ | ||||||
| 582 | |||||||
| 583 | =head1 BUGS | ||||||
| 584 | |||||||
| 585 | If you found this module on CPAN, please report any bugs or feature | ||||||
| 586 | requests through the web interface at L |
||||||
| 587 | notified, and then you'll automatically be notified of progress on your | ||||||
| 588 | bug as I make changes. | ||||||
| 589 | |||||||
| 590 | If you pulled this development version from my /svn/, please contact me | ||||||
| 591 | directly. | ||||||
| 592 | |||||||
| 593 | =head1 COPYRIGHT | ||||||
| 594 | |||||||
| 595 | Copyright (C) 2009 Eric L. Wilhelm, All Rights Reserved. | ||||||
| 596 | |||||||
| 597 | =head1 NO WARRANTY | ||||||
| 598 | |||||||
| 599 | Absolutely, positively NO WARRANTY, neither express or implied, is | ||||||
| 600 | offered with this software. You use this software at your own risk. In | ||||||
| 601 | case of loss, no person or entity owes you anything whatsoever. You | ||||||
| 602 | have been warned. | ||||||
| 603 | |||||||
| 604 | =head1 LICENSE | ||||||
| 605 | |||||||
| 606 | This program is free software; you can redistribute it and/or modify it | ||||||
| 607 | under the same terms as Perl itself. | ||||||
| 608 | |||||||
| 609 | =cut | ||||||
| 610 | |||||||
| 611 | # vi:ts=2:sw=2:et:sta | ||||||
| 612 | 1; |