HTML Dialect
Author: Henrik Mikael Kristensen Date: 3-Aug-2008/14:01:56+2:00 Copyright: 2008 - HMK Design Version: 0.0.5
Contents:
Introduction
Installation
Usage
Full Page
Enclosing Tags and Recursion
HTML Output
CSS Styles
HTML Doctypes
Dynamic Content
Forms
Using the Form Object
Dialect Rule Reference
block-types
value-types
cell-types
href-types
doc-types
verbatim-rules
eval-rules
base-rules
link-rules
image-rules
table-rules
table-row-rules
table-cell-rule
table-row-rule
table-format-rules
table-block-rules
tag-rule
tag-rules
loop-rules
format-rules
form-rules
page-rules
error-rules
word-rules
all-rules
Dialect Command Reference
page
===
tag
end-tag
at
do
loop
traverse
table
doc-types
block!
string!
number!
tuple!
money!
tag!
word!
get-word!
path!
lit-path!
refinement!
binary!
date!
time!
url!
email!
form
hidden
field
password
textarea
select
checkbox
radio
button
reset
submit
Practical Example
Future
This is alpha software under development. Features may change drastically during development.
Introduction
The purpose of the HTML dialect is to produce HTML code using a REBOL dialect. There are multiple reasons for this:
- Dialect code takes up much less space than HTML and is simpler and easier to write.
- Fits both static and dynamic HTML content generation.
- It's easier to make dynamic content.
- Provide standards compliant HTML, no matter the doctype, using the same dialect code.
- REBOL code all the way. No need to intermix REBOL code with HTML.
The HTML Dialect is currently in version 0.0.5 and is released under the BSD license.
Installation
In order to use the HTML dialect, include the html.r file in your code and you're ready to go! You will know it's loaded when the ctx-html context exists in memory.
Usage
Primarily this is for usage with a webserver, such as Cheyenne, so there is an output buffer (a plain text string) called out-buffer available. When you want to output the content of it, you can do this in an entirely normal fashion with print, or by saving it or whatever you want to do.
You generate HTML with the html-gen or the output-html function. This is similar to the layout function in VID in REBOL/View, if you've tried that.
The html-gen function performs the parsing of the dialect and fills out-buffer with HTML code. Example:
>> html-gen [=== test ["my code"]]
== true
>> out-buffer
== "<test>my code</test>"
Every time you add a word, string, element or other piece of dialect code inside the dialect, it's run through html-gen and appended to the end of out-buffer without spacing.
>> html-gen ["more code"]
== true
>> out-buffer
== "<test>my code</test>more code"
html-gen accepts a variety of datatypes and can therefore be used to generate small bits of HTML code, output a single char or even nothing, if your input is none!. html-gen uses itself extensively inside its own parser for this purpose to minimize code size and allow dialect recursion.
Example:
html-gen "Text"
Will append the following to out-buffer:
Text
This code:
html-gen 'a
Will append the following to out-buffer:
a
This code:
html-gen [tag [shout till noon]]
Will append the following to out-buffer:
<shout till="noon">
The output-html function wraps html-gen. The function first clears the out-buffer, then generates the HTML, then copies the out-buffer, then clears the out-buffer again and finally returns the copy. This function is mostly useful, if you want to see output directly returned to the console, or if you generate entire pages without appending content manually in the out-buffer. Example:
>> output-html [=== test ["my code"]]
== "<test>my code</test>"
>> output-html ["more code"]
== "more code"
Both html-gen and output-html accept none!, string!, tag!, file!, url!, number!, time!, date!, get-word!, word! and block! as input.
If you want to generate multiple pages in sequence with html-gen, use clear out-buffer between generating pages with html-gen, or use the output-html function directly.
For the examples below we will use output-html for simplicity.
Full Page
Example for generating one full page:
output-html [page "My Page Title" ["This is my Webpage"]]
That line produces the following HTML code (indentation used here for clarity, it is not present in the actual output):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>My Page Title</title>
</head>
<body>This is my Webpage</body>
</html>
If you want to include a style sheet to the basic page, you can add it as a parameter to the page command:
output-html [page "My Page Title" css style.css ["This is my Webpage"]]
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<link href="style.css" rel="stylesheet" type="text/css" />
<title>My Page Title</title>
</head>
<body>This is my Webpage</body>
</html>
Enclosing Tags and Recursion
The HTML dialect supports recursing tags in as many levels as you want. You can for example use enclosing tags with the the === command, tag and end-tag, or directly with a word for some tags:
output-html [=== p [=== pre [=== tt [=== a [Hello] ]]]]
== "<p><pre><tt><a>Hello</a></tt></pre></p>"
output-html [
tag [p] tag [pre] tag [tt] tag [a] "Hello"
end-tag end-tag end-tag end-tag
]
== "<p><pre><tt><a>Hello</a></tt></pre></p>"
output-html [p [pre [tt [a Hello]]]]
== "<p><pre><tt><a>Hello</a></tt></pre></p>"
The HTML dialect tracks which tags were inserted using the tag command, but not the === command or when using words directly. Whenever that happens, any tag that does not exist in the single-tags block inside the dialect context is tracked internally. Whenever you output an end-tag command, the last used tag is placed into the out-buffer.
For the following examples, consider that >br<, >hr< and >img< are in the single-tags block:
Examples:
output-html [tag [p] tag [hr] end-tag]
== "<p><hr /></p>"
If you attempt to put an end-tag where there are no more end-tags to track, an error is returned:
output-html [tag [p] tag [hr] end-tag end-tag]
** Script Error: Out of range or past end
** Where: html-gen
** Near: out close-tag either block? last
Tags are tracked across multiple uses of html-gen, so if you don't end a tag correctly in one use of html-gen, subsequent uses of it will also contain errors. Furthermore, the HTML dialect can't track if your complete page contains too few end-tags. The only way is to check if ctx-html/end-tags is empty at the end of page generation.
HTML Output
Some things about the output:
- There are never spaces between uses of html-gen, so any spaces that need to be there, must be added by you.
- The output is always a string.
- The output may not be very readable, as html-gen does not add newlines or indentations to the HTML code.
CSS Styles
Any tag can be followed by an optional issue! which describes a CSS class used for this style. If the issue! is not added, the style is not included for that tag.
Example:
output-html [div #headline "Hello"]
== {<div class="headline">Hello</div>}
HTML Doctypes
The HTML dialect supports most available versions of the HTML specs, though not yet adhering to them 100%. When including this on the web page, the !DOCTYPE tag is automatically included at the top of the webpage. Single standing tags in XHTML 1.0 and upwards are always postfixed with a /. The following types are supported:
html-2.0-dtd
html-3.2-dtd
html-4.01-strict
html-4.01-transitional
html-4.01-frameset
xhtml-1.0-strict
xhtml-1.0-transitional
xhtml-1.0-frameset
xhtml-1.0-dtd
xhtml-basic-1.0-dtd
xhtml-basic-1.1-dtd
mathml-1.01-dtd
xhtml-mathml-svg-dtd
svg-1.0-dtd
svg-1.1-full-dtd
svg-1.1-basic-dtd
svg-1.1-tiny-dtd
They are all stored in the doc-types block, which is used in the HTML dialect.
To switch the HTML version, just use it before any code:
output-html [html-4.01-strict tag [br]]
== {<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd"><br>}
output-html [xhtml-1.0-strict tag [br]]
== {<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><br />}
Dynamic Content
The use of get-word! types in code will automatically get a word from the global context, or whatever context the dialect code block is bound to at HTML generation time. This is a global rule.
a: "my string"
output-html [p [:a]]
== "<p>my string</p>"
Forms
Creating forms with the HTML dialect is very straight forward: You add form elements and a submit button, and then when you submit the form, the server receives them via POST.
The HTML dialect poses some intentional limitations on forms for simplifying the form system:
- All HTML dialect forms send via the POST method.
- There are no per-field settings yet, such as maximum field size.
- There is no scheme yet for form validation, neither server- nor client-side.
A form is created by stating the form action, name and possible default input values through an object. The form code is enveloped in a block. Each field element describes its associated name as a word! value. This method is fine, if you don't want default data to be put in the form or expect to revisit the form in a validation process, or if you are creating a form to be used on a completely static HTML page.
Example:
form submit.rsp [
div #label "Name" [field name]
div #label "Address" [field address]
div [submit "Submit Form"]
]
Produces:
<form action="submit.rsp" method="POST">
<div class="label">
Name
<input type="field" name="name" value="" />
</div>
<div class="label">
Address
<input type="field" name="address" value="" />
</div>
<div>
<input type="submit" name="Submit Form" />
</div>
</form>
The names used for each field in the form are the same as those used in the dialect.
Using the Form Object
If you use a get-word! in the form specifications, you will be able to attach an external object to the form. The object must already exist with the required content. This is a better method than the above one, if you desire to recreate the form with its existing data, or you wish default data to be put in the form.
While the job of the HTML dialect finishes when rendering the HTML code, it means you can essentially tie form data to a fixed object that simply updates its values when you submit form data to the server, granted that you must write this part yourself. The ctx-html internal value is form-object, which is by default none.
The added benefit is that the HTML dialect will let you auto-refill the form with the stored values in the object, when the page needs to be rendered again. The form object is stored internally in the ctx-html context as form-object.
Example, showing a pre-existing form object, that has the same words as used in the form:
form-data: make object! [
name: "Luke Lakeswimmer"
address: "Tatooine Rebol Base"
]
And in the form dialect code, we include form-data:
form submit.rsp :form-data [
div #label "Name" [field name]
div #label "Address" [field address]
div [submit "Submit Form"]
]
Produces:
<form action="submit.rsp" method="POST">
<div class="label">
Name
<input type="field" name="name" value="Luke Lakeswimmer" />
</div>
<div class="label">
Address
<input type="field" name="address" value="Tatooine Rebol Base" />
</div>
<div>
<input type="submit" name="Submit Form" />
</div>
</form>
You can also create the form-data object inline in the dialect code or as a block of key/value pairs.
Dialect Rule Reference
The following rule-sets describe how the dialect parser works. The entire parser is built from the rule blocks or sets below. Actions in the code (the parentheses), are left out for clarity. We start with the types and work our way from the low-level rules and up toward the main parser.
block-types
This describes all block types except for path!
block-types: [block! | hash! | list!]
value-types
This rule defines the datatypes that describe values directly, such as numbers, strings and urls. Tags are purposely disallowed.
value-types: [
money! | binary! | number! | date! | time! | tuple!
| url! | email! | file! | any-string! | char! | pair!
]
cell-types
This rule defines the allowed datatypes for input parameters for most tags. It should be the same types as allowed in html-gen.
cell-types: [
['do block-types]
| [
value-types | block-types | datatype! | word!
| get-word! | lit-word! | path! | lit-path!
| refinement! | logic!
]
]
href-types
These rules define URL inputs for use in page-rules.
href-types: [['do block-types] | [word! | url! | string! | path! | refinement!]]
doc-types
This rule is used as a word-rule.
doc-types: [
html-2.0-dtd
| html-3.2-dtd
| html-4.01-strict
| html-4.01-transitional
| html-4.01-frameset
| xhtml-1.0-strict
| xhtml-1.0-transitional
| xhtml-1.0-frameset
| xhtml-1.0-dtd
| xhtml-basic-1.0-dtd
| xhtml-basic-1.1-dtd
| mathml-1.01-dtd
| xhtml-mathml-svg-dtd
| svg-1.0-dtd
| svg-1.1-full-dtd
| svg-1.1-basic-dtd
| svg-1.1-tiny-dtd
]
verbatim-rules
These rules are output directly as they are input (verbatim). They are the first rules in the HTML dialect, as the input is not processed at all.
verbatim-rules: [
value-types | tag! | lit-word! | path! | lit-path!
| refinement! | datatype! | logic!
]
eval-rules
These rules are used for the TAG command.
eval-rules: [any ['do block-types | any-type!]]
base-rules
These rules produce various types of common tags and links and are used as base for higher levels of rules.
base-rules: [
'=== word! opt ['opts block-types] cell-types
| 'tag into eval-rules
| 'end-tag
| 'do block-types
| block-types
]
link-rules
These are the rules used with the at command to produce links using various input formats.
link-rules: [
'at [
'page word!
| 2 cell-types
any [
'vars [block! | object! | get-word!]
| 'words block!
]
]
]
image-rules
These are the rules for producing image links.
image-rules: [
image cell-types
]
table-rules
These are the main rules for building an HTML table. It uses several sub-rules which are described below.
table-rules: [
'table
opt issue!
any [
['rows [get-word! | into table-row-rules | into table-block-rules]]
| ['format block! 'rows [get-word! | into table-format-rules]]
]
]
table-row-rules
These rules define how a single row in a table can be shaped.
table-row-rules: [
any [
'row any [
['cell | 'header]
any [
'colspan integer! | 'align word!
| 'width integer! opt 'percent | 'class word!
]
[none! | cell-types]
]
]
]
table-cell-rule
This table rule is used to generate a table cell, where there are multiple columns per row in the input data, or the input data consists of objects.
table-cell-rule: cell-types
table-row-rule
This table rule is used to generate a table cell, where there is only one column per row in the input data and the input data does not consist of objects.
table-row-rule: cell-types
table-format-rules
These rules are used after the format command and are identical in structure to table-block-rules, however when using blocks of blocks or plain blocks as input, the formatting is ignored.
table-format-rules: [
any [object! | into [any table-cell-rule] | table-row-rule]
]
table-block-rules
These rules are used after the rows command without using format first. This means objects are just output cell by cell. The data row is parsed the same way as the formatting row.
table-block-rules: [
any [object! | into [any table-cell-rule] | table-row-rule]
]
tag-rule
These rules are used in cases where a normal HTML tag is wanted. It can be used recursively.
tag-rule: [
[
'html | 'head | 'title | 'body | 'p | 'strong
| 'em | 'b | 'i | 'u | 'tt | 'big | 'small
| 'strike | 'del | 'pre | 'ul | 'il | 'li
| 'sup | 'sub | 'samp | 'code | 'blockquote | 'q
| 'kbd | 'var | 'cite | 'tr | 'th | 'td
| 'table | 'a | 'div | 'span | 'dl | 'dt | 'dd
| 'h1 | 'h2 | 'h3 | 'h4 | 'h5 | 'h6
]
opt issue!
opt 'id word!
[tag-rule | get-word! | cell-types | ()]
]
tag-rules
It looks redundant here, but in the source code, this rule collects all tags properly from recursive runs of tag-rule and generates the required HTML code.
tag-rules: tag-rule
loop-rules
These rules produce loops, and allow traversing data blocks either wholly or partially.
loop-rules: [
'loop integer! block-types opt ['alternate block-types]
| 'traverse [block-types | get-word!]
opt ['using [word! | 'lit-word | get-word! | block-types]]
block-types
opt ['alternate block-types]
]
format-rules
These rules allow special formatting parsers for text. The rules are meant to be extensible later, and are not really useful now.
format-rules: ['format word! cell-types]
form-rules
These rules produce form tags and are considered a higher level of rules. They also manage the form content, either from words or a specific form object.
form-rules: [
'form cell-types opt [get-word! | ['vars | object!]] cell-types
| 'textarea word!
| ['field | 'button | 'hidden | 'password] word!
| 'checkbox word!
| 'radio word!
| 'select word! opt ['values | 'key-values] cell-types
| ['submit | 'reset] string!
]
page-rules
These rules produce the outer skeleton of the webpage by providing the HEAD and BODY section.
page-rules: [
'page cell-types any [
'redirect href-types integer!
| 'favicon href-types
| 'charset [string! | word!]
| 'css href-types
| 'description string!
| 'robots some [
'noindex | 'index | 'nofollow | 'follow
| 'noarchive | 'nosnippet | 'noodp | 'noydir
]
| 'script href-types
| 'meta ['name | 'http-equiv] 2 cell-types
] block-types
]
error-rules
These rules (actually only one rule for now) are used for handling and printing errors generated by the parser during HTML creation. They will be extended later to become more useful.
error-rules: 'errors
word-rules
The word rules are used for lists of words that are either dynamic to the parser, i.e. lists of words that are built during parsing or are built into the HTML dialect, such as the word list for doc-types.
word-rules: [doc-types | get-word! | word!]
all-rules
These rules are the collection of all the above mentioned rules. These rules are used directly by the parser, and you can see here in which order they are evaluated.
all-rules: [
any [
verbatim-rules | base-rules | link-rules | image-rules
| table-rules | tag-rules | loop-rules | format-rules
| form-rules | page-rules | error-rules | word-rules
]
]
Dialect Command Reference
page
Produces the outer skeleton for a webpage with correct HTML tags and DOCTYPE.
Parsed as:
'page cell-types <subcommands> block-types
Example:
page "My Page" []
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>My Page</title>
</head>
<body></body>
</html>
page supports a range of subcommands. The page command supports using the subcommands as many times as you want, before creating the main page in a block.
redirect
This allows setting a 302 redirect for a page along with the number of seconds to wait before redirecting.
Parsed as:
'redirect href-types integer!
Example:
page "Wrong page" redirect http://foo.com 5
["Redirecting to foo.com in 5 seconds"]
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="refresh" content="5; url=http://foo.com" />
<title>Wrong page</title>
</head>
<body>Redirecting to foo.com in 5 seconds</body>
</html>
refresh
This is an alias for redirect and does exactly the same thing.
Parsed as:
'refresh href-types integer!
favicon
This sets the favicon for the webpage. It does not generate a favicon, but only links to an .ico file stored on the server.
Parsed as:
'favicon href-types
Example:
page "My page" favicon icon.ico ["My web page"]
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<link rel="shortcut icon" href="icon.ico" />
<title>My page</title>
</head>
<body>My web page</body>
</html>
charset
This allows you to set the charset for the webpage. A full list of charsets is given here. Note that REBOL 2 does not support Unicode, so text will be encoded as plain ASCII. Therefore you should be careful when selecting UFT-8 encoding. There is no standard selected charset. Unicode will be supported with REBOL 3.
Parsed as:
'charset [string! | word!]
Example:
page "My Page" charset utf-8 ["My Unicode Webpage."]
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>My Page</title>
</head>
<body>My Unicode Webpage.</body>
</html>
description
This sets the description of the webpage for use by search engines.
Parsed as:
'description string!
Example:
page "My Page" description "A great page" ["My page"]
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta name="description" content="A great page" />
<title>My Page</title>
</head>
<body>My page</body>
</html>
robots
This manages settings for a webpage with respect to how webcrawlers and search engines like Google and Yahoo see them. There are several settings available, each one set as a block of words.
Beware that not all webcrawlers adhere to your robots settings and malware is certain to ignore them.
This subcommand does not generate a robots.txt file.
Parsed as:
'robots into [
some [
'noindex | 'index | 'nofollow | 'follow
| 'noarchive | 'nosnippet | 'noodp | 'noydir
]
]
| | noindex | Tells the search engine not to index this page.
|
| | index | Tells the search engine to specifically index this page. This is default.
|
| | nofollow | Tells the search engine not to follow links on this page for indexing.
|
| | follow | Tells the search engine to specifically follow links on this page for indexing. This is default.
|
| | noarchive | Tells Google not to store a cached copy of this page.
|
| | nosnippet | Tells Google not to display a text snippet under the listing.
|
| | noodp | Tells Google, MSN and Yahoo not to use search information gathered from the Open Directory Project.
|
| | noydir | Tells Yahoo not to use search information from the Yahoo Directory.
|
Example:
page "My Page" robots [index nofollow] ["My page"]
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta name="robots" content="index, nofollow" />
<title>My Page</title>
</head>
<body>My page</body>
</html>
css
This includes a CSS stylesheet file in the webpage. It neither produces CSS styles within the webpage nor produces a CSS file. The CSS file must already exist. You can include as many CSS stylesheet files as you want.
Parsed as:
'css href-types
Example:
page "My Page" css style.css css menu.css ["My Webpage"]
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<link rel="stylesheet" href="style.css" type="text/css" />
<link rel="stylesheet" href="menu.css" type="text/css" />
<title>My Page</title>
</head>
<body>My Webpage</body>
</html>
script
This includes javascript files in the webpage. It neither creates javascript code inside the webpage nor produces a separate javascript file. The javascript file must already exist.
Parsed as:
'script href-types
Example:
page "My Page" script menu.js ["My Webpage"]
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<script src="menu.js" type="text/javascript">
<title>My Page</title>
</head>
<body>My Webpage</body>
</html>
meta
Produces a generic META tag with name and content.
Parsed as:
'meta ['name | 'http-equiv] 2 cell-types
Example:
page "My Page" meta name keywords "wikipedia,encyclopedia" ["My Webpage"]
Produces:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta name="keywords" content="wikipedia,encyclopedia" />
<title>My Page</title>
</head>
<body>My Webpage</body>
</html>
===
This produces an enclosing tag set, such as around the content defined in the following cell-types. You can with opts define an options block for the tag. This is extensively used internally, and you should in most cases not need to use it directly.
Parsed as:
'=== word! opt ['opts block!] cell-types
Example:
=== tt opts [class clever] "My Text"
Produces:
<tt class="clever">My Text</tt>
I wrote earlier that styles are always written as an issue!. This is not the case here, as the opts block contains generic input that is fed to REBOL's built in build-tag function, which produces an # in the finished class string:
build-tag [a class #myclass]
== <a class="#myclass">
Which is no good, so in this one case, we are working without issue! But since this is a low level way of producing tags with options, you may never see it in your own dialect code.
tag
Creates a tag using REBOL's compose/deep and build-tag function. The HTML dialect tracks the use of tags that are not listed in the interal single-tags block and will store this tag in a stack for later retrieval by the end-tag command.
Parsed as:
'tag block!
Example:
tag [th]
Produces:
<th>
Example:
tag [th width 100]
Produces:
<th width="100">
end-tag
Complements tag, by providing. If this is used on a tag that is listed as a single tag or if the command is used too many times, compared to the number of tags, the parser will return an error.
Parsed as:
'end-tag
Example:
tag [th] end-tag
Produces:
<th></th>
at
This produces a link either directly or from a small dialect and is capable of building GET strings from objects, words or values of various formats. It is also capable of telling whether the link exists on the current page and renders the link as plain text instead.
It's recommended to use this method to produce any link in the dialect.
Parsed as:
'at [
'page word!
| 2 cell-types
any [
'vars [block! | object! | get-word!]
| 'words block!
]
]
There are two methods for providing links.
Direct Links
One is to provide the link directly as a URL along with a link name string.
Example:
at http://www.google.com "Go visit Google"
Produces:
<a href="html://www.google.com">Go visit google</a>
Links with get Information
The third method is to generate links with get information. It is possible to build this entirely in REBOL code, so there is no need to manually build such links. All the required encoding happens automatically. There are three ways to do this, depending on the input format you wish to use:
Use a block of words
Given that each word is already set in the context which the dialect block exists in, the words will be placed in the URL along with their values. The word block is preceded by the words word.
Example for words user-id and message-id set to 5 and 3 respectively:
at mylink.html "My Link" words [user-id message-id]
Produces:
<a href="mylink.html?user-id=5&message-id=3">My Link</a>
Use an object
The object should be preceded by the vars word.
at mylink.html "My Link" vars make object! [user-id: 5 message-id: 3]
Produces:
<a href="mylink.html?user-id=5&message-id=3">My Link</a>
Use a word/value block
The simplest method to deliver the content of the URL directly. The word/value block is preceded by vars.
Example:
at mylink.html "My Link" vars [user-id 5 message-id 3]
Produces:
<a href="mylink.html?user-id=5&message-id=3">My Link</a>
You can even mix different formats or use the same format in multiple successions. All words and values will be appended to the url. If the same word occurs twice, it will be added both times, so it's up to your webserver how this is handled in the get request.
Example:
at mylink.html "My Link" words [user-id] vars [message-id 3]
Produces:
<a href="mylink.html?user-id=5&message-id=3">My Link</a>
do
This runs the code inside the given block. The return value is then parsed again with html-gen and is useful for generating certain kinds of dynamic content.
Example:
do [
either empty? data [
"No data available, sorry."
][
table rows :data
]
]
This produces either a text or a table, depending on whether the comedians table is empty.
loop
This loops a block of code. The return value of the looped code is parsed with html-gen, which in turn is appended to the output buffer. You can supply an optional alternate dialect block that is used on even cases, while the first is then used on odd cases.
Parsed as:
'loop integer! block-types opt ['alternate block-types]
Example:
loop 3 ["Test"]
Produces:
TestTestTest
Example:
loop 3 [div #title "Test"]
Produces:
<div class="title">Test</div>
<div class="title">Test</div>
<div class="title">Test</div>
Example where we are setting an alternative design block for even loops and the original block is now only used on odd numbered loops:
loop 3 [div #title "Odd"] alternate [div #title "Even"]
Produces:
<div class="title">Odd</div>
<div class="title">Even</div>
<div class="title">Odd</div>
traverse
Traverses an input block and for each element, inserts it in a design block that is parsed with html-gen. This way you can loop a piece of code with slightly different content in it.
Parsed as:
'traverse
[block-types | get-word!]
opt ['using [block-types | lit-word! | word! | get-word!]]
block-types
opt ['alternate block-types]
The input can be one of three formats:
- A block of blocks containing an identical number of elements for all blocks
- A block of elements that are not blocks or objects
- A block of objects
If the input is anything else, traverse is not guaranteed to work properly.
The input can be managed with a word or a set of words in the using block, which is similar to how REBOL's foreach function works. These words are used as get-words in the design block. You can also use the idx word to indicate the index position of the input block as it moves through the loop.
Blocks of non-block/non-object elements
Example for a single word along with the use of 'idx:
traverse [alpha beta gamma] using [number] [div #title [:idx ":" :number]]
Produces:
<div class="title">1:alpha</div>
<div class="title">2:beta</div>
<div class="title">3:gamma</div>
The more words you have in the using block, the longer it skips, so if you have two words in the block, the traverse loop will skip two elements in the input block, just like foreach.
Example:
traverse
[alpha beta gamma delta] using [a b]
[:idx ": " :b " " :a " "]
Produces:
1: beta alpha 3: delta gamma
Blocks of Blocks
When the input block consists of many evenly sized blocks, the word block will be set to each value in the current block.
Example:
traverse
[[1 2 3][4 5 6]] using [title text date]
[div #title :a div #text :text div #date :date]
Produces:
<div class="title">1</div>
<div class="text">2</div>
<div class="date">3</div>
<div class="title">4</div>
<div class="text">5</div>
<div class="date">6</div>
If the size of the blocks vary anyway, one of two conditions will occur:
- The word block is shorter than the input block. All words are set, while the extra values from the input block are not used.
- The word block is longer than the input block. All values are assigned to their word and the words that are not set, are set to none. In the output, this translate to an empty string.
Blocks of Objects
When the input block consists of objects, each word in the word block is simply bound to the current object.
Example:
traverse
[
make object! [
title: "the title"
text: "my text"
date: 22-nov-2004
]
make object! [
title: "the second title"
text: "my second text"
date: 24-nov-2004
]
]
[div #title :title div #text :text div #date :date]
Produces:
<div class="title">the title</div>
<div class="text">my text</div>
<div class="date">22-nov-2004</div>
<div class="title">the second title</div>
<div class="text">my second text</div>
<div class="date">24-nov-2004</div>
If you are using an unknown word here for the object, REBOL will fail and produce an error for an unknown word, so make sure the words you use exist in all objects in the input block.
Do Blocks inside Traverse
For all cases, you can add extra flexibility in that a do block can be bound to the entry, and so instead of using get-word! directly, you can perform actions on the data as it's being rendered into HTML.
Example:
traverse
[[20 40][14 76]] using [a b]
["Number 1: " :a "Number 2: " :b "Sum: " do [a + b]]
The do block is ordinary REBOL code that is bound to the current object, which is why the words are not necessarily represented in that block as get-word!s.
Produces:
Number 1: 20 Number 2: 40 Sum: 60
Number 1: 14 Number 2: 76 Sum: 90
For all cases, you can also use a get-word! as data. Here's an example where the word numbers is set to [[20 40][14 76]]:
traverse :numbers using [a b] ["Number 1: " :a "Number 2: " :b "Sum: " do [a + b]]
Produces:
Number 1: 20 Number 2: 40 Sum: 60
Number 1: 14 Number 2: 76 Sum: 90
traverse supports using an alternate block for even numbered rows.
Example:
traverse
[[20 40][14 76][3 27]] using [a b]
[
div #odd [
"Number 1: " :a "Number 2: " :b
]
]
alternate [
div #even [
"Number 1: " :a1 "Number 2: " :b
]
]
Produces:
<div class="odd">Number 1: 20 Number 2: 40</div>
<div class="even">Number 1: 14 Number 2: 76</div>
<div class="odd">Number 1: 3 Number 2: 27</div>
Nested Traverse
traverse supports using multiple loops inside eachother. Remember that for each word block, the word block is bound only to the context that resides just inside that design block and not other deeper layered design blocks. The easiest way to handle that is never to use the same word twice in both the inner and the outer loop.
This is buggy and so the example will not work.
Example:
traverse
[
"Photoset 1" images/thumbs
[img1.jpg 23512 20-May-2008 img2.jpg 15922 21-May-2008]
"Photoset 2" images/thumbs
[img3.jpg 16508 22-May-2008 img4.jpg 52214 23-May-2008]
"Photoset 3" images/thumbs
[img5.jpg 28292 24-May-2008 img6.jpg 12080 25-May-2008]
]
using [photoset path images]
[
div #title [:photoset]
traverse :images using [image size date] [
div #photo [
div #image [image do [to-file reduce [dirize to-file :path probe :image]]]
div #size [:size " bytes"]
div #date :date
]
]
]
Produces:
No usable result.
table
This is a complex command to produce HTML tables using various types of input blocks. The following block types are supported:
- 1-dimensional block with any elements as content. This is rendered as a 1-column table.
- 2-dimensional blocks (blocks of blocks) with any elements as content. This is rendered in a normal 2D table grid.
- 1-dimensional block with objects as content. This is rendered as a 2D table using each object as a row and each word in that object as the cell content.
Whenever content for a cell is none! the table cell will be empty.
Example:
table #table-style rows [1 2 3]
Produces:
<table class="table-style" cellspacing="0" cellpadding="0">
<tr>
<td>1</td>
</tr>
<tr>
<td>2</td>
</tr>
<tr>
<td>3</td>
</tr>
</table>
When creating a 2D table, the output is of course a 2D HTML table.
Example:
table #table-style rows [[1 "foo" 3][4 5 "boo"]]
Produces:
<table class="table-style" cellspacing="0" cellpadding="0">
<tr>
<td>1</td>
<td>foo</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>5</td>
<td>boo</td>
</tr>
</table>
You are of course also not restricted to specific output sizes. For blocks that have 4 elements, 4 cells are rendered in the table. If you then only have 2 in the next row, only 2 cells are rendered.
You can describe rows across multiple sets of blocks, in case you are unable to describe them in one block or you want to split the table in multiple parts. This comes in handy, if you decide to change the table formatting midway through rendering. Similarly the first block could be a header description, while the rest is data.
Example:
table #table-style rows [1 2 3] rows [4 5 6]
Produces:
<table class="table-style" cellspacing="0" cellpadding="0">
<tr>
<td>1</td>
</tr>
<tr>
<td>2</td>
</tr>
<tr>
<td>3</td>
</tr>
<tr>
<td>4</td>
</tr>
</table>
Table Dialect
The table has its own internal dialect for table row, cell and header description. You can describe most (but not all) attributes available in a regular hand-written HTML table.
Example:
table
rows [
row
cell 1
cell width 300 2
cell class price 3
row
cell colspan 3 "boo!"
]
Produces:
<table cellspacing="0" cellpadding="0">
<tr>
<td>1</td>
<td width="300">2</td>
<td class="price">3</td>
</tr>
<tr>
<td colspan="3">boo!</td>
</tr>
</table>
This table dialect is both usable directly and also with the format word to specify the style of each data row in upcoming rows of data. This currently only works with block of objects, so in order to produce the examples below, I get personal help from 3 famous comedians:
comedians: reduce [
make object! [
first-name: "Jerry"
last-name: "Lewis"
fun-rating: "Funny"
]
make object! [
first-name: "George"
last-name: "Carlin"
fun-rating: "Funnier"
]
make object! [
first-name: "Jim"
last-name: "Carrey"
fun-rating: "Funny"
]
]
Now we can use the previously learned parts of the table dialect to produce formatted output. When producing formatted output, each object in the block is traversed and when that happens, you can get each value from the object by specifying it as a get-word!:
Example:
table
format [row cell :first-name cell :last-name]
rows :comedians
Produces:
<table cellspacing="0" cellpadding="0">
<tr>
<td>Jerry</td>
<td>Lewis</td>
</tr>
<tr>
<td>George</td>
<td>Carlin</td>
</tr>
<tr>
<td>Jim</td>
<td>Carrey</td>
</tr>
</table>
This output is in fact identical to:
table rows :comedians
So in order to make it more interesting, we add some formatting to the format block:
table
format [
row cell [div name :first-name] cell align right :last-name
]
rows :comedians
Produces:
<table cellspacing="0" cellpadding="0">
<tr>
<td><div class="name">Jerry</div></td>
<td align="right">Lewis</td>
</tr>
<tr>
<td><div class="name">George</div></td>
<td align="right">Carlin</td>
</tr>
<tr>
<td><div class="name">Jim</div></td>
<td align="right">Carrey</td>
</tr>
</table>
Then we can design a header for the comedians block. This is done simply by adding a rows block at the start, and use the header word to specify the >th< tag:
table
rows [row header "First name" header "Last name"]
format [
row cell [div name :first-name] cell align right :last-name
]
rows :comedians
Produces:
<table cellspacing="0" cellpadding="0">
<tr>
<th>First name</th>
<th>Last name</th>
</tr>
<tr>
<td><div class="name">Jerry</div></td>
<td align="right">Lewis</td>
</tr>
<tr>
<td><div class="name">George</div></td>
<td align="right">Carlin</td>
</tr>
<tr>
<td><div class="name">Jim</div></td>
<td align="right">Carrey</td>
</tr>
</table>
We are not restricted to just one row in the table, per object:
table
format [
row cell :first-name cell :last-name
row cell colspan 2 align right :fun-rating
]
rows :comedians
Produces:
<table cellspacing="0" cellpadding="0">
<tr>
<td>Jerry</td>
<td>Lewis</td>
</tr>
<tr>
<td colspan="2" align="right">Funny</td>
</tr>
<tr>
<td>George</td>
<td>Carlin</td>
</tr>
<tr>
<td colspan="2" align="right">Funnier</td>
</tr>
<tr>
<td>Jim</td>
<td>Carrey</td>
</tr>
<tr>
<td colspan="2" align="right">Funny</td>
</tr>
</table>
Currently table does not support tbody.
doc-types
This outputs the appropriate !DOCTYPE tag, for the given word.
Parsed as:
doc-types
Example:
html-2.0-dtd
Produces:
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
block!
Parses the block with html-gen.
string!
Adds the given string to the output buffer. The string is not molded.
number!
Molds the number! and adds it to the output buffer.
tuple!
Molds the tuple! and adds it to the output buffer.
money!
Molds the money! and adds it to the output buffer.
tag!
Adds the given tag to the output buffer.
word!
Adds the given word to the output buffer.
get-word!
Evaluates the word according to the context the dialect block is bound to and adds the result to the output.
path!
Molds the path and adds it to the output buffer.
lit-path!
Molds the lit-path! as a lit-path! and adds it to the output buffer.
refinement!
Molds the refinement! and adds it to the output buffer.
binary!
Molds the binary! and adds it to the output buffer.
date!
Molds the date! and adds it to the output buffer.
time!
Molds the time! and adds it to the output buffer.
url!
Molds the url! and adds it to the output buffer.
email!
Molds the email! and adds it to the output buffer.
form
This produces the outer tags for a form, handles the input fields via an object. All inputs that exist for this form must be placed in the last block parameter for the form.
Parsed as:
'form
cell-types
opt [get-word! | 'vars block! | object!]
cell-types
Example:
form :form-data [
field name
]
hidden
This makes a hidden variable for the form.
Parsed as:
'hidden word!
field
This makes a form field. The associated word is the name of the field.
Parsed as:
'field word!
Example:
field first-name
Produces:
<input type="text" name="first-name" value="" />
password
This makes a form password field, where you can't see what you enter. The associated word is the name of the field.
Parsed as:
'password word!
Example:
password passcode
Produces:
<input type="password" name="passcode" value="" />
textarea
This makes a form text area. The associated word is the name of the text area.
Parsed as:
'textarea word!
Example:
textarea comment
Produces:
<textarea name="comment"></textarea>
select
This makes a select popup for a form. It uses a block as input for generating all its options. The current index for the block is used as the selected position.
Parsed as:
'select
word!
['values | 'key-values]
cell-types
There are two input formats available:
| | values | Means a block of values, such as [1 2 3 4]. This means the value will be sent back to the server, while the value is displayed in the select button.
|
| | key-values | Means a block of word/value pairs, such as [a 1 b 2 c 3]. This means the word is the one to get sent back to the server.
|
Example, for the case of plain values:
select countries values [
"Switzerland"
"Norway"
"United States"
]
Produces:
<select name="countries">
<option>Switzerland</option>
<option>Norway</option>
<option>United States</option>
</select>
Example, for the case of key/value pairs:
select countries key-values [
ch "Switzerland"
no "Norway"
us "United States"
]
Produces:
<select name="countries">
<option value="ch">Switzerland</option>
<option value="no">Norway</option>
<option value="us">United States</option>
</select>
checkbox
Creates a checkbox for the form. The input value for it is anything that conforms to to-logic.
Parsed as:
'checkbox word!
Example:
checkbox receive-email
Produces:
<input type="checkbox" name="receive-email" />
radio
Creates a radio button for the form. As radio buttons can occur in series and are mutually exclusive, an additional group word is needed for each radio button. The selected value is the one that is returned to the server.
Parsed as:
'radio word! cell-types
Example:
radio mode "fast"
radio mode "slow"
Produces:
<input type="radio" name="mode" value="fast" />
<input type="radio" name="mode" value="slow" />
button
Creates a form button.
Parsed as:
'button word! string!
Example:
button click "Click here"
Produces:
<input type="button" name="click" value="Click here" />
reset
Creates a form reset button.
Parsed as:
'reset string!
Example:
reset "Reset Form"
Produces:
<input type="reset" value="Reset Form" />
submit
Creates a form submit button.
Parsed as:
'submit string!
Example:
submit "Submit Form"
Produces:
<input type="submit" value="Submit Form" />
Practical Example
I'm going to build a small source code browser using the HTML dialect. What it will do, is take all *.r files in a directory and display it in an HTML table on a webpage. The example uses RSP in Cheyenne, but you can use it for any place you need to generate a web page.
First we create a source code file, code.rsp, which is where all our code for displaying the source directory will reside.
Second we define what source code to read. I will first settle for reading the directory I need and get information on each file and store that information in a source-code block:
source-code: []
For the sake of simplicity, let's say the source files we want to examine are stored in the current directory, and remove all files that are not ending with the .r extension:
files: read %.
remove-each file files [%.r <> suffix? file]
The information returned from each file is an object, containing useful information like file size and modification date. Since the HTML dialect handles blocks of objects just fine for tables, we can just loop through the directory using the info? function:
foreach file files [
append source-code make info? file [file-name: file]
]
Next we create the page itself:
output-html [page "Code Browser" []]
Next we add the table inside the page block. The table specs are used to format the block to what we want to see for each row, or more accurately, for each object that is traversed in the input block, as the formatting is not limited to a single table row.
We describe three parts: The header, the format of the layout and then the files that need to be displayed. As the table renderer progresses, it can change the format as it moves along, every time it encounters a format or rows word. So the first row is going to be the header. The format changes the format of the following rows and the second rows provides the rest of the data.
table #file-list
rows [row header "File Name" header "Time" header "File size"]
format [row cell :file-name cell :date cell :size]
rows :files
We can then add some more features, such as reading the header of each source file and scour it for information that can be displayed in the cell. This requires an extra column called "Notes":
rows [
row
header "File Name"
header "Time"
header "File size"
header "Notes"
]
For the format column, we add the notes column by reading the file given for the file name in the current object. This is wrapped in a little block for the cell. Some files may not have a proper file header, so we want to ignore those, by wrapping the load code in an attempt and providing an alternative text string to print for those cases:
format [
row
cell [strong :file-name]
cell :date
cell align right [:size " bytes"]
cell [
do [
any [
attempt [get in first load/header :file-name 'title]
"No notes"
]
]
]
]
And that's it.
If we want to add a few bits more, such as right aligned size column and bold file name, we can add that in the format block. These changes are visible in the completed source code for the code.rsp page:
do %html.r
source-code: []
files: read %.
remove-each file files [find/match file %.]
foreach file files [
append source-code make info? file [file-name: file]
]
print output-html [
page "Code Browser" [
table #file-list
rows [
row
header "File Name"
header "Time"
header "File size"
header "Note"
]
format [
row
cell [strong :file-name]
cell :date
cell align right [:size " bytes"]
cell [
do [
any [
attempt [get in first load/header :file-name 'title]
"No notes"
]
]
]
]
rows :source-code
]
]
To further change the appearance of the table, it's recommended to use CSS. As you might be able to see, the CSS class for the table is file-list.
Future
As this is a very early development version of the dialect a lot of features are missing:
- CSS Dialect to simplify creation of CSS content.
- Further automatization and simplification of form creation and management.
- Stylize function similarly to VID, for customized tags with one word.
- High level functions for creating one-word templates, for extremely small page descriptions.
|