Initial commit: Klammertext source distribution

Curated source subset assembled by klammertext-dev's doc/make_dist.sh: the Klammermachine (mac), the Standard Klammer Set (sks), the commands (com), editor plugins and install guides (doc), a test subset (tst), and lib/bin placeholders. Builds with 'make -C com'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-18 18:48:23 +02:00
commit 2ba7ceee7a
272 changed files with 27634 additions and 0 deletions

1
sks/document/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.d

58
sks/document/Makefile Normal file
View File

@@ -0,0 +1,58 @@
# Klammertext sks/document/ Makefile
# Improved version with automatic header dependency tracking
K := $(KLAMMERTEXT_HOME)
KS := $(K)/sks
KM := $(K)/mac
include $(KM)/env/makefile.env
# Additional include paths for sks components
LOCAL_CPPFLAGS := -I$(KM) -I$(KS)/kutil -I$(KS)/target
# Shared library location
LIBDIR := $(K)/lib
LIBRARY := $(LIBDIR)/libklammertext.so
# Linker flags for document.so
DOC_LDFLAGS := $(SHARED) $(EXPORT_DYNAMIC) -Wl,-rpath,'$(ORIGIN)/../../lib' -L$(LIBDIR)
# Local object files
LOCAL_OBJECTS := document_class.o document_html.o document_latex.o heading.o reference.o
LOCAL_DEPFILES := document_class.d document_html.d document_latex.d heading.d reference.d document.d
# External object files from sks/ (mac/*.o now in libklammertext.so)
SKS_OBJECTS := $(KS)/kutil/kutil.o $(KS)/kutil/klammer_base.o \
$(KS)/target/html_util.o $(KS)/target/latex_util.o \
$(KS)/target/font_resolve.o
ALL_OBJECTS := $(LOCAL_OBJECTS) $(SKS_OBJECTS)
# Compiler flags for dependency generation
DEPFLAGS = -MMD -MP -MF $(@:.o=.d)
.PHONY: all clean deps
# Default target
all : document.so
# Build external dependencies first
deps :
$(MAKE) -C $(KS)/kutil
$(MAKE) -C $(KS)/target
# Pattern rule for object files
%.o : %.cpp
@echo Compiling $<
$(CXX) -c $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $< -o $@
# Shared object - link against libklammertext.so
document.so : $(LOCAL_OBJECTS) $(SKS_OBJECTS) $(LIBRARY) document.cpp | deps
$(CXX) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(CXXFLAGS) $(DOC_LDFLAGS) $(LDFLAGS) \
document.cpp -o $@ $(ALL_OBJECTS) -lklammertext $(LDLIBS)
clean :
rm -f $(LOCAL_OBJECTS) $(LOCAL_DEPFILES) *.so *~ __pycache__
# Include generated dependency files (if they exist)
-include $(LOCAL_DEPFILES)

View File

@@ -0,0 +1,453 @@
:root {
--hpad: 1rem;
--frame_weight: normal;
}
html, body, div {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
}
#title {
color: var(--frame_text_color);
background-color: var(--frame_background_color);
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
font-family: var(--sans-serif);
font-size: 1.4rem;
font-weight: var(--frame_weight);
padding: .4rem 1rem;
overflow: hidden;
border-top: 1px solid black;
border-bottom: 1px solid black;
/* text-shadow: 1px 1px 2px black; */
}
#middle {
display: flex;
padding: 0;
margin: 0;
border: 0;
align-content: stretch;
overflow: hidden;
}
/* In plain/article, #content is the only child of #middle and must
fill the full width. In book, #content shares #middle with #toc
and #resizer, so this rule must not apply. */
#content:only-child {
flex: 1;
}
#navtools, #searchtools {
display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
border: 0;
white-space: nowrap;
}
#searchtools {
}
#nav {
color: var(--nav_text_color);
background-color: var(--nav_background_color);
font-family: var(--sans-serif);
height: 1.4rem;
padding: .35rem 1rem;
font-size: .9rem;
line-height: 1.2rem;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid black;
font-weight: var(--frame_weight);
}
.navb {
padding: 0 .25em 0 .25em;
cursor: pointer;
user-select: none;
}
.navb:first-child {
padding: 0 .25em 0 0;
}
#search_input {
width: 25vw;
max-width: 12rem;
height: .9rem;
font-family: var(--sans-serif);
font-size: .8rem;
margin: 0;
border: 1px solid black;
padding-left: 0.25rem;
}
#search_input:focus {
outline-width: 0;
}
#search_box {
display: inline-block;
margin: 0;
padding: 0;
border: 0;
line-height: 0;
}
.search_target {
background-color: rgb(95%,95%,60%);
padding: 0 .25em 0 .25em;
border: 1px solid rgb(70%,70%,70%)
}
/*
#help {
padding-top: .2rem 0 .15rem 0;
}
*/
#text, #help_page, #search_page {
color: black;
background-color: white;
padding: 0 1.5rem 1.5rem 1.5rem;
overflow: auto;
height: 100%;
}
#status {
color: var(--frame_text_color);
background-color: var(--frame_background_color);
font-family: var(--sans-serif);
font-size: .8rem;
font-weight: var(--frame_weight);
padding: .25rem 0 .35rem 0;
line-height: 1.2rem;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
border-top: 1px solid black;
border-bottom: 1px solid black;
/* text-shadow: 1px 1px 2px black; */
}
.spacer {
display: inline-block;
width: .7em;
}
.endspace {
height: 50vh;
}
.tight {
border: 0;
margin: 0;
padding: .1em 0 .1em 0;
}
#xtoc {
font-family: var(--sans-serif);
width: auto;
cursor: default;
float: left;
white-space: nowrap;
position: absolute;
overflow: auto;
overflow-x: hidden;
background-color: lightyellow;
padding: 0;
line-height: 1.5;
font-size: .9rem;
}
.navlink {
cursor: pointer;
}
.navlink:hover .section-title {
text-decoration: underline;
text-underline-offset: 0.2em;
text-decoration-thickness: 1px;
}
#slider {
z-index: 90;
overflow: visible;
}
#pagetoc {
font-family: var(--sans-serif);
line-height: 1.2;
}
#resizer {
border-width: 0px 1px 0px 1px;
border-color: transparent black transparent black;
border-style: solid;
background-color: var(--nav_background_color);
height: 100%;
width: 4px;
cursor: e-resize;
}
#toc {
background-color: white;
font-family: var(--sans-serif);
font-size: .8rem;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
white-space: nowrap;
position: relative;
padding: 1rem 0 0 1.8rem;
box-sizing: border-box;
}
#toc, #toc_items ul, #toc_items ul li {
list-style-type: none;
}
#toc ul {
list-style-type: none;
padding-left: .5rem;
margin: 0;
}
#toc .caret {
padding-left: .65rem;
}
#toc li {
line-height: 1.5;
}
.toctitle {
font-family: var(--sans-serif);
font-size: 1.2rem;
margin-top: 1rem;
}
.toc {
font-family: var(--sans-serif);
font-size: 1.1rem;
margin-top: .1rem;
}
.tocnumber{
padding-right: .75rem;
}
.tab0 { padding-left: 1rem; }
.tab1 { padding-left: 2rem; }
.tab2 { padding-left: 3rem; }
.tab3 { padding-left: 4rem; }
.tab4 { padding-left: 5rem; }
.tab5 { padding-left: 6rem; }
.tab6 { padding-left: 7rem; }
.tab7 { padding-left: 8rem; }
.tab8 { padding-left: 9rem; }
.tab9 { padding-left: 10rem; }
/*
#toc ul {
padding-left: .5rem;
line-height: 1.5;
}
#toc li::marker {
font-size: 0px;
}
#toc_items {
margin: 0;
padding: 1em;
line-height: 1.4;
}
#toc_items li {
padding-left: 0;
}
#toc_items ul .unnumbered {
padding-left: 0;
}
*/
.caret {
cursor: pointer;
user-select: none;
font-size: .8rem;
padding: 0 .3rem 0 0;
vertical-align: middle;
/*
color: gray;
*/
font-weight: bold;
}
.caret::before {
display: inline-block;
content: "\25B7";
transform: translateY(-.4ex);
}
.caret-down::before {
transform: translateY(-.2ex) rotate(90deg);
}
.nested {
display: none;
}
.active {
display: block;
}
.highlight {
font-weight: bold;
padding: 0;
margin: 0;
}
.highlight_parent {
background-color: var(--nav_background_color);
border: solid 1px var(--nav_background_color);
}
.section-title {
padding: 0;
border: 0;
}
.caret {
margin-left: -1.75rem;
border: 0;
}
.section-number {
padding-right: .3rem;
}
#pagecache {
display: none;
}
#search_page {
background-color: var(--nav_background_color);
}
.search_result {
border: solid 1px black;
padding: .75rem;
margin: 1rem 0 1rem 0;
color: black;
text-decoration: none;
background-color: white;
line-height: 1.3;
display: block;
}
.search_result:hover {
text-decoration: none;
}
.search_result p, search_result div {
margin: 0;
padding: 0;
}
#text {
background-color: white;
padding-bottom: 2rem;
}
#search_clear_input_box {
display: inline-block;
width: 1.5rem;
border: 0;
cursor: pointer;
user-select: none;
}
#fit {
margin: 0 .25em 0 .5em;
user-select: none;
}
#linkshow {
margin: 0 0 0 .25em;
}
input[type="checkbox"] {
cursor: pointer;
user-select: none;
}
label {
user-select: none;
}
label[for=linkshow_check], label[for=fit_check] {
cursor: pointer;
}
.displayed_link {
/* background-color: var(--nav_background_color); */
/* background-color: rgb(209,255,127, .5); */
background-color: rgb(70%,80%,100%,.5);
text-decoration: none; /* underline; */
color: black;
/* padding: 0 .25em 0 .25em; */
}
.undisplayed_link {
background-color: transparent;
color: var(--link-blue);
text-decoration: none;
}
.footeritem {
padding: 0 .75em;
white-space: nowrap;
}
.no_spacing * {
margin: 0 !important;
padding: 0 !important;
vertical-align: middle;
}
/*
p::before {
content: "[" attr(id) "] ";
}
*/

View File

@@ -0,0 +1 @@
document.css

86
sks/document/document.cpp Normal file
View File

@@ -0,0 +1,86 @@
#include "document_class.h"
#include "latex_util.h"
#include "machine.h"
#include "show.h"
#include "log.h"
// extern "C" is needed for dlsym name lookup. Returning std::string from
// C-linkage functions is technically non-standard but works correctly on
// Linux (Itanium ABI) where C and C++ calling conventions are identical.
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
#endif
extern "C"
std::string document(Machine& machine)
{
(void)K::log(3);
try {
Document_class D(machine);
return D.result();
}
catch (Error& err) {
err.print_message();
std::cout << "\n";
exit(1);
}
}
extern "C"
std::string tex_to_pdf(Machine& machine)
{
(void)K::log(3);
try {
check_for_xelatex();
std::string outbase = machine.m_state.value("K_output_dir")
+ "/" + machine.m_state.value("K_output_basename");
std::string tex_filename = outbase + ".tex";
for (auto ext : {"aux", "log", "out", "toc"}) {
fs::remove(outbase + "." + ext);
}
std::string command = "xelatex -interaction=batchmode -halt-on-error " + tex_filename;
string_to_file(tex_filename, machine.m_result);
std::string xelatex_output = exec(command.c_str());
std::string xelatex_log = string_from_file(outbase + ".log");
std::vector<std::string> error_lines = find_latex_error_lines(xelatex_log);
if (!error_lines.empty()) {
std::stringstream ss {};
ss << "Errors reported in xelatex log file:\n";
for (auto line : error_lines) {
ss << " " << line << "\n";
}
ss << "Check log file: " << outbase << ".log";
throw Definition_error(ss.str(), Locator(), false);
}
if (std::regex_search(xelatex_log, std::regex("Package rerunfilecheck Warning:"))) {
(void)K::log(1, "Rerunning xelatex because document structure has changed");
xelatex_output = exec(command.c_str());
xelatex_log = string_from_file(outbase + ".log");
error_lines = find_latex_error_lines(xelatex_log);
if (!error_lines.empty()) {
std::stringstream ss {};
ss << "Errors reported in xelatex log file:\n";
for (auto line : error_lines) {
ss << " " << line << "\n";
}
ss << "Check log file: " << outbase << ".log";
throw Definition_error(ss.str(), Locator(), false);
}
}
/*
if (std::stoi(machine.m_state.value("K_verbose_level")) < 2) {
for (auto ext : word_split("tex out aux log toc")) {
fs::remove(outbase + "." + ext);
}
}
*/
return "";
}
catch (Error& err) {
std::cout << red;
err.print_message();
std::cout << black << "\n";
exit(1);
}
}

122
sks/document/document.k Normal file
View File

@@ -0,0 +1,122 @@
@@@argtype document_structure |
Structure of a document: plain, article, or book
:pattern plain^|article^|book
@@@
@@document.k
:title
:subtitle
:page_title
:author
:date
:version
:logo
:favicon
#[
:nav.bool false
:toc.bool false
]#
:structure.document_structure plain
:text
:files
:cache.bool true
:css_text
:css_files
:frame_background_color black
:frame_text_color white
:nav_background_color rgb(80%,85%,90%)
:nav_text_color black
:js_text
:js_files
:include_sks_css.bool true
:include_sks_js.bool true
:include_fonts.bool true
:local_fonts.list linux-libertine
:google_fonts.list Inconsolata Open+Sans:wght^@500 Libre+Baskerville
:serif_font Crimson Pro
:sans_font Open Sans
:mono_font Inconsolata
:font_scale 1.0
:landscape.bool.tex false
:paper_size..tex a4paper
:leading.float.tex 1.05
:point_size.int.tex 11
:ragged_right.bool.tex false
:cover..tex
:prolog..tex
:two_column.bool.tex false
:copyright
:bottom
:use_pages_dir.bool.html true
:create_output_directory.bool true
:output_directory_name
:resources_in_file.bool false
: Top-level document structure
@@
@@document :: @eval :cpp *KLAMMERTEXT_HOME*/sks/document/document document @ @@
# @link https://cdn.britannica.com/09/152309-050-5E0B2A42/Sahara-Morocco.jpg :text Niagara @
@@niagara :
@link https://cdn.britannica.com/09/152309-050-5E0B2A42/Sahara-Morocco.jpg :text Niagra @
@@
#[
@@niagara.html :
<a href="https://cdn.britannica.com/09/152309-050-5E0B2A42/Sahara-Morocco.jpg">Niagara</a>
@@
@@niagara.tex :
@link https://cdn.britannica.com/09/152309-050-5E0B2A42/Sahara-Morocco.jpg :text Niagra @
@@
]#
# @@moby : Moby @@
@@moby :
But here is an artist. He desires to paint you the dreamiest,
shadiest, quietest, most enchanting bit of romantic landscape in all
the valley of the Saco. What is the chief element he employs? There
stand his trees, each with a hollow trunk, as if a hermit and a
crucifix were within; and here sleeps his meadow, and there sleep his
cattle; and up from yonder cottage goes a sleepy smoke. Deep into
distant woodlands winds a mazy way, reaching to overlapping spurs of
mountains bathed in their hill-side blue. But though the picture lies
thus tranced, and though this pine-tree shakes down its sighs like
leaves upon this shepherd's head, yet all were vain, unless the
shepherd's eye were fixed upon the magic stream before him. Go visit
the Prairies in June, when for scores on scores of miles you wade
knee-deep among Tiger-lilies---what is the one charm wanting? Water
---there is not a drop of water there! Were @niagara@ but a cataract of
sand, would you travel your thousand miles to see it?
@@
@@smoby.html :
Go visit the Prairies in June, when for scores on scores of miles you
wade knee-deep among Tiger-lilies---what is the one charm wanting?
Water --- there is not a drop of water there! Were @niagara@ but a
cataract of sand, would you travel your thousand miles to see it?
@@
@@smoby.tex :
Go visit the Prairies in June, when for scores on scores of miles you
wade knee-deep among Tiger-lilies---what is the one charm wanting?
Water --- there is not a drop of water there! Were @niagara@ but a
cataract of sand, would you travel your thousand miles to see it?
@@
@@ssmoby :
Go visit the Prairies in June, when for scores on scores of miles you
wade knee-deep among Tiger-lilies---what is the one charm wanting?
Water---there is not a drop of water there!
@@

23
sks/document/document.py Normal file
View File

@@ -0,0 +1,23 @@
import kutil
import html_util
import tex_util
class Document(klammer_base.Klammer_base):
def __init__(self, K):
super().__init__(K)
def html(self):
if self.include_css == "true":
css_code, css_files = html_util.css(self.css, self._sks_enabled)
head = html_util.head(self.title, css_files=css_files, css_code=css_code)
else:
head = head = html_util.head(self.title, include_fonts=self.include_fonts == "true")
return html_util.page(head, self.body)
def tex(self):
ragged_right = self.ragged_right in {"true", "True", "1"}
return tex_util.page(
self.title, self.subtitle, self.leading, self.pointsize, ragged_right, self.body)
def txt(self):
return self.body

View File

@@ -0,0 +1,182 @@
#include "util.h"
#include "machine.h"
#include "error.h"
#include "kutil.h"
#include "html_util.h"
#include "latex_util.h"
#include "locator.h"
// #include "state.h"
#include "document_class.h"
#include "show.h"
#include "log.h"
#include "file.h"
bool strbool(std::string s, Locator loc)
{
std::vector<std::string> values = {"false", "False", "0", "true", "True", "1"};
if (is_not_in(s, values)) {
std::stringstream ss {};
ss << "The value \"" << s << "\" is not a Boolean values. Possible values are:\n"
<< join(values, ", ");
throw Argument_error(ss.str(), loc, false);
}
bool result = (find(values.begin(), values.end(), s) - values.begin()) > 2;
return result;
}
Document_class::Document_class(Machine& machine) : Klammer_base(machine)
{
(void)K::log(3);
// show("document in main SKS");
m_machine = machine;
sloc = get("K_loc");
m_title = get("title");
m_subtitle = get("subtitle");
m_page_title = get("page_title");
m_author = get("author");
m_date = get("date");
// m_nav = strbool(get("nav"), loc);
// m_toc = strbool(get("toc"), loc);
m_structure = docstruct(get("structure"));
m_date = get("date");
m_version = get("version");
m_logo = get("logo");
m_text = get("text");
m_files = word_split(get("files"));
m_css_text = get("css_text");
m_css_filenames = word_split(get("css_files"));
m_include_sks_css = strbool(get("include_sks_css"), loc);
frame_background_color = get("frame_background_color");
frame_text_color = get("frame_text_color");
nav_background_color = get("nav_background_color");
nav_text_color = get("nav_text_color");
js_text = get("js_text");
m_js_filenames = word_split(get("js_files"));
m_include_sks_js = strbool(get("include_sks_js"), loc);
// font_dirs = word_split(get("font_dirs"));
m_local_fonts = word_split(get("local_fonts"));
m_google_fonts = word_split(get("google_fonts"));
auto strip_quotes = [](std::string s) {
if (s.size() >= 2 && s.front() == '"' && s.back() == '"')
s = s.substr(1, s.size() - 2);
return s;
};
m_serif_font = strip_quotes(get("serif_font"));
m_sans_font = strip_quotes(get("sans_font"));
m_mono_font = strip_quotes(get("mono_font"));
m_font_scale = stof(get("font_scale"));
if (!m_serif_font.empty())
m_resolved_serif = resolve_font(m_serif_font);
if (!m_sans_font.empty())
m_resolved_sans = resolve_font(m_sans_font);
if (!m_mono_font.empty())
m_resolved_mono = resolve_font(m_mono_font);
paper_size = get("paper_size");
landscape = strbool(get("landscape"), loc);
leading = stof(get("leading"));
point_size = stoi(get("point_size"));
ragged_right = strbool(get("ragged_right"), loc);
cover = get("cover");
prolog = get("prolog");
m_two_column = strbool(get("two_column"), loc);
m_copyright = get("copyright");
m_bottom = get("bottom");
create_output_directory = strbool(get("create_output_directory"), loc);
output_directory_name = get("output_directory_name");
resources_in_file = strbool(get("resources_in_file"), loc);
//use_pages_dir = strbool(get("use_pages_dir"), loc);
// m_toc_only = strbool(get("K_toc_only"), loc);
m_kt_root_filename = get("K_input_filenames");
m_no_cache = !strbool(get("cache"), loc);
write_files = !strbool(get("K_stdout_only"), loc);
m_input_dir = get("K_input_dir");
if (m_text.size() == 0 and m_files.size() == 0) {
throw Argument_error(
"Neither the :text or :files options have values"); //,
//get("K_loc"));
}
/*
for (std::string file : m_files) {
sources.push_back(string_from_file(find_kt_file(file)));
}
*/
// std::cout << "IN DOCUMENT CLASS:\n" << m_state << "\n";
}
void Document_class::save_string_input_as_file()
{
if (!m_text.empty()) {
std::string input_text_filename = m_cache_dir + "/_text.kt";
// msg() << "Write string input to cache file: "
// << input_text_filename << "\n";
string_to_file(input_text_filename, m_text);
m_files.insert(m_files.begin(), input_text_filename);
}
}
fs::path parse_input_filename(std::string s, std::string input_dir)
{
fs::path p(s);
if (p.extension() != ".kt") {
p += ".kt";
}
if (!file_exists(p)) {
p = input_dir + "/kt/" + p.string();
}
return p;
}
void Document_class::write(std::string filename, std::string contents)
{
write_file(filename, contents, write_files);
}
void write_file(std::string filename, std::string contents, bool write_p)
{
if (write_p) {
string_to_file(filename, contents);
} else {
msg() << "Output filename: " << filename << "\n";
}
}
/*
void output_msg(std::string msg)
{
std::cout << red;
(void)K::log(1, msg);
std::cout << black;
}
void Document_class::create_directory(std::string directory)
{
if (write_files) {
fs::create_directories(directory);
} else {
output_msg("Output directory: " + directory);
}
}
*/

View File

@@ -0,0 +1,184 @@
#pragma once
#include "klammer_base.h"
#include "html_util.h"
#include "latex_util.h"
#include "machine.h"
#include "heading.h"
#include "font_resolve.h"
const std::string closed_symbol { "&^#9656;" };
const std::string open_symbol { "&^#9662;" };
inline std::map<std::string, int> html_tags {
{"kt-part", 0}, {"kt-chapter", 1}, {"h1", 2},
{"h2", 3}, {"h3", 4}, {"h4", 5},
{"h5", 6}, {"h6", 7}, {"h7", 8}};
using string_pairs_t = std::vector<std::pair<std::string,std::string>>;
std::string unescape_newlines(std::string s);
enum class docstruct_t {
plain,
article,
book
};
inline docstruct_t docstruct(std::string name)
{
if (name == "plain") {
return docstruct_t::plain;
} else if (name == "article") {
return docstruct_t::article;
} else {
return docstruct_t::book;
}
}
class Document_class : public Klammer_base {
public:
Document_class(Machine& machine);
~Document_class() = default;
void save_string_input_as_file();
//void create_directory(std::string directory);
void write(std::string filename, std::string contents);
/*
strings_t get_css_files(
std::string output_dir,
std::string css_text, strings_t css_files, bool write_file);
*/
strings_t get_js_files(
bool nav, bool include_sks_js,
std::string pages_basenames_filename); // , std::string output_dir, bool write_file);
argmap m_args {};
void write_basenames_js_file(std::string filename, std::vector<std::string> basenames);
std::string create_html_output_directories();
std::string create_tex_output_directories();
std::pair<std::vector<std::string>, std::vector<std::string>>
html_auxiliary_files(std::string output_directory, std::string pages_basename_filename,
std::vector<std::string> custom_css_files);
void process_input_files();
std::vector<Heading> insert_section_numbers(std::string marker="___NUM___", bool add_to_toc=true);
void insert_html_caption_numbers();
std::vector<std::tuple<std::string, std::string, std::string>> reference_list(std::string target_marker);
std::map<std::string, std::string> id_to_caption_number();
std::map<std::string, std::vector<std::pair<std::string, std::string>>> html_references();
void resolve_html_references();
std::string single_page_toc();
elements_t page(std::string body, std::string output_dir, int max_level);
std::string make_single_html_page(std::string output_directory);
std::string make_html_navigation_structure(
std::string output_directory, std::vector<Heading> headings);
std::string html();
std::string tex();
std::string make_help_page();
std::string color_definitions();
std::string font_definitions();
strings_t resolved_font_names();
Machine m_machine {};
std::string m_title {};
std::string m_subtitle {};
std::string m_page_title {};
std::string m_author {};
std::string m_date {};
std::string m_version {};
std::string m_logo {};
// bool m_nav = false;
// bool m_toc = false;
docstruct_t m_structure = docstruct_t::plain;
std::string m_text {};
strings_t m_files {};
std::string m_output_dir {};
bool include_css = true;
bool include_js = true;
std::string m_css_text {};
strings_t m_css_filenames {};
bool m_include_sks_css = true;
std::string frame_background_color {};
std::string frame_text_color {};
std::string nav_background_color {};
std::string nav_text_color {};
std::string js_text {};
strings_t m_js_filenames {};
bool m_include_sks_js = true;
strings_t m_font_dirs {};
strings_t m_local_fonts {};
strings_t m_google_fonts {};
std::string m_serif_font {};
std::string m_sans_font {};
std::string m_mono_font {};
float m_font_scale = 1.0f;
Resolved_font m_resolved_serif {};
Resolved_font m_resolved_sans {};
Resolved_font m_resolved_mono {};
std::string prolog {};
std::string paper_size {};
bool m_two_column = false;
bool landscape = false;
float leading = 1.0;
int point_size = 10;
bool ragged_right = false;
std::string cover {};
std::string m_copyright {};
std::string m_bottom {};
bool create_output_directory = true;
std::string output_directory_name {};
bool resources_in_file = false;
//bool use_pages_dir = true;
bool m_toc_only = false;
std::string m_kt_root_filename {};
bool m_no_cache = false;
bool write_files = true;
std::string m_input_dir {};
// strings_t sources {};
Locator loc {};
std::string sloc {};
bool m_html_only = false;
std::vector<std::pair<std::string, std::string>> m_file_components {};
std::vector<std::pair<std::string, std::string>> m_string_components {};
std::vector<std::tuple<std::string,std::string,std::string,std::string>> m_toc_components {};
std::string m_cache_dir {};
};
fs::path parse_input_filename(std::string s, std::string input_dir);
void write_file(std::string filename, std::string contents, bool write_files);
/*
std::vector<std::string> combine_files(
std::vector<std::string> filenames, std::string output_filename,
std::string prolog="", std::string epilog="",
bool write_file_p=true,
std::function <std::string(std::string)> processor=nullptr);
*/

View File

@@ -0,0 +1,771 @@
#include "util.h"
#include "error.h"
#include "log.h"
#include "kutil.h"
#include "html_util.h"
#include "heading.h"
#include "document_class.h"
#include "character.h"
#include "reference.h"
#include "show.h"
#include "file.h"
std::string unescape_newlines(std::string s)
{
return string_replace(s, " ___NL___ ", "\n");
}
int ID = 0;
std::string BASE {};
static const std::regex newline_rgx(R"(\n)");
static const std::regex element_open_rgx(R"(<([\w-]+)(\s+.*?)?>)");
static const std::regex id_attr_rgx("id=\"(.*?)\"");
std::string insert_missing_ids(std::smatch match)
{
(void)K::log(3);
std::string result = match[0];
std::string tag = match[1];
if (html::tag_is_block_element(tag)) {
std::string attrs = match[2];
std::stringstream ss {};
if (attrs.find(" id=") == std::string::npos) {
ss << "id=\"_e" << ID++<< "\"";
}
ss << attrs;
if (!BASE.empty()) {
ss << " data-basename=\"" << BASE << "\"";
}
result = "<" + tag + " " + trim(ss.str()) + ">";
}
return result;
}
std::string insert_ids(std::string html_text)
{
(void)K::log(3);
std::string result = html_text;
result = freplace(result, element_open_rgx, insert_missing_ids);
return result;
}
std::string Document_class::make_help_page()
{
(void)K::log(3);
std::string kt_filename = klammertext_filename("doc/html_help.kt");
std::string basename = "help";
std::string help_page;
//if (!in_modification_order(kt_filename, output_filename, no_cache)) {
if (cache_requires_update(m_cache_dir, kt_filename, basename)) {
Machine Mh = m_machine;
Mh.read(fs::path(klammertext_filename("doc/html_help.kt")));
help_page = Mh.apply("html");
help_page += "\n<div class=\"endspace\"></div>\n";
help_page = html::make_paragraphs(help_page);
write_to_cache(m_cache_dir, basename, help_page);
} else {
help_page = read_from_cache(m_cache_dir, basename);
}
return help_page;
}
std::string Document_class::color_definitions()
{
std::stringstream ss {};
ss << ":root {\n"
<< " --frame_background_color: " << frame_background_color << ";\n"
<< " --frame_text_color: " << frame_text_color << ";\n"
<< " --nav_background_color: " << nav_background_color << ";\n"
<< " --nav_text_color: " << nav_text_color << ";\n}\n";
return ss.str();
}
strings_t Document_class::resolved_font_names()
{
strings_t result {};
auto add = [&](const Resolved_font& rf) {
if (!rf.family_name.empty() &&
std::find(m_local_fonts.begin(), m_local_fonts.end(), rf.dir_name) == m_local_fonts.end())
result.push_back(rf.dir_name);
};
add(m_resolved_serif);
add(m_resolved_sans);
add(m_resolved_mono);
return result;
}
std::string Document_class::font_definitions()
{
std::stringstream ss {};
if (!m_serif_font.empty() || !m_sans_font.empty() || !m_mono_font.empty()) {
ss << ":root {\n";
if (!m_serif_font.empty())
ss << " --serif: " << m_serif_font << ", serif;\n";
if (!m_sans_font.empty())
ss << " --sans-serif: " << m_sans_font << ", sans-serif;\n";
if (!m_mono_font.empty())
ss << " --monospace: " << m_mono_font << ", monospace;\n";
ss << "}\n";
}
// Emit scale factors so sans and mono fonts match the serif font.
// Three scaling methods (uncomment the desired one):
// x-height: serif_xh / other_xh (matches lowercase, like fontspec MatchLowercase)
// cap-height: serif_ch / other_ch (matches capitals)
// average: mean(serif_xh,serif_ch) / mean(other_xh,other_ch) (compromise)
float serif_xh = m_resolved_serif.xheight_ratio;
float serif_ch = m_resolved_serif.capheight_ratio;
float serif_avg = (serif_xh + serif_ch) / 2.0f;
if (serif_avg > 0.0f) {
auto scale = [&](const Resolved_font& other) -> std::string {
float other_avg = (other.xheight_ratio + other.capheight_ratio) / 2.0f;
if (other_avg > 0.0f && other_avg != serif_avg) {
char buf[16];
// float ratio = serif_xh / other.xheight_ratio; // x-height
// float ratio = serif_ch / other.capheight_ratio; // cap-height
float ratio = serif_avg / other_avg; // average
std::snprintf(buf, sizeof(buf), "%.4f", ratio);
return buf;
}
return "";
};
std::string sans_scale = scale(m_resolved_sans);
std::string mono_scale = scale(m_resolved_mono);
if (!sans_scale.empty() || !mono_scale.empty()) {
ss << ":root {\n";
if (!sans_scale.empty())
ss << " --sans-serif-scale: " << sans_scale << ";\n";
if (!mono_scale.empty())
ss << " --monospace-scale: " << mono_scale << ";\n";
ss << "}\n";
}
}
// Global font scale: applied to body font-size
if (m_font_scale != 1.0f) {
char buf[16];
std::snprintf(buf, sizeof(buf), "%.4f", m_font_scale);
ss << "body { font-size: " << buf << "rem; }\n";
}
return ss.str();
}
void Document_class::write_basenames_js_file(std::string filename, std::vector<std::string> basenames)
{
(void)K::log(3);
std::string tab(" ");
std::stringstream ss {};
ss << "function pages_basenames()\n{\n return [\n";
int i = 0;
int last = basenames.size() - 1;
for (std::string base : basenames) {
ss << tab << "\"" << base << "\"";
if (i < last) {
ss << ",";
}
ss << "\n";
i++;
}
ss << " ];\n}\n";
write(filename, ss.str());
}
std::string Document_class::create_html_output_directories()
{
(void)K::log(3);
std::string output_directory =
absolute_pathname(get("K_output_dir")) + "/" + get("K_output_basename");
if (!m_toc_only) {
if (!file_exists(output_directory)) {
fs::create_directory(output_directory);
}
html::install_local_fonts(m_font_dirs, m_local_fonts, output_directory);
install_resolved_font(m_resolved_serif, output_directory);
install_resolved_font(m_resolved_sans, output_directory);
install_resolved_font(m_resolved_mono, output_directory);
}
m_cache_dir = cache_directory("_html_pages");
if (m_no_cache && fs::exists(m_cache_dir)) {
for (auto& entry : std::filesystem::directory_iterator(m_cache_dir)) {
msg() << " Removing cache directory: " << entry << "\n";
std::filesystem::remove_all(entry);
}
}
if (!m_toc_only) {
if (!file_exists(output_directory, false, true)) {
fs::create_directory(output_directory);
}
}
return output_directory;
}
strings_t get_css_files(strings_t custom_css_files)
{
(void)K::log(3);
strings_t result = sks_files_of_type("css");
result.insert(result.end(), custom_css_files.begin(), custom_css_files.end());
return result;
}
std::vector<std::string> js_basenames(bool nav)
{
if (nav) {
return { "kutil", "document", "image", "resize_window", "resize_text", "resize_toc",
"toc", "level", "next_last", "help", "show_links", "search", "state", "code", "link" };
} else {
return {"kutil", "document", "image"};
}
}
strings_t Document_class::get_js_files(
bool nav, bool include_sks_js,
std::string pages_basenames_filename)
{
(void)K::log(3);
strings_t result {};
if (include_sks_js) {
for (auto b : js_basenames(nav)) {
std::vector<fs::path> js_files =
find_file_recursive(klammertext_dir() + "/sks", b + ".js");
if (js_files.size() == 0) {
throw File_error("File " + q_(b) + " not found.", Locator());
}
result.push_back(js_files[0].string());
}
if (!pages_basenames_filename.empty()) {
result.push_back(pages_basenames_filename);
}
}
return result;
}
strings_t link_filenames(strings_t& full_filenames, std::string page_dir)
{
strings_t result {};
fs::path page_dir_path(page_dir);
// int i = 0;
std::transform(full_filenames.begin(), full_filenames.end(), std::back_inserter(result),
[&](std::string f) {
fs::path full(f);
//msg() << i << full << " " << full.filename() << "\n";
fs::path path(page_dir_path / full.filename());
//std::cout << i++ << path << "\n";
return path.string(); });
return result;
}
std::pair<std::vector<std::string>, std::vector<std::string>>
Document_class::html_auxiliary_files(std::string output_directory, std::string pages_basenames_filename,
std::vector<std::string> custom_css_files)
{
(void)K::log(3);
strings_t all_css_files = custom_css_files;
strings_t css_link_filenames {};
if (write_files && m_include_sks_css) {
all_css_files = get_css_files(m_css_filenames);
//output_dir, color_definitions(), css_files, write_files);
}
if (write_files) {
copy_preserving_basename(
all_css_files, output_directory, "css");
css_link_filenames = link_filenames(all_css_files, "css");
}
strings_t all_js_files {};
strings_t js_link_filenames {};
if (write_files) {
bool nav = m_structure == docstruct_t::book;
all_js_files = get_js_files(
nav, m_include_sks_js,
pages_basenames_filename); // , output_dir, write_files);
copy_preserving_basename(
all_js_files, output_directory, "js");
js_link_filenames = link_filenames(all_js_files, "js");
}
// msg() << "all_css_files:\n " << join(all_css_files, "\n ") << "\n\n";
// msg() << "all_js_files:\n " << join(all_js_files, "\n ") << "\n\n";
return {css_link_filenames, js_link_filenames};
}
void Document_class::process_input_files()
{
(void)K::log(3);
m_file_components.clear();
size_t basename_width = 0;
for (auto input_filename : m_files) {
fs::path inpath = input_filename;
if (!file_exists(inpath)) {
inpath = parse_input_filename(input_filename, m_input_dir);
}
std::string basename = file_basename(input_filename);
basename_width = std::max(basename_width, basename.size());
std::string html_text;
//if (true || !in_modification_order(inpath, cached_filename, m_no_cache)) {
if (m_no_cache || cache_requires_update(m_cache_dir, inpath, basename)) {
Machine M = m_machine;
M.read(inpath);
html_text = trim(M.apply("html"));
html_text = trim(html::make_paragraphs(html_text)) + "\n";
html_text = unescape_newlines(html_text);
BASE = basename;
html_text = insert_ids(html_text);
m_file_components.push_back({basename, html_text});
write_to_cache(m_cache_dir, basename, html_text);
} else {
html_text = read_from_cache(m_cache_dir, basename);
m_file_components.push_back({basename, html_text});
}
}
// for (auto [src, result] : m_file_components) {
// msg() << std::left << std::setw(basename_width) << src << " " << abbrev(result, 96) << "\n";
// }
}
std::string increment_level(int level, std::vector<int> &levels)
{
levels[level]++;
int zero_offset = level == 0 ? 2 : 1;
for (unsigned int i = level + zero_offset; i < levels.size(); i++) {
levels[i] = 0;
}
std::stringstream result {};
if (level == 0) {
result << levels[0];
} else { // Parts not included in numbering
for (auto i = 1; i <= level; i++) {
result << levels[i];
if (i < level) {
result << ".";
}
}
}
// msg() << level << sp_arrow << result.str() << sp_arrow << "\n";
return result.str();
}
std::string extract_id(std::string attr)
{
std::smatch match {};
if (!std::regex_match(attr, match, id_attr_rgx)) {
throw Internal_error("Pattern for id attribute incorrect: " + q_(attr));
}
return match[1];
}
std::vector<Heading>
Document_class::insert_section_numbers(std::string marker, bool add_to_toc)
{
std::vector<int> levels(9, 0);
std::smatch match {};
std::string pattern = R"(<([\w-]+)\s*(.*?)>(.*?)MARKER\s*</span>\s*(.*?)<.*)";
pattern = string_replace(pattern, "MARKER", marker);
std::regex heading_rgx(pattern);
std::vector<std::pair<std::string, std::string>> modified_components {};
std::vector<Heading> headings;
bool include_toc = m_structure != docstruct_t::plain;
for (auto [src, result] : m_file_components) {
std::stringstream modified {};
for (std::string line : regex_split(result, newline_rgx, false)) {
if (std::regex_match(line, match, heading_rgx)) {
// xheading
std::string tag = match[1];
std::string id = extract_id(match[2]);
std::string title = match[3];
std::string heading = match[4];
const int level = html_tags[match[1]];
// msg() << line << sp_arrow << broken_bar << tag << broken_bar
// << level << broken_bar << "ATTR" << broken_bar << title << broken_bar
// << heading << broken_bar << "\n";
std::string number = increment_level(level, levels);
line = string_replace(line, marker, number);
if (include_toc) {
line = "<a href=\"#_c" + id + "\" draggable=\"false\">" + line + "</a>";
}
//msg() << attr << " " << number << " " << heading << "\n";
if (add_to_toc) {
m_toc_components.push_back({tag, id, number, heading});
}
Heading hd(level, src, number, id, number, heading);
headings.push_back(hd);
// msg() << line << "\n\n";
}
modified << line << "\n";
}
modified_components.push_back({src, modified.str()});
}
m_file_components = modified_components;
return headings;
}
int get_caption_number(std::map<std::string, int>& numbers, std::string label)
{
int result;
if (numbers.count(label) == 0) {
result = 1;
numbers[label] = 2;
} else {
result = numbers[label];
numbers[label]++;
}
return result;
}
void Document_class::insert_html_caption_numbers()
{
// msg() << "insert_html_caption_numbers\n";
std::regex chapter_rgx(R"(.*sectionnumber">(\d+)</span>.*)");
std::regex caption_rgx(R"(__CAPTION__(.*?)__CAPTION__(.*?)__CAPTION__)");
std::string current_chapter {};
std::map<std::string, int> current_caption_number {};
std::vector<std::pair<std::string, std::string>> modified_components {};
for (auto [src, result] : m_file_components) {
std::stringstream modified {};
//std::vector<std::string> references {};
for (std::string line : regex_split(result, newline_rgx, false)) {
std::smatch chapter_match {};
if (std::regex_match(line, chapter_match, chapter_rgx)) {
current_chapter = chapter_match[1];
// msg() << "Chapter " << current_chapter << " " << line << "\n";
current_caption_number.clear();
}
// msg() << "Line: " << line << "\n";
std::smatch caption_match {};
if (std::regex_search(line, caption_match, caption_rgx)) {
std::string label = caption_match[1];
std::string ctext = caption_match[2];
int caption_number = get_caption_number(current_caption_number, label);
// msg() << " caption: " << current_chapter << "." << caption_number << "\n";
// msg() << "Label: " << label << " Text: " << ctext << "\n";
std::stringstream ss {};
ss << label << " ";
if (!current_chapter.empty()) {
ss << current_chapter << ".";
}
ss << caption_number << ctext;
line = std::regex_replace(line, caption_rgx, trim(ss.str()));
// msg() << "line: " << line << "\n";
// references.push_back(line);
}
// if (std::regex_search(line, reference_rgx)) {
// references.push_back(line);
// }
modified << line << "\n";
}
modified_components.push_back({src, modified.str()});
//m_reference_sequences.push_back(references);
}
m_file_components = modified_components;
}
std::vector<std::tuple<std::string, std::string, std::string>>
Document_class::reference_list(std::string target_marker)
{
std::regex reference_rgx("__REF__");
std::regex target_rgx("<div id=\"(.*?)\" data-label=\"(.*?)\" class=\"caption_");
std::vector<std::tuple<std::string, std::string, std::string>> references {};
for (auto [src, result] : m_file_components) {
for (std::string line : regex_split(result, newline_rgx, false)) {
if (std::regex_search(line, reference_rgx)) {
references.push_back({target_marker, src, line});
} else {
std::smatch match {};
if (std::regex_search(line, match, target_rgx)) {
references.push_back({"", match[2], match[1]});
}
}
}
}
return references;
}
std::map<std::string, std::string> Document_class::id_to_caption_number()
{
std::regex target_rgx("<div id=\"(.*?)\" data-label=\"(.*?)\" class=\"caption_");
std::regex caption_rgx(R"(<div id=.*?>(\w+\s+[\w.]+))");
std::map<std::string, std::string> result {};
for (auto [src, ftext] : m_file_components) {
auto lines = regex_split(ftext, newline_rgx, false);
for (unsigned int i = 0; i < lines.size(); i++) {
std::smatch match {};
if (std::regex_search(lines[i], match, target_rgx)) {
std::string id = match[1];
unsigned int j = i + 1;
std::smatch caption_match {};
while (j < lines.size()) {
if (std::regex_search(lines[j], caption_match, caption_rgx)) {
std::string caption = caption_match[1];
// msg() << id << sp_arrow << caption << "\n";
result[id] = caption;
break;
}
j++;
}
}
}
}
return result;
}
std::pair<int, int> offset_spec_to_count(std::string spec)
{
std::regex rgx(R"((\w+)\s*(\d*))");
std::smatch match {};
int offset = 1;
int dir = 1;
if (std::regex_match(spec, match, rgx)) {
std::string desc = match[1];
std::string value = match[2];
if (!value.empty()) {
offset = std::stoi(value);
}
if (desc == "before") {
dir = -1;
}
} else {
throw Internal_error("Reference offset spec incorrect: " + spec);
}
return {offset, dir};
}
std::map<std::string, std::vector<std::pair<std::string, std::string>>>
Document_class::html_references()
{
std::string target_marker = "@";
auto references = reference_list(target_marker);
// msg() << "\n";
for (unsigned int i = 0; i < references.size(); i++) {
auto [mark, name, ref] = references[i];
// msg() << " " << i << ": " << mark << " " << name << " " << ref << "\n";
}
// msg() << "\n";
auto id_captions = id_to_caption_number();
std::regex ref_rgx(R"(__REF__(.*?)__(.*?)__)");
std::string offset_spec;
std::string type;
std::map<std::string, std::vector<std::pair<std::string, std::string>>> result {};
for (unsigned int i = 0; i < references.size(); i++) {
auto [mark, name, ref] = references[i];
// msg() << " " << i << ": " << mark << " " << name << " " << ref << "\n";
if (mark == target_marker) {
std::smatch match {};
if (std::regex_search(ref, match, ref_rgx)) {
offset_spec = match[1];
type = match[2];
} else {
throw Internal_error("Reference form is incorrect: " + ref);
}
auto [offset, dir] = offset_spec_to_count(offset_spec);
int j = i + dir;
while (offset > 0 && j >= 0 && j < (int)references.size()) {
// msg() << " check: " << std::get<2>(references[j]) << "\n";
if (std::get<1>(references[j]) == type) {
offset--;
}
if (offset == 0) {
break;
}
j += dir;
}
if (j < 0 || j >= (int)references.size()) {
// msg() << "Not found: " << std::get<2>(references[i]) << "\n";
} else {
std::string id = std::get<2>(references[j]);
std::string line = std::get<2>(references[i]);
// msg() << "Ref: " << line << boldblack << " found: " << black << id << "\n";
std::string link = "<a href=\"#" + id + "\">" + id_captions[id] + "</a>";
// msg() << link << "\n";
std::string modified_line = std::regex_replace(line, ref_rgx, link);
// msg() << "modified_line: " << name << sp_arrow << modified_line << "\n";
result[name].push_back({line, modified_line});
}
}
}
return result;
}
void Document_class::resolve_html_references()
{
auto references = html_references();
std::vector<std::pair<std::string, std::string>> modified_components {};
for (auto [src, ftext] : m_file_components) {
// msg() << "resolve " << src << ":\n";
std::string modified_text = ftext;
for (auto [old_line, new_line] : references[src]) {
// msg() << old_line << sp_arrow << new_line << "\n";
modified_text = string_replace(modified_text, old_line, new_line);
}
modified_components.push_back({src, modified_text});
}
m_file_components = modified_components;
}
std::string Document_class::single_page_toc()
{
// <h1 id="_e9"><span class="sectionnumber">2.1</span> Section two.one</h1>
std::regex id_rgx("id=\"(.*?)\"");
std::stringstream ss {};
ss << "<div class=\"toctitle\">Contents</div>\n";
int minimum_depth = 7;
// Adjust left minimum margin based on whether parts or chapters are the top level:
for (auto [tag, id, number, heading] : m_toc_components) {
minimum_depth = std::min(minimum_depth, html_tags[tag]);
}
for (auto [tag, id, number, heading] : m_toc_components) {
int depth = html_tags[tag];
// msg() << "TOC: " << depth << " " << id << " " << number << " " << heading << "\n";
ss << "<div id=\"_c" << id << "\" class=\"tab" << depth - minimum_depth << " toc\"><a href=\"#" << id << "\">"
<< "<span class=\"tocnumber\">" << number << "</span>" << heading << "</a></div>\n";
}
// msg() << ss.str();
return ss.str();
}
elements_t Document_class::page(std::string body_text, std::string output_dir, int max_level)
{
(void)K::log(3);
std::string toc {}; // Calculate
bool text_only = m_structure == docstruct_t::plain;
// msg() << "output_dir: " << output_dir << "\n";
auto [css_filenames, js_filenames] =
html_auxiliary_files(output_dir, "", m_css_filenames);
std::string toc_text {};
elements_t body {};
html::add_title(body, m_title, m_logo);
if (m_structure == docstruct_t::book) {
html::add_nav(body, max_level);
}
html::add_text(body, body_text, toc_text, text_only);
// html::add_bottom_spacer(body);
html::add_status_bar(body, m_date, m_version, m_copyright);
if (!text_only) {
html::add_page_cache(body);
}
html::add_js_links(body, js_filenames, output_dir) ;
std::string css_text = color_definitions() + font_definitions() + "\n" + m_css_text;
std::string page_title = m_page_title.empty() ? m_title : m_page_title;
strings_t all_local_fonts = m_local_fonts;
for (auto& name : resolved_font_names())
all_local_fonts.push_back(name);
elements_t page = html::make_page(
body, page_title,
css_text, css_filenames, all_local_fonts, m_google_fonts);
return page;
}
std::string Document_class::make_single_html_page(std::string output_directory)
{
(void)K::log(3);
std::stringstream ss {};;
if (m_structure != docstruct_t::plain) {
ss << single_page_toc();
}
for (auto [basename, page_text] : m_file_components) {
ss << "<!-- " << basename << ".kt -->\n\n" << page_text << "\n";
}
std::string single_page = to_string(page(ss.str(), output_directory, 10));
std::string output_file = output_directory + "/index.html";
// msg() << "single_page to " << output_file << "\n";
string_to_file(output_file, single_page);
return "";
}
std::string Document_class::make_html_navigation_structure(
std::string output_directory, std::vector<Heading> headings)
{
msg() << "Navigation format\n";
std::vector<std::string> pages_basenames {};
for (auto [basename, page_text] : m_file_components) {
pages_basenames.push_back(basename);
msg() << basename << ": " << abbrev(page_text, 96) << "\n";
}
std::string pages_basenames_filename = m_cache_dir + "/_pages_basenames.js";
write_basenames_js_file(pages_basenames_filename, pages_basenames);
std::string help_content = make_help_page();
// Build embedded pagecache to avoid Same Origin Policy errors with file:// protocol
std::stringstream pagecache;
pagecache << "<div id=\"pagecache\" style=\"display:none\">\n";
for (auto [basename, page_text] : m_file_components) {
pagecache << "<div id=\"_page_" << basename
<< "\" class=\"content_page\">"
<< page_text << "</div>\n";
}
pagecache << "<div id=\"help_page\">" << help_content << "</div>\n";
pagecache << "<div id=\"search_page\"></div>\n";
pagecache << "</div>\n";
std::pair<int, std::string> toc_spec = make_navigation_table_of_contents(headings);
int max_level = std::get<0>(toc_spec);
std::string toc_body = std::get<1>(toc_spec);
std::string nav_body = "Navigation";
auto [all_css_files, all_js_files] =
html_auxiliary_files(output_directory, pages_basenames_filename, m_css_filenames);
std::string css_text = color_definitions() + font_definitions() + "\n" + m_css_text;
std::stringstream t {};
t << html::page(
m_title, m_page_title, output_directory, nav_body, max_level, toc_body,
m_file_components[0].second,
m_date, m_version, m_copyright,
css_text, all_css_files, all_js_files,
[&]() { strings_t f = m_local_fonts;
for (auto& n : resolved_font_names()) f.push_back(n);
return f; }(),
m_google_fonts,
m_logo);
std::string result = t.str();
result = string_replace(result,
"<div id=\"pagecache\"></div>",
pagecache.str());
std::string output_basename = m_machine.m_state.value("K_output_basename");
std::string output_filename = output_directory + "/index.html";
write(output_filename, result);
return "";
}
std::string Document_class::html()
{
(void)K::log(3);
// msg() << "HTML document\n";
// m_cache_dir = cache_directory(kt_root_filename, "_html_pages");
std::string output_directory = create_html_output_directories();
save_string_input_as_file();
process_input_files();
auto headings = insert_section_numbers();
insert_section_numbers("___NUM2___", false);
insert_html_caption_numbers();
resolve_html_references();
if (m_structure != docstruct_t::book) {
make_single_html_page(output_directory);
} else {
make_html_navigation_structure(output_directory, headings);
}
return "";
}

View File

@@ -0,0 +1,85 @@
#include "util.h"
#include "kutil.h"
#include "log.h"
#include "document_class.h"
#include "reference.h"
#include "file.h"
#include "show.h"
std::string Document_class::create_tex_output_directories()
{
(void)K::log(3);
std::string output_directory =
absolute_pathname(get("K_output_dir")); // + "/" + get("K_output_basename");
m_cache_dir = cache_directory("_latex_files");
if (!m_toc_only) {
if (!file_exists(output_directory, false, true)) {
fs::create_directory(output_directory);
}
}
return output_directory;
}
std::string Document_class::tex()
{
(void)K::log(3);
create_tex_output_directories();
// :text content is already processed (klammers applied by the outer Machine).
// Use it directly; don't re-process through a sub-Machine.
std::string latex_text {};
if (!m_text.empty()) {
latex_text = add_latex_caption_numbers(m_text);
}
if (m_files.size() > 0) {
for (auto input_filename : m_files) {
std::string basename = file_basename(input_filename);
std::string cached_filename = m_cache_dir + "/" + basename;
fs::path inpath = parse_input_filename(input_filename, m_input_dir);
// if (!in_modification_order(input_filename, cached_filename, m_no_cache)) {
if (m_no_cache || cache_requires_update(m_cache_dir, inpath, basename)) {
Machine M = m_machine;
M.m_state.set("document_landscape", landscape ? "true" : "false"); // , "Landscape mode");
M.read(inpath);
std::string processed = trim(M.apply("tex", false));
processed = add_latex_caption_numbers(processed);
write_to_cache(m_cache_dir, basename, processed);
latex_text += processed;
} else {
latex_text += read_from_cache(m_cache_dir, basename);
}
}
}
auto split_lines = regex_split(latex_text, std::regex(R"(\n)"), false);
latex_text = resolve_caption_references("latex", split_lines, latex_line_states(split_lines));
std::map<std::string, std::string> section_to_id {};
std::regex section_id_rgx(R"(%__sectionlink__(.*?)__(.*?)__)");
for (std::string id_section : find_all(latex_text, section_id_rgx, 0)) {
std::smatch match {};
std::regex_match(id_section, match, section_id_rgx);
section_to_id[match[2]] = match[1];
}
latex_text = std::regex_replace(latex_text, section_id_rgx, "");
std::regex seclink_rgx("(.*?)__seclink__(.*?)__(.*)");
auto lines = regex_split(latex_text, std::regex(R"(\n)"), false);
for (auto i = 0ul; i < lines.size(); i++) {
if (lines[i].find("__seclink__") != std::string::npos) {
std::smatch match {};
std::regex_match(lines[i], match, seclink_rgx);
std::stringstream ss {};
ss << match[1] << section_to_id[match[2]] << match[3];
lines[i] = ss.str();
}
}
latex_text = join(lines, "\n");
latex_text = string_replace(latex_text, "___AT___", "^@");
std::string structure_name =
m_structure == docstruct_t::book ? "book" :
m_structure == docstruct_t::article ? "article" : "plain";
return latex::page(structure_name,
m_title, m_subtitle, m_author, m_date, m_version, m_copyright,
m_bottom, prolog, paper_size, m_two_column, leading,
point_size, ragged_right,
cover, landscape, latex_text,
m_resolved_serif, m_resolved_sans, m_resolved_mono,
m_font_scale);
}

View File

@@ -0,0 +1,21 @@
@font-face {
font-family: 'Linux Libertine';
font-style: normal;
font-weight: 400;
src: url('linux-libertine/LinLibertine_Rah.ttf') format('truetype');
}
@font-face {
font-family: 'Linux Libertine';
font-style: italic;
font-weight: 400;
src: url('linux-libertine/LinLibertine_RIah.ttf') format('truetype');
}
@font-face {
font-family: 'Linux Libertine';
font-style: normal;
font-weight: bold;
src: url('linux-libertine/LinLibertine_RBah.ttf') format('truetype');
}

View File

@@ -0,0 +1,343 @@
GNU GENERAL PUBLIC LICENSE (with font exception)
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@@ -0,0 +1,7 @@
- Lizenz / Licence -
Unsere Schriften sind frei im Sinne der GPL, d.h. (stark vereinfacht) dass Veränderungen an der Schriftart erlaubt sind unter der Bedingung, dass diese wieder der Öffentlichkeit unter gleicher Lizenz freigegeben werden. Querdenker behaupten oft, dass bei der Verwendung einer GPL-Schrift eingebettet in beispielsweise eine PDF auch diese freigestellt werden müsse. Deshalb gibt es die sogenannte "Font-exception" der GPL (welche diesem Lizenztext hinzugefügt wurde). Weitere Informationen zur GPL (Lizenztext mit Font-Exzeption als GPL.txt in diesem Paket).
Zusätzlich stehen die Schriften unter der Open Font License (siehe OFL.txt).
Our fonts are free in the sense of the GPL. In short: Changing the font is allowed as long as the derivative work is published under the same licence again. Pedantics keep claiming that the embedded use of GPL-fonts in i.e. PDFs requires the free publication of the PDF as well. This is why our GPL contains the so called "font exception". Further information about the GPL (licence text with font exception see GPL.txt in this package).
Additionally our fonts are licensed under the Open Fonts License (see OFL.txt).

View File

@@ -0,0 +1,94 @@
Copyright (c) 20032012, Philipp H. Poll (www.linuxlibertine.org | gillian at linuxlibertine.org),
with Reserved Font Name "Linux Libertine" and "Biolinum".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

320
sks/document/heading.cpp Normal file
View File

@@ -0,0 +1,320 @@
#include <regex>
#include "util.h"
#include "file.h"
#include "html_util.h"
#include "heading.h"
#include "log.h"
#include "show.h"
std::ostream& operator<<(std::ostream& os, const Heading h)
{
os << h.m_level << " "
<< h.m_filename << " "
<< h.m_section << " "
<< h.m_id << " "
<< h.m_number << " "
<< h.m_title;
return os;
}
std::string levels_to_section(std::vector<unsigned int> levels)
{
(void)K::log(3);
std::string result {};
auto count = levels.size();
for (unsigned int i = 0; i < count; i++) {
if (levels[i] == 0)
break;
result += std::to_string(levels[i]);
if (i < count - 1)
result += ".";
}
if (count > 1)
result = result.substr(0, result.size()-1);
return result;
}
int part_number = 1;
int unnumbered_id = 0;
/*
std::tuple<std::string, std::vector<Heading>, std::vector<unsigned int>, unsigned int>
add_section_numbers(std::string s, std::string basename, std::vector<unsigned int> levels, unsigned int initial_id,
std::map<std::string, std::string>& section_id_map)
{
(void)K::log(3);
auto depth { levels.size() };
std::vector<Heading> headings {};
std::string text { s };
std::regex section_rgx (R"((.*?)<h(\d)(.*?)>(.*?)</h\2>)");
std::regex part_rgx (R"((.*?)<kt-part(.*?)>Part (\d+)\s*<br>\s*(.*?)\s*</kt-part>)");
std::regex id_rgx(R"((.*?)id=\"([-\w]+)\"(.*))");
unsigned int id_number { initial_id };
//std::string result {};
std::stringstream result {};
for (std::string line : regex_split(s, std::regex(R"(\n)"), false)) {
std::smatch match {};
if (std::regex_match(line, match, part_rgx)) {
std::string pre { match[1] };
std::string attr { match[2] };
std::string level { match[3] };
std::string title { match[4] };
std::string id_prefix { "_part_"};
std::string id {};
std::smatch id_match {};
if (std::regex_match(attr, id_match, id_rgx)) {
id = id_match[2];
} else {
id = id_prefix + std::to_string(part_number);
}
//title = "Part " + std::to_string(part_number) + " - " + title;
//std::string section = "Part " + level;
std::string section = "Part " + std::to_string(part_number);
elements_t part_title
{ html::elt("kt-part",
{ html::elt("span", section).attr("class", "sectionnumber"),
html::elt("span", trim(title)).attr("class", "sectiontitle") })
.attr("id", id)
.attr("data-level", level) };
result << pre << part_title << "\n";
headings.push_back(Heading(0, basename, section, id, "0", title));
part_number += 1;
} else if (std::regex_match(line, match, section_rgx)) {
std::string pre { match[1] };
int level { std::stoi(match[2]) };
std::string attr { match[3] };
std::string title { match[4] };
bool numbered = attr.find("numbered") != std::string::npos;
std::string section {};
if (numbered) {
levels[level-1] = levels[level-1] + 1;
for (unsigned int li = level; li < depth; li++)
levels[li] = 0;
section = levels_to_section(levels);
}
std::string id {};
std::smatch id_match {};
if (std::regex_match(attr, id_match, id_rgx)) {
id = id_match[2];
} else {
if (section.size() == 0) {
id = "_su_" + std::to_string(unnumbered_id++);
} else {
id = "_s_" + string_replace(section, ".", "_");
}
}
std::string link_target = file_basename(basename) + link_delimiter + id;
section_id_map[title] = link_target;
//<h1 id="image-tests" style="clear:both;" class="headerlink">
// <span class="sectionnumber">1</span>Image tests</h1>
elements_t title_parts {};
if (numbered) {
title_parts.push_back(html::elt("span", trim(section)).attr("class", "sectionnumber"));
}
title_parts.push_back(html::elt("span", trim(title)).attr("class", "sectiontitle"));
// msg() << "title_parts: " << title_parts << "\n";
elements_t section_title
{ html::elt("h"+std::to_string(level), title_parts)
.attr("id", id)
.attr("data-level", std::to_string(level))};
// msg() << pre << section_title << "\n";
result << pre << section_title << "\n";
headings.push_back(Heading(level, basename, section, id, "0", title));
} else {
result << line << "\n";
}
}
//std::cout << "add_section_numbers: " << result.str() << " "
//<< headings.size() << " " << levels.size() << " " << id_number << "\n";
return std::tuple(result.str(), headings, levels, id_number);
}
*/
std::string make_html_table_of_contents(std::vector<Heading> headings)
{
elements_t toc { html::elt("h1", "Contents") };
for (auto [level, filename, section, id, number, title] : headings) {
toc.push_back(
html::elt("div",
html::elt("a", {
html::elt("span", section).attr("class", "level_number"),
title })
.attr("href", "^#" + id)
.attr("class", "level"))
.attr("class", "level" + std::to_string(level)));
}
//auto toc_elt = html::elt("div", toc).attr("id", "toc");
std::string result { "<div id=\"pagetoc\">\n" + to_string(toc) + "\n</div>\n" };
return result;
}
std::string toc_link_attrs(
std::string section, std::string title, std::string filename, std::string target, int level)
{
std::string basename = file_basename(filename);
std::stringstream ss {};
ss << "class=\"navlink\" data-page=\"" << basename
<< "\" data-target=\"" << target << "\">\n<span class=\"section-title\" level=\"" << level << "\">\n";
if (section.size() > 0) {
ss << "<span class=\"section-number\">" << section << "</span>\n";
}
ss << "<span class=\"section-title-text\">" << title << "</span>\n"
<< "</span>\n";
return ss.str();
}
std::pair<int, std::string>
make_navigation_table_of_contents(std::vector<Heading> headings)
{
std::stringstream toc {};
long unsigned int i = 1;
//int max_level = 0;
//int last_level = 0;
int max_level = 1;
int last_level = 1;
toc << "<ul id=\"toc_items\">\n";
int min_level = 7;
for (auto h : headings) {
min_level = std::min(h.m_level, min_level);
}
bool unnumbered_start = headings.size() > 0 ? headings[0].m_section.empty() : false;
if (unnumbered_start) { // Unnumbered first section (preface, for example)
toc << "<ul>\n";
}
for (auto [item_level, filename, section, id, number, title] : headings) {
// msg() << " level: " << item_level << "; filename: " << filename << "; section: " << section
// << "; id: " << id << "; number: " << number << "; title: " << title << "\n";
bool promote_unnumbered = unnumbered_start && min_level == 0 and section.empty();
if (promote_unnumbered) {
item_level--;
}
std::string basename = file_basename(filename);
max_level = std::max(max_level, item_level);
//int next_level = i < headings.size() ? headings[i].m_level : 0;
int next_level = i < headings.size() ? headings[i].m_level : 1;
if (promote_unnumbered) {
next_level = std::max(0, next_level - 1);
}
// std::cout << "|" << item_level << "|" << section << "|" << id << "|"
// << title << "| " << "next: " << next_level << "\n";
std::string link = toc_link_attrs(section, title, basename, id, item_level);
if (next_level == item_level) {
toc << "<li " << link << "</li>\n";
/*
} else if (next_level > item_level) {
toc << "<li><span class=\"caret\" data-level=\"" << item_level << "\">"
<< "<span " << link<< "</span></span>\n"
<< "<ul class=\"nested\">\n";
*/
} else if (next_level > item_level) {
toc << "<li><span class=\"caret\" data-level=\"" << item_level << "\"></span>\n"
<< "<span " << link<< "</span>\n"
<< "<ul class=\"nested";
/*
if (section.size() == 0) {
toc << " unnumbered";
}
*/
toc << "\">\n";
} else {
//toc << "<li " << link << "</li>\n</ul>\n</li>\n";
toc << "<li " << link << "</li>\n";
/*
if (unnumbered_start) {
next_level += 1;
}
*/
int offset = unnumbered_start ? 1 : 0;
//int offset = unnumbered_start ? 3 : 2;
offset = 0;
for (int lev = item_level; lev > next_level + offset; lev--) {
toc << "</ul>\n</li>\n";
}
unnumbered_start = false;
}
i++;
last_level = item_level;
}
toc << "</ul>\n";
for (int j = 0; j < last_level - 2; j++) {
toc << "</li>\n</ul>\n";
}
return {max_level, toc.str()};
}
void show_table_of_contents(
std::string kt_root_filename, std::string title, std::vector<Heading> headings)
{
long unsigned int width = kt_root_filename.size();
for (Heading h : headings) {
width = std::max(width, h.m_filename.size());
}
width += 2;
int tab = 2;
std::string current_filename = "";
std::string bar = " | ";
std::cout << "\n" << std::string(width - kt_root_filename.size(), ' ')
<< kt_root_filename << bar << title << "\n";
for (auto [level, filename, section, id, number, htitle] : headings) {
std::string f = "";
if (filename != current_filename) {
f = std::string(width - filename.size(), ' ') + filename + bar;
current_filename = filename;
} else {
f = std::string(width, ' ') + bar;
}
std::cout << f << std::string((level-1) * tab, ' ') << section << " " << htitle << "\n";
}
std::cout << "\n";
}
std::string cache_table_of_contents(
std::string kt_root_filename, std::string title, std::vector<Heading> headings)
{
long unsigned int width = kt_root_filename.size();
for (Heading h : headings) {
width = std::max(width, h.m_filename.size());
}
width += 2;
int tab = 2;
std::string current_filename = "";
std::string bar = " | ";
std::stringstream ss {};
ss << "\n" << std::string(width - kt_root_filename.size(), ' ')
<< kt_root_filename << bar << title << "\n";
for (auto [level, filename, section, id, number, htitle] : headings) {
std::string f = "";
if (filename != current_filename) {
f = std::string(width - filename.size(), ' ') + filename + bar;
current_filename = filename;
} else {
f = std::string(width, ' ') + bar;
}
ss << f << std::string((level) * tab, ' ');
if (section.size() > 0) {
ss << section << " ";
}
ss << htitle << "\n";
}
ss << "\n";
std::string cache_filename =
cache_directory(kt_root_filename, "_html_pages") + "/toc.txt";
string_to_file(cache_filename, ss.str());
return cache_filename;
}

41
sks/document/heading.h Normal file
View File

@@ -0,0 +1,41 @@
#pragma once
#include <string>
#include <vector>
#include <iostream>
const std::string link_delimiter { "%" };
class Heading {
public:
Heading(int level, std::string filename, std::string section, std::string id,
std::string number, std::string title)
: m_level(level), m_filename(filename), m_section(section), m_id(id),
m_number(number), m_title(title)
{};
int m_level {};
std::string m_filename {};
std::string m_section {};
std::string m_id {};
std::string m_number {};
std::string m_title {};
};
std::ostream& operator<<(std::ostream& os, const Heading h);
/*
std::tuple<std::string, std::vector<Heading>, std::vector<unsigned int>, unsigned int>
add_section_numbers(std::string s, std::string basename, std::vector<unsigned int> levels, unsigned int initial_id,
std::map<std::string, std::string>& section_id_map);
*/
std::string make_html_table_of_contents(std::vector<Heading> headings);
std::pair<int, std::string>
make_navigation_table_of_contents(std::vector<Heading> headings);
void show_table_of_contents(
std::string kt_filename, std::string title, std::vector<Heading> headings);
std::string cache_table_of_contents(
std::string kt_root_filename, std::string title, std::vector<Heading> headings);

View File

@@ -0,0 +1,45 @@
function show_navigation_items(show_item) {
show_item = show_item === undefined ? true : false;
K.getv(".depthchoice").forEach(function (o) {
//K.visible(o, "hide");
K.get(o).hidden = !show_item;
});
["textshow", "next", "previous", "fit", "linkshow"].forEach(function (o) {
K.get("#"+o).hidden = !show_item;
});
}
function get_height(name) {
let div = K.get(name);
let result = 0;
if (div !== null) {
result = K.height(div);
}
return result;
}
function frame_resize() {
let nav = K.get("#nav");
let nav_height = (nav ? get_height(nav) : 0);
let content_height = window.innerHeight
- get_height("#title") - nav_height - get_height("#status") + 1;
K.height("#middle", content_height);
}
window.addEventListener("load", function (event) {
window.addEventListener("resize", function (e) {
frame_resize();
});
frame_resize();
if (typeof resize_toc === "function") resize_toc(true);
//let endspace_height = "calc(110% - " + K.height("#title")
// + " - " + K.height("#nav") + ")";
//K.style(".endspace", "height", endspace_height);
});

33
sks/document/js/help.js Normal file
View File

@@ -0,0 +1,33 @@
function modify_buttons_for_help(status) {
K.visible("#linkshow", status);
K.visible("#textshow", status);
K.visible("#searchtools", status);
K.get("#help").textContent = status == "show" ? "Help" : "Back";
}
function show_help() {
let header = K.get("#text h1");
if (header && header.textContent === "Navigation features") {
textshow();
modify_buttons_for_help("show");
frame_resize();
} else {
save_position();
K.get("#text").replaceWith(K.get("#help_page").cloneNode(true));
K.attr("#help_page", "id", "text");
K.visible("#toc", "hide");
K.visible("#resizer", "hide");
set_buttons("hide");
resize_text_width();
modify_buttons_for_help("hide");
frame_resize();
}
}
window.addEventListener("load", function (event) {
K.get("#help").addEventListener("click", function (e) {
event.preventDefault();
show_help();
});
define_keypress("H", "#help");
});

51
sks/document/js/level.js Normal file
View File

@@ -0,0 +1,51 @@
/* Open up the table of contents to a specified level in the hierarchy: */
/*
function show_all() {
let level_buttons = K.getv(".depthchoice");
level_buttons[level_buttons.length-1].click();
}
function show_highlighted(node) {
//let carets = K.getv(".caret");
let node_caret = node.parentNode.previousElementSibling;
console.log(node_caret);
}
*/
window.addEventListener("load", function (event) {
K.map(".depthchoice", function (depthchoice) {
depthchoice.addEventListener("click", function (event) {
//save_position();
let max_depth = K.text(event.srcElement);
K.map(".caret", function (e) {
let level = K.attr(e, "data-level");
let open = K.has_class(e, "caret-down");
if (level < max_depth) {
if (!open) {
e.click();
}
} else {
if (open) {
e.click();
}
}
});
//restore_position();
});
});
K.get("html").addEventListener("keypress", function (event) {
const max_depth = K.getv(".depthchoice").length;
const zero = 48; // Hah!
let depth = 1;
for (depth = 1; depth <= max_depth; depth += 1) {
if (event.which === zero + depth) {
K.get('.depthchoice[data-depth="' + depth + '"]').click();
}
}
});
highlight_headers_in_toc();
});

12
sks/document/js/list.txt Normal file
View File

@@ -0,0 +1,12 @@
fold.js
load.js
toc.js
resize_toc.js
document.js
resize_text.js
show_links.js
level.js
next_last.js
help.js
search.js
state.js

135
sks/document/js/load.js Normal file
View File

@@ -0,0 +1,135 @@
function first_basename() {
let first = K.get(".navlink");
return first ? K.attr(first, "data-page") : null;
}
function scroll_to_target(target_name) {
var text = K.get("#text");
if (!text) return;
var folder_top = text.getBoundingClientRect().top;
var target = K.get(target_name);
if (target) {
var target_top = target.getBoundingClientRect().top;
var scroll_position = text.scrollTop + target_top - folder_top;
text.scrollTop = scroll_position;
}
highlight_visible_toc_sections();
}
function modify_links() {
var links = K.getv("#text a");
links.forEach(function(link) {
if (link.classList.contains("linkd") || link.querySelector("img")) {
return;
}
var href = link.getAttribute("href");
if (!href) return;
if (href.indexOf("http://") != -1 ||
href.indexOf("https://") != -1 ||
href.indexOf(".pdf") != -1 ||
href.indexOf("mailto:") != -1 ||
href.indexOf("/") == 0 ||
href.indexOf("#") != -1 ||
href.indexOf("..") == 0) {
return;
}
var parts = href.split('#');
var len = parts[0].length;
var page = parts[0].substring(0, len - 5);
var target = "#" + parts[1];
var span = document.createElement("span");
span.className = "panelink";
span.setAttribute("data-page", page);
span.setAttribute("data-target", target);
span.setAttribute("data-title", link.textContent);
span.textContent = link.textContent;
link.parentNode.replaceChild(span, link);
});
}
function show_text() {
K.visible("#search_page", "hide");
K.visible("#help_page", "hide");
K.visible("#text", "show");
K.style("#content", "background-color", "white");
K.text("#help", "Help");
highlight_visible_toc_sections();
}
function new_page_callback() {
show_text();
modify_links();
resize();
current_page_callback();
}
var image_timer;
var last_basename;
function current_page_callback() {
if (target_name == "#") {
return;
}
var selector = '.navlink[data-target="' + (target_name.split("#")[1] || "") + '"]';
var navlink = K.get(selector);
if (navlink) {
update_page_title(navlink);
}
resize();
scroll_to_target(target_name);
}
async function load_page_content(basename, new_page, callback) {
if (true || basename != current_basename()) {
var url = "pages/" + basename + ".html";
await K.load("#text", url);
callback();
}
else {
callback();
}
}
function load_page(basename, target, callback, push_state) {
var init_complete = false;
var push = typeof push_state !== "undefined";
var state, state_target;
if (init_complete) {
var parts = window.location.hash.toString().split("#");
state = {"basename" : parts[1], "target" : "#" + parts[2]};
state_target = "#" + basename + target;
if (push) {
history.pushState(state, "", state_target);
} else {
history.replaceState(state, "", state_target);
}
load_page_content(
basename, true,
function() {
target_name = target;
initialize = false;
callback(); } );
} else {
basename = first_basename();
state = {"basename" : basename, "target" : "#"};
state_target = "#" + basename + "#";
init_complete = true;
load_page_content(
basename, true,
function() {
target_name = target;
initialize = true;
callback(); } );
history.replaceState(state, "", state_target);
}
}

View File

@@ -0,0 +1,105 @@
/*
Enable the "Next" and "Previous" buttons to move through the table of
contents, making entries visible if necessary.
*/
function first_highlighted_navlink() {
let highlighted_titles = [...K.getv(".section-title.highlight")];
if (highlighted_titles.length === 0) {
return null;
}
return highlighted_titles[0].parentNode;
}
function make_navlink_visible(navlink) {
const elts = [...K.getv(".navlink, .caret")];
let i;
for (i = elts.indexOf(navlink); i >= 0; i -= 1) {
let elt = elts[i];
if (K.has_class(elt, "caret")) {
if (!K.has_class(elt, "caret-down")) {
toggle_caret(elt);
}
if (K.attr(elt, "data-level") === "0") {
break;
}
}
}
}
function show_button_status(id, active_p) {
//const inactive_color = "rgb(60%,60%,60%)";
//const inactive_color = "rgb(20%,20%,20%)";
const inactive_color = "transparent";
//const text_color = "white";
const text_color = "black";
const node = K.get(id);
if (active_p) {
K.style(node, "color", text_color);
//K.style(node, "color", "white");
K.style(node, "text-decoration", "none");
//K.style(node, "font-style", "normal");
} else {
K.style(node, "color", inactive_color);
K.style(node, "text-decoration", "none");
//K.style(node, "font-style", "italic");
}
}
function set_next_last_button_status() {
const navlinks = [...K.getv(".navlink")];
const top = headers_in_text()[0][0];
let status;
if (!top) {
status = [true, false];
}
else if (top.id === K.attr(navlinks[0], "data-target")) {
status = [true, false];
} else if (top.id === K.attr(navlinks[navlinks.length-1], "data-target")) {
status = [false, true];
} else {
status = [true, true];
}
show_button_status("#next", status[0]);
show_button_status("#previous", status[1]);
}
function next_navlink() {
let navlinks = [...K.getv(".navlink")];
let index = navlinks.indexOf(first_highlighted_navlink());
if (index < navlinks.length - 1) {
let next = navlinks[index + 1];
next.children[0].children[0].click();
//make_navlink_visible(next);
}
return index + 1;
}
function previous_navlink() {
let navlinks = [...K.getv(".navlink")];
let index = navlinks.indexOf(first_highlighted_navlink());
if (index > 0) {
let previous = navlinks[index - 1];
previous.children[0].children[0].click();
//make_navlink_visible(previous);
}
return index - 1;
}
window.addEventListener("load", function (event) {
K.get("#next").addEventListener("click", function (event) {
// hide_help();
const index = next_navlink();
resize_toc();
});
K.get("#previous").addEventListener("click", function (event) {
// hide_help();
const index = previous_navlink();
resize_toc();
});
define_keypress("N", "#next");
define_keypress("P", "#previous");
set_next_last_button_status();
});

View File

@@ -0,0 +1,129 @@
/* Toggling between text with the table of contents and the text without it */
function resize_text_width(toc_width) {
const displayed = displayed_text();
const style = window.getComputedStyle(displayed, null);
const offset = parseInt(style.getPropertyValue("padding-left"))
+ parseInt(style.getPropertyValue("padding-right"));
if (toc_width !== undefined) {
K.width(displayed, K.width("#middle") - toc_width - offset);//Sec - K.width("#resizer"));
} else {
/*
const style = window.getComputedStyle(displayed, null);
const offset = parseInt(style.getPropertyValue("padding-left"))
+ parseInt(style.getPropertyValue("padding-right"));
*/
K.width(displayed, K.width("#middle") - offset);
}
}
function get_top_header() {
return header_in_window("#text > p, #text > div");
}
function set_top_pos(top_header, original_top_pos) {
const new_top_pos = top_header.getBoundingClientRect().top;
const offset = original_top_pos - new_top_pos;
if (offset !== 0) {
//const text = K.get("#text");
const text = displayed_text();
text.scrollTo({top : text.scrollTop - offset});
}
}
function maintain_text_position(func) {
//const text_rect = K.get("#text").getBoundingClientRect();
const text_rect = displayed_text().getBoundingClientRect();
const top = text_rect.top - 10;
let top_elt;
let elt;
//for (let elt of K.get("#text").children) {
for (elt of displayed_text().children) {
if (K.top(elt) >= top) {
top_elt = elt;
break;
}
}
if (top_elt) {
let current_top = top_elt.getBoundingClientRect().top;
func.call();
set_top_pos(top_elt, current_top);
} else {
func.call();
}
}
function set_buttons(mode) {
let button;
for (button of ["#next", "#previous", "#fit"]) {
K.visible(button, mode);
}
for (button of K.getv(".depthchoice")) {
K.visible(button, mode);
}
}
function textshow() {
if (K.visible("#toc")) {
save_position();
K.visible("#toc", "hide");
K.visible("#resizer", "hide");
K.text("#textshow", "Table of contents");
set_buttons("hide");
resize_text_width();
//resize_toc();
resize_images();
} else {
K.visible("#toc", "show");
K.visible("#resizer", "show");
K.text("#textshow", "Text only");
/*
let fit_active = K.attr("#fit_check", "active");
if (fit_active === "false") {
K.get("#fit_check").click();
}
*/
//resize_images();
restore_position();
set_buttons("show");
resize_toc();
resize_images();
// Restore previous state:
/*
if (fit_active === "false") {
K.get("#fit_check").click();
}
*/
}
//resize_images();
//adjust_widths();
//center_elements();
//set_code_comment_width();
//set_imagecode_width();
// adjust_footnotes();
}
window.addEventListener("load", function (event) {
K.get("#textshow").addEventListener(
"click",
function (event) {
event.preventDefault();
maintain_text_position(textshow);
});
window.addEventListener(
"resize",
function (event) {
maintain_text_position(
function () {
resize_text_width();
resize_toc();
//resize_images();
});
});
define_keypress("T", "#textshow");
});

View File

@@ -0,0 +1,110 @@
/*
Resizing the pane of the table of contents to fit the titles, with
a draggable right edge to change the with of the table of contents pane.
*/
var resizer_width = 4;
var resizer_with_borders = 6;
function current_full_toc_width() {
var width = 0;
K.map(".section-title-text",
function (elt) {
width = Math.max(width, K.right(elt));
});
// Add room for scrollbar:
//width += K.hpad("#toc_items") * 3.0 + resizer_width;
let caret = K.get(".caret");
let caret_width = caret ? K.hpad(caret) : 16; // Need to adjust for now nested sections
width += K.hpad("#toc") + caret_width + K.hpad("#toc ul")
// + K.hpad("#toc_items") //* 1.5 // room for scroll bar
+ resizer_width + 8; // Room for scroll bar
return width;
}
function resize_toc(force_fit) {
if (K.visible("#toc") && (force_fit || K.attr("#fit_check", "active") === "true")) {
const toc_width = current_full_toc_width();
const text_width = K.width("#middle")
- toc_width - resizer_with_borders;
//K.width("#text", text_width);
K.width(displayed_text(), text_width);
K.width("#toc", toc_width);
//resize_captions();
resize_images();
//K.height(".resizer", Math.max(K.height("#toc"),
// K.height("#toc_items")));
//console.log("resize_toc: " + text_width, toc_width);
/*
resize_images();
adjust_widths();
center_elements();
set_annotate_max_width();
set_code_comment_width();
set_imagecode_width();
*/
}
}
function initResize(e) {
e.preventDefault();
window.addEventListener("mousemove", Resize, false);
window.addEventListener("mouseup", stopResize, false);
}
function Resize(e) {
e.preventDefault();
// If fit is on, turn it off if the divider is moved manually.
if (K.attr("#fit_check", "active") == "true") {
K.get("#fit_check").click();
}
maintain_text_position(
function () {
//const toc_width = Math.max(0, e.clientX) + K.hpad("#text") + resizer_width;
const toc_width = Math.max(0, e.clientX) + K.hpad(displayed_text()) + resizer_width;
K.width("#toc", toc_width);
//K.width("#text", K.width("#middle") - toc_width);
K.width(displayed_text(), K.width("#middle") - toc_width);
resize_images();
highlight_headers_in_toc();
});
}
function stopResize(event) {
if (event) {
event.preventDefault();
}
window.removeEventListener("mousemove", Resize, false);
window.removeEventListener("mouseup", stopResize, false);
}
window.addEventListener("load", function (event) {
K.get("#fit_check").addEventListener(
"click",
function (e) {
//console.log("fit_check click");
maintain_text_position(resize_toc);
if (K.attr(e.srcElement, "active") === "false") {
K.attr(e.srcElement, "active", "true");
} else {
K.attr(e.srcElement, "active", "false");
}
resize_toc();
localStorage.setItem("klammertext_fit", K.attr("#fit_check", "active"));
});
// The native <label for="fit_check"> behavior already forwards
// clicks to the checkbox. No additional handler needed.
K.get("#resizer").addEventListener("mousedown", initResize, false);
initResize(event);
stopResize();
resize_toc();
define_keypress("F", "#fit_check");
});

View File

@@ -0,0 +1,84 @@
function adjust_search_input_pane() {
//if ($("#textshow").is(":visible")) {
if (K.visible("#textshow")) {
const search_input_width = 200;
//console.log(K.right("#textshow") + " " + K.right("#focus") + " " +
// K.right("#linkshow") + " " + K.right("#search_clear"));
const button_right = Math.max(
K.right("#textshow"), K.right("#focus"), K.right("#linkshow"), K.right("#search_clear"));
const help_left = K.left("#help");
//console.log("help_left: " + help_left);
//console.log("button_right: " + button_right);
//console.log("Search input: " + (help_left - button_right - 90));
let new_width = Math.min(search_input_width, help_left - button_right - 90);
if (new_width < 10) {
new_width = search_input_width;
}
K.width("#search_input", new_width);
}
}
function resize() {
/*
adjust_search_input_pane();
var height = $(window).height();
var title_height = K.height("#title");
var nav_height = K.height("#nav");
var status_height = K.height("#status");
K.height("#middle", height - title_height - nav_height - status_height + 1);
*/
//resize_text_width();
/*
set_annotate_max_width();
set_code_comment_width();
set_imagecode_width();
// adjust_footnotes();
adjust_slanted_header_table();
check_visible_link_boxes();
set_code_caption_width();
resize_images();
adjust_widths();
center_elements();
*/
}
/*
$(window).on("load", function() {
$(window).on("resize", function(event) {
console.log("RESIZE");
event.preventDefault();
resize();
//maintain_text_position(resize);
});
$("#toc").scroll( function() {
adjust_toc_slider_height();
});
//$("#toc").resizable({handles : "e"});
});
*/
window.addEventListener("load", function (event) {
window.addEventListener(
"resize",
function (event) {
event.preventDefault();
resize_images();
maintain_text_position(resize);
highlight_headers_in_toc();
/*
var top_header = get_top_header();
var original_pos = top_header.position().top;
resize();
set_top_pos(top_header, original_pos);
*/
});
resize();
//highlight_headers_in_toc();
});

313
sks/document/js/search.js Normal file
View File

@@ -0,0 +1,313 @@
/*
address article aside blockquote details dialog dd
div dl dt fieldset figcaption figure footer form
h1 h2 h3 h4 h5 h6 header hgroup hr li
main nav ol p section table ul td tr pre
*/
function blocks(root) {
let selector = [];
"blockquote kt-chapter div dd dt kt-part h1 h2 h3 h4 h5 li p td pre"
.split(" ").forEach(function (e) {
//selector.push("#pagecache .content_page " + e);
selector.push(root + " " + e);
});
return document.querySelectorAll(selector.join(", "));
}
function page_cache_blocks() {
return blocks("#pagecache .content_page");
}
function current_blocks() {
return blocks("#text");
}
function visible_toc_sections() {
let elements = current_blocks();
let last_heading = null;
let text_window = K.get("#text");
let text_top = text_window.scrollTop;
let text_bottom = text_top + K.height(text_window);
//console.log("text_top: " + text_top);
//console.log("text_bottom: " + text_bottom);
let result = [];
elements.forEach(function (elt) {
if (K.attr(elt, "data-level")) {
last_heading = elt;
//console.log("");
//console.log("LAST_HEADING:");
//console.log(last_heading);
}
//let elt_top = K.top(elt) - K.top(text_window);
let elt_top = elt.offsetTop; //
K.top(elt);
//console.log("");
//console.log("elt: " + elt_top);
//console.log(elt);
/*
if (elt_top >= text_top &&
elt_top <= text_bottom &&
!result.includes(last_heading)) {
*/
if (elt_top >= text_top &&
elt_top <= text_bottom) {
//console.log(" add: "
// + last_heading.children[0].textContent.trim());
if (!result.includes(last_heading)) {
result.push(last_heading);
}
}
});
if (result.length == 0) {
result.push(last_heading);
}
return result;
}
function buttons_before_search() {
//console.log("buttons_before_search()");
K.get("#search_clear").hidden = true;
K.get("#search_back_label").hidden = true;
K.get("#search_clear_input").hidden = true;
K.get("#search_label").hidden = false;
show_navigation_items();
}
function buttons_during_search() {
//console.log("buttons_during_search()");
K.get("#search_clear").hidden = true;
K.get("#search_back_label").hidden = false;
K.get("#search_clear_input").hidden = false;
K.get("#search_label").hidden = false;
show_navigation_items(false);
}
function buttons_after_search() {
//console.log("buttons_after_search()");
K.get("#search_clear").hidden = false;
K.get("#search_back_label").hidden = false;
K.get("#search_clear_input").hidden = false;
K.get("#search_label").hidden = false;
show_navigation_items();
}
function show_match(str) {
return "<span class=\"search_target\">" + str + "</span>";
}
function show_all_matches(page, rgx) {
//let cached_page = K.get("#pagecache #_page_" + id[2]);
let text_contents = page.innerHTML;
text_contents = text_contents.replaceAll(rgx, show_match);
page.innerHTML = text_contents;
}
function unhighlight_search_targets() {
K.getv(".search_target").forEach(function(e) {
e.outerHTML = e.innerHTML;
});
}
var saved_toc_width = 0;
function make_toc_visible(visible)
{
if (!visible) {
let current_width = K.width("#toc");
if (current_width > 0) {
saved_toc_width = current_width;
}
}
K.get("#toc").hidden = !visible;
K.get("#resizer").hidden = !visible;
if (visible) {
if (saved_toc_width > 0) {
K.width("#toc", saved_toc_width);
resize_text_width(saved_toc_width);
}
resize_toc();
} else {
resize_text_width();
}
}
function blocks_with_text(search_string) {
const rgx = new RegExp(search_string, "g");
const id_rgx = new RegExp("id=\"(.*?)\" data-basename=\"(.*?)\"");
let modified_blocks = [];
page_cache_blocks().forEach(function (o) {
let element_text = o.textContent;
if (element_text.search(rgx) > -1) {
//console.log(id_rgx);
//console.log(element_text);
let id = o.outerHTML.match(id_rgx);
//console.log("id: " + id);
//let element_contents = o.innerHTML;
let element_contents = o.outerHTML;
//element_contents = unhighlight_searches_in_text(element_contents);
//console.log(element_contents);
//console.log("\n----------------\n");
element_contents = element_contents.replaceAll(rgx, show_match);
//let modified = document.createElement("div");
//K.attr(modified, "class", "search_result");
let modified = document.createElement("a");
K.attr(modified, "class", "search_result");
K.attr(modified, "href", "#" + id[2] + "#" + id[1])
modified.innerHTML = element_contents;
modified_blocks.push(modified);
modified.addEventListener("click", function (event) {
event.preventDefault();
unhighlight_search_targets();
go_to_section(null, id[2], id[1], true, function() {
show_all_matches(displayed_text(), rgx);
});
make_toc_visible(true);
buttons_after_search();
});
}
});
return modified_blocks;
}
function search_heading(search_string, count) {
let heading = document.createElement("h1");
if (count > 0) {
heading.innerHTML = count + " occurrences of \"" + search_string + "\"";
} else {
heading.innerHTML = "No occurrences of \"" + search_string + "\"";
}
return heading;
}
function show_search_results(search_string) {
if (search_string.length === 0) {
return;
}
unhighlight_search_targets();
let search_page = K.get("#search_page");
search_page.innerHTML = "";
//search_page.style.width = K.width("#text");
let results = blocks_with_text(search_string);
search_page.append(search_heading(search_string, results.length));
if (results) {
results.forEach(function (o) {
search_page.append(o);
});
}
make_toc_visible(false);
buttons_during_search();
//let width = K.get("#text").clientWidth;
//console.log("outerWidth: " + width);
K.get("#text").replaceWith(search_page);
K.attr("#search_page", "id", "text");
search_page = document.createElement("div");
K.attr(search_page, "id", "search_page");
K.get("#pagecache").append(search_page);
}
window.addEventListener("load", function (event) {
K.get("#search_input").addEventListener("keyup", function (e) {
event.preventDefault();
let search_string = K.get("#search_input").value;
if (search_string.length > 0) {
let enter_key = 13;
if (e.which === enter_key) {
//console.log("save position with button");
unhighlight_search_targets();
save_position();
buttons_during_search();
//console.log("ENTER " + K.get("#search_input").value);
show_search_results(search_string);
}
}
});
K.get("#search_label").addEventListener("click", function (e) {
event.preventDefault();
let search_string = K.get("#search_input").value;
if (search_string.length > 0) {
//console.log("save position with return");
save_position();
buttons_during_search();
show_search_results(search_string);
} else {
let input_box = K.get("#search_input");
K.style(input_box, "background-color", "var(--nav_background_color)");
K.style(input_box, "font-style", "italic");
input_box.value = "Enter search text here";
setTimeout(function() {
K.style(input_box, "background-color", "white");
input_box.value = "";
K.style(input_box, "font-style", "normal");
}, 1500);
}
});
K.get("#search_clear").addEventListener("click", function (e) {
unhighlight_search_targets();
/*
text_elt.innerHTML = text_elt.innerHTML.replaceAll(id_rgx, "$1");
let text_contents = text_elt.innerHTML;
text_contents = text_contents.replaceAll(id_rgx, "$1");
text_elt.innerHTML = text_contents;
*/
//K.get("#search_clear").hidden = true;
buttons_before_search();
add_scrolling_to_text();
});
K.get("#search_clear_input").addEventListener("click", function (e) {
K.get("#search_input").value = "";
e.srcElement.hidden = true;
//K.attr("#text", "style", "background-color: var(--nav_background_color)");
//K.attr("#text", "style", "color: var(--nav_background_color)");
});
K.get("#search_back_label").addEventListener("click", function (e) {
//console.log("back");
make_toc_visible(true);
buttons_before_search();
add_scrolling_to_text();
restore_position();
unhighlight_search_targets();
});
K.get("html").addEventListener("keyup", function (event) {
let uppercase = "S".charCodeAt();
let lowercase = uppercase + 32;
if (search_not_active()) {
if (event.which === uppercase || event.which === lowercase) {
buttons_before_search();
K.get("#search_input").focus();
K.get("#search_input").value = "";
}
}
});
//define_keypress("B", "#search_back_label");
});

View File

@@ -0,0 +1,44 @@
function show_link_boxes(show) {
K.map("a", function(elt) {
if (show) {
K.remove_class(elt, "undisplayed_link");
K.add_class(elt, "displayed_link");
localStorage.setItem("klammertext_links", "true");
} else {
K.remove_class(elt, "displayed_link");
K.add_class(elt, "undisplayed_link");
localStorage.setItem("klammertext_links", "false");
}
});
K.map(".pagelink", function(elt) {
if (show) {
K.remove_class(elt, "undisplayed_link");
K.add_class(elt, "displayed_link");
} else {
K.remove_class(elt, "displayed_link");
K.add_class(elt, "undisplayed_link");
}
});
}
window.addEventListener("load", function (event) {
K.get("#linkshow_check").addEventListener("click", function (event) {
let box = K.get("#linkshow_check");
if (K.attr(box, "active") === "false") {
K.attr(box, "active", "true");
show_link_boxes(true);
} else {
K.attr(box, "active", "false");
show_link_boxes(false);
}
});
/*
K.get("#linkshow_check_label").addEventListener("click", function (event) {
K.get("#linkshow_check").click();
});
*/
define_keypress("L", "#linkshow_check");
});

201
sks/document/js/state.js Normal file
View File

@@ -0,0 +1,201 @@
[save_position, restore_position] = function () {
let basename = "";
let top_heading_id = "";
let y_offset = 0;
let toc_width = 0;
let save = function () {
console.log("\n");
console.log("save_position");
basename = window.location.toString().split("#")[1];
let top_heading = first_highlighted_navlink();
console.log("top heading:");
console.log(top_heading);
if (!top_heading) {
return;
}
top_heading_id = K.attr(top_heading, "data-target");
//y_offset = Math.round(K.top(top_heading) - K.top("#text"));
let textw = K.get("#text");
//y_offset = textw.scrollHeight -textw.scrollTop;
y_offset = K.get("#text").scrollTop;
console.log("y_offset: " + y_offset);
toc_width = K.width("#toc");
console.log("toc_width: " + toc_width);
console.log("save_position: " + basename + " " + top_heading_id + " " + y_offset);
}
let restore = function () {
let page = K.get("#pagecache #_page_" + basename);
K.get("#text").innerHTML = page.innerHTML;
resize_text_width(toc_width);
K.get("#text").scrollTo(0, y_offset);
}
return [save, restore];
}();
function page_cache_id(name) {
return "_page_" + name;
}
async function cache_pages() {
if (K.get("#pagecache").children.length === 0) {
K.style("#pagecache", "display", "none");
const pagecache = K.get("#pagecache");
let loads = [];
pages_basenames().forEach(function (p) {
let page = document.createElement("div");
loads.push(K.load(page, "pages/" + p + ".html"));
K.attr(page, "id", page_cache_id(p));
K.attr(page, "class", "content_page");
pagecache.append(page);
});
let help_page = document.createElement("div");
loads.push(K.load(help_page, "pages/help.html"));
K.attr(help_page, "id", "help_page");
pagecache.append(help_page);
let search_page = document.createElement("div");
K.attr(search_page, "id", "search_page");
pagecache.append(search_page);
await Promise.all(loads);
}
}
window.addEventListener("popstate", function (event) {
const parts = window.location.toString().split("#");
const basename = parts[1];
const target = parts[2];
if (basename && target) {
if (K.get("#help").textContent === "Back") {
if (!K.visible("#toc")) {
textshow();
}
modify_buttons_for_help("show");
frame_resize();
} else {
go_to_section(null, basename, target, true);
}
}
});
window.addEventListener("load", async function (event) {
let last_location = localStorage.getItem("klammertext_location");
let last_fit_check = localStorage.getItem("klammertext_fit");
let last_links_check = localStorage.getItem("klammertext_links");
await cache_pages();
K.getv("#toc .caret[data-level='0']").forEach(function (o) {
if (!K.has_class(o, "caret-down")) {
o.click();
}
});
// Set initial hash so the browser back button can return to the first page
if (!window.location.hash) {
let first = K.get(".navlink");
if (first) {
let basename = K.attr(first, "data-page");
let target = K.attr(first, "data-target");
history.replaceState(null, null, "#" + basename + "#" + target);
}
}
if (last_location) {
localStorage.setItem("klammertext_reload", "true");
let parts = last_location.split(":");
//console.log("\n");
//console.log("load parts:");
//console.log(parts);
go_to_section(null, parts[0], parts[1], true);
}
resize_toc(true);
// Restore fit and links states directly (not via .click()) to avoid
// desync with the browser's checkbox state restoration on refresh.
if (last_fit_check === "true") {
K.attr("#fit_check", "active", "true");
K.get("#fit_check").checked = true;
} else {
K.attr("#fit_check", "active", "false");
K.get("#fit_check").checked = false;
}
resize_toc();
if (last_links_check === "true") {
K.attr("#linkshow_check", "active", "true");
K.get("#linkshow_check").checked = true;
show_link_boxes(true);
} else {
K.attr("#linkshow_check", "active", "false");
K.get("#linkshow_check").checked = false;
}
/*
if (last_location) {
const parts = last_location.toString().split("#");
console.log("parts:");
console.log(parts);
//go_to_section(null, parts[1], parts[2]);
}
*/
/*
let hash = "#" + basename;
if (target) {
hash += "#" + target;
}
*/
//save_position();
/*
let last_location = localStorage.getItem("klammertext_location");
console.log("last_location from storage: " + last_location);
if (last_location && last_location != window.location.toString()) {
//history.replaceState(last_location.toString(), "");
const parts = last_location.toString().split("#");
const basename = parts[1];
const target = parts[2];
window.location.hash = "#" + basename + "#" + target;
console.log("last_location: " + basename + " " + target);
//go_to_section(null, basename, target);
let locator = "#toc .navlink[data-target='" + target + "'] .section-title-text";
console.log(locator);
let title = K.get(locator);
console.log("title");
console.log(title);
title.click();
}
*/
});
/*
function get_position() {
const basename = window.location.toString().split("#")[1];
return [basename, K.get("#text").scrollTop];
}
function set_position(basename, y) {
K.get("#text").innerHTML = K.get("#_page_" + basename).innerHTML;
K.get("#text").scrollTo(0, y);
}
*/

301
sks/document/js/toc.js Normal file
View File

@@ -0,0 +1,301 @@
function current_page_headers() {
let result = K.getv([1,2,3,4,5].map(function (o) {
return "#text h" + o;
}));
result = [...result];
["kt-part", "kt-chapter"].forEach(function (tag) {
let elt = K.get("#text " + tag);
if (elt) {
result.push(elt);
}
});
return result;
}
function add_navlink_mapping(navlink_map, navlink) {
let target = K.attr(navlink, "data-target");
navlink_map[target] = navlink.children[0];
return navlink_map;
}
function headers_in_text() {
//const headers = [...K.getv("h1,h2,h3,h4")];
const headers = current_page_headers();
//const text_rect = K.get("#text").getBoundingClientRect();
const text_rect = displayed_text().getBoundingClientRect();
const top = text_rect.top;
const bottom = top + text_rect.height;
let visible = [];
let invisible = [];
headers.forEach(function (h) {
const header_rect = h.getBoundingClientRect();
const header_bottom = header_rect.bottom;
if (header_bottom > top && header_bottom < bottom) {
visible.push(h);
} else {
invisible.push(h);
}
});
//console.log("headers_in_text: " + visible.length
// + " " + invisible.length);
return [visible, invisible];
}
function open_highlighted_sections(node) {
//console.log("open_highlighted_sections");
let parent = node.parentNode;
while (parent && parent.id != "toc_items") {
//console.log(parent.id);
//console.dir(parent);
//console.log(parent);
//console.log(parent.children);
let carets = [...parent.children].filter(function (node) {
return K.has_class(node, "caret")
&& !K.has_class(node, "caret-down");
});
carets.forEach(function (c) {
c.click();
});
//console.log("carets: ")
//console.log(carets);
parent = parent.parentNode;
}
}
function open_visible() {
//console.log("open_visible");
let open_sections = visible_toc_sections();
if (open_sections.length === 1 && !open_sections[0]) {
return;
}
//console.log("open_sections:");
//console.log(open_sections);
open_sections.forEach(function (e) {
//console.log("visible: " + e);
let section_id = K.attr(e, "id");
let navlink = K.get(".navlink[data-target=\"" + section_id + "\"]");
if (navlink) {
open_highlighted_sections(navlink);
}
});
}
var last_visible = [];
//function find_visible_element(node) {
// [...node.children].forEach(function (o) {
function highlight_headers_in_toc() {
let navlink_map = {};
[...K.getv(".navlink")].forEach(
function (o) { add_navlink_mapping(navlink_map, o); });
[...K.getv(".highlight")].forEach(function (o) {
K.remove_class(o, "highlight");
});
let [visible, invisible] = headers_in_text();
/*
console.log("-");
console.log("visible: ");
console.log(visible);
console.log("visible length: " + visible.length + " " + visible);
console.log("invisible: ");
console.log(invisible);
console.log("invisible length: " + invisible.length + " " + invisible);
console.log("-");
*/
let max_level = 0;
if (visible.length > 0) {
visible.forEach(function (h) {
if (h.id) {
K.add_class(navlink_map[h.id], "highlight");
//console.log(h);
max_level = Math.max(max_level, parseInt(K.attr(h, "data-level")));
}
});
}
//console.log("max_level: ", max_level);
if (invisible.length > 0) {
invisible.forEach(function (h) {
if (h.id) {
K.remove_class(navlink_map[h.id], "highlight");
}
});
}
//if (toc_count === 0) {
/*
if (visible.length !== 0) {
//console.log("last visible: " + last_visible);
//last_visible = visible[visible.length - 1];
last_visible = visible[0];
} else {
//console.log("None visible; setting:")
//console.log(last_visible);
//visible = [last_visible];
}
console.log("visible");
console.log(visible);
*/
//console.log("last_visible:");
//console.log(last_visible);
if (visible.length === 0 && last_visible.id && navlink_map[last_visible.id]) {
// No headers are visible; use the header of the displayed element
//console.log("setting visiblity of " + last_visible.id);
K.add_class(navlink_map[last_visible.id], "highlight");
/*
last_visible.forEach(function (h) {
K.add_class(navlink_map[h.id], "highlight");
});
*/
}
if (visible.length !== 0) {
//console.log("set last visible: " + last_visible.id);
//last_visible = visible[visible.length - 1];
last_visible = visible[visible.length - 1];
}
open_visible();
}
/*
function update_hash() {
const first_highlighted_navlink = K.get(".section-title.highlight").parentNode;
const basename = K.attr(first_highlighted_navlink, "data-page");
const target = K.attr(first_highlighted_navlink, "data-target");
window.location.hash = "#" + basename + "#" + target;
}
*/
var navigation_interval = null;
function go_to_section(elt, basename_arg, target_arg, force_reload, on_loaded) {
const basename = basename_arg || K.attr(elt, "data-page");
const target = target_arg || (elt ? K.attr(elt, "data-target") : null);
const parts = window.location.toString().split("#");
let hash = "#" + basename;
if (target) {
hash += "#" + target;
}
let basepage = K.get("#_page_" + basename);
if (!basepage) {
return;
}
if (navigation_interval) {
clearInterval(navigation_interval);
navigation_interval = null;
}
var retries = 0;
const max_retries = 100;
navigation_interval = setInterval(function() {
if (!K.get("#text") || parts[1] != basename || force_reload) {
displayed_text().innerHTML = K.get("#_page_" + basename).innerHTML;
resize_images();
if (!force_reload) {
unhighlight_search_targets();
}
}
force_reload = false;
var target_element = document.getElementById(target);
if (!target_element) {
if (++retries > max_retries) {
clearInterval(navigation_interval);
navigation_interval = null;
}
return;
}
clearInterval(navigation_interval);
navigation_interval = null;
if (on_loaded) {
on_loaded();
}
let text_div = displayed_text();
text_div.scrollTop = target_element.offsetTop - text_div.offsetTop;
remove_toc_highlighting();
highlight_headers_in_toc();
set_next_last_button_status();
set_link_listeners();
window.location.hash = hash;
localStorage.setItem("klammertext_location", `${basename}:${target}`);
}, 10);
K.get("#help").textContent = "Help";
}
function toggle_caret(node) {
//console.trace();
node.parentElement.querySelector(".nested").classList.toggle("active");
node.classList.toggle("caret-down");
resize_toc();
}
function add_scrolling_to_text() {
K.get("#text").addEventListener("scroll", function (event) {
//event.preventDefault();
//console.log("scroll " + K.get("#text").scrollTop);
highlight_headers_in_toc();
//show_all_highlighted();
});
}
window.addEventListener("load", function (event) {
var toggler = document.getElementsByClassName("caret");
for (i = 0; i < toggler.length; i++) {
toggler[i].addEventListener("click", function(event) {
toggle_caret(event.srcElement);
});
}
K.map(".section-number, .section-title-text", function(elt) {
elt.addEventListener("click", function (event) {
event.preventDefault();
buttons_before_search();
const caret = event.srcElement.parentNode.parentNode.parentNode.firstChild;
if (caret.nodeName !== "#text") {
const caret_down = K.has_class(caret, "caret-down");
if (!caret_down) {
toggle_caret(caret);
}
}
go_to_section(elt.parentNode.parentNode);
});
});
add_scrolling_to_text();
/*
K.get("#text").addEventListener("scroll", function (event) {
//event.preventDefault();
console.log("scroll " + K.get("#text").scrollTop);
highlight_headers_in_toc();
//show_all_highlighted();
});
*/
//highlight_headers_in_toc();
//open_visible();
//show_all_highlighted();
});

177
sks/document/reference.cpp Normal file
View File

@@ -0,0 +1,177 @@
#include "reference.h"
#include "target.h"
#include "util.h"
#include "show.h"
// Escape marker for backslash in pre-processed text
static const std::string BS = Target::escape_marker("\\");
std::ostream& operator<<(std::ostream& os, const numbered_element_spec& spec)
{
os << "[" << std::get<0>(spec) << "|"
<< std::get<1>(spec) << "|"
<< std::get<2>(spec) << "]";
return os;
}
std::ostream& operator<<(std::ostream& os, const numbered_elements& line_states)
{
bool found = false;
for (long unsigned i = 0; i < line_states.size(); i++) {
if (std::get<1>(line_states[i]) != "") {
os << i << ": " << line_states[i] << "\n";
found = true;
}
}
if (!found) {
os << "[No matching lines]";
}
return os;
}
std::string add_latex_caption_numbers(std::string latex_text)
{
std::string caption_delimiter { "__CAPTION__" }; // But also in kutil.py
std::regex caption_delimiter_rgx(caption_delimiter);
// Accept either the escape-marker form (from the :files path, where
// M.apply("tex", false) leaves markers in place) or the resolved
// literal-backslash form (from the :text path, already processed by
// the outer Machine).
std::regex section_rgx("(?:" + BS + R"(|\\)sA\{.*)");
std::map<std::string, int> index_map {};
bool section_seen = false;
std::string result {};
std::smatch match {};
for (std::string line : regex_split(latex_text, std::regex(R"(\n)"), false)) {
if (regex_match(line, match, section_rgx)) {
index_map.clear();
section_seen = true;
}
if (line.find(caption_delimiter) != std::string::npos) {
std::vector<std::string> parts = regex_split(line, caption_delimiter_rgx);
std::string before = parts[0];
std::string caption_type = parts[1];
std::string caption = parts[2];
std::string after = parts.size() == 4 ? parts[3] : "";
if (index_map.count(caption_type) == 0)
index_map[parts[1]] = 1;
int n = index_map[caption_type];
std::stringstream modified {};
modified << before << " " << caption_type << " ";
// Suppress the section/chapter prefix before any section has
// appeared, matching insert_html_caption_numbers behavior
// ("Figure 1" not "Figure 0.1").
if (section_seen) {
modified << "{\\ifthenelse{\\isundefined{\\thechapter}}{\\thesection}{\\thechapter}}.";
}
modified << n << " " << caption << after;
line = modified.str();
index_map[caption_type]++;
}
result += line + "\n";
}
return result;
}
numbered_elements latex_line_states(std::vector<std::string> lines)
{
// \hypertarget{Reference-Figure-0}{}\label{Label-Reference-Figure-0}
std::regex element_container_rgx(BS + R"(hypertarget\{(Reference-(\w+)-\d+)\}.*)");
std::regex element_caption_rgx(BS + R"(vstrut.*?\}\{.*?\}\{\s*(\w+)\s+(.+\.\d+).*)");
numbered_elements states(lines.size());
std::string id {};
std::string reftype {};
for (unsigned int i = 0; i < lines.size(); i++) {
std::smatch container_match {};
std::smatch caption_match {};
if (regex_match(lines[i], container_match, element_container_rgx)) {
id = container_match[1];
reftype = container_match[2];
} else if (regex_match(lines[i], caption_match, element_caption_rgx)) {
states[i] = {id, caption_match[1], caption_match[2]};
}
}
return states;
}
std::string find_target(std::string target,
std::vector<std::string>& lines, int start, numbered_elements states,
std::string target_type, int target_count, std::string direction)
{
// msg() << "find_target: " << target_type << " target_count: " << target_count
// << " direction: " << direction << "\n";
unsigned int offset = direction == "before" ? -1 : 1;
std::string current_type {};
int current_count = target_count;
unsigned int fi = start;
for (; fi > 0 && fi < lines.size(); fi += offset) {
// msg() << fi << " : " << states[fi] << "\n";
current_type = std::get<1>(states[fi]);
if (current_type == target_type) {
current_count--;
}
if (current_count == 0) {
break;
}
}
if (current_count > 0) {
std::stringstream msg {};
//ss << "No image exists that is \"" << direction << " " << current_count
// << "\" from line " << start << " in the HTML file.\n";
msg << "There is no " << target_type << " that is " << current_count
<< " " << direction << " the reference.\n";
//throw Argument_error(ss.str());
return "NOT FOUND: " + target_type + " " + direction;
}
std::stringstream ss {};
auto link_target = std::get<0>(states[fi]);
auto link_desc = std::get<2>(states[fi]);
if (target == "html") {
ss << "<a href=\"^#" << link_target << "\">" << target_type << " " << link_desc << "</a>";
} else if (target == "tex" || target == "latex") {
ss << BS << "figureref{" << link_target << "}{" << target_type << " " << link_desc << "}";
}
return ss.str();
}
std::string resolve_caption_references(
std::string target, std::vector<std::string> lines, numbered_elements states)
{
//auto lines = regex_split(text, std::regex(R"(\n)"), false);
// numbered_elements states = line_states(lines);
//std::regex reference_rgx(R"((.*?)___([A-Za-z]+) (\w+) (\d+)___([^\n]*))");
//std::regex reference_rgx(R"((.*?)<a href=\"__(\w+)( \d+)?__\">(\w+)</a>)");
std::regex reference_rgx(R"((.*?)__REF__(\w+)( \d+)?__(\w+)__)");
std::string result;
for (unsigned int i = 0; i < lines.size(); i++) {
std::string line = lines[i];
if (line.find("__REF__") == std::string::npos) {
result += line + "\n";
} else {
std::sregex_iterator end {};
size_t endpos = 0;
std::string mline {};
for (std::sregex_iterator p
{line.begin(), line.end(), reference_rgx}; p!= end; ++p) {
std::smatch match = *p;
std::string prefix = match[1];
std::string direction = match[2];
int count = match[3] == "" ? 1 : std::stoi(match[3]);
std::string target_type = match[4];
mline += prefix +
find_target(target, lines, i, states, target_type, count, direction);
endpos = match.position() + match.length();
}
if (endpos < line.size()) {
mline += line.substr(endpos);
}
// msg() << " mline: " << mline << "\n";
result += mline + "\n";
}
}
return result;
}

22
sks/document/reference.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <string>
#include <vector>
#include <tuple>
#include <regex>
#include "character.h"
using numbered_element_spec = std::tuple<std::string, std::string, std::string>;
//using numbered_elements = std::vector<std::vector<numbered_element_spec>>;
using numbered_elements = std::vector<numbered_element_spec>;
numbered_elements html_line_states(std::vector<std::string> lines);
numbered_elements latex_line_states(std::vector<std::string> lines);
std::pair<std::string, int> add_html_caption_numbers(std::string html_text, int& chapter_number);
std::string add_latex_caption_numbers(std::string text);
std::string resolve_caption_references(
std::string target, std::vector<std::string> lines, numbered_elements states);

View File

@@ -0,0 +1,71 @@
\usepackage{calc}
\usepackage{titletoc}
\usepackage{ifthen}
%% \pretolerance 1000
%% \tolerance 2000
%% \exhyphenpenalty 0
%% \clubpenalty 5000
%% \widowpenalty 5000
%% \displaywidowpenalty 5000
\setlength{\hoffset}{-1in}
\setlength{\voffset}{-1in}
\ifthenelse{\equal{\documenttwocolumn}{true}} {
\setlength{\columnsep}{24pt}
}
\setlength{\topmargin}{16pt}
\setlength{\textwidth}{\paperwidth - 2in}
\setlength{\oddsidemargin}{(\paperwidth - \textwidth) / 2}
\setlength{\evensidemargin}{\oddsidemargin}
\setlength{\headheight}{10mm}
\setlength{\footskip}{.33in}
\ifthenelse{\equal{\documentlandscape}{true}}
{\setlength{\textheight}{6.5in}\setlength{\headsep}{4mm}}
{\setlength{\textheight}{10.0in}\setlength{\headsep}{6mm}}
\setlength{\parindent}{0em}
\setlength{\parskip}{.7em}
%% \setlength{\voffset}{ % Adjust to for vertical centering
%% ((\paperheight - \headheight - \headsep - \textheight - \footskip) / 2)
%% - (1.25in + \topmargin)}
%% \newcommand{\arcbullet}{\raisebox{1.5pt}{\small$\bullet$}}
%% \renewcommand{\labelitemi}{\arcbullet}
%% \renewcommand{\labelitemii}{\arcbullet}
%% \renewcommand{\labelitemiii}{\arcbullet}
%% \renewcommand{\labelitemiv}{\arcbullet}
\setlength{\labelsep}{4pt}
\setlength{\fboxsep}{0pt}
% In non-book structures, \cleardoublepage is unnecessary (oneside layout).
% In book structure, preserve the real \cleardoublepage for recto chapter starts.
\ifthenelse{\equal{\documentisbook}{true}}{}{\renewcommand{\cleardoublepage}{\newpage}}
\newcommand{\vstrut}[1]{\rule[0pt]{0pt}{#1}}
\makeatletter\gdef\ttl^@savemark{}\makeatother
\newcommand{\figureref}[2]{\hyperlink{#1}{#2}}
%% \newcounter{currentpage}
%% \newcommand{\wfigureref}[2]{%
%% \refstepcounter{currentpage}{\label{current\thecurrentpage}}%
%% \ifthenelse{\equal{\pageref{Label-#1}}{\pageref{current\thecurrentpage}}}%
%% {\hyperlink{#1}{nopage #2}}%
%% {\hyperlink{#1}{#2} (page~\pageref{Label-#1})}}
%% \newcommand{\xfigureref}[2]{%
%% \ifthenelse{\equal{\pageref*{Label-#1}}{\thepage}}%
%% {\hyperlink{#1}{nopage #2}}%
%% {\hyperlink{#1}{#2} (page~\pageref*{Label-#1})}}
%% \newcommand{\yfigureref}[2]{%
%% \ifthenelse{\equal{\pageref{Label-#1}}{\thepage}}%
%% {\hyperlink{#1}{nopage #2}}%
%% {\hyperlink{#1}{#2} (page~\pageref{#1})}}

View File

@@ -0,0 +1 @@
document.sty