%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1997, 2006 The University of Melbourne.
% Copyright (C) 2014, 2018, 2022 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%-----------------------------------------------------------------------------%
%
% File: html.m.
% Author: fjh.
%
% This module provides a strongly-typed, quite declarative method
% for representing and outputting arbitrary HTML text.
% It is intended for use in CGI scripts.
%
% Basically all the predicates here are wrappers around io.write_string.
% However, the types defined here let you indicate the structure of your
% HTML text in the structure of the Mercury terms used to represent it.
%
%-----------------------------------------------------------------------------%
:- module html.
:- interface.
:- import_module bool.
:- import_module io.
:- import_module list.
:- import_module pair.
:- import_module string.
%-----------------------------------------------------------------------------%
:- type html
---> html(
header,
body
).
:- type header == list(header_item).
:- type header_item
---> title(markup)
; header_item(string). % String can contain any HTML markup.
% This is a general "catch-all" for
% anything not covered by the above
% cases.
:- type body == markup.
% XXX add anchors
:- type markup
---> heading(int, markup)
; style(style, markup) % a.k.a. logical style
; font(font, markup) % a.k.a. physical style
; text(string)
; definition_list(list(pair(markup)))
; list(list_type, list(markup))
; form(string, markup) % actionURL, form contents
; field(string, field) % name, field type
; address(markup)
; np % new paragraph
; br % line break
; hr % horizontal_rule
; markup(string) % String can contain any HTML markup.
% This is a general "catch-all" for
% anything not covered by the above
% cases.
; ','(markup, markup).
:- type list_type
---> ordered
; unordered
; menu
; directory.
:- type style
---> emph
; strong
; samp
; code
; keyboard
; cite
; var.
:- type font
---> italics
; bold
; underline
; typewriter. % typewriter fixed-width font
% XXX add maps
:- type field
---> text(
int, % size (display width in characters)
int, % maxlength
string % initial (default) value
)
; password(
int, % size
int, % maxlength
string % initial (default) value
)
; textarea(
int, int, % rows, columns
string % initial (default) value
)
; checkbox(
bool, % initial (default) value
string % value sent, if checkbox set
)
; radio(
bool, % initial (default) value
string % value sent, if button set
)
; select(
int, % size,
bool, % allow multiple selections?
list(pair(
string, % selection text
bool % selected?
))
)
; submit(
string % text on the pushbutton
)
; reset(
string % text on the pushbutton
)
; hidden(
string % value
).
:- pred output_content_type_html(io::di, io::uo) is det.
:- pred output_html(html::in, io::di, io::uo) is det.
:- pred output_header(header::in, io::di, io::uo) is det.
:- pred output_header_item(header_item::in, io::di, io::uo) is det.
:- pred output_body(body::in, io::di, io::uo) is det.
:- pred output_markup(markup::in, io::di, io::uo) is det.
:- pred output_field(string::in, field::in, io::di, io::uo) is det.
:- pred output_form_start(string::in, io::di, io::uo) is det.
:- pred output_form_end(io::di, io::uo) is det.
% Convert any special characters in a HTML markup string into
% appropriate HTML escapes.
%
:- func escape_html_string(string) = string.
:- pred escape_html_string(string::in, string::out) is det.
% Convert any special characters in a HTML attribute value string into
% appropriate HTML escapes.
%
:- func escape_attr_string(string) = string.
:- pred escape_attr_string(string::in, string::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module char.
:- import_module int.
%-----------------------------------------------------------------------------%
:- func list_type_name(list_type) = string.
list_type_name(ordered) = "ol".
list_type_name(unordered) = "ul".
list_type_name(menu) = "menu".
list_type_name(directory) = "dir".
:- func style_name(style) = string.
style_name(emph) = "em".
style_name(strong) = "strong".
style_name(samp) = "samp".
style_name(code) = "code".
style_name(keyboard) = "kbd".
style_name(cite) = "cite".
style_name(var) = "var".
:- func font_name(font) = string.
font_name(italics) = "it".
font_name(bold) = "b".
font_name(underline) = "u".
font_name(typewriter) = "tt".
%-----------------------------------------------------------------------------%
output_content_type_html(!IO) :-
io.write_string("Content-type: text/html\n\n", !IO).
output_html(html(Head, Body), !IO) :-
output_header(Head, !IO),
io.nl(!IO),
output_body(Body, !IO).
output_header(HeaderItems, !IO) :-
output_markup_scope("head",
output_list(output_header_item, HeaderItems), !IO).
output_header_item(title(Title), !IO) :-
output_markup_scope("title", output_markup(Title), !IO).
output_header_item(header_item(Markup), !IO):-
io.write_string(Markup, !IO).
output_body(Body, !IO) :-
output_markup_scope("body", output_markup(Body), !IO).
output_markup((Markup1, Markup2), !IO) :-
output_markup(Markup1, !IO),
output_markup(Markup2, !IO).
output_markup(address(Address), !IO) :-
output_markup_scope("address", output_markup(Address), !IO).
output_markup(heading(Level, Heading), !IO) :-
io.format("
\n", !IO).
output_markup(br, !IO) :-
io.write_string("
\n", !IO).
output_markup(hr, !IO) :-
io.write_string("