File Coverage

lib/App/Followme/Initialize.pm
Criterion Covered Total %
statement 115 122 94.2
branch 24 36 66.6
condition 2 6 33.3
subroutine 22 23 95.6
pod 0 10 0.0
total 163 197 82.7


line stmt bran cond sub pod time code
1             package App::Followme::Initialize;
2 2     2   1287 use 5.008005;
  2         18  
3 2     2   11 use strict;
  2         4  
  2         72  
4 2     2   31 use warnings;
  2         11  
  2         92  
5 2     2   15 use lib '../..';
  2         3  
  2         12  
6              
7 2     2   255 use IO::File;
  2         4  
  2         309  
8 2     2   1006 use MIME::Base64 qw(decode_base64);
  2         1622  
  2         123  
9 2     2   15 use File::Spec::Functions qw(splitdir catfile);
  2         3  
  2         86  
10              
11 2     2   12 use App::Followme::FIO;
  2         4  
  2         200  
12 2     2   500 use App::Followme::NestedText;
  2         5  
  2         251  
13              
14             our $VERSION = "2.01";
15              
16             require Exporter;
17             our @ISA = qw(Exporter);
18             our @EXPORT_OK = qw(initialize);
19              
20             our $var = {};
21 2     2   14 use constant CMD_PREFIX => '#>>>';
  2         12  
  2         2318  
22              
23             #----------------------------------------------------------------------
24             # Initialize a new web site
25              
26             sub initialize {
27 1     1 0 639 my ($directory) = @_;
28              
29 1 50       11 chdir($directory) if defined $directory;
30 1         5 my ($read, $unread) = data_readers();
31              
32 1         3 while (my ($command, $lines) = next_command($read, $unread)) {
33 14         43 my @args = split(' ', $command);
34 14         24 my $cmd = shift @args;
35              
36 14 50 33     64 write_error("Missing lines after command", $command)
37             if $cmd eq 'copy' && @$lines == 0;
38              
39 14 50 33     30 write_error("Unexpected lines after command", $command)
40             if $cmd ne 'copy' && @$lines > 0;
41              
42 14 50       27 if ($cmd eq 'copy') {
43 14         26 write_file($lines, @args);
44              
45             } else {
46 0         0 write_error("Error in command name", $command);
47             }
48             }
49              
50 1         9 return;
51             }
52              
53             #----------------------------------------------------------------------
54             # Copy a binary file
55              
56             sub copy_binary {
57 2     2 0 6 my($file, $lines, @args) = @_;
58 2 50       46 return if -e $file;
59              
60 2 50       17 my $out = IO::File->new($file, 'w') or die "Couldn't write $file: $!\n";
61 2         336 binmode($out);
62              
63 2         8 foreach my $line (@$lines) {
64 150         373 print $out decode_base64($line);
65             }
66              
67 2         64 close($out);
68 2         15 return;
69             }
70              
71             #----------------------------------------------------------------------
72             # Copy a configuration file
73              
74             sub copy_configuration {
75 2     2 0 4 my ($file, $lines, @args) = @_;
76            
77 2         4 my $config;
78 2         10 my %old_config = nt_parse_almost_yaml_string(join('', @$lines));
79              
80 2 100       45 if (-e $file) {
81 1         5 my $page = fio_read_page($file);
82              
83 1 50       6 if ($page =~ /:[ \n]/) {
84 1         4 my %new_config = nt_parse_almost_yaml_string($page);
85 1         4 $config = nt_merge_items(\%old_config, \%new_config);
86              
87             } else {
88 0         0 my $new_file = $file;
89 0         0 $new_file =~ s/\.*$/ocfg/;
90 0         0 rename($file, $new_file);
91 0         0 $config = \%old_config;
92             }
93              
94             } else {
95 1         3 $config = \%old_config;
96             }
97              
98 2         12 nt_write_almost_yaml_file($file, %$config);
99 2         10 return;
100             }
101              
102             #----------------------------------------------------------------------
103             # Copy a text file
104              
105             sub copy_text {
106 14     14 0 31 my ($file, $lines, @args) = @_;
107 14 50       255 return if -e $file;
108              
109 14 50       97 my $out = IO::File->new($file, 'w') or die "Couldn't write $file: $!\n";
110 14         1692 foreach my $line (@$lines) {
111 477         723 print $out $line;
112             }
113              
114 14         372 close($out);
115 14         81 return;
116             }
117              
118             #----------------------------------------------------------------------
119             # Check path and create directories as necessary
120              
121             sub create_dirs {
122 18     18 0 28 my ($file) = @_;
123              
124 18         58 my @dirs = splitdir($file);
125 18         103 pop @dirs;
126              
127 18         27 my @path;
128 18         38 while (@dirs) {
129 12         26 push(@path, shift(@dirs));
130 12         40 my $path = catfile(@path);
131              
132 12 100       206 if (! -d $path) {
133 4 50       170 mkdir($path) or die "Couldn't create $path: $!\n";
134 4 50       76 chmod(0755, $path) or die "Couldn't set permissions: $!\n";
135             }
136             }
137              
138 18         53 return;
139             }
140              
141             #----------------------------------------------------------------------
142             # Return closures to read the data section of this file
143              
144             sub data_readers {
145 1     1 0 2 my @pushback;
146              
147             my $read = sub {
148 653 100   653   1018 if (@pushback) {
149 13         30 return pop(@pushback);
150             } else {
151 640         1773 return <DATA>;
152             }
153 1         6 };
154              
155             my $unread = sub {
156 13     13   21 my ($line) = @_;
157 13         23 push(@pushback, $line);
158 1         4 };
159              
160 1         4 return ($read, $unread);
161             }
162              
163             #----------------------------------------------------------------------
164             # Is the line a command?
165              
166             sub is_command {
167 653     653 0 2433 my ($line) = @_;
168              
169 653         777 my $command;
170 653         833 my $prefix = CMD_PREFIX;
171              
172 653 100       1638 if ($line =~ s/^$prefix//) {
173 28         52 $command = $line;
174 28         54 chomp $command;
175             }
176              
177 653         1374 return $command;
178             }
179              
180             #----------------------------------------------------------------------
181             # Get the name and contents of the next file
182              
183             sub next_command {
184 15     15 0 35 my ($read, $unread) = @_;
185              
186 15         26 my $line = $read->();
187 15 100       38 return unless defined $line;
188              
189 14         26 my $command = is_command($line);
190 14 50       29 die "Command not supported: $line" unless $command;
191              
192 14         18 my @lines;
193 14         28 while ($line = $read->()) {
194 637 100       974 if (is_command($line)) {
195 13         28 $unread->($line);
196 13         17 last;
197              
198             } else {
199 624         1208 push(@lines, $line);
200             }
201             }
202              
203 14         51 return ($command, \@lines);
204             }
205              
206             #----------------------------------------------------------------------
207             # Die with error
208              
209             sub write_error {
210 0     0 0 0 my ($msg, $line) = @_;
211 0         0 die "$msg: " . substr($line, 0, 30) . "\n";
212             }
213              
214             #----------------------------------------------------------------------
215             # Write a copy of the input file
216              
217             sub write_file {
218 18     18 0 2510 my ($lines, @args) = @_;
219              
220 2     2   17 no strict;
  2         3  
  2         228  
221 18         34 my $type = shift(@args);
222 18         24 my $file = shift(@args);
223              
224 18         44 create_dirs($file);
225              
226 18         40 my $sub = "copy_$type";
227 18         66 &$sub($file, $lines, @args);
228              
229 18         100 return;
230             }
231              
232             1;
233             __DATA__
234             #>>> copy binary banner.jpg
235             /9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAeAAD/4QMraHR0cDov
236             L25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENl
237             aGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4
238             OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjAtYzA2MCA2MS4xMzQ3NzcsIDIwMTAvMDIvMTItMTc6
239             MzI6MDAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5
240             OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHht
241             bG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6
242             Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUu
243             Y29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBo
244             b3Rvc2hvcCBDUzUgTWFjaW50b3NoIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjBGMTg3OEQy
245             OTA5MjExRTE5OTJDQjgwQkE4RTNCQTdGIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjBGMTg3
246             OEQzOTA5MjExRTE5OTJDQjgwQkE4RTNCQTdGIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmlu
247             c3RhbmNlSUQ9InhtcC5paWQ6QjQ3NDNDNDk5MDkxMTFFMTk5MkNCODBCQThFM0JBN0YiIHN0UmVm
248             OmRvY3VtZW50SUQ9InhtcC5kaWQ6QjQ3NDNDNEE5MDkxMTFFMTk5MkNCODBCQThFM0JBN0YiLz4g
249             PC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9
250             InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAQCwsLDAsQDAwQFw8NDxcbFBAQFBsfFxcXFxcfHhca
251             GhoaFx4eIyUnJSMeLy8zMy8vQEBAQEBAQEBAQEBAQEBAAREPDxETERUSEhUUERQRFBoUFhYUGiYa
252             GhwaGiYwIx4eHh4jMCsuJycnLis1NTAwNTVAQD9AQEBAQEBAQEBAQED/wAARCAH0A8ADASIAAhEB
253             AxEB/8QAegABAQEBAQEBAAAAAAAAAAAAAAEEAgMFBgEBAQAAAAAAAAAAAAAAAAAAAAEQAQACAQID
254             AwYMBgIDAQAAAAABAgMRBCExElFxsUGRwXITBWGBodEiMlIjUxQkFUKCkrIzNOHSYkNzohEBAAAA
255             AAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/YRyCOQKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
256             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
257             AAAAAAAAE8gnkBHISOSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
258             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8hJ5ARyVI5AKIAo
259             gCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiA
260             KIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAq
261             TyCeQEchI5KIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
262             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8hJ5ARyVI5AKIAogCiAKIAo
263             gCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiA
264             KIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAqTyCeQJHJ
265             UjkAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIA
266             ogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCi
267             AKIAogCpPIJ5ARyCOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
268             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATyCeQJHIIAAAAAAAA
269             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
270             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACeQSCQqQAogCiAKIAogCiAKIAogCiAKIAogCiAKIAog
271             CiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAK
272             IAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCpISBAkAKIAogCiAKIAogCiAKIAogCiA
273             KIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAo
274             gCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIApKEgQJACiAKIAogCiAKIA
275             ogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCi
276             AKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKShIECQoAAA
277             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
278             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIkgQJCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
279             RE2mK1jWZ5QAO/YZ/wAOx7DP+HYHA6nDmrGs47adziJ1BR1XFltHVWk2jthzMTW01tGlo5xIAAAA
280             A6riy2jqrSbR2w5tW1J6bxNZnjpIAAALWtrzpSs27o1BB6RtdxPLHPniPSlsGevPHPxcfAHAnl05
281             T2O4w5bRFq0mYnlMA5HfsM/4dj2Gf8OwOBb48mOIm9ZrEzpEz2la2vOlY1nsgEFvjyUjW9ZrHLWU
282             AB1GHLaItWkzE8pgHIcYmYnhMcJgAFrS9/qVm3dD0/K7n8P5Y+cHkOr4stI1vSYjt01j5HETqCg7
283             jBmmImKTMTykHAmvyKADquLLeNaVm0ctYByO/YZ/w7HsM/4dgcBatqfXrNe+NAAI1mdI5zyd+wz/
284             AIdgcDv2Gf8ADsewz/h2BwFotW3TaNLdkuq48l41pWbRy4A5HfsM/wCHY9hn/DsDgLVtT69Zr3xo
285             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEiSBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPTb/AOxj
286             7/Q83e3/AM+Pv9APo589MGK2W+s1rprpz4zozfuu3+zfzR86+8Z/R376/wB0MMadMA+ng3mHPaa0
287             1i0cdLRpwZ/eGOtenNWNJmem3w6+U2eG0X9rMaREaR8Op7zvHsqY/wCK1o4fBAPfZT+njvnxYdxP
288             63N/L/bVs2U6beO+fFiz/wC7m/l/tgEAAWIm0xWOczpCNGzp1ZJvPKnLvkG2vRix1rrpEaViZ7Z4
289             fLLN7xx9WKMsc8c8fVnm8PeeWbWx4Kzp/HbT4PqtmLJGfDE249UaWj5JB86J1jUTpnHe2KedJ0+L
290             yEg07Xbxlnrv9SPJ2y15M2Db0ibzFK8oj5ohMMdGKleyI875WS87jcXy241iZrSOysA2z71xa/Rx
291             3mO3SI9K196YZ+tS9fhmImPklj0gB3uMlcm5m9J6qTEaS37Sf09Pj8ZfN0iOT6O0n9PT4/EHnk95
292             4aZLY5peZpOkzERp/cn7rh/Dyeav/ZkyRb8zmnSeNp46Gk9k+YHpu95Tc0rSlb1mtotM2iNNNJjy
293             TPa62f8AsV7peHLnGj22n+xXukHt7zn7ivrx4SyRyaveWs4K6Rr9OOXdLLETpynzAPo7Sf09Pj8Z
294             fO49k+Z9Dazpt6a/D4g+daf1Gb17eLTtdvGWeq/1I8nbLLaf1Gb17eL6eCOjDSPg1nvniDrLnw7a
295             kTeYrXlWI8vdEM37ri14Y7zHbw+djy2nPub5Lca1ma0j4I+ddAfSwbzDnnprM1t9m3CXjvNtHTOb
296             FGlq8bVjlMMcxpMWjhaOMT8L6mPJ7THW/wBqImQfL11rrD62GfuqerHg+Rp0XyY/JS0xHdrwfVxT
297             91T1Y8AfJxzxt60+Lt54+dvWnxegDdsJ+5n1p8IYW3Yz9zPrT6AXP7wxYMs4rUva0RE61iNOPfMO
298             I964fw8kfDpH/Zm3f+7b1auAfUxbjDuKz0TFu2s8/jiWPebeMX3uPhTXS1ezXywz1mcd4yV51+WO
299             x9TJEZcVqeS8THnB82k/Tp60eL6mTJGPHbJbjFIm0xHPSI1fI29teifhjxfT3PHbZYjjM0t4SDwj
300             3tgmNYx5PNX/ALL+64fw8nmr/wBmLHE9EfRnzOtJ7J8wGXLGfcTlrE1rMRGlufDu1bthP3VvXnwh
301             gbdjP3VvWnwgHeffYdvkjHeLTaY6voxrGkzMdvwPP902/wBm/mj52ffcd5X/AOceNnnpAPq1viz4
302             9Y0tS0eV8zJT2We+LyRxr3TxbtrSceGK24TMzOnZqwZ7xk3eS1eMRpXXu5gAAAAAAAAAAAAAAAAA
303             AAAAAAAAAAAAAEhIJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPTB/mp3+h5u8P+anf6AaPeE/pL
304             99f7oeeytSIt16RppMTLrfT+lv31/uhk6YtWNQbcvvDBThSfaW8kV5efkxTOTNknLl58ojyRHYRS
305             scodA27SfuI758WPN/uZv5f7YatrP3Md8+LJmn9Xl/l/tgANTUBvw1jFiiJ4Tzsx4KdeSInlHGXr
306             v8vTh6I+tknp+LygzY4ybnLkzVjXWeHwRyhs2tM2KbVvGlJ4xxjmzYdxG3x9MUm0zxmYd/uPbitp
307             3wDrf00tTNHq29DPPJvyVrlxTTyWjhPhL59JnSa24WjhMfDAPq1n6Mdz5GGNK6TzidJfQ22TqxR2
308             1+jPxMufFbFkteI1x3nWZ7JnmDkTWJXUBu2s/cU+PxYWzbT9xX4/EFtvttW00m+lqzpMaTz8yfn9
309             r+J8k/MwTETny6/bnxXpr2A9d3nx5r4vZW6or1dXCY56drraz9/HdLxiIjk9dtP30d0g2Zc+PDXr
310             yz01mdNdJnj8Ty/cNp9uf6bfM8/ePHDX148JZ4iNOQNn7htPtz/Tb5nvjy1yUi9J1rPKeXi+ZpHY
311             27adMFfj8QYbf5s3r28X1Mdvu690eD5c/wCfL68+LbtcnViiPLXhPoBhxcOqJ5xM6+d26z45xZbW
312             /wDXeddeyZ5udQG/bTpgp3MNK2vbprz8s9jdrXHTjwrSPkgHz7z+ozetL6WKfuqerHg+VjmbdWSe
313             d5m3nfTxT93T1Y8AfNx/xd8+Lt54p+t3z4vTUBs2U/dT60+hjatpP3c+tPoBn3M/rLd0OTcT+rt3
314             QagTyl9Ks9NIifJHFgw45yWif4I5z6GjdZfZ4L28sx01754Aw7adYpPbaPF9W+StKze06VrGsy+X
315             hjpikfDHi27uf02X1ZBfz+1/E+SfmPz+1/E+SfmYKVr0xwXpr2As3682S8TrWZ1iW3ZT93b1p8IY
316             o0jk17Sfu7et6IB4byY/O115dFde7qs20x4qcaVjv5/Kw7vju49SPGz12ubT7m38k+gHO53uS1rY
317             MMTSY4WvPP8Al+d4UpFK6Q1bvD1R7WkfTrzjthmi2sawCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE
318             hIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7xT97Tv8AQ4SY1jSQad7aJ214iYmda/3Qz15Q4jFS
319             J1iHYAANW2tEYo1mI4z4uprgmZtMUm085mI1YbY625w59jj7AfQ6cH2aeaHhu/Z1xxNIrE9URw05
320             cWb2OPsWMVInWIBs22ladUzETbj8TLnv7XczP8OP6Md/lc2x0txmHVaxWNIBQAadtljo6LTpNOWv
321             Y8NxWK5uuvGt+enbDztWLc4SuOteUA9KZL4rdVeOvOva003eG/O3TPZbgypNYnnANvs8NuPTWfhj
322             Qn8vj4z0V+GdIfPnDjnyEYcceQHplyRk3FrUnWukRq17e0RhrEzEc/FjiIjk5tjpbnAN/Tg1memm
323             s8ZnSDpwfZp5ofP9jj7D2OPsBq3fs64taRWJ1jlo428/e1meyXjGKkTrEOpiJjSQe++tE4qxE6/T
324             jwl4xycRipE6xDsBr29ojDWJmI5+LI4tjrbjMAv/ALss/wDlPi7re+O3XXj2x2w5rWKxpCg1U3WG
325             8aTMVny1twdeywzxitfiYprWecOJwY58gN9s2DDHG1ax2R80MmfcW3P0KRNcXlmedv8AhxGKkcod
326             RwA00rpDdjtWMdeMco8GJxOKlp1mAb+jbx/DTzQdOD7NPND5/scfYexx9gPfddNclIpERExOuj22
327             tojHOs6fSlkrjrXlBalbc4BumuG09VorNu2YjU6cHZTzQ+f7HH2HscfYDdfdbfHHG8TP2a8Z+Rjy
328             ZL7m8WtHTSv1a+mSMdI5Q6BY+tXvjxbZmlomtpiYnnE6MPN5zhxz5AfQ6MH2aeaDpwfZp5ofP9jj
329             7D2OPsB7biaxuIrSIivTE8O3WXvtbRFLazp9L0QyVpWvKC1K25wD03M67qJjjHRHjZzOvOOExxiX
330             NaVryh0DXhzxkprMxFo4Wj4WbPSMd+qmk0tziPJLztStucJGKlZ1iAdgAAAAAAAAAAAAAAAAAAAA
331             AAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
332             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAA
333             AAAAAAAAAAAAAAAAAAAAAAAAAAAA98G2tni01tFemdOLwbvds/Ryd8eAMufFODJGO0xMzGvDzOtv
334             trZ+rptFenTn8K+8p/V09T0y9vds/wCX+X0g8Nzt77eK2tMWradNY8kvF9bc4oz4bY/LMfRnsmOT
335             49JmY0nhMcJj4QdOqVm960rztOkOWz3fj1tbLPKv0a9/lBzfYZKUteb10rEzPPyMtbaxq+vuZ/T5
336             fUt4Pi4vqQDt6YMNs9+is6aRrMy830dhj6MPXP1snH4vIDPl2OXHjtfqi3TGuka68GaJiY1fXw58
337             eetppOsVtNJ74fJy4/YZ74vJE609WeQID0wYvbZYpyjnafggDHhy5Z0x1105z5Givu7JP1rxXuiZ
338             +ZrtfFt8U2nSmOkPn39657z9xjitfJa/GfNGgPafdtvJlif5f+Xhn2ubBScltJpGmsxPbOnlSPeG
339             +j7E/BpPzmffZM+3thvj6bW00tWeHCYnlPcCYMM57zSsxExGustH7bl+3XzS493cM8+pPjV7+8N1
340             l21KWxRWZtbSeqJnhpr5JgHn+25ft180n7bl+3XzS8f3Le/Zx+a3/Y/ct79nH5rf9geFbRKuaV6Y
341             dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAioAogCiAKIAogCiAKIAogCiAKIAo
342             gCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAK2+7p+jk748GFs2E/Ryd8A8feP8At09T0y9/d0/5
343             Pi9LP7wn9VT1PTL22E/5Pi9INH5jTeTgnlOOL179bRLFvcfstz1R9XLx/m8qbzJOP3hjyRzrSPNr
344             bVq3eOM+3np42j6dPi+cGHnwjnPJvy3jZ7K0x9ateHw3n/ll2NYyZIv/AA04/H5E955eu9MEcq/T
345             t38oBvzT+lvr+HPg+Ti+pD6maf01/UnwfKxfUgHtjpOTJWkfxTx7vK37zN+X21rV4W06ad88I8zw
346             2NPrZZ9WvpeO+vOfdU29eVOfrW+aAX3bf2N/Zz9XJH/6h7e8seta54504W9Wf+U/IaTExk0mOMTp
347             2fG1XrF6TS3GLRpPxg+VEtnu6I6slvLGkeLFETS1sdvrUnSWzYW0nJHdPiDn3rebTiw/wzM2t8XC
348             PFmiIiHv7yj73DfyaTE/JLwBRcVYtkrW3K06S991t8WHBbJXXWNNNZ7ZiANh/nn1J8Ye2/wZM9KR
349             jiJmttZ1nThoz7Cfvpn/AMJ8YaN3u/y0Uno6+udOegM/5PcdkedLbTPWs2mI0iNZ49jr90t+DP8A
350             V/wl/eNr0tT2Mx1RMa9XbHcDwiYmNVcUjSsQ6BRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAU
351             QBRAFEAUQBRAFEAUQBRAFQABAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEA
352             UQBRAFEAUQBWrYzwv3wyPbb7jHhi3tJmNZ4aRqCb7/ap6npl7bGf8nxell3GWmbPW+PWYiuk6xpx
353             1l6bfPjw9XtJmNdNNI1BN9x3kf8Azjxs1bPJ1YuiedOHxeRiz5aZtxGSms1isRxjTjrLrDljDk6p
354             +rMaWBux48eCtunhEzNp+N8vqnLe+af451ju8jRut5jy4bY8UzNr8J1iY4eV4RGldAfTzT+mv6k+
355             D5mKJmtYjnPCGrJvME4rY4meqazEcJ56PDbXx4pi2SdIrHDhrxB9HWuDDrP1cddZ+J8vDfJFpzxp
356             7S0zPHjze273VM+OMWLXjP09Y04Q8ojSNAen5vefajzNOz3GTJFq5ZibxxjThwY3VLzjyVvHk59w
357             PXfU6clc0crfRt3xycYMsYssWn6s8LO8+722XHbHrOs8p6Z5xyZ68a8QfSz4q58c0nh5az2T2sNs
358             OanC1Zn4a8YMW5y4Y6dOukco8sd0vePeGCfrdVe+NfDUHlhrf21J6Z0ieM6S0e8J/SX76/3Q5n3h
359             tY5WmfgitvmZ9zvK58c4qUtpbTW08OU6g9Nj/l/knxh17wre8Yums20mddImfI8dvlphv1X4R06d
360             vlho/cNt2z/TIM3s8n2Lf0yezyfYt/TLR+4bbtn+mV/cNt2z/TIMnoVxWdbWt5JtMx3TLoFEAUQB
361             RAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQAEAUQBRAFEAUQBRAFEAUQ
362             BRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFQANAANFQA0hUANIAA0hUAUQA0hUAVNIA
363             DSOwAFTSOwANI7DSOwAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAU
364             QBRAFEABAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEA
365             UQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBR
366             AFEAUQBRAFEAUQAEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
367             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAA
368             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
369             AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
370             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARUAU
371             QBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRA
372             FEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAU
373             QBUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
374             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAA
375             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
376             AAAAAAAAAAAAAAAAAEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAU
377             QBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRA
378             FEAUQBRAFEAUQBRAFEAUQBRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
379             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
380             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
381             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
382             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
383             AAAEAH//2Q==
384             #>>> copy text followme.cfg
385             run_before:
386             - App::Followme::FormatPage
387             - App::Followme::ConvertPage
388             #>>> copy text index.html
389             <!doctype html>
390             <html lang="en">
391             <head>
392             <meta charset="utf-8">
393             <meta name="viewport" content="width=device-width,initial-scale=1">
394             <!-- section meta -->
395             <title>Your Site</title>
396             <!-- endsection meta -->
397             <link rel="stylesheet" id="css_style" href="theme.css">
398             </head>
399             <body>
400             <header>
401             <div><img src="banner.jpg"></div>
402             <span class= "title"><a href="#">Your Title Here</a></span>
403             <div class="dropdown" style="float:right;">
404             <button class="dropbtn">&#9776;</button>
405             <nav class="dropdown-content">
406             <a href="essays/index.html">Essays</a>
407             <a href="photos/index.html">Photos</a>
408             </nav>
409             </div>
410             </header>
411             <article>
412             <section id="primary">
413             <!-- section primary -->
414             <h2>Followme</h2>
415              
416             <p>Usage: followme [file or directory]</p>
417              
418             <p>Update a static website after changes. Constant portions of each page are
419             updated to match, text files are converted to html, and indexes are created
420             for new files in the archive.</p>
421              
422             <p>The script is run on the directory or file passed as its argument. If no
423             argument is given, it is run on the current directory.</p>
424              
425             <p>If a file is passed, the script is run on the directory the file is in. In
426             addition, the script is run in quick mode, meaning that only the directory
427             the file is in is checked for changes. Otherwise not only that directory, but
428             all directories below it are checked.</p>
429              
430             <p>Followme can be downloaded from CPAN as App::Followme.</p>
431              
432             <p>This file is used as a template for the site. Any markup outside the section
433             comments will be shared between all pages. Modify it to get the desired look for
434             your site. The subdirectories show some of the capabilities of this application.
435             Keep or modify them as you wish.</p>
436              
437             <p>See <a href="help/index.html">help</a> for more information about this
438             script.</p>
439              
440             <!-- endsection primary-->
441             <section id="secondary">
442             <!-- section secondary -->
443             <!-- endsection secondary-->
444             </section>
445             </article>
446             <footer>
447             <nav class="footer-content">
448             <a href="essays/index.html">Essays</a>
449             <a href="photos/index.html">Photos</a>
450             </nav>
451             </footer>
452             </body>
453             </html>
454             #>>> copy text theme.css
455             /*
456             Global styles
457             *****************/
458             body {
459             font-family: helvetica, arial, sans-serif;
460             line-height: 1.5;
461             margin: 0 auto;
462             max-width: 50em;
463             padding: 0 1em;
464             }
465             h1, h2, h3, h4, h5, h6 {
466             margin: 1em 0 0.5em 0;
467             line-height: 1.2em;
468             }
469             img {
470             max-width: 100%;
471             }
472             figure {
473             margin: 1em 0;
474             text-align: center;
475             }
476             figcaption {
477             font-size: small;
478             }
479             pre, code, samp, kbd {
480             color: #009;
481             font-family: monospace, monospace;
482             font-size: 0.9em;
483             }
484             pre code, pre samp, pre kbd {
485             font-size: 1em;
486             }
487             pre kbd {
488             color: #060;
489             }
490             pre {
491             background: #eee;
492             padding: 0.5em;
493             overflow: auto;
494             }
495             blockquote {
496             background: #eee;
497             border-left: medium solid #ccc;
498             margin: 1em 0;
499             padding: 0.5em;
500             }
501             blockquote :first-child {
502             margin-top: 0;
503             }
504             blockquote :last-child {
505             margin-bottom: 0;
506             }
507             /*
508             Header
509             *****************/
510             header {
511             padding: 0;
512             background: #222;
513             border-radius: 6px;
514             }
515             .title {
516             padding: 16px;
517             font-size: 1.75em;
518             }
519             .title a {
520             text-decoration: none;
521             color: #aaa;
522             }
523             .title a:hover {
524             color: #fff;
525             }
526             /*
527             Footer
528             *****************/
529             footer {
530             padding: 8px;
531             background: #222;
532             border-radius: 6px;
533             }
534             .footer-content a {
535             padding: 16px;
536             text-decoration: none;
537             color: #aaa;
538             }
539             .footer-content a:hover {
540             color: #fff;
541             }
542             /*
543             Menu
544             *****************/
545             .dropbtn {
546             font-size: 1.75em;
547             background-color: #222;
548             color: #aaa;
549             border: none;
550             cursor: pointer;
551             }
552             .dropdown {
553             position: relative;
554             display: inline-block;
555             }
556             .dropdown-content {
557             display: none;
558             position: absolute;
559             right: 0;
560             background-color: #222;
561             min-width: 160px;
562             border-radius: 6px;
563             z-index: 1;
564             }
565             .dropdown-content a {
566             color: #aaa;
567             padding: 12px 16px;
568             border-radius: 6px;
569             text-decoration: none;
570             display: block;
571             }
572             .dropdown-content a:hover {
573             color: #fff;
574             }
575             .dropdown:hover .dropdown-content {
576             display: block;
577             }
578             .dropdown:hover .dropbtn {
579             color:#fff;
580             background-color: #222;
581             border-color: #222;
582             }
583             /*
584             Photo Gallery
585             *****************/
586             #gallery {
587             display: grid;
588             grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
589             grid-gap: 10px;
590             align-items: start;
591             }
592            
593             .thumb {
594             border: 1px solid #ccc;
595             box-shadow: 2px 2px 6px 0px rgba(0,0,0,0.3);
596             max-width: 100%;
597             }
598            
599             .lightbox {
600             position: fixed;
601             z-index: 999;
602             height: 0;
603             width: 0;
604             text-align: center;
605             top: 0;
606             left: 0;
607             background: rgba(0, 0, 0, 0.8);
608             opacity: 0;
609             }
610            
611             .lightbox img {
612             max-width: 90%;
613             max-height: 80%;
614             margin-top: 2%;
615             opacity: 0;
616             }
617            
618             .lightbox:target {
619             /** Remove default browser outline */
620             outline: none;
621             width: 100%;
622             height: 100%;
623             opacity: 1 !important;
624            
625             }
626            
627             .lightbox:target img {
628             border: solid 17px rgba(77, 77, 77, 0.8);
629             opacity: 1;
630             webkit-transition: opacity 0.6s;
631             transition: opacity 0.6s;
632             }
633            
634             .light-btn {
635             color: #fafafa;
636             background-color: #333;
637             border: solid 3px #777;
638             padding: 5px 15px;
639             border-radius: 1px;
640             text-decoration: none;
641             cursor: pointer;
642             vertical-align: middle;
643             position: absolute;
644             top: 45%;
645             z-index: 99;
646             }
647            
648             .light-btn:hover {
649             background-color: #111;
650             }
651            
652             .btn-prev {
653             left: 7%;
654             }
655            
656             .btn-next {
657             right: 7%;
658             }
659            
660             .btn-close {
661             position: absolute;
662             right: 2%;
663             top: 2%;
664             color: #fafafa;
665             background-color: #92001d;
666             border: solid 5px #ef4036;
667             padding: 10px 15px;
668             border-radius: 1px;
669             text-decoration: none;
670             }
671            
672             .btn-close:hover {
673             background-color: #740404;
674             }
675             #>>> copy text _templates/convert_page.htm
676             <html>
677             <head>
678             <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
679             <!-- section meta -->
680             <base href="$site_url/" />
681             <title>$title</title>
682             <meta name="date" content="$mdate" />
683             <meta name="description" content="$description" />
684             <meta name="keywords" content="$keywords" />
685             <meta name="author" content="$author" />
686             <!-- endsection meta -->
687             </head>
688             <body>
689             <header>
690             <h1>Site Title</h1>
691             </header>
692             <article>
693             <section id="primary">
694             <!-- section primary -->
695             <h2>$title</h2>
696             $body
697             <!-- endsection primary-->
698             </section>
699             <section id="secondary">
700             <!-- section secondary -->
701             <!-- endsection secondary-->
702             </section>
703             </article>
704             </body>
705             </html>
706             #>>> copy text _templates/create_gallery.htm
707             <html>
708             <head>
709             <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
710             <!-- section meta -->
711             <base href="$site_url/" />
712             <title>$title</title>
713             <meta name="date" content="$mdate" />
714             <meta name="description" content="$description" />
715             <meta name="keywords" content="$keywords" />
716             <meta name="author" content="$author" />
717             <!-- endsection meta -->
718             <link rel="stylesheet" id="css_style" href="theme.css">
719             </head>
720             <body>
721             <header>
722             <h1>Site Title</h1>
723             </header>
724             <article>
725             <section id="primary">
726             <!-- section primary -->
727             <!-- endsection primary-->
728             </section>
729             <section id="secondary">
730             <!-- section secondary -->
731             <section id="gallery">
732             <!-- for @files -->
733             <a href="$index_url#$target">
734             <!-- for @thumb_file -->
735             <img class="thumb" src="$url">
736             <!-- endfor -->
737             </a>
738             <div class="lightbox" id="$target">
739             <a href="$index_url#$target_previous" class="light-btn btn-prev">prev</a>
740             <a href="$index_url#_" class="btn-close">X</a>
741             <img src="$url">
742             <a href="$index_url#$target_next" class="light-btn btn-next">next</a>
743             </div>
744             <!-- endfor -->
745             </section>
746             <!-- endsection secondary-->
747             </section>
748             </article>
749             </body>
750             </html>
751             #>>> copy text _templates/create_index.htm
752             <html>
753             <head>
754             <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
755             <!-- section meta -->
756             <base href="$site_url/" />
757             <title>$title</title>
758             <meta name="date" content="$mdate" />
759             <meta name="description" content="$description" />
760             <meta name="keywords" content="$keywords" />
761             <meta name="author" content="$author" />
762             <!-- endsection meta -->
763             </head>
764             <body>
765             <header>
766             <h1>Site Title</h1>
767             </header>
768             <article>
769             <section id="primary">
770             </section>
771             <section id="secondary">
772             <!-- section secondary -->
773             <h2>$title</h2>
774              
775             <ul>
776             <!-- for @files_by_title -->
777             <li><a href="$url">$title</a></li>
778             <!-- endfor -->
779             </ul>
780             <!-- endsection secondary-->
781             </section>
782             </article>
783             </body>
784             </html>
785             #>>> copy text _templates/create_news.htm
786             <html>
787             <head>
788             <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
789             <!-- section meta -->
790             <base href="$site_url/" />
791             <title>$title</title>
792             <meta name="date" content="$mdate" />
793             <meta name="description" content="$description" />
794             <meta name="keywords" content="$keywords" />
795             <meta name="author" content="$author" />
796             <!-- endsection meta -->
797             </head>
798             <body>
799             <header>
800             <h1>Site Title</h1>
801             </header>
802             <article>
803             <section id="primary">
804             </section>
805             <section id="secondary">
806             <!-- section secondary -->
807             <!-- for @top_files_by_mdate_reversed -->
808             <p>$summary</p>
809             <p><a href="$url">More ...</a></p>
810              
811             <!-- endfor -->
812             <h3>Archive</h3>
813             <p>Other essays can be found in the following sections:</p>
814             <p>
815             <!-- for @folders -->
816             <a href="$url">$title</a>&nbsp;&nbsp;
817             <!-- endfor -->
818             </p>
819             <!-- endsection secondary-->
820             </section>
821             </article>
822             </body>
823             </html>
824             #>>> copy text essays/followme.cfg
825             run_before:
826             - App::Followme::CreateIndex
827             template_file: create_news.htm
828             #>>> copy text essays/index.md
829             ----
830             title: Essays Directory
831             description: A collection of short essays on various topics
832             keywords: essays
833             ----
834             This folder is configured (via followme.cfg) to contain short essays
835             on various topics. To use it, create subdirectories for each topic.
836             write your essay in Markdown format, and save the file in the appropriate
837             subdirectory. You can also include metadata for the essay, such as the
838             title at the top of the file, just as has been done in this file.
839              
840             When followme is run, it will create an index for files in the current directory
841             and its subdirectories that contain the text of the most recently modified files
842             together with links to the files. It can also be used to create a basic weblog.
843             #>>> copy text essays/archive/followme.cfg
844             run_before:
845             - App::Followme::CreateIndex
846             template_file: create_index.htm
847             #>>> copy text essays/archive/index.md
848             ----
849             title: Archive Directory
850             description: Archive of short essays
851             keywords: essays, archive
852             ----
853             This folder is configured (via followme.cfg) to contain an archive of
854             previously written essays. When followme is run, it will create an index
855             for files in the current directory containing a link to each essay in the
856             archive.
857             #>>> copy text photos/followme.cfg
858             run_before:
859             - App::Followme::CreateGallery
860             target_prefix: img
861             #>>> copy text photos/index.md
862             ----
863             title: Photo Gallery
864             description: A collection of photos of interest
865             ----
866             This folder is configured (via followme.cfg) to contain a photo
867             gallery. If you add or subtract photos from this folder and run
868             followme, it will update the gallery. Each photo must have a
869             thumbnail whose name is related to the photo like this:
870             photo-thumb.jpg. The suffix (-thumb) can be set in the configuration
871             file.