Some conveniences shared by our different forms of index.


§1. A temporary measure.

define LETTER_ALPHABETIZATION 1
define WORD_ALPHABETIZATION 2
define WRAPPER_none 1
define WRAPPER_epub 2
define WRAPPER_zip 3
int indoc_settings_test_index_mode = FALSE;
int indoc_settings_index_alphabetisation_algorithm = LETTER_ALPHABETIZATION;
int indoc_settings_navigation_simplified_letter_rows = FALSE;
int indoc_settings_wrapper = WRAPPER_none;

§2.

void IndexUtilities::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);
}

§3.

void IndexUtilities::escape_HTML_characters_in(text_stream *text) {
    TEMPORARY_TEXT(modified)
    for (int i=0, L=Str::len(text); i<L; i++) {
        inchar32_t c = Str::get_at(text, i);
        switch (c) {
            case '\"':      WRITE_TO(modified, "&quot;"); break;
            case '<':       WRITE_TO(modified, "&lt;"); break;
            case '>':       WRITE_TO(modified, "&gt;"); break;
            case '&':
                if (Str::get_at(text, i+1) == '#') { PUT_TO(modified, c); break; }
                int j = i+1;
                while (Characters::isalnum(Str::get_at(text, j))) j++;
                if ((j > i+1) && (Str::get_at(text, j) == ';')) { PUT_TO(modified, c); break; }
                WRITE_TO(modified, "&amp;");
                break;
            default:        PUT_TO(modified, c); break;
        }
    }
    Str::copy(text, modified);
    DISCARD_TEXT(modified)
}

§4. Alphabetisation. We flatten the casing and remove the singular articles; we count small numbers as words, so that "3 Wise Monkeys" is filed as if it were "Three Wise Monkeys"; with parts of multipart examples, such as "Disappointment Bay 3", we insert a 0 before the 3 so that up to 99 parts can appear and alphabetical sorting will agree with numerical.

dictionary *alphabetisation_exceptions = NULL;  hash of lemmas with unusual alphabetisations

void IndexUtilities::alphabetisation_exception(text_stream *term, text_stream *alphabetise_as) {
    if (alphabetisation_exceptions == NULL)
        alphabetisation_exceptions = Dictionaries::new(100, TRUE);
    text_stream *val = Dictionaries::create_text(alphabetisation_exceptions, term);
    Str::copy(val, alphabetise_as);
}

void IndexUtilities::improve_alphabetisation(text_stream *sort_key) {
    text_stream *alph = Dictionaries::get_text(alphabetisation_exceptions, sort_key);
    if (Str::len(alph) > 0) {
        Str::copy(sort_key, alph);
        LOOP_THROUGH_TEXT(pos, sort_key)
            Str::put(pos, Characters::tolower(Str::get(pos)));
    } else {
        LOOP_THROUGH_TEXT(pos, sort_key)
            Str::put(pos, Characters::tolower(Str::get(pos)));
        Regexp::replace(sort_key, U"a ", NULL, REP_ATSTART);
        Regexp::replace(sort_key, U"an ", NULL, REP_ATSTART);
        Regexp::replace(sort_key, U"the ", NULL, REP_ATSTART);
        LOOP_THROUGH_TEXT(pos, sort_key)
            Str::put(pos, Characters::tolower(Characters::remove_accent(Str::get(pos))));
        Regexp::replace(sort_key, U"%[ *%]", U"____SQUARES____", REP_REPEATING);
        Regexp::replace(sort_key, U"%[", NULL, REP_REPEATING);
        Regexp::replace(sort_key, U"%]", NULL, REP_REPEATING);
        Regexp::replace(sort_key, U"____SQUARES____", U"[]", REP_REPEATING);
        Regexp::replace(sort_key, U"%(", NULL, REP_REPEATING);
        Regexp::replace(sort_key, U"%)", NULL, REP_REPEATING);
        Regexp::replace(sort_key, U"1 ", U"one ", REP_ATSTART);
        Regexp::replace(sort_key, U"2 ", U"two ", REP_ATSTART);
        Regexp::replace(sort_key, U"3 ", U"three ", REP_ATSTART);
        Regexp::replace(sort_key, U"4 ", U"four ", REP_ATSTART);
        Regexp::replace(sort_key, U"5 ", U"five ", REP_ATSTART);
        Regexp::replace(sort_key, U"6 ", U"six ", REP_ATSTART);
        Regexp::replace(sort_key, U"7 ", U"seven ", REP_ATSTART);
        Regexp::replace(sort_key, U"8 ", U"eight ", REP_ATSTART);
        Regexp::replace(sort_key, U"9 ", U"nine ", REP_ATSTART);
        Regexp::replace(sort_key, U"10 ", U"ten ", REP_ATSTART);
        Regexp::replace(sort_key, U"11 ", U"eleven ", REP_ATSTART);
        Regexp::replace(sort_key, U"12 ", U"twelve ", REP_ATSTART);
        TEMPORARY_TEXT(x)
        Str::copy(x, sort_key);
        Str::clear(sort_key);
        match_results mr = Regexp::create_mr();
        while (Regexp::match(&mr, x, U"(%c*?)(%d+)(%c*)")) {
            WRITE_TO(sort_key, "%S", mr.exp[0]);
            Str::copy(x, mr.exp[2]);
            WRITE_TO(sort_key, "%08d", Str::atoi(mr.exp[1], 0));
        }
        WRITE_TO(sort_key, "%S", x);
        DISCARD_TEXT(x)
    }
}

§5.

int letters_taken[26];
void IndexUtilities::note_letter(inchar32_t c) {
    inchar32_t i = c - (inchar32_t) 'A';
    if (i<26) letters_taken[i] = TRUE;
}
void IndexUtilities::alphabet_row(OUTPUT_STREAM, int sequence) {
    switch (sequence) {
        case 1:
            for (int i=0; i<26; i++)
                letters_taken[i] = FALSE;
            break;
        case 2: {
            int faked = FALSE;
            for (int i=0; i<26; i++)
                if (letters_taken[i] == FALSE) {
                    if (faked == FALSE) { faked = TRUE; HTML_OPEN("p"); }
                    TEMPORARY_TEXT(singleton)
                    PUT_TO(singleton, (inchar32_t) ('A'+i));
                    HTML::anchor(OUT, singleton);
                    DISCARD_TEXT(singleton)
                }
            if (faked) { HTML_CLOSE("p"); }
            break;
        }
    }
    if (indoc_settings_navigation_simplified_letter_rows) {
        HTML_OPEN("p");
    } else {
        HTML_OPEN_WITH("table", "class=\"fullwidth\"");
        HTML_OPEN("tr");
        HTML_OPEN_WITH("td", "class=\"letterinrow\"");
    }
    IndexUtilities::general_link(OUT, I"letterlink", I"#A", I"A"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#B", I"B"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#C", I"C"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#D", I"D"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#E", I"E"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#F", I"F"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#G", I"G"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#H", I"H"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#I", I"I"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#J", I"J"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#K", I"K"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#L", I"L"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#M", I"M"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#N", I"N"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#O", I"O"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#P", I"P"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#Q", I"Q"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#R", I"R"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#S", I"S"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#T", I"T"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#U", I"U"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#V", I"V"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#W", I"W"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#X", I"X"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#Y", I"Y"); Between5.1;
    IndexUtilities::general_link(OUT, I"letterlink", I"#Z", I"Z");
    if (indoc_settings_navigation_simplified_letter_rows) {
        HTML_CLOSE("p");
    } else {
        HTML_CLOSE("td");
        HTML_CLOSE("tr");
        HTML_CLOSE("table");
    }
}

§5.1. Between5.1 =

    if (indoc_settings_navigation_simplified_letter_rows) WRITE(" / ");
    else {
        HTML_CLOSE("td");
        HTML_OPEN_WITH("td", "class=\"letterinrow\"");
    }

§6. This is mainly used for the typographically dramatic link letters A, B, C, ... but can also make fatter typographically dramatic headings, if it's stretched in width and a longer text is supplied.

void IndexUtilities::majuscule_heading(OUTPUT_STREAM, text_stream *display_text, int single_letter) {
    if (indoc_settings_navigation_simplified_letter_rows) {
        if (single_letter == 1) {
            HTML::begin_div_with_class_S(OUT, I"majuscule", __FILE__, __LINE__);
        } else {
            HTML::begin_div_with_class_S(OUT, I"stretchymajuscule", __FILE__, __LINE__);
        }
        HTML_OPEN_WITH("span", "class=\"majusculelettering\"");
        WRITE("%S", display_text);
        HTML_CLOSE("span");
        HTML::end_div(OUT);
    } else {
        WRITE("<b>%S</b>", display_text);
    }
}