Package tdi :: Package tools :: Package htmlform :: Module _main
[frames] | no frames]

Source Code for Module tdi.tools.htmlform._main

  1  # -*- coding: ascii -*- 
  2  r""" 
  3  :Copyright: 
  4   
  5   Copyright 2007 - 2015 
  6   Andr\xe9 Malo or his licensors, as applicable 
  7   
  8  :License: 
  9   
 10   Licensed under the Apache License, Version 2.0 (the "License"); 
 11   you may not use this file except in compliance with the License. 
 12   You may obtain a copy of the License at 
 13   
 14       http://www.apache.org/licenses/LICENSE-2.0 
 15   
 16   Unless required by applicable law or agreed to in writing, software 
 17   distributed under the License is distributed on an "AS IS" BASIS, 
 18   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 19   See the License for the specific language governing permissions and 
 20   limitations under the License. 
 21   
 22  ===================== 
 23   HTML forms reloaded 
 24  ===================== 
 25   
 26  Form helper classes. 
 27  """ 
 28  if __doc__: 
 29      # pylint: disable = redefined-builtin 
 30      __doc__ = __doc__.encode('ascii').decode('unicode_escape') 
 31  __author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape') 
 32  __docformat__ = "restructuredtext en" 
 33  __all__ = ['normalize_newlines', 'normalize_whitespaces', 'HTMLForm'] 
 34   
 35  import re as _re 
 36   
 37  from ._adapters import NullParameterAdapter 
 38  from ._input_field_generator import make_input 
 39   
 40   
41 -def normalize_newlines():
42 """ Make newline normalizer """ 43 SUB_U = _re.compile(ur'\r?\n|\r').sub 44 SUB_S = _re.compile(r'\r?\n|\r').sub 45 46 def normalize_newlines(value): 47 """ 48 Normalize the newlines of a string 49 50 All newlines are converted to \\n. 51 52 :Parameters: 53 `value` : ``basestring`` 54 The text to normalize 55 56 :Return: The normalized value, the type depends on the input type 57 :Rtype: ``basestring`` 58 """ 59 # pylint: disable = redefined-outer-name 60 61 if isinstance(value, unicode): 62 subber, repl = SUB_U, u"\n" 63 else: 64 subber, repl = SUB_S, "\n" 65 return subber(repl, value)
66 return normalize_newlines 67 normalize_newlines = normalize_newlines() 68 69
70 -def normalize_whitespaces():
71 """ Make whitespace normalizer """ 72 SUB_U = _re.compile(ur'\s').sub 73 SUB_S = _re.compile(r'\s').sub 74 75 def normalize_whitespaces(value): 76 """ 77 Normalize the whitespaces of a string 78 79 All whitespaces are converted to regular space. 80 81 :Parameters: 82 `value` : ``basestring`` 83 The text to normalize 84 85 :Return: The normalized value, the type depends on the input type 86 :Rtype: ``basestring`` 87 """ 88 # pylint: disable = redefined-outer-name 89 90 if isinstance(value, unicode): 91 subber, repl = SUB_U, u" " 92 else: 93 subber, repl = SUB_S, " " 94 return subber(repl, value)
95 return normalize_whitespaces 96 normalize_whitespaces = normalize_whitespaces() 97 98
99 -class HTMLForm(object):
100 """ 101 HTML form helper class 102 103 :IVariables: 104 `_action` : ``basestring`` 105 form action 106 107 `_method` : ``basestring`` 108 form method 109 110 `_param` : `ParameterAdapterInterface` 111 Parameter adapter 112 113 `_upload` : ``bool`` 114 Upload form? 115 116 `_charset` : ``basestring`` 117 Accepted character set for submission 118 119 `_xhtml` : ``bool`` 120 Use XHTML attributes (vs. short attributes)? 121 122 `_pre_proc` : `PreProcInterface` 123 Pre set node processing callable 124 125 `_post_proc` : `PostProcInterface` 126 Post set node processing callable 127 """ 128
129 - def __init__(self, action=None, method='get', param=None, upload=False, 130 accept_charset='utf-8', xhtml=True, pre_proc=None, 131 post_proc=None):
132 """ 133 Initialization 134 135 If you set `upload` to ``True``, the method will be ignored and 136 be set to ``post`` automatically. 137 138 :Parameters: 139 `action` : ``basestring`` 140 Form action URL 141 142 `method` : ``basestring`` 143 form submission method 144 145 `param` : `ParameterAdapterInterface` 146 Parameter adapter. If unset or ``None``, no values 147 will be taken out of the request. This is useful for initial 148 requests showing empty forms as there will be no special handling 149 required for this case. 150 151 `upload` : ``bool`` 152 Is this an upload form? 153 154 `accept_charset` : ``basestring`` 155 Accepted charset(s) for submission, if there are multiple charsets 156 given, they have to be unique and space separated. 157 158 `xhtml` : ``bool`` 159 Use XHTML attributes (vs. short attributes)? 160 161 `pre_proc` : `PreProcInterface` 162 Pre set node processing callable 163 164 `post_proc` : `PostProcInterface` 165 Post set node processing callable 166 """ 167 self._action = action 168 self._method = upload and 'post' or method 169 if param is None: 170 param = NullParameterAdapter() 171 self._param = param 172 self._upload = upload 173 self._charset = accept_charset 174 self._xhtml = bool(xhtml) 175 if pre_proc is None: 176 pre_proc_ = None 177 else: 178 def pre_proc_(method, node, *args): 179 """ Pre proc wrapper """ 180 node, kwargs = pre_proc(method, node, dict(args)) 181 return (node,) + tuple([ 182 kwargs.get(key, val) for key, val in args 183 ])
184 self._pre_proc = pre_proc_ 185 self._post_proc = post_proc
186
187 - def param(self):
188 """ Parameter adapter getter """ 189 return self._param
190 param = property(param, doc="Parameter adapter the form is using") 191
192 - def is_xhtml(self):
193 """ XHTML flag getter """ 194 return self._xhtml
195 is_xhtml = property(is_xhtml, doc="XHTML flag setting of the form") 196
197 - def is_upload(self):
198 """ Upload flag getter """ 199 return self._upload
200 is_upload = property(is_upload, doc="Upload flag setting of the form") 201
202 - def accept_charset(self):
203 """ Accept-charset getter """ 204 return self._charset
205 accept_charset = property( 206 accept_charset, 207 doc="Accepted charset of the form" 208 ) 209
210 - def action(self):
211 """ Form action getter """ 212 return self._action
213 action = property(action, doc="Configured form action") 214
215 - def method(self):
216 """ Form method getter """ 217 return self._method
218 method = property(method, doc="Configured form method") 219 220 normalize_newlines = staticmethod(normalize_newlines) 221 normalize_whitespaces = staticmethod(normalize_whitespaces) 222
223 - def form(self, node, hidden=None, hidden_="hidden", autocomplete=None, 224 novalidate=None, raw=False):
225 """ 226 Fill in the form starttag 227 228 The following attributes are possibly set: 229 - ``action`` (only if it's not ``None``) 230 - ``method`` 231 - ``accept-charset`` (only if it's not ``None``) 232 - ``enctype`` (only on upload forms) 233 - ``autocomplete`` 234 - ``novalidate`` 235 236 Rendering hidden fields 237 ~~~~~~~~~~~~~~~~~~~~~~~ 238 239 You can use this method to set a list of hidden fields at once. 240 It iterates over `hidden` and multiplies the node named by `hidden_` 241 accordingly. 242 243 The `hidden` iterable contains tuples of variable length, namely 244 from 1 to 3, like:: 245 246 [ 247 ('foo', 'bar'), 248 ('zonk', '"plop"', True), 249 ('x',), 250 ] 251 252 If `hidden` is empty, the hidden node will be deleted. 253 254 Field item tuples 255 ----------------- 256 257 The first (and maybe only) item is the name of the field. This 258 is always set unconditionally. 259 260 The second item is the value of the field. If the field does not 261 have a value at all - the second and third items are left out, 262 leaving the name only. 263 If the value is ``None`` it's taken out of the request and filled 264 into the field. The third parameter is ignored in this case. If the 265 name does not appear in the request, the field is skipped (not 266 rendered). If the request contains more than one value under 267 that name, a hidden field is generated for each of them. 268 In all other cases the value is written into the ``value`` attribute. 269 270 The third item determines whether the value should be treated 271 as raw or not. If it's unset, the `raw` parameter of the method 272 applies. 273 274 :Parameters: 275 `node` : `tdi.nodetree.Node` 276 The ``<form>`` node 277 278 `hidden` : iterable 279 Hidden fields to set. If unset or ``None``, no hidden 280 fields are touched. If it's an empty list, the hidden node is 281 removed. 282 283 `hidden_` : ``basestring`` 284 Name of the hidden field node, relative to the form 285 `node` (dotted notation) 286 287 `autocomplete` : ``bool`` 288 Set the default autocomplete state of the form (HTML5). If omitted 289 or ``None``, any autocomplete attribute present won't be touched. 290 291 `novalidate` : ``bool`` 292 Set the default novalidate attribute of the form (HTML5). If 293 omitted or ``None``, any novalidate attribute present won't be 294 touched. 295 296 `raw` : ``bool`` 297 Default "rawness" value for the hidden field list 298 """ 299 # pylint: disable = too-many-branches 300 301 pre_proc = self._pre_proc 302 if pre_proc is not None: 303 node, hidden, hidden_, raw = pre_proc( 304 'form', node, 305 ('hidden', hidden), ('hidden_', hidden_), ('raw', raw), 306 ) 307 308 if self._action is not None: 309 node[u'action'] = self._action 310 node[u'method'] = self._method 311 if self._charset is not None: 312 node[u'accept-charset'] = self._charset 313 if autocomplete is not None: 314 node[u'autocomplete'] = autocomplete and u'on' or u'off' 315 if self._upload: 316 node[u'enctype'] = u'multipart/form-data' 317 if novalidate is not None: 318 if novalidate: 319 node[u'novalidate'] = self._xhtml and u'novalidate' or None 320 else: 321 del node[u'novalidate'] 322 323 post_proc = self._post_proc 324 if post_proc is not None: 325 post_proc('form', node, dict( 326 hidden=hidden, hidden_=hidden_, raw=raw 327 )) 328 329 if hidden is not None: 330 partnodes = hidden_.split('.') 331 partnodes.reverse() 332 hiddennode = node(partnodes.pop()) 333 while partnodes: 334 hiddennode = hiddennode(partnodes.pop()) 335 336 # hidden fields 337 param = self._param 338 filtered = [] 339 for field in hidden: 340 name, value, thisraw = field[0], field[1:2], field[2:3] 341 if value: 342 value = value[0] 343 if value is None: 344 rval = param.getlist(name) 345 filtered.extend([(name, val, False) for val in rval]) 346 else: 347 filtered.append((name, value, (thisraw or [raw])[0])) 348 else: 349 filtered.append((name, None, None)) 350 for subnode, param in hiddennode.iterate(filtered): 351 self.hidden(subnode, *param)
352
353 - def hidden(self, node, name, value=None, raw=False):
354 """ 355 Render a hidden field 356 357 Hidden field values are never taken out of the request. The reason for 358 that seemingly inconsistent behaviour is that hidden fields have no 359 assigned semantics. In other words, the method can't know, *how* to 360 correctly retrieve the value out of the request. 361 362 :Parameters: 363 `node` : `tdi.nodetree.Node` 364 The hidden field node 365 366 `name` : ``basestring`` 367 Name of the hidden field 368 369 `value` : ``basestring`` 370 Optional value of the hidden field - if omitted or 371 ``None``, the value attribute is completey removed 372 373 `raw` : ``bool`` 374 Is `value` raw (not to be escaped) 375 """ 376 pre_proc = self._pre_proc 377 if pre_proc is not None: 378 node, name, value, raw = pre_proc( 379 'hidden', node, 380 ('name', name), ('value', value), ('raw', raw), 381 ) 382 383 node[u'type'] = u'hidden' 384 node[u'name'] = name 385 if value is None: 386 del node[u'value'] 387 elif raw: 388 node.raw[u'value'] = value 389 else: 390 node[u'value'] = value 391 392 post_proc = self._post_proc 393 if post_proc is not None: 394 post_proc('hidden', node, dict(name=name, value=value, raw=raw))
395 396 text = make_input( 397 'text', '', 398 'name', 'value', 'maxlength', 'readonly', 'disabled', 'required', 399 'autocomplete', 'placeholder', 'list', 'pattern', 'dirname', 400 'autofocus', 'raw', 401 ) 402 search = make_input( 403 'search', '(HTML5)', 404 'name', 'value', 'maxlength', 'readonly', 'disabled', 'required', 405 'autocomplete', 'placeholder', 'list', 'pattern', 406 'dirname', 'autofocus', 'raw', 407 ) 408 tel = make_input( 409 'tel', '(HTML5)', 410 'name', 'value', 'maxlength', 'readonly', 'disabled', 'required', 411 'autocomplete', 'placeholder', 'list', 'pattern', 'autofocus', 'raw', 412 ) 413 url = make_input( 414 'url', '(HTML5)', 415 'name', 'value', 'maxlength', 'readonly', 'disabled', 'required', 416 'autocomplete', 'placeholder', 'list', 'pattern', 'autofocus', 'raw', 417 ) 418 email = make_input( 419 'email', '(HTML5)', 420 'name', 'value', 'maxlength', 'readonly', 'disabled', 'required', 421 'autocomplete', 'placeholder', 'list', 'pattern', 422 'multiple', 'autofocus', 'raw', 423 ) 424 password = make_input( 425 'password', '', 426 'name', 'maxlength', 'readonly', 'disabled', 'required', 427 'autocomplete', 'placeholder', 'pattern', 'autofocus', 428 ) 429 datetime = make_input( 430 # pylint: disable = bad-continuation 431 'datetime', '(HTML5)\n\n ' 432 '(e.g. ``1979-10-14T12:00:00.001-04:00``)', # noqa 433 'name', 'value', 'readonly', 'disabled', 'required', 'autocomplete', 434 'list', 'max', 'min', 'step', 'autofocus', 'raw', 435 ) 436 date = make_input( 437 'date', '(HTML5)\n\n (e.g. ``1979-10-14``)', 438 'name', 'value', 'readonly', 'disabled', 'required', 'autocomplete', 439 'list', 'max', 'min', 'step', 'autofocus', 'raw', 440 ) 441 month = make_input( 442 'month', '(HTML5)\n\n (e.g. ``1979-10``)', 443 'name', 'value', 'readonly', 'disabled', 'required', 'autocomplete', 444 'list', 'max', 'min', 'step', 'autofocus', 'raw', 445 ) 446 week = make_input( 447 'week', '(HTML5)\n\n (e.g. ``1979-W42``)', 448 'name', 'value', 'readonly', 'disabled', 'required', 'autocomplete', 449 'list', 'max', 'min', 'step', 'autofocus', 'raw', 450 ) 451 time = make_input( 452 'time', '(HTML5)\n\n (e.g. ``12:00:00.001``)', 453 'name', 'value', 'readonly', 'disabled', 'required', 'autocomplete', 454 'list', 'max', 'min', 'step', 'autofocus', 'raw', 455 ) 456 datetime_local = make_input( 457 # pylint: disable = bad-continuation 458 'datetime-local', '(HTML5)\n\n ' 459 '(e.g. ``1979-10-14T12:00:00.001``)', # noqa 460 'name', 'value', 'readonly', 'disabled', 'required', 'autocomplete', 461 'list', 'max', 'min', 'step', 'autofocus', 'raw', 462 ) 463 number = make_input( 464 'number', '(HTML5)', 465 'name', 'value', 'readonly', 'disabled', 'required', 'autocomplete', 466 'placeholder', 'list', 'max', 'min', 'step', 'autofocus', 'raw', 467 ) 468 range = make_input( 469 'range', '(HTML5)', 470 'name', 'value', 'disabled', 'autocomplete', 'list', 'max', 471 'autofocus', 'min', 'step', 'raw', 472 ) 473 color = make_input( 474 'color', '(HTML5)\n\n (e.g. ``#D4D0C8``)', 475 'name', 'value', 'disabled', 'autocomplete', 'list', 'raw', 476 'autofocus', 477 ) 478 checkbox = make_input( 479 'checkbox', '', 480 'name', 'value', 'disabled', 'required', 'selected', 'autofocus', 481 value_default=u'1', multi_selected=True, 482 ) 483 radio = make_input( 484 'radio', '', 485 'name', 'value', 'disabled', 'required', 'selected', 'autofocus', 486 value_default=None, multi_selected=False, 487 ) 488 file = make_input( 489 'file', '', 490 'name', 'accept', 'disabled', 'required', 'multiple', 'autofocus', 491 assert_upload=True, 492 ) 493 submit = make_input( 494 'submit', '', 495 'name', 'value', 'disabled', 'action', 'enctype', 'method', 496 'novalidate', 'target', 'autofocus', 497 simple_value=True, name_optional=True, 498 ) 499 image = make_input( 500 'image', '', 501 'name', 'disabled', 'alt', 'src', 'width', 'height', 'action', 502 'enctype', 'method', 'novalidate', 'target', 'autofocus', 503 name_optional=True, 504 ) 505 reset = make_input( 506 'reset', '', 507 'value', 'disabled', 'autofocus', 508 simple_value=True, 509 ) 510 button = make_input( 511 'button', '', 512 'name', 'value', 'disabled', 'autofocus', 513 simple_value=True, name_optional=True, 514 ) 515
516 - def textarea(self, node, name, value=None, maxlength=None, readonly=None, 517 disabled=None, required=None, placeholder=None, dirname=None, 518 autofocus=None, raw=False):
519 """ 520 Render a 'textarea' input control 521 522 :Parameters: 523 `node` : `tdi.nodetree.Node` 524 The 'textarea' node 525 526 `name` : ``basestring`` 527 The name of the 'textarea' field 528 529 `value` : ``basestring`` 530 Optional value. If ``None``, it's taken out of the request. If 531 it does not appear in the request, it's treated like an empty 532 string. The `raw` parameter is ignored in this case. 533 534 `maxlength` : ``int`` 535 Maximum length. If omitted or ``None``, the attribute is 536 *deleted*. 537 538 `readonly` : ``bool`` 539 Readonly field? If unset or ``None``, the attribute is left 540 untouched. 541 542 `disabled` : ``bool`` 543 Disabled field? If unset or ``None``, the attribute is left 544 untouched. 545 546 `required` : ``bool`` 547 Required field? (HTML5). If omitted or ``None``, the attribute 548 is left untouched. 549 550 `placeholder` : ``basestring`` 551 Placeholder value (HTML5). If omitted or ``None``, the 552 attribute is left untouched. 553 554 `dirname` : ``basestring`` 555 Direction submission name (HTML5). If omitted or ``None``, the 556 attribute is left untouched. 557 558 `autofocus` : ``bool`` 559 Set autofocus? (HTML5). If omitted or ``None``, the attribute 560 is left untouched. 561 562 `raw` : ``bool`` 563 Is the value to be treated raw? 564 """ 565 # pylint: disable = too-many-arguments, too-many-branches 566 567 pre_proc = self._pre_proc 568 if pre_proc is not None: 569 ( 570 node, name, value, maxlength, readonly, disabled, 571 required, placeholder, dirname, autofocus, raw 572 ) = pre_proc( 573 'textarea', node, 574 575 ('name', name), 576 ('value', value), 577 ('maxlength', maxlength), 578 ('readonly', readonly), 579 ('disabled', disabled), 580 ('required', required), 581 ('placeholder', placeholder), 582 ('dirname', dirname), 583 ('autofocus', autofocus), 584 ('raw', raw), 585 ) 586 587 if name is not None: 588 node[u'name'] = name 589 if readonly is not None: 590 if readonly: 591 node[u'readonly'] = self._xhtml and u'readonly' or None 592 else: 593 del node[u'readonly'] 594 if disabled is not None: 595 if disabled: 596 node[u'disabled'] = self._xhtml and u'disabled' or None 597 else: 598 del node[u'disabled'] 599 if required is not None: 600 if required: 601 node[u'required'] = self._xhtml and u'required' or None 602 else: 603 del node[u'required'] 604 if autofocus is not None: 605 if autofocus: 606 node[u'autofocus'] = self._xhtml and u'autofocus' or None 607 else: 608 del node[u'autofocus'] 609 if placeholder is not None: 610 node[u'placeholder'] = placeholder 611 if dirname is not None: 612 node[u'dirname'] = dirname 613 if value is None: 614 value, raw = self._param.getfirst(name, u''), False 615 if not raw: 616 value = self.normalize_newlines(value).rstrip() 617 if maxlength is not None: 618 value = value[:int(maxlength)] 619 node[u'maxlength'] = unicode(maxlength) 620 else: 621 del node[u'maxlength'] 622 if raw: 623 node.raw.content = value 624 else: 625 node.content = value 626 627 post_proc = self._post_proc 628 if post_proc is not None: 629 post_proc('textarea', node, dict( 630 name=name, value=value, maxlength=maxlength, 631 readonly=readonly, disabled=disabled, required=required, 632 placeholder=placeholder, dirname=dirname, 633 autofocus=autofocus, raw=raw 634 ))
635
636 - def select(self, node, name, options=None, selected=None, option="option", 637 disabled=None, required=None, autofocus=None, multiple=False):
638 r""" 639 Render a 'select' input control 640 641 This method actually renders two nodes, namely the ``select`` 642 element and the ``option`` element:: 643 644 <select tdi="node"> 645 <option tdi="*option">foo</option> 646 </select> 647 648 The option node is repeated as necessary (matching the entries of 649 the `options` parameter). If `options` is empty, the whole ``select`` 650 node is emptied. The option is usually flagged with an asterisk, so 651 it doesn't trigger an automatic render-method call. 652 653 :Parameters: 654 `node` : `tdi.nodetree.Node` 655 The 'select' input node 656 657 `name` : ``basestring`` 658 The name of the 'select' field 659 660 `options` : iterable 661 The list of option values. Each item is expected to 662 be a 2-tuple of the option value and its description. The value 663 is what's put into the option's ``value`` attribute and submitted 664 by the browser if the option is selected. The description is the 665 visible part of the option. If the value is ``None``, it's treated 666 unset and the description is submitted as selected value instead. 667 If `options` is ``None``, only the ``select`` element will be 668 touched. 669 670 `selected` : ``basestring`` or iterable 671 The pre-selected value. If it's unset or ``None``, it's 672 taken out of the request. If it does not appear in the request, 673 there just won't be any pre-selected option. If `multiple` is 674 true, `selected` is expected to be an *iterable* of 675 ``basestring``\s. 676 677 `option` : ``str`` 678 The node of the ``option`` node, relative to the 679 ``select`` node. The parameter is expected in dotted notation. 680 681 `disabled` : ``bool`` 682 Disabled field? If unset or ``None``, the attribute is left 683 untouched. 684 685 `required` : ``bool`` 686 Required field? (HTML5). If omitted or ``None``, the attribute 687 is left untouched. 688 689 `autofocus` : ``bool`` 690 Set autofocus? (HTML5). If omitted or ``None``, the attribute 691 is left untouched. 692 693 `multiple` : ``bool`` 694 Is it a multiselect box? `selected` is expected to 695 be an ``iterable`` containing multiple selected values in this 696 case. 697 """ 698 # pylint: disable = too-many-locals, too-many-branches 699 700 pre_proc = self._pre_proc 701 if pre_proc is not None: 702 ( 703 node, name, options, selected, option, disabled, 704 required, autofocus, multiple 705 ) = pre_proc( 706 'select', node, 707 708 ('name', name), 709 ('options', options), 710 ('selected', selected), 711 ('option', option), 712 ('disabled', disabled), 713 ('required', required), 714 ('autofocus', autofocus), 715 ('multiple', multiple), 716 ) 717 718 if name is not None: 719 node[u'name'] = name 720 if disabled is not None: 721 if disabled: 722 node[u'disabled'] = self._xhtml and u'disabled' or None 723 else: 724 del node[u'disabled'] 725 if required is not None: 726 if required: 727 node[u'required'] = self._xhtml and u'required' or None 728 else: 729 del node[u'required'] 730 if autofocus is not None: 731 if autofocus: 732 node[u'autofocus'] = self._xhtml and u'autofocus' or None 733 else: 734 del node[u'autofocus'] 735 736 if options is not None: 737 options = list(options) 738 partnodes = option.split('.') 739 partnodes.reverse() 740 optnode = node(partnodes.pop()) 741 while partnodes: 742 optnode = optnode(partnodes.pop()) 743 if multiple: 744 node[u'multiple'] = self._xhtml and u'multiple' or None 745 if options is not None: 746 if selected is None: 747 selected = self._param.getlist(name) 748 selected_ = dict([(item, None) for item in selected]) 749 else: 750 del node[u'multiple'] # just in case 751 if options is not None: 752 if selected is None: 753 selected = self._param.getfirst(name) 754 selected_ = {selected: None} 755 756 post_proc = self._post_proc 757 if post_proc is not None: 758 post_proc('select', node, dict( 759 name=name, options=options, selected=selected, 760 option=option, disabled=disabled, required=required, 761 autofocus=autofocus, multiple=multiple 762 )) 763 764 if options is not None: 765 for subnode, tup in optnode.iterate(options): 766 value, desc, disabled = tup[0], tup[1], tup[2:] 767 if value is not None: 768 is_selected = unicode(value) in selected_ 769 else: 770 is_selected = unicode(desc) in selected_ 771 self.option( 772 subnode, value, 773 774 description=desc, 775 selected=is_selected, 776 disabled=disabled and disabled[0] or None, 777 )
778
779 - def datalist(self, node, id=None, options=None, option="option"):
780 """ 781 Render a 'datalist' element (especially its options) 782 783 This method actually renders two nodes, namely the ``datalist`` 784 element and the ``option`` element:: 785 786 <datalist tdi="node"> 787 <option tdi="*option" /> 788 </datalist> 789 790 The option node is repeated as necessary (matching the entries of 791 the `options` parameter). If `options` is empty, the whole 792 ``datalist`` node is emptied. The option is usually flagged with an 793 asterisk, so it doesn't trigger an automatic render-method call. 794 795 :Parameters: 796 `node` : `tdi.nodetree.Node` 797 The 'datalist' node 798 799 `id` : ``basestring`` 800 The ``id`` attribute of the 'datalist' field. If omitted or 801 ``None``, the attribute is left untouched. 802 803 `options` : iterable 804 The list of option values. Each item is expected to 805 be a 2-tuple of the option value and its description. The value 806 is what's put into the option's ``value`` attribute. The 807 description is the visible part of the option and put into the 808 'label' attribute. If the value is ``None``, it's treated as 809 unset. If `options` is ``None``, only the ``datalist`` element 810 will be touched. 811 812 `option` : ``str`` 813 The node of the ``option`` node, relative to the 814 ``select`` node. The parameter is expected in dotted notation. 815 """ 816 # pylint: disable = invalid-name, redefined-builtin 817 818 pre_proc = self._pre_proc 819 if pre_proc is not None: 820 ( 821 node, id, options, option 822 ) = pre_proc( 823 'datalist', node, 824 825 ('id', id), 826 ('options', options), 827 ('option', option), 828 ) 829 830 if id is not None: 831 node[u'id'] = id 832 833 if options is not None: 834 options = list(options) 835 partnodes = option.split('.') 836 partnodes.reverse() 837 optnode = node(partnodes.pop()) 838 while partnodes: 839 optnode = optnode(partnodes.pop()) 840 841 post_proc = self._post_proc 842 if post_proc is not None: 843 post_proc('datalist', node, dict( 844 id=id, options=options, option=option 845 )) 846 847 if options is not None: 848 for subnode, tup in optnode.iterate(options): 849 value, desc, disabled = tup[0], tup[1], tup[2:] 850 self.option( 851 subnode, value, 852 853 label=desc, 854 disabled=disabled and disabled[0] or None, 855 )
856
857 - def option(self, node, value, description=None, selected=None, 858 disabled=None, label=None):
859 """ 860 Render a single option 861 862 :Parameters: 863 `node` : `tdi.nodetree.Node` 864 The option node 865 866 `value` : ``basestring`` 867 The option value, if ``None``, the attribute will be 868 removed. 869 870 `description` : ``basestring`` 871 The visible part of the option. If omitted or ``None``, the 872 element's content is left untouched. 873 874 `selected` : ``bool`` 875 Is the option selected? If unset or ``None`` the 876 attribute will be left untouched. 877 878 `disabled` : ``bool`` 879 Is this option disabled? If unset or ``None``, the 880 attribute will be left untouched. 881 882 `label` : ``basestring`` 883 Label attribute (HTML5). If omitted or ``None``, any existing 884 attribute is deleted. 885 """ 886 # pylint: disable = too-many-branches 887 888 pre_proc = self._pre_proc 889 if pre_proc is not None: 890 ( 891 node, value, description, selected, disabled, label 892 ) = pre_proc( 893 'option', node, 894 895 ('value', value), 896 ('description', description), 897 ('selected', selected), 898 ('disabled', disabled), 899 ('label', label), 900 ) 901 902 if value is None: 903 del node[u'value'] 904 else: 905 node[u'value'] = value 906 if label is None: 907 del node[u'label'] 908 else: 909 node[u'label'] = label 910 if selected is not None: 911 if selected: 912 node[u'selected'] = self._xhtml and u'selected' or None 913 else: 914 del node[u'selected'] 915 if disabled is not None: 916 if disabled: 917 node[u'disabled'] = self._xhtml and u'disabled' or None 918 else: 919 del node[u'disabled'] 920 if description is not None: 921 node.content = description 922 923 post_proc = self._post_proc 924 if post_proc is not None: 925 post_proc('option', node, dict( 926 value=value, description=description, selected=selected, 927 disabled=disabled, label=label, 928 ))
929
930 - def keygen(self, node, name, keytype=None, challenge=None, disabled=None, 931 autofocus=None):
932 """ 933 Render a 'keygen' input control 934 935 :Parameters: 936 `node` : `tdi.nodetree.Node` 937 The 'keygen' node 938 939 `name` : ``basestring`` 940 The name of the 'keygen' field 941 942 `keytype` : ``basestring`` 943 Optional keytype. If omitted or ``None``, the attribute is left 944 untouched. 945 946 `challenge` : ``basestring`` 947 Optional challenge value. If omitted or ``None``, the attribute is 948 left untouched. 949 950 `disabled` : ``bool`` 951 Disabled field? If unset or ``None``, the attribute is left 952 untouched. 953 954 `autofocus` : ``bool`` 955 Set autofocus? (HTML5). If omitted or ``None``, the attribute 956 is left untouched. 957 """ 958 pre_proc = self._pre_proc 959 if pre_proc is not None: 960 ( 961 node, name, keytype, challenge, disabled, autofocus 962 ) = pre_proc( 963 'keygen', node, 964 965 ('name', name), 966 ('keytype', keytype), 967 ('challenge', challenge), 968 ('disabled', disabled), 969 ('autofocus', autofocus), 970 ) 971 972 if name is not None: 973 node[u'name'] = name 974 if disabled is not None: 975 if disabled: 976 node[u'disabled'] = self._xhtml and u'disabled' or None 977 else: 978 del node[u'disabled'] 979 if autofocus is not None: 980 if autofocus: 981 node[u'autofocus'] = self._xhtml and u'autofocus' or None 982 else: 983 del node[u'autofocus'] 984 if keytype is not None: 985 node[u'keytype'] = keytype 986 if challenge is not None: 987 node[u'challenge'] = challenge 988 989 post_proc = self._post_proc 990 if post_proc is not None: 991 post_proc('keygen', node, dict( 992 name=name, keytype=keytype, challenge=challenge, 993 disabled=disabled, autofocus=autofocus 994 ))
995