2019-02-05 02:44:07 +02:00
|
|
|
[HTMLUtilities::] HTML and JavaScript.
|
|
|
|
|
|
|
|
HTML utility code.
|
|
|
|
|
|
|
|
@h Definitions.
|
|
|
|
|
|
|
|
@ Keeping track of which images are referred to in the HTML we generate:
|
|
|
|
|
|
|
|
=
|
|
|
|
dictionary *image_usages = NULL; /* how many times, if at all, this image has been used */
|
|
|
|
|
|
|
|
typedef struct image_usage {
|
|
|
|
struct text_stream *leafname;
|
|
|
|
int usage_count;
|
|
|
|
struct filename *resolved_to;
|
2020-05-09 15:07:39 +03:00
|
|
|
CLASS_DEFINITION
|
2019-02-05 02:44:07 +02:00
|
|
|
} image_usage;
|
|
|
|
|
|
|
|
typedef struct image_source {
|
|
|
|
struct pathname *src; /* a location from which images can be taken */
|
2020-05-09 15:07:39 +03:00
|
|
|
CLASS_DEFINITION
|
2019-02-05 02:44:07 +02:00
|
|
|
} image_source;
|
|
|
|
|
|
|
|
@ We can either generate entirely fresh HTML pages, or we can inject
|
|
|
|
material into a division inside a copy of an existing page: this is called
|
|
|
|
"top-and-tailing".
|
|
|
|
|
|
|
|
=
|
|
|
|
int first_request_for_tt = TRUE; /* will the next request be the first? */
|
|
|
|
text_stream *DSET_main_tail_matter = NULL;
|
|
|
|
text_stream *DSET_main_top_matter = NULL;
|
|
|
|
text_stream *DSET_section_tail_matter = NULL;
|
|
|
|
text_stream *DSET_section_top_matter = NULL;
|
|
|
|
|
|
|
|
@h Images.
|
|
|
|
In the Inform application, a special |http| transport called |inform:| is used
|
|
|
|
for references to the in-app documentation, so an image called |orange.png|
|
|
|
|
will be found at the URL |inform:/orange.png|.
|
|
|
|
|
|
|
|
Otherwise, of course, we need to make an images folder alongside the HTML
|
|
|
|
we produce. It will be called |images|, so relative to the HTML, the URL
|
|
|
|
would then be |images/orange.png|.
|
|
|
|
|
|
|
|
However, it"s also possible that the book"s instructions tell us to use
|
|
|
|
images from an already-existing location instead.
|
|
|
|
|
|
|
|
Note that each time the URL of an image is looked up, we make a note of it,
|
|
|
|
so that we can keep track of which images we actually need.
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::image_URL(OUTPUT_STREAM, text_stream *leafname) {
|
|
|
|
if (image_usages == NULL) image_usages = Dictionaries::new(100, FALSE);
|
|
|
|
image_usage *iu = NULL;
|
|
|
|
if (Dictionaries::find(image_usages, leafname)) {
|
|
|
|
iu = Dictionaries::read_value(image_usages, leafname);
|
|
|
|
} else {
|
|
|
|
Dictionaries::create(image_usages, leafname);
|
|
|
|
iu = CREATE(image_usage);
|
|
|
|
iu->usage_count = 0;
|
|
|
|
iu->leafname = Str::duplicate(leafname);
|
|
|
|
Dictionaries::write_value(image_usages, leafname, iu);
|
|
|
|
}
|
|
|
|
iu->usage_count++;
|
2020-04-16 01:49:59 +03:00
|
|
|
filename *F = Filenames::in(indoc_settings->images_path, leafname);
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->html_for_Inform_application == 1) WRITE("inform:/%/f", F);
|
2019-02-05 02:44:07 +02:00
|
|
|
else {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->images_copy)
|
2020-04-16 01:49:59 +03:00
|
|
|
F = Filenames::in(Pathnames::from_text(I"images"), leafname);
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE("%/f", F);
|
|
|
|
}
|
|
|
|
iu->resolved_to = F;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ For ebook use only:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::note_images(void) {
|
|
|
|
image_usage *iu;
|
|
|
|
LOOP_OVER(iu, image_usage)
|
2019-02-17 01:28:02 +02:00
|
|
|
Epub::note_image(indoc_settings->ebook, iu->resolved_to);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@ Suppose we are indeed copying images into place: we have to get them from
|
|
|
|
somewhere. The somewheres are folders called "image sources", and this routine
|
|
|
|
adds one:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::add_image_source(pathname *path) {
|
|
|
|
image_source *is = CREATE(image_source);
|
|
|
|
is->src = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ During the copying process, we look for each image, say |orange.png|, in
|
|
|
|
each of these image sources, starting from the most recently added and
|
|
|
|
working backwards. As soon as we find a file of that name, we copy it over.
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::copy_images(void) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->images_copy) {
|
2020-04-16 01:49:59 +03:00
|
|
|
pathname *I = Pathnames::down(indoc_settings->destination, I"images");
|
2019-02-05 02:44:07 +02:00
|
|
|
Pathnames::create_in_file_system(I);
|
|
|
|
image_usage *iu;
|
|
|
|
LOOP_OVER(iu, image_usage) {
|
|
|
|
int found = FALSE;
|
|
|
|
image_source *is;
|
|
|
|
LOOP_BACKWARDS_OVER(is, image_source) {
|
2020-04-16 01:49:59 +03:00
|
|
|
filename *F = Filenames::in(is->src, iu->leafname);
|
2019-02-05 02:44:07 +02:00
|
|
|
if (TextFiles::exists(F)) {
|
|
|
|
found = TRUE;
|
|
|
|
Shell::copy(F, I, "-f");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found == FALSE) { Errors::with_text("unable to find the image %S", iu->leafname); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@h HTML file structure.
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::begin_file(OUTPUT_STREAM, volume *V) {
|
2019-02-17 01:28:02 +02:00
|
|
|
HTML::declare_as_HTML(OUT, indoc_settings->XHTML);
|
2019-02-05 02:44:07 +02:00
|
|
|
filename *CSS = NULL;
|
|
|
|
if ((V) && (Str::len(V->vol_CSS_leafname) > 0))
|
|
|
|
CSS = Filenames::from_text(V->vol_CSS_leafname);
|
|
|
|
HTML::begin_head(OUT, CSS);
|
|
|
|
TEMPORARY_TEXT(comment);
|
|
|
|
char *monthname[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
|
|
|
WRITE_TO(comment, "Generated by indoc on %d %s %d",
|
|
|
|
the_present->tm_mday, monthname[the_present->tm_mon], the_present->tm_year+1900);
|
|
|
|
HTML::comment(OUT, comment);
|
|
|
|
DISCARD_TEXT(comment);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HTMLUtilities::write_title(OUTPUT_STREAM, text_stream *title) {
|
|
|
|
WRITE("<title>%S</title>\n", title);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ Here's text within a link:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::general_link(OUTPUT_STREAM, text_stream *cl, text_stream *to, text_stream *text) {
|
|
|
|
HTML::begin_link_with_class(OUT, cl, to);
|
|
|
|
WRITE("%S", text);
|
|
|
|
HTML::end_link(OUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ And a link with both text and a title (tooltip) tag:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::titled_link(OUTPUT_STREAM, text_stream *cl, text_stream *to, text_stream *ti, text_stream *text) {
|
|
|
|
HTML::begin_link_with_class_title(OUT, cl, to, ti);
|
|
|
|
WRITE("%S", text);
|
|
|
|
HTML::end_link(OUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ And a standardised paragraph which is itself a link:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::textual_link(OUTPUT_STREAM, text_stream *to, text_stream *text) {
|
|
|
|
HTML_OPEN("p");
|
|
|
|
HTMLUtilities::general_link(OUT, I"standardlink", to, text);
|
|
|
|
HTML_CLOSE("p");
|
|
|
|
}
|
|
|
|
|
|
|
|
@h Miscellaneous HTML.
|
|
|
|
Horizontal ruled lines.
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::ruled_line(OUTPUT_STREAM) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->format == HTML_FORMAT) HTML_TAG("hr")
|
2019-02-05 02:44:07 +02:00
|
|
|
else WRITE("----------------------------------------------------------------------\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
@ Images.
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::image_element(OUTPUT_STREAM, text_stream *name) {
|
|
|
|
TEMPORARY_TEXT(details);
|
|
|
|
WRITE_TO(details, "alt=\"%S\" src=\"", name);
|
|
|
|
HTMLUtilities::image_URL(details, name);
|
|
|
|
WRITE_TO(details, "\"");
|
|
|
|
HTML::tag(OUT, "img", details);
|
|
|
|
DISCARD_TEXT(details);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HTMLUtilities::image_element_scaled(OUTPUT_STREAM, text_stream *name, int xsize, int ysize) {
|
|
|
|
TEMPORARY_TEXT(details);
|
|
|
|
WRITE_TO(details, "alt=\"%S\" src=\"", name);
|
|
|
|
HTMLUtilities::image_URL(details, name);
|
|
|
|
WRITE_TO(details, "\" width=\"%d\" height=\"%d\"", xsize, ysize);
|
|
|
|
HTML::tag(OUT, "img", details);
|
|
|
|
DISCARD_TEXT(details);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HTMLUtilities::image_with_id(OUTPUT_STREAM, text_stream *name, text_stream *id) {
|
|
|
|
TEMPORARY_TEXT(details);
|
|
|
|
WRITE_TO(details, "alt=\"%S\" src=\"", name);
|
|
|
|
HTMLUtilities::image_URL(details, name);
|
|
|
|
WRITE_TO(details, "\" id=\"%S\"", id);
|
|
|
|
HTML::tag(OUT, "img", details);
|
|
|
|
DISCARD_TEXT(details);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HTMLUtilities::asterisk_image(OUTPUT_STREAM, text_stream *name) {
|
|
|
|
TEMPORARY_TEXT(details);
|
|
|
|
WRITE_TO(details, "class=\"asterisk\" alt=\"*\" src=\"");
|
|
|
|
HTMLUtilities::image_URL(details, name);
|
|
|
|
WRITE_TO(details, "\"");
|
|
|
|
HTML::tag_sc(OUT, "img", details);
|
|
|
|
DISCARD_TEXT(details);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ The "extra" functions here are for revealing or concealing page content
|
|
|
|
when the user clicks a button. Each such piece of content is in its own
|
|
|
|
uniquely-ID'd |<div>|, as follows:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::extra_div_open(OUTPUT_STREAM, int id) {
|
|
|
|
HTML_OPEN_WITH("div", "id=\"extra%d\" style=\"display: none;\"", id);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ And the following links provide the wiring for the buttons:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::extra_link(OUTPUT_STREAM, int id) {
|
|
|
|
TEMPORARY_TEXT(onclick);
|
|
|
|
WRITE_TO(onclick, "showExtra('extra%d', 'plus%d'); return false;", id, id);
|
|
|
|
HTML::begin_link_with_class_onclick(OUT, NULL, I"#", onclick);
|
|
|
|
DISCARD_TEXT(onclick);
|
|
|
|
TEMPORARY_TEXT(details);
|
|
|
|
WRITE_TO(details, "alt=\"show\" id=\"plus%d\" src=\"", id);
|
|
|
|
HTMLUtilities::extra_icon(details, I"extra");
|
|
|
|
WRITE_TO(details, "\"");
|
|
|
|
HTML::tag(OUT, "img", details);
|
|
|
|
DISCARD_TEXT(details);
|
|
|
|
HTML_CLOSE("a");
|
|
|
|
WRITE(" ");
|
|
|
|
}
|
|
|
|
|
|
|
|
void HTMLUtilities::all_extras_link(OUTPUT_STREAM, text_stream *from) {
|
|
|
|
WRITE(" ");
|
|
|
|
TEMPORARY_TEXT(onclick);
|
|
|
|
WRITE_TO(onclick, "showExtra%S(); return false;", from);
|
|
|
|
HTML::begin_link_with_class_onclick(OUT, NULL, I"#", onclick);
|
|
|
|
DISCARD_TEXT(onclick);
|
|
|
|
TEMPORARY_TEXT(details);
|
|
|
|
WRITE_TO(details, "alt=\"show\" id=\"plus%S\" src=\"", from);
|
|
|
|
HTMLUtilities::extra_icon(details, I"extrab");
|
|
|
|
WRITE_TO(details, "\"");
|
|
|
|
HTML::tag(OUT, "img", details);
|
|
|
|
DISCARD_TEXT(details);
|
|
|
|
HTML_CLOSE("a");
|
|
|
|
}
|
|
|
|
|
|
|
|
@ These links call some standard Javascript functions, thus:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::write_javascript_for_buttons(OUTPUT_STREAM) {
|
|
|
|
WRITE(" function showExtra(id, imid) {\n");
|
|
|
|
WRITE(" if (document.getElementById(id).style.display == 'block') {\n");
|
|
|
|
WRITE(" document.getElementById(id).style.display = 'none';\n");
|
|
|
|
WRITE(" document.getElementById(imid).src = '");
|
|
|
|
HTMLUtilities::extra_icon(OUT, I"extra");
|
|
|
|
WRITE("';\n");
|
|
|
|
WRITE(" } else {\n");
|
|
|
|
WRITE(" document.getElementById(id).style.display = 'block';\n");
|
|
|
|
WRITE(" document.getElementById(imid).src = '");
|
|
|
|
HTMLUtilities::extra_icon(OUT, I"extraclose");
|
|
|
|
WRITE("';\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
WRITE(" function onLoaded() {\n");
|
|
|
|
WRITE(" if (window.location.hash) {\n");
|
|
|
|
WRITE(" var hash = window.location.hash.substring(2);\n");
|
|
|
|
WRITE(" if (hash.search(\"_\") >= 0) {\n");
|
|
|
|
WRITE(" var res = hash.split(\"_\");\n");
|
|
|
|
WRITE(" showExample(\"example\"+res[1]);\n");
|
|
|
|
WRITE(" } else {\n");
|
|
|
|
WRITE(" showExample(\"example\"+hash);\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
WRITE(" window.onload=onLoaded;\n");
|
|
|
|
WRITE(" function showExample(id) {\n");
|
|
|
|
WRITE(" if (document.getElementById(id).style.display == 'block') {\n");
|
|
|
|
WRITE(" document.getElementById(id).style.display = 'none';\n");
|
|
|
|
WRITE(" } else {\n");
|
|
|
|
WRITE(" document.getElementById(id).style.display = 'block';\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
WRITE(" function openExtra(id, imid) {\n");
|
|
|
|
WRITE(" document.getElementById(id).style.display = 'block';\n");
|
|
|
|
WRITE(" document.getElementById(imid).src = '");
|
|
|
|
HTMLUtilities::extra_icon(OUT, I"extraclose");
|
|
|
|
WRITE("';\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
WRITE(" function closeExtra(id, imid) {\n");
|
|
|
|
WRITE(" document.getElementById(id).style.display = 'none';\n");
|
|
|
|
WRITE(" document.getElementById(imid).src = '");
|
|
|
|
HTMLUtilities::extra_icon(OUT, I"extra");
|
|
|
|
WRITE("';\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
@ Where:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::extra_icon(OUTPUT_STREAM, text_stream *name) {
|
|
|
|
TEMPORARY_TEXT(img);
|
|
|
|
WRITE_TO(img, "%S.png", name);
|
|
|
|
HTMLUtilities::image_URL(OUT, img);
|
|
|
|
DISCARD_TEXT(img);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ And the following compiles a set of specific functions for numbered buttons,
|
|
|
|
used on the Contents page:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::write_javascript_for_contents_buttons(OUTPUT_STREAM) {
|
|
|
|
volume *V;
|
|
|
|
LOOP_OVER(V, volume) {
|
|
|
|
WRITE(" function showExtra%S() {\n", V->vol_abbrev);
|
|
|
|
WRITE(" if (document.getElementById('plus%S').src.indexOf('", V->vol_abbrev);
|
|
|
|
HTMLUtilities::extra_icon(OUT, I"extrab");
|
|
|
|
WRITE("') >= 0) {\n");
|
|
|
|
for (int i=0; i<V->vol_chapter_count; i++) {
|
|
|
|
int bn = (V->allocation_id)*1000+i;
|
|
|
|
WRITE(" openExtra('extra%d', 'plus%d');\n", bn, bn);
|
|
|
|
}
|
|
|
|
WRITE(" document.getElementById('plus%S').src = '", V->vol_abbrev);
|
|
|
|
HTMLUtilities::extra_icon(OUT, I"extracloseb");
|
|
|
|
WRITE("';\n");
|
|
|
|
WRITE(" } else {\n");
|
|
|
|
for (int i=0; i<V->vol_chapter_count; i++) {
|
|
|
|
int bn = (V->allocation_id)*1000+i;
|
|
|
|
WRITE(" closeExtra('extra%d', 'plus%d');\n", bn, bn);
|
|
|
|
}
|
|
|
|
WRITE(" document.getElementById('plus%S').src = '", V->vol_abbrev);
|
|
|
|
HTMLUtilities::extra_icon(OUT, I"extrab");
|
|
|
|
WRITE("';\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
WRITE(" }\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ JavaScript to handle the paste icon works differently on different platforms.
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::paste_script(OUTPUT_STREAM, text_stream *content, int code_num) {
|
|
|
|
HTML::open_javascript(OUT, FALSE);
|
|
|
|
WRITE("function pasteCode");
|
|
|
|
if (Str::len(content) == 0) WRITE("(code");
|
|
|
|
else WRITE("%d(code", code_num);
|
|
|
|
WRITE(") {\n");
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->javascript_paste_method == PASTEMODE_Andrew)
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE(" var myProject = window.Project;\n");
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->javascript_paste_method == PASTEMODE_David)
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE(" var myProject = external.Project;\n");
|
|
|
|
WRITE("\n");
|
|
|
|
WRITE(" myProject.selectView('source');\n");
|
|
|
|
WRITE(" myProject.pasteCode(");
|
|
|
|
if (Str::len(content) == 0) { WRITE("code"); }
|
|
|
|
else WRITE("'%S'", content);
|
|
|
|
WRITE(");\n}\n");
|
|
|
|
HTML::close_javascript(OUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HTMLUtilities::create_script(OUTPUT_STREAM, text_stream *content, int code_num, text_stream *titling) {
|
|
|
|
HTML::open_javascript(OUT, FALSE);
|
|
|
|
WRITE("function createNewProject");
|
|
|
|
if (Str::len(content) == 0) WRITE("(code");
|
|
|
|
else WRITE("%d(code", code_num);
|
|
|
|
WRITE(", title) {\n");
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->javascript_paste_method == PASTEMODE_Andrew)
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE(" var myProject = window.Project;\n");
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->javascript_paste_method == PASTEMODE_David)
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE(" var myProject = external.Project;\n");
|
|
|
|
|
|
|
|
WRITE("\n");
|
|
|
|
WRITE(" myProject.createNewProject(");
|
|
|
|
if (Str::len(content) == 0) WRITE("title");
|
|
|
|
else WRITE("'%S'", titling);
|
|
|
|
WRITE(", ");
|
|
|
|
if (Str::len(content) == 0) WRITE("code");
|
|
|
|
else WRITE("'%S'", content);
|
|
|
|
WRITE(");\n}\n");
|
|
|
|
HTML::close_javascript(OUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
@h Definitions.
|
|
|
|
Some nice little boxes for syntax definitions in computery manuals:
|
|
|
|
|
|
|
|
=
|
|
|
|
int no_definition_anchors = 0;
|
|
|
|
|
|
|
|
void HTMLUtilities::definition_box(OUTPUT_STREAM, text_stream *defn, text_stream *sigil, volume *V, section *S) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->format == HTML_FORMAT) {
|
2019-02-05 02:44:07 +02:00
|
|
|
TEMPORARY_TEXT(anchor);
|
|
|
|
WRITE_TO(anchor, "defn%d", no_definition_anchors++);
|
|
|
|
|
|
|
|
TEMPORARY_TEXT(comment);
|
|
|
|
WRITE_TO(comment, "START PHRASE \"%S\"", anchor);
|
|
|
|
HTML::comment(OUT, comment);
|
|
|
|
DISCARD_TEXT(comment);
|
|
|
|
|
|
|
|
HTML_OPEN_WITH("div", "class=\"definition\"");
|
|
|
|
HTML::anchor(OUT, anchor);
|
|
|
|
HTMLUtilities::defn_unpack(OUT, defn, V, S, anchor);
|
|
|
|
WRITE("\n");
|
|
|
|
DISCARD_TEXT(anchor);
|
|
|
|
|
|
|
|
HTML::comment(OUT, I"END PHRASE");
|
|
|
|
|
|
|
|
TEMPORARY_TEXT(defnhead);
|
|
|
|
WRITE_TO(defnhead, "definition of %S", sigil);
|
|
|
|
HTML::comment(OUT, defnhead);
|
|
|
|
DISCARD_TEXT(defnhead);
|
|
|
|
} else {
|
|
|
|
WRITE("PHRASE: %S\n", defn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HTMLUtilities::end_definition_box(OUTPUT_STREAM) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->format == HTML_FORMAT) {
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE("\n");
|
|
|
|
HTML::comment(OUT, I"end definition");
|
|
|
|
HTML_CLOSE("div");
|
|
|
|
} else WRITE("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
@ Though these are in fact quite a lot of trouble:
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::defn_unpack(OUTPUT_STREAM, text_stream *given_defn, volume *V, section *S, text_stream *anchor) {
|
|
|
|
match_results mr = Regexp::create_mr();
|
|
|
|
@<Given alternates divided by an ampersand, recurse to unpack each in turn@>;
|
|
|
|
|
|
|
|
TEMPORARY_TEXT(defn);
|
|
|
|
Str::copy(defn, given_defn);
|
|
|
|
|
|
|
|
TEMPORARY_TEXT(index_as);
|
|
|
|
@<Work out an index mark for this definition@>;
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->inform_definitions_mode) {
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Rewrite definition as an HTML paragraph of class defnprototype@>;
|
|
|
|
@<Set the kind of the result of the phrase in italic, not bold@>;
|
|
|
|
@<Specially set and specially index a phrase to decide a condition@>;
|
|
|
|
@<Specially set and specially index a text substitution@>;
|
|
|
|
@<Tidy up the definition paragraph@>;
|
|
|
|
WRITE("%S", defn);
|
|
|
|
} else {
|
|
|
|
HTML_OPEN_WITH("span", "class=\"definitionterm\"");
|
|
|
|
WRITE("%S", defn);
|
|
|
|
HTML_CLOSE("span");
|
|
|
|
}
|
|
|
|
Regexp::dispose_of(&mr);
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Given alternates divided by an ampersand, recurse to unpack each in turn@> =
|
|
|
|
if (Regexp::match(&mr, given_defn, L"(%c*?) & (%c*)")) {
|
|
|
|
HTMLUtilities::defn_unpack(OUT, mr.exp[0], V, S, anchor);
|
|
|
|
HTML_TAG("br");
|
|
|
|
WRITE("<i>or:</i>   ");
|
|
|
|
HTMLUtilities::defn_unpack(OUT, mr.exp[1], V, S, anchor);
|
|
|
|
Regexp::dispose_of(&mr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Work out an index mark for this definition@> =
|
|
|
|
Str::copy(index_as, given_defn);
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->inform_definitions_mode) Regexp::replace(index_as, L" ...%c*", NULL, 0);
|
2019-02-05 02:44:07 +02:00
|
|
|
Regexp::replace(index_as, L": *", NULL, 0);
|
|
|
|
WRITE_TO(index_as, "=___=!definition");
|
|
|
|
Indexes::mark_index_term(index_as, V, S, anchor, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
@<Rewrite definition as an HTML paragraph of class defnprototype@> =
|
|
|
|
TEMPORARY_TEXT(proto);
|
|
|
|
HTML::open(proto, "p", I"class='defnprototype'");
|
|
|
|
/* given alternative wordings, put only the first in boldface */
|
|
|
|
Regexp::replace(defn, L"(%i+?)/(%C+)", L"%0</b>/%1<b>", REP_REPEATING);
|
|
|
|
WRITE_TO(proto, "<b>%S</b>", defn);
|
|
|
|
HTML::close(proto, "p");
|
|
|
|
Str::copy(defn, proto);
|
|
|
|
|
|
|
|
@<Set the kind of the result of the phrase in italic, not bold@> =
|
|
|
|
if (Regexp::match(&mr, defn, L"(%c*) ... (%c*?)</b>")) {
|
|
|
|
WRITE_TO(defn, "%S</b> ... <i>", mr.exp[0]);
|
|
|
|
Regexp::replace(mr.exp[1], L"<b>", NULL, REP_REPEATING);
|
|
|
|
Regexp::replace(mr.exp[1], L"</b>", NULL, REP_REPEATING);
|
|
|
|
WRITE_TO(defn, "%S</i>", mr.exp[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Specially set and specially index a phrase to decide a condition@> =
|
|
|
|
if ((Regexp::match(&mr, defn, L"<b>if (%c*)")) &&
|
|
|
|
(Regexp::match(NULL, defn, L"%c*a condition%c*"))) {
|
|
|
|
WRITE_TO(defn, "<i>if</i> <b>%S", mr.exp[0]);
|
|
|
|
Regexp::replace(index_as, L"if ", NULL, REP_ATSTART);
|
|
|
|
text_stream *index_alph = NULL;
|
|
|
|
if (Regexp::match(&mr, index_as, L"%(%c*?%) (%c*)")) index_alph = mr.exp[0];
|
|
|
|
TEMPORARY_TEXT(term);
|
|
|
|
WRITE_TO(term, "%S=___=!if-definition", index_as);
|
|
|
|
Indexes::mark_index_term(term, V, S, anchor, NULL, NULL, index_alph);
|
|
|
|
DISCARD_TEXT(term);
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Specially set and specially index a text substitution@> =
|
|
|
|
if (Regexp::match(&mr, defn, L"<b>say \"%[(%c*)%]\"</b>")) {
|
|
|
|
WRITE_TO(defn, "say \"[<b>%S</b>]\"", mr.exp[0]);
|
|
|
|
Regexp::replace(index_as, L"say ", NULL, REP_ATSTART);
|
|
|
|
text_stream *index_alph = NULL;
|
|
|
|
if (Regexp::match(&mr, index_as, L"%[%(?:%(%c*?%) %)?(%c*)%]")) index_alph = mr.exp[0];
|
|
|
|
TEMPORARY_TEXT(term);
|
|
|
|
WRITE_TO(term, "%S=___=!say-definition", index_as);
|
|
|
|
Indexes::mark_index_term(term, V, S, anchor, NULL, NULL, index_alph);
|
|
|
|
DISCARD_TEXT(term);
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Tidy up the definition paragraph@> =
|
|
|
|
Regexp::replace(defn, L"<b></b>", NULL, REP_REPEATING);
|
|
|
|
Regexp::replace(defn, L"</b><b>", NULL, REP_REPEATING);
|
|
|
|
|
|
|
|
TEMPORARY_TEXT(star);
|
|
|
|
Str::copy(star, defn);
|
|
|
|
Str::clear(defn);
|
|
|
|
int bl = 0;
|
|
|
|
while (Regexp::match(&mr, star, L"(%c)(%c*)")) {
|
|
|
|
text_stream *ch = mr.exp[0]; Str::copy(star, mr.exp[1]);
|
|
|
|
if (Str::eq(ch, I"(")) { if (bl == 0) WRITE_TO(defn, "</b>"); bl++; }
|
|
|
|
WRITE_TO(defn, "%S", ch);
|
|
|
|
if (Str::eq(ch, I")")) { bl--; if (bl == 0) WRITE_TO(defn, "<b>"); }
|
|
|
|
}
|
|
|
|
DISCARD_TEXT(star);
|
|
|
|
|
|
|
|
@h Topping and tailing.
|
|
|
|
If we're preparing HTML for use in a website, we probably want our
|
|
|
|
documentation content to be just one |<div>| in a larger page whose
|
|
|
|
design |indoc| knows nothing about. The following therefore lets us
|
|
|
|
use an external file as the prototype, cutting it up into a head before
|
|
|
|
the text |[TEXT]| and a tail afterwards.
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::read_top_and_tail_matter(filename *F, int main_flag) {
|
|
|
|
tt_helper_state tths;
|
|
|
|
tths.pretext = TRUE;
|
|
|
|
if (main_flag) {
|
|
|
|
DSET_main_top_matter = Str::new();
|
|
|
|
DSET_main_tail_matter = Str::new();
|
|
|
|
tths.top_text = DSET_main_top_matter;
|
|
|
|
tths.tail_text = DSET_main_tail_matter;
|
|
|
|
} else {
|
|
|
|
DSET_section_top_matter = Str::new();
|
|
|
|
DSET_section_tail_matter = Str::new();
|
|
|
|
tths.top_text = DSET_section_top_matter;
|
|
|
|
tths.tail_text = DSET_section_tail_matter;
|
|
|
|
}
|
|
|
|
TextFiles::read(F, FALSE, "can't read top and tail file",
|
|
|
|
TRUE, HTMLUtilities::tt_helper, NULL, &tths);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ =
|
|
|
|
typedef struct tt_helper_state {
|
|
|
|
int pretext;
|
|
|
|
text_stream *top_text;
|
|
|
|
text_stream *tail_text;
|
|
|
|
} tt_helper_state;
|
|
|
|
|
|
|
|
void HTMLUtilities::tt_helper(text_stream *line, text_file_position *tfp, void *v_tths) {
|
|
|
|
tt_helper_state *tths = (tt_helper_state *) v_tths;
|
|
|
|
Str::trim_white_space_at_end(line);
|
|
|
|
match_results mr = Regexp::create_mr();
|
|
|
|
if (Regexp::match(&mr, line, L"(%c*?)%[TEXT%](%c*)")) {
|
|
|
|
WRITE_TO(tths->top_text, "%S", mr.exp[0]);
|
|
|
|
HTML::begin_div_with_id(tths->top_text, "bodytext");
|
|
|
|
HTML::end_div(tths->tail_text);
|
|
|
|
WRITE_TO(tths->tail_text, "%S\n", mr.exp[1]);
|
|
|
|
Regexp::dispose_of(&mr);
|
|
|
|
tths->pretext = FALSE;
|
|
|
|
} else {
|
|
|
|
if (tths->pretext) WRITE_TO(tths->top_text, "%S\n", line);
|
|
|
|
else WRITE_TO(tths->tail_text, "%S\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ And this routine caches requests for tops or tails.
|
|
|
|
|
|
|
|
=
|
|
|
|
void HTMLUtilities::get_tt_matter(OUTPUT_STREAM, int top, int main) {
|
|
|
|
if (first_request_for_tt) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (indoc_settings->top_and_tail_sections)
|
|
|
|
HTMLUtilities::read_top_and_tail_matter(indoc_settings->top_and_tail_sections, FALSE);
|
|
|
|
if (indoc_settings->top_and_tail)
|
|
|
|
HTMLUtilities::read_top_and_tail_matter(indoc_settings->top_and_tail, TRUE);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
first_request_for_tt = FALSE;
|
|
|
|
if ((top == 1) && (main == 1)) WRITE("%S", DSET_main_top_matter);
|
|
|
|
if ((top == 1) && (main == 0)) WRITE("%S", DSET_main_tail_matter);
|
|
|
|
if ((top == 0) && (main == 1)) WRITE("%S", DSET_section_top_matter);
|
|
|
|
if ((top == 0) && (main == 0)) WRITE("%S", DSET_section_tail_matter);
|
|
|
|
}
|