(***********************************************************************)
(*                             OCamldoc                                *)
(*                                                                     *)
(*            Maxence Guesdon, projet Cristal, INRIA Rocquencourt      *)
(*                                                                     *)
(*  Copyright 2001 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the Q Public License version 1.0.               *)
(*                                                                     *)
(***********************************************************************)

(* $Id: odoc_html.ml,v 1.64.2.1 2008/11/10 13:03:55 guesdon Exp $ *)

(** Generation of html documentation.*)

let print_DEBUG s = print_string s ; print_newline ()

open Odoc_info
open Parameter
open Value
open Type
open Exception
open Class
open Module


(** The functions used for naming files and html marks.*)
module Naming =
  struct
    (** The prefix for types marks. *)
    let mark_type = "TYPE"

    (** The prefix for functions marks. *)
    let mark_function = "FUN"

    (** The prefix for exceptions marks. *)
    let mark_exception = "EXCEPTION"

    (** The prefix for values marks. *)
    let mark_value = "VAL"

    (** The prefix for attributes marks. *)
    let mark_attribute = "ATT"

    (** The prefix for methods marks. *)
    let mark_method = "METHOD"

    (** The prefix for code files.. *)
    let code_prefix = "code_"

    (** The prefix for type files.. *)
    let type_prefix = "type_"

    (** Return the two html files names for the given module or class name.*)
    let html_files name =
      let html_file = name^".html" in
      let html_frame_file = name^"-frame.html" in
      (html_file, html_frame_file)

    (** Return the target for the given prefix and simple name. *)
    let target pref simple_name = pref^simple_name

    (** Return the complete link target (file#target) for the given prefix string and complete name.*)
    let complete_target pref complete_name =
      let simple_name = Name.simple complete_name in
      let module_name =
        let s = Name.father complete_name in
        if s = "" then simple_name else s
      in
      let (html_file, _) = html_files module_name in
      html_file^"#"^(target pref simple_name)

    (** Return the link target for the given type. *)
    let type_target t = target mark_type (Name.simple t.ty_name)

    (** Return the complete link target for the given type. *)
    let complete_type_target t = complete_target mark_type t.ty_name

    (** Return the link target for the given exception. *)
    let exception_target e = target mark_exception (Name.simple e.ex_name)

    (** Return the complete link target for the given exception. *)
    let complete_exception_target e = complete_target mark_exception e.ex_name

    (** Return the link target for the given value. *)
    let value_target v = target mark_value (Name.simple v.val_name)

    (** Return the given value name where symbols accepted in infix values
       are replaced by strings, to avoid clashes with the filesystem.*)
    let subst_infix_symbols name =
      let len = String.length name in
      let buf = Buffer.create len in
      let ch c = Buffer.add_char buf c in
      let st s = Buffer.add_string buf s in
      for i = 0 to len - 1 do
        match name.[i] with
        | '|' -> st "_pipe_"
        | '<' -> st "_lt_"
        | '>' -> st "_gt_"
        | '@' -> st "_at_"
        | '^' -> st "_exp_"
        | '&' -> st "_amp_"
        | '+' -> st "_plus_"
        | '-' -> st "_minus_"
        | '*' -> st "_star_"
        | '/' -> st "_slash_"
        | '$' -> st "_dollar_"
        | '%' -> st "_percent_"
        | '=' -> st "_equal_"
        | ':' -> st "_column_"
        | '~' -> st "_tilde_"
        | '!' -> st "_bang_"
        | '?' -> st "_questionmark_"
        | c -> ch c
      done;
      Buffer.contents buf

    (** Return the complete link target for the given value. *)
    let complete_value_target v = complete_target mark_value v.val_name

    (** Return the complete filename for the code of the given value. *)
    let file_code_value_complete_target v =
      let f = code_prefix^mark_value^(subst_infix_symbols v.val_name)^".html" in
      f

    (** Return the link target for the given attribute. *)
    let attribute_target a = target mark_attribute (Name.simple a.att_value.val_name)

    (** Return the complete link target for the given attribute. *)
    let complete_attribute_target a = complete_target mark_attribute a.att_value.val_name

    (** Return the complete filename for the code of the given attribute. *)
    let file_code_attribute_complete_target a =
      let f = code_prefix^mark_attribute^a.att_value.val_name^".html" in
      f

    (** Return the link target for the given method. *)
    let method_target m = target mark_method (Name.simple m.met_value.val_name)

    (** Return the complete link target for the given method. *)
    let complete_method_target m = complete_target mark_method m.met_value.val_name

    (** Return the complete filename for the code of the given method. *)
    let file_code_method_complete_target m =
      let f = code_prefix^mark_method^m.met_value.val_name^".html" in
      f

    (** Return the link target for the given label section. *)
    let label_target l = target "" l

    (** Return the complete link target for the given section label. *)
    let complete_label_target l = complete_target "" l

    (** Return the complete filename for the code of the type of the
       given module or module type name. *)
    let file_type_module_complete_target name =
      let f = type_prefix^name^".html" in
      f

    (** Return the complete filename for the code of the
       given module name. *)
    let file_code_module_complete_target name =
      let f = code_prefix^name^".html" in
      f

    (** Return the complete filename for the code of the type of the
       given class or class type name. *)
    let file_type_class_complete_target name =
      let f = type_prefix^name^".html" in
      f
  end

module StringSet = Set.Make (struct type t = string let compare = compare end)

(** A class with a method to colorize a string which represents OCaml code. *)
class ocaml_code =
  object(self)
    method html_of_code b ?(with_pre=true) code =
      Odoc_ocamlhtml.html_of_code b ~with_pre: with_pre code
  end

let new_buf () = Buffer.create 1024
let bp = Printf.bprintf
let bs = Buffer.add_string


(** Generation of html code from text structures. *)
class virtual text =
  object (self)
    (** We want to display colorized code. *)
    inherit ocaml_code

    (** Escape the strings which would clash with html syntax, and
       make some replacements (double newlines replaced by <br>). *)
    method escape s = Odoc_ocamlhtml.escape_base s

    method keep_alpha_num s =
      let len = String.length s in
      let buf = Buffer.create len in
      for i = 0 to len - 1 do
        match s.[i] with
          'a'..'z' | 'A'..'Z' | '0'..'9' -> Buffer.add_char buf s.[i]
        | _ -> ()
      done;
      Buffer.contents buf

    (** Return a label created from the first sentence of a text. *)
    method label_of_text t=
      let t2 = Odoc_info.first_sentence_of_text t in
      let s = Odoc_info.string_of_text t2 in
      let s2 = self#keep_alpha_num s in
      s2

    (** Create a label for the associated title.
       Return the label specified by the user or a label created
       from the title level and the first sentence of the title. *)
    method create_title_label (n,label_opt,t) =
      match label_opt with
        Some s -> s
      | None -> Printf.sprintf "%d_%s" n (self#label_of_text t)

    (** Print the html code corresponding to the [text] parameter. *)
    method html_of_text b t =
      List.iter (self#html_of_text_element b) t

    (** Print the html code for the [text_element] in parameter. *)
    method html_of_text_element b te =
      print_DEBUG "text::html_of_text_element";
      match te with
      | Odoc_info.Raw s -> self#html_of_Raw b s
      | Odoc_info.Code s -> self#html_of_Code b s
      | Odoc_info.CodePre s -> self#html_of_CodePre b s
      | Odoc_info.Verbatim s -> self#html_of_Verbatim b s
      | Odoc_info.Bold t -> self#html_of_Bold b t
      | Odoc_info.Italic t -> self#html_of_Italic b t
      | Odoc_info.Emphasize t -> self#html_of_Emphasize b t
      | Odoc_info.Center t -> self#html_of_Center b t
      | Odoc_info.Left t -> self#html_of_Left b t
      | Odoc_info.Right t -> self#html_of_Right b t
      | Odoc_info.List tl -> self#html_of_List b tl
      | Odoc_info.Enum tl -> self#html_of_Enum b tl
      | Odoc_info.Newline -> self#html_of_Newline b
      | Odoc_info.Block t -> self#html_of_Block b t
      | Odoc_info.Title (n, l_opt, t) -> self#html_of_Title b n l_opt t
      | Odoc_info.Latex s -> self#html_of_Latex b s
      | Odoc_info.Link (s, t) -> self#html_of_Link b s t
      | Odoc_info.Ref (name, ref_opt) -> self#html_of_Ref b name ref_opt
      | Odoc_info.Superscript t -> self#html_of_Superscript b t
      | Odoc_info.Subscript t -> self#html_of_Subscript b t
      | Odoc_info.Module_list l -> self#html_of_Module_list b l
      | Odoc_info.Index_list -> self#html_of_Index_list b
      | Odoc_info.Custom (s,t) -> self#html_of_custom_text b s t

    method html_of_custom_text b s t = ()

    method html_of_Raw b s = bs b (self#escape s)

    method html_of_Code b s =
      if !Args.colorize_code then
        self#html_of_code b ~with_pre: false s
      else
        (
         bs b "<code class=\"";
         bs b Odoc_ocamlhtml.code_class ;
         bs b "\">";
         bs b (self#escape s);
         bs b "</code>"
        )

    method html_of_CodePre =
        let remove_useless_newlines s =
          let len = String.length s in
          let rec iter_first n =
            if n >= len then
              None
            else
              match s.[n] with
              | '\n' -> iter_first (n+1)
              | _ -> Some n
          in
          match iter_first 0 with
            None -> ""
          | Some first ->
              let rec iter_last n =
                if n <= first then
                  None
                else
                  match s.[n] with
                    '\t'  -> iter_last (n-1)
                  | _ -> Some n
              in
              match iter_last (len-1) with
                None -> String.sub s first 1
              | Some last -> String.sub s first ((last-first)+1)
        in
        fun b s ->
      if !Args.colorize_code then
        (
         bs b "<pre></pre>";
         self#html_of_code b (remove_useless_newlines s);
         bs b "<pre></pre>"
        )
      else
        (
         bs b "<pre><code class=\"";
         bs b Odoc_ocamlhtml.code_class;
         bs b "\">" ;
         bs b (self#escape (remove_useless_newlines s));
         bs b "</code></pre>"
        )

    method html_of_Verbatim b s =
      bs b "<pre>";
      bs b (self#escape s);
      bs b "</pre>"

    method html_of_Bold b t =
      bs b "<b>";
      self#html_of_text b t;
      bs b "</b>"

    method html_of_Italic b t =
      bs b "<i>" ;
      self#html_of_text b t;
      bs b "</i>"

    method html_of_Emphasize b t =
      bs b "<em>" ;
      self#html_of_text b t ;
      bs b "</em>"

    method html_of_Center b t =
      bs b "<center>";
      self#html_of_text b t;
      bs b "</center>"

    method html_of_Left b t =
      bs b "<div align=left>";
      self#html_of_text b t;
      bs b "</div>"

    method html_of_Right b t =
      bs b "<div align=right>";
      self#html_of_text b t;
      bs b "</div>"

    method html_of_List b tl =
      bs b "<ul>\n";
      List.iter
        (fun t -> bs b "<li>"; self#html_of_text b t; bs b "</li>\n")
        tl;
      bs b "</ul>\n"

    method html_of_Enum b tl =
      bs b "<OL>\n";
      List.iter
        (fun t -> bs b "<li>"; self#html_of_text b t; bs b"</li>\n")
        tl;
      bs b "</OL>\n"

    method html_of_Newline b = bs b "\n<p>\n"

    method html_of_Block b t =
      bs b "<blockquote>\n";
      self#html_of_text b t;
      bs b "</blockquote>\n"

    method html_of_Title b n label_opt t =
      let label1 = self#create_title_label (n, label_opt, t) in
      bs b "<a name=\"";
      bs b (Naming.label_target label1);
      bs b "\"></a>\n";
      let (tag_o, tag_c) =
        if n > 6 then
          (Printf.sprintf "div class=\"h%d\"" n, "div")
        else
          let t = Printf.sprintf "h%d" n in (t, t)
      in
      bs b "<";
      bs b tag_o;
      bs b ">";
      self#html_of_text b t;
      bs b "</";
      bs b tag_c;
      bs b ">"

    method html_of_Latex b _ = ()
      (* don't care about LaTeX stuff in HTML. *)

    method html_of_Link b s t =
      bs b "<a href=\"";
      bs b s ;
      bs b "\">";
      self#html_of_text b t;
      bs b "</a>"

    method html_of_Ref b name ref_opt =
      match ref_opt with
        None ->
          self#html_of_text_element b (Odoc_info.Code name)
      | Some kind ->
          let h name = Odoc_info.Code (Odoc_info.use_hidden_modules name) in
          let (target, text) =
            match kind with
              Odoc_info.RK_module
            | Odoc_info.RK_module_type
            | Odoc_info.RK_class
            | Odoc_info.RK_class_type ->
                let (html_file, _) = Naming.html_files name in
                (html_file, h name)
            | Odoc_info.RK_value -> (Naming.complete_target Naming.mark_value name, h name)
            | Odoc_info.RK_type -> (Naming.complete_target Naming.mark_type name, h name)
            | Odoc_info.RK_exception -> (Naming.complete_target Naming.mark_exception name, h name)
            | Odoc_info.RK_attribute -> (Naming.complete_target Naming.mark_attribute name, h name)
            | Odoc_info.RK_method -> (Naming.complete_target Naming.mark_method name, h name)
            | Odoc_info.RK_section t -> (Naming.complete_label_target name,
                                         Odoc_info.Italic [Raw (Odoc_info.string_of_text t)])
          in
          bs b ("<a href=\""^target^"\">");
          self#html_of_text_element b text;
          bs b "</a>"

    method html_of_Superscript b t =
      bs b "<sup class=\"superscript\">";
      self#html_of_text b t;
      bs b "</sup>"

    method html_of_Subscript b t =
      bs b "<sub class=\"subscript\">";
      self#html_of_text b t;
      bs b "</sub>"

    method virtual html_of_info_first_sentence : _

    method html_of_Module_list b l =
      bs b "<br>\n<table class=\"indextable\">\n";
      List.iter
        (fun name ->
          bs b "<tr><td>";
          (
           try
             let m =
               List.find (fun m -> m.m_name = name) self#list_modules
             in
             let (html, _) = Naming.html_files m.m_name in
             bp b "<a href=\"%s\">%s</a></td>" html m.m_name;
             bs b "<td>";
             self#html_of_info_first_sentence b m.m_info;
           with
             Not_found ->
               Odoc_messages.pwarning (Odoc_messages.cross_module_not_found name);
               bp b "%s</td><td>" name
          );
          bs b "</td></tr>\n"
        )
        l;
      bs b "</table>\n"

    method html_of_Index_list b =
      let index_if_not_empty l url m =
        match l with
          [] -> ()
        | _ -> bp b "<a href=\"%s\">%s</a><br>\n" url m
      in
      index_if_not_empty self#list_types self#index_types Odoc_messages.index_of_types;
      index_if_not_empty self#list_exceptions self#index_exceptions Odoc_messages.index_of_exceptions;
      index_if_not_empty self#list_values self#index_values Odoc_messages.index_of_values;
      index_if_not_empty self#list_attributes self#index_attributes Odoc_messages.index_of_attributes;
      index_if_not_empty self#list_methods self#index_methods Odoc_messages.index_of_methods;
      index_if_not_empty self#list_classes self#index_classes Odoc_messages.index_of_classes;
      index_if_not_empty self#list_class_types self#index_class_types Odoc_messages.index_of_class_types;
      index_if_not_empty self#list_modules self#index_modules Odoc_messages.index_of_modules;
      index_if_not_empty self#list_module_types self#index_module_types Odoc_messages.index_of_module_types

    method virtual list_types : Odoc_info.Type.t_type list
    method virtual index_types : string
    method virtual list_exceptions : Odoc_info.Exception.t_exception list
    method virtual index_exceptions : string
    method virtual list_values : Odoc_info.Value.t_value list
    method virtual index_values : string
    method virtual list_attributes : Odoc_info.Value.t_attribute list
    method virtual index_attributes : string
    method virtual list_methods : Odoc_info.Value.t_method list
    method virtual index_methods : string
    method virtual list_classes : Odoc_info.Class.t_class list
    method virtual index_classes : string
    method virtual list_class_types : Odoc_info.Class.t_class_type list
    method virtual index_class_types : string
    method virtual list_modules : Odoc_info.Module.t_module list
    method virtual index_modules : string
    method virtual list_module_types : Odoc_info.Module.t_module_type list
    method virtual index_module_types : string

  end

(** A class used to generate html code for info structures. *)
class virtual info =
  object (self)
    (** The list of pairs [(tag, f)] where [f] is a function taking
       the [text] associated to [tag] and returning html code.
       Add a pair here to handle a tag.*)
    val mutable tag_functions = ([] : (string * (Odoc_info.text -> string)) list)

    (** The method used to get html code from a [text]. *)
    method virtual html_of_text : Buffer.t -> Odoc_info.text -> unit

    (** Print html for an author list. *)
    method html_of_author_list b l =
      match l with
        [] -> ()
      | _ ->
          bp b "<b>%s:</b> %s<br>\n"
            Odoc_messages.authors
            (String.concat ", " l)

    (** Print html code for the given optional version information.*)
    method html_of_version_opt b v_opt =
      match v_opt with
        None -> ()
      | Some v ->
           bp b "<b>%s:</b> %s<br>\n" Odoc_messages.version v

    (** Print html code for the given optional since information.*)
    method html_of_since_opt b s_opt =
      match s_opt with
        None -> ()
      | Some s ->
          bp b "<b>%s</b> %s<br>\n" Odoc_messages.since s

    (** Print html code for the given list of raised exceptions.*)
    method html_of_raised_exceptions b l =
      match l with
        [] -> ()
      | (s, t) :: [] ->
          bp b "<b>%s</b> <code>%s</code> "
            Odoc_messages.raises
            s;
          self#html_of_text b t;
          bs b "<br>\n"
      | _ ->
          bp b "<b>%s</b><ul>" Odoc_messages.raises;
          List.iter
            (fun (ex, desc) ->
              bp b "<li><code>%s</code> " ex ;
              self#html_of_text b desc;
              bs b "</li>\n"
            )
            l;
          bs b "</ul>\n"

    (** Print html code for the given "see also" reference. *)
    method html_of_see b (see_ref, t)  =
      let t_ref =
        match see_ref with
          Odoc_info.See_url s -> [ Odoc_info.Link (s, t) ]
        | Odoc_info.See_file s -> (Odoc_info.Code s) :: (Odoc_info.Raw " ") :: t
        | Odoc_info.See_doc s -> (Odoc_info.Italic [Odoc_info.Raw s]) :: (Odoc_info.Raw " ") :: t
      in
      self#html_of_text b t_ref

    (** Print html code for the given list of "see also" references.*)
    method html_of_sees b l =
      match l with
        [] -> ()
      | see :: [] ->
          bp b "<b>%s</b> " Odoc_messages.see_also;
          self#html_of_see b see;
          bs b "<br>\n"
      | _ ->
          bp b "<b>%s</b><ul>" Odoc_messages.see_also;
          List.iter
            (fun see ->
              bs b "<li>" ;
              self#html_of_see b see;
              bs b "</li>\n"
            )
            l;
          bs b "</ul>\n"

    (** Print html code for the given optional return information.*)
    method html_of_return_opt b return_opt =
      match return_opt with
        None -> ()
      | Some s ->
          bp b "<b>%s</b> " Odoc_messages.returns;
          self#html_of_text b s;
          bs b "<br>\n"

    (** Print html code for the given list of custom tagged texts. *)
    method html_of_custom b l =
      List.iter
        (fun (tag, text) ->
          try
            let f = List.assoc tag tag_functions in
            Buffer.add_string b (f text)
          with
            Not_found ->
              Odoc_info.warning (Odoc_messages.tag_not_handled tag)
        )
        l

    (** Print html code for a description, except for the [i_params] field.
       @param indent can be specified not to use the style of info comments;
       default is [true].
    *)
    method html_of_info ?(indent=true) b info_opt =
      match info_opt with
        None ->
          ()
      | Some info ->
          let module M = Odoc_info in
          if indent then bs b "<div class=\"info\">\n";
          (
           match info.M.i_deprecated with
            None -> ()
           | Some d ->
               bs b "<span class=\"warning\">";
               bs b Odoc_messages.deprecated ;
               bs b "</span>" ;
               self#html_of_text b d;
               bs b "<br>\n"
          );
          (
           match info.M.i_desc with
             None -> ()
           | Some d when d = [Odoc_info.Raw ""] -> ()
           | Some d -> self#html_of_text b d; bs b "<br>\n"
          );
          self#html_of_author_list b info.M.i_authors;
          self#html_of_version_opt b info.M.i_version;
          self#html_of_since_opt b info.M.i_since;
          self#html_of_raised_exceptions b info.M.i_raised_exceptions;
          self#html_of_return_opt b info.M.i_return_value;
          self#html_of_sees b info.M.i_sees;
          self#html_of_custom b info.M.i_custom;
          if indent then bs b "</div>\n"

    (** Print html code for the first sentence of a description.
       The titles and lists in this first sentence has been removed.*)
    method html_of_info_first_sentence b info_opt =
      match info_opt with
        None -> ()
      | Some info ->
          let module M = Odoc_info in
          let dep = info.M.i_deprecated <> None in
          bs b "<div class=\"info\">\n";
          if dep then bs b "<font color=\"#CCCCCC\">";
          (
           match info.M.i_desc with
             None -> ()
           | Some d when d = [Odoc_info.Raw ""] -> ()
           | Some d ->
               self#html_of_text b
                 (Odoc_info.text_no_title_no_list
                    (Odoc_info.first_sentence_of_text d));
               bs b "\n"
          );
          if dep then bs b "</font>";
          bs b "</div>\n"

  end



let opt = Odoc_info.apply_opt

let print_concat b sep f =
  let rec iter = function
      [] -> ()
    | [c] -> f c
    | c :: q ->
        f c;
        bs b sep;
        iter q
  in
  iter

let newline_to_indented_br s =
  let len = String.length s in
  let b = Buffer.create len in
  for i = 0 to len - 1 do
    match s.[i] with
      '\n' -> Buffer.add_string b "<br>     "
    | c -> Buffer.add_char b c
  done;
  Buffer.contents b

(** This class is used to create objects which can generate a simple html documentation. *)
class html =
  object (self)
    inherit text
    inherit info

    val mutable doctype =
      "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
    val mutable character_encoding =
      "<meta content=\"text/html; charset=iso-8859-1\" http-equiv=\"Content-Type\">\n"

    (** The default style options. *)
    val mutable default_style_options =
      ["a:visited {color : #416DFF; text-decoration : none; }" ;
        "a:link {color : #416DFF; text-decoration : none;}" ;
        "a:hover {color : Red; text-decoration : none; background-color: #5FFF88}" ;
        "a:active {color : Red; text-decoration : underline; }" ;
        ".keyword { font-weight : bold ; color : Red }" ;
        ".keywordsign { color : #C04600 }" ;
        ".superscript { font-size : 4 }" ;
        ".subscript { font-size : 4 }" ;
        ".comment { color : Green }" ;
        ".constructor { color : Blue }" ;
        ".type { color : #5C6585 }" ;
        ".string { color : Maroon }" ;
        ".warning { color : Red ; font-weight : bold }" ;
        ".info { margin-left : 3em; margin-right : 3em }" ;
        ".param_info { margin-top: 4px; margin-left : 3em; margin-right : 3em }" ;
        ".code { color : #465F91 ; }" ;
        "h1 { font-size : 20pt ; text-align: center; }" ;

        "h2 { font-size : 20pt ; border: 1px solid #000000; "^
        "margin-top: 5px; margin-bottom: 2px;"^
        "text-align: center; background-color: #90BDFF ;"^
        "padding: 2px; }" ;

        "h3 { font-size : 20pt ; border: 1px solid #000000; "^
        "margin-top: 5px; margin-bottom: 2px;"^
        "text-align: center; background-color: #90DDFF ;"^
        "padding: 2px; }" ;

        "h4 { font-size : 20pt ; border: 1px solid #000000; "^
        "margin-top: 5px; margin-bottom: 2px;"^
        "text-align: center; background-color: #90EDFF ;"^
        "padding: 2px; }" ;

        "h5 { font-size : 20pt ; border: 1px solid #000000; "^
        "margin-top: 5px; margin-bottom: 2px;"^
        "text-align: center; background-color: #90FDFF ;"^
        "padding: 2px; }" ;

        "h6 { font-size : 20pt ; border: 1px solid #000000; "^
        "margin-top: 5px; margin-bottom: 2px;"^
        "text-align: center; background-color: #C0FFFF ; "^
        "padding: 2px; }" ;

        "div.h7 { font-size : 20pt ; border: 1px solid #000000; "^
        "margin-top: 5px; margin-bottom: 2px;"^
        "text-align: center; background-color: #E0FFFF ; "^
        "padding: 2px; }" ;

        "div.h8 { font-size : 20pt ; border: 1px solid #000000; "^
        "margin-top: 5px; margin-bottom: 2px;"^
        "text-align: center; background-color: #F0FFFF ; "^
        "padding: 2px; }" ;

        "div.h9 { font-size : 20pt ; border: 1px solid #000000; "^
        "margin-top: 5px; margin-bottom: 2px;"^
        "text-align: center; background-color: #FFFFFF ; "^
        "padding: 2px; }" ;

        ".typetable { border-style : hidden }" ;
        ".indextable { border-style : hidden }" ;
        ".paramstable { border-style : hidden ; padding: 5pt 5pt}" ;
        "body { background-color : White }" ;
        "tr { background-color : White }" ;
        "td.typefieldcomment { background-color : #FFFFFF ; font-size: smaller ;}" ;
        "pre { margin-bottom: 4px }" ;

        "div.sig_block {margin-left: 2em}" ;
      ]

    (** The style file for all pages. *)
    val mutable style_file = "style.css"

    (** The code to import the style. Initialized in [init_style]. *)
    val mutable style = ""

    (** The known types names.
       Used to know if we must create a link to a type
       when printing a type. *)
    val mutable known_types_names = StringSet.empty

    (** The known class and class type names.
       Used to know if we must create a link to a class
       or class type or not when printing a type. *)
    val mutable known_classes_names = StringSet.empty

    (** The known modules and module types names.
       Used to know if we must create a link to a type or not
       when printing a module type. *)
    val mutable known_modules_names = StringSet.empty

    method index_prefix =
      if !Odoc_args.out_file = Odoc_messages.default_out_file then
        "index"
      else
        Filename.basename !Odoc_args.out_file

    (** The main file. *)
    method index =
      let p = self#index_prefix in
      Printf.sprintf "%s.html" p

    (** The file for the index of values. *)
    method index_values = Printf.sprintf "%s_values.html" self#index_prefix
    (** The file for the index of types. *)
    method index_types = Printf.sprintf "%s_types.html" self#index_prefix
    (** The file for the index of exceptions. *)
    method index_exceptions = Printf.sprintf "%s_exceptions.html" self#index_prefix
    (** The file for the index of attributes. *)
    method index_attributes = Printf.sprintf "%s_attributes.html" self#index_prefix
    (** The file for the index of methods. *)
    method index_methods = Printf.sprintf "%s_methods.html" self#index_prefix
    (** The file for the index of classes. *)
    method index_classes = Printf.sprintf "%s_classes.html" self#index_prefix
    (** The file for the index of class types. *)
    method index_class_types = Printf.sprintf "%s_class_types.html" self#index_prefix
    (** The file for the index of modules. *)
    method index_modules = Printf.sprintf "%s_modules.html" self#index_prefix
    (** The file for the index of module types. *)
    method index_module_types = Printf.sprintf "%s_module_types.html" self#index_prefix


    (** The list of attributes. Filled in the [generate] method. *)
    val mutable list_attributes = []
    method list_attributes = list_attributes
    (** The list of methods. Filled in the [generate] method. *)
    val mutable list_methods = []
    method list_methods = list_methods
    (** The list of values. Filled in the [generate] method. *)
    val mutable list_values = []
    method list_values = list_values
    (** The list of exceptions. Filled in the [generate] method. *)
    val mutable list_exceptions = []
    method list_exceptions = list_exceptions
    (** The list of types. Filled in the [generate] method. *)
    val mutable list_types = []
    method list_types = list_types
    (** The list of modules. Filled in the [generate] method. *)
    val mutable list_modules = []
    method list_modules = list_modules
    (** The list of module types. Filled in the [generate] method. *)
    val mutable list_module_types = []
    method list_module_types = list_module_types
    (** The list of classes. Filled in the [generate] method. *)
    val mutable list_classes = []
    method list_classes = list_classes
    (** The list of class types. Filled in the [generate] method. *)
    val mutable list_class_types = []
    method list_class_types = list_class_types

    (** The header of pages. Must be prepared by the [prepare_header] method.*)
    val mutable header = fun b -> fun ?(nav=None) -> fun ?(comments=[]) -> fun _ -> ()

    (** Init the style. *)
    method init_style =
      (match !Args.css_style with
        None ->
          let default_style = String.concat "\n" default_style_options in
          (
           try
             let file = Filename.concat !Args.target_dir style_file in
             if Sys.file_exists file then
               Odoc_info.verbose (Odoc_messages.file_exists_dont_generate file)
             else
               (
                let chanout = open_out file in
                output_string chanout default_style ;
                flush chanout ;
                close_out chanout;
                Odoc_info.verbose (Odoc_messages.file_generated file)
               )
           with
             Sys_error s ->
               prerr_endline s ;
               incr Odoc_info.errors ;
          )
      | Some f ->
          style_file <- f
      );
      style <- "<link rel=\"stylesheet\" href=\""^style_file^"\" type=\"text/css\">\n"

    (** Get the title given by the user *)
    method title = match !Args.title with None -> "" | Some t -> self#escape t

    (** Get the title given by the user completed with the given subtitle. *)
    method inner_title s =
      (match self#title with "" -> "" | t -> t^" : ")^
      (self#escape s)

    (** Get the page header. *)
    method print_header b ?nav ?comments title = header b ?nav ?comments title

    (** A function to build the header of pages. *)
    method prepare_header module_list =
      let f b ?(nav=None) ?(comments=[]) t  =
        let link_if_not_empty l m url =
          match l with
            [] -> ()
          | _ ->
              bp b "<link title=\"%s\" rel=Appendix href=\"%s\">\n" m url
        in
        bs b "<head>\n";
        bs b style;
        bs b character_encoding ;
        bs b "<link rel=\"Start\" href=\"";
        bs b self#index;
        bs b "\">\n" ;
        (
         match nav with
           None -> ()
         | Some (pre_opt, post_opt, name) ->
             (match pre_opt with
               None -> ()
             | Some name ->
                 bp b "<link rel=\"previous\" href=\"%s\">\n"
                   (fst (Naming.html_files name));
             );
             (match post_opt with
               None -> ()
             | Some name ->
                 bp b "<link rel=\"next\" href=\"%s\">\n"
                   (fst (Naming.html_files name));
             );
             (
              let father = Name.father name in
              let href = if father = "" then self#index else fst (Naming.html_files father) in
              bp b "<link rel=\"Up\" href=\"%s\">\n" href
             )
        );
        link_if_not_empty self#list_types Odoc_messages.index_of_types self#index_types;
        link_if_not_empty self#list_exceptions Odoc_messages.index_of_exceptions self#index_exceptions;
        link_if_not_empty self#list_values Odoc_messages.index_of_values self#index_values;
        link_if_not_empty self#list_attributes Odoc_messages.index_of_attributes self#index_attributes;
        link_if_not_empty self#list_methods Odoc_messages.index_of_methods self#index_methods;
        link_if_not_empty self#list_classes Odoc_messages.index_of_classes self#index_classes;
        link_if_not_empty self#list_class_types Odoc_messages.index_of_class_types self#index_class_types;
        link_if_not_empty self#list_modules Odoc_messages.index_of_modules self#index_modules;
        link_if_not_empty self#list_module_types Odoc_messages.index_of_module_types self#index_module_types;
        let print_one m =
          let html_file = fst (Naming.html_files m.m_name) in
          bp b "<link title=\"%s\" rel=\"Chapter\" href=\"%s\">"
            m.m_name html_file
        in
        print_concat b "\n" print_one module_list;
        self#html_sections_links b comments;
        bs b "<title>";
        bs b t ;
        bs b "</title>\n</head>\n"
      in
      header <- f

    (** Build the html code for the link tags in the header, defining section and
       subsections for the titles found in the given comments.*)
    method html_sections_links b comments =
      let titles = List.flatten (List.map Odoc_info.get_titles_in_text comments) in
      let levels =
        let rec iter acc l =
          match l with
            [] -> acc
          | (n,_,_) :: q ->
              if List.mem n acc
              then iter acc q
              else iter (n::acc) q
        in
        iter [] titles
      in
      let sorted_levels = List.sort compare levels in
      let (section_level, subsection_level) =
        match sorted_levels with
          [] -> (None, None)
        | [n] -> (Some n, None)
        | n :: m :: _ -> (Some n, Some m)
      in
      let titles_per_level level_opt =
        match level_opt with
          None -> []
        | Some n -> List.filter (fun (m,_,_) -> m = n) titles
      in
      let section_titles = titles_per_level section_level in
      let subsection_titles = titles_per_level subsection_level in
      let print_lines s_rel titles =
        List.iter
          (fun (n,lopt,t) ->
            let s = Odoc_info.string_of_text t in
            let label = self#create_title_label (n,lopt,t) in
            bp b "<link title=\"%s\" rel=\"%s\" href=\"#%s\">\n" s s_rel label
          )
          titles
      in
      print_lines "Section" section_titles ;
      print_lines "Subsection" subsection_titles


    (** Html code for navigation bar.
       @param pre optional name for optional previous module/class
       @param post optional name for optional next module/class
       @param name name of current module/class *)
    method print_navbar b pre post name =
      bs b "<div class=\"navbar\">";
      (
       match pre with
         None -> ()
       | Some name ->
           bp b "<a href=\"%s\">%s</a>\n"
             (fst (Naming.html_files name))
             Odoc_messages.previous
      );
      bs b "&nbsp;";
      let father = Name.father name in
      let href = if father = "" then self#index else fst (Naming.html_files father) in
      bp b "<a href=\"%s\">%s</a>\n" href Odoc_messages.up;
      bs b "&nbsp;";
      (
       match post with
         None -> ()
       | Some name ->
           bp b "<a href=\"%s\">%s</a>\n"
             (fst (Naming.html_files name))
             Odoc_messages.next
      );
      bs b "</div>\n"

    (** Return html code with the given string in the keyword style.*)
    method keyword s =
      "<span class=\"keyword\">"^s^"</span>"

    (** Return html code with the given string in the constructor style. *)
    method constructor s = "<span class=\"constructor\">"^s^"</span>"

    (** Output the given ocaml code to the given file name. *)
    method private output_code in_title file code =
      try
        let chanout = open_out file in
        let b = new_buf () in
        bs b "<html>";
        self#print_header b (self#inner_title in_title);
        bs b"<body>\n";
        self#html_of_code b code;
        bs b "</body></html>";
        Buffer.output_buffer chanout b;
        close_out chanout
      with
        Sys_error s ->
          incr Odoc_info.errors ;
          prerr_endline s

    (** Take a string and return the string where fully qualified
       type (or class or class type) idents
       have been replaced by links to the type referenced by the ident.*)
    method create_fully_qualified_idents_links m_name s =
      let f str_t =
        let match_s = Str.matched_string str_t in
        let rel = Name.get_relative m_name match_s in
        let s_final = Odoc_info.apply_if_equal
            Odoc_info.use_hidden_modules
            match_s
            rel
        in
        if StringSet.mem match_s known_types_names then
           "<a href=\""^(Naming.complete_target Naming.mark_type match_s)^"\">"^
           s_final^
           "</a>"
        else
          if StringSet.mem match_s known_classes_names then
            let (html_file, _) = Naming.html_files match_s in
            "<a href=\""^html_file^"\">"^s_final^"</a>"
          else
            s_final
      in
      let s2 = Str.global_substitute
          (Str.regexp "\\([A-Z]\\([a-zA-Z_'0-9]\\)*\\.\\)+\\([a-z][a-zA-Z_'0-9]*\\)")
          f
          s
      in
      s2

    (** Take a string and return the string where fully qualified module idents
       have been replaced by links to the module referenced by the ident.*)
    method create_fully_qualified_module_idents_links m_name s =
      let f str_t =
        let match_s = Str.matched_string str_t in
        let rel = Name.get_relative m_name match_s in
        let s_final = Odoc_info.apply_if_equal
            Odoc_info.use_hidden_modules
            match_s
            rel
        in
        if StringSet.mem match_s known_modules_names then
          let (html_file, _) = Naming.html_files match_s in
          "<a href=\""^html_file^"\">"^s_final^"</a>"
        else
          s_final
      in
      let s2 = Str.global_substitute
          (Str.regexp "\\([A-Z]\\([a-zA-Z_'0-9]\\)*\\.\\)+\\([A-Z][a-zA-Z_'0-9]*\\)")
          f
          s
      in
      s2

    (** Print html code to display a [Types.type_expr]. *)
    method html_of_type_expr b m_name t =
      let s = Odoc_info.remove_ending_newline (Odoc_info.string_of_type_expr t) in
      let s2 = newline_to_indented_br s in
      bs b "<code class=\"type\">";
      bs b (self#create_fully_qualified_idents_links m_name s2);
      bs b "</code>"

    (** Print html code to display a [Types.type_expr list]. *)
    method html_of_type_expr_list ?par b m_name sep l =
      print_DEBUG "html#html_of_type_expr_list";
      let s = Odoc_info.string_of_type_list ?par sep l in
      print_DEBUG "html#html_of_type_expr_list: 1";
      let s2 = newline_to_indented_br s in
      print_DEBUG "html#html_of_type_expr_list: 2";
      bs b "<code class=\"type\">";
      bs b (self#create_fully_qualified_idents_links m_name s2);
      bs b "</code>"

    (** Print html code to display a [Types.type_expr list] as type parameters
       of a class of class type. *)
    method html_of_class_type_param_expr_list b m_name l =
      let s = Odoc_info.string_of_class_type_param_list l in
      let s2 = newline_to_indented_br s in
      bs b "<code class=\"type\">[";
      bs b (self#create_fully_qualified_idents_links m_name s2);
      bs b "]</code>"

    method html_of_class_parameter_list b father c =
      let s = Odoc_info.string_of_class_params c in
      let s = Odoc_info.remove_ending_newline s in
      let s2 = newline_to_indented_br s in
      bs b "<code class=\"type\">";
      bs b (self#create_fully_qualified_idents_links father s2);
      bs b "</code>"

    (** Print html code to display a list of type parameters for the given type.*)
    method html_of_type_expr_param_list b m_name t =
      let s = Odoc_info.string_of_type_param_list t in
      let s2 = newline_to_indented_br s in
      bs b "<code class=\"type\">";
      bs b (self#create_fully_qualified_idents_links m_name s2);
      bs b "</code>"

    (** Print html code to display a [Types.module_type]. *)
    method html_of_module_type b ?code m_name t =
      let s = Odoc_info.remove_ending_newline (Odoc_info.string_of_module_type ?code t) in
      bs b "<code class=\"type\">";
      bs b (self#create_fully_qualified_module_idents_links m_name s);
      bs b "</code>"

    (** Print html code to display the given module kind. *)
    method html_of_module_kind b father ?modu kind =
      match kind with
        Module_struct eles ->
          self#html_of_text b [Code "sig"];
          (
           match modu with
             None ->
               bs b "<div class=\"sig_block\">";
               List.iter (self#html_of_module_element b father) eles;
               bs b "</div>"
           | Some m ->
               let (html_file, _) = Naming.html_files m.m_name in
               bp b " <a href=\"%s\">..</a> " html_file
          );
          self#html_of_text b [Code "end"]
      | Module_alias a ->
          bs b "<code class=\"type\">";
          bs b (self#create_fully_qualified_module_idents_links father a.ma_name);
          bs b "</code>"
      | Module_functor (p, k) ->
          if !Odoc_info.Args.html_short_functors then
            bs b " "
          else
            bs b "<div class=\"sig_block\">";
          self#html_of_module_parameter b father p;
          (
           match k with
             Module_functor _ -> ()
           | _ when !Odoc_info.Args.html_short_functors ->
               bs b ": "
           | _ -> ()
          );
          self#html_of_module_kind b father ?modu k;
          if not !Odoc_info.Args.html_short_functors then
            bs b "</div>"
      | Module_apply (k1, k2) ->
          (* TODO: l'application n'est pas correcte dans un .mli.
             Que faire ? -> afficher le module_type du typedtree  *)
          self#html_of_module_kind b father k1;
          self#html_of_text b [Code "("];
          self#html_of_module_kind b father k2;
          self#html_of_text b [Code ")"]
      | Module_with (k, s) ->
          (* TODO: à modifier quand Module_with sera plus détaillé *)
          self#html_of_module_type_kind b father ?modu k;
          bs b "<code class=\"type\"> ";
          bs b (self#create_fully_qualified_module_idents_links father s);
          bs b "</code>"
      | Module_constraint (k, tk) ->
          (* TODO: on affiche quoi ? *)
          self#html_of_module_kind b father ?modu k

    method html_of_module_parameter b father p =
      let (s_functor,s_arrow) =
        if !Odoc_info.Args.html_short_functors then
          "", ""
        else
          "functor ", "-> "
      in
      self#html_of_text b
        [
          Code (s_functor^"(");
          Code p.mp_name ;
          Code " : ";
        ] ;
      self#html_of_module_type_kind b father p.mp_kind;
      self#html_of_text b [ Code (") "^s_arrow)]

    method html_of_module_element b father ele =
      match ele with
        Element_module m ->
          self#html_of_module b ~complete: false m
      | Element_module_type mt ->
          self#html_of_modtype b ~complete: false mt
      | Element_included_module im ->
          self#html_of_included_module b im
      | Element_class c ->
          self#html_of_class b ~complete: false c
      | Element_class_type ct ->
          self#html_of_class_type b ~complete: false ct
      | Element_value v ->
          self#html_of_value b v
      | Element_exception e ->
          self#html_of_exception b e
      | Element_type t ->
          self#html_of_type b t
      | Element_module_comment text ->
          self#html_of_module_comment b text

    (** Print html code to display the given module type kind. *)
    method html_of_module_type_kind b father ?modu ?mt kind =
      match kind with
        Module_type_struct eles ->
          self#html_of_text b [Code "sig"];
          (
           match mt with
             None ->
               (
                match modu with
                  None ->
                    bs b "<div class=\"sig_block\">";
                    List.iter (self#html_of_module_element b father) eles;
                    bs b "</div>"
                | Some m ->
                    let (html_file, _) = Naming.html_files m.m_name in
                    bp b " <a href=\"%s\">..</a> " html_file
               )
           | Some mt ->
               let (html_file, _) = Naming.html_files mt.mt_name in
               bp b " <a href=\"%s\">..</a> " html_file
          );
          self#html_of_text b [Code "end"]
      | Module_type_functor (p, k) ->
          self#html_of_module_parameter b father p;
          self#html_of_module_type_kind b father ?modu ?mt k
      | Module_type_alias a ->
          bs b "<code class=\"type\">";
          bs b (self#create_fully_qualified_module_idents_links father a.mta_name);
          bs b "</code>"
      | Module_type_with (k, s) ->
          self#html_of_module_type_kind b father ?modu ?mt k;
          bs b "<code class=\"type\"> ";
          bs b (self#create_fully_qualified_module_idents_links father s);
          bs b "</code>"

    (** Print html code to display the type of a module parameter.. *)
    method html_of_module_parameter_type b m_name p =
      self#html_of_module_type b m_name ~code: p.mp_type_code p.mp_type

    (** Generate a file containing the module type in the given file name. *)
    method output_module_type in_title file mtyp =
      let s = Odoc_info.remove_ending_newline (Odoc_info.string_of_module_type ~complete: true mtyp) in
      self#output_code in_title file s

    (** Generate a file containing the class type in the given file name. *)
    method output_class_type in_title file ctyp =
      let s = Odoc_info.remove_ending_newline (Odoc_info.string_of_class_type ~complete: true ctyp) in
      self#output_code in_title file s

    (** Print html code for a value. *)
    method html_of_value b v =
      Odoc_info.reset_type_names ();
      bs b "<pre>";
      bs b (self#keyword "val");
      bs b " ";
      (* html mark *)
      bp b "<a name=\"%s\"></a>" (Naming.value_target v);
      (
       match v.val_code with
         None -> bs b (self#escape (Name.simple v.val_name))
       | Some c ->
           let file = Naming.file_code_value_complete_target v in
           self#output_code v.val_name (Filename.concat !Args.target_dir file) c;
           bp b "<a href=\"%s\">%s</a>" file (self#escape (Name.simple v.val_name))
      );
      bs b " : ";
      self#html_of_type_expr b (Name.father v.val_name) v.val_type;
      bs b "</pre>";
      self#html_of_info b v.val_info;
      (
       if !Args.with_parameter_list then
         self#html_of_parameter_list b (Name.father v.val_name) v.val_parameters
       else
         self#html_of_described_parameter_list b (Name.father v.val_name) v.val_parameters
      )

    (** Print html code for an exception. *)
    method html_of_exception b e =
      Odoc_info.reset_type_names ();
      bs b "<pre>";
      bs b (self#keyword "exception");
      bs b " ";
      (* html mark *)
      bp b "<a name=\"%s\"></a>%s"
        (Naming.exception_target e)
        (Name.simple e.ex_name);
      (
       match e.ex_args with
         [] -> ()
       | _ ->
           bs b (" "^(self#keyword "of")^" ");
           self#html_of_type_expr_list
             ~par: false b (Name.father e.ex_name) " * " e.ex_args
      );
      (
       match e.ex_alias with
         None -> ()
       | Some ea ->
           bs b " = ";
           (
            match ea.ea_ex with
              None -> bs b ea.ea_name
            | Some e ->
                bp b "<a href=\"%s\">%s</a>" (Naming.complete_exception_target e) e.ex_name
           )
      );
      bs b "</pre>\n";
      self#html_of_info b e.ex_info

    (** Print html code for a type. *)
    method html_of_type b t =
      Odoc_info.reset_type_names ();
      let father = Name.father t.ty_name in
      bs b
        (match t.ty_manifest, t.ty_kind with
          None, Type_abstract -> "<pre>"
        | None, Type_variant _
        | None, Type_record _ -> "<br><code>"
        | Some _, Type_abstract -> "<pre>"
        | Some _, Type_variant _
        | Some _, Type_record _ -> "<pre>"
        );
      bs b ((self#keyword "type")^" ");
      (* html mark *)
      bp b "<a name=\"%s\"></a>" (Naming.type_target t);
      self#html_of_type_expr_param_list b father t;
      (match t.ty_parameters with [] -> () | _ -> bs b " ");
      bs b ((Name.simple t.ty_name)^" ");
      let priv = t.ty_private = Asttypes.Private in
      (
       match t.ty_manifest with
         None -> ()
       | Some typ ->
           bs b "= ";
           if priv then bs b "private ";
           self#html_of_type_expr b father typ;
           bs b " "
      );
      (match t.ty_kind with
        Type_abstract -> bs b "</pre>"
      | Type_variant l ->
          bs b "= ";
          if priv then bs b "private ";
          bs b
            (
             match t.ty_manifest with
               None -> "</code>"
             | Some _ -> "</pre>"
            );
          bs b "<table class=\"typetable\">\n";
          let print_one constr =
            bs b "<tr>\n<td align=\"left\" valign=\"top\" >\n";
            bs b "<code>";
            bs b (self#keyword "|");
            bs b "</code></td>\n<td align=\"left\" valign=\"top\" >\n";
            bs b "<code>";
            bs b (self#constructor constr.vc_name);
            (
             match constr.vc_args with
               [] -> ()
             | l ->
                 bs b (" " ^ (self#keyword "of") ^ " ");
                 self#html_of_type_expr_list ~par: false b father " * " l;
            );
            bs b "</code></td>\n";
            (
             match constr.vc_text with
               None -> ()
             | Some t ->
                 bs b "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >";
                 bs b "<code>";
                 bs b "(*";
                 bs b "</code></td>";
                 bs b "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >";
                 self#html_of_text b t;
                 bs b "</td>";
                 bs b "<td class=\"typefieldcomment\" align=\"left\" valign=\"bottom\" >";
                 bs b "<code>";
                 bs b "*)";
                 bs b "</code></td>";
            );
            bs b "\n</tr>"
          in
          print_concat b "\n" print_one l;
          bs b "</table>\n"

      | Type_record l ->
          bs b "= ";
          if priv then bs b "private " ;
          bs b "{";
          bs b
            (
             match t.ty_manifest with
               None -> "</code>"
             | Some _ -> "</pre>"
            );
          bs b "<table class=\"typetable\">\n" ;
          let print_one r =
            bs b "<tr>\n<td align=\"left\" valign=\"top\" >\n";
            bs b "<code>&nbsp;&nbsp;</code>";
            bs b "</td>\n<td align=\"left\" valign=\"top\" >\n";
            bs b "<code>";
            if r.rf_mutable then bs b (self#keyword "mutable&nbsp;") ;
            bs b (r.rf_name ^ "&nbsp;: ") ;
            self#html_of_type_expr b father r.rf_type;
            bs b ";</code></td>\n";
            (
             match r.rf_text with
               None -> ()
             | Some t ->
                 bs b "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >";
                 bs b "<code>";
                 bs b "(*";
                 bs b "</code></td>";
                 bs b "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >";
                 self#html_of_text b t;
                 bs b "</td><td class=\"typefieldcomment\" align=\"left\" valign=\"bottom\" >";
                 bs b "<code>*)</code></td>";
            );
            bs b "\n</tr>"
          in
          print_concat b "\n" print_one l;
          bs b "</table>\n}\n"
      );
      bs b "\n";
      self#html_of_info b t.ty_info;
      bs b "\n"

    (** Print html code for a class attribute. *)
    method html_of_attribute b a =
      let module_name = Name.father (Name.father a.att_value.val_name) in
      bs b "<pre>" ;
      bs b (self#keyword "val");
      bs b " ";
      (* html mark *)
      bp b "<a name=\"%s\"></a>" (Naming.attribute_target a);
      (
       if a.att_virtual then
         bs b ((self#keyword "virtual")^ " ")
       else
         ()
      );
      (
       if a.att_mutable then
         bs b ((self#keyword Odoc_messages.mutab)^ " ")
       else
         ()
      );(
       match a.att_value.val_code with
         None -> bs b (Name.simple a.att_value.val_name)
       | Some c ->
           let file = Naming.file_code_attribute_complete_target a in
           self#output_code a.att_value.val_name (Filename.concat !Args.target_dir file) c;
           bp b "<a href=\"%s\">%s</a>" file (Name.simple a.att_value.val_name);
      );
      bs b " : ";
      self#html_of_type_expr b module_name a.att_value.val_type;
      bs b "</pre>";
      self#html_of_info b a.att_value.val_info

    (** Print html code for a class method. *)
    method html_of_method b m =
      let module_name = Name.father (Name.father m.met_value.val_name) in
      bs b "<pre>";
      bs b ((self#keyword "method")^" ");
      (* html mark *)
      bp b "<a name=\"%s\"></a>" (Naming.method_target m);
      if m.met_private then bs b ((self#keyword "private")^" ");
      if m.met_virtual then bs b ((self#keyword "virtual")^" ");
      (
       match m.met_value.val_code with
         None -> bs b  (Name.simple m.met_value.val_name)
       | Some c ->
           let file = Naming.file_code_method_complete_target m in
           self#output_code m.met_value.val_name (Filename.concat !Args.target_dir file) c;
           bp b "<a href=\"%s\">%s</a>" file (Name.simple m.met_value.val_name);
      );
      bs b " : ";
      self#html_of_type_expr b module_name m.met_value.val_type;
      bs b "</pre>";
      self#html_of_info b m.met_value.val_info;
      (
       if !Args.with_parameter_list then
         self#html_of_parameter_list b
           module_name m.met_value.val_parameters
       else
         self#html_of_described_parameter_list b
           module_name m.met_value.val_parameters
      )

    (** Print html code for the description of a function parameter. *)
    method html_of_parameter_description b p =
      match Parameter.names p with
        [] ->
          ()
      | name :: [] ->
          (
           (* Only one name, no need for label for the description. *)
           match Parameter.desc_by_name p name with
             None -> ()
           | Some t -> self#html_of_text b t
          )
      | l ->
          (*  A list of names, we display those with a description. *)
          let l2 = List.filter
              (fun n -> (Parameter.desc_by_name p n) <> None)
              l
          in
          let print_one n =
            match Parameter.desc_by_name p n with
              None -> ()
            | Some t ->
                bs b "<code>";
                bs b n;
                bs b "</code> : ";
                self#html_of_text b t
          in
          print_concat b "<br>\n" print_one l2

    (** Print html code for a list of parameters. *)
    method html_of_parameter_list b m_name l =
      match l with
        [] -> ()
      | _ ->
          bs b "<div class=\"param_info\">";
          bs b "<table border=\"0\" cellpadding=\"3\" width=\"100%\">\n";
          bs b "<tr>\n<td align=\"left\" valign=\"top\" width=\"1%\">";
          bs b "<b>";
          bs b Odoc_messages.parameters;
          bs b ": </b></td>\n" ;
          bs b "<td>\n<table class=\"paramstable\">\n";
          let print_one p =
            bs b "<tr>\n<td align=\"center\" valign=\"top\" width=\"15%\" class=\"code\">\n";
            bs b
              (
               match Parameter.complete_name p with
                 "" -> "?"
               | s -> s
              );
            bs b "</td>\n<td align=\"center\" valign=\"top\">:</td>\n";
            bs b "<td>";
            self#html_of_type_expr b m_name (Parameter.typ p);
            bs b "<br>\n";
            self#html_of_parameter_description b p;
            bs b "\n</tr>\n";
          in
          List.iter print_one l;
          bs b "</table>\n</td>\n</tr>\n</table></div>\n"

    (** Print html code for the parameters which have a name and description. *)
    method html_of_described_parameter_list b m_name l =
      (* get the params which have a name, and at least one name described. *)
      let l2 = List.filter
          (fun p ->
            List.exists
              (fun n -> (Parameter.desc_by_name p n) <> None)
              (Parameter.names p))
          l
      in
      let f p =
        bs b "<div class=\"param_info\"><code class=\"code\">";
        bs b (Parameter.complete_name p);
        bs b "</code> : " ;
        self#html_of_parameter_description b p;
        bs b "</div>\n"
      in
      List.iter f l2

    (** Print html code for a list of module parameters. *)
    method html_of_module_parameter_list b m_name l =
      match l with
        [] ->
          ()
      | _ ->
          bs b "<table border=\"0\" cellpadding=\"3\" width=\"100%\">\n";
          bs b "<tr>\n";
          bs b "<td align=\"left\" valign=\"top\" width=\"1%%\"><b>";
          bs b Odoc_messages.parameters ;
          bs b ": </b></td>\n<td>\n";
          bs b "<table class=\"paramstable\">\n";
          List.iter
            (fun (p, desc_opt) ->
              bs b "<tr>\n";
              bs b "<td align=\"center\" valign=\"top\" width=\"15%\">\n<code>" ;
              bs b p.mp_name;
              bs b "</code></td>\n" ;
              bs b "<td align=\"center\" valign=\"top\">:</td>\n";
              bs b "<td>" ;
              self#html_of_module_parameter_type b m_name p;
              bs b "\n";
              (
               match desc_opt with
                 None -> ()
               | Some t ->
                   bs b "<br>";
                   self#html_of_text b t;
                   bs b "\n</tr>\n" ;
              )
            )
            l;
          bs b "</table>\n</td>\n</tr>\n</table>\n"

    (** Print html code for a module. *)
    method html_of_module b ?(info=true) ?(complete=true) ?(with_link=true) m =
      let (html_file, _) = Naming.html_files m.m_name in
      let father = Name.father m.m_name in
      bs b "<pre>";
      bs b ((self#keyword "module")^" ");
      (
       if with_link then
         bp b "<a href=\"%s\">%s</a>" html_file (Name.simple m.m_name)
       else
         bs b (Name.simple m.m_name)
      );
      (
       match m.m_kind with
         Module_functor _ when !Odoc_info.Args.html_short_functors  ->
           ()
       | _ -> bs b ": "
      );
      self#html_of_module_kind b father ~modu: m m.m_kind;
      bs b "</pre>";
      if info then
        (
         if complete then
           self#html_of_info ~indent: false
         else
           self#html_of_info_first_sentence
        ) b m.m_info
      else
        ()

    (** Print html code for a module type. *)
    method html_of_modtype b ?(info=true) ?(complete=true) ?(with_link=true) mt =
      let (html_file, _) = Naming.html_files mt.mt_name in
      let father = Name.father mt.mt_name in
      bs b "<pre>";
      bs b ((self#keyword "module type")^" ");
      (
       if with_link then
         bp b "<a href=\"%s\">%s</a>" html_file (Name.simple mt.mt_name)
         else
         bs b (Name.simple mt.mt_name)
      );
      (match mt.mt_kind with
        None -> ()
      | Some k ->
          bs b " = ";
          self#html_of_module_type_kind b father ~mt k
      );
      bs b "</pre>";
      if info then
        (
         if complete then
           self#html_of_info ~indent: false
         else
           self#html_of_info_first_sentence
        ) b mt.mt_info
      else
        ()

    (** Print html code for an included module. *)
    method html_of_included_module b im =
      bs b "<pre>";
      bs b ((self#keyword "include")^" ");
      (
       match im.im_module with
         None ->
           bs b im.im_name
       | Some mmt ->
           let (file, name) =
             match mmt with
               Mod m ->
                 let (html_file, _) = Naming.html_files m.m_name in
                 (html_file, m.m_name)
             | Modtype mt ->
                 let (html_file, _) = Naming.html_files mt.mt_name in
                 (html_file, mt.mt_name)
           in
           bp b "<a href=\"%s\">%s</a>" file name
      );
      bs b "</pre>\n";
      self#html_of_info b im.im_info

    method html_of_class_element b element =
      match element with
        Class_attribute a ->
          self#html_of_attribute b a
      | Class_method m ->
          self#html_of_method b m
      | Class_comment t ->
          self#html_of_class_comment b t

    method html_of_class_kind b father ?cl kind =
      match kind with
        Class_structure (inh, eles) ->
          self#html_of_text b [Code "object"];
          (
           match cl with
             None ->
               bs b "\n";
               (
                match inh with
                  [] -> ()
                | _ ->
                    self#generate_inheritance_info b inh
               );
               List.iter (self#html_of_class_element b) eles;
           | Some cl ->
               let (html_file, _) = Naming.html_files cl.cl_name in
               bp b " <a href=\"%s\">..</a> " html_file
          );
          self#html_of_text b [Code "end"]

      | Class_apply capp ->
          (* TODO: afficher le type final à partir du typedtree *)
          self#html_of_text b [Raw "class application not handled yet"]

      | Class_constr cco ->
          (
           match cco.cco_type_parameters with
             [] -> ()
           | l ->
               self#html_of_class_type_param_expr_list b father l;
               bs b " "
          );
          bs b "<code class=\"type\">";
          bs b (self#create_fully_qualified_idents_links father cco.cco_name);
          bs b "</code>"

      | Class_constraint (ck, ctk) ->
          self#html_of_text b [Code "( "] ;
          self#html_of_class_kind b father ck;
          self#html_of_text b [Code " : "] ;
          self#html_of_class_type_kind b father ctk;
          self#html_of_text b [Code " )"]

    method html_of_class_type_kind b father ?ct kind =
      match kind with
        Class_type cta ->
          (
           match cta.cta_type_parameters with
             [] -> ()
           | l ->
               self#html_of_class_type_param_expr_list b father l;
               bs b " "
          );
          bs b "<code class=\"type\">";
          bs b (self#create_fully_qualified_idents_links father cta.cta_name);
          bs b "</code>"

      | Class_signature (inh, eles) ->
          self#html_of_text b [Code "object"];
          (
           match ct with
             None ->
               bs b "\n";
               (
                match inh with
                  [] -> ()
                | _ -> self#generate_inheritance_info b inh
               );
               List.iter (self#html_of_class_element b) eles
           | Some ct ->
               let (html_file, _) = Naming.html_files ct.clt_name in
               bp b " <a href=\"%s\">..</a> " html_file
          );
          self#html_of_text b [Code "end"]

    (** Print html code for a class. *)
    method html_of_class b ?(complete=true) ?(with_link=true) c =
      let father = Name.father c.cl_name in
      Odoc_info.reset_type_names ();
      let (html_file, _) = Naming.html_files c.cl_name in
      bs b "<pre>";
      bs b ((self#keyword "class")^" ");
      (* we add a html tag, the same as for a type so we can
         go directly here when the class name is used as a type name *)
      bp b "<a name=\"%s\"></a>"
        (Naming.type_target
           { ty_name = c.cl_name ;
             ty_info = None ; ty_parameters = [] ;
             ty_kind = Type_abstract ; ty_private = Asttypes.Public; ty_manifest = None ;
             ty_loc = Odoc_info.dummy_loc ;
             ty_code = None ;
           }
        );
      print_DEBUG "html#html_of_class : virtual or not" ;
      if c.cl_virtual then bs b ((self#keyword "virtual")^" ");
      (
       match c.cl_type_parameters with
         [] -> ()
       | l ->
           self#html_of_class_type_param_expr_list b father l;
           bs b " "
      );
      print_DEBUG "html#html_of_class : with link or not" ;
      (
       if with_link then
         bp b "<a href=\"%s\">%s</a>" html_file (Name.simple c.cl_name)
       else
         bs b (Name.simple c.cl_name)
      );

      bs b " : " ;
      self#html_of_class_parameter_list b father c ;
      self#html_of_class_kind b father ~cl: c c.cl_kind;
      bs b "</pre>" ;
      print_DEBUG "html#html_of_class : info" ;
      (
       if complete then
         self#html_of_info ~indent: false
       else
         self#html_of_info_first_sentence
      ) b c.cl_info

    (** Print html code for a class type. *)
    method html_of_class_type b ?(complete=true) ?(with_link=true) ct =
      Odoc_info.reset_type_names ();
      let father = Name.father ct.clt_name in
      let (html_file, _) = Naming.html_files ct.clt_name in
      bs b "<pre>";
      bs b ((self#keyword "class type")^" ");
      (* we add a html tag, the same as for a type so we can
         go directly here when the class type name is used as a type name *)
      bp b "<a name=\"%s\"></a>"
        (Naming.type_target
           { ty_name = ct.clt_name ;
             ty_info = None ; ty_parameters = [] ;
             ty_kind = Type_abstract ; ty_private = Asttypes.Public; ty_manifest = None ;
             ty_loc = Odoc_info.dummy_loc ;
             ty_code = None ;
           }
        );
      if ct.clt_virtual then bs b ((self#keyword "virtual")^" ");
      (
       match ct.clt_type_parameters with
        [] -> ()
      | l ->
          self#html_of_class_type_param_expr_list b father l;
          bs b " "
      );

      if with_link then
        bp b "<a href=\"%s\">%s</a>" html_file (Name.simple ct.clt_name)
      else
        bs b (Name.simple ct.clt_name);

      bs b " = ";
      self#html_of_class_type_kind b father ~ct ct.clt_kind;
      bs b "</pre>";
      (
       if complete then
         self#html_of_info ~indent: false
       else
         self#html_of_info_first_sentence
      ) b ct.clt_info

    (** Return html code to represent a dag, represented as in Odoc_dag2html. *)
    method html_of_dag dag =
      let f n =
        let (name, cct_opt) = n.Odoc_dag2html.valu in
        (* if we have a c_opt = Some class then we take its information
           because we are sure the name is complete. *)
        let (name2, html_file) =
          match cct_opt with
            None -> (name, fst (Naming.html_files name))
          | Some (Cl c) -> (c.cl_name, fst (Naming.html_files c.cl_name))
          | Some (Cltype (ct, _)) -> (ct.clt_name, fst (Naming.html_files ct.clt_name))
        in
        let new_v =
          "<table border=1>\n<tr><td>"^
          "<a href=\""^html_file^"\">"^name2^"</a>"^
          "</td></tr>\n</table>\n"
        in
        { n with Odoc_dag2html.valu = new_v }
      in
      let a = Array.map f dag.Odoc_dag2html.dag in
      Odoc_dag2html.html_of_dag { Odoc_dag2html.dag = a }

    (** Print html code for a module comment.*)
    method html_of_module_comment b text =
      bs b "<br>\n";
      self#html_of_text b text;
      bs b "<br>\n"

    (** Print html code for a class comment.*)
    method html_of_class_comment b text =
      (* Add some style if there is no style for the first part of the text. *)
      let text2 =
        match text with
        | (Odoc_info.Raw s) :: q ->
            (Odoc_info.Title (2, None, [Odoc_info.Raw s])) :: q
        | _ -> text
      in
      self#html_of_text b text2

    (** Generate html code for the given list of inherited classes.*)
    method generate_inheritance_info b inher_l =
      let f inh =
        match inh.ic_class with
          None -> (* we can't make the link. *)
            (Odoc_info.Code inh.ic_name) ::
            (match inh.ic_text with
              None -> []
            | Some t -> (Odoc_info.Raw "    ") :: t)
        | Some cct ->
            (* we can create the link. *)
            let real_name = (* even if it should be the same *)
              match cct with
                Cl c -> c.cl_name
              | Cltype (ct, _) -> ct.clt_name
            in
            let (class_file, _) = Naming.html_files real_name in
            (Odoc_info.Link (class_file, [Odoc_info.Code real_name])) ::
            (match inh.ic_text with
              None -> []
            | Some t -> (Odoc_info.Raw "    ") :: t)
      in
      let text = [
        Odoc_info.Bold [Odoc_info.Raw Odoc_messages.inherits] ;
        Odoc_info.List (List.map f inher_l)
      ]
      in
      self#html_of_text b text

    (** Generate html code for the inherited classes of the given class. *)
    method generate_class_inheritance_info b cl =
      let rec iter_kind k =
        match k with
          Class_structure ([], _) ->
            ()
        | Class_structure (l, _) ->
            self#generate_inheritance_info b l
        | Class_constraint (k, ct) ->
            iter_kind k
        | Class_apply _
        | Class_constr _ ->
            ()
      in
      iter_kind cl.cl_kind

    (** Generate html code for the inherited classes of the given class type. *)
    method generate_class_type_inheritance_info b clt =
      match clt.clt_kind with
        Class_signature ([], _) ->
          ()
      | Class_signature (l, _) ->
          self#generate_inheritance_info b l
      | Class_type _ ->
          ()

    (** A method to create index files. *)
    method generate_elements_index :
        'a.
        'a list ->
          ('a -> Odoc_info.Name.t) ->
            ('a -> Odoc_info.info option) ->
              ('a -> string) -> string -> string -> unit =
    fun elements name info target title simple_file ->
      try
        let chanout = open_out (Filename.concat !Args.target_dir simple_file) in
        let b = new_buf () in
        bs b "<html>\n";
        self#print_header b (self#inner_title title);
        bs b "<body>\n<center><h1>";
        bs b title;
        bs b "</h1></center>\n" ;

        let sorted_elements = List.sort
            (fun e1 e2 -> compare (Name.simple (name e1)) (Name.simple (name e2)))
            elements
        in
        let groups = Odoc_info.create_index_lists sorted_elements (fun e -> Name.simple (name e)) in
        let f_ele e =
          let simple_name = Name.simple (name e) in
          let father_name = Name.father (name e) in
          bp b "<tr><td><a href=\"%s\">%s</a> " (target e) (self#escape simple_name);
          if simple_name <> father_name && father_name <> "" then
            bp b "[<a href=\"%s\">%s</a>]" (fst (Naming.html_files father_name)) father_name;
          bs b "</td>\n<td>";
          self#html_of_info_first_sentence b (info e);
          bs b "</td></tr>\n";
        in
        let f_group l =
          match l with
            [] -> ()
          | e :: _ ->
              let s =
                match (Char.uppercase (Name.simple (name e)).[0]) with
                  'A'..'Z' as c -> String.make 1 c
                | _ -> ""
              in
              bs b "<tr><td align=\"left\"><br>";
              bs b s ;
              bs b "</td></tr>\n" ;
              List.iter f_ele l
        in
        bs b "<table>\n";
        List.iter f_group groups ;
        bs b "</table><br>\n" ;
        bs b "</body>\n</html>";
        Buffer.output_buffer chanout b;
        close_out chanout
      with
        Sys_error s ->
          raise (Failure s)

    (** A method to generate a list of module/class files. *)
    method generate_elements :
        'a. ('a option -> 'a option -> 'a -> unit) -> 'a list -> unit =
      fun f_generate l ->
        let rec iter pre_opt = function
            [] -> ()
          | ele :: [] -> f_generate pre_opt None ele
          | ele1 :: ele2 :: q ->
              f_generate pre_opt (Some ele2) ele1 ;
              iter (Some ele1) (ele2 :: q)
        in
        iter None l

    (** Generate the code of the html page for the given class.*)
    method generate_for_class pre post cl =
      Odoc_info.reset_type_names ();
      let (html_file, _) = Naming.html_files cl.cl_name in
      let type_file = Naming.file_type_class_complete_target cl.cl_name in
      try
        let chanout = open_out (Filename.concat !Args.target_dir html_file) in
        let b = new_buf () in
        let pre_name = opt (fun c -> c.cl_name) pre in
        let post_name = opt (fun c -> c.cl_name) post in
        bs b doctype ;
        bs b "<html>\n";
        self#print_header b
          ~nav: (Some (pre_name, post_name, cl.cl_name))
          ~comments: (Class.class_comments cl)
          (self#inner_title cl.cl_name);
        bs b "<body>\n";
        self#print_navbar b pre_name post_name cl.cl_name;
        bs b "<center><h1>";
        bs b (Odoc_messages.clas^" ");
        if cl.cl_virtual then bs b "virtual " ;
        bp b "<a href=\"%s\">%s</a>" type_file cl.cl_name;
        bs b "</h1></center>\n<br>\n";
        self#html_of_class b ~with_link: false cl;
        (* parameters *)
        self#html_of_described_parameter_list b
          (Name.father cl.cl_name) cl.cl_parameters;
        (* class inheritance *)
        self#generate_class_inheritance_info b cl;
        (* a horizontal line *)
        bs b "<hr width=\"100%\">\n";
        (* the various elements *)
        List.iter (self#html_of_class_element b)
          (Class.class_elements ~trans:false cl);
        bs b "</body></html>";
        Buffer.output_buffer chanout b;
        close_out chanout;

        (* generate the file with the complete class type *)
        self#output_class_type
          cl.cl_name
          (Filename.concat !Args.target_dir type_file)
          cl.cl_type
      with
        Sys_error s ->
          raise (Failure s)

    (** Generate the code of the html page for the given class type.*)
    method generate_for_class_type pre post clt =
      Odoc_info.reset_type_names ();
      let (html_file, _) = Naming.html_files clt.clt_name in
      let type_file = Naming.file_type_class_complete_target clt.clt_name in
      try
        let chanout = open_out (Filename.concat !Args.target_dir html_file) in
        let b = new_buf () in
        let pre_name = opt (fun ct -> ct.clt_name) pre in
        let post_name = opt (fun ct -> ct.clt_name) post in
        bs b doctype ;
        bs b "<html>\n";
        self#print_header b
          ~nav: (Some (pre_name, post_name, clt.clt_name))
          ~comments: (Class.class_type_comments clt)
          (self#inner_title clt.clt_name);

        bs b "<body>\n";
        self#print_navbar b pre_name post_name clt.clt_name;
        bs b "<center><h1>";
        bs b (Odoc_messages.class_type^" ");
        if clt.clt_virtual then bs b "virtual ";
        bp b "<a href=\"%s\">%s</a>" type_file clt.clt_name;
        bs b "</h1></center>\n<br>\n";
        self#html_of_class_type b ~with_link: false clt;

        (* class inheritance *)
        self#generate_class_type_inheritance_info b clt;
        (* a horizontal line *)
        bs b "<hr width=\"100%\">\n";
        (* the various elements *)
        List.iter (self#html_of_class_element b)
          (Class.class_type_elements ~trans: false clt);
        bs b "</body></html>";
        Buffer.output_buffer chanout b;
        close_out chanout;

        (* generate the file with the complete class type *)
        self#output_class_type
          clt.clt_name
          (Filename.concat !Args.target_dir type_file)
          clt.clt_type
      with
        Sys_error s ->
          raise (Failure s)

    (** Generate the html file for the given module type.
       @raise Failure if an error occurs.*)
    method generate_for_module_type pre post mt =
      try
        let (html_file, _) = Naming.html_files mt.mt_name in
        let type_file = Naming.file_type_module_complete_target mt.mt_name in
        let chanout = open_out (Filename.concat !Args.target_dir html_file) in
        let b = new_buf () in
        let pre_name = opt (fun mt -> mt.mt_name) pre in
        let post_name = opt (fun mt -> mt.mt_name) post in
        bs b doctype ;
        bs b "<html>\n";
        self#print_header b
          ~nav: (Some (pre_name, post_name, mt.mt_name))
          ~comments: (Module.module_type_comments mt)
          (self#inner_title mt.mt_name);
        bs b "<body>\n";
        self#print_navbar b pre_name post_name mt.mt_name;
        bp b "<center><h1>";
        bs b (Odoc_messages.module_type^" ");
        (
         match mt.mt_type with
           Some _ -> bp b "<a href=\"%s\">%s</a>" type_file mt.mt_name
         | None-> bs b mt.mt_name
        );
        bs b "</h1></center>\n<br>\n" ;
        self#html_of_modtype b ~with_link: false mt;

        (* parameters for functors *)
        self#html_of_module_parameter_list b
          (Name.father mt.mt_name)
          (Module.module_type_parameters mt);
        (* a horizontal line *)
        bs b "<hr width=\"100%\">\n";
        (* module elements *)
        List.iter
          (self#html_of_module_element b (Name.father mt.mt_name))
          (Module.module_type_elements mt);

        bs b "</body></html>";
        Buffer.output_buffer chanout b;
        close_out chanout;

        (* generate html files for submodules *)
        self#generate_elements self#generate_for_module (Module.module_type_modules mt);
        (* generate html files for module types *)
        self#generate_elements self#generate_for_module_type (Module.module_type_module_types mt);
        (* generate html files for classes *)
        self#generate_elements self#generate_for_class (Module.module_type_classes mt);
        (* generate html files for class types *)
        self#generate_elements self#generate_for_class_type (Module.module_type_class_types mt);

        (* generate the file with the complete module type *)
        (
         match mt.mt_type with
           None -> ()
         | Some mty ->
             self#output_module_type
               mt.mt_name
               (Filename.concat !Args.target_dir type_file)
               mty
        )
      with
        Sys_error s ->
          raise (Failure s)

    (** Generate the html file for the given module.
       @raise Failure if an error occurs.*)
    method generate_for_module pre post modu =
      try
        Odoc_info.verbose ("Generate for module "^modu.m_name);
        let (html_file, _) = Naming.html_files modu.m_name in
        let type_file = Naming.file_type_module_complete_target modu.m_name in
        let code_file = Naming.file_code_module_complete_target modu.m_name in
        let chanout = open_out (Filename.concat !Args.target_dir html_file) in
        let b = new_buf () in
        let pre_name = opt (fun m -> m.m_name) pre in
        let post_name = opt (fun m -> m.m_name) post in
        bs b doctype ;
        bs b "<html>\n";
        self#print_header b
          ~nav: (Some (pre_name, post_name, modu.m_name))
          ~comments: (Module.module_comments modu)
          (self#inner_title modu.m_name);
        bs b "<body>\n" ;
        self#print_navbar b pre_name post_name modu.m_name ;
        bs b "<center><h1>";
        if modu.m_text_only then
          bs b modu.m_name
        else
          (
           bs b
             (
              if Module.module_is_functor modu then
                Odoc_messages.functo
              else
                Odoc_messages.modul
             );
           bp b " <a href=\"%s\">%s</a>" type_file modu.m_name;
           (
            match modu.m_code with
              None -> ()
            | Some _ -> bp b " (<a href=\"%s\">.ml</a>)" code_file
           )
          );
        bs b "</h1></center>\n<br>\n";

        if not modu.m_text_only then self#html_of_module b ~with_link: false modu;

        (* parameters for functors *)
        self#html_of_module_parameter_list b
          (Name.father modu.m_name)
          (Module.module_parameters modu);

        (* a horizontal line *)
        if not modu.m_text_only then bs b "<hr width=\"100%\">\n";

        (* module elements *)
        List.iter
          (self#html_of_module_element b (Name.father modu.m_name))
          (Module.module_elements modu);

        bs b "</body></html>";
        Buffer.output_buffer chanout b;
        close_out chanout;

        (* generate html files for submodules *)
        self#generate_elements  self#generate_for_module (Module.module_modules modu);
        (* generate html files for module types *)
        self#generate_elements  self#generate_for_module_type (Module.module_module_types modu);
        (* generate html files for classes *)
        self#generate_elements  self#generate_for_class (Module.module_classes modu);
        (* generate html files for class types *)
        self#generate_elements  self#generate_for_class_type (Module.module_class_types modu);

        (* generate the file with the complete module type *)
        self#output_module_type
          modu.m_name
          (Filename.concat !Args.target_dir type_file)
          modu.m_type;

        match modu.m_code with
          None -> ()
        | Some code ->
            self#output_code
              modu.m_name
              (Filename.concat !Args.target_dir code_file)
              code
      with
        Sys_error s ->
          raise (Failure s)

    (** Generate the [<index_prefix>.html] file corresponding to the given module list.
       @raise Failure if an error occurs.*)
    method generate_index module_list =
      try
        let chanout = open_out (Filename.concat !Args.target_dir self#index) in
        let b = new_buf () in
        let title = match !Args.title with None -> "" | Some t -> self#escape t in
        bs b doctype ;
        bs b "<html>\n";
        self#print_header b self#title;
        bs b "<body>\n";
        bs b "<center><h1>";
        bs b title;
        bs b "</h1></center>\n" ;
        let info = Odoc_info.apply_opt
            (Odoc_info.info_of_comment_file module_list)
            !Odoc_info.Args.intro_file
        in
        (
         match info with
           None ->
             self#html_of_Index_list b;
             bs b "<br/>";
             self#html_of_Module_list b
               (List.map (fun m -> m.m_name) module_list);
             bs b "</body>\n</html>"
         | Some i -> self#html_of_info ~indent: false b info
        );
        Buffer.output_buffer chanout b;
        close_out chanout
      with
        Sys_error s ->
          raise (Failure s)

    (** Generate the values index in the file [index_values.html]. *)
    method generate_values_index module_list =
      self#generate_elements_index
        self#list_values
        (fun v -> v.val_name)
        (fun v -> v.val_info)
        Naming.complete_value_target
        Odoc_messages.index_of_values
        self#index_values

    (** Generate the exceptions index in the file [index_exceptions.html]. *)
    method generate_exceptions_index module_list =
      self#generate_elements_index
        self#list_exceptions
        (fun e -> e.ex_name)
        (fun e -> e.ex_info)
        Naming.complete_exception_target
        Odoc_messages.index_of_exceptions
        self#index_exceptions

    (** Generate the types index in the file [index_types.html]. *)
    method generate_types_index module_list =
      self#generate_elements_index
        self#list_types
        (fun t -> t.ty_name)
        (fun t -> t.ty_info)
        Naming.complete_type_target
        Odoc_messages.index_of_types
        self#index_types

    (** Generate the attributes index in the file [index_attributes.html]. *)
    method generate_attributes_index module_list =
      self#generate_elements_index
        self#list_attributes
        (fun a -> a.att_value.val_name)
        (fun a -> a.att_value.val_info)
        Naming.complete_attribute_target
        Odoc_messages.index_of_attributes
        self#index_attributes

    (** Generate the methods index in the file [index_methods.html]. *)
    method generate_methods_index module_list =
      self#generate_elements_index
        self#list_methods
        (fun m -> m.met_value.val_name)
        (fun m -> m.met_value.val_info)
        Naming.complete_method_target
        Odoc_messages.index_of_methods
        self#index_methods

    (** Generate the classes index in the file [index_classes.html]. *)
    method generate_classes_index module_list =
      self#generate_elements_index
        self#list_classes
        (fun c -> c.cl_name)
        (fun c -> c.cl_info)
        (fun c -> fst (Naming.html_files c.cl_name))
        Odoc_messages.index_of_classes
        self#index_classes

    (** Generate the class types index in the file [index_class_types.html]. *)
    method generate_class_types_index module_list =
      self#generate_elements_index
        self#list_class_types
        (fun ct -> ct.clt_name)
        (fun ct -> ct.clt_info)
        (fun ct -> fst (Naming.html_files ct.clt_name))
        Odoc_messages.index_of_class_types
        self#index_class_types

    (** Generate the modules index in the file [index_modules.html]. *)
    method generate_modules_index module_list =
      self#generate_elements_index
        self#list_modules
        (fun m -> m.m_name)
        (fun m -> m.m_info)
        (fun m -> fst (Naming.html_files m.m_name))
        Odoc_messages.index_of_modules
        self#index_modules

    (** Generate the module types index in the file [index_module_types.html]. *)
    method generate_module_types_index module_list =
      self#generate_elements_index
        self#list_module_types
        (fun mt -> mt.mt_name)
        (fun mt -> mt.mt_info)
        (fun mt -> fst (Naming.html_files mt.mt_name))
        Odoc_messages.index_of_module_types
        self#index_module_types

    (** Generate all the html files from a module list. The main
       file is [<index_prefix>.html]. *)
    method generate module_list =
      (* init the style *)
      self#init_style ;
      (* init the lists of elements *)
      list_values <- Odoc_info.Search.values module_list ;
      list_exceptions <- Odoc_info.Search.exceptions module_list ;
      list_types <- Odoc_info.Search.types module_list ;
      list_attributes <- Odoc_info.Search.attributes module_list ;
      list_methods <- Odoc_info.Search.methods module_list ;
      list_classes <- Odoc_info.Search.classes module_list ;
      list_class_types <- Odoc_info.Search.class_types module_list ;
      list_modules <- Odoc_info.Search.modules module_list ;
      list_module_types <- Odoc_info.Search.module_types module_list ;

      (* prepare the page header *)
      self#prepare_header module_list ;
      (* Get the names of all known types. *)
      let types = Odoc_info.Search.types module_list in
      known_types_names <-
        List.fold_left
          (fun acc t -> StringSet.add t.ty_name acc)
          known_types_names
          types ;
      (* Get the names of all class and class types. *)
      let classes = Odoc_info.Search.classes module_list in
      let class_types = Odoc_info.Search.class_types module_list in
      known_classes_names <-
        List.fold_left
          (fun acc c -> StringSet.add c.cl_name acc)
          known_classes_names
          classes ;
      known_classes_names <-
        List.fold_left
          (fun acc ct -> StringSet.add ct.clt_name acc)
          known_classes_names
          class_types ;
      (* Get the names of all known modules and module types. *)
      let module_types = Odoc_info.Search.module_types module_list in
      let modules = Odoc_info.Search.modules module_list in
      known_modules_names <-
        List.fold_left
          (fun acc m -> StringSet.add m.m_name acc)
          known_modules_names
          modules ;
      known_modules_names <-
        List.fold_left
          (fun acc mt -> StringSet.add mt.mt_name acc)
          known_modules_names
          module_types ;
      (* generate html for each module *)
      if not !Args.index_only then
        self#generate_elements self#generate_for_module module_list ;

      try
        self#generate_index module_list;
        self#generate_values_index module_list ;
        self#generate_exceptions_index module_list ;
        self#generate_types_index module_list ;
        self#generate_attributes_index module_list ;
        self#generate_methods_index module_list ;
        self#generate_classes_index module_list ;
        self#generate_class_types_index module_list ;
        self#generate_modules_index module_list ;
        self#generate_module_types_index module_list ;
      with
        Failure s ->
          prerr_endline s ;
          incr Odoc_info.errors

    initializer
      Odoc_ocamlhtml.html_of_comment :=
        (fun s ->
          let b = new_buf () in
          self#html_of_text b (Odoc_text.Texter.text_of_string s);
          Buffer.contents b
        )
  end
