[Checkins] SVN: z3c.formjs/trunk/src/z3c/formjs/ First attempt at making multiple handlers per element working. Still a

Stephan Richter srichter at cosmos.phy.tufts.edu
Sat Jul 7 02:15:13 EDT 2007


Log message for revision 77556:
  First attempt at making multiple handlers per element working. Still a 
  little clumsy and more tests are missing.
  
  Now you can also pass form fields to handler decorator.
  

Changed:
  U   z3c.formjs/trunk/src/z3c/formjs/interfaces.py
  U   z3c.formjs/trunk/src/z3c/formjs/jsaction.py
  U   z3c.formjs/trunk/src/z3c/formjs/jsaction.txt
  U   z3c.formjs/trunk/src/z3c/formjs/jsevent.py

-=-
Modified: z3c.formjs/trunk/src/z3c/formjs/interfaces.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/interfaces.py	2007-07-07 05:45:10 UTC (rev 77555)
+++ z3c.formjs/trunk/src/z3c/formjs/interfaces.py	2007-07-07 06:15:12 UTC (rev 77556)
@@ -128,6 +128,10 @@
         """call the handler, passing it the form."""
 
 
+class IJSEventHandlers(zope.interface.Interface):
+    pass
+
+
 # -----[ Validator ]--------------------------------------------------------
 
 

Modified: z3c.formjs/trunk/src/z3c/formjs/jsaction.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsaction.py	2007-07-07 05:45:10 UTC (rev 77555)
+++ z3c.formjs/trunk/src/z3c/formjs/jsaction.py	2007-07-07 06:15:12 UTC (rev 77556)
@@ -20,9 +20,11 @@
 import zope.component
 import zope.interface
 import zope.location
-from z3c.form import button, action
+from zope.interface import adapter
+from z3c.form import button, action, util
 from z3c.form.browser.button import ButtonWidget
-from z3c.form.interfaces import IFormLayer, IFieldWidget, IFormAware
+from z3c.form.interfaces import IField, IFieldWidget
+from z3c.form.interfaces import IFormLayer, IFormAware
 from z3c.form.interfaces import IButtonAction, IAfterWidgetUpdateEvent
 
 from z3c.formjs import interfaces, jsevent
@@ -66,22 +68,63 @@
     def id(self):
         return self.name.replace('.', '-')
 
-    def update(self):
-        super(JSButtonAction, self).update()
-        # Step 1: Get the handler.
-        handler = self.form.handlers.getHandler(self.field)
-        # Step 2: Create a selector.
-        selector = WidgetSelector(self)
-        # Step 3: Make sure that the form has JS subscriptions, otherwise add
-        #         it.
-        if not interfaces.IHaveJSSubscriptions.providedBy(self.form):
-            self.form.jsSubscriptions = jsevent.JSSubscriptions()
-            zope.interface.alsoProvides(
-                self.form, interfaces.IHaveJSSubscriptions)
-        # Step 4: Add the subscription to the form:
-        self.form.jsSubscriptions.subscribe(handler.event, selector, handler)
 
+class JSHandlers(object):
+    """Javascript event handlers for fields and buttons."""
+    zope.interface.implements(interfaces.IJSEventHandlers)
 
+    def __init__(self):
+        self._registry = adapter.AdapterRegistry()
+        self._handlers = ()
+
+    def addHandler(self, field, event, handler):
+        """See interfaces.IEventHandlers"""
+        # Create a specification for the field and event
+        fieldSpec = util.getSpecification(field)
+        eventSpec = util.getSpecification(event)
+        if isinstance(fieldSpec, util.classTypes):
+            fieldSpec = zope.interface.implementedBy(fieldSpec)
+        if isinstance(eventSpec, util.classTypes):
+            eventSpec = zope.interface.implementedBy(eventSpec)
+        # Register the handler
+        self._registry.register(
+            (fieldSpec, eventSpec), interfaces.IJSEventHandler, '', handler)
+        self._handlers += ((field, event, handler),)
+
+    def getHandlers(self, field):
+        """See interfaces.IButtonHandlers"""
+        fieldProvided = zope.interface.providedBy(field)
+        handlers = ()
+        for event in jsevent.EVENTS:
+            eventProvided = zope.interface.providedBy(event)
+            handler = self._registry.lookup(
+                (fieldProvided, eventProvided), interfaces.IJSEventHandler)
+            if handler:
+                handlers += (handler,)
+        return handlers
+
+    def copy(self):
+        """See interfaces.IButtonHandlers"""
+        handlers = Handlers()
+        for button, handler in self._handlers:
+            handlers.addHandler(button, handler)
+        return handlers
+
+    def __add__(self, other):
+        """See interfaces.IButtonHandlers"""
+        if not isinstance(other, Handlers):
+            raise NotImplementedError
+        handlers = self.copy()
+        for button, handler in other._handlers:
+            handlers.addHandler(button, handler)
+        return handlers
+
+    def __repr__(self):
+        return '<JSHandlers %r>' % (
+            self.__class__.__name__,
+            [handler for button, handler in self._handlers])
+
+
 class JSHandler(object):
     zope.interface.implements(interfaces.IJSEventHandler)
 
@@ -99,12 +142,16 @@
 
 def handler(field, **kwargs):
     """A decorator for defining a javascript event handler."""
+    # As a convenience, we also accept form fields to the handler, but get the
+    # real field immediately
+    if IField.providedBy(field):
+        field = field.field
     def createHandler(func):
         handler = JSHandler(field, func, **kwargs)
         frame = sys._getframe(1)
         f_locals = frame.f_locals
-        handlers = f_locals.setdefault('handlers', button.Handlers())
-        handlers.addHandler(field, handler)
+        handlers = f_locals.setdefault('jshandlers', JSHandlers())
+        handlers.addHandler(field, handler.event, handler)
         return handler
     return createHandler
 
@@ -116,9 +163,7 @@
     if not (IFieldWidget.providedBy(widget) and IFormAware.providedBy(widget)):
         return
     # Step 1: Get the handler.
-    handler = widget.form.handlers.getHandler(widget.field)
-    if handler is None:
-        return
+    handlers = widget.form.jshandlers.getHandlers(widget.field)
     # Step 2: Create a selector.
     selector = WidgetSelector(widget)
     # Step 3: Make sure that the form has JS subscriptions, otherwise add
@@ -128,4 +173,5 @@
         zope.interface.alsoProvides(
             widget.form, interfaces.IHaveJSSubscriptions)
     # Step 4: Add the subscription to the form:
-    widget.form.jsSubscriptions.subscribe(handler.event, selector, handler)
+    for handler in handlers:
+        widget.form.jsSubscriptions.subscribe(handler.event, selector, handler)

Modified: z3c.formjs/trunk/src/z3c/formjs/jsaction.txt
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsaction.txt	2007-07-07 05:45:10 UTC (rev 77555)
+++ z3c.formjs/trunk/src/z3c/formjs/jsaction.txt	2007-07-07 06:15:12 UTC (rev 77556)
@@ -135,6 +135,11 @@
   >>> zope.component.provideAdapter(
   ...     jsaction.JSButtonAction, provides=IButtonAction)
 
+Finally, for the Javascript subscriptions to be registered, we need an event
+listener that reacts to "after widget/action update" events:
+
+  >>> zope.component.provideHandler(jsaction.createSubscriptionsForWidget)
+
 Action managers are instantiated using the form, request, and
 context/content. A button-action-manager implementation is avaialble in the
 ``z3c.form.button`` package:
@@ -273,12 +278,37 @@
 Multiple Handlers
 -----------------
 
-Currently it is not possible to have multiple handlers for one dom element,
-even though the event is different!!!
+Since there are multiple events in Javascript, one element can have multiple
+handlers. So let's define a new form that declares two handlers for the same
+button:
 
-XXX: to be done
+  >>> class Form(form.Form):
+  ...     buttons = button.Buttons(IButtons).select('hello')
+  ...
+  ...     @jsaction.handler(buttons['hello'])
+  ...     def showHelloWorldMessage(self, selector):
+  ...         return 'alert("Hello World!");'
+  ...
+  ...     @jsaction.handler(buttons['hello'], event=jsevent.DBLCLICK)
+  ...     def showDoubleHelloWorldMessage(self, selector):
+  ...         return 'alert("Hello World! x 2");'
 
+Let's now instantiate and update the form:
 
+  >>> demoform = Form(None, request)
+  >>> demoform.update()
+
+The subscriptions are now available:
+
+  >>> list(demoform.jsSubscriptions)
+  [<JSSubscription event=<JSEvent "click">,
+                   selector=<WidgetSelector "form-buttons-hello">,
+                   handler=<JSHandler for <JSButton 'hello' u'Hello World!'>>>,
+   <JSSubscription event=<JSEvent "dblclick">,
+                   selector=<WidgetSelector "form-buttons-hello">,
+                   handler=<JSHandler for <JSButton 'hello' u'Hello World!'>>>]
+
+
 Submit and Javascript Buttons Together
 --------------------------------------
 
@@ -300,16 +330,14 @@
 Even though somewhat pointless, whenever the age field is clicked on or the
 name changed, we would like to get an alert:
 
-  XXX: Ugliness! fields['age'].field --> fields['age']
-
   >>> class PersonAddForm(form.AddForm):
   ...     fields = field.Fields(IPerson)
   ...
-  ...     @jsaction.handler(fields['age'].field)
+  ...     @jsaction.handler(fields['age'])
   ...     def ageClickEvent(self, selector):
   ...         return 'alert("The Age was Clicked!");'
   ...
-  ...     @jsaction.handler(fields['name'].field, event=jsevent.CHANGE)
+  ...     @jsaction.handler(fields['name'], event=jsevent.CHANGE)
   ...     def nameChangeEvent(self, selector):
   ...         return 'alert("The Name was Changed!");'
 
@@ -326,22 +354,6 @@
   >>> print addform.render()
   <html>
     <head>
-    </head>
-    ...
-  </html>
-
-As you can see there were no subscriptions rendererd. This reason is that we
-have not yet registered the event listener to the ``IAfterWidgetUpdateEvent``
-event:
-
-  >>> zope.component.provideHandler(jsaction.createSubscriptionsForWidget)
-
-So, let's try this again:
-
-  >>> addform.update()
-  >>> print addform.render()
-  <html>
-    <head>
       <script type="text/javascript">
         $(document).ready(function(){
           $("#form-widgets-name").bind("change",
@@ -352,8 +364,6 @@
       </script>
     </head>
     <body>
-  <BLANKLINE>
-  <BLANKLINE>
       <form action=".">
         <div class="row">
           <label for="form-widgets-name">Name</label>

Modified: z3c.formjs/trunk/src/z3c/formjs/jsevent.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsevent.py	2007-07-07 05:45:10 UTC (rev 77555)
+++ z3c.formjs/trunk/src/z3c/formjs/jsevent.py	2007-07-07 06:15:12 UTC (rev 77556)
@@ -52,6 +52,9 @@
 SELECT = JSEvent("select")
 SUBMIT = JSEvent("submit")
 
+EVENTS = (CLICK, DBLCLICK, CHANGE, LOAD, BLUR, FOCUS, KEYDOWN, KEYUP,
+          MOUSEDOWN, MOUSEMOVE, MOUSEOUT, MOUSEOVER, MOUSEUP, RESIZE, SELECT,
+          SUBMIT)
 
 class IdSelector(object):
     zope.interface.implements(interfaces.IIdSelector)



More information about the Checkins mailing list