Hypertext in Mokvino Web


Mokvino Web is a collection of scripts for GNU Awk, GNU Make and Mokvino which can be used to build and maintain websites.

To generate HTML files from your Mokvino source file, you need to import some Mokvino Web macros:

# In src/www/foo.m5
import(`m5web/html')\
import(`m5web/matrix')\
matrix(`langs'=``en-GB'',

`fmts'=``HTML'',

`title'=``My Home Page'',

`body'=``

'p(``Welcome to my world!'')`

'')\

This states that you want to build pub/www/foo.en-GB.html (if TARGET_LANGUAGE is not set in Makefile), or pub/www/foo.html (if TARGET_LANGUAGE is set to en-GB). The page's content will be similar to:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html lang="en-GB">
<head>
  <meta name="generator" content=
  "HTML Tidy for Linux (vers 25 March 2009), see www.w3.org">
  <meta http-equiv="Content-Type" content=
  "text/html; charset=utf-8">
  <meta name="viewport" content=
  "width=device-width; initial-scale=1">

  <title>My Home Page</title>
</head>

<body>
  <p>Welcome to my world!</p>
</body>
</html>

You can add other entries to <head> by defining the parameter head:

`head'=``

'meta(``by'', ``foo@example.com'')`

'',

To set the description or keywords, use the parameters desc and keywords.

If you need to type a character that is special to HTML, just type it. Mokvino Web will automatically convert it into an escape sequence such as &lt;. If you need a special character that Mokvino uses, escape it with a backslash, e.g., \'. Escape a hash # with a Unicode sequence: \u0023. This means that your source code does not have to be escaped according to what it will finally be output as.

Consistent mark-up structure

To get a consistent document structure across all pages, it is recommended to write a module to define a macro to call matrix in the right way, e.g.:

# In etc/page.m5
import(`m5web/html')\
import(`m5web/matrix')\
define(`PAGE', `indir(`matrix'$$!?vname$$!?langs$$!?fmts, `flags'=`C',

`title'=`ifelse($$title,,, `$$title` — '')`My Website''$$!?desc,

`head'=``

'lrel_html(``start'', `index')`

'$$head'$$!?body.class,

`body'=``
...
'$$body`
...
''$$?@`'cshiftp(`body',`title',`desc',`head'$$!?@))',

`title,desc,body')\

Then you can simply write:

# In src/www/foo.m5
load(`page')\
PAGE(`langs'=``en-GB'',

`fmts'=``HTML'',

`title'=``My Home Page'',

`body'=``

'p(``Welcome to my world!'')`

'')\

Basic elements

The module m5web/html includes m5web/hypertext, which defines several macros to build basic HTML elements. For block elements, there are p, div, pre, blockquote, etc. For in-line elements, there are span, samp, code, etc. Most of these can take parameters such as id, title, class, lang. The values of id and lang parameters should be single-quoted, while title and class should be double-quoted.

Multilingual pages

Apache can be set up to serve the several pages in different languages under the same address, using content negotiation. If you want a page to be available in several languages, you can list them in the langs parameter of matrix (or your chosen wrapper, like PAGE):

# In src/www/foo.m5
load(`page')\
import(`mokvino/language')\
PAGE(`langs'=``en-GB', `fr'',

`fmts'=``HTML'',

`title'=`iflang(``fr'', ``Ma page d'accueil'', ``My Home Page'')',

`body'=``

'p(`iflang(``fr'', ``Bienvenue dans mon monde!'', ``Welcome to my world!'')'')`

'')\

If TARGET_LANGUAGE is not set, this generates two pages, pub/www/foo.en-GB.html and pub/www/foo.fr.html. The module mokvino/language provides a macro iflang, which can be used to select content based on which of these files is being generated. The first parameter, and every alternate, is actually a list of matching languages.

If you set lang on an enclosing element, it will influence calls to iflang embedded in its content. For example:

div(`lang'=`de', `iflang(``de'', ``Guten Tag!'', ``Good day!'')')

…yields:

<div lang="de">Guten Tag!</div>

…regardless of the language of the containing page.

External linking

link(``http://www.google.com/'', ``Google'')

…yields:

<a href="http://www.google.com/">Google</a>

If you try to put one link in another, the inner one will be disabled. For example:

link(``http://www.facebook.com/'', ``Facebook and 'link(``http://www.google.com/'', ``Google'')` are great'')

…yields:

<a href="http://www.facebook.com/">Facebook and <span>Google</span> are great</a>

In an English context, this code:

link(``http://www.example.com/'', ``Example'', `title'=``Exemple'', `tlang'=`fr')

…yields:

<a title="Exemple" href="http://www.example.com/" lang="fr"><span lang="en">Example</span></a>

Internal linking

When a file such as src/www/foo/bar.m5 is processed, it is expanded with a predefined macro VNAME with the value `foo/bar'. The macro matrix uses this to work out that files such as pub/www/foo/bar.html are to be created. It also temporarily defines VFILE as VNAME plus the suffix of the type it is generating.

VFILE is used to create relative links between pages within the same site. The macro reluri in m5web/paths takes two parameters, name and suffix, and relativizes `$$name'$$suffix against VFILE. For example:

import(`m5web/paths')\
define(`VFILE', `foo/bar.html')\
reluri(`foo/fong', ``.html'')
reluri(`pics/pretty', ``.png'')
reluri(`home', ``.xhtml'')
reluri(`foo/bar', ``.html'')
reluri(`foo/bar', ``.xhtml'')

…yields the following:

fong.html
../pics/pretty.png
../home.xhtml
bar.html
bar.xhtml

The value of INDEXBASE specifies which links should map to a directory-like URI. By default, it is index, so links to index and foo/index, etc, will actually be encoded as ./ and foo/. For example:

import(`m5web/paths')\
define(`INDEXBASE', `home')\
define(`VFILE', `foo/bar.html')\
reluri(`foo/fong', ``.html'')
reluri(`pics/pretty', ``.png'')
reluri(`home', ``.xhtml'')
reluri(`foo/home', ``.xhtml'')
reluri(`foo/bar', ``.html'')
reluri(`foo/bar', ``.xhtml'')

…yields the following:

fong.html
../pics/pretty.png
../
./
bar.html
bar.xhtml

You should set INDEXBASE to nothing when generating a local page hierarchy, so that no link to a path ending in index gets converted to a link that doesn't work on a local filesystem.

In m5web/hypertext, the first parameter of macro llink is a path $$vname (comparable with VNAME), followed by content, and wraps $$content in an <a> linking to the specified path, appending $$suffix. However, if this path is the same as the page you're linking from, it will wrap the content in a <span> instead. The optional parameter class is used to set the class of the <a> or <span>, while liveclass only sets the class of <a>, and deadclass only sets the class of <span>. To reference a fragment, provide the fragment identifier as anchor. For example, in src/www/baz/qux.m5:

llink(`foo/bar', `suffix'=``.html'', `anchor'=`top', ``Top of page'')

…yields the following:

<a href="../foo/bar.html#top">Top of page</a>

You're more likely to use llink_html, which is identical, except that suffix has a default value of HTML_LNK_SUFFIX, which itself has a default value of `.html'.

To set an anchor point in a page, set id on an appropriate element:

div(`id'=`here', ``The content'')

This sets the id attribute on the <div>, and it influences llink, such that links to this element from within it will be dead.

Tables

m5web/hypertext includes a macro table for building tables. The usual HTML attributes can be set with parameters like id, lang, border, summary, etc. The cellspacing and cellpadding attributes are controlled with the parameters spacing and padding.

The body of the <table> begins with a <caption>, if caption is defined. Its attributes can be controlled by parameters such as caption.lang, etc.

The value of $$columns is expanded next. Here, use the macros col(``class'') to create <col> elements, colgroup(``class'', `col col col') to create <colgroup> elements with a span implied by content, and colspan(3, ``class'') to create empty <colgroup> elements with an explicit span.

If the parameter head is defined, a <thead> is created next to contain $$head. $$foot is then similarly wrapped in a <tfoot>. These can be filled with calls to macros tr, th and td to create HTML elements of the same names.

The value of the first parameter content then completes the <table> element, and should contain the main table data, consisting of calls to macros tr, th and td, optionally partitioned into tbodys.

The macros th and td take the expected parameters, except for cols which expands to the attribute colspan, and rows which expands to rowspan.

Forms

m5web/hypertext includes three macros for HTML forms. getform creates <form method="GET" action="'$$action`">'$$content`</form>, where action and content are the first two parameters. posttextform creates a POST form using application/x-www-form-urlencoded as the content type. postform creates a POST form using multipart/form-data. There are also three macros lgetform, lposttextform and lpostform, which take a local document name instead of action, and an optional suffix parameter, both passed to reluri.

For a plain button, use button. For a submit button, use submitbutton. For a reset button, use resetbutton. These take content as their first parameter.

textfield(``name'', ``value'') defines a text field, taking optional parameters with the usual names for attributes, except max for maxlength. passwordfield(``name'', ``value'') defines an obscured password field, with the same optional parameters. hiddenfield(``name'', ``value'') defines a hidden field.

uploadfield expands to <input type="file">, again with the usual optional parameters.

radiobutton and checkbox generate <input type="radio"> and <input type="checkbox">, with name and value being the first two parameters. The attribute checked is controlled by the presence of the parameter checked, so you only need checkbox(``name'', ``value'', `checked'=) to set it.

selectfield expands to <select>…</select>, and macros optgroup and option should be used to produce the expected elements within it.

label should be used to wrap a control in <label>…</label>. labelfor(`foo', ``Text'') should be used to remotely attach the label Text to an element such as textfield(`id'=`foo').