diff --git a/build.pl b/build.pl
index bd8a4b6..6c63856 100755
--- a/build.pl
+++ b/build.pl
@@ -9,7 +9,10 @@ use File::Slurp;
use POSIX qw(strftime);
use Text::FrontMatter::YAML;
use Text::Template;
-use Markdent::Simple::Fragment;
+use FindBin;
+use lib "$FindBin::RealBin/lib";
+use Classy::Simple;
+use Classy::Dialect;
use strict;
use utf8;
@@ -31,70 +34,18 @@ my $post_tmpl = Text::Template->new(SOURCE => "$tmpl_dir/post.html.tmpl");
my $assets_path = $cwd . ASSETS_PATH;
my $out_path = $cwd . OUT_PATH;
my $postout_path = $out_path . POSTS_PATH;
-my $return_link = qq{};
+my $return_link = qq{};
# used across a couple functions
-my $parser = Markdent::Simple::Fragment->new;
+my $parser = Classy::Simple->new;
sub make_fragment {
- my $body = shift;
$parser->markdown_to_html(
- dialect => 'GitHub',
- markdown => $body,
+ dialects => [ 'Classy::Dialect' ],
+ markdown => shift,
);
}
-sub strip_id {
- my $id = shift;
- $id =~ s/[ \t]/-/g;
- $id =~ s/[^A-Za-z0-9_-]//g;
- return $id;
-}
-
-sub do_footnotes {
- my $text = shift;
-
- my %footnotes;
- my @used;
-
- my $footnote_counter = 0;
-
- # First grab the definitions
- while ($text =~ s{
- \n\[\^([^\n]+?)\]\:[ \t]*
- \n?
- (.*?)(?:\n{1,2} # end at new paragraph
- (?=\n[ ]{0,4}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
- }{\n}sx) {
- my $id = strip_id($1);
- $footnotes{$1} = make_fragment(qq{$2 [$return_link](#fnref:$id)});
- }
-
- # then replace the inline footnotes
- $text =~ s{
- \[\^(.*?)\]
- }{
- my $result = '';
- my $id = strip_id($1);
- if (defined $footnotes{$id}) {
- $footnote_counter++;
- $result = qq{[]};
- push(@used, $id);
- }
- $result;
- }xsge;
-
- my $fn_block = qq{
};
-
- return ($text, $fn_block);
-}
-
# Converts a post file to a metadata hash.
#
# Takes one argument, the path to the post file.
@@ -166,11 +117,7 @@ sub post_to_meta {
$return;
}egmxs;
- my $fn_body;
- ($body, $fn_body) = do_footnotes($body);
- $fn_body = "" unless defined $fn_body;
-
- $metadata->{content} = make_fragment($body) . $fn_body;
+ $metadata->{content} = make_fragment($body); # . $fn_body;
}
# HACK: Stuffing the basename in the metadata because I don't want
@@ -238,14 +185,21 @@ sub mkpath {
}
}
+# subroutines used in templates
+my %utils = (
+ strftime => \&strftime,
+ include_tmpl => \&include_tmpl,
+);
sub include_tmpl {
my $tmpl_name = shift;
# TODO: do we want to cache snippet-type templates like this?
my $tmpl = Text::Template->new(SOURCE => "$tmpl_dir/$tmpl_name.tmpl");
if (defined $tmpl) {
- my $props = shift;
- $tmpl->fill_in(HASH => { props => \$props });
+ my %page_hash = %utils;
+ my $postdata = shift;
+ $page_hash{props} = $postdata;
+ $tmpl->fill_in(HASH => \%page_hash);
} else {
"error: $tmpl_dir/$tmpl_name.tmpl is missing"
}
@@ -254,12 +208,6 @@ sub include_tmpl {
## End subroutines
-# subroutines used in templates
-my %utils = (
- strftime => \&strftime,
- include_tmpl => \&include_tmpl,
-);
-
# make posts dir or die
mkpath($postout_path) or die "Unable to create directory.";
diff --git a/lib/Classy.pm b/lib/Classy.pm
new file mode 100644
index 0000000..dbb29e4
--- /dev/null
+++ b/lib/Classy.pm
@@ -0,0 +1,9 @@
+package Classy;
+
+use 5.010;
+use strict;
+use warnings;
+
+1;
+
+__END__
diff --git a/lib/Classy/Dialect.pm b/lib/Classy/Dialect.pm
new file mode 100644
index 0000000..1db0d25
--- /dev/null
+++ b/lib/Classy/Dialect.pm
@@ -0,0 +1,11 @@
+package Classy::Dialect;
+
+use strict;
+use warnings;
+use namespace::autoclean;
+
+our $VERSION = '0.10';
+
+1;
+
+__END__
diff --git a/lib/Classy/Dialect/BlockParser.pm b/lib/Classy/Dialect/BlockParser.pm
new file mode 100644
index 0000000..9b1fae1
--- /dev/null
+++ b/lib/Classy/Dialect/BlockParser.pm
@@ -0,0 +1,140 @@
+package Classy::Dialect::BlockParser;
+
+use Classy::Event::AsideBlock;
+use Markdent::Regexes qw( :block );
+
+use Markdent::Parser::BlockParser;
+
+use Moose::Role;
+
+with 'Markdent::Dialect::GitHub::BlockParser';
+
+around _possible_block_matches => sub {
+ my $orig = shift;
+ my $self = shift;
+
+ my @look_for = $self->$orig();
+ unshift @look_for, 'aside_block';
+
+ return @look_for;
+};
+
+after parse_document => sub {
+ my $self = shift;
+
+ return if $self->_span_parser->_fn_list_count eq 0;
+
+ print('Dumping footnotes!') if $self->debug;
+
+ $self->_send_event(
+ 'StartHTMLTag',
+ tag => 'div',
+ attributes => {
+ id => 'footnotes',
+ },
+ );
+ $self->_send_event('HorizontalRule');
+ $self->_send_event('StartOrderedList');
+
+ for my $fn_id (@{ $self->_span_parser->_note_idx_map }) {
+ my $fndata = $self->_span_parser->_get_fn_by_id($fn_id);
+ print("fn-> $fn_id = $fndata\n");
+ $self->_send_event(
+ 'StartHTMLTag',
+ tag => 'li',
+ attributes => {
+ id => "fn:$fn_id",
+ }
+ );
+ $self->_span_parser->parse_block( "$fndata " );
+
+ $self->_send_event(
+ 'StartLink',
+ uri => "#fnref:$fn_id",
+ );
+ # TODO: make this editable
+ $self->_send_event(
+ 'HTMLTag',
+ tag => 'img',
+ attributes => {
+ src => '/assets/img/return.gif',
+ class => 'backbtn',
+ },
+ );
+
+ $self->_send_event('EndLink');
+ $self->_send_event(
+ 'EndHTMLTag',
+ tag => 'li',
+ );
+ }
+
+ $self->_send_event('EndOrderedList');
+ $self->_send_event(
+ 'EndHTMLTag',
+ tag => 'div',
+ );
+};
+
+sub _match_aside_block {
+ my $self = shift;
+ my $text = shift;
+
+ return unless ${$text} =~ / \G
+ $BlockStart
+ !!!
+ ([\w-]+)? # optional extra class name
+ \n
+ ( # alert block content
+ (?:.|\n)+?
+ \n # last newline required for _parse_text
+ )
+ !!!
+ \n
+ /xmgc;
+ my $inner = $2;
+ my $classes = "aside" . (defined $1 ? " $1" : "");
+
+ $self->_debug_parse_result(
+ $inner,
+ 'aside block',
+ ) if $self->debug;
+
+ $self->_send_event(
+ 'StartHTMLTag',
+ tag => 'div',
+ attributes => {
+ class => $classes,
+ }
+ );
+
+ $self->_parse_text( \$inner );
+
+ $self->_send_event(
+ 'EndHTMLTag',
+ tag => 'div',
+ );
+
+ return 1;
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 DESCRIPTION
+
+This role is similar to L, but adds parsing for
+custom "aside" contexts used by datagirl.xyz.
+
+=head1 ROLES
+
+This role does the L role.
+
+=head1 BUGS
+
+We'll find out!
+
+=cut
diff --git a/lib/Classy/Dialect/SpanParser.pm b/lib/Classy/Dialect/SpanParser.pm
new file mode 100644
index 0000000..e45d029
--- /dev/null
+++ b/lib/Classy/Dialect/SpanParser.pm
@@ -0,0 +1,144 @@
+package Classy::Dialect::SpanParser;
+
+use strict;
+use warnings;
+use namespace::autoclean;
+
+our $VERSION = '0.10';
+
+use Markdent::Event::StartHTMLTag;
+use Markdent::Event::EndHTMLTag;
+use Markdent::Event::HTMLTag;
+use Markdent::Event::StartOrderedList;
+use Markdent::Event::EndOrderedList;
+use Markdent::Event::HorizontalRule;
+use Markdent::Event::StartLink;
+use Markdent::Event::EndLink;
+use Markdent::Event::Text;
+use Markdent::Types;
+
+use Moose::Role;
+
+with 'Markdent::Dialect::GitHub::SpanParser';
+
+has _notes_by_id => (
+ traits => ['Hash'],
+ is => 'ro',
+ isa => t( 'HashRef', of => t('Str') ),
+ default => sub { {} },
+ init_arg => undef,
+ handles => {
+ _add_fn_by_id => 'set',
+ _get_fn_by_id => 'get',
+ _reset_footnotes => 'clear',
+ },
+);
+
+has _note_idx_map => (
+ traits => ['Array'],
+ is => 'ro',
+ isa => t( 'ArrayRef', of => t('Str') ),
+ default => sub { [] },
+ handles => {
+ _add_fn_to_map => 'push',
+ _get_fn_list => 'get',
+ _fn_list_count => 'count',
+ _reset_fn_map => 'clear',
+ },
+);
+
+# Footnotes are kinda like links, but IDs are prefixed with a caret (^)
+around extract_link_ids => sub {
+ my $orig = shift;
+ my $self = shift;
+ my $text = shift;
+
+ ${$text} =~ s/ ^
+ \p{SpaceSeparator}{0,3}
+ \[ \^ ([^]]+) \]
+ :
+ \p{SpaceSeparator}*
+ \n?
+ \p{SpaceSeparator}*
+ (
+ (.|\n[^\n])+
+ )
+ (?:\n\n|$)
+ /
+ $self->_process_id_for_fn( $1, $2 );
+ ''
+ /egxm;
+ $self->$orig($text);
+};
+
+around _possible_span_matches => sub {
+ my $orig = shift;
+ my $self = shift;
+ my @look_for = $self->$orig();
+
+ unshift @look_for, 'footnote';
+
+ return @look_for;
+};
+
+sub _process_id_for_fn {
+ my $self = shift;
+ my $id = shift;
+ my $id_text = shift;
+
+ $id_text =~ s/\s+$//;
+ $id_text =~ s/\n+/ /g;
+ $self->_debug_parse_result(
+ $id,
+ 'footnote',
+ [ text => $id_text ],
+ ) if $self->debug;
+
+ $self->_add_fn_by_id( $id => $id_text );
+}
+
+# Matches the inline footnote
+sub _match_footnote {
+ my $self = shift;
+ my $text = shift;
+
+ my $pos = pos ${$text} || 0;
+
+ return
+ unless ${$text} =~ / \G
+ \[ \^ ([^]]+) \] # footnote id
+ /xgc;
+
+ my $fn_id = $1;
+
+ $self->_add_fn_to_map($fn_id);
+ my $fn_idx = $self->_fn_list_count;
+
+ # [idx]
+ my $start_sup = $self->_make_event( StartHTMLTag => { tag => "sup" });
+ my $start_brack = '[';
+ my $end_brack = ']';
+ my $end_sup = $self->_make_event( EndHTMLTag => { tag => "sup" });
+ my $start_link = $self->_make_event( StartHTMLTag => {
+ tag => 'a',
+ attributes => {
+ href => "#fn:$fn_id",
+ id => "fnref:$fn_id",
+ },
+ });
+ my $end_link = $self->_make_event('EndLink');
+
+ $self->_markup_event($start_sup);
+ $self->_parse_text( \$start_brack );
+ $self->_markup_event($start_link);
+ $self->_parse_text( \$fn_idx );
+ $self->_markup_event($end_link);
+ $self->_parse_text( \$end_brack );
+ $self->_markup_event($end_sup);
+
+ return 1;
+}
+
+1;
+
+__END__
diff --git a/lib/Classy/Event.pm b/lib/Classy/Event.pm
new file mode 100644
index 0000000..4b2fdeb
--- /dev/null
+++ b/lib/Classy/Event.pm
@@ -0,0 +1,11 @@
+package Classy::Event;
+
+use strict;
+use warnings;
+use namespace::autoclean;
+
+our $VERSION = '0.10';
+
+1;
+
+__END__
diff --git a/lib/Classy/Event/AsideBlock.pm b/lib/Classy/Event/AsideBlock.pm
new file mode 100644
index 0000000..1889828
--- /dev/null
+++ b/lib/Classy/Event/AsideBlock.pm
@@ -0,0 +1,56 @@
+package Classy::Event::AsideBlock;
+
+use strict;
+use warnings;
+use namespace::autoclean;
+
+our $VERSION = '0.10';
+
+use Markdent::Types;
+
+use Moose;
+use MooseX::StrictConstructor;
+
+has inner => (
+ is => 'ro',
+ isa => t('Str'),
+ required => 1,
+);
+
+has class => (
+ is => 'ro',
+ isa => t('Str'),
+ predicate => 'has_class',
+);
+
+with 'Markdent::Role::Event' => { event_class => __PACKAGE__ };
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
+__END__
+
+=pod
+
+=head1 DESCRIPTION
+
+This class represents an aside in the article.
+
+=head1 ATTRIBUTES
+
+This class has the following attributes:
+
+=head2 inner
+
+The inner markdown text of the block.
+
+=head2 type
+
+Either C