1#############################################################################
2##
3#W  GAPDoc2HTML.gi                 GAPDoc                        Frank Lübeck
4##
5##
6#Y  Copyright (C)  2000,  Frank Lübeck,  Lehrstuhl D für Mathematik,
7#Y  RWTH Aachen
8##
9##  The  files GAPDoc2HTML.g{d,i}  contain  a  conversion program  which
10##  produces from a  GAPDoc XML-document an HTML version for reading the
11##  document with a Web-browser.
12##
13
14##  REMARKS:
15##
16##  We add to all  nodes of the parse tree an entry  .root which points to
17##  the document root.  The toc-, index- and  bib-information is collected
18##  in the root.
19##
20##  The set  of elements  is partitioned  into two  subsets -  those which
21##  contain whole paragraphs and those which don't.
22##
23##  The     handler    of   a   paragraph     containing    element   (see
24##  GAPDoc2HTMLProcs.ParEls below)  gets a  list as  argument to  which it
25##  adds entries pairwise:   the first of such a   pair is  the  paragraph
26##  counter (like [3,2,1,5] meaning Chap.3,   Sec.2, Subsec.1, Par.5)  and
27##  the second is the formatted text of this paragraph.
28##
29##  Some   handlers  of paragraph   containing  elements do the formatting
30##  themselves (e.g., .List), the others are handled in the main recursion
31##  function `GAPDoc2HTMLContent'.
32##
33##  We produce  a full version of  the document in HTML  format, including
34##  title  page,  abstract and  other  front  matter, table  of  contents,
35##  bibliography (via  BibTeX-data files) and  index. For this we  have to
36##  process a document twice (similar to LaTeX).
37##
38
39##  Small utility to throw away SGML markup
40BindGlobal("FilterSGMLMarkup", function(str)
41  local p2, p1, res;
42  p2 := Position(str, '<');
43  if p2 = fail then
44    return str;
45  fi;
46  p1 := 0;
47  res := "";
48  while p2 <> fail do
49    Append(res, str{[p1+1..p2-1]});
50    p1 := Position(str, '>', p2);
51    if p1 = fail then
52      return res;
53    fi;
54    p2 := Position(str, '<', p1);
55    if p2 = fail then
56      Append(res, str{[p1+1..Length(str)]});
57      return res;
58    fi;
59  od;
60end);
61
62
63
64InstallValue(GAPDoc2HTMLProcs, rec());
65
66##  Some text attributes ([begin, end] pairs)
67GAPDoc2HTMLProcs.TextAttr := rec();
68GAPDoc2HTMLProcs.TextAttr.Heading := ["<span class=\"Heading\">", "</span>"];
69
70GAPDoc2HTMLProcs.TextAttr.Func := ["<code class=\"func\">", "</code>"];
71GAPDoc2HTMLProcs.TextAttr.Arg := ["<var class=\"Arg\">", "</var>"];
72GAPDoc2HTMLProcs.TextAttr.Example := ["<div class=\"Example\">", "</div>"];
73GAPDoc2HTMLProcs.TextAttr.Package := ["<strong class=\"pkg\">", "</strong>"];
74GAPDoc2HTMLProcs.TextAttr.URL := ["<span class=\"URL\">", "</span>"];
75GAPDoc2HTMLProcs.TextAttr.Mark := ["<strong class=\"Mark\">", "</strong>"];
76
77GAPDoc2HTMLProcs.TextAttr.K := ["<code class=\"keyw\">", "</code>"];
78GAPDoc2HTMLProcs.TextAttr.C := ["<code class=\"code\">", "</code>"];
79GAPDoc2HTMLProcs.TextAttr.F := ["<code class=\"file\">", "</code>"];
80GAPDoc2HTMLProcs.TextAttr.I := ["<code class=\"i\">", "</code>"];
81GAPDoc2HTMLProcs.TextAttr.B := ["<strong class=\"button\">", "</strong>"];
82GAPDoc2HTMLProcs.TextAttr.Emph := ["<em>", "</em>"];
83GAPDoc2HTMLProcs.TextAttr.Ref := ["<span class=\"RefLink\">", "</span>"];
84GAPDoc2HTMLProcs.TextAttr.M := ["<span class=\"SimpleMath\">", "</span>"];
85GAPDoc2HTMLProcs.TextAttr.Math := ["<span class=\"Math\">", "</span>"];
86GAPDoc2HTMLProcs.TextAttr.GAPprompt :=
87                                ["<span class=\"GAPprompt\">", "</span>"];
88GAPDoc2HTMLProcs.TextAttr.GAPbrkprompt :=
89                                ["<span class=\"GAPbrkprompt\">", "</span>"];
90GAPDoc2HTMLProcs.TextAttr.GAPinput :=
91                                ["<span class=\"GAPinput\">", "</span>"];
92
93# like in Text converter, but a heading and an address are not a paragraph here
94GAPDoc2HTMLProcs.ParEls :=
95[ "Display", "Example", "Log", "Listing", "List", "Enum", "Item", "Table",
96  "TitlePage", "Abstract", "Copyright", "Acknowledgements",
97  "Colophon", "TableOfContents", "Bibliography", "TheIndex",
98  "Subsection", "ManSection", "Description", "Returns", "Section",
99  "Chapter", "Appendix", "Body", "Book", "WHOLEDOCUMENT", "Attr", "Fam",
100  "Filt", "Func", "InfoClass", "Meth", "Oper", "Constr", "Prop", "Var", "Verb"];
101
102##  arg: a list of strings
103##  for now only ??????
104SetGapDocHTMLOptions := function(arg)
105  local   gdp;
106  gdp := GAPDoc2HTMLProcs;
107  return;
108end;
109
110GAPDoc2HTMLProcs.Head1 := "\
111<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
112\n\
113<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n\
114         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\
115\n\
116<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n\
117<head>\n\
118<title>GAP (";
119
120GAPDoc2HTMLProcs.MathJaxURL := "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
121
122GAPDoc2HTMLProcs.Head1MathJax := "\
123<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
124\n\
125<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n\
126         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\
127\n\
128<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n\
129<head>\n\
130<script type=\"text/javascript\"\n\
131  src=\"MATHJAXURL\">\n\
132</script>\n\
133<title>GAP (";
134
135GAPDoc2HTMLProcs.Head1Trans := "\
136<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
137\n\
138<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\
139         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\
140\n\
141<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\
142<head>\n\
143<title>GAP (";
144
145GAPDoc2HTMLProcs.Head1MML := "\
146<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
147<?xml-stylesheet type=\"text/xsl\" href=\"mathml.xsl\"?>\n\
148\n\
149<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\"\n\
150       \"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd\" [\n\
151       <!ENTITY mathml \"http://www.w3.org/1998/Math/MathML\">\n\
152       ] >\n\
153\n\
154<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n\
155<head>\n\
156<link rel=\"stylesheet\" type=\"text/css\" href=\"mathml.css\" />\n\
157<title>GAP (";
158
159GAPDoc2HTMLProcs.Head2 := "\
160</title>\n\
161<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />\n\
162<meta name=\"generator\" content=\"GAPDoc2HTML\" />\n\
163<link rel=\"stylesheet\" type=\"text/css\" href=\"manual.css\" />\n\
164<script src=\"manual.js\" type=\"text/javascript\"></script>\n\
165<script type=\"text/javascript\">overwriteStyle();</script>\n\
166</head>\n<body onload=\"jscontent()\">\n";
167
168GAPDoc2HTMLProcs.Tail := "\n\
169<hr />\n\
170<p class=\"foot\">generated by <a \
171href=\"http://www.math.rwth-aachen.de/~Frank.Luebeck/GAPDoc\">GAPDoc2HTML\
172</a></p>\
173\n</body>\n</html>\n";
174
175GAPDoc2HTMLProcs.PutFilesTogether := function(l, r)
176  local   files,  n,  tt,  i, chnrs, chlink, prev, next, toplink;
177
178  chnrs := Set(List([2,4..Length(l)], i-> l[i-1][1]));
179  chnrs := Concatenation(Filtered(chnrs, a-> not a in ["Bib", "Ind"]),
180                         Filtered(chnrs, a-> a in ["Bib", "Ind"]));
181  chlink := Concatenation("\n<div class=\"chlinktop\"><span class=\"chlink1\">",
182            GAPDocTexts.d.GotoChapter, ": </span>");
183  for n in chnrs do
184    Append(chlink, Concatenation("<a href=\"chap", String(n),
185           GAPDoc2HTMLProcs.ext, "\">"));
186    if n = 0 then
187      Append(chlink, GAPDocTexts.d.Top);
188    else
189      Append(chlink, String(n));
190    fi;
191    Append(chlink,"</a>  ");
192  od;
193  Append(chlink, "</div>\n");
194
195  toplink := Concatenation( "&nbsp;<a href=\"chap0", GAPDoc2HTMLProcs.ext,
196             "\">[", GAPDocTexts.d.TopofBook, "]</a>&nbsp;  ",
197             "<a href=\"chap0", GAPDoc2HTMLProcs.ext, "#contents",
198             "\">[", GAPDocTexts.d.Contents, "]</a>&nbsp;  " );
199  prev := [];
200  next := [];
201  for i in [1..Length(chnrs)] do
202    if i > 1 then
203      Add(prev, Concatenation("&nbsp;<a href=\"chap", String(chnrs[i-1]),
204                  GAPDoc2HTMLProcs.ext, "\">[", GAPDocTexts.d.PreviousChapter,
205                  "]</a>&nbsp;  "));
206    else
207      Add(prev, "");
208    fi;
209    if i < Length(chnrs) then
210      Add(next, Concatenation("&nbsp;<a href=\"chap", String(chnrs[i+1]),
211                        GAPDoc2HTMLProcs.ext, "\">[", GAPDocTexts.d.NextChapter,
212                        "]</a>&nbsp;  "));
213    else
214      Add(next, "");
215    fi;
216  od;
217  # putting the paragraphs together (one string (file) for each chapter)
218  files := rec();
219  for i in [1..Length(chnrs)] do
220    n := chnrs[i];
221    if r.root.mathmode = "MathML" then
222      # this MathML is no longer documented
223      files.(n) := rec(text :=
224                   ShallowCopy(GAPDoc2HTMLProcs.Head1MML), ssnr := []);
225    elif r.root.mathmode = "Tth" then
226      files.(n) := rec(text :=
227                   ShallowCopy(GAPDoc2HTMLProcs.Head1Trans), ssnr := []);
228    elif r.root.mathmode = "MathJax" then
229      files.(n) := rec(text := SubstitutionSublist(
230                               GAPDoc2HTMLProcs.Head1MathJax, "MATHJAXURL",
231                               GAPDoc2HTMLProcs.MathJaxURL), ssnr := []);
232    else
233      files.(n) := rec(text := ShallowCopy(GAPDoc2HTMLProcs.Head1), ssnr := []);
234    fi;
235    tt := Concatenation(r.bookname, ") - ");
236    if n=0 then
237      Append(tt, GAPDocTexts.d.Contents);
238    elif IsInt(n) then
239      Append(tt, Concatenation(GAPDocTexts.d.Chapter, " ", String(n), ": ",
240             FilterSGMLMarkup(r.chaptitle.(n))));
241    elif n="Bib" then
242      Append(tt, GAPDocTexts.d.References);
243    elif n="Ind" then
244      Append(tt, GAPDocTexts.d.Index);
245    else
246      Append(tt, Concatenation(GAPDocTexts.d.Appendix, " ", n, ": ",
247             FilterSGMLMarkup(r.chaptitle.(n))));
248    fi;
249    Append(files.(n).text, tt);
250    Append(files.(n).text, GAPDoc2HTMLProcs.Head2);
251    # allow for chapter-wise CSS config
252    files.(n).text := SubstitutionSublist(files.(n).text, "<body",
253                        Concatenation("<body class=\"chap", String(n), "\" "));
254    Append(files.(n).text, Concatenation("\n", chlink));
255    Append(files.(n).text, Concatenation(
256           "\n<div class=\"chlinkprevnexttop\">",
257           toplink, prev[i], next[i], "</div>\n\n"));
258    if IsBound(r.root.LinkToMathJax) then
259      # cross link to same chapter with MathJax enabled
260      Append(files.(n).text,
261                Concatenation("<p id=\"mathjaxlink\" ",
262                "class=\"pcenter\"><a href=\"chap",
263                String(n), "_mj.html\">[MathJax on]</a></p>\n"));
264    elif r.root.mathmode = "MathJax" then
265      # cross link to non-MathJax version
266      Append(files.(n).text,
267                Concatenation("<p id=\"mathjaxlink\" ",
268                "class=\"pcenter\"><a href=\"chap",
269                String(n), ".html\">[MathJax off]</a></p>\n"));
270    else
271      # we still want the hook for the [Style] link
272      Append(files.(n).text,
273                Concatenation("<p id=\"mathjaxlink\" ",
274                "class=\"pcenter\"></p>\n"));
275    fi;
276  od;
277  for i in [2,4..Length(l)] do
278    n := files.(l[i-1][1]);
279    if Length(n.ssnr)=0 or l[i-1]{[1..3]} <> n.ssnr[Length(n.ssnr)] then
280      Add(n.ssnr, l[i-1]{[1..3]});
281      tt := GAPDoc2HTMLProcs.SectionLabel(r, l[i-1], "Subsection")[2];
282      Append(n.text, Concatenation("<p><a id=\"", tt, "\" name=\"", tt,
283                                   "\"></a></p>\n"));
284    fi;
285
286    Append(n.text, l[i]);
287  od;
288
289  for i in [1..Length(chnrs)] do
290    n := chnrs[i];
291    Append(files.(n).text, Concatenation(
292           "\n<div class=\"chlinkprevnextbot\">",
293           toplink, prev[i], next[i], "</div>\n\n"));
294    Append(files.(n).text,  SubstitutionSublist(chlink, "chlinktop",
295                                                      "chlinkbot", false));
296    Append(files.(n).text, GAPDoc2HTMLProcs.Tail);
297  od;
298  # finally tell result the file extensions
299  files.ext := GAPDoc2HTMLProcs.ext;
300  return files;
301end;
302
303##
304##  <#GAPDoc Label="GAPDoc2HTML">
305##  <ManSection >
306##  <Func Arg="tree[, bibpath[, gaproot]][, mtrans]" Name="GAPDoc2HTML" />
307##  <Returns>record  containing  HTML  files  as  strings  and  other
308##  information</Returns>
309##  <Description>
310##  <Index Key="MathJax"><Package>MathJax</Package></Index>
311##  The   argument  <A>tree</A>   for   this  function   is  a   tree
312##  describing  a   &GAPDoc;  XML   document  as  returned   by  <Ref
313##  Func="ParseTreeXMLString"  /> (probably  also  checked with  <Ref
314##  Func="CheckAndCleanGapDocTree"  />).   Without  an  <A>mtrans</A>
315##  argument this function  produces an HTML version  of the document
316##  which can  be read  with any  Web-browser and  also be  used with
317##  &GAP;'s online help (see <Ref BookName="Ref" Func="SetHelpViewer"
318##  />).  It  includes  title  page,  bibliography,  and  index.  The
319##  bibliography is made from &BibTeX; databases. Their location must
320##  be given with the argument <A>bibpath</A> (as string or directory
321##  object, if not given the current directory is used). If the third
322##  argument <A>gaproot</A> is given and is a string then this string
323##  is  interpreted as relative  path to &GAP;'s main root directory.
324##  Reference-URLs to external HTML-books  which begin with the &GAP;
325##  root path  are then  rewritten to start  with the  given relative
326##  path.  This  makes  the HTML-documentation  portable  provided  a
327##  package is  installed in some  standard location below  the &GAP;
328##  root.<P/>
329##
330##  The  output is  a  record  with one  component  for each  chapter
331##  (with  names   <C>"0"</C>,  <C>"1"</C>,  ...,   <C>"Bib"</C>, and
332##  <C>"Ind"</C>).  Each  such  component   is again  a  record  with
333##  the following components:
334##
335##  <List >
336##  <Mark><C>text</C></Mark>
337##  <Item>the text of an HTML file containing the whole chapter (as a
338##  string)</Item>
339##  <Mark><C>ssnr</C></Mark>
340##  <Item>list of subsection numbers in  this chapter (like <C>[3, 2,
341##  1]</C>  for  chapter&nbsp;3,  section&nbsp;2,  subsection&nbsp;1)
342##  </Item>
343##  </List>
344##
345##  <Emph>Standard output format without</Emph> <A>mtrans</A>
346##  <Emph>argument</Emph><P/>
347##
348##  The   HTML   code   produced   with   this   converter   conforms
349##  to   the  W3C   specification   <Q>XHTML   1.0  strict</Q>,   see
350##  <URL>http://www.w3.org/TR/xhtml1</URL>.  First, this  means  that
351##  the   HTML   files   are   valid   XML   files.   Secondly,   the
352##  extension  <Q>strict</Q>   says  in  particular  that   the  code
353##  doesn't  contain  any explicit  font  or  color information.<P/>
354##
355##  Mathematical  formulae  are  handled  as in  the  text  converter
356##  <Ref  Func="GAPDoc2Text"/>.  We don't  want  to  assume that  the
357##  browser can  use symbol  fonts. Some &GAP;  users like  to browse
358##  the  online  help  with   <C>lynx</C>,  see  <Ref  BookName="Ref"
359##  Func="SetHelpViewer"  />, which  runs  inside  the same  terminal
360##  windows as &GAP;.<P/>
361##
362##  To view the generated files in graphical browsers, stylesheet files
363##  with layout configuration should be copied into the directory
364##  with the generated HTML files, see <Ref Subsect="StyleSheets"/>.
365##  <P/>
366##
367##  <Label Name="mtransarg"/>
368##  <Emph>Output format with</Emph> <A>mtrans</A> argument <P/>
369##
370##  Currently, there  are three variants of  this converter available
371##  which handle mathematical formulae differently. They are accessed
372##  via the optional last <A>mtrans</A> argument.<P/>
373##
374##  If  <A>mtrans</A>   is  set  to  <C>"MathJax"</C>   the  formulae
375##  are  essentially  translated  as  for  &LaTeX;  documents  (there
376##  is  no  processing  of   <C>&lt;M&gt;</C>  elements  as  decribed
377##  in  <Ref   Subsect="M"/>).  Inline  formulae  are   delimited  by
378##  <C>\(</C>  and  <C>\)</C>  and displayed  formulae  by  <C>\[</C>
379##  and    <C>\]</C>.   With    <Package>MathJax</Package>   webpages
380##  can   contain   nicely    formatted   scalable   and   searchable
381##  formulae.   The  resulting   files  link   by  default   to  <URL
382##  Text="http://cdn.mathjax.org">http://cdn.mathjax.org</URL> to get
383##  the  <Package>MathJax</Package>  script  and  fonts.  This  means
384##  that  they   can  only  be   used  on  computers   with  internet
385##  access.   An  alternative   URL   can  be   set  by   overwriting
386##  <C>GAPDoc2HTMLProcs.MathJaxURL</C>   before   building  the  HTML
387##  version   of   a   manual.   This  way   a   local   installation
388##  of   <Package>MathJax</Package>   could   be   used.   See   <URL
389##  Text="http://www.mathjax.org/">http://www.mathjax.org/</URL>  for
390##  more details.<P/>
391##
392##  The following  possibilities for <A>mtrans</A> are  still supported,
393##  but since the <Package>MathJax</Package> approach seems much better,
394##  their use is deprecated.<P/>
395##
396##  If  the  argument <A>mtrans</A>  is  set  to <C>"Tth"</C>  it  is
397##  assumed that you  have installed the &LaTeX;  to HTML translation
398##  program <C>tth</C>. This is used to translate the contents of the
399##  <C>M</C>,  <C>Math</C>  and  <C>Display</C>  elements  into  HTML
400##  code.  Note that  the resulting  code is  not compliant  with any
401##  standard.  Formally  it  is  <Q>XHTML  1.0  Transitional</Q>,  it
402##  contains  explicit  font  specifications and  the  characters  of
403##  mathematical  symbols  are  included  via  their  position  in  a
404##  <Q>Symbol</Q>  font. Some  graphical browsers  can be  configured
405##  to  display  this  in  a  useful  manner,  check  <URL  Text="the
406##  Tth homepage">http://hutchinson.belmont.ma.us/tth/</URL> for more
407##  details.<P/>
408##
409##  This  function works  by  running recursively  through the  document
410##  tree   and   calling   a   handler  function   for   each   &GAPDoc;
411##  XML   element.  Many   of  these   handler  functions   (usually  in
412##  <C>GAPDoc2TextProcs.&lt;ElementName&gt;</C>)  are  not  difficult to
413##  understand (the  greatest complications are some  commands for index
414##  entries, labels  or the  output of page  number information).  So it
415##  should be easy to adjust certain details to your own taste by slight
416##  modifications of the program. <P/>
417##
418##  The result  of this converter  can be  written to files  with the
419##  command <Ref Func="GAPDoc2HTMLPrintHTMLFiles" />.<P/>
420##
421##  There are two user preferences for reading the HTML manuals produced by
422##  &GAPDoc;. A user can choose among several style files which determine the
423##  appearance of the manual pages with
424##  <C>SetUserPreference("GAPDoc", "HTMLStyle", [...]);</C> where the list in
425##  the third argument are arguments for <Ref Func="SetGAPDocHTMLStyle"/>.
426##  The second preference is set by
427##  <C>SetUserPreference("GAPDoc", "UseMathJax", ...);</C> where the third
428##  argument is <K>true</K> or <K>false</K> (default). If this is set to
429##  <K>true</K>, the &GAP; help system displays the <Package>MathJax</Package>
430##  version of the HTML manuals.
431##  </Description>
432##  </ManSection>
433##  <#/GAPDoc>
434##
435##  <#GAPDoc Label="HTMLStyleSheets">
436##  <Subsection Label="StyleSheets">
437##  <Heading>Stylesheet files</Heading>
438##  <Index>CSS stylesheets</Index>
439##
440##  For graphical  browsers the layout  of the generated HTML manuals  can be
441##  highly  configured  by  cascading stylesheet  (CSS)  and  javascript
442##  files. Such files are provided in the <F>styles</F> directory of the
443##  &GAPDoc; package.<P/>
444##
445##  We recommend that these files  are copied into each manual directory
446##  (such  that each  of  them  is selfcontained).  There  is a  utility
447##  function  <Ref  Func="CopyHTMLStyleFiles"  /> which  does  this.  Of
448##  course, these files  may be changed or new styles  may be added. New
449##  styles  may  also be  sent  to  the  &GAPDoc; authors  for  possible
450##  inclusion in future versions.<P/>
451##
452##  The  generated   HTML  files refer to  the   file  <F>manual.css</F>
453##  which   conforms   to   the   W3C   specification   CSS   2.0,   see
454##  <URL>http://www.w3.org/TR/REC-CSS2</URL>,  and  the javascript  file
455##  <F>manual.js</F> (only in browsers  which support CSS or javascript,
456##  respectively;  but   the  HTML  files  are   also  readable  without
457##  any  of  them).  To  add  a style  <C>mystyle</C>  one  or  both  of
458##  <F>mystyle.css</F> and <F>mystyle.js</F> must be provided; these can
459##  overwrite  default settings  and add  new javascript  functions. For
460##  more details see the comments in <F>manual.js</F>.<P/>
461##  </Subsection>
462##  <ManSection >
463##  <Func Arg="dir" Name="CopyHTMLStyleFiles" />
464##  <Returns>nothing</Returns>
465##  <Description>
466##  This utility function copies  the <F>*.css</F> and <F>*.js</F> files
467##  from the  <F>styles</F> directory of  the &GAPDoc; package  into the
468##  directory
469##  <A>dir</A>.
470##  </Description>
471##  </ManSection>
472##  <#/GAPDoc>
473##
474##  the basic call, used recursively with a result r from GetElement
475##  and a string str or list l to which the output should be appended
476# arg: r[, bibpath]    (then a list is returned, only for whole document)
477# or:  r, str          (then the output is appended to string or list str)
478InstallGlobalFunction(GAPDoc2HTML, function(arg)
479  local   r,  str,  linelength,  name;
480  r := arg[1];
481  # first check for the mode
482  if arg[Length(arg)] in ["MathML", "Tth", "MathJax"] then
483    r.mathmode := arg[Length(arg)];
484    arg := arg{[1..Length(arg)-1]};
485  else
486    r.mathmode := "Text";
487  fi;
488
489  if Length(arg) > 1 then
490    str := arg[2];
491  else
492    str := [];
493  fi;
494  if r.name = "WHOLEDOCUMENT" then
495    # choose different file name conventions such that these
496    # conversions can coexist
497    if r.mathmode = "MathML" then
498      GAPDoc2HTMLProcs.ext := "_mml.xml";
499    elif r.mathmode = "Tth" then
500      GAPDoc2HTMLProcs.ext := "_sym.html";
501    elif r.mathmode = "MathJax" then
502      GAPDoc2HTMLProcs.ext := "_mj.html";
503    else
504      GAPDoc2HTMLProcs.ext := ".html";
505    fi;
506    if IsDirectory(str) then
507      r.bibpath := str;
508    else
509      if Length(str) = 0 then
510        str := ".";
511      fi;
512      r.bibpath := Directory(str);
513    fi;
514    str := [];
515    if Length(arg) > 2 and IsString(arg[3]) then
516      GAPDoc2HTMLProcs.RelPath := arg[3];
517      GAPInfo.MainRootPath := Filtered(GAPInfo.RootPaths, a->
518                            Filename([Directory(a)], "lib/init.g")<>fail)[1];
519    else
520      Unbind(GAPDoc2HTMLProcs.RelPath);
521    fi;
522  fi;
523
524  name := r.name;
525  if not IsBound(GAPDoc2HTMLProcs.(name)) then
526    Info(InfoGAPDoc, 1, "#W WARNING: Don't know how to process element ", name,
527          " ---- ignored\n");
528  else
529    GAPDoc2HTMLProcs.(r.name)(r, str);
530  fi;
531
532  if r.name ="WHOLEDOCUMENT" then
533    # put final record together and return it
534    return GAPDoc2HTMLProcs.PutFilesTogether(str, r);
535  fi;
536
537  return str;
538end);
539
540##  recursion through the tree and collecting paragraphs
541BindGlobal("GAPDoc2HTMLContent", function(r, l)
542  local par, cont, i, count, s, a;
543
544  # utility: append counter and formatted paragraph to l
545  par := function(s)
546    if Length(s)>0 then
547      s := NormalizedWhitespace(s);
548      if Length(s)>0 then
549        Add(l, count);
550        Add(l, Concatenation("<p>", s, "</p>\n\n"));
551      fi;
552    fi;
553  end;
554
555  # if not containing paragraphs, then l is string to append to
556  if not r.name in GAPDoc2HTMLProcs.ParEls then
557    for a in r.content do
558      GAPDoc2HTML(a, l);
559    od;
560    return;
561  fi;
562
563  # otherwise we have to collect text and paragraphs
564  cont := r.content;
565  # checking for alternatives
566  i := 1;
567  while i < Length(cont) do
568    if cont[i].name = "Alt" and GAPDoc2HTMLProcs.AltYes(cont[i]) then
569      cont := Concatenation(cont{[1..i-1]}, cont[i].content,
570              cont{[i+1..Length(cont)]});
571    else
572      i := i + 1;
573    fi;
574  od;
575  count := r.count;
576  s := "";
577  for a in cont do
578    if a.count <> count  then
579      par(s);
580      count := a.count;
581      s := "";
582    fi;
583    if a.name in GAPDoc2HTMLProcs.ParEls then
584      # recursively collect paragraphs
585      GAPDoc2HTML(a, l);
586    else
587      # collect text for current paragraph
588      GAPDoc2HTML(a, s);
589    fi;
590  od;
591  if Length(s)>0 then
592    par(s);
593  fi;
594end);
595
596
597##  write head and foot of HTML file.
598GAPDoc2HTMLProcs.WHOLEDOCUMENT := function(r, par)
599  local i, pi, t, el, remdiv, math, pos, pos1, str, dat, datbt, bib, b,
600        keys, need, labels, tmp, j, diff, text, a, k, l, ind, opts;
601
602  ##  add paragraph numbers to all nodes of the document
603  AddParagraphNumbersGapDocTree(r);
604
605  if not IsBound(r.six) then
606    Info(InfoGAPDoc, 1, "#W WARNING: Using labels from section numbers, \n",
607      "#W consider running the converter for the text version first!\n");
608  fi;
609
610  ##  add a link .root to the root of the document to all nodes
611  ##  (then we can collect information about indexing and so on
612  ##  there)
613  AddRootParseTree(r);
614  r.index := [];
615  r.toc := "";
616  r.labels := rec();
617  r.labeltexts := rec();
618  if not IsBound(r.bibkeys) then
619    r.bibkeys := [];
620  fi;
621  r.chaptitle := rec();
622  r.chapsectlinks := rec();
623
624  ##  checking for processing instructions before the book starts
625  ##  example:  <?HTML option1="value1" ?>
626  i := 1;
627  pi := rec();
628  while not r.content[i].name = "Book" do
629    if r.content[i].name = "XMLPI" then
630      t := r.content[i].content;
631      if Length(t) > 4 and t{[1..5]} = "HTML " then
632        el := GetSTag(Concatenation("<", t, ">"), 2);
633        for a in NamesOfComponents(el.attributes) do
634          pi.(a) := el.attributes.(a);
635        od;
636      fi;
637    fi;
638    i := i+1;
639  od;
640
641  # setup for external conversion of maths (MathML with ttm, tth, ...)
642  if r.mathmode in ["MathML", "Tth"] then
643    r.ConvInput := "";
644    r.MathCount := 0;
645  fi;
646
647  ##  Now the actual work starts, we give the processing instructions found
648  ##  so far to the Book handler.
649  ##  We call the Book handler twice and produce index, bibliography, toc
650  ##  in between.
651  Info(InfoGAPDoc, 1, "#I First run, collecting cross references, ",
652        "index, toc, bib and so on . . .\n");
653  # with this flag we avoid unresolved references warnings in first run
654  GAPDoc2HTMLProcs.FirstRun := true;
655  GAPDoc2HTMLProcs.Book(r.content[i], [], pi);
656  GAPDoc2HTMLProcs.FirstRun := false;
657
658  # now the toc is ready
659  Info(InfoGAPDoc, 1, "#I Table of contents complete.\n");
660  r.toctext := r.toc;
661  r.chapsectlinkstext := r.chapsectlinks;
662
663  # utility to remove <div> tags
664  remdiv := function(s)
665    local pos, pos1;
666    pos := PositionSublist(s, "<div");
667    while pos <> fail do
668      pos1 := Position(s, '>', pos);
669      s := Concatenation(s{[1..pos-1]}, s{[pos1+1..Length(s)]});
670      pos := PositionSublist(s, "<div");
671    od;
672    pos := PositionSublist(s, "</div");
673    while pos <> fail do
674      pos1 := Position(s, '>', pos);
675      s := Concatenation(s{[1..pos-1]}, s{[pos1+1..Length(s)]});
676      pos := PositionSublist(s, "</div");
677    od;
678    return s;
679  end;
680
681  # MathML or Tth translation
682  if r.mathmode in ["MathML", "Tth"] then
683    Info(InfoGAPDoc, 1, "#I   translating formulae with \c");
684    FileString("tempCONV.tex", r.ConvInput);
685    if r.mathmode = "MathML" then
686      Info(InfoGAPDoc, 1, "ttm.\n");
687      Exec("rm -f tempCONV.html; ttm -L -r tempCONV.tex > tempCONV.html");
688    elif r.mathmode = "Tth" then
689      Info(InfoGAPDoc, 1, "tth.\n");
690      Exec("rm -f tempCONV.html; tth -w2 -r -u tempCONV.tex > tempCONV.html");
691    fi;
692    math := StringFile("tempCONV.html");
693    # correct the <var> tags
694    math := SubstitutionSublist(math, "&lt; var &gt;", "<var>");
695    math := SubstitutionSublist(math, "&lt; /var &gt;", "</var>");
696    math := SubstitutionSublist(math,
697                  "<mo>&lt;</mo><mi>var</mi><mo>&gt;</mo>", "<var>");
698    math := SubstitutionSublist(math,
699                  "<mo>&lt;</mo><mo>/</mo><mi>var</mi><mo>&gt;</mo>", "</var>");
700    r.MathList := [];
701    pos := PositionSublist(math, "TeXFormulaeDelim");
702    while pos <> fail do
703      pos1 := PositionSublist(math, "TeXFormulaeDelim", pos);
704      if pos1 <> fail then
705        Add(r.MathList, remdiv(math{[pos+16..pos1-1]}));
706      else
707        Add(r.MathList, remdiv(math{[pos+16..Length(math)]}));
708      fi;
709      pos := pos1;
710    od;
711  fi;
712
713  # .index has entries of form [sorttext, subsorttext, numbertext,
714  # entrytext, url[, subtext]]
715  Info(InfoGAPDoc, 1, "#I Producing the index . . .\n");
716  SortBy(r.index, a-> [a[1],STRING_LOWER(a[2]),
717                       List(SplitString(a[3],".-",""), Int)]);
718  str := "";
719  ind := r.index;
720  k := 1;
721  while k <= Length(ind) do
722    if k > 1 and ind[k][4] = ind[k-1][4] then
723      Append(str, "&nbsp;&nbsp;&nbsp;&nbsp;");
724    else
725      Append(str, ind[k][4]);
726    fi;
727    if IsBound(ind[k][6]) then
728      if k = 1 or ind[k][4] <> ind[k-1][4] then
729        Append(str, ", ");
730      fi;
731      Append(str, ind[k][6]);
732    elif Length(ind[k][2]) > 0 then
733      if k = 1 or ind[k][4] <> ind[k-1][4] then
734        Append(str, ", ");
735      fi;
736      Append(str, ind[k][2]);
737    fi;
738    l := k;
739    while l <= Length(ind) and ind[l][4] = ind[k][4] and
740          ((IsBound(ind[k][6]) and IsBound(ind[l][6])
741            and ind[k][6] = ind[l][6]) or
742           (not IsBound(ind[k][6]) and not IsBound(ind[l][6])
743           and ind[k][2] = ind[l][2]))  do
744      Append(str, Concatenation("  <a href=\"", ind[l][5], "\">",
745                              ind[l][3], "</a>  "));
746      l := l+1;
747    od;
748    Append(str, "<br />\n");
749    k := l;
750  od;
751  r.indextext := str;
752
753  if Length(r.bibkeys) > 0 then
754    GAPDocAddBibData(r);
755    Info(InfoGAPDoc, 1, "#I Writing bibliography . . .\n");
756    if r.root.mathmode = "MathJax" then
757      opts := rec(MathJax := true);
758    else
759      opts := rec();
760    fi;
761    need := List(r.bibentries, a-> RecBibXMLEntry(a, "HTML", r.bibstrings,
762            opts));
763    # copy the unique labels
764    for a in [1..Length(need)] do
765      need[a].key := r.biblabels[a];
766    od;
767    text := "";
768    for a in need do
769      # an anchor for links from the citations
770      Append(text, Concatenation("\n<p><a id=\"biB", a.Label,
771                        "\" name=\"biB", a.Label, "\"></a></p>\n"));
772      Append(text, StringBibAsHTML(a, false));
773    od;
774    r.bibtext := text;
775  fi;
776
777  # second run
778  r.index := [];
779  Info(InfoGAPDoc, 1, "#I Second run through document . . .\n");
780  GAPDoc2HTMLProcs.Book(r.content[i], par, pi);
781
782  for a in ["MathList", "MathCount", "index", "toc"] do
783    Unbind(r.(a));
784  od;
785  ##  remove the links to the root  ???
786##    RemoveRootParseTree(r);
787end;
788
789##  comments and processing instructions are in general ignored
790GAPDoc2HTMLProcs.XMLPI := function(r, str)
791  return;
792end;
793GAPDoc2HTMLProcs.XMLCOMMENT := function(r, str)
794  return;
795end;
796
797# two utilities for attribute values like labels or text with special
798# XML (or LaTeX) characters which gets printed
799GAPDoc2HTMLProcs.EscapeAttrVal := function(str)
800  str := SubstitutionSublist(str, "&", "&amp;");
801  str := SubstitutionSublist(str, "<", "&lt;");
802  str := SubstitutionSublist(str, ">", "&gt;");
803  return str;
804end;
805
806# do nothing with Ignore
807GAPDoc2HTMLProcs.Ignore := function(arg)
808end;
809
810# just process content
811GAPDoc2HTMLProcs.Book := function(r, par, pi)
812  # copy the name of the book to the root
813  r.root.bookname := r.attributes.Name;
814  GAPDoc2HTMLContent(r, par);
815end;
816
817##  Body is sectioning element
818GAPDoc2HTMLProcs.Body := GAPDoc2HTMLContent;
819
820##  the title page,  the most complicated looking function
821GAPDoc2HTMLProcs.TitlePage := function(r, par)
822  local   strn,  l,  s,  a,  aa,  cont,  ss;
823
824  strn := "<div class=\"pcenter\">\n";
825  # title
826  l := Filtered(r.content, a-> a.name = "Title");
827  s := "";
828  GAPDoc2HTMLContent(l[1], s);
829  s := Concatenation("\n<h1>", NormalizedWhitespace(s), "</h1>\n\n");
830  Append(strn, s);
831
832  # subtitle
833  l := Filtered(r.content, a-> a.name = "Subtitle");
834  if Length(l)>0 then
835    s := "";
836    GAPDoc2HTMLContent(l[1], s);
837    s := Concatenation("\n<h2>", NormalizedWhitespace(s), "</h2>\n\n");
838    Append(strn, s);
839  fi;
840
841  # version
842  l := Filtered(r.content, a-> a.name = "Version");
843  if Length(l)>0 then
844    s := "<p>";
845    GAPDoc2HTMLContent(l[1], s);
846    while Length(s)>0 and s[Length(s)] in  WHITESPACE do
847      Unbind(s[Length(s)]);
848    od;
849    Append(s, "</p>\n\n");
850    Append(strn, s);
851  fi;
852
853  # date
854  l := Filtered(r.content, a-> a.name = "Date");
855  if Length(l)>0 then
856    s := "<p>";
857    GAPDoc2HTMLContent(l[1], s);
858    Append(strn, s);
859    Append(strn, "</p>\n\n");
860  fi;
861  Append(strn, "</div>\n");
862
863  # an extra comment
864  l := Filtered(r.content, a-> a.name = "TitleComment");
865  if Length(l) > 0 then
866    s := "<p>";
867    GAPDoc2HTMLContent(l[1], s);
868    Append(s, "</p>\n");
869    Append(strn, s);
870  fi;
871
872  # author name(s)
873  l := Filtered(r.content, a-> a.name = "Author");
874  for a in l do
875    s := "<p><b>";
876    aa := ShallowCopy(a);
877    aa.content := Filtered(a.content,
878                  b-> not b.name in ["Email", "Homepage", "Address"]);
879    GAPDoc2HTMLContent(aa, s);
880    Append(strn, s);
881    Append(strn, "</b>\n");
882    cont := List(a.content, b-> b.name);
883    if "Email" in cont then
884      s := "";
885      GAPDoc2HTML(a.content[Position(cont, "Email")], s);
886      s := NormalizedWhitespace(s);
887      Append(strn, Concatenation("<br />", GAPDocTexts.d.Email, ": ", s, "\n"));
888    fi;
889    if "Homepage" in cont then
890      s := "";
891      GAPDoc2HTML(a.content[Position(cont, "Homepage")], s);
892      s := NormalizedWhitespace(s);
893      Append(strn, Concatenation("<br />", GAPDocTexts.d.Homepage,
894                                 ": ", s, "\n"));
895    fi;
896    if "Address" in cont then
897      s := "";
898      GAPDoc2HTMLContent(a.content[Position(cont, "Address")], s);
899      s := NormalizedWhitespace(s);
900      Append(strn, Concatenation("<br />", GAPDocTexts.d.Address,
901                                 ": <br />", s, "\n"));
902    fi;
903    Append(strn, "</p>");
904  od;
905  Append(strn, "\n\n");
906
907  Add(par, r.count);
908  Add(par, strn);
909
910  # an Address element in title page
911  l := Filtered(r.content, a-> a.name = "Address");
912  if Length(l) > 0 then
913    s := "";
914    GAPDoc2HTMLContent(l[1], s);
915    s := NormalizedWhitespace(s);
916    Append(strn, Concatenation("<p><b>", GAPDocTexts.d.Address,
917                               ":</b><br />\n", s, "</p>\n"));
918  fi;
919
920  # abstract, copyright page, acknowledgements, colophon
921  for ss in ["Abstract", "Copyright", "Acknowledgements", "Colophon" ] do
922    l := Filtered(r.content, a-> a.name = ss);
923    if Length(l)>0 then
924      Add(par, l[1].count);
925      Add(par, Concatenation("<h3>", GAPDocTexts.d.(ss), "</h3>\n"));
926      GAPDoc2HTMLContent(l[1], par);
927    fi;
928  od;
929end;
930
931##  these produce text for an URL
932##  arg:  r, str[, pre]
933GAPDoc2HTMLProcs.Link := GAPDoc2HTMLContent;
934GAPDoc2HTMLProcs.LinkText := GAPDoc2HTMLContent;
935GAPDoc2HTMLProcs.URL := function(arg)
936  local r, str, pre, rr, txt, s;
937  r := arg[1];
938  str := arg[2];
939  if Length(arg)>2 then
940    pre := arg[3];
941  else
942    pre := "";
943  fi;
944  rr := First(r.content, a-> a.name = "LinkText");
945  if rr <> fail then
946    txt := "";
947    GAPDoc2HTML(rr, txt);
948    rr := First(r.content, a-> a.name = "Link");
949    if rr = fail then
950      Info(InfoGAPDoc, 1, "#W missing <Link> element for text ", txt, "\n");
951      s := "MISSINGLINK";
952    else
953      s := "";
954      GAPDoc2HTMLContent(rr, s);
955    fi;
956  else
957    s := "";
958    GAPDoc2HTMLContent(r, s);
959    if IsBound(r.attributes.Text) then
960      txt := r.attributes.Text;
961    else
962      txt := s;
963    fi;
964  fi;
965  Append(str, Concatenation(GAPDoc2HTMLProcs.TextAttr.URL[1],
966                    "<a href=\"", pre, s, "\">", txt, "</a>"));
967  Append(str, GAPDoc2HTMLProcs.TextAttr.URL[2]);
968end;
969
970GAPDoc2HTMLProcs.Homepage := GAPDoc2HTMLProcs.URL;
971
972GAPDoc2HTMLProcs.Email := function(r, str)
973  # we add the `mailto:' phrase
974  GAPDoc2HTMLProcs.URL(r, str, "mailto:");
975end;
976
977##  utility: generate a chapter or (sub)section-number string
978GAPDoc2HTMLProcs.SectionNumber := function(count, sect)
979  local   res;
980  res := "";
981  if IsString(count[1]) or count[1]>0 then
982    Append(res, String(count[1]));
983  fi;
984  if sect="Chapter" or sect="Appendix" then
985    return res;
986  fi;
987  Add(res, '.');
988  if count[2]>0 then
989    Append(res, String(count[2]));
990  fi;
991  if sect="Section" then
992    return res;
993  fi;
994  if count[3]>0 then
995    Append(res, Concatenation("-", String(count[3])));
996  fi;
997  return res;
998end;
999
1000##  utility: generate a chapter or (sub)section-number string
1001GAPDoc2HTMLProcs.SectionLabel := function(r, count, sect)
1002  local   res, a;
1003  if IsString(count[1]) or count[1]>0 then
1004    res := Concatenation("chap", String(count[1]), GAPDoc2HTMLProcs.ext);
1005  else
1006    res := Concatenation("chap0", GAPDoc2HTMLProcs.ext);
1007  fi;
1008  res := [res, ""];
1009  if not IsBound(r.root.six) then
1010    if sect="Chapter" then
1011      return res;
1012    fi;
1013    Append(res[2], Concatenation("s", String(count[2])));
1014    Append(res[2], Concatenation("ss", String(count[3])));
1015    return res;
1016  else
1017##      a := First(r.root.six, a-> a[3] = count{[1..3]});
1018    a := PositionSet(r.root.sixcount, count{[1..3]});
1019    if a <> fail then
1020      a := r.root.six[r.root.sixindex[a]];
1021    fi;
1022    if a = fail or not IsBound(a[7]) then
1023      return GAPDoc2HTMLProcs.SectionLabel(rec(root:=rec()), count, sect);
1024    else
1025      Append(res[2], a[7]);
1026      return res;
1027    fi;
1028  fi;
1029end;
1030
1031##  the sectioning commands are just translated and labels are
1032##  generated, if given as attribute
1033GAPDoc2HTMLProcs.ChapSect := function(r, par, sect)
1034  local   num, posh, s, ind, strn, lab, types, nrs, hord, a, pos, l, i;
1035
1036  types := ["Chapter", "Appendix", "Section", "Subsection"];
1037  nrs := ["3", "3", "4", "5"];
1038  hord := nrs[Position(types, sect)];
1039
1040  Add(par, r.count);
1041  # if available, start with links to sections in chapter/appendix
1042  if sect in ["Chapter", "Appendix"] then
1043    if IsBound(r.root.chapsectlinkstext) and
1044       IsBound(r.root.chapsectlinkstext.(r.count[1])) then
1045      Add(par, r.root.chapsectlinkstext.(r.count[1]));
1046    fi;
1047  fi;
1048  if par[Length(par)] = r.count then
1049    Add(par, "");
1050  fi;
1051
1052  # section number as string
1053  num := GAPDoc2HTMLProcs.SectionNumber(r.count, sect);
1054  # and as anchor
1055  lab := GAPDoc2HTMLProcs.SectionLabel(r, r.count, "Subsection");
1056  lab := Concatenation(lab[1], "#", lab[2]);
1057
1058  # the heading
1059  posh := Position(List(r.content, a-> a.name), "Heading");
1060  if posh <> fail then
1061    s := "";
1062    # first the .six entry
1063    GAPDoc2HTMLProcs.Heading1(r.content[posh], s);
1064    if hord = "3" then
1065      r.root.chaptitle.(r.count[1]) := s;
1066    fi;
1067
1068    # label entry, if present
1069    if IsBound(r.attributes.Label) then
1070      r.root.labels.(r.attributes.Label) := [num, lab];
1071      r.root.labeltexts.(r.attributes.Label) := s;
1072    fi;
1073
1074    # the heading text
1075    Append(par[Length(par)],
1076       Concatenation("\n<h", hord, ">", num, " ", s, "</h", hord, ">\n\n"));
1077
1078    # table of contents entry
1079    s := Concatenation( num, " ", s);
1080    if sect in ["Chapter", "Appendix"] then
1081      if r.count[1] >= 1 then
1082        ind := "<div class=\"ContChap\">";
1083      fi;
1084    elif sect="Section" then
1085      ind := "";
1086      if r.count[2] >= 1 then
1087        Append(ind, "<div class=\"ContSect\">");
1088      fi;
1089      Append(ind, "<span class=\"tocline\"><span class=\"nocss\">&nbsp;</span>");
1090    elif sect="Subsection" then
1091      ind := "<span class=\"ContSS\"><br /><span class=\"nocss\">&nbsp;&nbsp;</span>";
1092    else
1093      ind := "";
1094    fi;
1095    Append(r.root.toc, Concatenation(ind, "<a href=\"",
1096                                        lab, "\">", s, "</a>\n"));
1097    if sect="Section" then
1098      Append(r.root.toc, "</span>\n<div class=\"ContSSBlock\">\n");
1099    fi;
1100  fi;
1101
1102  # the actual content
1103  GAPDoc2HTMLContent(r, par);
1104
1105  # possibly close <div> or <span> in content
1106  if posh <> fail then
1107    if sect in ["Chapter", "Appendix" ] then
1108      Append(r.root.toc, "</div>\n");
1109    elif sect="Section" then
1110      # remove last line if no subsections
1111      l := Length(r.root.toc);
1112      if r.root.toc{[l-25..l]} = "<div class=\"ContSSBlock\">\n" then
1113        for i in [0..25] do Unbind(r.root.toc[l-i]); od;
1114        Append(r.root.toc, "</div>\n");
1115      else
1116        Append(r.root.toc, "</div></div>\n");
1117      fi;
1118    elif sect="Subsection" then
1119      Append(r.root.toc, "</span>\n");
1120    fi;
1121  fi;
1122  # if in chapter, also use the section links in top of page
1123  if sect in ["Chapter", "Appendix"] then
1124    a := Reversed(r.root.toc);
1125    pos := PositionSublist(a, Reversed("<div class=\"ContChap\">"));
1126    a := Reversed(a{[1..pos-1]});
1127    r.root.chapsectlinks.(r.count[1]) := Concatenation(
1128                                         "<div class=\"ChapSects\">", a);
1129  fi;
1130end;
1131
1132
1133##  this really produces the content of the heading
1134GAPDoc2HTMLProcs.Heading1 := function(r, str)
1135  GAPDoc2HTMLProcs.WrapAttr(r, str, "Heading");
1136end;
1137##  and this ignores the heading (for simpler recursion)
1138GAPDoc2HTMLProcs.Heading := function(r, str)
1139end;
1140
1141GAPDoc2HTMLProcs.Chapter := function(r, par)
1142  GAPDoc2HTMLProcs.ChapSect(r, par, "Chapter");
1143end;
1144
1145GAPDoc2HTMLProcs.Appendix := function(r, par)
1146  GAPDoc2HTMLProcs.ChapSect(r, par, "Appendix");
1147end;
1148
1149GAPDoc2HTMLProcs.Section := function(r, par)
1150  GAPDoc2HTMLProcs.ChapSect(r, par, "Section");
1151end;
1152
1153GAPDoc2HTMLProcs.Subsection := function(r, par)
1154  GAPDoc2HTMLProcs.ChapSect(r, par, "Subsection");
1155end;
1156
1157##  table of contents, just puts "TOC" in first run
1158GAPDoc2HTMLProcs.TableOfContents := function(r, par)
1159  Add(par, r.count);
1160  if IsBound(r.root.toctext) then
1161    Add(par, Concatenation("\n<div class=\"contents\">\n<h3>",
1162          GAPDocTexts.d.Contents,
1163          "<a id=\"contents\" name=\"contents\"></a></h3>\n\n",
1164          r.root.toctext, "<br />\n</div>\n"));
1165  else
1166    Add(par,"<p>TOC\n-----------</p>\n\n");
1167  fi;
1168end;
1169
1170##  bibliography, just "BIB" in first run, store databases in root
1171GAPDoc2HTMLProcs.Bibliography := function(r, par)
1172  local   s;
1173  # add references to TOC
1174  Append(r.root.toc, Concatenation("<div class=\"ContChap\"><a href=\"chapBib",
1175                        GAPDoc2HTMLProcs.ext, "\"><span class=\"Heading\">",
1176                        GAPDocTexts.d.References, "</span></a></div>\n"));
1177  r.root.bibdata := r.attributes.Databases;
1178  if IsBound(r.attributes.Style) then
1179    r.root.bibstyle := r.attributes.Style;
1180  fi;
1181  Add(par, r.count);
1182  if IsBound(r.root.bibtext) then
1183    Add(par, Concatenation("\n<h3>", GAPDocTexts.d.References, "</h3>\n\n",
1184            r.root.bibtext, "<p> </p>\n\n"));
1185  else
1186    Add(par,"<p>BIB\n-----------</p>\n");
1187  fi;
1188end;
1189
1190GAPDoc2HTMLProcs.PCDATAFILTER := function(r, str)
1191  local s, a;
1192  s := r.content;
1193  if not IsBound(r.HTML) and (Position(s, '<') <> fail or
1194       Position(s, '&') <> fail or Position(s, '>') <> fail) then
1195    for a in s do
1196      if a='<' then
1197        Append(str, "&lt;");
1198      elif a='&' then
1199        Append(str, "&amp;");
1200      elif a='>' then
1201        Append(str, "&gt;");
1202      else
1203        Add(str, a);
1204      fi;
1205    od;
1206  else
1207    Append(str, s);
1208  fi;
1209end;
1210# and without filter (e.g., for collecting formulae
1211GAPDoc2HTMLProcs.PCDATANOFILTER := function(r, str)
1212  Append(str, r.content);
1213end;
1214
1215GAPDoc2HTMLProcs.PCDATAFILTER;
1216## default is with filter
1217GAPDoc2HTMLProcs.PCDATA := GAPDoc2HTMLProcs.PCDATAFILTER;
1218
1219##  end of paragraph (end with double newline)
1220GAPDoc2HTMLProcs.P := function(r, str)
1221# nothing to do, the "<p>" are added in main loop (GAPDoc2HTML)
1222end;
1223
1224GAPDoc2HTMLProcs.Br := function(r, str)
1225  Append(str, "<br />\n");
1226end;
1227
1228##  wrapping text attributes
1229GAPDoc2HTMLProcs.WrapAttr := function(r, str, a)
1230  local   s,  tt;
1231  s := "";
1232  GAPDoc2HTMLContent(r, s);
1233  tt := GAPDoc2HTMLProcs.TextAttr.(a);
1234  Append(str, Concatenation(tt[1], s, tt[2]));
1235end;
1236
1237##  GAP keywords
1238GAPDoc2HTMLProcs.K := function(r, str)
1239  GAPDoc2HTMLProcs.WrapAttr(r, str, "K");
1240end;
1241
1242##  buttons
1243GAPDoc2HTMLProcs.B := function(r, str)
1244  GAPDoc2HTMLProcs.WrapAttr(r, str, "B");
1245end;
1246
1247##  verbatim GAP code
1248GAPDoc2HTMLProcs.C := function(r, str)
1249  GAPDoc2HTMLProcs.WrapAttr(r, str, "C");
1250end;
1251
1252##  file names
1253GAPDoc2HTMLProcs.F := function(r, str)
1254  GAPDoc2HTMLProcs.WrapAttr(r, str, "F");
1255end;
1256
1257##  argument names (same as Arg)
1258GAPDoc2HTMLProcs.A := function(r, str)
1259  GAPDoc2HTMLProcs.WrapAttr(r, str, "Arg");
1260end;
1261
1262GAPDoc2HTMLProcs.MathConvHelper := function(r, str, db, de)
1263  local s, x;
1264  if IsBound(r.root.MathList) then
1265    # conversion already available
1266    r.root.MathCount := r.root.MathCount + 1;
1267    Append(str, r.root.MathList[r.root.MathCount]);
1268  else
1269    # add to input for converter
1270    if IsString(r.content) then
1271      s := r.content;
1272    else
1273      s := "";
1274      GAPDoc2HTMLProcs.PCDATA := GAPDoc2HTMLProcs.PCDATANOFILTER;
1275      for x in r.content do
1276        GAPDoc2HTML(x, s);
1277      od;
1278      GAPDoc2HTMLProcs.PCDATA := GAPDoc2HTMLProcs.PCDATAFILTER;
1279    fi;
1280    s := Concatenation("TeXFormulaeDelim", db, s, de);
1281    Append(r.root.ConvInput, s);
1282    Append(str, "FORMULA");
1283  fi;
1284end;
1285
1286
1287##  simple maths, here we try to substitute TeX command to something which
1288##  looks ok in text mode
1289GAPDoc2HTMLProcs.M := function(r, str)
1290  local s, ss, save;
1291  if r.root.mathmode in ["MathML", "Tth"] then
1292    GAPDoc2HTMLProcs.MathConvHelper(r, str, "$", "$");
1293    return;
1294  fi;
1295  s := "";
1296  GAPDoc2HTMLProcs.PCDATA := GAPDoc2HTMLProcs.PCDATANOFILTER;
1297  # a hack, since we want to allow <A> in formulae
1298  save := GAPDoc2HTMLProcs.TextAttr.Arg;
1299  GAPDoc2HTMLProcs.TextAttr.Arg := ["TEXTaTTRvARBEGIN", "TEXTaTTRvAREND"];
1300  GAPDoc2HTMLContent(r, s);
1301  GAPDoc2HTMLProcs.TextAttr.Arg := save;
1302  GAPDoc2HTMLProcs.PCDATA := GAPDoc2HTMLProcs.PCDATAFILTER;
1303  ss := "";
1304  if r.root.mathmode = "MathJax" then
1305    s := Concatenation("\\(",s,"\\)");
1306    GAPDoc2HTMLProcs.PCDATAFILTER(rec(content := s), ss);
1307    ss := SubstitutionSublist(ss, "TEXTaTTRvARBEGIN", "\\textit{");
1308    ss := SubstitutionSublist(ss, "TEXTaTTRvAREND", "}");
1309  else
1310    s := TextM(s);
1311    GAPDoc2HTMLProcs.PCDATAFILTER(rec(content := s), ss);
1312    ss := SubstitutionSublist(ss, "TEXTaTTRvARBEGIN", save[1]);
1313    ss := SubstitutionSublist(ss, "TEXTaTTRvAREND", save[2]);
1314  fi;
1315  Append(str, WrapTextAttribute(ss, GAPDoc2HTMLProcs.TextAttr.M));
1316end;
1317
1318##  in HTML this is shown in TeX format
1319GAPDoc2HTMLProcs.Math := function(r, str)
1320  local s;
1321  if r.root.mathmode in ["MathML", "Tth"] then
1322    GAPDoc2HTMLProcs.MathConvHelper(r, str, "$", "$");
1323    return;
1324  fi;
1325  if r.root.mathmode = "MathJax" then
1326    s := "";
1327    GAPDoc2HTMLProcs.M(r, s);
1328    Append(str, s);
1329    return;
1330  fi;
1331  s := "";
1332  GAPDoc2HTMLContent(r, s);
1333  Append(str, WrapTextAttribute(s, GAPDoc2HTMLProcs.TextAttr.Math));
1334end;
1335
1336##  displayed maths (also in TeX format, but centered paragraph in itself)
1337GAPDoc2HTMLProcs.Display := function(r, par)
1338  local   s, a, str;
1339  if r.root.mathmode in ["MathML", "Tth"] then
1340    str := "";
1341    GAPDoc2HTMLProcs.MathConvHelper(r, str, "\n\\[", "\\]\n\n");
1342    Add(par, r.count);
1343    Add(par, Concatenation("<table class=\"display\"><tr>",
1344             "<td class=\"display\">",
1345             str, "</td></tr></table>\n"));
1346    return;
1347  fi;
1348  s := "";
1349  for a in r.content do
1350    GAPDoc2HTML(a, s);
1351  od;
1352  if r.root.mathmode = "MathJax" then
1353    Add(par, r.count);
1354    Add(par, Concatenation("<p class=\"center\">\\[",
1355                            s, "\\]</p>\n\n"));
1356    return;
1357  fi;
1358  if IsBound(r.attributes.Mode) and r.attributes.Mode = "M" then
1359    s := TextM(s);
1360  fi;
1361  s := Concatenation("<p class=\"pcenter\">", s, "</p>\n\n");
1362  Add(par, r.count);
1363  Add(par, s);
1364end;
1365
1366##  emphazised text
1367GAPDoc2HTMLProcs.Emph := function(r, str)
1368  GAPDoc2HTMLProcs.WrapAttr(r, str, "Emph");
1369end;
1370
1371##  quoted text
1372GAPDoc2HTMLProcs.Q := function(r, str)
1373  Append(str, "\"");
1374  GAPDoc2HTMLContent(r, str);
1375  Append(str, "\"");
1376end;
1377
1378##  Package names
1379GAPDoc2HTMLProcs.Package := function(r, str)
1380  GAPDoc2HTMLProcs.WrapAttr(r, str, "Package");
1381end;
1382
1383##  menu items
1384GAPDoc2HTMLProcs.I := function(r, str)
1385  GAPDoc2HTMLProcs.WrapAttr(r, str, "I");
1386end;
1387
1388GAPDoc2HTMLProcs.AddColorPromptMarkup := function(cont)
1389  local patt, batt, iatt, res, pos, s, rows;
1390  patt := GAPDoc2HTMLProcs.TextAttr.GAPprompt;
1391  batt := GAPDoc2HTMLProcs.TextAttr.GAPbrkprompt;
1392  iatt := GAPDoc2HTMLProcs.TextAttr.GAPinput;
1393  res := "";
1394  rows := SplitString(cont, "\n", "");
1395  for s in rows do
1396    if Length(s) > 7 and s{[1..8]} = "gap&gt; " then
1397      Append(res, Concatenation(WrapTextAttribute("gap&gt;", patt),
1398                       " ", WrapTextAttribute(s{[9..Length(s)]}, iatt)));
1399    elif Length(s) > 4 and s{[1..5]} = "&gt; " then
1400      Append(res, Concatenation(WrapTextAttribute("&gt;", patt),
1401                       " ", WrapTextAttribute(s{[6..Length(s)]}, iatt)));
1402    elif Length(s) > 2 and s{[1..3]} = "brk" then
1403      pos := Position(s, ' ');
1404      if pos <> fail then
1405        Append(res, Concatenation(WrapTextAttribute(s{[1..pos-1]}, batt),
1406                        " ", WrapTextAttribute(s{[pos+1..Length(s)]}, iatt)));
1407      else
1408        Append(res, WrapTextAttribute(s, batt));
1409      fi;
1410    else
1411      Append(res, WrapTextAttribute(s, ["",""]));
1412    fi;
1413    Add(res, '\n');
1414  od;
1415  if Length(cont) > 0 and cont[Length(cont)] <> '\n' then
1416    Unbind(res[Length(res)]);
1417  fi;
1418  return res;
1419end;
1420
1421GAPDoc2HTMLProcs.ExampleLike := function(r, par, label, colorpr)
1422  local   str,  cont,  a,  s;
1423  str := "\n<div class=\"example\"><pre>";
1424  cont := "";
1425  for a in r.content do
1426    # here we try to avoid reformatting
1427    if IsString(a.content) then
1428      GAPDoc2HTMLProcs.PCDATA(a, cont);
1429    else
1430      s := "";
1431      GAPDoc2HTML(a, s);
1432      Append(cont, s);
1433    fi;
1434  od;
1435  if colorpr then
1436    cont := GAPDoc2HTMLProcs.AddColorPromptMarkup(cont);
1437  fi;
1438  Append(str, cont);
1439  Append(str, "</pre></div>\n\n");
1440  Add(par, r.count);
1441  Add(par, str);
1442end;
1443
1444##  log of session and GAP code is typeset the same way as <Example>
1445GAPDoc2HTMLProcs.Example := function(r, par)
1446  GAPDoc2HTMLProcs.ExampleLike(r, par, "Example", true);
1447end;
1448GAPDoc2HTMLProcs.Log := function(r, par)
1449  GAPDoc2HTMLProcs.ExampleLike(r, par, "Log", true);
1450end;
1451GAPDoc2HTMLProcs.Listing := function(r, par)
1452  GAPDoc2HTMLProcs.ExampleLike(r, par, "Code", false);
1453end;
1454
1455GAPDoc2HTMLProcs.Verb := function(r, par)
1456  local   str,  cont,  a,  s;
1457  str := "\n<pre class=\"normal\">\n";
1458  cont := "";
1459  for a in r.content do
1460    # here we try to avoid reformatting
1461    if IsString(a.content) then
1462      GAPDoc2HTMLProcs.PCDATA(a, cont);
1463    else
1464      s := "";
1465      GAPDoc2HTML(a, s);
1466      Append(cont, s);
1467    fi;
1468  od;
1469  Append(str, cont);
1470  Append(str, "\n</pre>\n\n");
1471  Add(par, r.count);
1472  Add(par, str);
1473end;
1474
1475##  explicit labels
1476GAPDoc2HTMLProcs.Label := function(r, str)
1477  local num,  lab;
1478  num := GAPDoc2HTMLProcs.SectionNumber(r.count, "Subsection");
1479  lab := GAPDoc2HTMLProcs.SectionLabel(r, r.count, "Subsection");
1480  r.root.labels.(r.attributes.Name) := [num, Concatenation(lab[1],"#",lab[2])];
1481end;
1482
1483##  citations
1484GAPDoc2HTMLProcs.Cite := function(r, str)
1485  local   key,  pos;
1486  key := r.attributes.Key;
1487  pos := Position(r.root.bibkeys, key);
1488  if pos = fail then
1489    Add(r.root.bibkeys, key);
1490    Append(str, Concatenation("[?", key, "?]"));
1491  elif  not IsBound(r.root.biblabels) then
1492    Append(str, Concatenation("[?", key, "?]"));
1493  else
1494    # here we include a link to the corresponding entry in bibliography
1495    Append(str, Concatenation("<a href=\"chapBib", GAPDoc2HTMLProcs.ext,
1496                              "#biB", key, "\">[", r.root.biblabels[pos]));
1497    if IsBound(r.attributes.Where) then
1498      Append(str, ", ");
1499      Append(str, r.attributes.Where);
1500    fi;
1501    Append(str, "]</a>");
1502  fi;
1503end;
1504
1505##  explicit index entries
1506GAPDoc2HTMLProcs.Subkey := GAPDoc2HTMLContent;
1507GAPDoc2HTMLProcs.Index := function(r, str)
1508  local s, sub, entry, url, a;
1509
1510  s := "";
1511  sub := "";
1512  for a in r.content do
1513    if a.name = "Subkey" then
1514      GAPDoc2HTML(a, sub);
1515    else
1516      GAPDoc2HTML(a, s);
1517    fi;
1518  od;
1519  NormalizeWhitespace(s);
1520  NormalizeWhitespace(sub);
1521  if IsBound(r.attributes.Key) then
1522    entry := [STRING_LOWER(r.attributes.Key)];
1523  else
1524    entry := [STRING_LOWER(s)];
1525  fi;
1526  if IsBound(r.attributes.Subkey) then
1527    Add(entry, r.attributes.Subkey);
1528  else
1529    Add(entry, sub);
1530  fi;
1531  Add(entry, GAPDoc2HTMLProcs.SectionNumber(r.count, "Subsection"));
1532  Add(entry, s);
1533  url := GAPDoc2HTMLProcs.SectionLabel(r, r.count, "Subsection");
1534  Add(entry, Concatenation(url[1],"#",url[2]));
1535  if Length(sub) > 0 then
1536    Add(entry, sub);
1537  fi;
1538  Add(r.root.index, entry);
1539end;
1540
1541##  helper to add markup to the args
1542GAPDoc2HTMLProcs.WrapArgs := function(argstr)
1543  local res, noletter, c;
1544  res := "";
1545  noletter := true;
1546  for c in argstr do
1547    if noletter then
1548      if not c in ", []" then
1549        noletter := false;
1550        Append(res, GAPDoc2HTMLProcs.TextAttr.Arg[1]);
1551      fi;
1552    elif c in ", []" then
1553      noletter := true;
1554      Append(res, GAPDoc2HTMLProcs.TextAttr.Arg[2]);
1555    fi;
1556    Add(res, c);
1557  od;
1558  if not noletter then
1559    Append(res, GAPDoc2HTMLProcs.TextAttr.Arg[2]);
1560  fi;
1561  return res;
1562end;
1563
1564##  this produces an implicit index entry and a label entry
1565GAPDoc2HTMLProcs.LikeFunc := function(r, par, typ)
1566  local   attr,  s,  name,  lab, url;
1567  attr := GAPDoc2HTMLProcs.TextAttr.Func;
1568  name := GAPDoc2HTMLProcs.EscapeAttrVal(r.attributes.Name);
1569  s := Concatenation(attr[1], "&#8227; ", name, attr[2]);
1570  if IsBound(r.attributes.Arg) then
1571    Append(s, Concatenation("( ", GAPDoc2HTMLProcs.WrapArgs(
1572              GAPDoc2HTMLProcs.EscapeAttrVal(
1573              NormalizedArgList(r.attributes.Arg))), " )"));
1574  fi;
1575  # index entry
1576  attr := GAPDoc2HTMLProcs.TextAttr.Func;
1577  url := GAPDoc2HTMLProcs.SectionLabel(r, r.count, "Subsection");
1578  url := Concatenation(url[1],"#",url[2]);
1579  if IsBound(r.attributes.Label) then
1580    lab := r.attributes.Label;
1581  else
1582    lab := "";
1583  fi;
1584  Add(r.root.index, [STRING_LOWER(name), lab,
1585          GAPDoc2HTMLProcs.SectionNumber(r.count, "Subsection"),
1586          Concatenation(attr[1], name, attr[2]),
1587          url]);
1588  # label (if not given, the default is the Name)
1589  if IsBound(r.attributes.Label) then
1590    if IsBound(r.attributes.Name) then
1591      lab := Concatenation(r.attributes.Name, " (", r.attributes.Label, ")");
1592    else
1593      lab := r.attributes.Label;
1594    fi;
1595  else
1596    lab := r.attributes.Name;
1597  fi;
1598  GAPDoc2HTMLProcs.Label(rec(count := r.count, attributes := rec(Name
1599                                             := lab), root := r.root), par);
1600  # adding  hint about the type of the variable
1601  s := Concatenation("<div class=\"func\"><table class=\"func\" ",
1602               "width=\"100%\">",
1603               "<tr><td class=\"tdleft\">", s,
1604               "</td><td class=\"tdright\">(&nbsp;", typ,
1605               "&nbsp;)</td></tr></table></div>\n");
1606  Add(par, r.count);
1607  Add(par, s);
1608end;
1609
1610GAPDoc2HTMLProcs.Func := function(r, str)
1611  GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.Func);
1612end;
1613
1614GAPDoc2HTMLProcs.Oper := function(r, str)
1615  GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.Oper);
1616end;
1617
1618GAPDoc2HTMLProcs.Constr := function(r, str)
1619  GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.Constr);
1620end;
1621
1622GAPDoc2HTMLProcs.Meth := function(r, str)
1623  GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.Meth);
1624end;
1625
1626GAPDoc2HTMLProcs.Filt := function(r, str)
1627  # r.attributes.Type could be "representation", "category", ...
1628  if IsBound(r.attributes.Type) then
1629    GAPDoc2HTMLProcs.LikeFunc(r, str, LowercaseString(r.attributes.Type));
1630  else
1631    GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.Filt);
1632  fi;
1633end;
1634
1635GAPDoc2HTMLProcs.Prop := function(r, str)
1636  GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.Prop);
1637end;
1638
1639GAPDoc2HTMLProcs.Attr := function(r, str)
1640  GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.Attr);
1641end;
1642
1643GAPDoc2HTMLProcs.Var := function(r, str)
1644  GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.Var);
1645end;
1646
1647GAPDoc2HTMLProcs.Fam := function(r, str)
1648  GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.Fam);
1649end;
1650
1651GAPDoc2HTMLProcs.InfoClass := function(r, str)
1652  GAPDoc2HTMLProcs.LikeFunc(r, str, GAPDocTexts.d.InfoClass);
1653end;
1654
1655##  using the HelpData(.., .., "ref") interface
1656GAPDoc2HTMLProcs.ResolveExternalRef := function(bookname,  label, nr)
1657  local info, match, res;
1658  info := HELP_BOOK_INFO(bookname);
1659  if info = fail then
1660    return fail;
1661  fi;
1662  match := Concatenation(HELP_GET_MATCHES(info, SIMPLE_STRING(label), true));
1663  if Length(match) < nr then
1664    return fail;
1665  fi;
1666  res := GetHelpDataRef(info, match[nr][2]);
1667  res[1] := SubstitutionSublist(res[1], " (not loaded): ", ": ", "one");
1668  return res;
1669end;
1670
1671##  helper for external URLs, remove GAPDocStyle part and maybe add "_mj"
1672GAPDoc2HTMLProcs.AdjustExtURL := function(r, url)
1673  local pos, pos2, res, fnam, mjnam;
1674  pos := PositionSublist(url, "?GAPDocStyle=");
1675  if pos <> fail then
1676    pos2 := Position(url, '#', pos);
1677    if pos2 = fail then
1678      pos2 := Length(url)+1;
1679    fi;
1680    res := url{[1..pos-1]};
1681    Append(res, url{[pos2..Length(url)]});
1682  else
1683    res :=url;
1684  fi;
1685  if r.root.mathmode = "MathJax" then
1686    pos := Position(res, '#');
1687    if pos <> fail then
1688      fnam := res{[1..pos-1]};
1689    else
1690      pos := Length(res)+1;
1691      fnam := res;
1692    fi;
1693    if Length(fnam) >= 5 and fnam{[Length(fnam)-4..Length(fnam)]} = ".html" then
1694      mjnam := Concatenation(fnam{[1..Length(fnam)-5]}, "_mj.html");
1695      if IsExistingFile(mjnam) then
1696        res := Concatenation(mjnam, res{[pos..Length(res)]});
1697      fi;
1698    fi;
1699  fi;
1700  return res;
1701end;
1702
1703##  a try to make it somewhat shorter than for the Text and LaTeX conversions
1704GAPDoc2HTMLProcs.Ref := function(r, str)
1705  local int,  txt,  ref,  lab,  attr,  sectlike, rattr;
1706
1707  rattr := GAPDoc2HTMLProcs.TextAttr.Ref;
1708  int := Difference(NamesOfComponents(r.attributes), ["BookName", "Label",
1709         "Style"]);
1710  if Length(int)>0 and int[1] <> "Text" then
1711    lab := r.attributes.(int[1]);
1712  else
1713    lab := "";
1714  fi;
1715  if IsBound(r.attributes.Label) then
1716    if Length(lab) > 0 then
1717      lab := Concatenation(lab, " (", r.attributes.Label, ")");
1718    else
1719      lab := r.attributes.Label;
1720    fi;
1721  fi;
1722  if IsBound(r.attributes.BookName) then
1723    ref := GAPDoc2HTMLProcs.ResolveExternalRef(r.attributes.BookName, lab, 1);
1724    if ref <> fail and ref[6] <> fail then
1725      ref[6] := GAPDoc2HTMLProcs.AdjustExtURL(r, ref[6]);
1726      if IsBound(GAPDoc2HTMLProcs.RelPath) and
1727         PositionSublist(ref[6], GAPInfo.MainRootPath) = 1 then
1728         ref[6] := Concatenation(GAPDoc2HTMLProcs.RelPath, "/",
1729                   ref[6]{[Length(GAPInfo.MainRootPath)+1..Length(ref[6])]});
1730      fi;
1731      if IsBound(r.attributes.Style) and r.attributes.Style = "Number" then
1732        ref := Concatenation("<a href=\"", ref[6], "\">",rattr[1],
1733               r.attributes.BookName, " ", ref[2], rattr[2],"</a>");
1734      elif IsBound(r.attributes.Text) then
1735        ref := Concatenation("<a href=\"", ref[6], "\">", rattr[1],
1736               r.attributes.Text, rattr[2], "</a>");
1737      else
1738        ref := Concatenation("<a href=\"", ref[6], "\">", rattr[1], ref[1],
1739               rattr[2], "</a>");
1740      fi;
1741    elif ref <> fail then
1742      ref := Concatenation(rattr[1], ref[1], rattr[2]);
1743    else
1744      if GAPDoc2HTMLProcs.FirstRun <> true then
1745        Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ",
1746                            r.attributes, "\n");
1747      fi;
1748      ref := Concatenation(rattr[1], "???", rattr[2]);
1749    fi;
1750  else
1751    if IsBound(r.root.labels.(lab)) then
1752      if not IsBound(r.attributes.Text) then
1753        ref := Concatenation("<a href=\"", r.root.labels.(lab)[2], "\">",
1754                             rattr[1], r.root.labels.(lab)[1], rattr[2],
1755                             "</a>");
1756      else
1757        ref := Concatenation("<a href=\"", r.root.labels.(lab)[2], "\">",
1758                             rattr[1], r.attributes.Text, rattr[2], "</a>");
1759      fi;
1760    else
1761      if GAPDoc2HTMLProcs.FirstRun <> true then
1762        Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ",
1763                            r.attributes, "\n");
1764      fi;
1765      ref := Concatenation(rattr[1], "???", rattr[2]);
1766    fi;
1767  fi;
1768  if Length(int)>0 and int[1] in [ "Func", "Oper", "Constr", "Meth", "Filt",
1769                               "Prop", "Attr", "Var", "Fam", "InfoClass" ] then
1770    attr := GAPDoc2HTMLProcs.TextAttr.Func;
1771    txt := Concatenation(attr[1],
1772             GAPDoc2HTMLProcs.EscapeAttrVal(r.attributes.(int[1])), attr[2]);
1773    # avoid reference to current subsection
1774    if IsBound(r.attributes.BookName) or not IsBound(r.root.labels.(lab))
1775      or GAPDoc2HTMLProcs.SectionNumber(r.count, "Subsection") <>
1776                                                r.root.labels.(lab)[1] then
1777      Append(txt, Concatenation(" (", ref, ")"));
1778    fi;
1779  elif Length(int)>0 and
1780       int[1] in [ "Sect", "Subsect", "Chap", "Appendix"] and
1781       IsBound(r.attributes.Style) and
1782       r.attributes.Style = "Text" then
1783    if IsBound(r.root.labeltexts.(lab)) then
1784      txt := Concatenation("<a href=\"",r.root.labels.(lab)[2],
1785             "\">",rattr[1],r.root.labeltexts.(lab),rattr[2],"</a>");
1786    else
1787      if GAPDoc2HTMLProcs.FirstRun <> true then
1788        Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ",
1789                            r.attributes, "\n");
1790      fi;
1791      txt := Concatenation(rattr[1], "???", rattr[2]);
1792    fi;
1793  else
1794    txt := ref;
1795  fi;
1796  Append(str, txt);
1797end;
1798
1799GAPDoc2HTMLProcs.Description := GAPDoc2HTMLContent;
1800
1801GAPDoc2HTMLProcs.Returns := function(r, par)
1802  local l;
1803  l := [];
1804  GAPDoc2HTMLContent(r, l);
1805  if Length(l) > 0 then
1806    l[2] := Concatenation("<p>", GAPDocTexts.d.Returns,
1807                          ": ", l[2]{[4..Length(l[2])]});
1808  fi;
1809  Append(par, l);
1810end;
1811
1812GAPDoc2HTMLProcs.ManSection := function(r, par)
1813  local   strn,  funclike,  i,  num,  s,  lab, ind;
1814
1815  # if there is a Heading then handle as subsection
1816  if ForAny(r.content, a-> IsRecord(a) and a.name = "Heading") then
1817    GAPDoc2HTMLProcs.ChapSect(r, par, "Subsection");
1818    return;
1819  fi;
1820  strn := "";
1821  # function like elements
1822  funclike := [ "Func", "Oper", "Constr", "Meth", "Filt", "Prop", "Attr",
1823                "Var", "Fam", "InfoClass" ];
1824
1825  # heading comes from name of first function like element
1826  i := 1;
1827  while not r.content[i].name in funclike do
1828    i := i+1;
1829  od;
1830
1831  num := GAPDoc2HTMLProcs.SectionNumber(r.count, "Subsection");
1832  s := Concatenation(num, " ",
1833         GAPDoc2HTMLProcs.EscapeAttrVal(r.content[i].attributes.Name));
1834  Add(par, r.count);
1835  # avoid MathJax interpretation in \(, \[ outside <code>
1836  if '\\' in s then
1837    s := Concatenation("<code>",s,"</code>");
1838  fi;
1839  Add(par, Concatenation("\n<h5>", s, "</h5>\n\n"));
1840
1841  # append to TOC as subsection
1842  lab := GAPDoc2HTMLProcs.SectionLabel(r, r.count, "Subsection");
1843  lab := Concatenation(lab[1], "#", lab[2]);
1844  ind := "<span class=\"ContSS\"><br /><span class=\"nocss\">&nbsp;&nbsp;</span>";
1845  Append(r.root.toc, Concatenation(ind, "<a href=\"", lab, "\">", s,
1846          "</a></span>\n"));
1847
1848  # label entry, if present
1849  if IsBound(r.attributes.Label) then
1850    r.root.labels.(r.attributes.Label) := [num, lab];
1851    r.root.labeltexts.(r.attributes.Label) := s;
1852  fi;
1853
1854  GAPDoc2HTMLContent(r, par);
1855end;
1856
1857GAPDoc2HTMLProcs.Mark := function(r, str)
1858  GAPDoc2HTMLProcs.WrapAttr(r, str, "Mark");
1859end;
1860
1861GAPDoc2HTMLProcs.Item := function(r, str)
1862  GAPDoc2HTMLContent(r, str);
1863end;
1864
1865# must do the complete formatting
1866GAPDoc2HTMLProcs.List := function(r, par)
1867  local   s,  a,  ss, i;
1868  s := "\n";
1869  if "Mark" in List(r.content, a-> a.name) then
1870    # a <dl> list
1871    Append(s, "<dl>\n");
1872    for a in r.content do
1873      if a.name = "Mark" then
1874        ss := "";
1875        GAPDoc2HTMLProcs.Mark(a, ss);
1876        Append(s, Concatenation("<dt>", ss, "</dt>\n"));
1877      elif a.name = "Item" then
1878        ss := "";
1879        GAPDoc2HTMLProcs.Item(a, ss);
1880        ss := Concatenation(Filtered(ss, IsString));
1881        ss := Concatenation("<dd>", ss, "</dd>\n");
1882        Append(s, ss);
1883      fi;
1884    od;
1885    Append(s, "</dl>\n");
1886  else
1887    # a <ul> list
1888    Append(s, "<ul>\n");
1889    for a in r.content do
1890      if a.name = "Item" then
1891        ss := "";
1892        GAPDoc2HTMLProcs.Item(a, ss);
1893        ss := Concatenation(Filtered(ss, IsString));
1894        Append(s, Concatenation("<li>", ss, "</li>\n"));
1895      fi;
1896    od;
1897    Append(s, "</ul>\n");
1898  fi;
1899  Add(par, r.count);
1900  Add(par, s);
1901end;
1902
1903##  and this is an <ol> list
1904GAPDoc2HTMLProcs.Enum := function(r, par)
1905  local   s,  i,  a,  ss,  num;
1906  s := "";
1907  # a <ul> list
1908  Append(s, "<ol>\n");
1909  for a in r.content do
1910    if a.name = "Item" then
1911      ss := "";
1912      GAPDoc2HTMLProcs.Item(a, ss);
1913      if not IsString(ss) then
1914        ss := Concatenation(Filtered(ss, IsString));
1915      fi;
1916      Append(s, Concatenation("<li>", ss, "</li>\n"));
1917    fi;
1918  od;
1919  Append(s, "</ol>\n");
1920  Add(par, r.count);
1921  Add(par, s);
1922end;
1923
1924GAPDoc2HTMLProcs.TheIndex := function(r, par)
1925  local   s;
1926
1927  # add index to TOC
1928  Append(r.root.toc, Concatenation("<div class=\"ContChap\"><a href=\"chapInd",
1929                        GAPDoc2HTMLProcs.ext, "\"><span class=\"Heading\">",
1930                        GAPDocTexts.d.Index, "</span></a></div>\n"));
1931  # the text, if available
1932  Add(par, r.count);
1933  if IsBound(r.root.indextext) then
1934    Add(par, Concatenation("\n<div class=\"index\">\n<h3>",
1935                           GAPDocTexts.d.Index, "</h3>\n\n",
1936          r.root.indextext, "<p> </p>\n</div>\n"));
1937  else
1938    Add(par,"<p>INDEX\n-----------</p>\n\n");
1939  fi;
1940end;
1941
1942GAPDoc2HTMLProcs.AltYes := function(r)
1943  local mark, mj, l;
1944  # recursively mark text as HTML code (no escaping of HTML markup)
1945  mark := function(r)
1946    local a;
1947    if IsString(r.content) then
1948      r.HTML := true;
1949    elif IsList(r.content) then
1950      for a in r.content do
1951        mark(a);
1952      od;
1953    fi;
1954  end;
1955  mj := r.root.mathmode="MathJax";
1956  if IsBound(r.attributes.Only) then
1957    l := SplitString(r.attributes.Only, "", "\n\r\t ,");
1958    if "HTML" in l then
1959      if "MathJax" in l then
1960        if mj then
1961          mark(r);
1962          return true;
1963        else
1964          return false;
1965        fi;
1966      elif "noMathJax" in l then
1967        if not mj then
1968          mark(r);
1969          return true;
1970        else
1971          return false;
1972        fi;
1973      else
1974        mark(r);
1975        return true;
1976      fi;
1977    else
1978      return false;
1979    fi;
1980  elif IsBound(r.attributes.Not) then
1981    # here, even if it is used, it is not HTML specific, so we not 'mark'
1982    l := SplitString(r.attributes.Not, "", "\n\r\t ,");
1983    if "HTML" in l then
1984      if "MathJax" in l then
1985        if mj then
1986          return false;
1987        else
1988          return true;
1989        fi;
1990      elif "noMathJax" in l then
1991        if not mj then
1992          return false;
1993        else
1994          return true;
1995        fi;
1996      else
1997        return false;
1998      fi;
1999    else
2000      return true;
2001    fi;
2002  fi;
2003  return true;
2004end;
2005
2006GAPDoc2HTMLProcs.Alt := function(r, str)
2007  if GAPDoc2HTMLProcs.AltYes(r) then
2008    GAPDoc2HTMLContent(r, str);
2009  fi;
2010end;
2011
2012# nothing special to do
2013GAPDoc2HTMLProcs.Address := function(r, str)
2014  GAPDoc2HTMLContent(r, str);
2015end;
2016
2017# copy a few entries with two element names
2018GAPDoc2HTMLProcs.E := GAPDoc2HTMLProcs.Emph;
2019GAPDoc2HTMLProcs.Keyword := GAPDoc2HTMLProcs.K;
2020GAPDoc2HTMLProcs.Code := GAPDoc2HTMLProcs.C;
2021GAPDoc2HTMLProcs.File := GAPDoc2HTMLProcs.F;
2022GAPDoc2HTMLProcs.Button := GAPDoc2HTMLProcs.B;
2023GAPDoc2HTMLProcs.Arg := GAPDoc2HTMLProcs.A;
2024GAPDoc2HTMLProcs.Quoted := GAPDoc2HTMLProcs.Q;
2025GAPDoc2HTMLProcs.Par := GAPDoc2HTMLProcs.P;
2026
2027# tables and utilities, not so nice since | and <Horline/> cannot be handled
2028GAPDoc2HTMLProcs.Table := function(r, s)
2029  local str, cap, al,  a, i;
2030  str := "";
2031  if not GAPDoc2HTMLProcs.AltYes(r) then
2032    return;
2033  fi;
2034  # label
2035  if IsBound(r.attributes.Label) then
2036    GAPDoc2HTMLProcs.Label(rec(count := r.count, root := r.root,
2037              attributes := rec(Name := r.attributes.Label)), str);
2038  fi;
2039  # alignments, table has borders and lines everywhere if any | or HorLine
2040  # is specified
2041  Append(str, "<div class=\"pcenter\"><table class=\"GAPDocTable");
2042  if not '|' in r.attributes.Align and
2043                                  Length(XMLElements(r, "HorLine")) = 0 then
2044    Append(str, "noborder");
2045  fi;
2046  Append(str, "\">\n");
2047  # the caption, if given
2048  cap := Filtered(r.content, a-> a.name = "Caption");
2049  if Length(cap) > 0 then
2050    GAPDoc2HTMLProcs.Caption1(cap[1], str);
2051  fi;
2052
2053  al := Filtered(r.attributes.Align, x-> x <> '|');
2054  for i in [1..Length(al)] do
2055    if al[i] = 'c' then
2056      al[i] := "\"tdcenter\"";
2057    elif al[i] = 'l' then
2058      al[i] := "\"tdleft\"";
2059    else
2060      al[i] := "\"tdright\"";
2061    fi;
2062  od;
2063  # the rows of the table
2064  for a in r.content do
2065    if a.name = "Row" then
2066      GAPDoc2HTMLProcs.Row(a, str, al);
2067    fi;
2068  od;
2069  Append(str, "</table><br /><p>&nbsp;</p><br />\n");
2070  Append(str, "</div>\n\n");
2071  Add(s, r.count);
2072  Add(s, str);
2073end;
2074
2075# do nothing, we call .Caption1 directly in .Table
2076GAPDoc2HTMLProcs.Caption := function(r, str)
2077  return;
2078end;
2079
2080# here the caption text is produced
2081GAPDoc2HTMLProcs.Caption1 := function(r, str)
2082  Append(str, Concatenation("<caption class=\"GAPDocTable\"><b>",
2083                                  GAPDocTexts.d.Table, ": </b>"));
2084  GAPDoc2HTMLContent(r, str);
2085  Append(str, "</caption>\n");
2086end;
2087
2088# cannot be chosen in HTML
2089GAPDoc2HTMLProcs.HorLine := function(r, str)
2090end;
2091
2092GAPDoc2HTMLProcs.Row := function(r, str, al)
2093  local s, i, l;
2094  Append(str, "<tr>\n");
2095  l := Filtered(r.content, a-> a.name = "Item");
2096  for i in [1..Length(l)] do
2097    s := "";
2098    GAPDoc2HTMLContent(l[i], s);
2099    if not IsString(s) then
2100      s := Concatenation(Filtered(s, IsString));
2101    fi;
2102    # throw away <p> tags in table entries
2103    if Length(s) > 5 and s{[1..3]} = "<p>" and
2104                         s{[Length(s)-5..Length(s)]} = "</p>\n\n" then
2105      s := s{[4..Length(s)-6]};
2106    fi;
2107    Append(str, Concatenation("<td class=", al[i], ">", s, "</td>\n"));
2108  od;
2109  while i < Length(al) do
2110    Append(str, "<td>&#160;</td>\n");
2111    i := i+1;
2112  od;
2113  Append(str, "</tr>\n");
2114end;
2115
2116
2117##
2118##  <#GAPDoc Label="GAPDoc2HTMLPrintHTMLFiles">
2119##  <ManSection >
2120##  <Func Arg="t[, path]" Name="GAPDoc2HTMLPrintHTMLFiles" />
2121##  <Returns>nothing</Returns>
2122##  <Description>
2123##  The  first   argument  must   be  a   result  returned   by  <Ref
2124##  Func="GAPDoc2HTML"/>. The second argument is a path for the files
2125##  to write, it can be given as string or directory object. The text
2126##  of  each  chapter is  written  into  a  separate file  with  name
2127##  <F>chap0.html</F>,  <F>chap1.html</F>,  ...,  <F>chapBib.html</F>,
2128##  and <F>chapInd.html</F>.<P/>
2129##
2130##  The <Package>MathJax</Package> versions are written to files
2131##  <F>chap0_mj.html</F>, ..., <F>chapInd_mj.html</F>. <P/>
2132##
2133##  The  experimental version  which  is  produced with  <C>tth</C>
2134##  uses  different  names  for   the  files,  namely
2135##  <F>chap0_sym.html</F>,  and so  on  for  files which  need
2136##  symbol  fonts.
2137##  <P/>
2138##
2139##  You should also add stylesheet files to the directory with the HTML
2140##  files, see <Ref Subsect="StyleSheets"/>.
2141##  </Description>
2142##  </ManSection>
2143##  <#/GAPDoc>
2144##
2145##  Finally a function to print the text files:
2146InstallGlobalFunction(GAPDoc2HTMLPrintHTMLFiles, function(t, path)
2147  local   a;
2148  if IsString(path) then
2149    path := Directory(path);
2150  fi;
2151  for a in NamesOfComponents(t) do
2152    if IsRecord(t.(a)) and IsBound(t.(a).text) then
2153      FileString(Filename(path, Concatenation("chap", a, t.ext)), t.(a).text);
2154    fi;
2155  od;
2156end);
2157
2158InstallGlobalFunction(CopyHTMLStyleFiles, function(dir)
2159  local d, todo, l, s, e, f;
2160  if not IsDirectory(dir) then
2161    dir := Directory(dir);
2162  fi;
2163  d := DirectoriesPackageLibrary("GAPDoc","styles")[1];
2164  todo := [];
2165  for f in DirectoryContents(d) do
2166    if f = "chooser.html" then
2167      Add(todo, f);
2168    else
2169      l := Length(f);
2170      if l > 3 and f{[l-2..l]} = ".js" then
2171        Add(todo, f);
2172      elif l > 4 and f{[l-3..l]} = ".css" then
2173        Add(todo, f);
2174      fi;
2175    fi;
2176  od;
2177  for f in todo do
2178    s := StringFile(Filename(d,f));
2179    if s = fail then
2180      Info(InfoGAPDoc, 1, "Cannot read file ", Filename(d,f), "\n");
2181    else
2182      e := FileString(Filename(dir,f), s);
2183      if e = fail then
2184        Info(InfoGAPDoc, 1, "Cannot write file ", Filename(dir,f), "\n");
2185      fi;
2186    fi;
2187  od;
2188end);
2189
2190