Athena
Nevow Athena is a bi-directional AJAX framework. It's more similar to COMET in principal than Ajax though. Most of the capability comes from the ability to couple tightly with the webserver (in this case Twisted) - something which is less practical in other deployment strategies.
Enamel provides a very different approach to creating Athena pages. You still require a base Athena page onto which Fragments (representing a single portion of the page in which the javascript code will act) will be attached.
We start out with an Athena page which is used like almost any other page.
class AthenaTest(pages.Athena): elements = {'myFragment': (myFragment, 'myFragment.Stuff', './myFragment.js')} def body(self): return tags.div[self.element_myFragment]
The obvious difference is the elements variable. Many elements (or Fragments) can be defined. The reason for the naming discrepancy is that soon Enamel must support the new-style way in which Nevow does this. We declare an absolute path to the javascript code as well as a module name and module class (since we can share javascript code between fragments)
myFragment is a subclass of pages.AthenaFragment?
class myFragment(pages.AthenaFragment): kicks = ['showNode'] def element(self): return tags.div(id='aDiv', style='display: none')['Hello!']
The element function returns tags which are rendered into the Fragment. The kicks parameter declares javascript functions which are called on start - this is handy for recursive deferred loops. The variable startDelay can be used to tune the delay of when the functions will be called after the page is rendered.
We can also define Python methods to be called by the JavaScript? code. These must be exposed with the exposeAthena decorator.
class myFragment(pages.AthenaFragment): kicks = ['showNode'] def element(self): return tags.div(id='aDiv', style='display: none')['Hello!'] @pages.exposeAthena def myFunc(self): return u"rawer"
Note that strings must be returned as Unicode.
The JavaScript? behind all of this looks like this
// import Nevow.Athena myFragment.Stuff = Nevow.Athena.Widget.subclass('myFragment.Stuff'); myFragment.Stuff.methods( function showNode(self) { var aDiv = document.getElementById('aDiv'); aDiv.style.display = 'block'; myCallback = function(result){ aDiv.innerHTML = result; } self.callRemote('myFunc').addCallback(myCallback); } );
The comment which imports Nevow.Athena is important!
For more on Athena go here: http://divmod.org/trac/wiki/DivmodNevow/Athena
Using templates with Athena
It's probably convenient with larger systems to use templates for Athena fragments. Enamel Athena page classes and fragment classes respect the same directives as normal pages for templates with a few architectural differences.
Consider the following page
class Page(pages.Athena): elements = {'partEditor': (partEditor, 'partEditor.E', os.path.join(self.enamel.js_path, 'partEditor.js'))} def document(self): return pages.template('parted.xml', templateDir = self.enamel.templates_path)
A render method is already created for our partEditor fragment which we can call in our template. We also need to include the liveglue header.
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" xmlns:n="http://nevow.com/ns/nevow/0.1">
<head>
<n:invisible n:render="liveglue"/>
</head>
<body>
<div id="pageContent">
<div n:render="partEditor"/>
</div>
</body>
</html>
Our fragment class follows simply as
class partEditor(pages.AthenaFragment): def document(self): return pages.template('parted_fragment.xml', templateDir = '/home/installer/templates')
Where parted_fragment.xml follows the skeleton of
<?xml version="1.0" encoding="iso-8859-1" ?> <div xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" xmlns:n="http://nevow.com/ns/nevow/0.1" n:render='liveFragment'> <div id="somestuff"/> </div>
