diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml
index 4c94041..56c9bb7 100644
--- a/.gitea/workflows/deploy.yaml
+++ b/.gitea/workflows/deploy.yaml
@@ -13,11 +13,6 @@ jobs:
with:
key: ${{ secrets.DEPLOY_KEY }}
known_hosts: ${{ secrets.SSH_HOST }}
- - uses: dlang-community/setup-dlang@v2
- with:
- compiler: ldc-latest
- - name: Build DDoc
- run: ./ddoc-build.sh
- uses: actions/setup-python@v6
with:
python-version: '3.12'
diff --git a/.gitignore b/.gitignore
index 7d87afd..d290a89 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,4 @@
site/
.cache/
# Ignore working dir for ddoc building.
-.ddoc/
-docs/ddoc/
\ No newline at end of file
+.ddoc/
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.html b/docs/ddoc/data/handy_http_data.html
new file mode 100644
index 0000000..583bfaf
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.html
@@ -0,0 +1,40 @@
+
+
+
+ handy_http_data (handy_http_data)
+
+
+
+
+
+
+
+
+
+
+
+
+
handy_http_data
json module handy_http_data.json
Defines functions to read and write JSON values when handling HTTP requests.
multipart module handy_http_data.multipart
Defines data structures and parsing methods for dealing with multipart
+encoded request bodies.
xml module handy_http_data.xml
handy_http_data.json public
+import handy_http_data .json ;
Undocumented in source.
handy_http_data.multipart public
+import handy_http_data .multipart ;
Undocumented in source.
handy_http_data.xml public
+import handy_http_data .xml ;
Undocumented in source.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.json.html b/docs/ddoc/data/handy_http_data.json.html
new file mode 100644
index 0000000..b25472f
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.json.html
@@ -0,0 +1,38 @@
+
+
+
+ handy_http_data.json (handy_http_data.json)
+
+
+
+
+
+
+
+
+
+
+
+
+
handy_http_data.json
readJsonBodyAs T readJsonBodyAs (ServerHttpRequest request)
Reads a JSON value from the body of the given HTTP request, parsing it using
+the ASDF library according to the template type T.
writeJsonBody void writeJsonBody (ServerHttpResponse response, T bodyContent)
Writes a JSON value to the body of the given HTTP response, serializing it
+using the ASDF library. Will also set Content-Type and Content-Length
+headers before writing.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.json.readJsonBodyAs.html b/docs/ddoc/data/handy_http_data.json.readJsonBodyAs.html
new file mode 100644
index 0000000..c5cf365
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.json.readJsonBodyAs.html
@@ -0,0 +1,36 @@
+
+
+
+ readJsonBodyAs (handy_http_data.json.readJsonBodyAs)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.json.writeJsonBody.html b/docs/ddoc/data/handy_http_data.json.writeJsonBody.html
new file mode 100644
index 0000000..1fe612d
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.json.writeJsonBody.html
@@ -0,0 +1,38 @@
+
+
+
+ writeJsonBody (handy_http_data.json.writeJsonBody)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MAX_ELEMENTS.html b/docs/ddoc/data/handy_http_data.multipart.MAX_ELEMENTS.html
new file mode 100644
index 0000000..c3dd5fb
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MAX_ELEMENTS.html
@@ -0,0 +1,37 @@
+
+
+
+ MAX_ELEMENTS (handy_http_data.multipart.MAX_ELEMENTS)
+
+
+
+
+
+
+
+
+
+
+
+
+
MAX_ELEMENTS
const
auto MAX_ELEMENTS =
1024 ;
+
handy_http_data multipart
+
classes functions structs variables
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartElement.content.html b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.content.html
new file mode 100644
index 0000000..d41eec9
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.content.html
@@ -0,0 +1,35 @@
+
+
+
+ MultipartElement.content (handy_http_data.multipart.MultipartElement.content)
+
+
+
+
+
+
+
+
+
+
+
+
+
MultipartElement.content
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartElement.filename.html b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.filename.html
new file mode 100644
index 0000000..3842a45
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.filename.html
@@ -0,0 +1,37 @@
+
+
+
+ MultipartElement.filename (handy_http_data.multipart.MultipartElement.filename)
+
+
+
+
+
+
+
+
+
+
+
+
+
MultipartElement.filename
struct MultipartElement
Optional !string filename ;
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartElement.headers.html b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.headers.html
new file mode 100644
index 0000000..80427bc
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.headers.html
@@ -0,0 +1,35 @@
+
+
+
+ MultipartElement.headers (handy_http_data.multipart.MultipartElement.headers)
+
+
+
+
+
+
+
+
+
+
+
+
+
MultipartElement.headers
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartElement.html b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.html
new file mode 100644
index 0000000..0619400
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.html
@@ -0,0 +1,39 @@
+
+
+
+ MultipartElement (handy_http_data.multipart.MultipartElement)
+
+
+
+
+
+
+
+
+
+
+
+
+
MultipartElement
struct MultipartElement {
}
content string content ;
The body content of this element.
filename Optional !string filename ;
The filename of this element, as declared by this part's
+Content-Disposition header filename property. Note that this may be
+null if no filename exists.
headers string [string ] headers ;
The headers that were present with this element.
name string name ;
The name of this element, as declared by this part's Content-Disposition
+header name property. There is no guarantee that this name is unique
+among all elements.
+
handy_http_data multipart
+
classes functions structs variables
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartElement.name.html b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.name.html
new file mode 100644
index 0000000..239b5ac
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartElement.name.html
@@ -0,0 +1,37 @@
+
+
+
+ MultipartElement.name (handy_http_data.multipart.MultipartElement.name)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartFormData.elements.html b/docs/ddoc/data/handy_http_data.multipart.MultipartFormData.elements.html
new file mode 100644
index 0000000..671920a
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartFormData.elements.html
@@ -0,0 +1,35 @@
+
+
+
+ MultipartFormData.elements (handy_http_data.multipart.MultipartFormData.elements)
+
+
+
+
+
+
+
+
+
+
+
+
+
MultipartFormData.elements
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartFormData.has.html b/docs/ddoc/data/handy_http_data.multipart.MultipartFormData.has.html
new file mode 100644
index 0000000..d3c0705
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartFormData.has.html
@@ -0,0 +1,37 @@
+
+
+
+ MultipartFormData.has (handy_http_data.multipart.MultipartFormData.has)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartFormData.html b/docs/ddoc/data/handy_http_data.multipart.MultipartFormData.html
new file mode 100644
index 0000000..088b9a6
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartFormData.html
@@ -0,0 +1,38 @@
+
+
+
+ MultipartFormData (handy_http_data.multipart.MultipartFormData)
+
+
+
+
+
+
+
+
+
+
+
+
+
MultipartFormData
struct MultipartFormData {
}
has bool has (string elementName)
Determines if this form-data has an element with the given name. This
+is case-sensitive. Note that there may be more than one element with a
+given name.
elements MultipartElement [] elements ;
Undocumented in source.
+
handy_http_data multipart
+
classes functions structs variables
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartFormatException.html b/docs/ddoc/data/handy_http_data.multipart.MultipartFormatException.html
new file mode 100644
index 0000000..074f4e5
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartFormatException.html
@@ -0,0 +1,37 @@
+
+
+
+ MultipartFormatException (handy_http_data.multipart.MultipartFormatException)
+
+
+
+
+
+
+
+
+
+
+
+
+
MultipartFormatException
class MultipartFormatException : HttpStatusException {
}
this this (string message)
Undocumented in source.
+
handy_http_data multipart
+
classes functions structs variables
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.MultipartFormatException.this.html b/docs/ddoc/data/handy_http_data.multipart.MultipartFormatException.this.html
new file mode 100644
index 0000000..669915a
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.MultipartFormatException.this.html
@@ -0,0 +1,35 @@
+
+
+
+ MultipartFormatException.this (handy_http_data.multipart.MultipartFormatException.this)
+
+
+
+
+
+
+
+
+
+
+
+
+
MultipartFormatException.this
class MultipartFormatException
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.html b/docs/ddoc/data/handy_http_data.multipart.html
new file mode 100644
index 0000000..a96bc5b
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.html
@@ -0,0 +1,43 @@
+
+
+
+ handy_http_data.multipart (handy_http_data.multipart)
+
+
+
+
+
+
+
+
+
+
+
+
+
handy_http_data.multipart
MultipartFormatException class MultipartFormatException
An exception that's thrown if parsing multipart/form-data fails due to
+invalid formatting or unexpected characters. This is a sub-class of the
+HttpStatusException , with each multipart exception being a BAD_REQUEST.
parseMultipartFormData MultipartFormData parseMultipartFormData (string content, string boundary)
A simple linear parser for multipart/form-data encoded content. Reads a
+series of elements separated by a given boundary. An exception is thrown
+if the given content doesn't conform to standard multipart format.
readBodyAsMultipartFormData MultipartFormData readBodyAsMultipartFormData (ServerHttpRequest request, bool allowInfiniteRead)
Reads a request's body as multipart/form-data encoded elements.
MultipartElement struct MultipartElement
A single element that's part of a multipart/form-data body.
MultipartFormData struct MultipartFormData
A multipart/form-data body containing multiple elements, and some helper
+methods for those elements.
MAX_ELEMENTS auto MAX_ELEMENTS ;
The maximum number of parts to read in a multipart/form-data body. This is
+declared as a safety measure to avoid infinite reading of malicious or
+corrupted payloads.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.parseMultipartFormData.html b/docs/ddoc/data/handy_http_data.multipart.parseMultipartFormData.html
new file mode 100644
index 0000000..d586ec8
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.parseMultipartFormData.html
@@ -0,0 +1,39 @@
+
+
+
+ parseMultipartFormData (handy_http_data.multipart.parseMultipartFormData)
+
+
+
+
+
+
+
+
+
+
+
+
+
handy_http_data multipart
+
classes functions structs variables
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.multipart.readBodyAsMultipartFormData.html b/docs/ddoc/data/handy_http_data.multipart.readBodyAsMultipartFormData.html
new file mode 100644
index 0000000..b753673
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.multipart.readBodyAsMultipartFormData.html
@@ -0,0 +1,35 @@
+
+
+
+ readBodyAsMultipartFormData (handy_http_data.multipart.readBodyAsMultipartFormData)
+
+
+
+
+
+
+
+
+
+
+
+
+
readBodyAsMultipartFormData
readBodyAsMultipartFormData
+
handy_http_data multipart
+
classes functions structs variables
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.xml.html b/docs/ddoc/data/handy_http_data.xml.html
new file mode 100644
index 0000000..35cf9eb
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.xml.html
@@ -0,0 +1,37 @@
+
+
+
+ handy_http_data.xml (handy_http_data.xml)
+
+
+
+
+
+
+
+
+
+
+
+
+
handy_http_data.xml
readXMLBody DOMEntity !string readXMLBody (ServerHttpRequest request)
Reads an XML request body.
writeXMLBody void writeXMLBody (ServerHttpResponse response, void delegate(ref XMLWriter!(Appender!string)) dg)
Writes an XML response body, by using the provided delegate function to
+compose the XML tag tree using a provided XMLWriter.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.xml.readXMLBody.html b/docs/ddoc/data/handy_http_data.xml.readXMLBody.html
new file mode 100644
index 0000000..2cfaef4
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.xml.readXMLBody.html
@@ -0,0 +1,35 @@
+
+
+
+ readXMLBody (handy_http_data.xml.readXMLBody)
+
+
+
+
+
+
+
+
+
+
+
+
+
readXMLBody
DOMEntity !string
readXMLBody
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/handy_http_data.xml.writeXMLBody.html b/docs/ddoc/data/handy_http_data.xml.writeXMLBody.html
new file mode 100644
index 0000000..5affc1b
--- /dev/null
+++ b/docs/ddoc/data/handy_http_data.xml.writeXMLBody.html
@@ -0,0 +1,36 @@
+
+
+
+ writeXMLBody (handy_http_data.xml.writeXMLBody)
+
+
+
+
+
+
+
+
+
+
+
+
+
writeXMLBody
void
writeXMLBody
( , void delegate ( ref XMLWriter !(Appender !string )
) dg )
response ServerHttpResponse dg void delegate ( ref XMLWriter !(Appender !string )
)
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/index.html b/docs/ddoc/data/index.html
new file mode 100644
index 0000000..01fe8a1
--- /dev/null
+++ b/docs/ddoc/data/index.html
@@ -0,0 +1,35 @@
+
+
+
+ index (index)
+
+
+
+
+
+
+
+
+
+
+
+
+
index
handy_http_data module handy_http_data
Undocumented in source.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ddoc/data/script.js b/docs/ddoc/data/script.js
new file mode 100644
index 0000000..b47257e
--- /dev/null
+++ b/docs/ddoc/data/script.js
@@ -0,0 +1,180 @@
+window.addEventListener("load", function() {
+
+ var lineWrappers = document.querySelectorAll(".with-line-wrappers");
+ for(var i = 0; i < lineWrappers.length; i++) {
+ var l = lineWrappers[i];
+
+ var codeblock = document.createElement("div");
+ codeblock.className = "codeblock";
+ l.parentNode.insertBefore(codeblock, l);
+
+ var header = document.createElement("header");
+ codeblock.appendChild(header);
+ codeblock.appendChild(l);
+
+ var btn = document.createElement("button");
+ btn.setAttribute("type", "button");
+ var canCopyToClipboard = document.queryCommandSupported("copy");
+ btn.addEventListener("click", (function(l) { return function() {
+ document.body.classList.add("hide-line-numbers");
+ window.getSelection().selectAllChildren(l);
+ if(canCopyToClipboard)
+ if(!document.execCommand("copy")) {
+ alert("copy failed, try ctrl+c manually");
+ }
+ };})(l));
+ btn.textContent = canCopyToClipboard ? "Copy to Clipboard" : "Select All";
+ header.appendChild(btn);
+
+ var btn = document.createElement("button");
+ btn.setAttribute("type", "button");
+ btn.addEventListener("click", function() {
+ document.body.classList.toggle("hide-line-numbers");
+ });
+ btn.textContent = "Toggle Line Numbers";
+ header.appendChild(btn);
+ }
+
+ /* // still sucks in firefox!
+ document.addEventListener("copy", function(event) {
+ document.body.classList.add("hide-line-numbers");
+ });
+ */
+
+ document.body.addEventListener("mouseover", function(event) {
+ if(event.target.hasAttribute("data-ident")) {
+ var all = document.querySelectorAll("[data-ident=\""+event.target.getAttribute("data-ident")+"\"]");
+ for(var i = 0; i < all.length; i++)
+ all[i].className += " active";
+ }
+ });
+ document.body.addEventListener("mouseout", function(event) {
+ if(event.target.hasAttribute("data-ident")) {
+ var all = document.querySelectorAll("[data-ident=\""+event.target.getAttribute("data-ident")+"\"]");
+ for(var i = 0; i < all.length; i++)
+ all[i].className = all[i].className.replace(" active", "");
+ }
+ });
+ /*
+ document.body.addEventListener("dblclick", function(event) {
+ if(event.target.hasAttribute("data-ident")) {
+ location.href = "/" + event.target.getAttribute("data-ident");
+ }
+ });
+ */
+
+ var sn = document.getElementById("source-navigation");
+ if(sn) {
+ sn.addEventListener("click", function(event) {
+ if(event.target.tagName != "A" || event.target.className == "docs")
+ return true;
+ if(event.target.nextSibling) {
+ var s = event.target.nextSibling;
+ if(s.style.display == "" || s.style.display == "none" || s.className.indexOf("search-hit") != -1) {
+ s.style.display = "block";
+ var items = s.getElementsByTagName("ul");
+ var i;
+ for(i = 0; i < items.length; i++)
+ items[i].style.display = "";
+ items = s.getElementsByTagName("li");
+ for(i = 0; i < items.length; i++)
+ items[i].style.display = "";
+ } else
+ s.style.display = "";
+ }
+
+ //var id = event.target.href.substring(event.target.href.indexOf("#") + 1);
+ //sn.style.marginTop = (document.getElementById(id).offsetTop - event.target.offsetTop + 16) + "px";
+ });
+
+ var search = document.createElement("input");
+ search.setAttribute("type", "search");
+ function searchHelper() {
+ var regex = new RegExp(search.value, "i");
+ var items = document.querySelectorAll("#source-navigation a[href^=\"#\"]");
+ var stxt = search.value;
+ for(var i = 0; i < items.length; i++) {
+ var a = items[i];
+ if(stxt.length && regex.test(a.textContent)) {
+ var p = a.parentNode;
+ while(p.tagName != "DIV") {
+ if(p.tagName == "LI")
+ p.style.display = "list-item";
+ else
+ p.style.display = "block";
+ p.className += " search-hit";
+ p = p.parentNode;
+ }
+ } else {
+ var p = a.parentNode;
+ if(stxt.length == 0) {
+ p.style.display = "";
+ while(p.tagName != "DIV") {
+ p.style.display = "";
+ p = p.parentNode;
+ }
+ } else
+ p.style.display = "none";
+ p.className = p.className.replace(" search-hit", "");
+ }
+ }
+ }
+ search.addEventListener("keyup", searchHelper);
+ sn.insertBefore(search, sn.firstChild);
+ }
+
+ /*
+ function updateDynamicStyle() {
+ var thing = document.getElementById("page-content");
+ var newStyle = document.getElementById("dynamic-style");
+ if(!newStyle) {
+ newStyle = document.createElement("style");
+ newStyle.setAttribute("id", "dynamic-style");
+ newStyle.type = "text/css";
+ document.head.appendChild(newStyle);
+ }
+
+ var maxContentWidth = window.innerWidth;
+ // 800 is the threshold for putting nav vertically
+ if(maxContentWidth < 800)
+ maxContentWidth = 800;
+ else
+ maxContentWidth =
+ document.body.offsetWidth -
+ document.getElementById("page-nav").offsetWidth -
+ document.getElementById("page-nav").offsetLeft -
+ 64;
+
+ // sanity check lol
+ if(maxContentWidth < 800)
+ maxContentWidth = 800;
+
+ newStyle.innerHTML = ".member-list:not(.constructors) dt .simplified-prototype:hover { width: " + (thing.offsetWidth - 32) + "px; } #page-content pre.d_code, #page-content .overload-option, #page-content .member-list dt { max-width: " + (maxContentWidth) + "px; }";
+ }
+
+ updateDynamicStyle();
+
+ window.onresize = updateDynamicStyle;
+ */
+
+ // Disable line numbers in IE because the copy/paste with them sucks - it includes all line numbers
+ // in the middle making it too hard to use. Copy/paste is more important than line displays.
+ if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) {
+ var items = document.querySelectorAll(".with-line-wrappers");
+ for(var a = 0; a < items.length; a++)
+ items[a].className = items[a].className.replace("with-line-wrappers", "");
+ }
+
+ // Keybind to focus search bar on '?' keydown.
+ document.addEventListener("keydown", (event) => {
+ if (event.key == "?") {
+ var searchBox = document.getElementsByName("searchTerm")[0];
+ // Hack so the '?' doesn't auto-populate in the search bar.
+ this.setTimeout(() => {
+ searchBox.focus();
+ }, 100);
+ }
+ });
+
+
+});
diff --git a/docs/ddoc/data/search-docs.html b/docs/ddoc/data/search-docs.html
new file mode 100644
index 0000000..522aab4
--- /dev/null
+++ b/docs/ddoc/data/search-docs.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+ The "offline" searcher requires Javascript.
+ To get search without JS, build the `locate.d` file (see the adrdox makefile) and use it as a CGI program on a regular web server, or build with -version=embedded_httpd to embed its own server and run that.
+ Then, configure the server to call that script instead of loading this file, or change your skeleton.html search form action to point to it.
+
+
diff --git a/docs/ddoc/data/search-docs.js b/docs/ddoc/data/search-docs.js
new file mode 100644
index 0000000..768a988
--- /dev/null
+++ b/docs/ddoc/data/search-docs.js
@@ -0,0 +1,421 @@
+/*
+ This is the source for offline web search; it will be embedded
+ in a generated search page along with the search index xml.
+
+ You will almost certainly want to gzip this when delivering it!
+ Also be sure it has the proper cache headers to give a remotely
+ acceptable performance result. Configure the web server to do
+ both these. When storing it for offline usage, you might just
+ leave it unzipped though for convenience of use without a web server.
+
+ Tip to the end user: you might want to open this page and keep it
+ open in a reused tab.
+
+ The file generated should be the skeleton.html with the search
+ index in a
+
+
+
+
+
+
+
+
+
+
+
+