[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/form/browser/
Provided a nifty JS-based widget that really allows ordered
selection
Stephan Richter
srichter at cosmos.phy.tufts.edu
Thu Oct 14 03:56:29 EDT 2004
Log message for revision 28147:
Provided a nifty JS-based widget that really allows ordered selection
lists instead of sets.
Thanks goes to LArs Heber for the JS.
Changed:
U Zope3/trunk/src/zope/app/form/browser/__init__.py
U Zope3/trunk/src/zope/app/form/browser/configure.zcml
U Zope3/trunk/src/zope/app/form/browser/itemswidgets.py
U Zope3/trunk/src/zope/app/form/browser/orderedSelectionList.pt
U Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py
-=-
Modified: Zope3/trunk/src/zope/app/form/browser/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/__init__.py 2004-10-14 07:54:04 UTC (rev 28146)
+++ Zope3/trunk/src/zope/app/form/browser/__init__.py 2004-10-14 07:56:27 UTC (rev 28147)
@@ -57,6 +57,7 @@
# These widgets are multi-views on (field, vocabulary)
from zope.app.form.browser.itemswidgets import MultiSelectWidget
from zope.app.form.browser.itemswidgets import MultiCheckBoxWidget
+from zope.app.form.browser.itemswidgets import OrderedMultiSelectWidget
from zope.app.form.browser.sequencewidget import SequenceWidget
from zope.app.form.browser.sequencewidget import TupleSequenceWidget
Modified: Zope3/trunk/src/zope/app/form/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/configure.zcml 2004-10-14 07:54:04 UTC (rev 28146)
+++ Zope3/trunk/src/zope/app/form/browser/configure.zcml 2004-10-14 07:56:27 UTC (rev 28147)
@@ -295,9 +295,11 @@
<!-- These widgets are minimal and only support lists with unique members,
without ordering capabilities -->
+
+ <!-- TODO: This will generate a list and not a set, so be careful!!!-->
<view
type="zope.publisher.interfaces.browser.IBrowserRequest"
- for="zope.schema.interfaces.IList
+ for="zope.schema.interfaces.ISet
zope.schema.interfaces.IVocabularyTokenized"
provides="zope.app.form.interfaces.IInputWidget"
factory=".MultiSelectWidget"
@@ -308,6 +310,15 @@
type="zope.publisher.interfaces.browser.IBrowserRequest"
for="zope.schema.interfaces.IList
zope.schema.interfaces.IVocabularyTokenized"
+ provides="zope.app.form.interfaces.IInputWidget"
+ factory=".OrderedMultiSelectWidget"
+ permission="zope.Public"
+ />
+
+ <view
+ type="zope.publisher.interfaces.browser.IBrowserRequest"
+ for="zope.schema.interfaces.IList
+ zope.schema.interfaces.IVocabularyTokenized"
provides="zope.app.form.interfaces.IDisplayWidget"
factory=".SetDisplayWidget"
permission="zope.Public"
Modified: Zope3/trunk/src/zope/app/form/browser/itemswidgets.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/itemswidgets.py 2004-10-14 07:54:04 UTC (rev 28146)
+++ Zope3/trunk/src/zope/app/form/browser/itemswidgets.py 2004-10-14 07:56:27 UTC (rev 28147)
@@ -27,6 +27,7 @@
from zope.app.form.browser.widget import SimpleInputWidget, renderElement
from zope.app.form.interfaces import IInputWidget, IDisplayWidget
from zope.app.form.interfaces import ConversionError
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
from zope.app.i18n import ZopeMessageIDFactory as _
@@ -523,7 +524,29 @@
class MultiSelectWidget(ItemsMultiEditWidgetBase):
"""Provide a selection list for the list to be selected."""
+class OrderedMultiSelectWidget(ItemsMultiEditWidgetBase):
+ """A multi-selection widget with ordering support."""
+ template = ViewPageTemplateFile('orderedSelectionList.pt')
+
+ def choices(self):
+ """Return a set of tuples (text, value) that are available."""
+ selected_values = self.context.get(self.context.context)
+ return [{'text': self.textForValue(term), 'value': term.token}
+ for term in self.vocabulary
+ if term.value not in selected_values]
+
+ def selected(self):
+ """Return a list of tuples (text, value) that are selected."""
+ terms = [self.vocabulary.getTerm(value)
+ for value in self.context.get(self.context.context)]
+ return [{'text': self.textForValue(term), 'value': term.token}
+ for term in terms]
+
+ def __call__(self):
+ return self.template()
+
+
class MultiCheckBoxWidget(ItemsMultiEditWidgetBase):
"""Provide a list of checkboxes that provide the choice for the list."""
@@ -558,3 +581,4 @@
value=value,
checked=None)
return self._joinButtonToMessageTemplate %(elem, text)
+
Modified: Zope3/trunk/src/zope/app/form/browser/orderedSelectionList.pt
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/orderedSelectionList.pt 2004-10-14 07:54:04 UTC (rev 28146)
+++ Zope3/trunk/src/zope/app/form/browser/orderedSelectionList.pt 2004-10-14 07:56:27 UTC (rev 28147)
@@ -1,91 +1,127 @@
-<html><head><title>Move items between selection boxes and sort them manually</title></head>
-<body>
-
-<form name="myForm">
-<table border="1">
- <tbody><tr>
- <td>
- <select name="from" size="5"><option>Anton</option><option>Berta</option><option>Cäsar</option><option>Dora</option><option>Emil</option></select>
- </td>
- <td>
- <p><button name="from2toButton" type="button" value=" ->" onclick="javascript:from2to()"> -></button></p>
- <p><button name="to2fromButton" type="button" value="<- " onclick="javascript:to2from()"><- </button></p>
- </td>
- <td>
- <select name="to" size="5"><option>Friedrich</option><option>Gustav</option><option>Heinrich</option></select>
- </td>
- <td>
- <p><button name="upButton" type="button" value="^" onclick="javascript:moveUp()">^</button></p>
- <p><button name="downButton" type="button" value="v" onclick="javascript:moveDown()">v</button></p>
- </td>
- </tr>
-</tbody></table>
-</form>
-
-
<script type="text/javascript">
-// shortcuts for selection fields
-fromSel = document.myForm.from;
-toSel = document.myForm.to;
-// move item from "from" selection to "to" selection
-function from2to()
+function moveItems(from, to)
{
- if (fromSel.selectedIndex == -1) selectionError();
+ // shortcuts for selection fields
+ var src = document.getElementById(from);
+ var tgt = document.getElementById(to);
+
+ if (src.selectedIndex == -1) selectionError();
else
{
- // need to create a new temporary object with values of item to copy
- // simple item moving between selection lists works fine
- // with Mozilla but doesn't work with IE *grrrhh*
- temp = new Option(fromSel.options[fromSel.selectedIndex].text, fromSel.options[fromSel.selectedIndex].value);
- toSel.options[toSel.length] = temp;
- // want to select newly created item
- temp.selected = true;
- // luckily, simple deletion of items DOES work in Mozilla and IE
- fromSel.options[fromSel.selectedIndex] = null;
+ // iterate over all selected items
+ // --> attribute "selectedIndex" doesn't support multiple selection.
+ // Anyway, it works here, as a moved item isn't selected anymore,
+ // thus "selectedIndex" indicating the "next" selected item :)
+ while (src.selectedIndex > -1)
+ if (src.options[src.selectedIndex].selected)
+ {
+ // create a new virtal object with values of item to copy
+ temp = new Option(src.options[src.selectedIndex].text,
+ src.options[src.selectedIndex].value);
+ // append virtual object to targe
+ tgt.options[tgt.length] = temp;
+ // want to select newly created item
+ temp.selected = true;
+ // delete moved item in source
+ src.options[src.selectedIndex] = null;
+ }
}
}
+// move item from "from" selection to "to" selection
+function from2to(name)
+ {
+ moveItems(name+".from", name+".to");
+ copyDataForSubmit(name);
+ }
+
// move item from "to" selection back to "from" selection
-function to2from()
+function to2from(name)
{
- if (toSel.selectedIndex == -1) selectionError();
- else
- {
- temp = new Option(toSel.options[toSel.selectedIndex].text, toSel.options[toSel.selectedIndex].value);
- fromSel.options[fromSel.length] = temp;
- temp.selected = true;
- toSel.options[toSel.selectedIndex] = null;
- }
- // fromSel.options[fromSel.length] = toSel.options[toSel.selectedIndex];
+ moveItems(name+".to", name+".from");
+ copyDataForSubmit(name);
}
+function swapFields(a, b)
+ {
+ // swap items
+ var temp = a.text;
+ a.text = b.text;
+ b.text = temp;
+ // swap selection
+ var temp = a.selected;
+ a.selected = b.selected;
+ b.selected = temp;
+ }
// move selected item in "to" selection one up
-function moveUp()
+function moveUp(name)
{
- if (toSel.selectedIndex == -1) selectionError();
- else if (toSel.selectedIndex < 1) alert("Cannot move further up!");
- else
+ // shortcuts for selection field
+ var toSel = document.getElementById(name+".to");
+
+ if (toSel.selectedIndex == -1)
+ selectionError();
+ else if (toSel.options[0].selected)
+ alert("Cannot move further up!");
+ else for (var i = 0; i < toSel.length; i++)
+ if (toSel.options[i].selected)
+ {
+ swapFields(toSel.options[i-1], toSel.options[i]);
+ copyDataForSubmit(name);
+ }
+ }
+
+// move selected item in "to" selection one down
+function moveDown(name)
+ {
+ // shortcuts for selection field
+ var toSel = document.getElementById(name+".to");
+
+ if (toSel.selectedIndex == -1)
+ selectionError();
+ else if (toSel.options[toSel.length-1].selected)
+ alert("Cannot move further down!");
+ else for (var i = toSel.length-1; i >= 0; i--)
+ if (toSel.options[i].selected)
{
- pos = toSel.selectedIndex; temp = toSel.options[pos-1].text;
- toSel.options[pos-1].text = toSel.options[pos].text;
- toSel.options[pos].text = temp;
- toSel.selectedIndex = pos-1;
+ swapFields(toSel.options[i+1], toSel.options[i]);
+ copyDataForSubmit(name);
}
}
-// move selected item in "to" selection one down
-function moveDown()
+// copy each item of "toSel" into one hidden input field
+function copyDataForSubmit(name)
{
- if (toSel.selectedIndex == -1) selectionError();
- else if (toSel.selectedIndex > toSel.length-2) alert("Cannot move further down!");
- else
+ // shortcuts for selection field and hidden data field
+ var toSel = document.getElementById(name+".to");
+ var toDataContainer = document.getElementById(name+".toDataContainer");
+
+ // delete all child nodes (--> complete content) of "toDataContainer" span
+ while (toDataContainer.hasChildNodes())
+ toDataContainer.removeChild(toDataContainer.firstChild);
+
+ // create new hidden input fields - one for each selection item of
+ // "to" selection
+ for (var i = 0; i < toSel.length; i++)
{
- pos = toSel.selectedIndex; temp = toSel.options[pos+1].text;
- toSel.options[pos+1].text = toSel.options[pos].text;
- toSel.options[pos].text = temp;
- toSel.selectedIndex = pos+1;
+ // create virtual node with suitable attributes
+ var newNode = document.createElement("input");
+ var newAttr = document.createAttribute("name");
+ newAttr.nodeValue = name;
+ newNode.setAttributeNode(newAttr);
+
+ newAttr = document.createAttribute("type");
+ newAttr.nodeValue = "hidden";
+ newNode.setAttributeNode(newAttr);
+
+ newAttr = document.createAttribute("value");
+ newAttr.nodeValue = toSel[i].text;
+ newNode.setAttributeNode(newAttr);
+
+ // actually append virtual node to DOM tree
+ toDataContainer.appendChild(newNode);
}
}
@@ -95,5 +131,57 @@
</script>
+<table border="0">
+ <tr>
+ <td>
+ <select id="from" name="from" size="5" multiple="" style="width:40%"
+ tal:attributes="name string:${view/name}.from;
+ id string:${view/name}.from">
+ <option tal:repeat="entry view/choices"
+ tal:attributes="value entry/value"
+ tal:content="entry/text" />
+ </select>
+ </td>
+ <td>
+ <button type="button" value=" ->"
+ onclick="javascript:from2to()"
+ tal:attributes="onClick string:javascript:from2to('${view/name}')"
+ > -></button>
+ <br />
+ <button name="to2fromButton" type="button" value="<- "
+ onclick="javascript:to2from()"
+ tal:attributes="onClick string:javascript:to2from('${view/name}')"
+ ><- </button>
+ </td>
+ <td>
+ <select id="to" name="to" size="5" multiple="" style="width:40%"
+ tal:attributes="name string:${view/name}.to;
+ id string:${view/name}.to">
+ <option tal:repeat="entry view/selected"
+ tal:attributes="value entry/value"
+ tal:content="entry/text" />
+ </select>
+ <span id="toDataContainer"
+ tal:attributes="id string:${view/name}.toDataContainer">
+ <script type="text/javascript">
+ // initial copying of field "field.to" --> "field"
+ copyDataForSubmit("<i tal:replace="${view/name}"/>");
+ </script>
+ </span>
+ </td>
+ <td>
+ <button
+ name="upButton" type="button" value="^"
+ onclick="javascript:moveUp()"
+ tal:attributes="onClick string:javascript:moveUp('${view/name}')"
+ >^</button>
+ <br />
+ <button
+ name="downButton" type="button" value="v"
+ onclick="javascript:moveDown()"
+ tal:attributes="onClick string:javascript:moveDown('${view/name}')"
+ >v</button>
+ </td>
+ </tr>
+</table>
-</body></html>
\ No newline at end of file
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py 2004-10-14 07:54:04 UTC (rev 28146)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py 2004-10-14 07:56:27 UTC (rev 28147)
@@ -33,6 +33,7 @@
from zope.app.form.browser.itemswidgets import RadioWidget
from zope.app.form.browser.itemswidgets import ItemsMultiEditWidgetBase
from zope.app.form.browser.itemswidgets import MultiSelectWidget
+from zope.app.form.browser.itemswidgets import OrderedMultiSelectWidget
from zope.app.form.browser.itemswidgets import MultiCheckBoxWidget
from zope.app.form.browser.tests.support import VerifyResults
from zope.app.tests.placelesssetup import PlacelessSetup
@@ -416,6 +417,23 @@
_widget = MultiSelectWidget
+class OrderedMultiSelectWidgetTest(ItemsMultiEditWidgetBaseTest):
+
+ _widget = OrderedMultiSelectWidget
+
+ def test_choices(self):
+ widget = self._makeWidget()
+ choices = [choice['text'] for choice in widget.choices()]
+ choices.sort()
+ self.assertEqual(choices, ['One', 'Three', 'Two'])
+
+ def test_selected(self):
+ widget = self._makeWidget(nums=['one'])
+ selected = [select['text'] for select in widget.selected()]
+ selected.sort()
+ self.assertEqual(selected, ['One'])
+
+
class MultiCheckBoxWidgetTest(ItemsMultiEditWidgetBaseTest):
_widget = MultiCheckBoxWidget
@@ -474,6 +492,7 @@
suite.addTest(unittest.makeSuite(RadioWidgetTest))
suite.addTest(unittest.makeSuite(ItemsMultiEditWidgetBaseTest))
suite.addTest(unittest.makeSuite(MultiSelectWidgetTest))
+ suite.addTest(unittest.makeSuite(OrderedMultiSelectWidgetTest))
suite.addTest(unittest.makeSuite(MultiCheckBoxWidgetTest))
return suite
More information about the Zope3-Checkins
mailing list