UserLand Software
Scripting Links

Frontier Web Tutorial

About This Tutorial

About Web Site Management

Why Frontier?

Starting Up

Getting Comfortable With Tables

Exploring the Examples

Your First Web Site

Frontier HTML Basics, and the Directive Hierarchy

Other Automatic HTML

Getting Comfortable With Outlines

Templates

Outline Formatting

Includes and Macros

Handling Images

Glossaries and Filters

Defines and Custom Directives

Releasing

Site Outline and NextPrev

Relative References

Leveraging Your Work

Narrative of a Rendering

Where To Go From Here


Narrative of a Rendering


For advanced work it is useful to know the narrative of the process whereby a page is built; this averts mistakes, and orients you when exploring or debugging. There are various such narratives available; no criticism of these is intended! These are just the particular details I've found it useful to have on hand.

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 html.data.page 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 html.data.page.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 the data page 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 View In Browser.

If prefs.threadedRendering, semaphores are used; this doesn't cause rendering to be threaded, but rather keeps it safe from threading -- if a second thread might do rendering, it should wait until active rendering is done.

The data page is cleaned out; #f is defined as the empty string [I don't understand why] and #flPreview made true to show we're previewing. We call...


html.buildObject()

Various user tables and prefs are initialized if they don't exist. 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 value. If we've already copied something with a certain name, though, we don't copy it again -- thus enforcing object-orientation.

What we're looking for are: a tools table; a glossary table; and anything starting 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 #ftpsite too can be direct (a table) or indirect (a name, taken to be in user.html.sites): this is handled later by going through html.getSiteTable() whenever we want to look inside our #ftpsite.


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 (that's what #flpreview was for), it's the Frontier folder, plus a Websites folder, plus a folder representing #adrSiteRootTable, plus a folder or folders representing #subdirectoryPath, plus #fname.

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


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


html.data.standardMacros.renderObject()

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 renderObject() 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). From these we construct a statement of the following form:

  dataPageEntry = theValue

and evaluate that statement in order to perform the assignment. [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 toys.outlineToList()) and stick it in as the value of that entry (with toys.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.ucmds.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 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 html.data.page.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. [It uses the location of the #filters table to mark the top of the hierarchy -- not the #ftpSite table or #adrSiteRootTable.]

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 "<meat>") with the rendered object, ro.

Now we deal with macros and glossary items. We 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 html.data.page (in case it's a directive name or a name put in via #defineScript or #define).


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; we also add our own name and content meta, and, if prefs.includeMetaCharset, a charset meta is constructed using #charset. If there is a #javascript, a script tag is constructed. The body tag is constructed using #background, #alink, #bgcolor, #text, #link, #vlink, #topmargin, and #leftmargin. In the footer, if #noHintsInHeader is false, suites.fatPages.buildPageAtts() is called to construct the hint comment; this includes the netAddress based on #serverNetAddress, the menubar based on #menubar, and the fat page data based on #adrPageData.


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 tables encountered up through the database hierarchy from the object being rendered, then in user.html.glossary. 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 [this last bit of code is repeated identically twice, probably a mistake].


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, toys.isoFilter() is called to perform character translation. This calls string.iso8859encode(). An internal substitution table, identical to the one at html.utilities.iso8859.table, is used unless there is a table at user.html.prefs.iso8859map.


The finalFilter is called (with no parameters); if desired, it can revise html.data.page.renderedText. [The default finalFilter contains a call to html.data.standardMacros.glossaryPatcher(), probably a mistake as we are about to call it anyway.]

We 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 take 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. Also, note the inconsistency: automatic glossPatch glossary entries were created relative to the location of the #filters table, but are resolved relative to the location of the #ftpSite table.]


We construct #fname for a third time (!!!) and 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.

Releasing a page and releasing a table are fundamentally similar to 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); and, when releasing a table, each entry in the table is checked with html.traversalSkip() to see if there's a reason not to release it (such reasons are that its name starts with "#" or is "images", "glossary", or "tools").

[ Previous | Next ]


This page was built on a Macintosh running Frontier. Last modified 7/8/97; 1:28:24 PM. © Copyright 1997, Userland Software, Inc. Written by Matt Neuburg, matt@tidbits.com.