1 import itertools
2 from datetime import datetime
3
4 from turbogears import validators, expose
5 from turbojson import jsonify
6 from turbogears.widgets.base import CSSLink, JSLink, CSSSource, JSSource, \
7 Widget, WidgetsList, static, mochikit, \
8 CoreWD
9 from turbogears.widgets.i18n import CalendarLangFileLink
10 from turbogears.widgets.forms import FormField, CompoundFormField, TextField, \
11 HiddenField, TableForm, CheckBox, \
12 RadioButtonList
13 from turbogears.widgets.rpc import RPC
14
15 __all__ = ["CalendarDatePicker", "CalendarDateTimePicker",
16 "AutoCompleteField", "AutoCompleteTextField",
17 "LinkRemoteFunction", "RemoteForm", "AjaxGrid", "URLLink"]
21 """Use a Javascript calendar system to allow picking of calendar dates."""
22
23 template = """
24 <span xmlns:py="http://purl.org/kid/ns#" class="${field_class}">
25 <input type="text" id="${field_id}" class="${field_class}" name="${name}" value="${strdate}" py:attrs="attrs"/>
26 <input type="button" id="${field_id}_trigger" class="date_field_button" value="${button_text}"/>
27 <script type="text/javascript">
28 Calendar.setup({
29 inputField : "${field_id}",
30 ifFormat : "${format}",
31 button : "${field_id}_trigger"
32 <span py:if="picker_shows_time" py:replace="', showsTime : true'"/>
33 });
34 </script>
35 </span>
36 """
37 params = ["attrs", "skin", "picker_shows_time", "button_text",
38 "format", "calendar_lang"]
39 params_doc = {'attrs': 'Extra attributes',
40 'skin': 'For alternate skins, such as "calendar-blue" or "skins/aqua/theme"',
41 'picker_shows_time': 'Whether the calendar should let you pick a time, too',
42 'button_text': 'Text for the button that will show the calendar picker',
43 'format': 'The date format (default is mm/dd/yyyy)',
44 'calendar_lang': 'The language to be used in the calendar picker.'}
45 attrs = {}
46 skin = "calendar-system"
47 picker_shows_time = False
48 button_text = "Choose"
49 format = "%m/%d/%Y"
50 calendar_lang = None
51 _default = None
52
53 - def __init__(self, name=None, default=None, not_empty=True,
54 calendar_lang=None, validator=None, format=None, **kw):
76
78 if self._default is None and self.not_empty:
79 return datetime.now()
80 return self._default
81 default = property(_get_default)
82
84 super(CalendarDatePicker, self).update_params(d)
85 if hasattr(d['value'], 'strftime'):
86 d['strdate'] = d['value'].strftime(d['format'])
87 else:
88 d['strdate'] = d['value']
89
95
98 """Javascript calendar system to allow picking of dates with times."""
99
100 format = "%Y/%m/%d %H:%M"
101 picker_shows_time = True
102
108
111 """Mixin class for autocomplete fields.
112
113 Performs Ajax-style autocompletion by requesting search
114 results from the server as the user types.
115
116 """
117 javascript = [mochikit, JSLink(static,"autocompletefield.js")]
118 css = [CSSLink(static,"autocompletefield.css")]
119 params = ["search_controller", "search_param", "result_name", "attrs",
120 "only_suggest", "complete_delay", "take_focus", "min_chars", "show_spinner"]
121 params_doc = {'attrs': 'Extra attributes',
122 'search_controller': 'Name of the controller returning the auto completions',
123 'search_param': 'Name of the search parameter ("*" passes all form fields)',
124 'result_name': 'Name of the result list returned by the controller',
125 'only_suggest': 'If true, pressing enter does not automatically submit the first list item.',
126 'complete_delay': 'Delay (in seconds) before loading new auto completions',
127 'take_focus': 'If true, take focus on load.',
128 'min_chars': 'Minimum number of characters to type before autocomplete activates',
129 'show_spinner': 'If false, the spinner (load indicator) is not shown.'}
130 attrs = {}
131 search_controller = ""
132 search_param = "searchString"
133 result_name = "textItems"
134 only_suggest = False
135 complete_delay = 0.200
136 take_focus = False
137 min_chars = 1
138 show_spinner = True
139
142 """Text field with auto complete functionality and hidden key field."""
143
144 template = """
145 <span xmlns:py="http://purl.org/kid/ns#" id="${field_id}" class="${field_class}">
146 <script type="text/javascript">
147 AutoCompleteManager${field_id} = new AutoCompleteManager('${field_id}',
148 '${text_field.field_id}', '${hidden_field.field_id}',
149 '${search_controller}', '${search_param}', '${result_name}',${str(only_suggest).lower()},
150 '${show_spinner and tg.url([tg.widgets, 'turbogears.widgets/spinner.gif']) or None}',
151 ${complete_delay}, ${str(take_focus).lower()}, ${min_chars});
152 addLoadEvent(AutoCompleteManager${field_id}.initialize);
153 </script>
154 ${text_field.display(value_for(text_field), **params_for(text_field))}
155 <img py:if="show_spinner" id="autoCompleteSpinner${field_id}"
156 src="${tg.url([tg.widgets, 'turbogears.widgets/spinnerstopped.png'])}" alt=""/>
157 <span class="autoTextResults" id="autoCompleteResults${field_id}"/>
158 ${hidden_field.display(value_for(hidden_field), **params_for(hidden_field))}
159 </span>
160 """
161
162 member_widgets = ["text_field", "hidden_field"]
163 text_field = TextField(name="text")
164 hidden_field = HiddenField(name="hidden")
165
168
169 name = "AutoCompleteField"
170
171 codes = """AK AL AR AS AZ CA CO CT DC DE FL FM GA GU HI IA ID IL IN KS
172 KY LA MA MD ME MH MI MN MO MP MS MT NC ND NE NH NJ NM NV NY OH
173 OK OR PA PR PW RI SC SD TN TX UM UT VA VI VT WA WI WV WY""".split()
174
175 states = """Alaska Alabama Arkansas American_Samoa Arizona
176 California Colorado Connecticut District_of_Columbia
177 Delaware Florida Federated_States_of_Micronesia Georgia Guam
178 Hawaii Iowa Idaho Illinois Indiana Kansas Kentucky Louisiana
179 Massachusetts Maryland Maine Marshall_Islands Michigan
180 Minnesota Missouri Northern_Mariana_Islands Mississippi
181 Montana North_Carolina North_Dakota Nebraska New_Hampshire
182 New_Jersey New_Mexico Nevada New_York Ohio Oklahoma Oregon
183 Pennsylvania Puerto_Rico Palau Rhode_Island South_Carolina
184 South_Dakota Tennessee Texas U.S._Minor_Outlying_Islands
185 Utah Virginia Virgin_Islands_of_the_U.S. Vermont Washington
186 Wisconsin West_Virginia Wyoming""".split()
187 states = map(lambda s: s.replace('_', ' '), states)
188
189 state_code = dict(zip(codes, states))
190
191 template = """
192 <form xmlns:py="http://purl.org/kid/ns#" onsubmit="if (
193 this.elements[0].value && this.elements[1].value)
194 alert('The alpha code for '+this.elements[0].value
195 +' is '+this.elements[1].value+'.');return false"><table>
196 <tr><th>State</th><td py:content="for_widget.display()"/>
197 <td><input type="submit" value="Show alpha code"/></td></tr>
198 </table></form>
199 """
200
201 full_class_name = "turbogears.widgets.AutoCompleteField"
202
208
209 @expose(format="json")
211 states = []
212 code = state.upper()
213 if code in self.state_code:
214 states.append((self.state_code[code], code))
215 else:
216 states.extend([s for s in zip(self.states, self.codes)
217 if s[0].lower().startswith(state.lower())])
218 return dict(states=states)
219
220
221 -class AutoCompleteTextField(TextField, AutoComplete):
222 """Text field with auto complete functionality."""
223
224 template = """
225 <span xmlns:py="http://purl.org/kid/ns#" class="${field_class}">
226 <script type="text/javascript">
227 AutoCompleteManager${field_id} = new AutoCompleteManager('${field_id}', '${field_id}', null,
228 '${search_controller}', '${search_param}', '${result_name}', ${str(only_suggest).lower()},
229 '${show_spinner and tg.url([tg.widgets, 'turbogears.widgets/spinner.gif']) or None}',
230 ${complete_delay}, ${str(take_focus).lower()}, ${min_chars});
231 addLoadEvent(AutoCompleteManager${field_id}.initialize);
232 </script>
233 <input type="text" name="${name}" class="${field_class}" id="${field_id}"
234 value="${value}" py:attrs="attrs"/>
235 <img py:if="show_spinner" id="autoCompleteSpinner${field_id}"
236 src="${tg.url([tg.widgets, 'turbogears.widgets/spinnerstopped.png'])}" alt=""/>
237 <span class="autoTextResults" id="autoCompleteResults${field_id}"/>
238 </span>
239 """
240
243
244 name = "AutoCompleteTextField"
245
246 states = AutoCompleteFieldDesc.states
247 state_code = AutoCompleteFieldDesc.state_code
248
249 template = """
250 <table xmlns:py="http://purl.org/kid/ns#">
251 <tr><th>State</th><td py:content="for_widget.display()"/></tr>
252 </table>
253 """
254
255 full_class_name = "turbogears.widgets.AutoCompleteTextField"
256
257 - def __init__(self, *args, **kw):
258 super(AutoCompleteTextFieldDesc, self).__init__(*args, **kw)
259 self.for_widget = AutoCompleteTextField(name="state_only",
260 search_controller="%s/search" % self.full_class_name,
261 search_param="state", result_name="states")
262
263 @expose(format="json")
264 - def search(self, state):
265 states = []
266 code = state.upper()
267 if code in self.state_code:
268 states.append(self.state_code[code])
269 else:
270 states.extend([s for s in self.states
271 if s.lower().startswith(state.lower())])
272 return dict(states=states)
273
276 """Link with remote execution.
277
278 Returns a link that executes a POST asynchronously
279 and updates a DOM Object with the result of it.
280
281 """
282 template = """
283 <a xmlns:py="http://purl.org/kid/ns#" name="${name}"
284 py:attrs="attrs" py:content="value" onclick="${js}" href="#"/>
285 """
286
287 params = ["attrs"]
288 attrs = {}
289
292
293 name = "AJAX remote function"
294
295 states = AutoCompleteFieldDesc.states
296
297 template = """
298 <div id="items">
299 ${for_widget.display("States starting with the letter 'N'", update="items")}
300 </div>
301 """
302
303 full_class_name = "turbogears.widgets.LinkRemoteFunction"
304
310
311 @expose()
313 return '<br/>'.join(filter(
314 lambda item: item.startswith(state_starts_with), self.states))
315
328
363
364
365 ajaxgridcounter = itertools.count()
368 """AJAX updateable datagrid based on widget.js grid"""
369
370 template = """<div id="${id}" xmlns:py="http://purl.org/kid/ns#">
371 <a py:if="refresh_text"
372 href="#"
373 onclick="javascript:${id}_AjaxGrid.refresh(${defaults});return false;">
374 ${refresh_text}
375 </a>
376 <div id="${id}_update"></div>
377 <script type="text/javascript">
378 addLoadEvent(partial(${id}_AjaxGrid.refresh, ${defaults}));
379 </script>
380 </div>
381 """
382
383 params = ["refresh_text", "id", "defaults"]
384
385 defaults = {}
386 refresh_text = "Update"
387 id = "ajaxgrid_%d" % ajaxgridcounter.next()
388
389 - def __init__(self, refresh_url, *args, **kw):
390 super(AjaxGrid, self).__init__(*args, **kw)
391 target = "%s_update" % self.id
392 self.javascript = [
393 mochikit,
394 JSLink("turbogears", "js/widget.js"),
395 JSLink(static, "ajaxgrid.js"),
396 JSSource("""
397 %(id)s_AjaxGrid = new AjaxGrid('%(refresh_url)s', '%(target)s');
398 """ % dict(id=self.id, refresh_url=refresh_url, target=target)
399 ),
400 ]
401
405
408
409 name = "AJAX Grid"
410
411 @staticmethod
413 total = 1
414 yield 0, 1
415 for x in xrange(1, n+1):
416 total *= x
417 yield x, total
418
419 full_class_name = "turbogears.widgets.AjaxGrid"
420
429
430 @expose(format="json")
432 return dict(
433 headers = ["N", "fact(N)"],
434 rows = list(self.facgen(self.update_count.next())),
435 )
436
439 """Hyperlink"""
440
441 template = """
442 <a xmlns:py="http://purl.org/kid/ns#"
443 href="$link"
444 target="$target"
445 py:attrs="attrs"
446 >$text</a>
447 """
448
449 params = ["target", "text", "link", "attrs"]
450 attrs = {}
451 params_doc = {'link': 'Hyperlink',
452 'target': 'Specify where the link should be opened',
453 'text': 'The message to be shown for the link',
454 'attrs': 'Extra attributes'}
455