Up and Running with Frontier Web Site Management
by Matt Neuburg
Author of the book Frontier: The Definitive Guide

Prev | TOC | Next


Narrative of a Rendering


It is useful, even for a relative beginner, to have in mind a narrative of the process whereby a page is built; this helps in avoiding mistakes, and provides an overall orientation when exploring or debugging.

In what follows, I refer to the thing we are rendering as "the object". I refer to user.html.prefs as prefs. I refer to websites.#data as "the data page"; and, as a shorthand, I refer to things in the data page like directives, starting with "#", even though names in the data page do not actually start with "#".

So, when I say #folder is set, I generally mean websites.#data.folder is set. When I say #tagSubstitution is consulted I generally mean that html.getPref() is called to check both the data page and prefs, but note that this isn't always done -- sometimes one or the other is consulted directly [in theory I suppose this shouldn't be the case].

Note: The discussion is schematized with nested levels of indendation. On my machine, Netscape doesn't render it properly; if you don't see the nesting, try MSIE.

Here we go. Suppose you select a page and choose Preview Page.

We call...


html.buildObject()

Various user tables and prefs are initialized if they don't exist, and the data page is cleaned out. Then we call...


html.buildPageTable()

We point #adrObject at the object.

Now we collect table-based directives, examining the table containing the object, then the table containing that, then the table containing that, and so on until we hit the top level of the database. At each level, we copy information into the data page; generally, outlines and tables are "copied" by address, other things are copied by coercing their value to a string.

If we encounter something under a name which we have already copied into the data page, we don't copy it -- thus enforcing object-orientation.

What we're looking for are: a tools table; a glossary table; and anything starting with "#". (In other words, it is permitted for the tools and glossary table to be called tools and glossary instead of #tools and #glossary, though this is now generally discouraged.) If we find a #prefs table we treat its contents as directives even if they don't start with "#".

Two directives are handled in special ways:


For #template, if it's an outline or wptext, we also make #indirectTemplate false, and if it's an outline, we copy the outline itself. Otherwise #indirectTemplate is true. Thus #template can be direct (an outline or wptext) or indirect (a name, taken to be in user.html.templates); we'll see later how this is handled.

When we come to #ftpSite, we also set #adrSiteRootTable to point to the table where we found it, and #subdirectoryPath to the pathname of the object relative to that [this is how the location of #ftpSite shows where the whole site is, within the database].

[Note that in theory #ftpsite too can be direct (a table) or indirect (a name, taken to be in user.html.sites): this is supposed to be handled later by going through html.getSiteTable() whenever we want to look inside our #ftpsite. But in fact, you should probably never use an indirect #ftpsite, since the glossPatch glossary entries will come out all wrong.]


If we discover at the end of this process that we have never defined #ftpsite, #template, #title, or #subdirectoryPath, we assign them default values (because we cannot proceed otherwise); but it is probably unwise to let this default mechanism operate.

We construct #fname (the name of the file we'll be writing to disk) based on the name of the object being rendered, by calling html.getFileName(), which adds the #fileExtension suffix and calls html.normalizeName() to adjust the name depending on #dropNonAlphas, #lowerCaseFileNames, and #maxFileNameLength.

From #fname, we construct #f, the full pathname of the file we'll be writing to disk -- since this is a preview, it's the Frontier folder, plus a Websites folder, plus a folder representing #adrSiteRootTable, plus a folder or folders representing #subdirectoryPath, plus #fname. [If this were not a preview, #ftpsite.folder would be obeyed; note that this can now be a script!]

We also construct #url, the URL for our page, based on the url value in #ftpsite, using #subdirectoryPath and #fname.


We call the firstFilter().

Next we raise a semaphore...


[Note: whenever the semaphore is raised, as happens three times during the process, html.data.adrPageTable is created, pointing at websites.["#data"]. Thus, routines called while the semaphore is up can use html.data.adrPageTable^ to access the data page.]


and "render" the object (collect its internal directives and turn it into a string) by calling...


html.tenderRender()

The object is copied into a local called lo (for "local object", no doubt). What happens next depends upon what kind of object it is.

If it's an address,


we call tenderRender() on what it points to.

If it's a table,

we construct an HTML table that schematizes it.

If it's a script,

we call the script (with no parameters) to get its result. [Since we're working with a renamed copy of the script, the script must not have an eponymous handler.]

If it's a string, a wptext, or a fileSpec,

we call html.runDirectives() on it (or its contents), to gather page-based directives...


html.runDirectives()

We examine the string line by line, looking for directives (paragraphs beginning with "#"), until the end, or (if #directivesOnlyAtBeginning) until we hit a non-directive. Each time we find one, we delete it and call html.runDirective() on it...


html.runDirective()

The "#" has already been removed, and the directive paragraph is divided into two pieces: everything before the first space (the name) and everything after it (the value). We evaluate the value and assign it into the name in the data table. [That's important to understand in writing directive values.]

Note that we are now over-writing any directives of the same name that we put into the page table earlier, i.e. in html.BuildPageTable(); this enforces object-orientation further: page-based directives have priority over table- based.

If we encounter a #template, we additionally set #indirectTemplate to true.


If it's an outline,

we call html.runOutlineDirectives() on it, to gather its directives...


html.runOutlineDirectives()

We examine the outline line by line, looking for directives (summit-level lines beginning with "#"). No attention is paid to #directivesOnlyAtBeginning, probably because we have to cycle through the whole outline regardless in order to delete summit-level comments. Each time we find a directive, we delete it and call html.runDirective() on it to get it into the page table (see above). If the directive is a #define or a #defineScript, we also obey it: we put an entry in the data page with the defined name, then grab the material subordinate to the directive (with op.outlineToList()) and stick it in as the value of that entry (with op.listToOutline()).


We then render the outline. First, we find the renderer, from #renderOutlineWith: first we try to interpret it directly as an object reference; then we look in the tools table pointed to from the data page; then we look in user.html.renderers. Next, we call the renderer; or, if no #renderOutlineWith was present, we call html.getOutlineHtml() to render the outline as a UL-type list (the "default renderer"). Now the outline has become a string with HTML formatting inserted (plus anything else like returns, tabs, etc.).


If the object to be rendered wasn't any of those types,

we just make it a string and surround it by "<PRE>" tags.

The rendered object now sits in a local called ro (for "rendered object", surely). We lower the semaphore.

We now construct #fname and #f all over again, just as we did in html.buildPageTable()! I take it that this is because the object being rendered might have contained new #fileExtension, #dropNonAlphas, #lowerCaseFileNames, or #maxFileNameLength directives. [Indeed, in theory it could have contained new #ftpSite, #subdirectoryPath, #adrSiteRootTable, and #url directives (note that #url is not recalculated here, a mistake?); the page, instead of being located meaningfully in the database, might thus explicitly specify its own location in the site.]

We call the pageFilter (with no parameters); if it likes, it can operate on #bodytext to revise our thus-far rendered object. The default pageFilter makes a dropcap, and creates a glossPatch entry for this page in the glossary pointed to by the data page, by calling html.addPageToGlossary().

We are now ready to integrate the rendered object into the template. We obtain the template, either from user.html.templates (if indirectTemplate) or from the page table. We gather its directives: either we call html.runDirectives(), or, if it's an outline, we call html.runOutlineDirectives() and convert directly to a string (a template has no outline renderer). If #tagSubstitution, we now replace "<title>" with #title, and "{bodytext}" (or "<bodytext>" or "<meat>") with the rendered object, ro.

Now we deal with macros and glossary items. Again we raise our semaphore, and call...


html.processMacros()

This calls...


string.processHtmlMacros()

This is a kernel call so it's a black box; however, one can guess what it does.

Things in angle-brackets and in quotes are protected from macro expansion unless the first angle-bracket or quote is escaped.

Things in curly braces (unless the first curly brace is escaped) are processed as macros (if #processMacros) by evaluating as a UserTalk expression; this takes place in the context of a "with" so that we look for object references first in the tools table pointed to by the data page, then in user.html.macros, then in html.data.standardmacros, then in the data table (in case it's a directive name or a name put in via #defineScript or #define). If a macro evaluation results in a quoted string, it is handed on to html.refGlossary() (see below).

Incidentally, if an error occurs during macro evaluation, there is now delightful error reporting.

I guess this is the place to describe...


the pageHeader() and pageFooter macros

A template typically starts with a {pageHeader()} call and ends with a {pageFooter()} call. This creates the surrounding HTML. If there is a #meta it is inserted literally. If #includeMetaGenerator, we add our own generator meta, and, if #includeMetaCharset, a charset meta is constructed using #charset. Custom name/content and http-equiv/content metas are inserted using the new #metaXXX and #metaEquivXXX directives. If there is a #javascript, a script tag is constructed around it.

The bodyTag() macro is called to create the body tag. It calls bgImageRef() to handle the background parameter: If there is a #background directive that isn't a URL, the background image is written to disk and the parameter is returned. The rest of the body tag is constructed using #alink, #bgcolor, #text, #link, #vlink, #topmargin, and #leftmargin. It is now permitted to supply an RGB triplet as a color value.

In the footer, the closing body and html tags are added.

Note that instead of a call to {pageheader()} it is permitted to use a #pageheader directive; see below.


Literal strings (unless the first quote is escaped) are processed as glossary entries (if #expandGlossaryItems) by calling html.refGlossary()...


html.refGlossary()

We look for the glossary entry first in the glossary table pointed to by the data page, then in any glossary or #glossary tables encountered up through the database hierarchy from the object being rendered, then in user.html.glossary.

If we find the entry in any of these places, then if it's a script we call it; if it's a table we build a #glossPatch expression out of it; otherwise we coerce it to a string, also sending it to to html.processMacros() if it starts and ends with curly braces.

Finally, as a last ditch effort, we look in the table containing the object being rendered to see if it's the name of another Web page, and if so we construct a relative reference link to it.


Now "<p>" is substituted for two return-characters if #autoParagraphs, lines starting with "***" are surrounded with "<b>" tags if #clayCompatibility, and URLs are turned into live links if #activeURLs. Finally, escaped characters are simplified: so, "\{" becomes "{", "\\" becomes "\", and so on.


If #isoFilter, we call string.iso8859encode() to handle high-ascii characters. This now takes the address of a substitution table, since we need different tables for different platforms


We unlock the semaphore. We next resolve glossPatch calls into relative links, by calling...


html.data.standardMacros.glossaryPatcher()

If #useGlossPatcher, we look in renderedText for substrings of the form "[[#glossPatch xxx|yyy]]" and make "xxx" a relative link to "yyy". [The procedure takes no advantage of #adrSiteRootTable, #subdirectoryPath, and #url, but assumes the #ftpSite table sits in the site itself to mark top of the hierarchy; this breaks if #ftpSite is indirect.]


We now handle the #pageHeader attribute if this was used instead of the {pageHeader()} macro. If it's an address, we follow it and coerce to a string; otherwise, we just coerce to a string. We lock our semaphore, send the string to html.processMacros(), and unlock our semaphore. The result is prefixed to our rendered text.

If #addLineFeeds, cr is changed to cr-lf, thus taking care of platform differences.

If #noHintsInHeader is false, or #isFatPage is true, we fatPages.buildPageAtts() to generate material to follow our rendered text. You can supply #serverNetAddress and #menubar to go among the hints, but I don't know what these are for. To make a fatpage, supply #adrPageData.

The finalFilter is called (with no parameters); if desired, it can revise renderedText in the data table.


We call html.ftpText(), which writes the rendered text out to disk at pathname #f, assigning it the creator #textFileCreator. We ask the web browser to open that file, and we're done.

Publishing a page or table is fundamentally the same as the above. The main things to know are these: #flPreview is now false, so #ftpSite.folder is used (and so View In Finder works afterwards, on Mac OS); and, when publishing a table, each entry in the table is checked with html.traversalSkip() to see if there's a reason not to publish it (such reasons are that its name starts with "#" or is "images", "glossary", or "tools").


Prev | TOC | Next

All text is by Matt Neuburg, phd, matt@tidbits.com.
For information about the book Frontier: The Definitive Guide, see my home page:
http://www.tidbits.com/matt
All text copyright Matt Neuburg, 1997 and 1998. ALL RIGHTS RESERVED.
No one else has any right to copy or reproduce in any form, including electronic. You may download this material but you may not post it for others to see or distribute it to others without explicit permission from the author.
Downloadable versions at http://www.ojai.net/matt/downloads/webTutorial.hqx and http://www.ojai.net/matt/downloads/webTutorial.zip.
Please do not confuse this tutorial with a certain other Frontier 5 tutorial based upon my earlier work.
This page created with Frontier, 2/11/2000; 7:06:53 PM.