File Coverage

lib/App/Followme/Initialize.pm
Criterion Covered Total %
statement 114 121 94.2
branch 23 34 67.6
condition 5 9 55.5
subroutine 22 23 95.6
pod 0 10 0.0
total 164 197 83.2


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