[Checkins] SVN: z3c.formext/trunk/ initial import z3c.formext package.

Paul Carduner paulcarduner at gmail.com
Wed Jan 21 23:50:33 EST 2009


Log message for revision 94921:
  initial import z3c.formext package.

Changed:
  A   z3c.formext/trunk/
  A   z3c.formext/trunk/README.txt
  A   z3c.formext/trunk/bootstrap.py
  A   z3c.formext/trunk/buildout.cfg
  A   z3c.formext/trunk/setup.py
  A   z3c.formext/trunk/src/
  A   z3c.formext/trunk/src/z3c/
  A   z3c.formext/trunk/src/z3c/__init__.py
  A   z3c.formext/trunk/src/z3c/formext/
  A   z3c.formext/trunk/src/z3c/formext/README.txt
  A   z3c.formext/trunk/src/z3c/formext/__init__.py
  A   z3c.formext/trunk/src/z3c/formext/component.py
  A   z3c.formext/trunk/src/z3c/formext/component.txt
  A   z3c.formext/trunk/src/z3c/formext/configure.zcml
  A   z3c.formext/trunk/src/z3c/formext/converter.py
  A   z3c.formext/trunk/src/z3c/formext/form.pt
  A   z3c.formext/trunk/src/z3c/formext/form.py
  A   z3c.formext/trunk/src/z3c/formext/form.txt
  A   z3c.formext/trunk/src/z3c/formext/interfaces.py
  A   z3c.formext/trunk/src/z3c/formext/jsmodule.py
  A   z3c.formext/trunk/src/z3c/formext/jsoncompat.py
  A   z3c.formext/trunk/src/z3c/formext/jsoncompat.txt
  A   z3c.formext/trunk/src/z3c/formext/meta.zcml
  A   z3c.formext/trunk/src/z3c/formext/metadirectives.py
  A   z3c.formext/trunk/src/z3c/formext/resources/
  A   z3c.formext/trunk/src/z3c/formext/resources/form-script.js
  A   z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js
  A   z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js
  A   z3c.formext/trunk/src/z3c/formext/testing.py
  A   z3c.formext/trunk/src/z3c/formext/tests/
  A   z3c.formext/trunk/src/z3c/formext/tests/__init__.py
  A   z3c.formext/trunk/src/z3c/formext/tests/jsmodules/
  A   z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js
  A   z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js
  A   z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js
  A   z3c.formext/trunk/src/z3c/formext/tests/script.js
  A   z3c.formext/trunk/src/z3c/formext/tests/test_doc.py
  A   z3c.formext/trunk/src/z3c/formext/widget.py
  A   z3c.formext/trunk/src/z3c/formext/zcml.py
  A   z3c.formext/trunk/src/z3c/formext/zcml.txt

-=-
Added: z3c.formext/trunk/README.txt
===================================================================
--- z3c.formext/trunk/README.txt	                        (rev 0)
+++ z3c.formext/trunk/README.txt	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+This package provides integration of ExtJS into the z3c.form framework.
\ No newline at end of file


Property changes on: z3c.formext/trunk/README.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/bootstrap.py
===================================================================
--- z3c.formext/trunk/bootstrap.py	                        (rev 0)
+++ z3c.formext/trunk/bootstrap.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation 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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)


Property changes on: z3c.formext/trunk/bootstrap.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/buildout.cfg
===================================================================
--- z3c.formext/trunk/buildout.cfg	                        (rev 0)
+++ z3c.formext/trunk/buildout.cfg	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,19 @@
+[buildout]
+develop = .
+parts = test coverage-test coverage-report
+index = http://download.zope.org/zope3.4
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.formext [test]
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = z3c.formext [test]
+defaults = ['--coverage', '../../coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')


Property changes on: z3c.formext/trunk/buildout.cfg
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/setup.py
===================================================================
--- z3c.formext/trunk/setup.py	                        (rev 0)
+++ z3c.formext/trunk/setup.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,84 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Setup
+
+$Id$
+"""
+import os
+import xml.sax.saxutils
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    text = open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+    text = unicode(text, 'utf-8').encode('ascii', 'xmlcharrefreplace')
+    return xml.sax.saxutils.escape(text)
+
+setup (
+    name='z3c.formext',
+    version='0.1.0dev',
+    author = "Paul Carduner",
+    description = "ExtJS integration for z3c.form",
+    long_description=read('README.txt'),
+    license = "ZPL 2.1",
+    keywords = "zope3 form extjs",
+    classifiers = [
+        'Development Status :: 3 - Alpha',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Programming Language :: Python',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Topic :: Internet :: WWW/HTTP',
+        'Framework :: Zope3'],
+    packages = find_packages('src'),
+    include_package_data = True,
+    package_dir = {'':'src'},
+    namespace_packages = ['z3c'],
+    extras_require = dict(
+        test = [
+            'zope.app.container',
+            'zope.testing',
+            'z3c.coverage',
+            'z3c.template',
+            'zope.app.i18n',
+            ],
+        adding = ['zope.app.container'],
+        ),
+    install_requires = [
+        'setuptools',
+        'zope.app.pagetemplate',
+        'zope.app.testing',
+        'zope.component',
+        'zope.configuration',
+        'zope.event',
+        'zope.i18n',
+        'zope.i18nmessageid',
+        'zope.interface',
+        'zope.lifecycleevent',
+        'zope.location',
+        'zope.pagetemplate',
+        'zope.publisher',
+        'zope.schema',
+        'zope.security',
+        'zope.viewlet',
+        'z3c.form',
+        'z3c.formjs',
+        'z3c.pagelet',
+        'z3c.versionedresource',
+        'python-cjson',
+        'rwproperty'
+        ],
+    zip_safe = False,
+    )


Property changes on: z3c.formext/trunk/setup.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/__init__.py
===================================================================
--- z3c.formext/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/__init__.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,7 @@
+try:
+    # Declare this a namespace package if pkg_resources is available.
+    import pkg_resources
+    pkg_resources.declare_namespace('z3c')
+except ImportError:
+    pass
+


Property changes on: z3c.formext/trunk/src/z3c/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/README.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/README.txt	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/README.txt	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,6 @@
+=================
+Forms and Widgets
+=================
+
+  >>> 1+1
+  2
\ No newline at end of file


Property changes on: z3c.formext/trunk/src/z3c/formext/README.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/__init__.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/__init__.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/__init__.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+# Make a package.


Property changes on: z3c.formext/trunk/src/z3c/formext/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/component.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/component.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/component.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,265 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""ExtJS Component representation.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+from zope.traversing.browser.absoluteurl import absoluteURL
+
+from z3c.form.interfaces import IForm, DISPLAY_MODE, HIDDEN_MODE
+from z3c.form.interfaces import IPasswordWidget, IRadioWidget, IButtonAction
+from z3c.form.interfaces import ITextAreaWidget
+from z3c.form.interfaces import ITextWidget, ISelectWidget, ISingleCheckBoxWidget
+
+from z3c.formext import interfaces
+from z3c.formext.jsoncompat import jsonEncode
+
+class Component(object):
+
+    def _getConfig(self):
+        return {}
+
+    def getConfig(self, json=False):
+        return jsonEncode(self._getConfig()) if json else self._getConfig()
+
+class Field(Component):
+
+    xtype = None
+
+    def __init__(self, widget):
+        self.widget = widget
+
+    def _getConfig(self):
+        config = dict(
+            name = self.widget.name,
+            fieldLabel = self.widget.label,
+            id = self.widget.id,
+            value = self.widget.value)
+        if self.xtype:
+            config['xtype'] = self.xtype
+        if self.widget.title:
+            config['title'] = self.widget.title
+        if self.widget.mode == DISPLAY_MODE:
+            config['disabled'] = True
+        if self.widget.mode == HIDDEN_MODE:
+            config['hidden'] = True
+        return config
+
+
+class TextField(Field):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(ITextWidget)
+
+    xtype = 'textfield'
+
+    def _getConfig(self, json=False):
+        config = super(TextField, self)._getConfig()
+        if IPasswordWidget.providedBy(self.widget):
+            config['inputType'] = 'password'
+        return config
+
+
+class TextArea(TextField):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(ITextAreaWidget)
+
+    xtype = 'textarea'
+
+
+class DateField(TextField):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(interfaces.IExtJSDateWidget)
+
+    xtype = 'datefield'
+
+class ComboBox(Field):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(ISelectWidget)
+
+    xtype = 'combo'
+    def _getConfig(self, json=False):
+        config = super(ComboBox, self)._getConfig()
+        config['hiddenName'] = config['name']+':list'
+        config['triggerAction'] = 'all'
+        config['editable'] = False
+        #XXX: commented out, not sure why this was here
+        #del config['name']
+        config['store'] = [(item['value'], item['content'])
+                           for item in self.widget.items]
+        return config
+
+class CheckBox(Field):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(ISingleCheckBoxWidget)
+
+    xtype = 'checkbox'
+    def _getConfig(self, json=False):
+        config = super(CheckBox, self)._getConfig()
+        checkbox = self.widget.items[0]
+        config['checked'] = checkbox['checked']
+        config['fieldLabel'] = checkbox['label']
+        del config['value']
+        return config
+
+class RadioGroup(Field):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(IRadioWidget)
+
+    def _getConfig(self, json=False):
+        config = dict(
+            fieldLabel = self.widget.label,
+            id = self.widget.id,
+            xtype = 'radiogroup',
+            items = [dict(boxLabel=item['label'],
+                          id='%s-%s' % (self.widget.id, index),
+                          name=self.widget.name,
+                          inputValue=item['value'],
+                          checked=item['checked'])
+                     for index, item in enumerate(self.widget.items)]
+            )
+        # we must pass in an items list even if there aren't any.  So
+        # we will just pass in one item that is hidden.  This is most
+        # certainly less than ideal.
+        if not config['items']:
+            config['items'].append(dict(hidden=True))
+        if self.widget.title:
+            config['title'] = self.widget.title
+        return config
+
+class Button(Field):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(IButtonAction)
+
+    xtype = 'button'
+    def _getConfig(self, json=False):
+        config = super(Button, self)._getConfig()
+        config['text'] = self.widget.value
+        del config['value']
+        del config['fieldLabel']
+        return config
+
+
+def getButtonsConfig(form, asDict=True):
+    if not hasattr(form, 'actions'):
+        form.updateActions()
+    if not asDict:
+        return [interfaces.IExtJSComponent(action).getConfig()
+                for action in form.actions.values()]
+    return dict([(name, interfaces.IExtJSComponent(action).getConfig())
+                 for name, action in form.actions.items()])
+
+def getWidgetsConfig(form, asDict=True):
+    if not asDict:
+        widgets = []
+        for widget in form.widgets.values():
+            factory = interfaces.IExtJSComponent
+            if hasattr(widget, 'componentFactory'):
+                factory = widget.componentFactory
+            widgets.append(factory(widget).getConfig())
+        return widgets
+    widgets = {}
+    for name, widget in form.widgets.items():
+        factory = interfaces.IExtJSComponent
+        if hasattr(widget, 'componentFactory'):
+            factory = widget.componentFactory
+        widgets[name] = factory(widget).getConfig()
+    return widgets
+
+class FormPanel(Component):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(IForm)
+
+    xtype = 'formpanel'
+
+    def __init__(self, form):
+        self.form = form
+
+    def _getConfig(self, json=False):
+        config = dict(
+            xtype=self.xtype,
+            id=self.form.id,
+            submitURL=self.form.action)
+        if self.form.label:
+            config['title'] = self.form.label
+        if not self.form.widgets:
+            self.form.updateWidgets()
+        items = getWidgetsConfig(self.form, asDict=False)
+        if items:
+            config['items'] = items
+
+        buttons = getButtonsConfig(self.form, asDict=False)
+        if buttons:
+            config['buttons'] = buttons
+        if hasattr(self.form, 'renderTo'):
+            config['renderTo'] = self.form.renderTo
+
+        return config
+
+
+class ClientButton(Button):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(interfaces.IClientButtonAction)
+
+    def _getConfig(self, json=False):
+        config = super(ClientButton, self)._getConfig()
+        config['handler'] = {}
+        if self.widget.field.success:
+            config['handler']['success'] = self.widget.field.success
+        if self.widget.field.failure:
+            config['handler']['failure'] = self.widget.field.failure
+        return config
+
+def getAjaxButtonsConfig(form, asDict=True):
+    if not asDict:
+        buttons = getButtonsConfig(form, asDict=False)
+        if hasattr(form, 'ajaxRequestHandlers'):
+            for name, handler in form.ajaxRequestHandlers.items():
+                if name in form.actions:
+                    index = form.actions.keys().index(name)
+                    buttons[index]['url'] = '%s/@@ajax/%s' % (
+                        absoluteURL(form, form.request), name)
+        return buttons
+    buttons = getButtonsConfig(form, asDict=True)
+    if hasattr(form, 'ajaxRequestHandlers'):
+        for name, handler in form.ajaxRequestHandlers.items():
+            if name in form.actions:
+                buttons[name]['url'] = '%s/@@ajax/%s' % (
+                    absoluteURL(form, form.request), name)
+    return buttons
+
+
+class ExtFormPanel(FormPanel):
+    zope.interface.implements(interfaces.IExtJSComponent)
+    zope.component.adapts(interfaces.IExtJSForm)
+
+    def _getConfig(self, json=False):
+        config = super(ExtFormPanel, self)._getConfig()
+        config['ajaxHandlers'] = {}
+        if hasattr(self.form, 'ajaxRequestHandlers'):
+            for name, handler in self.form.ajaxRequestHandlers.items():
+                if name in self.form.actions:
+                    index = self.form.actions.keys().index(name)
+                    config['buttons'][index]['url'] = '%s/@@ajax/%s' % (
+                        absoluteURL(self.form, self.form.request), name)
+                    id = self.form.actions[name].id
+                    config['ajaxHandlers'][id] = '%s/@@ajax/%s' % (
+                        absoluteURL(self.form, self.form.request), name)
+        if hasattr(self.form, 'ownerCt'):
+            config['ownerCt'] = self.form.ownerCt
+        return config
+


Property changes on: z3c.formext/trunk/src/z3c/formext/component.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/component.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/component.txt	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/component.txt	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,438 @@
+================
+ExtJS Components
+================
+
+We can construct configuration objects for basic extjs components.
+
+  >>> from z3c.formext import interfaces, component, testing
+
+  >>> from pprint import pprint
+
+Setup
+-----
+
+  >>> testing.setupExtJSComponents()
+
+  >>> from z3c.form.testing import setupFormDefaults, TestRequest
+  >>> setupFormDefaults()
+
+Widgets
+-------
+
+TextWidget
+..........
+
+    >>> from z3c.form.interfaces import HIDDEN_MODE, DISPLAY_MODE
+    >>> from z3c.form.browser.text import TextWidget
+
+    >>> widget = TextWidget(TestRequest())
+    >>> widget.label = u'Widget Label'
+    >>> widget.id = 'widget-id'
+    >>> widget.name = 'widget.name'
+    >>> widget.value = 'widget value'
+
+    >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+    {'fieldLabel': u'Widget Label',
+     'id': 'widget-id',
+     'name': 'widget.name',
+     'value': 'widget value',
+     'xtype': 'textfield'}
+
+    >>> widget.mode = HIDDEN_MODE
+    >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+    {'fieldLabel': u'Widget Label',
+     'hidden': True,
+     'id': 'widget-id',
+     'name': 'widget.name',
+     'value': 'widget value',
+     'xtype': 'textfield'}
+
+    >>> widget.mode = DISPLAY_MODE
+    >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+    {'disabled': True,
+     'fieldLabel': u'Widget Label',
+     'id': 'widget-id',
+     'name': 'widget.name',
+     'value': 'widget value',
+     'xtype': 'textfield'}
+
+TextArea
+........
+
+    >>> from z3c.form.browser.textarea import TextAreaWidget
+
+    >>> widget = TextAreaWidget(TestRequest())
+    >>> widget.label = u'Widget Label'
+    >>> widget.id = 'widget-id'
+    >>> widget.name = 'widget.name'
+    >>> widget.value = 'widget value'
+
+    >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+    {'fieldLabel': u'Widget Label',
+     'id': 'widget-id',
+     'name': 'widget.name',
+     'value': 'widget value',
+     'xtype': 'textarea'}
+
+Date Widget
+...........
+
+    >>> from z3c.formext.widget import ExtJSDateWidget
+
+    >>> widget = ExtJSDateWidget(TestRequest())
+    >>> widget.label = u'Widget Label'
+    >>> widget.id = 'widget-id'
+    >>> widget.name = 'widget.name'
+    >>> widget.value = 'widget value'
+
+    >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+    {'fieldLabel': u'Widget Label',
+     'id': 'widget-id',
+     'name': 'widget.name',
+     'value': 'widget value',
+     'xtype': 'datefield'}
+
+Select Widget
+.............
+
+    >>> from z3c.form.browser.select import SelectFieldWidget
+    >>> from zope.schema import Choice
+
+    >>> widget = SelectFieldWidget(
+    ...     Choice(values=[1,2,3,4]),
+    ...     TestRequest())
+    >>> widget.update()
+    >>> widget.label = u'Widget Label'
+    >>> widget.id = 'widget-id'
+    >>> widget.name = 'widget.name'
+    >>> widget.value = 'widget value'
+
+    >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+    {'editable': False,
+     'fieldLabel': u'Widget Label',
+     'hiddenName': 'widget.name:list',
+     'id': 'widget-id',
+     'name': 'widget.name',
+     'store': [('1', '1'), ('2', '2'), ('3', '3'), ('4', '4')],
+     'triggerAction': 'all',
+     'value': 'widget value',
+     'xtype': 'combo'}
+
+Radio Widget
+............
+
+    >>> from z3c.form.browser.radio import RadioFieldWidget
+    >>> widget = RadioFieldWidget(
+    ...     Choice(values=[1, 2, 3]),
+    ...     TestRequest())
+    >>> widget.update()
+    >>> widget.label = u'Widget Label'
+    >>> widget.id = 'widget-id'
+    >>> widget.name = 'widget.name'
+    >>> widget.value = 'widget value'
+
+    >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+    {'fieldLabel': u'Widget Label',
+     'id': 'widget-id',
+     'items': [{'boxLabel': '1',
+                'checked': False,
+                'id': 'widget-id-0',
+                'inputValue': '1',
+                'name': 'widget.name'},
+               {'boxLabel': '2',
+                'checked': False,
+                'id': 'widget-id-1',
+                'inputValue': '2',
+                'name': 'widget.name'},
+               {'boxLabel': '3',
+                'checked': False,
+                'id': 'widget-id-2',
+                'inputValue': '3',
+                'name': 'widget.name'}],
+     'xtype': 'radiogroup'}
+
+An edge case is a radio widget that has no values to select.  ExtJS
+requires there to be an items attribute that is a non empty list.  To
+get a similar effect, we just add one hidden item to the items list.
+
+    >>> widget = RadioFieldWidget(
+    ...     Choice(values=[]),
+    ...     TestRequest())
+    >>> widget.update()
+    >>> widget.label = u'Widget Label'
+    >>> widget.id = 'widget-id'
+    >>> widget.name = 'widget.name'
+    >>> widget.value = 'widget value'
+
+    >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+    {'fieldLabel': u'Widget Label',
+     'id': 'widget-id',
+     'items': [{'hidden': True}],
+     'xtype': 'radiogroup'}
+
+
+Form Panels
+-----------
+
+ExtJS has the concept of a form panel.  We can directly adapt a from
+from z3c.form.
+
+  >>> from z3c.form import form, field, button
+  >>> import zope.schema
+  >>> import zope.interface
+
+
+  >>> class IPerson(zope.interface.Interface):
+  ...
+  ...     id = zope.schema.TextLine(
+  ...         title=u'ID',
+  ...         readonly=True,
+  ...         required=True)
+  ...
+  ...     isCool = zope.schema.Bool(
+  ...         title=u'Are you Cool?',
+  ...         required=True)
+  ...
+  ...     name = zope.schema.TextLine(
+  ...         title=u'Name',
+  ...         required=True)
+  ...
+  ...     gender = zope.schema.Choice(
+  ...         title=u'Gender',
+  ...         values=('male', 'female'),
+  ...         required=False)
+  ...
+  ...     age = zope.schema.Int(
+  ...         title=u'Age',
+  ...         description=u"The person's age.",
+  ...         min=0,
+  ...         default=20,
+  ...         required=False)
+  ...
+  ...     passwd = zope.schema.Password(
+  ...         title=u'Password',
+  ...         required=True)
+  ...
+  ...     birthDay = zope.schema.Date(
+  ...         title=u'Birthday')
+  ...
+  ...     isNice = zope.schema.Bool(
+  ...         title=u'Are you Nice?',
+  ...         required=True)
+  ...
+
+  >>> from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
+  >>> from z3c.form.browser.password import PasswordFieldWidget
+  >>> from z3c.form.interfaces import HIDDEN_MODE
+  >>> from z3c.formext.widget import ExtJSDateFieldWidget
+  >>> class ContactForm(form.Form):
+  ...     label = u'My Contact Form'
+  ...     ignoreContext = True
+  ...     fields = field.Fields(IPerson)
+  ...     fields['isCool'].widgetFactory = SingleCheckBoxFieldWidget
+  ...     fields['passwd'].widgetFactory = PasswordFieldWidget
+  ...     fields['birthDay'].widgetFactory = ExtJSDateFieldWidget
+  ...     buttons = button.Buttons(button.Button(__name__='save', title=u'Save'))
+  ...     renderTo = 'my-dom-id'
+  ...     def updateWidgets(self):
+  ...         super(ContactForm, self).updateWidgets()
+  ...         self.widgets['id'].mode = HIDDEN_MODE
+
+  >>> myForm = ContactForm('context', TestRequest())
+
+  >>> formPanel = interfaces.IExtJSComponent(myForm)
+  >>> config = formPanel.getConfig()
+
+Now we can look more closely at what we actually get
+
+  >>> config['xtype']
+  'formpanel'
+  >>> config['title']
+  u'My Contact Form'
+  >>> config['id']
+  'form'
+  >>> config['renderTo']
+  'my-dom-id'
+
+Here is a simple text widget
+
+  >>> pprint(config['items'][0])
+  {'fieldLabel': u'ID',
+   'hidden': True,
+   'id': 'form-widgets-id',
+   'name': 'form.widgets.id',
+   'value': u'',
+   'xtype': 'textfield'}
+
+Here is a single checkbox widget
+
+  >>> pprint(config['items'][1])
+  {'checked': False,
+   'fieldLabel': u'Are you Cool?',
+   'id': 'form-widgets-isCool',
+   'name': 'form.widgets.isCool',
+   'xtype': 'checkbox'}
+
+Here is another simple text widget
+
+  >>> pprint(config['items'][2])
+  {'fieldLabel': u'Name',
+   'id': 'form-widgets-name',
+   'name': 'form.widgets.name',
+   'value': u'',
+   'xtype': 'textfield'}
+
+Here is a select widget
+
+    >>> pprint(config['items'][3])
+    {'editable': False,
+     'fieldLabel': u'Gender',
+     'hiddenName': 'form.widgets.gender:list',
+     'id': 'form-widgets-gender',
+     'name': 'form.widgets.gender',
+     'store': [('--NOVALUE--', u'no value'),
+               ('male', 'male'),
+               ('female', 'female')],
+     'triggerAction': 'all',
+     'value': (),
+     'xtype': 'combo'}
+
+  >>> pprint(config['buttons'])
+  [{'id': 'form-buttons-save',
+    'name': 'form.buttons.save',
+    'text': u'Save',
+    'title': u'Save',
+    'xtype': 'button'}]
+
+Here is an integer widget
+
+  >>> pprint(config['items'][4])
+  {'fieldLabel': u'Age',
+   'id': 'form-widgets-age',
+   'name': 'form.widgets.age',
+   'value': u'20',
+   'xtype': 'textfield'}
+
+Here is a password widget
+
+  >>> pprint(config['items'][5])
+  {'fieldLabel': u'Password',
+   'id': 'form-widgets-passwd',
+   'inputType': 'password',
+   'name': 'form.widgets.passwd',
+   'value': u'',
+   'xtype': 'textfield'}
+
+Here is a date widget
+
+  >>> pprint(config['items'][6])
+  {'fieldLabel': u'Birthday',
+   'id': 'form-widgets-birthDay',
+   'name': 'form.widgets.birthDay',
+   'value': u'',
+   'xtype': 'datefield'}
+
+Here is a radio group
+
+  >>> pprint(config['items'][7])
+  {'fieldLabel': u'Are you Nice?',
+   'id': 'form-widgets-isNice',
+   'items': [{'boxLabel': u'yes',
+              'checked': False,
+              'id': 'form-widgets-isNice-0',
+              'inputValue': 'true',
+              'name': 'form.widgets.isNice'},
+             {'boxLabel': u'no',
+              'checked': False,
+              'id': 'form-widgets-isNice-1',
+              'inputValue': 'false',
+              'name': 'form.widgets.isNice'}],
+   'xtype': 'radiogroup'}
+
+Helper functions
+----------------
+
+It is sometimes the case that we want to provide information about an
+extjs component in a format that doesn't quite fit what extjs wants to
+use, but which is easier for custom components to configure and play
+with.  For example, extjs likes container items to be a list, but if
+the script that uses the config object wants to present items in a
+different order, it is easier to reference the items by name rather
+than by index.
+
+Buttons
+.......
+
+For example, we can get the set of buttons in either a dictionary
+based format (easy for rearraging) or a list format (the way extjs
+likes it).
+
+  >>> pprint(component.getButtonsConfig(myForm))
+  {'save': {'id': 'form-buttons-save',
+            'name': 'form.buttons.save',
+            'text': u'Save',
+            'title': u'Save',
+            'xtype': 'button'}}
+
+  >>> pprint(component.getButtonsConfig(myForm, asDict=False))
+  [{'id': 'form-buttons-save',
+    'name': 'form.buttons.save',
+    'text': u'Save',
+    'title': u'Save',
+    'xtype': 'button'}]
+
+Widgets
+.......
+
+This works just like buttons, except for widgets.
+
+  >>> pprint(component.getWidgetsConfig(myForm)['isNice'])
+  {'fieldLabel': u'Are you Nice?',
+   'id': 'form-widgets-isNice',
+   'items': [{'boxLabel': u'yes',
+              'checked': False,
+              'id': 'form-widgets-isNice-0',
+              'inputValue': 'true',
+              'name': 'form.widgets.isNice'},
+             {'boxLabel': u'no',
+              'checked': False,
+              'id': 'form-widgets-isNice-1',
+              'inputValue': 'false',
+              'name': 'form.widgets.isNice'}],
+   'xtype': 'radiogroup'}
+
+  >>> pprint(component.getWidgetsConfig(myForm, asDict=False)[0])
+  {'fieldLabel': u'ID',
+   'hidden': True,
+   'id': 'form-widgets-id',
+   'name': 'form.widgets.id',
+   'value': u'',
+   'xtype': 'textfield'}
+
+
+Custom Widget Component Factories
+---------------------------------
+
+We can also specify custom components for widgets.
+
+  >>> class MyComponent(component.TextField):
+  ...   xtype = 'my-component'
+  >>> class ContactForm(form.Form):
+  ...     ignoreContext = True
+  ...     fields = field.Fields(IPerson).select('id')
+  ...     def updateWidgets(self):
+  ...         super(ContactForm, self).updateWidgets()
+  ...         self.widgets['id'].componentFactory = MyComponent
+
+  >>> myForm = ContactForm('context', TestRequest())
+
+  >>> formPanel = interfaces.IExtJSComponent(myForm)
+  >>> config = formPanel.getConfig()
+  >>> pprint(config['items'][0])
+  {'disabled': True,
+   'fieldLabel': u'ID',
+   'id': 'form-widgets-id',
+   'name': 'form.widgets.id',
+   'value': u'',
+   'xtype': 'my-component'}
+


Property changes on: z3c.formext/trunk/src/z3c/formext/component.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/configure.zcml
===================================================================
--- z3c.formext/trunk/src/z3c/formext/configure.zcml	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/configure.zcml	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,68 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    xmlns:z3c="http://namespaces.zope.org/z3c">
+
+
+  <!-- Data Converters -->
+  <adapter
+      factory=".converter.ExtJSDateDataConverter"
+      />
+  <adapter
+      factory=".converter.ExtJSSingleCheckBoxDataConverter"
+      />
+
+  <browser:versionedResource
+      name="z3c.formext.loader.js"
+      file="resources/z3c.formext.loader.js"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:jsmodule
+      file="resources/z3c.formext.form.js"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <!-- ExtJS Component adapters -->
+  <adapter
+      factory=".component.FormPanel"
+      />
+  <adapter
+      factory=".component.ExtFormPanel"
+      />
+  <adapter
+      factory=".component.TextField"
+      />
+  <adapter
+      factory=".component.TextArea"
+      />
+  <adapter
+      factory=".component.DateField"
+      />
+  <adapter
+      factory=".component.ComboBox"
+      />
+  <adapter
+      factory=".component.CheckBox"
+      />
+  <adapter
+      factory=".component.RadioGroup"
+      />
+  <adapter
+      factory=".component.Button"
+      />
+  <adapter
+      factory=".component.ClientButton"
+      />
+
+  <adapter
+      factory=".form.ClientButtonAction"
+      provides="z3c.form.interfaces.IButtonAction"
+      />
+
+  <z3c:template
+      for=".form.ScriptProvider"
+      template="form.pt"
+      layer="z3c.form.interfaces.IFormLayer" />
+
+</configure>


Property changes on: z3c.formext/trunk/src/z3c/formext/configure.zcml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/converter.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/converter.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/converter.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,59 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Special ExtJS data converters
+
+$Id$
+"""
+
+import zope.i18n.format
+from z3c.form import converter
+from z3c.formext import interfaces
+
+class ExtJSDateDataConverter(converter.CalendarDataConverter):
+    zope.component.adapts(
+        zope.schema.interfaces.IDate, interfaces.IExtJSDateWidget)
+    type = "date"
+
+    def toWidgetValue(self, value):
+        """See interfaces.IDataConverter"""
+        if value is self.field.missing_value:
+            return u''
+        return self.formatter.format(value, pattern="MM/dd/yyyy")
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        if value == u'':
+            return self.field.missing_value
+        try:
+            # Note: use ExtJS.DateField.format = 'm/d/Y', not the default!
+            return self.formatter.parse(value, pattern="MM/dd/yyyy")
+        except zope.i18n.format.DateTimeParseError, err:
+            raise converter.FormatterValidationError(err.args[0], value)
+
+
+class ExtJSSingleCheckBoxDataConverter(converter.BaseDataConverter):
+    zope.component.adapts(
+        zope.schema.interfaces.IBool, interfaces.IExtJSSingleCheckBoxWidget)
+
+    def toWidgetValue(self, value):
+        """Convert from Python bool to HTML representation."""
+        if value:
+            return 'on'
+        return 'off'
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        if value == 'on':
+            return True
+        return False


Property changes on: z3c.formext/trunk/src/z3c/formext/converter.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/form.pt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/form.pt	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/form.pt	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+<script tal:replace="structure view/scriptTag"/>
\ No newline at end of file


Property changes on: z3c.formext/trunk/src/z3c/formext/form.pt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/form.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/form.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/form.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,209 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""ExtJS Component representation.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import sys
+from rwproperty import getproperty, setproperty
+
+import zope.interface
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+from zope.i18n import translate
+from zope.pagetemplate.interfaces import IPageTemplate
+from zope.schema.fieldproperty import FieldProperty
+from zope.security.proxy import removeSecurityProxy
+
+import z3c.form.form
+from z3c.form.button import Button, Buttons
+from z3c.form.button import ButtonAction
+from z3c.form.button import ButtonAction
+from z3c.form.interfaces import IFormLayer
+from z3c.form.util import SelectionManager
+from z3c.formjs.ajax import AJAXHandler, AJAXHandlers
+from z3c.formjs.ajax import AJAXRequestHandler
+from z3c.pagelet.browser import BrowserPagelet
+
+from z3c.formext import interfaces
+from z3c.formext.jsoncompat import jsonEncode
+
+
+class JSProperties(SelectionManager):
+    """JSProperties selection manager."""
+    zope.interface.implements(interfaces.IJSProperties)
+    managerInterface = interfaces.IJSProperties
+
+    def __init__(self, *args):
+        super(JSProperties, self).__init__()
+        for arg in args:
+            if self.managerInterface.providedBy(arg):
+                for name, prop in arg.items():
+                    self._data_keys.append(name)
+                    self._data_values.append(prop)
+                    self._data[name] = prop
+            else:
+                self._data_keys.append(arg.__name__)
+                self._data_values.append(arg)
+                self._data[arg.__name__] = arg
+
+
+def jsproperty(func):
+    frame = sys._getframe(1)
+    properties = frame.f_locals.setdefault('jsproperties', JSProperties())
+    frame.f_locals['jsproperties'] += JSProperties(func)
+    return frame.f_locals['jsproperties'][func.__name__]
+
+
+def dependencyWrap(dep):
+    return ('z3c.formext.ModuleLoader.load(\n'
+            '  "%s",\n'
+            '  function(){\n'
+            '    %%s\n'
+            '  });' % dep)
+
+class ScriptProvider(object):
+
+    script = ''
+    scriptDependencies = ()
+
+    @property
+    def scriptTag(self):
+        tagWrap = '<script type="text/javascript" language="Javascript">\n%s\n</script>'
+        closureWrap = '  (function(){\n%s\n})();'
+
+        depWraps = '%s'
+        for dep in self.scriptDependencies:
+            depWraps = depWraps % dependencyWrap(dep)
+
+        script = self.script
+        if hasattr(script, '__call__'):
+            #this is a page template.
+            script = script()
+
+        jsVars = ''
+        if hasattr(self, 'jsproperties'):
+            jsVars = '\n'.join(['    var %s=%s;' % (name, jsonEncode(prop(self)))
+                                for name, prop in self.jsproperties.items()])
+
+        return tagWrap % (closureWrap % (depWraps % ('%s\n%s' % (jsVars, script))))
+
+
+class ScriptPagelet(ScriptProvider, AJAXRequestHandler, BrowserPagelet):
+    """A class that can be extended to get all this functionality."""
+
+
+class ExtJSForm(ScriptProvider, AJAXRequestHandler, z3c.form.form.Form):
+    zope.interface.implements(interfaces.IExtJSForm)
+    jsonResponse = None
+
+    script = ViewPageTemplateFile('resources/form-script.js')
+
+    @jsproperty
+    def config(self):
+        return interfaces.IExtJSComponent(self).getConfig()
+
+    @property
+    def response(self):
+        return jsonEncode(self.jsonResponse or dict(success=True))
+
+    def addFormError(self, error):
+        self.jsonResponse['success'] = False
+        self.jsonResponse.setdefault('formErrors', [])
+        if isinstance(error, unicode):
+            error = translate(error)
+        self.jsonResponse['formErrors'].append(str(error))
+
+    def extractData(self):
+        data, errors = super(ExtJSForm, self).extractData()
+        self.jsonResponse = dict(success=True)
+        if errors:
+            self.jsonResponse = dict(
+                success=False,
+                # I shouldn't need the below security proxy
+                errors={},
+                formErrors=[])
+            for error in errors:
+                error = removeSecurityProxy(error)
+                message = translate(error.message)
+                if error.widget:
+                    self.jsonResponse['errors'][error.widget.id] = message
+                else:
+                    self.jsonResponse['formErrors'].append(message)
+        return data, errors
+
+
+class ClientButton(Button):
+    zope.interface.implements(interfaces.IClientButton)
+
+    success = FieldProperty(interfaces.IClientButton['success'])
+    failure = FieldProperty(interfaces.IClientButton['failure'])
+
+    def __init__(self, *args, **kwargs):
+        self.success = kwargs.pop('success', None)
+        self.failure = kwargs.pop('failure', None)
+        super(ClientButton, self).__init__(*args, **kwargs)
+
+
+class ClientButtonAction(ButtonAction):
+    zope.interface.implements(interfaces.IClientButtonAction)
+    zope.component.adapts(IFormLayer, interfaces.IClientButton)
+
+    _success = FieldProperty(interfaces.IClientButton['success'])
+    _failure = FieldProperty(interfaces.IClientButton['failure'])
+
+    @getproperty
+    def success(self):
+        if self._success is None:
+            return self.field.success
+        return self._success
+
+    @getproperty
+    def failure(self):
+        if self._failure is None:
+            return self.field.failure
+        return self._failure
+
+    @setproperty
+    def success(self, value):
+        self._success = value
+
+    @setproperty
+    def failure(self, value):
+        self._failure = value
+
+
+def buttonAndHandler(title, **kwargs):
+
+    # Add the title to button constructor keyword arguments
+    kwargs['title'] = title
+
+    # Extract directly provided interfaces:
+    provides = kwargs.pop('provides', ())
+
+    # Create button and add it to the button manager
+    button = ClientButton(**kwargs)
+    zope.interface.alsoProvides(button, provides)
+    frame = sys._getframe(1)
+    f_locals = frame.f_locals
+    buttons = f_locals.setdefault('buttons', Buttons())
+    f_locals['buttons'] += Buttons(button)
+
+    def ajaxHandler(func):
+        handler = AJAXHandler(func)
+        handlers = f_locals.setdefault('ajaxRequestHandlers', AJAXHandlers())
+        handlers.addHandler(button.__name__, handler)
+        return handler
+
+    return ajaxHandler


Property changes on: z3c.formext/trunk/src/z3c/formext/form.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/form.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/form.txt	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/form.txt	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,232 @@
+ExtJS Form Integration
+======================
+
+This package also provides a tight integration between z3c.form -
+which provides a server side model and validation system, and Ext JS
+forms which provide an advanced client side form model.
+
+  >>> from z3c.formext import form, interfaces
+
+Setup
+-----
+
+  >>> from z3c.formext import testing
+  >>> testing.setupFormExt()
+
+Script Provider
+---------------
+
+We have a base class that represents a component that provides some
+javascript.  It has support for several features which we will show
+off here.
+
+    >>> from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+    >>> class MyPage(form.ScriptProvider):
+    ...   script = ViewPageTemplateFile('tests/script.js')
+    ...   scriptDependencies = ('z3c.formext.form','someOtherLib')
+    ...   @form.jsproperty
+    ...   def appearsInJS(self):
+    ...       return ['yay','for',{'complex':'types'}]
+
+    >>> myPage = MyPage()
+    >>> myPage.request = testing.TestRequest()
+    >>> myPage.context = object()
+    >>> print myPage.scriptTag
+    <script type="text/javascript" language="Javascript">
+      (function(){
+    z3c.formext.ModuleLoader.load(
+      "z3c.formext.form",
+      function(){
+        z3c.formext.ModuleLoader.load(
+      "someOtherLib",
+      function(){
+            var appearsInJS=["yay", "for", {"complex": "types"}];
+    alert("foo!");
+      });
+      });
+    })();
+    </script>
+
+Creating forms
+--------------
+
+Ext JS forms are created in the same manner as regular z3c.form
+forms. First we will create an interface for the form.
+
+
+  >>> from z3c.form import field, button
+  >>> import zope.schema
+  >>> import zope.interface
+
+  >>> class IPerson(zope.interface.Interface):
+  ...
+  ...     id = zope.schema.TextLine(
+  ...         title=u'ID',
+  ...         readonly=True,
+  ...         required=True)
+  ...
+  ...     isCool = zope.schema.Bool(
+  ...         title=u'Are you Cool?',
+  ...         required=True)
+  ...
+  ...     name = zope.schema.TextLine(
+  ...         title=u'Name',
+  ...         required=True)
+  ...
+  ...     gender = zope.schema.Choice(
+  ...         title=u'Gender',
+  ...         values=('male', 'female'),
+  ...         required=False)
+  ...
+  ...     age = zope.schema.Int(
+  ...         title=u'Age',
+  ...         description=u"The person's age.",
+  ...         min=0,
+  ...         default=20,
+  ...         required=False)
+  ...
+  ...     passwd = zope.schema.Password(
+  ...         title=u'Password',
+  ...         required=True)
+
+Now we can create the form.
+
+  >>> from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
+  >>> from z3c.form.browser.password import PasswordFieldWidget
+  >>> from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+  >>> class ContactForm(testing.TestingForm, form.ExtJSForm):
+  ...     label = u'My Contact Form'
+  ...     ignoreContext = True
+  ...     fields = field.Fields(IPerson)
+  ...     fields['isCool'].widgetFactory = SingleCheckBoxFieldWidget
+  ...     fields['passwd'].widgetFactory = PasswordFieldWidget
+  ...
+  ...     ownerCt = 'my-ext-component-id'
+  ...
+  ...     @form.buttonAndHandler(title=u'Save', success='handleSave')
+  ...     def handleSave(self):
+  ...         pass
+  ...     jsproperties = form.ExtJSForm.jsproperties.copy()
+  ...     @form.jsproperty
+  ...     def foo(self):
+  ...         return 'the foo property'
+
+Note that the jsproperties are not changed on the superclass
+
+  >>> form.ExtJSForm.jsproperties.keys()
+  ['config']
+
+  >>> ContactForm.jsproperties.keys()
+  ['config', 'foo']
+
+Now we can invoke the form and try to extract the data.
+
+  >>> myForm = ContactForm(None, testing.TestRequest())
+  >>> myForm.update()
+  >>> data, errors = myForm.extractData()
+
+Since we have not entered in any data, there will be errors.
+
+  >>> len(errors)
+  3
+
+To propogate these errors to the client side ext js form, we must
+construct a json response.  This is done for us by ``ExtJSForm``.
+
+  >>> from pprint import pprint
+  >>> pprint(myForm.jsonResponse)
+  {'errors': {'form-widgets-isCool': u'Required input is missing.',
+              'form-widgets-name': u'Required input is missing.',
+              'form-widgets-passwd': u'Required input is missing.'},
+   'formErrors': [],
+   'success': False}
+
+The other nice part of this is the button handler, which is
+automatically converted into an ajax request handler
+
+  >>> myForm.handleSave
+  <AJAXHandler 'handleSave'>
+
+The ajax handler is published using the name of the button rather than
+the name of the handler.
+
+  >>> myForm.ajaxRequestHandlers
+  <AJAXHandlers ['save']>
+
+The form config object will then have an ajaxHandlers property that is
+a mapping from button id to the url which should be called for that button.
+
+  >>> config = interfaces.IExtJSComponent(myForm).getConfig()
+  >>> config['ajaxHandlers']
+  {'form-buttons-save': 'http://127.0.0.1/index.html/@@ajax/save'}
+
+This url is also available on the button.
+
+  >>> from pprint import pprint
+  >>> pprint(config['buttons'][0])
+  {'handler': {'success': 'handleSave'},
+   'id': 'form-buttons-save',
+   'name': 'form.buttons.save',
+   'text': u'Save',
+   'title': u'Save',
+   'url': 'http://127.0.0.1/index.html/@@ajax/save',
+   'xtype': 'button'}
+
+If we want to get the listing of buttons separately, and have it
+include the url, we can use the helper function getAjaxButtonsConfig:
+
+  >>> from z3c.formext import component
+  >>> pprint(component.getAjaxButtonsConfig(myForm))
+  {'save': {'handler': {'success': 'handleSave'},
+            'id': 'form-buttons-save',
+            'name': 'form.buttons.save',
+            'text': u'Save',
+            'title': u'Save',
+            'url': 'http://127.0.0.1/index.html/@@ajax/save',
+            'xtype': 'button'}}
+
+This also works in list form as well:
+
+    >>> pprint(component.getAjaxButtonsConfig(myForm, asDict=False))
+    [{'handler': {'success': 'handleSave'},
+      'id': 'form-buttons-save',
+      'name': 'form.buttons.save',
+      'text': u'Save',
+      'title': u'Save',
+      'url': 'http://127.0.0.1/index.html/@@ajax/save',
+      'xtype': 'button'}]
+
+
+    >>> print myForm.script()
+    z3c.formext.ModuleLoader.load(
+    'z3c.formext.form',
+    function(){
+      if (config.ownerCt){
+        var container = Ext.getCmp(config.ownerCt);
+        delete config.ownerCt;
+        container.add(new z3c.formext.form.Z3CFormPanel(config));
+        container.doLayout();
+      } else if (config.renderTo){
+        new z3c.formext.form.Z3CFormPanel(config);
+      }
+    });
+
+    >>> print myForm.scriptTag
+    <script type="text/javascript" language="Javascript">
+      (function(){
+        var config={"xtype": "formpanel", "title": "My Contact Form", "items": ...
+        var foo="the foo property";
+    z3c.formext.ModuleLoader.load(
+      'z3c.formext.form',
+      function(){
+        if (config.ownerCt){
+          var container = Ext.getCmp(config.ownerCt);
+          delete config.ownerCt;
+          container.add(new z3c.formext.form.Z3CFormPanel(config));
+          container.doLayout();
+        } else if (config.renderTo){
+          new z3c.formext.form.Z3CFormPanel(config);
+        }
+      });
+    })();
+    </script>


Property changes on: z3c.formext/trunk/src/z3c/formext/form.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/interfaces.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/interfaces.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/interfaces.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,102 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""ExtJS integration.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema
+
+from z3c.form.interfaces import ITextWidget
+from z3c.form.interfaces import ISingleCheckBoxWidget
+from z3c.form.interfaces import IForm
+from z3c.form.interfaces import IButton
+from z3c.form.interfaces import IButtonAction
+from z3c.form.interfaces import ISelectionManager
+from z3c.formjs.interfaces import IAJAXRequestHandler
+from z3c.versionedresource.interfaces import IVersionedResource
+
+
+class IClientButton(IButton):
+    success = zope.schema.ASCIILine(
+        title=u'Success function',
+        required=False)
+
+    failure = zope.schema.ASCIILine(
+        title=u'Failure function',
+        required=False)
+
+class IClientButtonAction(IButtonAction):
+    success = zope.schema.ASCIILine(
+        title=u'Success function',
+        required=False)
+
+    failure = zope.schema.ASCIILine(
+        title=u'Failure function',
+        required=False)
+
+
+class IJSModule(IVersionedResource):
+    """A javascript module."""
+
+
+class IExtJSDateWidget(ITextWidget):
+    """Marker interface for an ExtJSDate widget.
+
+    When ExtJS sends back data for a date, it does not fit the
+    standard l10n locale specific date format.  This Widget thus has a
+    special data converter associated with it that handles the format
+    given by ExtJS.
+    """
+
+
+class IExtJSSingleCheckBoxWidget(ISingleCheckBoxWidget):
+    """Marker interface for an ExtJSSingleChexkBox widget.
+
+    When ExtJS sends back data for a checkbox, it does not fit the
+    standard checkbox format.  This Widget thus has a special data
+    converter associated with it that handles the format given by
+    ExtJS.
+    """
+
+
+class IExtJSComponent(zope.interface.Interface):
+    """An object that represents and ExtJS component.
+
+    This object provides a serialized representation of itself that
+    can be used as the config object for an extjs component.
+    """
+
+    def getConfig(json=False):
+        """Return the configuration object of this component.
+
+        If the json flag is passed, the result is returned in json
+        serialization form.
+        """
+
+class IJSProperties(ISelectionManager):
+    """A Selection Manager for JavaScript Properties"""
+
+
+class IExtJSForm(IAJAXRequestHandler, IForm):
+
+    jsonResponse = zope.schema.Field(
+        title=u'JSON Response',
+        description=u'The response data structure')
+
+    response = zope.schema.Text(
+        title=u'Response',
+        description=u'A json encoded response')


Property changes on: z3c.formext/trunk/src/z3c/formext/interfaces.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/jsmodule.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/jsmodule.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/jsmodule.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,68 @@
+##############################################################################
+#
+# Copyright (c) 2008 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.
+#
+##############################################################################
+"""Versioned Resources Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import cjson
+from zope.publisher.interfaces import NotFound
+from zope.app.publisher.browser import resource, resources
+from zope.app.publisher.browser import directoryresource
+from zope.app.publisher.browser import fileresource
+from zope.app.publisher.browser import pagetemplateresource
+from zope.viewlet.viewlet import ViewletBase
+from zope.viewlet.viewlet import JavaScriptViewlet
+from z3c.versionedresource import interfaces
+from z3c.versionedresource.resource import FileResource
+
+from z3c.formext import interfaces
+
+class JSModuleResourceFactory(fileresource.FileResourceFactory):
+    resourceClass = FileResource
+
+    def __init__(self, path, checker, name, namespace, dependencies):
+        super(JSModuleResourceFactory, self).__init__(path, checker, name)
+        self.namespace = namespace
+        self.dependencies = dependencies
+
+    def __call__(self, request):
+        resource = super(JSModuleResourceFactory, self).__call__(request)
+        resource.namespace = self.namespace
+        resource.dependencies = self.dependencies
+        return resource
+
+
+class JSModulesViewlet(ViewletBase):
+
+    def render(self):
+        result = {}
+        for name, resource in zope.component.getAdapters((self.request,),
+                                                         interfaces.IJSModule):
+            result[resource.namespace] = {
+                "name":resource.namespace,
+                "scripts":[resource()],
+                "requires":resource.dependencies,
+                "url":resource(),
+                "dependencies":resource.dependencies}
+        return ('<script type="text/javascript">'
+                'Ext.ns("z3c.formext"); '
+                'z3c.formext.JS_MODULES = %s;'
+                'for (pkg in z3c.formext.JS_MODULES){'
+                'z3c.formext.ModuleLoader.register(z3c.formext.JS_MODULES[pkg]);'
+                '}'
+                '</script>' % cjson.encode(result))
+
+LoaderViewlet = JavaScriptViewlet('z3c.formext.loader.js')


Property changes on: z3c.formext/trunk/src/z3c/formext/jsmodule.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/jsoncompat.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/jsoncompat.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/jsoncompat.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,54 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""ExtJS Component representation.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+try:
+    # the fast way
+    import cjson
+    encode = cjson.encode
+    jsonDecode = cjson.decode
+except ImportError:
+    try:
+        # the python 2.6 way
+        import json
+        encode = json.dumps
+        jsonDecode = json.loads
+    except ImportError:
+        # the slow python < 2.6 way
+        import simplejson
+        encode = simplejson.dumps
+        jsonDecode = simplejson.loads
+
+from zope.i18n import translate
+
+def translateObject(o):
+    if isinstance(o, list):
+        for index, value in enumerate(o):
+            o[index] = translateObject(value)
+    elif isinstance(o, tuple):
+        o = [translateObject(value) for value in o]
+    elif isinstance(o, dict):
+        for key, value in o.items():
+            o[key] = translateObject(value)
+    elif isinstance(o, unicode):
+        o = translate(o)
+    return o
+
+def jsonEncode(o):
+    o = translateObject(o)
+    return encode(o)


Property changes on: z3c.formext/trunk/src/z3c/formext/jsoncompat.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/jsoncompat.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/jsoncompat.txt	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/jsoncompat.txt	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,56 @@
+=================================
+JSON Encoder Compatibility Module
+=================================
+
+Different versions of python have different json encoding modules
+available to them.  There is also c-optimized json encoder optionally
+available.  The ``z3c.formext.jsoncompat`` module picks the best
+available json encoder.
+
+
+i18n support
+------------
+
+Since much of the json that gets produced ends up being used in the
+UI, it's important that all translatable strings are translated before
+being sent to the encoder (which doesn't know about translation).  The
+``jsonEncode`` function in ``z3c.formext.jsoncompat`` translates all
+the messages before passing them on to the json encoder.  Let's try
+this out.
+
+
+  >>> from zope.i18n.simpletranslationdomain import SimpleTranslationDomain
+  >>> test = SimpleTranslationDomain('test',
+  ...   {
+  ...     ('es', u'Hello'): u'Hola',
+  ...     ('es', u"What's up"): u'Que pasa',
+  ...     ('es', u'How are you'): u'Como estas',
+  ...   })
+
+  >>> from zope.component import provideUtility
+  >>> provideUtility(test, name='test')
+
+  >>> from zope.i18nmessageid.message import MessageFactory
+  >>> _ = MessageFactory('test')
+
+  >>> data = {
+  ...   'aList': [_(u'Hello'), _(u"What's up"), _(u'How are you')],
+  ...   'aTuple':(_(u'Hello'), _(u"What's up"), _(u'How are you')),
+  ...   'aNest': ([{'foo':_(u'How are you')}, _('Hello')], _(u"What's up")),
+  ...   }
+
+  >>> from z3c.formext import jsoncompat
+  >>> import zope.i18n
+  >>> jsoncompat.translate = lambda s: zope.i18n.translate(s, target_language='es')
+
+  >>> zope.i18n.translate(_(u'Hello'), target_language='es')
+  u'Hola'
+
+
+  >>> from pprint import pprint as pp
+  >>> pp(jsoncompat.jsonDecode(jsoncompat.jsonEncode(data)))
+  {'aList': ['Hola', 'Que pasa', 'Como estas'],
+   'aNest': [[{'foo': 'Como estas'}, 'Hola'], 'Que pasa'],
+   'aTuple': ['Hola', 'Que pasa', 'Como estas']}
+
+  >>> jsoncompat.translate = zope.i18n.translate
\ No newline at end of file


Property changes on: z3c.formext/trunk/src/z3c/formext/jsoncompat.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/meta.zcml
===================================================================
--- z3c.formext/trunk/src/z3c/formext/meta.zcml	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/meta.zcml	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,16 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:meta="http://namespaces.zope.org/meta"
+    xmlns:zcml="http://namespaces.zope.org/zcml">
+
+  <meta:directives namespace="http://namespaces.zope.org/z3c">
+
+    <meta:directive
+        name="jsmodule"
+        schema="z3c.formext.metadirectives.IJSModuleDirective"
+        handler=".zcml.jsmoduleHandler"
+        />
+
+  </meta:directives>
+
+</configure>


Property changes on: z3c.formext/trunk/src/z3c/formext/meta.zcml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/metadirectives.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/metadirectives.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/metadirectives.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Copyright (c) 2008 Zope Corporation 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.
+#
+##############################################################################
+"""jsmodule meta directives
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.app.publisher.browser.metadirectives import IBasicResourceInformation
+from zope.schema import TextLine
+from zope.configuration.fields import Path, Tokens
+
+class IJSModuleDirective(IBasicResourceInformation):
+    """
+    Defines a browser resource
+    """
+
+    name = TextLine(
+        title=u"The name of the resource",
+        description=u"""
+        This is the name used in resource urls. Resource urls are of
+        the form site/@@/resourcename, where site is the url of
+        "site", a folder with a site manager.
+
+        We make resource urls site-relative (as opposed to
+        content-relative) so as not to defeat caches.""",
+        required=False
+        )
+
+    namespace = TextLine(
+        title=u"The namespace of the JavaScript module",
+        description=u'''
+        This is the namespace used to reference the javascript module
+        in module loaders.  If not specified it will be extracted from
+        the file by looking for Ext.ns("my.module.namespace");
+        declarations.''',
+        required=False
+        )
+
+    dependencies = Tokens(
+        title=u"Other modules that this module depends on",
+        description=u'''
+        A whitespace-separated list of other modules that this module
+        depends on.
+        ''',
+        value_type=TextLine(),
+        required=False
+        )
+
+    file = Path(
+        title=u"File",
+        description=u"The file containing the JavaScript Module.",
+        required=False
+        )


Property changes on: z3c.formext/trunk/src/z3c/formext/metadirectives.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/resources/form-script.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/resources/form-script.js	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/resources/form-script.js	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,12 @@
+z3c.formext.ModuleLoader.load(
+  'z3c.formext.form',
+  function(){
+    if (config.ownerCt){
+      var container = Ext.getCmp(config.ownerCt);
+      delete config.ownerCt;
+      container.add(new z3c.formext.form.Z3CFormPanel(config));
+      container.doLayout();
+    } else if (config.renderTo){
+      new z3c.formext.form.Z3CFormPanel(config);
+    }
+  });
\ No newline at end of file


Property changes on: z3c.formext/trunk/src/z3c/formext/resources/form-script.js
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,85 @@
+Ext.ns('z3c.formext.form');
+
+(function(){
+
+var mod = z3c.formext.form;
+
+mod.Z3CFormPanel = Ext.extend(
+  Ext.FormPanel,
+  {
+    initComponent: function(){
+      this.items = [
+        {
+          xtype: 'panel',
+          hidden: true,
+          cls: 'x-form-invalid-msg',
+          id: this.id+'-errors'
+        }].concat(this.items || []);
+
+      if (this.buttons){
+        for (var i=0; i < this.buttons.length; i++){
+          var button = this.buttons[i];
+          if (typeof button.handler === 'object'){
+            var thisForm = this;
+            //damn, we have to do a copy.  hopefully a shallow copy is good enough.
+            button.handlerConfig = {};
+            for (var prop in button.handler){
+              button.handlerConfig[prop] = button.handler[prop];
+            }
+            if (typeof button.handlerConfig.success === 'string'){
+              button.handlerConfig.success = this.handlers[button.handlerConfig.success];
+            }
+            if (typeof button.handlerConfig.failure === 'string'){
+              button.handlerConfig.failure = this.handlers[button.handlerConfig.failure];
+            }
+            Ext.applyIf(
+              button.handlerConfig,
+              {
+                url: this.ajaxHandlers[button.id],
+                failure: this.handleErrors,
+                success: function(){}
+              });
+            button.handler = function(){
+              this.handlerConfig.method = thisForm.getForm().getValues(true) ? 'POST':'GET';
+              thisForm.getForm().doAction(
+                'submit',
+                this.handlerConfig
+              );
+            };
+          }
+        }
+
+      }
+      mod.Z3CFormPanel.superclass.initComponent.call(this);
+    },
+
+    afterRender: function(){
+      mod.Z3CFormPanel.superclass.afterRender.call(this);
+      this.getForm().items.each(
+        function(field){
+          if (field.title){
+            field.on(
+              'render',
+              function(){
+                Ext.QuickTips.register(
+                  {
+                    target: this.getEl(),
+                    text: this.title
+                  });
+
+              }, field, {single:true});
+          }
+        });
+    },
+
+    handleErrors: function(form, action){
+      var errors = Ext.getCmp(this.id+'-errors');
+      if (errors) {
+        errors.getEl().update(action.result.formErrors.join('\n'));
+        errors.show();
+      }
+    }
+  });
+Ext.reg('z3c-formpanel', mod.Z3CFormPanel);
+
+})();


Property changes on: z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,136 @@
+//see http://extjs.com/forum/showthread.php?t=34808
+/**
+ * Javascript ResourceManager definition inc preloaders
+ * @author Adam J Benson
+ * @todo Add in the CSS on demand loaders
+ * @version 1.3.1
+ *
+ * EXAMPLE PACKAGE DEFINITION: (All packages should be defined prior to trying to load them)
+ *
+ * var pack={
+ *    name: "ExamplePack",
+ *    description:"This is an example 'package object' which is used by the resource managers",
+ *    scripts:["url1","url2","url3","url4"],
+ *    callback: function(){},
+ *    requires:["packageName"] //Dependancies for this package to be loaded before it...
+ * }
+ *
+ * EXAMPLE USAGE (with callback)
+ * ScriptManager('Common', function(){
+ *   //YOUR CODE HERE
+ *   var simple = new Ext.FormPanel();
+ * });
+ *
+ * EXAMPLE USAGE (without callback)
+ * ScriptManager('Common');
+ *
+ */
+Ext.ns('z3c.formext');
+
+var ScriptManager = window.ScriptManager = function( packageName, callback ) {
+  /**
+   * Constructor
+   */
+  ScriptManager.load(packageName,callback);
+};
+
+z3c.formext.ModuleLoader = ScriptManager;
+
+ScriptManager.prototype = {
+  registeredPackages:[], //List of packages that have been registered
+  loadQueue:[], //Queue of packages that have been requested to load (we load sequentially to avoid dependancy issues)
+  processing: false,
+
+  register: function(packageConfig){
+    //Register the package for loading
+    this.registeredPackages[packageConfig.name]=packageConfig;
+    this.registeredPackages[packageConfig.name].loaded=false;
+  },
+
+  load: function(packageName, callback) {
+    /**
+     * Load the specified package, with an optional callback (in addition to the callback configured by the packages config)
+     */
+    if(!this.registeredPackages[packageName]){
+      //Package Not Found
+      if (console && console.log){
+        console.log("The package "+packageName+" has not been registered but was requested by a script.");
+      }
+      return;
+    }
+    if(this.registeredPackages[packageName].loaded==true){
+      //Already loaded... Trigger the scripted callback
+      ScriptManager.prototype.nextQueueItem();
+      if(callback)callback.call();
+      return;
+    }
+    if(this.registeredPackages[packageName].requires){
+      //Required Scripts
+      var required=this.registeredPackages[packageName].requires;
+      for(var i=0, m=required.length; i < m;i++){
+        if(this.registeredPackages[required[i]].loaded!=true){
+          //There is a dependant not loaded, so add self to the queue and load it instead...
+
+          /* the original author pushed the callback function onto the
+          queue.  But then the callback would be called for each
+          requirement being loaded.  Now we pass in an empty function
+          as the call back so only the requested package's load does
+          the callback */
+          this.loadQueue.push([packageName,/*callback*/ function(){}]);
+          this.load(required[i]);
+        }
+      }
+    }
+    if(ScriptManager.processing){
+      //Already proccessing a script (or document body hasn't finished loading)! Add it to the queue...
+      this.loadQueue.push([packageName,callback]);
+      return;
+    }
+    ScriptManager.processing=true;
+    ScriptManager.srcScript(this.registeredPackages[packageName], callback);
+  },
+  genScriptNode : function() {
+    var scriptNode = document.createElement("script");
+    scriptNode.setAttribute("type", "text/javascript");
+    return scriptNode;
+  },
+  srcScript : function(packageConfig, callback) {
+    var scriptNode = ScriptManager.prototype.genScriptNode();
+    scriptNode.setAttribute("src", packageConfig.scripts[0]);
+    scriptNode.onload = scriptNode.onreadystatechange = function() {
+      if (!scriptNode.readyState || scriptNode.readyState == "loaded" || scriptNode.readyState == "complete" ||
+          scriptNode.readyState == 4 && scriptNode.status == 200) {
+        setTimeout(
+          function(){
+            ScriptManager.registeredPackages[packageConfig.name].loaded=true;
+            ScriptManager.prototype.scriptLoaded();
+            if(packageConfig.callback)packageConfig.callback.call();
+            if(callback)callback.call();
+          }, 200);
+      }
+    };
+    var headNode = document.getElementsByTagName("head")[0];
+    headNode.appendChild(scriptNode);
+  },
+  nextQueueItem : function(){
+    if(this.loadQueue.length > 0){
+      var currentItem=this.loadQueue.shift();
+      ScriptManager.prototype.load(currentItem[0],currentItem[1]);
+    }
+
+  },
+  scriptLoaded : function(){
+    /**
+     * Callback function for whenever a script finishes loading
+     */
+    ScriptManager.processing=false;
+    ScriptManager.prototype.nextQueueItem();
+  }
+
+};
+ScriptManager.register = ScriptManager.prototype.register;
+ScriptManager.load = ScriptManager.prototype.load;
+ScriptManager.loadQueue = ScriptManager.prototype.loadQueue;
+ScriptManager.registeredPackages = ScriptManager.prototype.registeredPackages;
+ScriptManager.srcScript = ScriptManager.prototype.srcScript;
+ScriptManager.nextQueueItem=ScriptManager.prototype.nextQueueItem();
\ No newline at end of file


Property changes on: z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/testing.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/testing.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/testing.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Test Setup.
+
+$Id$
+"""
+import zope.component
+import zope.interface
+from zope.traversing.testing import setUp as setupTraversing
+from zope.traversing.interfaces import IContainmentRoot
+
+import z3c.form.testing
+from z3c.form.interfaces import IButtonAction
+from z3c.form.testing import setupFormDefaults
+from z3c.formext import component, form, converter
+
+TestRequest = z3c.form.testing.TestRequest
+
+def setupExtJSComponents():
+    zope.component.provideAdapter(component.TextField)
+    zope.component.provideAdapter(component.TextArea)
+    zope.component.provideAdapter(component.DateField)
+    zope.component.provideAdapter(component.FormPanel)
+    zope.component.provideAdapter(component.ExtFormPanel)
+    zope.component.provideAdapter(component.ComboBox)
+    zope.component.provideAdapter(component.CheckBox)
+    zope.component.provideAdapter(component.RadioGroup)
+    zope.component.provideAdapter(component.Button)
+    zope.component.provideAdapter(component.ClientButton)
+    zope.component.provideAdapter(form.ClientButtonAction,
+                                  provides=IButtonAction)
+
+def setupFormExt():
+    setupExtJSComponents()
+    setupFormDefaults()
+    setupTraversing()
+    zope.component.provideAdapter(converter.ExtJSDateDataConverter)
+    zope.component.provideAdapter(converter.ExtJSSingleCheckBoxDataConverter)
+
+
+class Context(object):
+    zope.interface.implements(IContainmentRoot)
+    __name__ = ''
+
+class TestingForm(object):
+
+    __name__ = 'index.html'
+
+    def getContent(self):
+        if self.context is None:
+            self.context = Context()
+        return self.context


Property changes on: z3c.formext/trunk/src/z3c/formext/testing.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/tests/__init__.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/__init__.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/__init__.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+# Make a package.


Property changes on: z3c.formext/trunk/src/z3c/formext/tests/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,5 @@
+Ext.ns("my.module");
+
+my.module.someFunc = function(){
+  alert("this function lives in my.module!");
+};
\ No newline at end of file


Property changes on: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,2 @@
+//a different js module that depends on my.module
+Ext.ns("my.other");
\ No newline at end of file


Property changes on: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+/* an example js file from a third party that does not specify a namespace. */
\ No newline at end of file


Property changes on: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/tests/script.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/script.js	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/script.js	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+alert("foo!");
\ No newline at end of file


Property changes on: z3c.formext/trunk/src/z3c/formext/tests/script.js
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/tests/test_doc.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/test_doc.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/test_doc.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,64 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""z3c.form Test Module
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import unittest
+import re
+
+from zope.testing import doctest, renormalizing
+from zope.app.testing import placelesssetup
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+from z3c.form import testing
+
+class ITestLayer(IDefaultBrowserLayer):
+    pass
+
+def test_suite():
+    return unittest.TestSuite((
+
+        doctest.DocFileSuite(
+            '../README.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+
+        doctest.DocFileSuite(
+            '../zcml.txt',
+            setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+
+        doctest.DocFileSuite(
+            '../jsoncompat.txt',
+            setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+
+        doctest.DocFileSuite(
+            '../component.txt',
+            setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+
+        doctest.DocFileSuite(
+            '../form.txt',
+            setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+
+        ))


Property changes on: z3c.formext/trunk/src/z3c/formext/tests/test_doc.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/widget.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/widget.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/widget.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,56 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""ExtJS Widgets.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema.interfaces
+import zope.component
+
+from z3c.form.browser.checkbox import SingleCheckBoxWidget
+from z3c.form.browser.text import TextWidget
+from z3c.form.interfaces import IFieldWidget
+from z3c.form.interfaces import IFormLayer
+from z3c.form.interfaces import ITextWidget
+from z3c.form.interfaces import NOVALUE
+from z3c.form.widget import FieldWidget, Widget
+
+from z3c.formext import interfaces
+
+class ExtJSDateWidget(TextWidget):
+    zope.interface.implementsOnly(interfaces.IExtJSDateWidget)
+
+ at zope.component.adapter(zope.schema.interfaces.IField, IFormLayer)
+ at zope.interface.implementer(IFieldWidget)
+def ExtJSDateFieldWidget(field, request):
+    """IFieldWidget factory for TextWidget."""
+    return FieldWidget(field, ExtJSDateWidget(request))
+
+
+
+class ExtJSSingleCheckBoxWidget(SingleCheckBoxWidget):
+    zope.interface.implementsOnly(interfaces.IExtJSSingleCheckBoxWidget)
+
+    def extract(self, default=u'off'):
+        return Widget.extract(self, default=default)
+
+
+ at zope.component.adapter(zope.schema.interfaces.IField, IFormLayer)
+ at zope.interface.implementer(IFieldWidget)
+def ExtJSSingleCheckBoxFieldWidget(field, request):
+    """IFieldWidget factory for TextWidget."""
+    return FieldWidget(field, ExtJSSingleCheckBoxWidget(request))


Property changes on: z3c.formext/trunk/src/z3c/formext/widget.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/zcml.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/zcml.py	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/zcml.py	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Copyright (c) 2008 Zope Corporation 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.
+#
+##############################################################################
+"""jsmodule directives
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+import os
+import re
+
+from zope.app.publisher.browser.pagetemplateresource import PageTemplateResourceFactory
+from zope.app.publisher.browser.resourcemeta import ResourceFactoryWrapper
+from zope.app.publisher.browser.resourcemeta import allowed_names
+from zope.component.zcml import handler
+from zope.configuration.exceptions import ConfigurationError
+from zope.interface import Interface
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+from zope.security.checker import CheckerPublic, NamesChecker
+
+from z3c.formext import interfaces, jsmodule
+from z3c.versionedresource.resource import FileResourceFactory
+
+
+EXTNamespaceRE = "((?<=Ext.ns\((\"|\'))|(?<=Ext.namespace\((\"|\'))).*(?=(\"|\'))"
+
+def jsmoduleHandler(_context, file, layer=IDefaultBrowserLayer,
+                    permission='zope.Public', name=None,
+                    namespace=None, dependencies=None):
+
+    if permission == 'zope.Public':
+        permission = CheckerPublic
+
+    checker = NamesChecker(allowed_names, permission)
+
+    if dependencies is None:
+        dependencies = []
+
+    if namespace is None:
+        contents = open(file, 'r').read()
+        match = re.search(EXTNamespaceRE, contents)
+        if not match:
+            raise ConfigurationError(
+                "No namespace was specified and no namespace could be extracted")
+        namespace = contents[match.start() : match.end()]
+    if name is None:
+        name = '%s.js' % namespace
+
+    factory = jsmodule.JSModuleResourceFactory(file, checker, name,
+                                               namespace, dependencies)
+
+    _context.action(
+        discriminator = ('resource', name, IBrowserRequest, layer),
+        callable = handler,
+        args = ('registerAdapter', factory, (layer,),
+                interfaces.IJSModule, name, _context.info),
+        )


Property changes on: z3c.formext/trunk/src/z3c/formext/zcml.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Added: z3c.formext/trunk/src/z3c/formext/zcml.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/zcml.txt	                        (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/zcml.txt	2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,238 @@
+==================
+JavaScript Modules
+==================
+
+When writing a big application built on top of ExtJS, you often don't
+want to load *all* the client JavaScript at the same time.  Instead,
+it is much better to load only the javascript necessary to run a
+particular part of the application.  Unfortunately, JavaScript itself
+has no concept of a module which can be imported as needed, so the
+server side application must provide this concept.
+
+Here we provide a ``jsmodule`` zcml directive that allows you to
+declare a javascript file as a module (and also as a versioned
+resource).  The idea is that by declaring a bunch of javascript
+modules up front, we can generate a dependency graph that can be sent
+to the client for use by a client side lazy module loader.
+
+Setup
+~~~~~
+
+In order to test the zcml directives, we have to do some setup.  First
+lets set the path to some of our testing files.
+
+  >>> import os.path
+  >>> import z3c.formext.tests
+  >>> modulesdir = os.path.join(
+  ...     os.path.dirname(z3c.formext.tests.__file__),
+  ...     'jsmodules')
+
+Since our jsmodules are just another incarnation of a versioned
+resource, we have to set up the version manager.
+
+  >>> from z3c.versionedresource import version
+  >>> manager = version.VersionManager('1.0.0')
+  >>> import zope.component
+  >>> zope.component.provideUtility(manager)
+
+Now we will create a resources view so we can traverse to the
+resources we will be creating.
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> from z3c.versionedresource import resource
+  >>> request = TestRequest()
+  >>> context = object()
+  >>> resources = resource.Resources(context, request)
+
+Finally, we have to set up the zcml context by loading the meta.zcml
+file:
+
+  >>> from zope.configuration import xmlconfig
+  >>> import z3c.formext
+  >>> context = xmlconfig.file('meta.zcml', z3c.formext)
+
+
+ZCML Directive
+~~~~~~~~~~~~~~
+
+Simple Modules
+--------------
+
+Using the ``jsmodule`` directive, we can now register a javascript
+module.  All we need to specify is the file.
+
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:jsmodule
+  ...       file="%s"
+  ...       />
+  ... </configure>
+  ... """ % os.path.join(modulesdir, 'my.module.js') , context=context)
+
+We want all of our modules to have a namespace associated with them so
+they can be sanely referenced within the dependency graph and by the
+client-side lazy loader.  If we do not specify the ``namespace``
+attribute in the zcml, as we did in this case, then the namespace will
+be extracted from the file.  See the *Extracting Namespaces* section
+for how this works.  Now the javascript resource can be accessed using
+a name generated from the extracted namespace (which just happens to
+be the same as the file name.
+
+  >>> moduleResource = resources.publishTraverse(request, '1.0.0')\
+  ...                           .publishTraverse(request, 'my.module.js')
+
+The resource we get is just a plain FileResource object except that it
+contains some additional metadata used by the dependency graph
+generator.  In particular, we store the namespace and dependencies
+associated with this file resource.
+
+  >>> moduleResource
+  <FileResource u'.../z3c/formext/tests/jsmodules/my.module.js'>
+  >>> moduleResource.namespace
+  'my.module'
+  >>> moduleResource.dependencies
+  []
+  >>> moduleResource()
+  'http://127.0.0.1/@@/1.0.0/my.module.js'
+
+
+Modules with dependencies
+-------------------------
+
+We can also declar that a jsmodule has dependencies on other modules
+by specifying the ``dependencies`` attribute.
+
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:jsmodule
+  ...       file="%s"
+  ...       dependencies="my.module jQuery"
+  ...       />
+  ... </configure>
+  ... """ % os.path.join(modulesdir, 'my.other.js') , context=context)
+
+This attribtute gets converted into a list.
+
+  >>> moduleResource = resources.publishTraverse(request, '1.0.0')\
+  ...                           .publishTraverse(request, 'my.other.js')
+  >>> moduleResource.dependencies
+  [u'my.module', u'jQuery']
+
+
+Third Party Modules
+-------------------
+
+In cases where we are using third party libraries which do not have
+namespaces defined within the file.  We must specify the namespace
+manually.  One example where the namespace extraction won't work is
+for jQuery.  If the namespace extraction fails and we do not specify a
+namepsace manually, we get a configuration error:
+
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:jsmodule
+  ...       file="%s"
+  ...       />
+  ... </configure>
+  ... """ % os.path.join(modulesdir, 'thirdParty.js') , context=context)
+  Traceback (most recent call last):
+  ...
+  ZopeXMLConfigurationError: File "<string>", line 4.2-6.8
+      ConfigurationError: No namespace was specified and no namespace could be extracted
+
+We know however that jQuery uses the ``jQuery`` namespace so we can
+declare that manually and no namespace extraction will be attempted.
+
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:jsmodule
+  ...       file="%s"
+  ...       namespace="jQuery"
+  ...       />
+  ... </configure>
+  ... """ % os.path.join(modulesdir, 'thirdParty.js') , context=context)
+  >>> resources.publishTraverse(request, '1.0.0')\
+  ...          .publishTraverse(request, 'jQuery.js')()
+  'http://127.0.0.1/@@/1.0.0/jQuery.js'
+
+Note that the name of the resource is based on the namespace and *not*
+the filename.  You can also specify a ``name`` attribtue manually, but
+this is not recommended.
+
+Listing ``JSModule``s
+---------------------
+
+Now that we have a couple JavaScript modules registered, we can get a
+list of all the modules pretty easily:
+
+  >>> import zope.component
+  >>> from z3c.formext import interfaces
+  >>> for name, resource in zope.component.getAdapters((request,), interfaces.IJSModule):
+  ...   print resource.namespace.ljust(10), str(resource.dependencies).ljust(25), resource()
+  jQuery     []                        http://127.0.0.1/@@/1.0.0/jQuery.js
+  my.module  []                        http://127.0.0.1/@@/1.0.0/my.module.js
+  my.other   [u'my.module', u'jQuery'] http://127.0.0.1/@@/1.0.0/my.other.js
+
+The ``JSModulesViewlet``
+------------------------
+
+We can also render a viewlet that makes all the jsmodule information
+available on the client.
+
+  >>> from z3c.formext import jsmodule
+  >>> viewlet = jsmodule.JSModulesViewlet(
+  ...     'fake context', request,
+  ...     'fake view','fake manager')
+  >>> print viewlet.render()
+  <script type="text/javascript">Ext.ns("z3c.formext"); z3c.formext.JS_MODULES = {"jQuery": {"url": "http://127.0.0.1/@@/1.0.0/jQuery.js", "dependencies": [], "requires": [], "name": "jQuery", "scripts": ["http://127.0.0.1/@@/1.0.0/jQuery.js"]}, "my.other": {"url": "http://127.0.0.1/@@/1.0.0/my.other.js", "dependencies": ["my.module", "jQuery"], "requires": ["my.module", "jQuery"], "name": "my.other", "scripts": ["http://127.0.0.1/@@/1.0.0/my.other.js"]}, "my.module": {"url": "http://127.0.0.1/@@/1.0.0/my.module.js", "dependencies": [], "requires": [], "name": "my.module", "scripts": ["http://127.0.0.1/@@/1.0.0/my.module.js"]}};for (pkg in z3c.formext.JS_MODULES){z3c.formext.ModuleLoader.register(z3c.formext.JS_MODULES[pkg]);}</script>
+
+
+Extracting Namespaces
+~~~~~~~~~~~~~~~~~~~~~
+
+In ExtJS, we can declare namespaces using the ``Ext.ns`` or
+``Ext.namespace`` functions.  Namespaces for JavaScript modules are
+extracted by looking for these patterns in the JavaScript file.
+
+  >>> from z3c.formext import zcml
+
+Here we test the usage of Ext.ns with double quotes,
+
+  >>> import re
+  >>> contents = """
+  ... Ext.ns("my.module");
+  ... """
+  >>> match = re.search(zcml.EXTNamespaceRE, contents)
+  >>> print contents[match.start(): match.end()]
+  my.module
+
+and single quotes:
+
+  >>> contents = """
+  ... Ext.ns('my.module');
+  ... """
+  >>> match = re.search(zcml.EXTNamespaceRE, contents)
+  >>> print contents[match.start(): match.end()]
+  my.module
+
+Here we test the usage of Ext.namespace with double quotes,
+
+  >>> contents = """
+  ... Ext.namespace("my.module");
+  ... """
+  >>> match = re.search(zcml.EXTNamespaceRE, contents)
+  >>> print contents[match.start(): match.end()]
+  my.module
+
+and single quotes:
+
+  >>> contents = """
+  ... Ext.namespace('my.module');
+  ... """
+  >>> match = re.search(zcml.EXTNamespaceRE, contents)
+  >>> print contents[match.start(): match.end()]
+  my.module


Property changes on: z3c.formext/trunk/src/z3c/formext/zcml.txt
___________________________________________________________________
Added: svn:eol-style
   + native



More information about the Checkins mailing list