Here's the interface to this class:
#================================================================
# Functions and classes
#----------------------------------------------------------------
# - - - - - c l a s s T C C P a g e - - - - -
class TCCPage:
"""Represents one page in the TCC style.
Exports:
TCCPage ( title=None, navList=None, author=None, url=None ):
[ (title is the page's title as a string, default empty) and
(navList describes the page's standard set of
navigational links as NavLink objects, default empty) and
(author is the page's author credit string, default empty) and
(url is the page's URL as a string, default empty) ->
return a new, empty TCCPage object representing
those values ]
.headTitle:
[ self's head's title as a DOM title element,
initially the title argument, read/write ]
.bodyTitle:
[ self's body title as a DOM h1 element, initially the
title argument, read/write ]
.content:
[ a DOM div element in self's body between the top
and bottom nav links ]
.address:
[ self's address as a DOM address element, initially
the author argument, read/write ]
.url: [ as passed to constructor, read-only ]
.write ( outFile=None ):
[ outFile is a writeable file, defaulting to sys.stdout ->
outFile +:= an XHTML representation of self ]
State/Invariants:
.doc: [ self's Document object ]
.head: [ self's head element ]
.body: [ self's body element ]
"""
The navList argument describes a
sequence of standard navigational links that appear on the
bottom of each page, and optionally on the top as well.
This argument is a list of zero or more NavLink objects, each of which describes
one navigational link and the place or places where that
link goes. See Section 7, “class NavLink: Describes
one navigational feature”.
The constructor for this class creates the XHTML page as a
DOM Document tree, fills in the top
and bottom content, and creates self.content as an empty div element.
# - - - T C C P a g e . _ _ i n i t _ _ - - -
def __init__ ( self, title=None, navList=None, author=None,
url=None ):
"""Constructor for a TCCPage."""
We divide the construction of the page into four parts:
creation of a Doctype object for
XHTML; creation of the Document
object; creation of its head
element; and creation of its body
element.
#-- 1 --
self.title = title or ""
self.navList = navList or []
self.author = author or ""
self.url = url or ""
#-- 2 --
# [ doctype := a new DocumentType object representing
# the XHTML 1.0 document type ]
doctype = xc.DocumentType ( "html",
"-//W3C//DTD XHTML 1.0 Strict//EN",
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" )
#-- 3 --
# [ self.doc := a new Document of type doctype ]
self.doc = xc.Document ( "html", doctype )
#-- 4 --
# [ self.doc := self.doc with a new head element added
# containing self's head content
# self.head := that element ]
self.__createHead()
#-- 5 --
# [ self.doc := self.doc with a new body element added
# containing self's body content
# self.body := that element
# self.bodyTitle := the h1 element containing self.title
# self.content := a div element for the body content
# self.address := the address element in the colophon ]
self.__createBody()
This method sets up the page's head
element and all its content.
# - - - T C C P a g e . _ _ c r e a t e H e a d - - -
def __createHead ( self ):
"""Set up the page head element.
[ self.doc := self.doc with a new head element added
containing self's head content
self.head := that element ]
"""
First we create the actual head
element.
#-- 1 --
# [ self.doc := self.doc with a new head element added
# self.head := that element ]
self.head = xc.Element ( self.doc.root, "head" )
Next we set up the title element, if any, and set attribute
self.headTitle to point to it.
Unless the value of self.title is an
empty string, we then add the title text.
#-- 2 --
# [ self.head := self.head with a new title element
# added containing self.title (if nonempty)
# self.headTitle := that element ]
self.headTitle = xc.Element ( self.head, "title" )
if self.title:
xc.Text ( self.headTitle, self.title )
The stylesheet link is pro forma, and links to the URL given
by CSS_URL.
#-- 3 --
# [ self.head := self.head with a stylesheet link added ]
xc.Element ( self.head, "link", rel="stylesheet",
href=CSS_URL )
This method sets up the overall page body structure.
# - - - T C C P a g e . _ _ c r e a t e B o d y - - -
def __createBody ( self ):
"""Set up the page body.
[ self.doc := self.doc with a new body element added
containing self's body content
self.body := that element
self.bodyTitle := the h1 element containing self.title
self.content := a div element for the body content
self.address := the address element in the colophon ]
"""
The principal divisions of the body content, in order, are:
Top navigational bar.
Title block with TCC logo.
The div element we export as
attribute self.content. Here,
the caller adds the actual page content.
Horizontal rule and bottom navigational links.
Another horizontal rule and the colophon section.
#-- 1 --
# [ self.doc := self.doc with a new body element added
# self.body := that element ]
self.body = xc.Element ( self.doc.root, "body" )
#-- 2 --
# [ self.body := self.body with the top nav bar added ]
self.__topNav()
#-- 3 --
# [ self.body := self.body with a title block added
# self.bodyTitle := the h1 within that title block ]
self.__titleBlock()
#-- 4 --
# [ self.body := self.body with a new div element added
# self.content := that div element ]
self.content = xc.Element ( self.body, "div" )
#-- 5 --
# [ self.body := self.body with bottom nav links added ]
self.__botNav()
#-- 6 --
# [ self.body := self.body with the colophon added
# self.address := the address element within that
# colophon ]
self.__colophon()
The top navigational bar consists of a series of strings
separated by " / ". The content
comes from the elements of the self.navList argument, each of which is a
NavLink object. Each string may be a
link or not, depending on whether the corresponding object's
.destList has any members or not.
# - - - T C C P a g e . _ _ t o p N a v - - -
def __topNav ( self ):
"""Build the row of navigational links across the page top.
[ (self.navList as invariant) and
(self.body as invariant) ->
self.body := self.body with the top nav bar added ]
"""
First we create a div element to
hold the pieces of the nav bar.
#-- 1 --
# [ self.body := self.body with a new div element added
# navBar := that element ]
navBar = xc.Element ( self.body, "div" )
navBar["class"] = "top-nav"
Now all that remains is to add one link for each element of
self.navList. The only tricky part
is getting the TOP_NAV_SEP separator
between elements, but not initially or finally. I am
indebted to Dr. Allan M. Stavely for the best (and easiest
to verify) way to do this. We set a variable called
prefix to the empty string
initially. Each time through the loop, we add a copy of
prefix, then we add the new
content, and then we set prefix to
TOP_NAV_SEP.
#-- 2 --
prefix = ""
#-- 3 --
# [ navBar +:= (prefix) + (navBar with top elements of
# self.navList added, separated by TOP_NAV_SEP
# prefix := TOP_NAV_SEP ]
for navItem in self.navList:
#-- 3 body --
# [ navItem is a NavLink object ->
# if navItem.noTop ->
# I
# else ->
# navBar +:= (prefix) + (navItem as a top nav link) ]
if not navItem.noTop:
#-- 3.1 --
# [ if prefix is "" ->
# I
# else ->
# navBar +:= prefix ]
if prefix:
xc.Text ( navBar, prefix )
#-- 3.2 --
# [ navBar +:= navItem as a top nav link ]
self.__topNavItem ( navBar, navItem )
#-- 3.3 --
prefix = TOP_NAV_SEP
This method translates one NavLink
object into a page-top navigational link. If the given
navItem.destList has a URL in it,
the link has link text navItem.shortName and points at that URL.
(This is intended only for links such as
“Next” that have a single URL. If there are
multiple URLs, we use the first one.) If there are no URLs,
the navItem.shortName attribute is
inserted as plain text (not a link).
# - - - T C C P a g e . _ _ t o p N a v I t e m - - -
def __topNavItem ( self, navBar, navItem ):
"""Add one item to the top navigational bar.
[ (navBar is an xc.Element) and
(navItem is a NavLink) ->
navBar +:= navItem as a top nav link ]
"""
The test “if navItem.destList:” succeeds if that list is nonempty. It fails if
navItem.destList is either
None or an empty list.
#-- 1 --
if navItem.destList:
#-- 1.1 --
# [ navBar +:= a link to navItem.destList[0] with text
# navItem.shortName ]
title, url = navItem.destList[0]
a = xc.Element ( navBar, "a", href=url )
xc.Text ( a, navItem.shortName )
else:
# [ navBar +:= navItem.shortName as text ]
xc.Text ( navBar, navItem.shortName )
The purpose of this method is to output the small table (one
row, two columns) that positions the main page title
h1 element to the left of the TCC
logo graphic.
Here's the XHTML we're generating:
<table width="100%%">
<tr valign="top">
<td align="left"><h1>title text</h1>
<td align="right">
<a href="http://www.nmt.edu/"><img src="/tcc/images/logo.png"
alt="Tech Computer Center logo"></a>
</tr>
</table>
Here's the code.
# - - - T C C P a g e . _ _ t i t l e B l o c k - - -
def __titleBlock ( self ):
"""Set up the main page title block.
[ self.title as invariant ->
self.body := self.body with a title block added
self.bodyTitle := the h1 within that title block ]
"""
#-- 1 --
# [ self.body := self.body with a new table element added
# table := that table element ]
table = xc.Element ( self.body, "table", width="100%%" )
#-- 2 --
# [ table := table with a new tr element added
# row := that tr element ]
row = xc.Element ( table, "tr", valign="top" )
#-- 3 --
# [ row := row with a new cell added containing self.title
# inside an h1 element
# self.bodyTitle := that h1 element ]
td = xc.Element ( row, "td", align="left" )
self.bodyTitle = xc.Element ( td, "h1" )
if self.title:
xc.Text ( self.bodyTitle, self.title )
#-- 4 --
# [ row := row with a new cell added containing the
# TCC logo as a link to TCC_MAIN_URL ]
td = xc.Element ( row, "td", align="right" )
a = xc.Element ( td, "a", href=TCC_MAIN_URL )
img = xc.Element ( a, "img", alt="Tech Computer Center logo",
src=TCC_LOGO_URL )
This method outputs the hr element
below the page body, followed by page-bottom navigational
links made from self.navList.
# - - - T C C P a g e . _ _ b o t N a v - - -
def __botNav ( self ):
"""Add the page-bottom navigational links section.
[ (self.body as invariant) and (self.navList as invariant) ->
self.body := self.body with bottom nav links added
from self.navList ]
"""
First we add the separator rule.
#-- 1 --
# [ self.body +:= an hr element ]
xc.Element ( self.body, "hr" )
Then we add the links from self.navList.
#-- 2 --
# [ self.body +:= bottom nav links made from self.navList ]
for navItem in self.navList:
#-- 2 body --
# [ navItem is a NavLink ->
# self.body +:= a bottom nav link made from navItem ]
self.__botNavItem ( navItem )
For each NavItem object in
self.navList, there are three
cases:
If the NavItem object's
.destList is empty, no content
is generated.
For some links, such as “Site map”, the short name is the same as the title. It would be silly to generate the text “Site map: Site map” with the second repetition being the link.
In that case, the .destList
attribute has only one (title, URL) tuple, and the title
is empty. We generate a link containing the boldfaced
.shortName attribute, pointing
at the given URL.
In the general case, we start by generating the
boldfaced .shortName attribute,
but not as a link. This is followed by all links to
each element of .destList,
using the title of the (title, URL) tuple as the link
text and the URL as the destination.
# - - - T C C P a g e . _ _ b o t N a v I t e m - - -
def __botNavItem ( self, navItem ):
"""Generate one page-bottom navigational link.
[ navItem is a NavLink object ->
self.body +:= a bottom nav link made from navItem ]
"""
First we eliminate the case where nothing is generated.
#-- 1 --
# [ if navItem.destList is empty or None ->
# return
# else ->
# self.body +:= a new, empty div element
# div := that div element ]
if not navItem.destList:
return
else:
div = xc.Element ( self.body, "div" )
Next we check for the special case where the short name is the same as the title.
#-- 2 --
# [ if navItem.destList[0] has an empty title ->
# div +:= a link to the url of navItem.destList[0]
# whose link text is navItem.shortName
# return
# else -> I ]
title, url = navItem.destList[0]
if not title:
#-- 2.1 --
# [ self.body +:= a link to the url of
# navItem.destList[0] whose link text is
# navItem.shortName
# return ]
a = xc.Element ( div, "a", href=url )
b = xc.Element ( a, "b" )
xc.Text ( b, navItem.shortName )
return
In the general case, we first generate the boldfaced short
name, then add all the elements of navItem.destList separated by BOT_NAV_SEP.
#-- 3 --
# [ div +:= (navItem.shortName, boldfaced) + ": "
# prefix := "" ]
b = xc.Element ( div, "b" )
xc.Text ( b, navItem.shortName )
xc.Text ( b, ": " )
prefix = ""
#-- 4 --
# [ div +:= (prefix) + (elements of navItem.destList,
# made into links, separated by BOT_NAV_SEP)
# prefix := BOT_NAV_SEP ]
for title, url in navItem.destList:
#-- 4 body --
# [ div +:= (prefix) + (a link to url with link
# text=title)
# prefix := BOT_NAV_SEP ]
if prefix:
xc.Text ( div, prefix )
a = xc.Element ( div, "a", href=url )
xc.Text ( a, title )
prefix = BOT_NAV_SEP
The last part of the page to be generated contains a
horizontal rule, the address
element, the time of last update, and the URL (if known).
# - - - T C C P a g e . _ _ c o l o p h o n - - -
def __colophon ( self ):
"""Add the colophon section to the page.
[ (self.author as invariant) and (self.url as invariant) ->
self.body := self.body with the colophon added
self.address := the address element within that
colophon ]
"""
First, the horizontal rule:
#-- 1 --
# [ self.body +:= an hr element ]
xc.Element ( self.body, "hr" )
Next, the address element. We
insert self.author if it is
nonempty.
#-- 2 --
# [ self.body +:= a new address element containing
# self.author
# self.address := that element ]
self.address = xc.Element ( self.body, "address" )
if self.author:
xc.Text ( self.address, self.author )
We package the timestamp in a div element to make it small, since
this is not content most people care about.
#-- 3 --
# [ self.body +:= current time ]
div = xc.Element ( self.body, "div" )
xc.Text ( div, "Last updated: %s" %
time.strftime ( "%Y-%m-%d %H:%M %Z" ) )
Finally, we add another small heading with the URL, if it is known.
#-- 4 --
# [ if self.url ->
# self.body +:= self.url ]
if self.url:
div = xc.Element ( self.body, "div" )
xc.Text ( div, "URL: " )
tt = xc.Element ( div, "tt" )
xc.Text ( tt, self.url )