Monday, November 08, 2010 at 12:05 AM.
system.verbs.builtins.radio.html.drawNavigatorLinks
on drawNavigatorLinks (renderedFileExtension="html") {
<<Changes
<<8/14/03; 5:33:30 PM by JES
<<Fix a bug where links which include an anchor would in some cases be rendered without the anchor part.
<<10/24/02; 11:48:03 PM by JES
<<Wrap non-linked items (items for the current page) in a span with a CSS class of navigatorLinkCurrent.
<<6/30/02; 3:43:07 PM by JES
<<Added CSS classes to links: class="navigatorLink".
<<4/11/02; 3:05:28 AM by JES
<<Process macros before returning the navigator HTML -- the navigator is inserted into the page after normal macro processing, so macros need to be processed separately here.
<<2/13/02; 1:39:00 PM by JES
<<Get the item and separator format using a call to radio.weblog.getThemePref.
<<1/24/02; 8:00:04 PM by JES
<<If pta^.renderedFileExtension is defined, use it instead of the value of the archiveFileExtension parameter.
<<1/19/02; 8:12:08 PM by JES
<<Rewrote the code that generates links in static pages, to fix two bugs, one where the links would be to an index file, even though you specified a folder path, and another where the links would have a .txt extension even though the file in the cloud has a .html extension. Added an optional parameter, renderedFileExtension, which defaults to .html.
<<1/2/02; 12:25:42 AM by JES
<<Decode entities in the name attribute, to allow for entity-encoded accented characters.
<<12/12/01; 2:37:21 PM by JES
<<When linking to a folder, don't cause an error if the user forgot the trailing slash character.
<<11/28/01; 6:26:13 PM by DW
<<Instead of getting filetext from the cache, read it from the file.
<<Instead of using outlineAsString, coerce the outline to a string.
<<11/26/01; 12:27:50 PM by JES
<<If an error occurs, return a macro error. Errors in this script were causing the desktop website home page to break.
<<11/19/01; 12:24:07 AM by JES
<<Created. Renders the links from a navigatorLinks.xml files. Implements a smart cache in temp.radio.navigatorLinksCache.
local (htmltext);
try { //if an error occurs, return a macro error
local (pta = html.getPageTableAddress ());
if defined (pta^.renderedFileExtension) {
renderedFileExtension = pta^.renderedFileExtension};
if not (renderedFileExtension beginsWith ".") {
renderedFileExtension = "." + renderedFileExtension};
local (pc = file.getPathChar ());
if not defined (pta^.radioResponder.atts.navigatorLinks) { //return the empty string
return ("")};
local (indentlevel = 0);
on add (s) {
htmltext = htmltext + s};
local (f = pta^.radioResponder.atts.navigatorLinks);
local (fmodified = file.modified (f));
local (adrblog = radio.weblog.init ());
local (itemFormat = radio.weblog.getThemePref ("navigator.itemFormat", adrblog));
local (separatorFormat = radio.weblog.getThemePref ("navigator.separatorFormat", adrblog));
local (adrcache);
bundle { //set adrcache, possibly return the cached HTML
local (adrcachetable = @system.temp.radio.navigatorLinksCache);
if not defined (adrcachetable^) {
new (tableType, adrcachetable)};
if pta^.radioResponder.flStaticRendering {
adrcachetable = @system.temp.radio.navigatorLinksCache.static}
else {
adrcachetable = @system.temp.radio.navigatorLinksCache.dynamic};
if not defined (adrcachetable^) {
new (tableType, adrcachetable)};
local (cachename = pta^.radioResponder.fileBeingRendered);
adrcache = @adrcachetable^.[cachename];
if defined (adrcache^) { //return the cached HTML if possible
if adrcache^.fileLastModified == fmodified { //return the cached text
if (adrcache^.itemFormat == itemFormat) and (adrcache^.separatorFormat == separatorFormat) {
return (adrcache^.renderedText)}}}};
local (adrfile);
radio.file.getFileAttributes (f, @adrfile);
local (mimetype = adrfile^.mimeType);
case mimetype {
"text/xml" { //parse the xml and use it to generate the links
local (xtable);
xml.compile (file.readwholefile (f), @xtable);
local (adrnav = xml.getAddress (@xtable, "navigator"));
local (adritem, ctitems = 0);
for adritem in adrnav { //loop over all the <item>s in <navigator>
if xml.convertToDisplayName (nameOf (adritem^)) == "item" { //ignore anything that's not an <item>
local (linktext);
local (name = xml.getAttributeValue (adritem, "name"));
name = radio.string.decodeEntities (name, false);
local (pagename = "");
try {pagename = xml.getAttributeValue (adritem, "pagename")};
local (anchor = "");
if pagename contains "#" {
anchor = "#" + string.nthField (pagename, "#", 2);
pagename = string.nthField (pagename, "#", 1)};
if pagename contains ":" { //absolute link
<<linktext = html.getLink (name, pagename)
linktext = "<a href=\"" + pagename + anchor + "\" class=\"navigatorLink\">" + name + "</a>"}
else { //relative link
if pagename == "" {
linktext = name}
else {
if pta^.radioResponder.flStaticRendering { //link to the pages in the cloud
bundle { //new code
local (flerror = false, flpopindexfile = false);
on locateIndexFile (folderPath, adrfindex) {
local (adr, findex);
for adr in @user.radio.prefs.indexFileNames {
findex = filebeinglinked + adr^;
if file.exists (findex) {
adrfindex^ = findex;
return (true)}};
return (false)};
local (filebeinglinked = radio.file.getAbsolutePath (pagename));
if (pagename endsWith "/") { //try to locate the index file
if locateIndexFile (filebeinglinked, @filebeinglinked) {
flpopindexfile = true}}
else { //locate the file, ignoring its extension
if not radio.file.locateFileIgnoringExtension (radio.file.getAbsolutePath (pagename), @filebeinglinked) {
if file.exists (filebeinglinked) { //check to see if this is a folder
if file.isFolder (filebeinglinked) {
if not (filebeinglinked endsWith pc) {
pagename = pagename + "/";
filebeinglinked = filebeinglinked + pc;
if locateIndexFile (filebeinglinked, @filebeingrendered) {
flpopindexfile = true}}}}
else { //error
flerror = true}}};
if not flerror {
if string.lower (filebeinglinked) == string.lower (pta^.radioResponder.fileBeingRendered) { //bold, not a link
<<linktext = "<b>" + name + "</b>"
linktext = "<b><span class=\"navigatorLinkCurrent\">" + name + "</span></b>"}
else { //link to it
local (url);
url = radio.upstream.getFileUrl (fileBeingLinked);
if flpopindexfile { //link to a folder -- strip off the "index.html"
url = string.popSuffix (url, "/") + "/"}
else { //link to a file, not to a folder -- possibly patch the file extension
local (lowerurl = string.lower (url));
if (lowerurl endsWith ".txt") or (lowerurl endsWith ".opml") {
local (flrendered = true);
if (lowerurl endswith "/directory.opml") {
if file.exists (file.folderFromPath (filebeinglinked) + radio.data.fileNames.upstreamFileName) {
flrendered = false}}
else { //gather attributes, to see if the file is rendered or not
local (atts);
radio.webserver.gatherAttributes (filebeinglinked, @atts, @atts);
if defined (atts.flRender) {
flrendered = atts.flRender}};
if flRendered {
url = string.popSuffix (url) + renderedFileExtension}}};
<<linktext = html.getLink (name, url + anchor)
linktext = "<a href=\"" + url + anchor + "\" class=\"navigatorLink\">" + name + "</a>"}};
if flerror { //put an error in place of the link so the webmaster can fix the problem
linktext = "<b>Error: Can't find file, \"" + pagename + "\".</b>"}}}
<<bundle //original code
<<local (flerror = false, flfolder = false)
<<local (filebeinglinked = radio.file.getAbsolutePath (pagename))
<<if (pagename endsWith "/") //try to locate the index file
<<local (adr, findex)
<<for adr in @user.radio.prefs.indexFileNames
<<findex = filebeinglinked + adr^
<<if file.exists (findex)
<<filebeinglinked = findex
<<break
<<else //locate the file, ignoring its extension
<<if not radio.file.locateFileIgnoringExtension (radio.file.getAbsolutePath (pagename), @filebeinglinked)
<<if file.exists (filebeinglinked) //check to see if this is a folder
<<if file.isFolder (filebeinglinked)
<<if not (filebeinglinked endsWith pc)
<<pagename = pagename + "/"
<<filebeinglinked = filebeinglinked + pc
<<flfolder = true
<<else //error
<<flerror = true
<<if not flerror
<<if string.lower (filebeinglinked) == string.lower (pta^.radioResponder.fileBeingRendered) //bold, not a link
<<linktext = "<b>" + name + "</b>"
<<else //link to it
<<local (url)
<<url = radio.upstream.getFileUrl (fileBeingLinked)
<<linktext = html.getLink (name, url + anchor)
<<if flerror //put an error in place of the link so the webmaster can fix the problem
<<linktext = "<b>Error: Can't find file, \"" + pagename + "\"."
else { //link to the local dynamic pages
if not (pagename beginsWith "/") { //always link from the top of the site
pagename = "/" + pagename};
if string.lower (pta^.uri) == string.lower (pagename) {
linktext = "<b><span class=\"navigatorLinkCurrent\">" + name + "</span></b>"}
else { //link to it
<<linktext = html.getLink (name, pagename + anchor)
linktext = "<a href=\"" + pagename + anchor + "\" class=\"navigatorLink\">" + name + "</a>"}}}};
add (string.replace (itemFormat, "<%item%>", linktext, false) + separatorFormat);
ctitems++}};
if ctitems > 0 {
htmltext = string.mid (htmltext, 1, sizeOf (htmltext) - sizeOf (separatorFormat))}};
"text/opml" {
htmltext = string (adrfile^.outline)}}
else { //include the file as is
htmltext = string (file.readwholefile (f))};
bundle { //update the cache
if not defined (adrcache^) {
new (tableType, adrcache)};
adrcache^.itemFormat = itemFormat;
adrcache^.separatorFormat = separatorFormat;
adrcache^.renderedText = htmltext;
adrcache^.fileLastModified = fmodified};
htmltext = html.processMacros (htmltext)}
else { //bad XML -- macro error
htmltext = "[Macro error: Can't render navigator links because an error occurred: \"" + string.replaceAll (tryError, "<", "<") + "\".]"};
return (htmltext)}
<<bundle //debugging
<<new (tableType, @temp.radio.navigatorLinksCache)
<<local (pagetable = workspace.pt)
<<html.setPageTableAddress (@pagetable)
<<wp.newTextObject (drawNavigatorLinks (), @scratchpad.navLinks); edit (@scratchpad.navLinks)
<<html.deletePageTableAddress ()
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.