AJAX
2007-05-08
Software is lame
I may sound like a fanboy or something, but I'm starting to think that it's impossible to have a perfect piece of software, especially with larger systems. Two things that happened to me lately that made me think this:
- How lame is that Windows doesn't automount USB sticks when they're present at boot time? Why would I have to remove+reinsert the stick just so Windows will see the bloody thing? My Kubuntu, of course, handles perfectly everything in this department (at least in my case).
- How lame is that Prototype handles in a really stupid way forms that have multiple submit buttons and are submitted through Ajax? Not even Form.request() will do the proper thing, which is to only leave one of the submit inputs in the stream, the one that has been clicked on. The problem is with zope.formlib, which gets the action and the validation from the submit button that was pressed. More then one submit input in the request and things become uncontrollable.
To fix this I have added the following onsubmit handler to my Javascript code:
my_form.onsubmit = function(event){
button = document.activeElement || event.explicitOriginalTarget; // IE, Mozilla, Opera
this.getInputs('submit').each(function(el){
if (el.name != button.name) {
el.disable();
}
}
}This disables all the submit input controls before serializing the form and doing an ajax request with Form.request(), as disabled inputs are ignored by serialize().
UPDATE: I am told that Prototype 1.6 will support multiple submit buttons.
2007-02-28
A Zope 3 AJAX viewlet manager
I've finally got tired of writing small fragments of JavaScript code and views to be able to make different areas updatable through ajax for my current Zope 3 application. Plus, when I've started writing this application, I've started doing the layout with viewlets and managers, and it seems that for each viewlet that I'm creating I have to have a way to get its content through AJAX, which means having a page available for it, and this is very odd, as it almost defeats the point of having viewlets.
So I've decided to create the "Ajax viewlet manager". I'm not sure how good of an idea this is, I'll have to give it some thought and see how I feel about it in the future. Let me describe my solution.
First, I want to be able to have the manager available at a certain URL, to be able to reload this URL through an Ajax call later on. So I've created a new namespace traverser, ++vmanager++. A viewlet manager would be accessible (as rendered HTML) at a location such as http://localhost:8080/mysite/myobject/++vmanager++ITop. This is the traverser code:
from zope.publisher.interfaces.browser import IBrowserView
from zope.traversing.interfaces import TraversalError
from zope import component
from zope.interface import implements
from zope.traversing.namespace import SimpleHandler
from zope.viewlet.interfaces import IViewletManager
class vmanager(SimpleHandler):
implements(IBrowserView)
def __init__(self, context, request):
self.context = context
self.request = request
def traverse(self, name, ignored):
manager = component.queryMultiAdapter((self.context, self.request, self), IViewletManager, name=name)
if manager:
return manager
else:
raise TraversalError(self.context, name)
The traverser is registered like this:
<adapter
name="vmanager" for="*"
provides="zope.traversing.interfaces.ITraversable"
factory=".namespace.vmanager"
/>
<view
name="vmanager" for="*"
type="zope.interface.Interface"
provides="zope.traversing.interfaces.ITraversable"
factory=".namespace.vmanager"
/>
The traverse() in the vmanager class will return the manager. One thing to note is that once the manager returned, Zope will try to get the default view for this object, so a default page (index.html) is needed for IViewletManager
class ViewViewletManager(object):
"""View a rendered viewlet manager"""
def __call__(self):
manager = self.context
manager.update()
return manager.render()
This page is registered as
<browser:page
name="index.html"
class=".browser.ViewViewletManager"
for="zope.viewlet.interfaces.IViewletManager"
permission="zope.View"
/>
Next, to automate things a bit more, I wrote a template, along these lines:
<tal:vars
tal:define="vmgr_name view/__name__; here_url context/@@absolute_url">
<div id="provider_ITop"
tal:attributes="id string:provider_$vmgr_name">
<div tal:repeat="viewlet view/viewlets" tal:omit-tag="">
<div tal:replace="structure viewlet/render" />
</div>
</div>
<script
tal:content="string:
function reload_${vmgr_name}(){
new Ajax.Updater('provider_${vmgr_name}', '$here_url/++vmanager++${vmgr_name}')
}" />
</tal:vars>
This template inserts the viewlet contents in a named container and creates a JavaScript function (which uses Prototype) that can be called later to update that container. The last piece is to tell my viewlet providers to use this template (observe the template argument):
<viewletManager name="IFooter" provides=".IFooterSlotManager"
class="z3c.viewlet.manager.WeightOrderedViewletManager"
template="vmanager.pt"
permission="zope.View" />
I'll probably still have to write some views and JavaScript to make things dynamics, but this solution will sure help to reduce the amount of code I have to write.
2007-02-16
Fresh meat for programmers
I've just discovered (or rediscovered) some resources that I think are important enough to highlight here.
- The Zope Corporation has several eggs that are not published in the svn.zope.org subversion repository, eggs located at download.zope.org. There are several very interesting packages there, including some to build an intranet. I'll definately have a look at them.
- I've rediscovered OpenJSAN, a Javascript repository full of goodies.
- And its really nice Planet Javascript
2007-02-12
Using FCKEditor in Ajax views on Zope 3
I'm working on a new, AJAX based application for a friend, which will run under Zope 3. Being a rather "CMS-ish" type of application, I need to provide an easy way to edit some rich text fields. I've settled on the FCKEditor, for which there is an already packaged library as zope.html (also depends on zope.file). I would have used TinyMCE, but I hit on a problem: all these visual editors have difficulties when loaded in "dynamic loaded views".
- When using FCKEditor, everything seemed be fine for the first time, but the second time the editor was loaded, after the form was reloaded, there would be an error about a missing JavaScript object and the editor would fail to load. Form submission is handled by a function that serializes the form and makes an Ajax call, but the form object only contains the old values, not the new ones, as modified by the visual editor, so this had to be solved as well.
- I love TinyMCE for being able to scale down in terms of interface very easily, but I couldn't make it work in my scenario just as easily, so I gave up. The editor would load just fine, but when submitting the form, the entire web page would be replaced by a white page and would continue to keep loading, without any results. I've found some mentioning of this problem on the web, and even in the TinyMCE wiki, but I couldn't work out what needs to be done in the short time that I had.
To solve the FCKEditor problems I had to do the following (blessed be the other bloggers of the Internet which already had to deal with this problem):
- For the first problem, I've inserted the following snippet in the form header:
<script type="text/javascript">
FCKeditorAPI = null;
__FCKeditorNS = null;
FCKTools = null;
</script>
- For the second problem, the form submit handler, I have the following code:
if (FCKeditorAPI) {
for (instance in FCKeditorAPI.__Instances) {
field_name = instance.toString();
field_value = FCKeditorAPI.GetInstance(field_name).GetXHTML();
sub_form[field_name].value = field_value;
}
}While searching the net for other editors that might not have this problem, I've found this page that contains a big listing of all types of HTML visual editors. To tell the truth, in my use case, I'd be happy with something like Epoz (and I even have checked it on the web), but the project seems dead and I think I would have had to strip the zope/plone integration out of it.
After more then 6 or 7 years of not having to deal with JavaScript I'm very very rusty. Even for the most simple questions - like: how do you get the properties of an object? how do you check if an object has a property? how do you check if an object exists - I had to look at references. But it's all part of the learning experience, which fortunately, is the part that I enjoy most.