Friday, June 23, 2006


PJS (pronounced PeeJays) is the Python equivlent to RJS done in Rails. Its still a little wet behind the ears but is already quite useful.

The intention is to have a language native way to get at the Javascript code your client is running. In this case the language is Python and we expose the underlying Prototype primitives, but it would be possible to re-target this whole deal for something like Dojo or MochiKit.

So what does it do? How do you use it? Pretty simple really. Now just to disclaim again, not everything you might want to do works but enough does that I am still going to show this. All the tests and runtime are pure Python with no Zope dependencies, but in the context of these examples I use a handy FSJavascript object which means we have a file in your Plone skin path with a .pjs extension that will render itself as text/javascript. There is an implicit page object available to FSJavascript objects, but if you were doing this by hand if would just be:

page = pjs.JavascriptPage()
print page

Ok, now for a real example, or what goes in the ...

There are a number of very standard things we want to do from PJS.

Access an element of the page by its id.


Refer to a Javascript variable directly


and then call methods on those and pass them around.


which might highlight the news portlet for one second when triggered.

We can also do slightly more complex things. For example suppose we have a Tree widget in Javascript and we want to add elements to it that result from doing a query. An AJAX callback to the server might trigger a .pjs file that looks
as follows.

catalog = context.portal_catalog
ob = catalog(UID=of)[0].getObject()

files = page.files
for title, data in ob.treeFolderContents():
files.addItem(title, data)

Looks just like a regular Python Script in Zope. Parameters tells Zope what to demand from the caller (in this case the AJAX request) and then runs as normal.
This produces the proper Javascript which is returned to the client. All in all its pretty simple. You'd have to understand that files is a Tree object and has a addItem methodand you didn't have to worry about marshalling arguments, or escaping code in some strange cross language way (if you don't know what I mean count your blessings).

There are unit and doc tests for this stuff and its still evolving. Its fun to play with, give it a try.

This is checked into Bling which can still be had from


whit said...

uh...looks cool. link?

Anonymous said...

"D:\web\zoperoot286\Products\Bling\", line 42, in initialize
import pjs
File "D:\web\zoperoot286\Products\Bling\", line 47
json.write(v)) for k,v in dict.iteritems()))
SyntaxError: invalid syntax
getting the above error when I start Zope

Benjamin Saller said...

line 47
json.write(v)) for k,v in dict.iteritems()))
SyntaxError: invalid syntax

Requires Python2.4+

Lukasz said...

It looks very promising, but I can't tell if it works. I'm very interested in this product. But I have some problems.

First of all its not a Zope product but Plone and it requires one of the newest versions. I was able to run it under Plone 2.1.3, nothing older.

Examples in portal_skins doesn't work or maybe I'm not understanding something. I've found 4 examples:
- test_bling - error does not appear when I type 'this'. The form is not passed to validator script
- test_bling_ed - when I click on a line it changes to textarea but where is 'Save' button. Also oryginal text is still visible
- test_bling_timer - nothing happens, I see only javascript code on the page
- test_tabpanel - This should do something more than showing 5 tabs I suppose

Also your skins files breaking my Plone - KeyError: title.
URL: file:Bling/skins/bling/
Line 25, Column 16
Expression: standard:'action/title'

You've mentioned something about Python 2.5 (, lines 35-40). Please, don't go there and if it possible make your product compatible with Python 2.3. As far as I noticed the problem is only in line 47, that was mentioned in comment above. I changed it, and hope that this is the same:

result = ""
for k,v in dict.iteritems():
..result += "%s:%s,"%(json.write(k), ....json.write(v))
return "{%s}"%result

I was testing on MacOS X Tiger: Firefox (on Safari nothing worked) and Windows Firefox 1.0.7 and IE 6