[Checkins] SVN: z3c.formjs/trunk/ Implemented JS functions.
Stephan Richter
srichter at cosmos.phy.tufts.edu
Fri Jul 20 18:44:12 EDT 2007
Log message for revision 78227:
Implemented JS functions.
Changed:
U z3c.formjs/trunk/TODO.txt
U z3c.formjs/trunk/src/z3c/formjs/interfaces.py
A z3c.formjs/trunk/src/z3c/formjs/jsfunction.py
A z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt
U z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py
-=-
Modified: z3c.formjs/trunk/TODO.txt
===================================================================
--- z3c.formjs/trunk/TODO.txt 2007-07-20 16:55:02 UTC (rev 78226)
+++ z3c.formjs/trunk/TODO.txt 2007-07-20 22:44:12 UTC (rev 78227)
@@ -2,22 +2,15 @@
TODO
====
- - A way to write a javascript function in python such that you can
- render a call to it from python
-
- class SomeForm:
- @jsfunction('bar') # bar is the namespace
- def foo(self):
- return 'alert("foo");'
- renderJSFunction(SomeForm.foo)
- 'function z3c_formjs_foo(){ alert("foo"); }'
- renderJSCall(SomeForm.foo)
- 'bar_foo()'
-
- ajax form submission - ala "save" button
- - ajax widget switching
-
- client side js validators for simple fields. (maybe we can use an
existing library?)
+- Make decorators less intrusive. They should return the original object, not
+ something they created, so that we can better pipe them, like::
+
+ @jsevent.handler(IFields['one'])
+ @jsevent.handler(IFields['two'])
+ def handleOneAndTwo(self, ...):
+ pass
Modified: z3c.formjs/trunk/src/z3c/formjs/interfaces.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/interfaces.py 2007-07-20 16:55:02 UTC (rev 78226)
+++ z3c.formjs/trunk/src/z3c/formjs/interfaces.py 2007-07-20 22:44:12 UTC (rev 78227)
@@ -83,21 +83,62 @@
"""Subscribe an event for a DOM element executing the handler's
result."""
- def __iter__(self):
+ def __iter__():
"""Return an iterator of all subscriptions."""
class IRenderer(zope.interface.Interface):
"""Render a component in the intended output format."""
- def update(self):
+ def update():
"""Update renderer."""
- def render(self):
+ def render():
"""Render content."""
-# -----[ Wiidgets ]-----------------------------------------------------------
+# -----[ Javascript Functions ]----------------------------------------------
+class IJSFunction(zope.interface.Interface):
+ """A Javascript Function."""
+
+ name = zope.schema.BytesLine(
+ title=u"Name",
+ description=u"The name of the function.",
+ required=True)
+
+ arguments = zope.schema.List(
+ title=u"Arguments",
+ description=u"A list of arguments of the function.",
+ required=True)
+
+ def render():
+ """Render the content of the JS function."""
+
+
+class IJSFunctions(zope.interface.Interface):
+ """A manager of Javascript functions."""
+
+ def add(function, namespace=''):
+ """Add a new function to the given namespace."""
+
+ def render(self):
+ """Render all functions."""
+
+class IHaveJSFunctions(zope.interface.Interface):
+ """An component that has a JS functions manager .
+
+ This component is most often a view component. When rendering a page this
+ interface is used to check whether any functions must be rendered.
+ """
+
+ jsFunctions = zope.schema.Object(
+ title=u"Javascript Functions",
+ description=u"Attribute holding the JS Functions Manager.",
+ schema = IJSFunctions,
+ required=True)
+
+# -----[ Widgets ]------------------------------------------------------------
+
class IWidgetSelector(ISelector):
"""Select a DOM element using the action."""
Added: z3c.formjs/trunk/src/z3c/formjs/jsfunction.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsfunction.py (rev 0)
+++ z3c.formjs/trunk/src/z3c/formjs/jsfunction.py 2007-07-20 22:44:12 UTC (rev 78227)
@@ -0,0 +1,115 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Javascript Functions.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import inspect
+import sys
+import zope.component
+import zope.interface
+from zope.viewlet import viewlet
+from zope.publisher.interfaces.browser import IBrowserRequest
+
+from z3c.formjs import interfaces
+
+class JSFunction(object):
+ zope.interface.implements(interfaces.IJSFunction)
+
+ def __init__(self, namespace, function):
+ self.namespace = namespace
+ self.function = function
+
+ @property
+ def name(self):
+ return self.function.func_name
+
+ @property
+ def arguments(self):
+ args = inspect.getargspec(self.function)[0]
+ if args[0] is 'self':
+ del args[0]
+ return args
+
+ def render(self):
+ return self.function(*[x for x in ['self'] + self.arguments])
+
+ def __repr__(self):
+ return '<%s %s>' % (
+ self.__class__.__name__, self.name)
+
+
+class JSFunctions(object):
+ zope.interface.implements(interfaces.IJSFunctions)
+
+ def __init__(self):
+ self._functions = {}
+
+ def add(self, function, namespace=''):
+ jsFunction = JSFunction(namespace, function)
+ ns = self._functions.setdefault(namespace, [])
+ ns.append(jsFunction)
+ return jsFunction
+
+ def render(self):
+ result = ''
+ # Render non-namespaced functions
+ for func in self._functions.get('', []):
+ args = func.arguments
+ result += 'function %s(%s) {\n' %(
+ func.name, ', '.join(args) )
+ code = func.render()
+ result += ' ' + code.replace('\n', '\n ') + '\n'
+ result += '}\n'
+ # Render namespaced functions
+ for ns, funcs in self._functions.items():
+ if ns == '':
+ continue
+ result += 'var %s = {\n' %ns
+ for func in funcs:
+ args = func.arguments
+ result += ' %s: function(%s) {\n' %(
+ func.name, ', '.join(args) )
+ code = func.render()
+ result += ' ' + code.replace('\n', '\n ') + '\n'
+ result += ' },\n'
+ result = result[:-2] + '\n'
+ result += '}\n'
+ return result
+
+ def __repr__(self):
+ return '<%s>' % (self.__class__.__name__)
+
+def function(namespace=''):
+ """A decorator for defining a javascript function."""
+ def createFunction(func):
+ frame = sys._getframe(1)
+ f_locals = frame.f_locals
+ funcs = f_locals.setdefault('jsFunctions', JSFunctions())
+ return funcs.add(func, namespace)
+ return createFunction
+
+
+class JSFunctionsViewlet(viewlet.ViewletBase):
+ """An viewlet for the JS viewlet manager rendering functions."""
+ zope.component.adapts(
+ zope.interface.Interface,
+ IBrowserRequest,
+ interfaces.IHaveJSFunctions,
+ zope.interface.Interface)
+
+ def render(self):
+ content = self.__parent__.jsFunctions.render()
+ return u'<script type="text/javascript">\n%s\n</script>' % content
Property changes on: z3c.formjs/trunk/src/z3c/formjs/jsfunction.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt (rev 0)
+++ z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt 2007-07-20 22:44:12 UTC (rev 78227)
@@ -0,0 +1,112 @@
+====================
+JavaScript Functions
+====================
+
+When developing JavaScript-enabled user interfaces, it is often necessary to
+create small callback functions. The usual way of creating those functions is
+to write and register a resource, then create a viewlet for it that integrates
+it. Those steps can be tedious when writing small functions. Thus, this
+package provides a way to convert a Python method to a Javascript function.
+
+ >>> from z3c.formjs import jsfunction
+
+So let's create a simple view with a JavaScript function in it:
+
+ >>> class View(object):
+ ...
+ ... @jsfunction.function('hw')
+ ... def showHelloWorldMessage(self):
+ ... return u"alert('Hello World!');"
+
+The argument to ``jsfunction.function`` is the namspace into which the
+function will be placed. This argument is optional. The Python method is
+expected to return the Javascript code as a string. All functions are
+collected in a special attribute called "jsFunctions"
+
+ >>> View.jsFunctions
+ <JSFunctions>
+
+The functions can be rendered directly:
+
+ >>> print View.jsFunctions.render()
+ var hw = {
+ showHelloWorldMessage: function() {
+ alert('Hello World!');
+ }
+ }
+
+Similarly to Javascript subscriptions, a JavaScript viewlet exists for any
+view containing JavaScript functions that provides the following output:
+
+ >>> viewlet = jsfunction.JSFunctionsViewlet(
+ ... object(), object(), View(), object())
+ >>> viewlet.update()
+ >>> print viewlet.render()
+ <script type="text/javascript">
+ var hw = {
+ showHelloWorldMessage: function() {
+ alert('Hello World!');
+ }
+ }
+ </script>
+
+Let's now have a closer look that the decorator. As mentioned before, the
+namespace is option. So what happens if the namespace is not specified? Then
+the function should be declared normally:
+
+ >>> class View(object):
+ ...
+ ... @jsfunction.function()
+ ... def showHelloWorldMessage(self):
+ ... return u"alert('Hello World!');"
+
+ >>> print View.jsFunctions.render()
+ function showHelloWorldMessage() {
+ alert('Hello World!');
+ }
+
+Of course you can mix namespace and non-namespace functions:
+
+ >>> class View(object):
+ ...
+ ... @jsfunction.function()
+ ... def show1(self):
+ ... return u"alert('Hello World!');"
+ ...
+ ... @jsfunction.function('ns1')
+ ... def show1(self):
+ ... return u"alert('Hello World!');"
+ ...
+ ... @jsfunction.function('ns1')
+ ... def show2(self):
+ ... return u"alert('Hello World!');"
+
+ >>> print View.jsFunctions.render()
+ function show1() {
+ alert('Hello World!');
+ }
+ var ns1 = {
+ show1: function() {
+ alert('Hello World!');
+ },
+ show2: function() {
+ alert('Hello World!');
+ }
+ }
+
+What about arguments? The arguments are directly extracted into the
+code. Currently, keyword arguments, and variable positional and keyword
+arguments are not supported, as they are not supported by JavaScript either:
+
+ >>> class View(object):
+ ...
+ ... @jsfunction.function()
+ ... def show(self, title):
+ ... return u"alert('Title' + title);"
+
+ >>> print View.jsFunctions.render()
+ function show(title) {
+ alert('Title' + title);
+ }
+
+And that is realy everything that there is to it.
Property changes on: z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py 2007-07-20 16:55:02 UTC (rev 78226)
+++ z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py 2007-07-20 22:44:12 UTC (rev 78227)
@@ -34,6 +34,11 @@
optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
zope.testing.doctest.ELLIPSIS),
zope.testing.doctest.DocFileSuite(
+ '../jsfunction.txt',
+ setUp=testing.setUp, tearDown=testing.tearDown,
+ optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+ zope.testing.doctest.ELLIPSIS),
+ zope.testing.doctest.DocFileSuite(
'../jsaction.txt',
setUp=testing.setUp, tearDown=testing.tearDown,
optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
More information about the Checkins
mailing list