+package Gruntmaster::Page::Base;
+
+use 5.014000;
+use strict;
+use warnings;
+
+use File::Slurp qw/read_file/;
+use HTML::Template::Compiled;
+
+##################################################
+
+use POSIX ();
+use Gruntmaster::Data ();
+use List::Util ();
+
+sub import {
+ my $caller = caller;
+ my ($self, $name, $title) = @_;
+
+ Gruntmaster::Data->export_to_level(1, $caller);
+ List::Util->export_to_level(1, $caller, qw/sum/);
+
+ no strict 'refs';
+ *{"${caller}::strftime"} = \&POSIX::strftime;
+ *{"${caller}::NAME"} = sub () { $name };
+ *{"${caller}::TITLE"} = sub () { $title };
+}
+
+##################################################
+
+my %orig_header_templates = (
+ en => <<'HTML',
+<!DOCTYPE html>
+<title>TITLE_GOES_HERE</title>
+<link rel="stylesheet" href="/gm.css">
+<script src="/zepto.var" defer></script>
+<script src="/view.js" defer></script>
+<script src="/form.js" defer></script>
+<meta charset="utf-8">
+
+<span id="admin"></span>
+<div id="title"><span class="i">i</span><span class="Eval">Eval</span></div>
+<div id="subtitle">TITLE_GOES_HERE</div>
+
+<nav><ul><li><a href="/learn.var">Learn</a><li><a href="/pb/">Practice</a><li><a href="/ct/">Compete</a><li><a href="/log/">Job log</a></ul></nav>
+
+HTML
+);
+
+my %orig_footer_templates = (
+ en => <<'HTML',
+
+<footer>
+Dilmom: Why don't you call your product the Gruntmaster 6000?
+Dilbert: What kind of product do you see when you imagine a Gruntmaster 6000?
+Dilmom: Well, it's a stripped-down version of the Gruntmaster 9000, of course. But it's software-upgradeable.
+</footer>
+HTML
+);
+
+sub patch_templates {
+ my $root = $ENV{GRUNTMASTER_TEMPLATE_ROOT} or return;
+ my ($templates, $name) = @_;
+ my %out = %$templates;
+ for (<$root/$name*>) {
+ m/\.(.+)$/;
+ $out{$1} = read_file $_
+ }
+
+ %out
+}
+
+sub reload_templates (){ $ENV{GRUNTMASTER_RELOAD_TEMPLATES} }
+
+my %header_templates = patch_templates \%orig_header_templates, 'header';
+my %footer_templates = patch_templates \%orig_footer_templates, 'footer';
+
+sub header{
+ my ($language, $title) = @_;
+ %header_templates = patch_templates \%orig_header_templates, 'header' if reload_templates;
+ $header_templates{$language} =~ s/TITLE_GOES_HERE/$title/ger;
+}
+
+sub footer{
+ %footer_templates = patch_templates \%orig_footer_templates, 'footer' if reload_templates;
+ $footer_templates{$_[0]};
+}
+
+sub cook_templates {
+ my ($templates, $name, $title) = @_;
+
+ my %out = patch_templates $templates, $name;
+ $out{$_} = header ($_, $title) . $out{$_} for keys %out;
+ $out{$_} .= footer $_ for keys %out;
+
+ %out
+}
+
+##################################################
+
+my %templates;
+
+sub generate{
+ my ($self, $path, $lang) = @_;
+
+ $templates{$self} = { cook_templates $self->TEMPLATES, $self->NAME => $self->TITLE } if !exists $templates{$self} or reload_templates;
+
+ my $htc = HTML::Template::Compiled->new(scalarref => \$templates{$self}{$lang}, default_escape => 'HTML',);
+ $self->_generate($htc, $path, $lang);
+ $htc->output
+}
+
+sub _generate {}
+
+1