diff --git a/templates.py b/templates.py
index e656c65..86227b5 100644
--- a/templates.py
+++ b/templates.py
@@ -1,7 +1,11 @@
+import re
import os
import os.path
import sys
+PREPROCESS_RE = re.compile("^\s*#")
+INCLUDE_RE = re.compile('^\s*#include\s*"(\w+)"')
+
class Templates (object):
def __init__(self, templatedirs, extension):
self.extension = extension
@@ -23,11 +27,27 @@ class Templates (object):
def get_in(self, templatedir, name):
filename = self.get_template_filename(templatedir, name)
f = open(filename, "r")
- template = f.read()
+ template = self.read_template(f);
f.close()
self.cached_templates[name] = template
return template
+ def read_template(self, f):
+ res = ""
+ for line in f.readlines():
+ if PREPROCESS_RE.match(line):
+ res += self.preprocessline(line)
+ else:
+ res += line
+ return res
+
+ def preprocessline(self, line):
+ m = INCLUDE_RE.match(line)
+ if m:
+ return self.get(m.group(1))
+ else:
+ raise Exception("Bad preprocessor line '%s' in template." % line)
+
def get_template_filename(self, templatedir, name):
return os.path.join(templatedir,
self.extension,
diff --git a/templates/html/begin.html b/templates/html/begin.html
index f02ae4c..3d3b600 100644
--- a/templates/html/begin.html
+++ b/templates/html/begin.html
@@ -2,202 +2,15 @@
-
- Gamebook Title Here
+#include "viewport"
+#include "title"
-
+#include "intro"
diff --git a/templates/html/collections.html b/templates/html/collections.html
new file mode 100644
index 0000000..21a866c
--- /dev/null
+++ b/templates/html/collections.html
@@ -0,0 +1,2 @@
+
+
diff --git a/templates/html/collectiontemplate.html b/templates/html/collectiontemplate.html
new file mode 100644
index 0000000..99a73d8
--- /dev/null
+++ b/templates/html/collectiontemplate.html
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/templates/html/end.html b/templates/html/end.html
index 0b8ce3f..c3cc145 100644
--- a/templates/html/end.html
+++ b/templates/html/end.html
@@ -1,14 +1,7 @@
-
-
-
-
-
-
-
+#include "collections"
+#include "collectiontemplate"
diff --git a/templates/html/endscript.html b/templates/html/endscript.html
new file mode 100644
index 0000000..926a878
--- /dev/null
+++ b/templates/html/endscript.html
@@ -0,0 +1,3 @@
+if (this.gamebook) {
+ gamebook.turnTo(1);
+}
diff --git a/templates/html/script.html b/templates/html/script.html
new file mode 100644
index 0000000..9c7c7a0
--- /dev/null
+++ b/templates/html/script.html
@@ -0,0 +1,168 @@
+ var gamebook = {
+ 'player' : {
+ 'currentSection' : null,
+ 'collections' : {},
+
+ 'collect' : function(type, name) {
+ this.collections[type] = {
+ 'name' : name,
+ 'contents' : [],
+ 'add' : function(what) {
+ if (this.contents.indexOf(what) === -1
+ && !(what in gamebook.dropped[type])) {
+ this.contents.push(what);
+ this.contents.sort();
+ }
+ },
+ 'drop' : function(what) {
+ var i = this.contents.indexOf(what);
+ if (i >= 0) {
+ this.contents.splice(i, 1);
+ gamebook.dropped[type][what] = true;
+ }
+ },
+ 'has' : function(what) {
+ return this.contents.indexOf(what) >= 0;
+ }
+ };
+ gamebook.dropped[type] = {};
+ gamebook.addCollectionView(type, name);
+ },
+
+ 'add' : function(type, what) {
+ this.collections[type].add(what);
+ gamebook.updateCollectionsView();
+ },
+
+ 'drop' : function(type, what) {
+ this.collections[type].drop(what);
+ gamebook.updateCollectionsView();
+ },
+
+ 'has' : function(type, what) {
+ return this.collections[type].has(what);
+ }
+ },
+
+ 'sections' : {},
+
+ 'turnToFunctions' : {},
+
+ 'dropped' : {},
+
+ 'addSection' : function(nr, element) {
+ var section = {'element' : element, 'nr' : nr};
+ this.sections[nr] = section;
+ },
+
+ 'turnTo' : function(nr) {
+ console.log("Turning to " + nr + ".");
+ if (!nr in this.sections) {
+ throw new Exception("Can not turn to non-existing section "
+ + nr + ".");
+ }
+ this.displaySection(nr);
+ },
+
+ 'displaySection' : function(nr) {
+ if (this.player.currentSection) {
+ this.player.currentSection.element.style.display = 'none';
+ }
+ var e = this.sections[nr].element;
+ this.runActions(e.getElementsByClassName('sectiontext')[0]);
+ e.style.display = 'block';
+ this.player.currentSection = gamebook.sections[nr];
+ },
+
+ 'runActions' : function(e) {
+ var enableNextLink = true;
+ var hasXorScope = false;
+ var hasAutoScope = false;
+ var xorEnableNext = false;
+ var autoDisableAllRemainingLinks = false;
+ Array.prototype.forEach.call(e.childNodes, function(c) {
+ if (/sectionref$/.test(c.className)) {
+ if (enableNextLink && !autoDisableAllRemainingLinks) {
+ gamebook.enableLink(c);
+ if (hasAutoScope) {
+ autoDisableAllRemainingLinks = true;
+ }
+ } else {
+ gamebook.disableLink(c);
+ }
+ enableNextLink = !(hasXorScope && !xorEnableNext);
+ hasAutoScope = false;
+ hasXorScope = false;
+ } else if (c.className === 'collect') {
+ gamebook.player.collect(c.dataset.type, c.dataset.name);
+ } else if (c.className === 'add') {
+ gamebook.player.add(c.dataset.type, c.dataset.what);
+ } else if (c.className === 'drop') {
+ gamebook.player.drop(c.dataset.type, c.dataset.what);
+ } else if (c.className === 'has') {
+ enableNextLink = gamebook.player.has(c.dataset.type,
+ c.dataset.what);
+ console.log("has " + c.dataset.type +
+ " " + c.dataset.what + " " + enableNextLink);
+ } else if (c.className === 'hasnot') {
+ enableNextLink = !gamebook.player.has(c.dataset.type,
+ c.dataset.what);
+ console.log("has not " + c.dataset.type +
+ " " + c.dataset.what + " " + enableNextLink);
+ } else if (c.className === 'xor') {
+ hasXorScope = true;
+ xorEnableNext = !enableNextLink;
+ } else if (c.className === 'auto') {
+ hasAutoScope = true;
+ }
+ });
+ },
+
+ 'enableLink' : function(e) {
+ e.addEventListener('click',
+ gamebook.getTurnToFunction(e.dataset.ref));
+ e.className = "enabledsectionref";
+ },
+
+ 'disableLink' : function(e) {
+ e.removeEventListener('click',
+ gamebook.getTurnToFunction(e.dataset.ref));
+ e.className = "disabledsectionref";
+ },
+
+ 'addCollectionView' : function(type, name) {
+ var ce = document.getElementById('collections');
+ var template = document.getElementById('collectionTemplate');
+ var e = template.cloneNode(true);
+ e.className = "collection";
+ e.getElementsByClassName('collectionheading')[0].innerHTML = name;
+ e.dataset.type = type;
+ ce.appendChild(e);
+ },
+
+ 'updateCollectionsView' : function() {
+ var ce = document.getElementById('collections');
+ Array.prototype.forEach.call(ce.childNodes, function(c) {
+ if (c.className === 'collection') {
+ var type = c.dataset.type;
+ var collection = gamebook.player.collections[type];
+ var cc = c.getElementsByClassName('collectioncontents')[0];
+ cc.innerHTML = collection.contents.join(', ');
+ }
+ });
+ },
+
+ 'getTurnToFunction' : function(nr) {
+ if (nr in this.turnToFunctions) {
+ return this.turnToFunctions[nr];
+ } else {
+ var f = function () {
+ gamebook.turnTo(nr);
+ };
+ this.turnToFunctions[nr] = f;
+ return f;
+ }
+ }
+
+ };
+
diff --git a/templates/html/style.html b/templates/html/style.html
new file mode 100644
index 0000000..559e702
--- /dev/null
+++ b/templates/html/style.html
@@ -0,0 +1,20 @@
+ .enabledsectionref {font-weight: bold; cursor: pointer;}
+ .disabledsectionref {font-weight: bold; color: #aaa; cursor: not-allowed;}
+ .sectionnumber {font-weight: bolder;
+ margin-left: 50%%;
+ margin-right: 50%%;}
+ .section {display: none; width: 90%%; margin-left: 5%%; margin-right: 5%%;}
+ .sectiontext {margin-top: 0.5em;}
+ .gamebook {max-width: 30em; padding: 1em; width: 100%%; font-size: 133%%;}
+ .collections {margin-top: 4em;}
+ .collection {background: #ddd; margin-top: 1em;}
+ .collectionheading {}
+ .collectionheading::after {content: ": ";}
+ .collectioncontents {}
+ .collect {}
+ .add {font-weight: bold;}
+ .found {font-weight: bold; cursor: pointer;}
+ .drop {font-style: italic;}
+ .has {font-style: italic;}
+ .hasnot {font-style: italic;}
+ .collectionTemplate {display: none;}
diff --git a/templates/html/title.html b/templates/html/title.html
new file mode 100644
index 0000000..d94d5a6
--- /dev/null
+++ b/templates/html/title.html
@@ -0,0 +1 @@
+ Gamebook
diff --git a/templates/html/viewport.html b/templates/html/viewport.html
new file mode 100644
index 0000000..81d7f88
--- /dev/null
+++ b/templates/html/viewport.html
@@ -0,0 +1,2 @@
+
diff --git a/templates/tex/begin.tex b/templates/tex/begin.tex
index bd5a759..64fc6ff 100644
--- a/templates/tex/begin.tex
+++ b/templates/tex/begin.tex
@@ -1,10 +1,9 @@
-\documentclass[A5,twocolumn]{article}
+#include "documentclass"
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[hidelinks]{hyperref}
-\usepackage[top=3.3cm, bottom=3.3cm, left=2cm, right=2cm]{geometry}
-
+#include "geometry"
\newif\ifpdf
\ifx\pdfoutput\undefined
\pdffalse
@@ -16,7 +15,7 @@
\fi
\fi
-\title{Gamebook}
+#include "title"
\author{}
\date{}
diff --git a/templates/tex/documentclass.tex b/templates/tex/documentclass.tex
new file mode 100644
index 0000000..979305c
--- /dev/null
+++ b/templates/tex/documentclass.tex
@@ -0,0 +1 @@
+\documentclass[A5,twocolumn]{article}
diff --git a/templates/tex/geometry.tex b/templates/tex/geometry.tex
new file mode 100644
index 0000000..7d18bd0
--- /dev/null
+++ b/templates/tex/geometry.tex
@@ -0,0 +1 @@
+\usepackage[top=3.3cm, bottom=3.3cm, left=2cm, right=2cm]{geometry}
diff --git a/templates/tex/title.tex b/templates/tex/title.tex
new file mode 100644
index 0000000..62886d1
--- /dev/null
+++ b/templates/tex/title.tex
@@ -0,0 +1 @@
+\title{Gamebook}
diff --git a/todo.org b/todo.org
index 531e24b..f56020d 100644
--- a/todo.org
+++ b/todo.org
@@ -1,4 +1,4 @@
-* TODO [27/59] [45%]
+* TODO [28/59] [47%]
- [X] Debug output
- [X] DOT output
- [X] LaTeX output
@@ -36,7 +36,7 @@
- [ ] Book option to set author
- [ ] Book option to set date
- [ ] Quote strings to not break formatting.
-- [ ] Include other templates from a template.
+- [X] Include other templates from a template.
- [ ] Template for book introduction (including rules etc)
Sections with some markup (has number 0?) are added as chapters
of introduction, otherwise formatted identical to other sections.