Package turbogears :: Package widgets :: Module big_widgets

Source Code for Module turbogears.widgets.big_widgets

  1  """The bigger TurboGears widgets""" 
  2   
  3  __all__ = ['CalendarDatePicker', 'CalendarDateTimePicker', 
  4      'AutoCompleteField', 'AutoCompleteTextField', 
  5      'LinkRemoteFunction', 'RemoteForm', 'AjaxGrid', 'URLLink'] 
  6   
  7  import itertools 
  8  from datetime import datetime 
  9   
 10  from turbogears import validators, expose 
 11  from turbojson import jsonify 
 12  from turbogears.widgets.base import (CSSLink, JSLink, CSSSource, JSSource, 
 13      Widget, WidgetsList, static, mochikit, CoreWD) 
 14  from turbogears.widgets.i18n import CalendarLangFileLink 
 15  from turbogears.widgets.forms import (FormField, CompoundFormField, TextField, 
 16      HiddenField, TableForm, CheckBox, RadioButtonList) 
 17  from turbogears.widgets.rpc import RPC 
18 19 20 -class CalendarDatePicker(FormField):
21 """Use a Javascript calendar system to allow picking of calendar dates.""" 22 23 template = """ 24 <span xmlns:py="http://genshi.edgewall.org/" 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 = { 40 'attrs': 'Extra attributes', 41 'skin': 'For alternate skins, such as "calendar-blue" or "skins/aqua/theme"', 42 'picker_shows_time': 'Whether the calendar should let you pick a time, too', 43 'button_text': 'Text for the button that will show the calendar picker', 44 'format': 'The date format (default is mm/dd/yyyy)', 45 'calendar_lang': 'The language to be used in the calendar picker.' 46 } 47 attrs = {} 48 skin = 'calendar-system' 49 picker_shows_time = False 50 button_text = 'Choose' 51 format = '%m/%d/%Y' 52 calendar_lang = None 53 _default = None 54
55 - def __init__(self, name=None, default=None, not_empty=True, 56 calendar_lang=None, validator=None, format=None, **kw):
57 super(CalendarDatePicker, self).__init__(name, **kw) 58 self.not_empty = not_empty 59 if default is not None or not self.not_empty: 60 self._default = default 61 if format is not None: 62 self.format = format 63 if validator is None: 64 self.validator = validators.DateTimeConverter( 65 format=self.format, not_empty=self.not_empty) 66 else: 67 self.validator = validator 68 if calendar_lang: 69 self.calendar_lang = calendar_lang 70 javascript = [JSLink(static, 'calendar/calendar.js'), 71 JSLink(static, 'calendar/calendar-setup.js')] 72 javascript.append(CalendarLangFileLink(static, 73 language=self.calendar_lang)) 74 self.javascript = self.javascript + javascript 75 if self.skin: 76 css = [CSSLink(static, 'calendar/%s.css' % self.skin)] 77 self.css = self.css + css
78 79 @property
80 - def default(self):
81 if self._default is None and self.not_empty: 82 return datetime.now() 83 return self._default
84
85 - def update_params(self, d):
86 super(CalendarDatePicker, self).update_params(d) 87 if hasattr(d['value'], 'strftime'): 88 d['strdate'] = d['value'].strftime(d['format']) 89 else: 90 d['strdate'] = d['value']
91
92 93 -class CalendarDatePickerDesc(CoreWD):
94 95 name = "Calendar" 96 for_widget = CalendarDatePicker('date_picker')
97
98 99 -class CalendarDateTimePicker(CalendarDatePicker):
100 """Javascript calendar system to allow picking of dates with times.""" 101 102 format = '%Y/%m/%d %H:%M' 103 picker_shows_time = True
104
105 106 -class CalendarDateTimePickerDesc(CoreWD):
107 108 name = "Calendar with time" 109 for_widget = CalendarDateTimePicker("datetime_picker")
110
111 112 -class AutoComplete(Widget):
113 """Mixin class for autocomplete fields. 114 115 Performs Ajax-style autocompletion by requesting search 116 results from the server as the user types. 117 118 """ 119 120 javascript = [mochikit, JSLink(static,"autocompletefield.js")] 121 css = [CSSLink(static,"autocompletefield.css")] 122 params = ['search_controller', 'search_param', 'result_name', 'attrs', 123 'only_suggest', 'complete_delay', 'take_focus', 'min_chars', 'show_spinner'] 124 params_doc = { 125 'attrs': 'Extra attributes', 126 'search_controller': 'Name of the controller returning the auto completions', 127 'search_param': 'Name of the search parameter ("*" passes all form fields)', 128 'result_name': 'Name of the result list returned by the controller', 129 'only_suggest': 'If true, pressing enter does not automatically submit the first list item.', 130 'complete_delay': 'Delay (in seconds) before loading new auto completions', 131 'take_focus': 'If true, take focus on load.', 132 'min_chars': 'Minimum number of characters to type before autocomplete activates', 133 'show_spinner': 'If false, the spinner (load indicator) is not shown.' 134 } 135 attrs = {} 136 search_controller = '' 137 search_param = 'searchString' 138 result_name = 'textItems' 139 only_suggest = False 140 complete_delay = 0.200 141 take_focus = False 142 min_chars = 1 143 show_spinner = True
144
145 146 -class AutoCompleteField(CompoundFormField, AutoComplete):
147 """Text field with auto complete functionality and hidden key field.""" 148 149 template = """ 150 <span xmlns:py="http://genshi.edgewall.org/" id="${field_id}" class="${field_class}"> 151 <script type="text/javascript"> 152 AutoCompleteManager${field_id} = new AutoCompleteManager('${field_id}', 153 '${text_field.field_id}', '${hidden_field.field_id}', 154 '${search_controller}', '${search_param}', '${result_name}',${str(only_suggest).lower()}, 155 '${show_spinner and tg.url([tg.widgets, 'turbogears.widgets/spinner.gif']) or None}', 156 ${complete_delay}, ${str(take_focus).lower()}, ${min_chars}); 157 addLoadEvent(AutoCompleteManager${field_id}.initialize); 158 </script> 159 ${text_field.display(value_for(text_field), **params_for(text_field))} 160 <img py:if="show_spinner" id="autoCompleteSpinner${field_id}" 161 src="${tg.url([tg.widgets, 'turbogears.widgets/spinnerstopped.png'])}" alt=""/> 162 <span class="autoTextResults" id="autoCompleteResults${field_id}"/> 163 ${hidden_field.display(value_for(hidden_field), **params_for(hidden_field))} 164 </span> 165 """ 166 member_widgets = ['text_field', 'hidden_field'] 167 text_field = TextField(name='text') 168 hidden_field = HiddenField(name='hidden')
169
170 171 -class AutoCompleteFieldDesc(CoreWD):
172 173 name = "AutoCompleteField" 174 codes = """AK AL AR AS AZ CA CO CT DC DE FL FM GA GU HI IA ID IL IN KS 175 KY LA MA MD ME MH MI MN MO MP MS MT NC ND NE NH NJ NM NV NY OH 176 OK OR PA PR PW RI SC SD TN TX UM UT VA VI VT WA WI WV WY""".split() 177 states = """Alaska Alabama Arkansas American_Samoa Arizona 178 California Colorado Connecticut District_of_Columbia 179 Delaware Florida Federated_States_of_Micronesia Georgia Guam 180 Hawaii Iowa Idaho Illinois Indiana Kansas Kentucky Louisiana 181 Massachusetts Maryland Maine Marshall_Islands Michigan 182 Minnesota Missouri Northern_Mariana_Islands Mississippi 183 Montana North_Carolina North_Dakota Nebraska New_Hampshire 184 New_Jersey New_Mexico Nevada New_York Ohio Oklahoma Oregon 185 Pennsylvania Puerto_Rico Palau Rhode_Island South_Carolina 186 South_Dakota Tennessee Texas U.S._Minor_Outlying_Islands 187 Utah Virginia Virgin_Islands_of_the_U.S. Vermont Washington 188 Wisconsin West_Virginia Wyoming""".split() 189 states = map(lambda s: s.replace('_', ' '), states) 190 state_code = dict(zip(codes, states)) 191 template = """ 192 <form xmlns:py="http://genshi.edgewall.org/" onsubmit="if ( 193 this.elements[0].value &amp;&amp; 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 full_class_name = "turbogears.widgets.AutoCompleteField" 201
202 - def __init__(self, *args, **kw):
203 super(AutoCompleteFieldDesc, self).__init__(*args, **kw) 204 self.for_widget = AutoCompleteField(name='state_and_code', 205 search_controller='%s/search' % self.full_class_name, 206 search_param='state', result_name='states')
207 208 @expose('json')
209 - def search(self, state):
210 states = [] 211 code = state.upper() 212 if code in self.state_code: 213 states.append((self.state_code[code], code)) 214 else: 215 states.extend([s for s in zip(self.states, self.codes) 216 if s[0].lower().startswith(state.lower())]) 217 return dict(states=states)
218
219 220 -class AutoCompleteTextField(TextField, AutoComplete):
221 """Text field with auto complete functionality.""" 222 223 template = """ 224 <span xmlns:py="http://genshi.edgewall.org/" class="${field_class}"> 225 <script type="text/javascript"> 226 AutoCompleteManager${field_id} = new AutoCompleteManager('${field_id}', '${field_id}', null, 227 '${search_controller}', '${search_param}', '${result_name}', ${str(only_suggest).lower()}, 228 '${show_spinner and tg.url([tg.widgets, 'turbogears.widgets/spinner.gif']) or None}', 229 ${complete_delay}, ${str(take_focus).lower()}, ${min_chars}); 230 addLoadEvent(AutoCompleteManager${field_id}.initialize); 231 </script> 232 <input type="text" name="${name}" class="${field_class}" id="${field_id}" 233 value="${value}" py:attrs="attrs"/> 234 <img py:if="show_spinner" id="autoCompleteSpinner${field_id}" 235 src="${tg.url([tg.widgets, 'turbogears.widgets/spinnerstopped.png'])}" alt=""/> 236 <span class="autoTextResults" id="autoCompleteResults${field_id}"/> 237 </span> 238 """
239
240 241 -class AutoCompleteTextFieldDesc(CoreWD):
242 243 name = "AutoCompleteTextField" 244 states = AutoCompleteFieldDesc.states 245 state_code = AutoCompleteFieldDesc.state_code 246 template = """ 247 <table xmlns:py="http://genshi.edgewall.org/"> 248 <tr><th>State</th><td py:content="for_widget.display()"/></tr> 249 </table> 250 """ 251 full_class_name = "turbogears.widgets.AutoCompleteTextField" 252
253 - def __init__(self, *args, **kw):
254 super(AutoCompleteTextFieldDesc, self).__init__(*args, **kw) 255 self.for_widget = AutoCompleteTextField(name="state_only", 256 search_controller='%s/search' % self.full_class_name, 257 search_param='state', result_name='states')
258 259 @expose('json')
260 - def search(self, state):
261 states = [] 262 code = state.upper() 263 if code in self.state_code: 264 states.append(self.state_code[code]) 265 else: 266 states.extend([s for s in self.states 267 if s.lower().startswith(state.lower())]) 268 return dict(states=states)
269
270 271 -class LinkRemoteFunction(RPC):
272 """Link with remote execution. 273 274 Returns a link that executes a POST asynchronously 275 and updates a DOM Object with the result of it. 276 277 """ 278 279 template = """ 280 <a xmlns:py="http://genshi.edgewall.org/" name="${name}" 281 py:attrs="attrs" py:content="value" onclick="${js}" href="#"/> 282 """ 283 284 params = ['attrs'] 285 attrs = {}
286
287 288 -class LinkRemoteFunctionDesc(CoreWD):
289 290 name = "AJAX remote function" 291 states = AutoCompleteFieldDesc.states 292 template = """ 293 <div id="items"> 294 ${for_widget.display("States starting with the letter 'N'", update="items")} 295 </div> 296 """ 297 full_class_name = 'turbogears.widgets.LinkRemoteFunction' 298
299 - def __init__(self, *args, **kw):
300 super(LinkRemoteFunctionDesc, self).__init__(*args, **kw) 301 self.for_widget = LinkRemoteFunction( 302 name='linkrf', action='%s/search_linkrf' % self.full_class_name, 303 data=dict(state_starts_with='N'))
304 305 @expose()
306 - def search_linkrf(self, state_starts_with):
307 return '<br/>'.join( 308 [s for s in self.states if s.startswith(state_starts_with)])
309
310 311 -class RemoteForm(RPC, TableForm):
312 """AJAX table form. 313 314 A TableForm that submits the data asynchronously and loads the resulting 315 HTML into a DOM object 316 317 """ 318
319 - def update_params(self, d):
320 super(RemoteForm, self).update_params(d) 321 d['form_attrs']['onSubmit'] = "return !remoteFormRequest(this, '%s', %s);" % ( 322 d.get('update', ''), jsonify.encode(self.get_options(d)))
323
324 325 -class RemoteFormDesc(CoreWD):
326 327 name = "AJAX Form" 328 template = """ 329 <div> 330 ${for_widget.display()} 331 <div id="post_data">&nbsp;</div> 332 </div> 333 """ 334 full_class_name = 'turbogears.widgets.RemoteForm' 335
336 - class TestFormFields(WidgetsList):
337 name = TextField() 338 age = TextField() 339 check = CheckBox() 340 radio = RadioButtonList(options=list(enumerate( 341 "Python Java Pascal Ruby".split())), default=3)
342
343 - def __init__(self, *args, **kw):
344 super(RemoteFormDesc, self).__init__(*args, **kw) 345 self.for_widget = RemoteForm( 346 fields=self.TestFormFields(), 347 name='remote_form', 348 update='post_data', 349 action='%s/post_data_rf' % self.full_class_name, 350 before="alert('pre-hook')", 351 confirm="Confirm?", 352 )
353 354 @expose()
355 - def post_data_rf(self, **kw):
356 return """Received data:<br/>%r""" % kw
357 358 359 ajaxgridcounter = itertools.count()
360 361 -class AjaxGrid(Widget):
362 """AJAX updateable datagrid based on widget.js grid""" 363 364 template = """<div id="${id}" xmlns:py="http://genshi.edgewall.org/"> 365 <a py:if="refresh_text" 366 href="#" 367 onclick="javascript:${id}_AjaxGrid.refresh(${defaults});return false;"> 368 ${refresh_text} 369 </a> 370 <div id="${id}_update"></div> 371 <script type="text/javascript"> 372 addLoadEvent(partial(${id}_AjaxGrid.refresh, ${defaults})); 373 </script> 374 </div> 375 """ 376 params = ['refresh_text', 'id', 'defaults'] 377 defaults = {} 378 refresh_text = "Update" 379 id = 'ajaxgrid_%d' % ajaxgridcounter.next() 380
381 - def __init__(self, refresh_url, *args, **kw):
382 super(AjaxGrid, self).__init__(*args, **kw) 383 target = '%s_update' % self.id 384 self.javascript = [ 385 mochikit, 386 JSLink('turbogears', 'js/widget.js'), 387 JSLink(static, 'ajaxgrid.js'), 388 JSSource(""" 389 %(id)s_AjaxGrid = new AjaxGrid('%(refresh_url)s', '%(target)s'); 390 """ % dict(id=self.id, refresh_url=refresh_url, target=target) 391 ), 392 ]
393
394 - def update_params(self, d):
395 super(AjaxGrid, self).update_params(d) 396 d['defaults'] = jsonify.encode(d['defaults'])
397
398 399 -class AjaxGridDesc(CoreWD):
400 401 name = "AJAX Grid" 402 full_class_name = 'turbogears.widgets.AjaxGrid' 403 404 @staticmethod
405 - def facgen(n):
406 total = 1 407 yield 0, 1 408 for k in xrange(1, n+1): 409 total *= k 410 yield k, total
411 412
413 - def __init__(self, *args, **kw):
414 super(AjaxGridDesc, self).__init__(*args, **kw) 415 self.for_widget = AjaxGrid( 416 refresh_url = "%s/update" % self.full_class_name, 417 # Dummy default params, just POC 418 defaults = dict(), 419 ) 420 self.update_count = itertools.count()
421 422 @expose('json')
423 - def update(self):
424 return dict( 425 headers = ['N', 'fact(N)'], 426 rows = list(self.facgen(self.update_count.next())), 427 )
428 446