To provide routines to help build the various HTML index files, none of which are actually created in this section.


§1. If only we had an index file, we could look it up under "index file"...

The Index of a project is a set of HTML files describing its milieu: the definitions it makes, the world model resulting, and the rules in force, which arise as a combination of the source text and the extensions (in particular, the Standard Rules).

Each index page is divided into "elements".

typedef struct index_page {
    int no_elements;
    struct text_stream *key_colour;
    struct text_stream *page_title;
    struct text_stream *page_explanation;
    struct text_stream *page_leafname;
    CLASS_DEFINITION
} index_page;

§2.

typedef struct index_element {
    int atomic_number;  1, 2, 3, ..., within its page
    struct text_stream *chemical_symbol;
    struct text_stream *element_name;
    struct text_stream *explanatory_note;
    struct index_page *owning_page;
    CLASS_DEFINITION
} index_element;

§3. The index is written at the end of each successful compilation. During indexing, only one index file is ever open for output at a time: this always has the file handle ifl. The following routine is called to open a new index file for output.

index_page *current_index_page = NULL;

void Index::new_page(text_stream *col, text_stream *title, text_stream *exp, text_stream *leaf) {
    current_index_page = CREATE(index_page);
    current_index_page->no_elements = 0;
    current_index_page->key_colour = Str::duplicate(col);
    current_index_page->page_title = Str::duplicate(title);
    current_index_page->page_explanation = Str::duplicate(exp);
    current_index_page->page_leafname = Str::duplicate(leaf);
}

void Index::new_segment(text_stream *abb, text_stream *title, text_stream *explanation) {
    if (current_index_page == NULL)
        internal_error("template creates index elements improperly");
    if (Str::len(abb) > 2)
        internal_error("abbreviation for index element too long");
    index_element *ie = CREATE(index_element);
    ie->owning_page = current_index_page;
    ie->atomic_number = ++(current_index_page->no_elements);
    ie->chemical_symbol = Str::duplicate(abb);
    ie->element_name = Str::duplicate(title);
    ie->explanatory_note = Str::duplicate(explanation);
}

int index_file_counter = 0;
text_stream *ifl = NULL;  Current destination of index text
text_stream index_file_struct;  The current index file being written
text_stream *Index::open_file(text_stream *index_leaf, text_stream *title, int sub, text_stream *explanation) {
    filename *F = Task::index_file(index_leaf, sub);
    if (ifl) Index::close_index_file();
    if (STREAM_OPEN_TO_FILE(&index_file_struct, F, UTF8_ENC) == FALSE)
        Problems::fatal_on_file("Can't open index file", F);
    ifl = &index_file_struct;
    text_stream *OUT = ifl;

    Set the current index page3.1;

    HTML::header(OUT, title,
        Supervisor::file_from_installation(CSS_FOR_STANDARD_PAGES_IRES),
        Supervisor::file_from_installation(JAVASCRIPT_FOR_STANDARD_PAGES_IRES));
    index_file_counter++;
    if (Str::get_first_char(title) == '<') {
        Index::index_banner_line(OUT, 1, I"^", I"Details",
            I"A single action in detail.|About the action rulebooks<ARSUMMARY>",
            "../Actions.html");
        HTML_TAG("hr");
    } else Write the periodic table3.3;
    if ((Str::get_first_char(title) != '<') && (Str::eq_wide_string(index_leaf, L"Welcome.html") == FALSE))
        Write the index elements3.4;
    return OUT;
}

§3.1. Set the current index page3.1 =

    index_page *ip;
    LOOP_OVER(ip, index_page)
        if (ip->allocation_id == index_file_counter) {
            current_index_page = ip; break;
        }

§3.2. Writing the periodic table. Each index page is arranged as follows: at the top is a navigational bar in a DIV called "periodictable", and underneath that are chunks of actual indexing material in DIVs called "element1", "element2", ... Different pages have different numbers of elements.

The periodic table is shown complete on the Welcome page, but otherwise is reduced to just the relevant row (orbital?).

We will call the table opened if the surround is fully visible, in which case the actual content is hidden but the 8 rows of the table are all visible; and closed if the surround is hidden, so that only 1 row of the table is visible, and some or all of the content of the page is visible below.

Each row, except the top one, begins with a rectangle called the sidebar. If the table is closed then clicking on the sidebar opens it; if it's open, clicking on the sidebar for a given row closes the table and goes to the relevant page of the index. The contents of the sidebar cell are defined by a DIV whose class is "sidebar".

The sidebar is followed by a series of cells called the "boxes", one for each element in that row's index page. These contain DIVs with the class "box" and the ID "boxN_M", where N is the row number, 1 to 7, and M is the column number, 1 to E, where E is the number of elements in that row. Each box then contains three pieces of text: an abbreviation like Kd, in a DIV with class "symbol"; a spelled-out name like Kinds, in a DIV with class "rubric"; and an element number like 3, in a DIV with class "indexno".

Following that is a broad cell, spanning the rest of the table's width, which contains text like "Kinds Index". This contains a DIV of class "headingbox", inside which is a main heading in a DIV of class "headingtext" and text underneath in another of class "headingrubric".

§3.3. So let's generate all of that:

Write the periodic table3.3 =

    int max_elements = 0;
    index_page *ip;
    LOOP_OVER(ip, index_page)
        if (max_elements < ip->no_elements)
            max_elements = ip->no_elements;

    HTML_OPEN_WITH("div", "id=\"periodictable\"");
    HTML_OPEN_WITH("table", "cellspacing=\"3\" border=\"0\" width=\"100%%\"");
    if (Str::eq_wide_string(index_leaf, L"Welcome.html"))
        Write the heading row of the surround3.3.1;
    LOOP_OVER(ip, index_page)
        if (((Str::eq_wide_string(index_leaf, L"Welcome.html")) || (ip == current_index_page)) &&
            (Str::eq_wide_string(ip->page_leafname, L"Welcome") == FALSE)) {
            Start a row of the periodic table3.3.2;
            index_element *ie;
            LOOP_OVER(ie, index_element)
                if (ie->owning_page == ip)
                    Write an element-box of the periodic table3.3.3;
            End a row of the periodic table3.3.4;
        }
    HTML_CLOSE("table");
    HTML_CLOSE("div");

§3.3.1. Write the heading row of the surround3.3.1 =

    HTML_OPEN_WITH("tr", "id=\"surround0\"");
    HTML_OPEN("td");
    HTML_CLOSE("td");
    HTML_OPEN_WITH("td", "colspan=\"2\"");
    HTML_TAG_WITH("img", "src='inform:/doc_images/index@2x.png' border=1 width=115 height=115");
    HTML_CLOSE("td");
    HTML_OPEN_WITH("td", "colspan=\"%d\" style=\"width:100%%;\"", max_elements - 1);
    HTML_OPEN_WITH("div", "class=\"headingboxhigh\"");
    HTML_OPEN_WITH("div", "class=\"headingtext\"");
    WRITE("Welcome to the Index");
    HTML_CLOSE("div");
    HTML_OPEN_WITH("div", "class=\"headingrubric\"");
    WRITE("A guide which grows with your project");
    HTML_CLOSE("div");
    HTML_CLOSE("div");
    HTML_CLOSE("td");
    HTML_CLOSE("tr");

§3.3.2. Start a row of the periodic table3.3.2 =

    if (Str::eq_wide_string(index_leaf, L"Welcome.html")) {
        HTML_OPEN("tr");
        HTML_OPEN_WITH("td", "onclick=\"window.location='%S.html'; return false;\"",
            ip->page_leafname);
        HTML_OPEN_WITH("div", "class=\"sidebar\"");
        HTML_CLOSE("div");
        HTML_CLOSE("td");
    } else {
        HTML_OPEN_WITH("tr", "id=\"surround%d\"", ip->allocation_id+1);
        HTML_OPEN_WITH("td", "onclick=\"window.location='Welcome.html'; return false;\"");
        HTML_OPEN_WITH("div", "class=\"sidebar\"");
        HTML_CLOSE("div");
        HTML_CLOSE("td");
    }

§3.3.3. Write an element-box of the periodic table3.3.3 =

    if (ip == current_index_page) {
        HTML_OPEN_WITH("td", "onclick=\"click_element_box('segment%d'); return false;\"",
            ie->atomic_number);
    } else {
        HTML_OPEN_WITH("td", "onclick=\"window.location='%S.html?segment%d'; return false;\"",
            ip->page_leafname, ie->atomic_number);
    }
    HTML_OPEN_WITH("div", "id=\"box%d_%d\" class=\"box\"", ip->allocation_id+1, ie->atomic_number);
    HTML_OPEN_WITH("a", "class=\"symbol\" title=\"%S\" href=\"#\"", ie->element_name);
    WRITE("%S", ie->chemical_symbol);
    HTML_CLOSE("a"); WRITE("\n");
    HTML_OPEN_WITH("div", "class=\"indexno\"");
    WRITE("%d", ie->atomic_number);
    HTML_CLOSE("div");
    HTML_OPEN_WITH("div", "class=\"rubric\"");
    WRITE("%S", ie->element_name);
    HTML_CLOSE("div");
    HTML_CLOSE("div");
    HTML_CLOSE("td");

§3.3.4. End a row of the periodic table3.3.4 =

    TEMPORARY_TEXT(tds)
    if (ip == current_index_page) {
        WRITE_TO(tds, "onclick=\"show_all_elements(); return false;\" ");
    } else {
        WRITE_TO(tds, "onclick=\"window.location='%S.html'; return false;\" ",
            ip->page_leafname);
    }
    if (ip->no_elements < max_elements)
        WRITE_TO(tds, "colspan=\"%d\" ", max_elements - ip->no_elements + 1);
    WRITE_TO(tds, "style=\"width:100%%\"");
    HTML_OPEN_WITH("td", "%S", tds);
    DISCARD_TEXT(tds)
    WRITE("\n");

    HTML_OPEN_WITH("div", "class=\"headingbox\"");
    HTML_OPEN_WITH("div", "class=\"headingtext\"");
    WRITE("%S", ip->page_title);
    HTML_CLOSE("div");
    HTML_OPEN_WITH("div", "class=\"headingrubric\"");
    WRITE("%S", ip->page_explanation);
    HTML_CLOSE("div");
    HTML_CLOSE("div");

    HTML_CLOSE("td");
    HTML_CLOSE("tr");

§3.4. Writing the elements. Each element is contained inside a DIV with id "segment1", "segment2", and so on. There's then a banner line — a sort of subheading; then the index content at last; and then a rule.

Write the index elements3.4 =

    index_element *ie;
    LOOP_OVER(ie, index_element)
        if (ie->owning_page == current_index_page) {
            HTML_OPEN_WITH("div", "id=\"segment%d\"", ie->atomic_number);
            HTML_TAG("hr");
            Index::index_banner_line(OUT, ie->atomic_number, ie->chemical_symbol,
                ie->element_name, ie->explanatory_note, NULL);
            index_page *save_ip = current_index_page;
            Index::index_actual_element(OUT, ie->chemical_symbol);
            current_index_page = save_ip;
            HTML_CLOSE("div");
        }
    HTML_TAG("hr");

§4. This is abstracted as a routine because it's also used for the much smaller and simpler navigation on the Actions detail pages.

void Index::index_banner_line(OUTPUT_STREAM, int N, text_stream *sym, text_stream *name, text_stream *exp, char *link) {
    HTML_OPEN_WITH("table", "cellspacing=\"3\" border=\"0\" style=\"background:#eeeeee;\"");
    HTML_OPEN("tr");
    Write the banner mini-element-box4.1;
    Write the row titling element4.2;
    HTML_CLOSE("tr");
    HTML_CLOSE("table");
    WRITE("\n");
}

§4.1. Write the banner mini-element-box4.1 =

    HTML_OPEN_WITH("td", "valign=\"top\" align=\"left\"");
    HTML_OPEN_WITH("div", "id=\"minibox%d_%d\" class=\"smallbox\"",
        current_index_page->allocation_id+1, N);
    TEMPORARY_TEXT(dets)
    WRITE_TO(dets, "class=\"symbol\" title=\"%S\" ", name);
    if (link) WRITE_TO(dets, "href=\"%s\"", link);
    else WRITE_TO(dets, "href=\"#\" onclick=\"click_element_box('segment%d'); return false;\"", N);
    HTML_OPEN_WITH("a", "%S", dets);
    DISCARD_TEXT(dets)
    WRITE("%S", sym);
    HTML_CLOSE("a");
    HTML_OPEN_WITH("div", "class=\"indexno\"");
    WRITE("%d\n", N);
    HTML_CLOSE("div");
    HTML_CLOSE("div");
    HTML_CLOSE("td");

§4.2. Write the row titling element4.2 =

    HTML_OPEN_WITH("td", "style=\"width:100%%;\" align=\"left\" valign=\"top\"");
    HTML_OPEN_WITH("p", "style=\"margin-top:0px;padding-top:0px;"
        "margin-bottom:0px;padding-bottom:0px;line-height:150%%;\"");
    WRITE("<b>%S</b> &mdash; \n", name);
    Index::explain(OUT, exp);
    HTML_CLOSE("p");
    HTML_CLOSE("td");

§5. So here goes with the CSS and Javascript.

define ADDITIONAL_SCRIPTING_HTML_CALLBACK Index::scripting
void Index::scripting(OUTPUT_STREAM) {
    if (current_index_page == NULL) return;

    HTML_OPEN_WITH("style", "type=\"text/css\" media=\"screen, print\"");
    Write some CSS styles for all these classes5.1;
    HTML_CLOSE("style");

    HTML_OPEN_WITH("script", "type=\"text/javascript\"");
    WRITE("var qq; window.onload = function() {\n");
    WRITE("    if (location.search.length > 0) {\n");
    WRITE("        qq = location.search.substring(1, location.search.length);\n");
    WRITE("        show_only_one_element(qq);\n");
    WRITE("    }\n");
    WRITE("}\n");
    Write Javascript code for clicking on an element box5.3;
    Write Javascript code for clicking on the sidebar5.4;

    Write Javascript code for showing every element on the page5.5;
    Write Javascript code for showing only one element on the page5.6;
    Write Javascript code for entering the periodic table display5.7;

    Write Javascript code for showing and hiding a single element5.8;
    Write Javascript code for lighting up or greying down an element box5.9;
    HTML_CLOSE("script");
}

§5.1. Write some CSS styles for all these classes5.1 =

    WRITE("p {\n");
    WRITE("font-family: \"Lucida Grande\", \"Lucida Sans Unicode\", Helvetica, Arial, Verdana, sans-serif;\n");
    WRITE("}\n");
    WRITE("\n");
    WRITE(".box a:link { text-decoration: none; }\n");
    WRITE(".box a:visited { text-decoration: none; }\n");
    WRITE(".box a:active { text-decoration: none; }\n");
    WRITE(".box a:hover { text-decoration: none; color: #444444; }\n");
    WRITE("\n");
    WRITE(".smallbox a:link { text-decoration: none; }\n");
    WRITE(".smallbox a:visited { text-decoration: none; }\n");
    WRITE(".smallbox a:active { text-decoration: none; }\n");
    WRITE(".smallbox a:hover { text-decoration: none; color: #444444; }\n");
    WRITE("\n");
    WRITE(".symbol {\n");
    WRITE(" position: absolute;\n");
    WRITE(" top: -4px;\n");
    WRITE(" left: -1px;\n");
    WRITE(" width: 100%%;\n");
    WRITE(" color: #ffffff;\n");
    WRITE(" padding: 14px 0px 14px 1px;\n");
    WRITE(" font-size: 20px;\n");
    WRITE(" font-weight: bold;\n");
    WRITE(" text-align: center;\n");
    WRITE("}\n");
    WRITE(".indexno {\n");
    WRITE(" position: absolute;\n");
    WRITE(" top: 1px;\n");
    WRITE(" left: 3px;\n");
    WRITE(" color: #ffffff;\n");
    WRITE(" font-size: 7pt;\n");
    WRITE(" text-align: left;\n");
    WRITE("}\n");
    WRITE(".rubric {\n");
    WRITE(" position: absolute;\n");
    WRITE(" top: 35px;\n");
    WRITE(" width: 100%%;\n");
    WRITE(" color: #ffffff;\n");
    WRITE(" font-size: 9px;\n");
    WRITE(" font-weight: bold;\n");
    WRITE(" text-align: center;\n");
    WRITE("}\n");
    WRITE("\n");
    WRITE(".box {\n");
    WRITE(" position: relative;\n");
    WRITE(" height: 56px;\n");
    WRITE(" width: 56px;\n");
    WRITE(" padding: 0px;\n");
    WRITE("font-family: \"Lucida Grande\", \"Lucida Sans Unicode\", Helvetica, Arial, Verdana, sans-serif;\n");
    WRITE("-webkit-font-smoothing: antialiased;\n");
    WRITE("}\n");
    WRITE(".sidebar {\n");
    WRITE(" height: 56px;\n");
    WRITE(" width: 16px;\n");
    WRITE(" background: #888;\n");
    WRITE("font-family: \"Lucida Grande\", \"Lucida Sans Unicode\", Helvetica, Arial, Verdana, sans-serif;\n");
    WRITE("-webkit-font-smoothing: antialiased;\n");
    WRITE("}\n");
    WRITE(".sidebar:hover { background: #222; }\n");
    WRITE("\n");
    WRITE(".smallbox {\n");
    WRITE(" position: relative;\n");
    WRITE(" height: 40px;\n");
    WRITE(" width: 40px;\n");
    WRITE(" padding: 0px;\n");
    WRITE("font-family: \"Lucida Grande\", \"Lucida Sans Unicode\", Helvetica, Arial, Verdana, sans-serif;\n");
    WRITE("-webkit-font-smoothing: antialiased;\n");
    WRITE("}\n");
    WRITE("\n");
    index_page *ip;
    LOOP_OVER(ip, index_page) {
        index_element *ie;
        LOOP_OVER(ie, index_element)
            if (ie->owning_page == ip) {
                WRITE("#box%d_%d {\n", ip->allocation_id+1, ie->atomic_number);
                WRITE(" background: #%S;\n", ip->key_colour);
                WRITE(" }\n");
                WRITE("#minibox%d_%d {\n", ip->allocation_id+1, ie->atomic_number);
                WRITE(" background: #%S;\n", ip->key_colour);
                WRITE(" }\n");
            }
    }
    WRITE("\n");

    WRITE("ul.leaders {\n");
    WRITE("    padding: 0;\n");
    WRITE("    margin-top: 1px;\n");
    WRITE("    margin-bottom: 0;\n");
    WRITE("    overflow-x: hidden;\n");
    WRITE("    list-style: none}\n");
    WRITE("ul.leaders li.leaded:before {\n");
    WRITE("    float: left;\n");
    WRITE("    width: 0;\n");
    WRITE("    white-space: nowrap;\n");
    WRITE("    content:\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"\n");
    WRITE(" \".  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \"}\n");
    WRITE("ul.leaders li.leaded span:first-child {\n");
    WRITE("    padding-right: 0.33em;\n");
    WRITE("    background: white}\n");
    WRITE("ul.leaders li.leaded span + span {\n");
    WRITE("    float: right;\n");
    WRITE("    padding-left: 0.33em;\n");
    WRITE("    background: white}\n");
    int i;
    for (i=1; i<10; i++) {
        WRITE("li.indent%d span:first-child {\n", i);
        WRITE("    padding-left: %dpx;\n", 25*i);
        WRITE("}\n");
    }
    WRITE("\n");
    WRITE("li.unleaded:before {\n");
    WRITE(" content: \"\";\n");
    WRITE("}\n");

§5.2. Now we come to the Javascript. The page can be in one of three states:

The page loads in state (1). Note that on a page with just one element, states (1) and (2) are indistinguishable.

We'll structure the Javascript routines on three levels. At the top level, we have routines called when buttons on the page are clicked:

§5.3. This is called when the user clicks on an element box corresponding to something on the current page. If that's hidden, we go to state (2) for the element clicked on. If it's showing, we see which state we're in: if we're in state (2) we go to state (1), and otherwise go to state (2). (The trick is deciding what state we're in: we do that by counting the number of visible elements.)

Write Javascript code for clicking on an element box5.3 =

    WRITE("function click_element_box(id) {\n");
    WRITE("    if (document.getElementById(id).style.display == 'none') {\n");
    WRITE("        show_only_one_element(id);\n");
    WRITE("    } else {\n");
    WRITE("        var x = 0;\n");
    int i;
    for (i=1; i<=current_index_page->no_elements; i++)
        WRITE("        if (document.getElementById('segment%d').style.display == '') { x++; }\n", i);
    WRITE("        if (x == 1) { show_all_elements(); }\n");
    WRITE("        else { show_only_one_element(id); }\n");
    WRITE("    }\n");
    WRITE("}\n");

§5.4. If we're in state (1) or (2), go to state (3); if we're in state (3), go to state (1).

Write Javascript code for clicking on the sidebar5.4 =

    WRITE("function click_sidebar() {\n");
    WRITE("    if (document.getElementById('surround0').style.display == 'none') {\n");
    WRITE("        enter_periodic_table();\n");
    WRITE("    } else {\n");
    WRITE("        show_all_elements();\n");
    WRITE("    }\n");
    WRITE("}\n");

§5.5. At the middle level of our Javascript, we have routines which move the page to a new state. This routine goes to state (1):

Write Javascript code for showing every element on the page5.5 =

    WRITE("function show_all_elements() {\n");
    for (int i=1; i<=current_index_page->no_elements; i++) {
        WRITE("    show_element('segment%d');\n", i);
        WRITE("    light_up('segment%d');\n", i);
    }
    WRITE(" }\n");

§5.6. This routine goes to state (2), where the id is the ID of the content element — segment1, segment2, ...

Write Javascript code for showing only one element on the page5.6 =

    WRITE("function show_only_one_element(id) {\n");
    for (int i=1; i<=current_index_page->no_elements; i++) {
        WRITE("    hide_element('segment%d');\n", i);
        WRITE("    light_down('segment%d');\n", i);
    }
    WRITE("    show_element(id);\n");
    WRITE("    light_up(id);\n");
    WRITE("}\n");

§5.7. This routine goes to state (3):

Write Javascript code for entering the periodic table display5.7 =

    WRITE("function enter_periodic_table() {\n");
    for (int i=1; i<=current_index_page->no_elements; i++) {
        WRITE("    hide_element('segment%d');\n", i);
        WRITE("    light_up('segment%d');\n", i);
    }
    WRITE("}\n");

§5.8. And at the bottom level of the Javascript code we have service routines to show, hide and colour things:

Write Javascript code for showing and hiding a single element5.8 =

    WRITE("function show_element(id) {\n");
    WRITE("    document.getElementById(id).style.display = '';\n");
    WRITE("}\n");
    WRITE("function hide_element(id) {\n");
    WRITE("    document.getElementById(id).style.display = 'none';\n");
    WRITE("}\n");

§5.9. Write Javascript code for lighting up or greying down an element box5.9 =

    WRITE("function light_up(id) {\n");
    Write Javascript to produce the corresponding icon name5.9.1;
    WRITE("    document.getElementById(ic).style.background = '#%S';\n",
        current_index_page->key_colour);
    WRITE("}\n");
    WRITE("function light_down(id) {\n");
    Write Javascript to produce the corresponding icon name5.9.1;
    WRITE("    document.getElementById(ic).style.background = '#cccccc';\n");
    WRITE("}\n");

§5.9.1. Write Javascript to produce the corresponding icon name5.9.1 =

    WRITE("    var ic = 'box%d_1';\n", current_index_page->allocation_id+1);
    for (int i=2; i<=current_index_page->no_elements; i++)
        WRITE("    if (id == 'segment%d') { ic = 'box%d_%d';}\n",
            i, current_index_page->allocation_id+1, i);

§6.

int do_not_update_census = FALSE;  Set by the -no-update-census command line option
void Index::disable_or_enable_census(int which) {
    do_not_update_census = which;
}

void Index::index_actual_element(OUTPUT_STREAM, text_stream *elt) {
    if (Str::eq_wide_string(elt, L"C")) {
        IndexHeadings::index(OUT);
        IndexExtensions::index(OUT);
        if (do_not_update_census == FALSE)
            ExtensionWebsite::index_after_compilation(Task::project());
        return;
    }
    if (Str::eq_wide_string(elt, L"Vl")) {
        IXVariables::index_all(OUT);
        Equations::index(OUT);
        return;
    }
    if (Str::eq_wide_string(elt, L"Fi")) {
        IXFigures::index_all(OUT);
        IXSounds::index_all(OUT);
        IXExternalFiles::index_all(OUT);
        return;
    }
    if (Str::eq_wide_string(elt, L"Tb")) {
        Tables::index(OUT);
        return;
    }
    if (Str::eq_wide_string(elt, L"Cd")) {
        #ifdef IF_MODULE
        IXBibliographicData::Library_Card(OUT);
        #endif
        return;
    }
    if (Str::eq_wide_string(elt, L"In")) {
        Index::innards(OUT, Supervisor::current_vm());
        return;
    }

    if (Str::eq_wide_string(elt, L"Ph")) {
        Phrases::Index::index_page_Phrasebook(OUT);
        return;
    }
    if (Str::eq_wide_string(elt, L"Lx")) {
        IndexLexicon::index(OUT);
        return;
    }
    if (Str::eq_wide_string(elt, L"Rl")) {
        IXRelations::index_table(OUT);
        return;
    }
    if (Str::eq_wide_string(elt, L"Vb")) {
        IndexLexicon::index_verbs(OUT);
        return;
    }

    if (Str::eq_wide_string(elt, L"Ch")) {
        Data::Objects::page_Kinds(OUT);
        Kinds::Index::index_kinds(OUT, 2);
        return;
    }
    if (Str::eq_wide_string(elt, L"Ar")) {
        Kinds::Dimensions::index_dimensional_rules(OUT);
        return;
    }

    if (Str::eq_wide_string(elt, L"Mp")) {
        Data::Objects::page_World(OUT);
        return;
    }
    if (Str::eq_wide_string(elt, L"Gz")) {
        IndexLexicon::index_common_nouns(OUT);
        return;
    }

    if (Str::eq_wide_string(elt, L"Pl")) {
        #ifdef IF_MODULE
        IXScenes::index(OUT);
        #endif
        return;
    }
    if (Str::eq_wide_string(elt, L"Ev")) {
        IXRules::index_timed_rules(OUT);  rules which happen at set times of day
        return;
    }
    if (Str::eq_wide_string(elt, L"RS")) {
        #ifdef IF_MODULE
        IXScenes::index_rules(OUT);
        #endif
        return;
    }

    if (Str::eq_wide_string(elt, L"St")) {
        IXRules::Rules_page(OUT, 1);
        return;
    }
    if (Str::eq_wide_string(elt, L"Xt")) {
        IXRules::Rules_page(OUT, 2);
        return;
    }

    if (Str::eq_wide_string(elt, L"A1")) {
        #ifdef IF_MODULE
        CommandsIndex::page(OUT);
        #endif
        return;
    }
    if (Str::eq_wide_string(elt, L"Bh")) {
        #ifdef IF_MODULE
        IXActions::index_named_patterns(OUT);
        #endif
        return;
    }
    if (Str::eq_wide_string(elt, L"Cm")) {
        #ifdef IF_MODULE
        CommandsIndex::commands(OUT);
        #endif
        return;
    }
    if (Str::eq_wide_string(elt, L"To")) {
        #ifdef IF_MODULE
        CommandsIndex::tokens(OUT);
        #endif
        return;
    }
    if (Str::eq_wide_string(elt, L"A2")) {
        #ifdef IF_MODULE
        CommandsIndex::alphabetical(OUT);
        #endif
        return;
    }
    HTML_OPEN("p"); WRITE("NO CONTENT"); HTML_CLOSE("p");
}

§7.

void Index::explain(OUTPUT_STREAM, text_stream *explanation) {
    int italics_open = FALSE;
    for (int i=0, L=Str::len(explanation); i<L; i++) {
        switch (Str::get_at(explanation, i)) {
            case '|':
                HTML_TAG("br");
                WRITE("<i>"); italics_open = TRUE; break;
            case '<': {
                TEMPORARY_TEXT(link)
                WRITE("&nbsp;");
                i++;
                while ((i<L) && (Str::get_at(explanation, i) != '>'))
                    PUT_TO(link, Str::get_at(explanation, i++));
                Index::DocReferences::link(OUT, link);
                DISCARD_TEXT(link)
                break;
            }
            case '[': {
                TEMPORARY_TEXT(link)
                WRITE("&nbsp;");
                i++;
                while ((i<L) && (Str::get_at(explanation, i) != '>'))
                    PUT_TO(link, Str::get_at(explanation, i++));
                Index::below_link(OUT, link);
                DISCARD_TEXT(link)
                break;
            }
            default: WRITE("%c", Str::get_at(explanation, i)); break;
        }
    }
    if (italics_open) WRITE("</i>");
}

§8. Written index files are closed either when the next one is opened (see above), or when the .i6t interpreter signals the end of indexing by calling Index::complete below.

void Index::complete(void) {
    if (ifl) Index::close_index_file();
    #ifdef IF_MODULE
    CommandsIndex::detail_pages();
    #endif
}

void Index::close_index_file(void) {
    if (ifl == NULL) return;
    HTML::footer(ifl);
    STREAM_CLOSE(ifl); ifl = NULL;
}

§9. Links to source. When index files need to reference source text material, they normally do so by means of orange back-arrow icons which are linked to positions in the source as typed by the user. But source text also comes from extensions. We don't want to provide source links to those, because they can't easily be opened in the Inform application (on some platforms, anyway), and in any case, can't easily be modified (or should not be, anyway). Instead, we produce links.

So, then, source links are omitted if the reference is to a location in the Standard Rules; if it is to an extension other than that, the link is made to the documentation for the extension; and otherwise we make a link to the source text in the application.

void Index::link(OUTPUT_STREAM, int wn) {
    Index::link_to_location(OUT, Lexer::word_location(wn), TRUE);
}

void Index::link_location(OUTPUT_STREAM, source_location sl) {
    Index::link_to_location(OUT, sl, TRUE);
}

void Index::link_to(OUTPUT_STREAM, int wn, int nonbreaking_space) {
    Index::link_to_location(OUT, Lexer::word_location(wn), nonbreaking_space);
}

void Index::link_to_location(OUTPUT_STREAM, source_location sl, int nonbreaking_space) {
    inform_extension *E = Extensions::corresponding_to(sl.file_of_origin);
    if (E) {
        if (Extensions::is_standard(E) == FALSE) {
            if (nonbreaking_space) WRITE("&nbsp;"); else WRITE(" ");
            Works::begin_extension_link(OUT, E->as_copy->edition->work, NULL);
            HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/Revealext.png");
            Works::end_extension_link(OUT, E->as_copy->edition->work);
        }
        return;
    }
    SourceLinks::link(OUT, sl, nonbreaking_space);
}

§10. Links to detail pages. The "Beneath" icon is used for links to details pages seen as beneath the current index page: for instance, for the link from the Actions page to the page about the taking action.

void Index::detail_link(OUTPUT_STREAM, char *stub, int sub, int down) {
    WRITE("&nbsp;");
    HTML_OPEN_WITH("a", "href=%s%d_%s.html", (down)?"Details/":"", sub, stub);
    HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/Beneath.png");
    HTML_CLOSE("a");
}

§11. "See below" links. These are the grey magnifying glass icons. The links are done by internal href links to anchors lower down the same HTML page. These can be identified either by number, or by name: whichever is more convenient for the indexing code.

void Index::below_link(OUTPUT_STREAM, text_stream *p) {
    WRITE("&nbsp;");
    HTML_OPEN_WITH("a", "href=#%S", p);
    HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/Below.png");
    HTML_CLOSE("a");
}

void Index::anchor(OUTPUT_STREAM, text_stream *p) {
    HTML_OPEN_WITH("a", "name=%S", p); HTML_CLOSE("a");
}

void Index::below_link_numbered(OUTPUT_STREAM, int n) {
    WRITE("&nbsp;");
    HTML_OPEN_WITH("a", "href=#A%d", n);
    HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/Below.png");
    HTML_CLOSE("a");
}

void Index::anchor_numbered(OUTPUT_STREAM, int n) {
    HTML_OPEN_WITH("a", "name=A%d", n); HTML_CLOSE("a");
}

§12. "Show extra" links, and also a spacer of equivalent width.

void Index::extra_link(OUTPUT_STREAM, int id) {
    HTML_OPEN_WITH("a", "href=\"#\" onclick=\"showExtra('extra%d', 'plus%d'); return false;\"", id, id);
    HTML_TAG_WITH("img", "border=0 id=\"plus%d\" src=inform:/doc_images/extra.png", id);
    HTML_CLOSE("a");
    WRITE("&nbsp;");
}

void Index::extra_link_with(OUTPUT_STREAM, int id, char *icon) {
    if (id == 2000000) {
        HTML_OPEN_WITH("a", "href=\"#\" onclick=\"showAllResp(%d); return false;\"", NUMBER_CREATED(rule));
        HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/%s.png", icon);
    } else {
        HTML_OPEN_WITH("a", "href=\"#\" onclick=\"showResp('extra%d', 'plus%d'); return false;\"", id, id);
        HTML_TAG_WITH("img", "border=0 id=\"plus%d\" src=inform:/doc_images/%s.png", id, icon);
    }
    HTML_CLOSE("a");
    WRITE("&nbsp;");
}

void Index::noextra_link(OUTPUT_STREAM) {
    HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/noextra.png");
    WRITE("&nbsp;");
}

§13. These open up divisions:

void Index::extra_div_open(OUTPUT_STREAM, int id, int indent, char *colour) {
    HTML_OPEN_WITH("div", "id=\"extra%d\" style=\"display: none;\"", id);
    HTML::open_indented_p(OUT, indent, "");
    HTML::open_coloured_box(OUT, colour, ROUND_BOX_TOP+ROUND_BOX_BOTTOM);
}

void Index::extra_div_close(OUTPUT_STREAM, char *colour) {
    HTML::close_coloured_box(OUT, colour, ROUND_BOX_TOP+ROUND_BOX_BOTTOM);
    HTML_CLOSE("p");
    HTML_CLOSE("div");
}

void Index::extra_div_open_nested(OUTPUT_STREAM, int id, int indent) {
    HTML_OPEN_WITH("div", "id=\"extra%d\" style=\"display: none;\"", id);
    HTML::open_indented_p(OUT, indent, "");
}

void Index::extra_div_close_nested(OUTPUT_STREAM) {
    HTML_CLOSE("p");
    HTML_CLOSE("div");
}

§14. "Deprecation" icons.

void Index::deprecation_icon(OUTPUT_STREAM, int id) {
    HTML_OPEN_WITH("a", "href=\"#\" onclick=\"showExtra('extra%d', 'plus%d'); return false;\"", id, id);
    HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/deprecated.png");
    HTML_CLOSE("a");
    WRITE("&nbsp;");
}

§15. Miscellaneous utilities. First: to print a double-quoted word into the index, without its surrounding quotes.

void Index::dequote(OUTPUT_STREAM, wchar_t *p) {
    int i = 1;
    if ((p[0] == 0) || (p[1] == 0)) return;
    for (i=1; p[i+1]; i++) {
        int c = p[i];
        switch(c) {
            case '"': WRITE("&quot;"); break;
            default: PUT_TO(OUT, c); break;
        }
    }
}

§16. Describing the current VM.

void Index::innards(OUTPUT_STREAM, target_vm *VM) {
    Index::index_VM(OUT, VM);
    NewUseOptions::index(OUT);
    HTML_OPEN("p");
    Index::extra_link(OUT, 3);
    WRITE("See some technicalities for Inform maintainers only");
    HTML_CLOSE("p");
    Index::extra_div_open(OUT, 3, 2, "e0e0e0");
    Index::show_configuration(OUT);
    Add some paste buttons for the debugging log16.1;
    Index::extra_div_close(OUT, "e0e0e0");
}

§16.1. The index provides some hidden paste icons for these:

Add some paste buttons for the debugging log16.1 =

    HTML_OPEN("p");
    WRITE("Debugging log:");
    HTML_CLOSE("p");
    HTML_OPEN("p");
    for (int i=0; i<NO_DEFINED_DA_VALUES; i++) {
        debugging_aspect *da = &(the_debugging_aspects[i]);
        if (Str::len(da->unhyphenated_name) > 0) {
            TEMPORARY_TEXT(is)
            WRITE_TO(is, "Include %S in the debugging log.", da->unhyphenated_name);
            PasteButtons::paste_text(OUT, is);
            WRITE("&nbsp;%S", is);
            DISCARD_TEXT(is)
            HTML_TAG("br");
        }
    }
    HTML_CLOSE("p");

§17.

void Index::index_VM(OUTPUT_STREAM, target_vm *VM) {
    if (VM == NULL) internal_error("target VM not set yet");
    Index::anchor(OUT, I"STORYFILE");
    HTML_OPEN("p"); WRITE("Story file format: ");
    ExtensionIndex::plot_icon(OUT, VM);
    TargetVMs::write(OUT, VM);
    HTML_CLOSE("p");
}

§18.

void Index::show_configuration(OUTPUT_STREAM) {
    HTML_OPEN("p");
    Index::anchor(OUT, I"CONFIG");
    WRITE("Inform language definition:\n");
    PluginManager::list_plugins(OUT, "Included", TRUE);
    PluginManager::list_plugins(OUT, "Excluded", FALSE);
    HTML_CLOSE("p");
}