1=begin properties 2 3constructor 4canonicalizer 5available_values 6ajax_validates 7autocompleter 8 9default_value 10valid_values 11validator 12render_as 13label 14hints 15placeholder 16display_length 17max_length 18mandatory 19 20=end properties 21 22=cut 23 24use warnings; 25use strict; 26 27package Jifty::Web::Form::Field; 28 29=head1 NAME 30 31Jifty::Web::Form::Field - Web input of some sort 32 33=head1 DESCRIPTION 34 35Describes a form input in a L<Jifty::Action>. 36C<Jifty::Web::Form::Field>s know what action they are on, and acquire 37properties from the L<Jifty::Action> which they are part of. Each key 38in the L<Jifty::Action/arguments> hash becomes a 39C<Jifty::Web::Form::Field> whose L</name> is that key. 40 41C<Jifty::Web::Form::Field>s stringify using the L</render> method, to 42aid in placing them in L<HTML::Mason> components. 43 44=cut 45 46use base 'Jifty::Web::Form::Element'; 47 48use Scalar::Util qw/weaken/; 49use Scalar::Defer qw/force/; 50use HTML::Entities; 51 52# We need the anonymous sub because otherwise the method of the base class is 53# always called, instead of the appropriate overridden method in a child class. 54use overload '""' => sub { shift->render }, bool => sub { 1 }; 55 56=head2 new 57 58Creates a new L<Jifty::Web::Form::Field> (possibly magically blessing into a subclass). 59Should only be called from C<< $action->arguments >>. 60 61=cut 62 63sub new { 64 my $class = shift; 65 my $self = $class->SUPER::new( 66 { type => 'text', 67 class => '', 68 input_name => '', 69 default_value => '', 70 sticky_value => '', 71 render_mode => 'update' }); 72 my $args = ref($_[0]) ? $_[0] : {@_}; 73 74 $self->rebless( $args->{render_as} || $args->{type} || 'text' ); 75 76 for my $field ( $self->accessors() ) { 77 $self->$field( $args->{$field} ) if exists $args->{$field}; 78 } 79 80 # If they key and/or value imply that this argument is going to be 81 # a mapped argument, then do the mapping and mark the field as hidden. 82 # but ignore that if the field is a container in the model 83 my ($key, $value) = Jifty::Request::Mapper->query_parameters($self->input_name, $self->current_value); 84 if ($key ne $self->input_name && !$self->action->arguments->{$self->name}{container}) { 85 $self->rebless('Hidden'); 86 $self->input_name($key); 87 $self->default_value($value); 88 $self->sticky_value(undef); 89 } 90 91 # now that the form field has been instantiated, register the action with the form. 92 if ($self->action and Jifty->web->form->is_open and not (Jifty->web->form->printed_actions->{$self->action->moniker})) { 93 Jifty->web->form->register_action( $self->action); 94 Jifty->web->form->print_action_registration($self->action->moniker); 95 } 96 return $self; 97} 98 99=head2 $self->rebless($widget) 100 101Turn the current blessed class into the given widget class. 102 103=cut 104 105sub rebless { 106 my ($self, $widget) = @_; 107 my $widget_class = $widget =~ m/::/ ? $widget : "Jifty::Web::Form::Field::".ucfirst($widget); 108 109 $self->log->error("Invalid widget class $widget_class") 110 unless Jifty::Util->require($widget_class); 111 112 bless $self, $widget_class; 113} 114 115=head2 accessors 116 117Lists the accessors that are able to be called from within a call to 118C<new>. Subclasses should extend this list. 119 120=cut 121 122my @new_fields = qw( 123 name type sticky sticky_value default_value action 124 mandatory ajax_validates ajax_canonicalizes autocompleter preamble hints 125 placeholder focus render_mode display_length max_length _element_id 126 disable_autocomplete multiple attributes 127); 128 129my @semiexposed_fields = qw( 130 label input_name available_values 131); 132 133sub accessors { 134 shift->SUPER::accessors(), @new_fields, @semiexposed_fields; 135} 136 137__PACKAGE__->mk_accessors(@new_fields, map { "_$_" } @semiexposed_fields); 138 139=head2 name [VALUE] 140 141Gets or sets the name of the field. This is separate from the name of 142the label (see L</label>) and the form input name (see 143L</input_name>), though both default to this name. This name should 144match to a key in the L<Jifty::Action/arguments> hash. If this 145C<Jifty::Web::Form::Field> was created via L<Jifty::Action/form_field>, 146this is automatically set for you. 147 148=head2 class [VALUE] 149 150Gets or sets the CSS display class applied to the label and widget. 151 152=head2 type [VALUE] 153 154Gets or sets the type of the HTML <input> field -- that is, 'text' or 155'password'. Defaults to 'text'. 156 157=head2 key_binding VALUE 158 159Sets this form field's "submit" key binding to VALUE. 160 161=head2 key_binding_label VALUE 162 163Sets this form field's key binding label to VALUE. If none is specified 164the normal label is used. 165 166=head2 default_value [VALUE] 167 168Gets or sets the default value for the form. 169 170=head2 sticky_value [VALUE] 171 172Gets or sets the value for the form field that was submitted in the last action. 173 174=head2 mandatory [VALUE] 175 176A boolean indicating that the argument B<must> be present when the 177user submits the form. 178 179=head2 focus [VALUE] 180 181If true, put focus on this form field when the page loads. 182 183=head2 ajax_validates [VALUE] 184 185A boolean value indicating if user input into an HTML form field for 186this argument should be L<validated|Jifty::Manual::Glossary/validate> 187via L<AJAX|Jifty::Manual::Glossary/AJAX> as the user fills out the 188form, instead of waiting until submit. Arguments will B<always> be 189validated before the action is run, whether or not they also 190C<ajax_validate>. 191 192=head2 ajax_canonicalizes [VALUE] 193 194A boolean value indicating if user input into an HTML form field for 195this argument should be L<canonicalized|Jifty::Manual::Glossary/canonicalize> 196via L<AJAX|Jifty::Manual::Glassary/AJAX> as the user fills out the 197form, instead of waiting until submit. Arguments will B<always> be 198canonicalized before the action is run, whether or not they also 199C<ajax_canonicalize> 200 201=head2 disable_autocomplete [VALUE] 202 203Gets or sets whether to disable _browser_ autocomplete for this field. 204 205=head2 preamble [VALUE] 206 207Gets or sets the preamble located in front of the field. 208 209=head2 multiple [VALUE] 210 211A boolean indicating that the field is multiple. 212aka. has multiple attribute, which is useful for select field. 213 214=head2 id 215 216For the purposes of L<Jifty::Web::Form::Element>, the unique id of 217this field is its input name. 218 219=head2 attributes 220 221If the field object is generated by L<Jifty::Action::Record>, this 222holds the additional attributes declared to the corresponding column 223of the model. 224 225=cut 226 227sub id { 228 my $self = shift; 229 return $self->input_name; 230} 231 232=head2 input_name [VALUE] 233 234Gets or sets the form field input name, as it is rendered in the HTML. 235If we've been explicitly named, return that, otherwise return a name 236based on the moniker of the action and the name of the form. 237 238=cut 239 240sub input_name { 241 my $self = shift; 242 243# If we've been explicitly handed a name, we should run with it. 244# Otherwise, we should ask our action, how to turn our "name" 245# into a form input name. 246 247 my $ret = $self->_input_name(@_); 248 return $ret if $ret; 249 250 my $action = $self->action; 251 return $action ? $self->action->form_field_name( $self->name ) 252 : ''; 253} 254 255 256=head2 fallback_name 257 258Return the form field's fallback name. This should be used to create a 259hidden input with a value of 0 to accompany checkboxes or to let 260comboboxes fall back to the text input if, and only if no value is 261selected from the SELECT. (We use this order, so that we can stick the 262label and not the value in the INPUT field. To make that work, we also need 263to clear the SELECT after the user types in the INPUT. 264 265=cut 266 267sub fallback_name { 268 my $self = shift; 269 270 if ($self->action) { 271 return $self->action->fallback_form_field_name( $self->name ); 272 } 273 else { 274 # XXX TODO, we should have a more graceful way to compose these in the absence of an action 275 my $name = $self->input_name; 276 $name =~ s/^J:A:F/J:A:F:F/; 277 return($name) 278 } 279} 280 281 282=head2 label [VALUE] 283 284Gets or sets the label on the field. This defaults to the name of the 285object. 286 287=cut 288 289sub label { 290 my $self = shift; 291 my $val = $self->_label(@_); 292 defined $val ? $val : $self->name; 293 294} 295 296=head2 hints [VALUE] 297 298Hints for the user to explain this field 299 300=cut 301 302sub hints { 303 my $self = shift; 304 return $self->_hints_accessor unless @_; 305 306 my $hint = shift; 307 # people sometimes say hints are "foo" rather than hints is "foo" 308 if (ref $hint eq 'ARRAY') { 309 $hint = shift @$hint; 310 } 311 return $self->_hints_accessor($hint); 312} 313 314 315=head2 element_id 316 317Returns a unique C<id> attribute for this field based on the field name. This is 318consistent for the life of the L<Jifty::Web::Form::Field> object but isn't predictable; 319 320=cut 321 322 323sub element_id { 324 my $self = shift; 325 return $self->_element_id || $self->_element_id( $self->input_name ."-".Jifty->web->serial); 326} 327 328=head2 action [VALUE] 329 330Gets or sets the L<Jifty::Action> object that this 331C<Jifty::Web::Form::Field> is associated with. This is called 332automatically if this C<Jifty::Action> was created via 333L<Jifty::Web::Form::Field/form_field>. 334 335=cut 336 337sub action { 338 my $self = shift; 339 340 if (@_) { 341 $self->{action} = shift; 342 343 # weaken our circular reference 344 weaken $self->{action}; 345 } 346 347 return $self->{action}; 348 349} 350 351=head2 current_value 352 353Gets the current value we should be using for this form field. 354 355If the argument is marked as "sticky" (default) and there is a value for this 356field from a previous action submit AND that action did not have a "success" 357response, returns that submit field's value. Otherwise, returns the action's argument's 358default_value for this field. 359 360=cut 361 362sub current_value { 363 my $self = shift; 364 365 if ($self->sticky_value and $self->sticky) { 366 return $self->sticky_value; 367 } else { 368 # the force is here because very often this will be a Scalar::Defer object that we REALLY 369 # want to be able to check definedness on. 370 # Because of a core limitation in perl, Scalar::Defer can't return undef for an object. 371 return force $self->default_value; 372 } 373} 374 375=head2 render 376 377Outputs this form element in a span with class C<form_field>. This 378outputs the label, the widget itself, any hints, any errors, and any 379warnings using L</render_label>, L</render_widget>, L</render_hints>, 380L</render_errors>, and L</render_warnings>, respectively. Returns an 381empty string. 382 383This is also what C<Jifty::Web::Form::Field>s do when stringified. 384 385=cut 386 387sub render { 388 my $self = shift; 389 $self->render_wrapper_start(); 390 $self->render_preamble(); 391 392 393 $self->render_label(); 394 if ($self->render_mode eq 'update') { 395 $self->render_widget(); 396 $self->render_inline_javascript(); 397 $self->render_hints(); 398 $self->render_errors(); 399 $self->render_warnings(); 400 $self->render_canonicalization_notes(); 401 } elsif ($self->render_mode eq 'read'){ 402 $self->render_value(); 403 $self->render_preload_javascript(); 404 } 405 $self->render_wrapper_end(); 406 return (''); 407} 408 409=head2 render_inline_javascript 410 411Render a <script> tag (if necessary) containing any inline javascript that 412should follow this form field. This is used to add an autocompleter, 413placeholder, keybinding, or preloading to form fields where needed. 414 415=cut 416 417sub render_inline_javascript { 418 my $self = shift; 419 420 my $javascript; 421 422 $javascript = join( 423 "\n", 424 grep {$_} ( 425 $self->autocomplete_javascript(), 426 $self->placeholder_javascript(), 427 $self->key_binding_javascript(), 428 $self->preload_javascript(), 429 ) 430 ); 431 432 if($javascript =~ /\S/) { 433 Jifty->web->out(qq{<script type="text/javascript">$javascript</script> 434}); 435 } 436} 437 438=head2 render_preload_javascript 439 440Render a <script> tag (if necessary) containing any inline preload javascript 441that should follow this form field. 442 443=cut 444 445sub render_preload_javascript { 446 my $self = shift; 447 448 my $javascript = $self->preload_javascript; 449 450 if($javascript =~ /\S/) { 451 Jifty->web->out(qq{<script type="text/javascript">$javascript</script> 452}); 453 } 454} 455 456=head2 classes 457 458Renders a default CSS class for each part of our widget. 459 460=cut 461 462 463sub classes { 464 my $self = shift; 465 my $name = $self->name; 466 return join(' ', ($self->class||()), ($name ? "argument-".$name : ())); 467} 468 469 470=head2 render_wrapper_start 471 472Output the start of div that wraps the form field 473 474=cut 475 476sub render_wrapper_start { 477 my $self = shift; 478 my @classes = qw(form_field); 479 if ($self->mandatory) { push @classes, 'mandatory' } 480 if ($self->name) { push @classes, 'argument-'.$self->name } 481 Jifty->web->out('<div class="'.join(' ', @classes).'">' ."\n"); 482} 483 484=head2 render_wrapper_end 485 486Output the div that wraps the form field 487 488=cut 489 490sub render_wrapper_end { 491 my $self = shift; 492 Jifty->web->out("</div>"."\n"); 493} 494 495=head2 render_preamble 496 497Outputs the preamble of this form field, using a <span> HTML element 498with CSS class C<preamble> and whatever L</class> specifies. Returns an 499empty string. 500 501Use this for sticking instructions right in front of a widget 502 503=cut 504 505 506sub render_preamble { 507 my $self = shift; 508 Jifty->web->out( 509qq!<span class="preamble @{[$self->classes]}">@{[_($self->preamble) || '' ]}</span>\n! 510 ); 511 512 return ''; 513} 514 515=head2 render_label 516 517Outputs the label of this form field, using a <label> HTML element 518with the CSS class C<label> and whatever L</class> specifies. Returns 519an empty string. 520 521=cut 522 523sub render_label { 524 my $self = shift; 525 return '' unless defined $self->label and length $self->label; 526 if ( $self->render_mode eq 'update' ) { 527 Jifty->web->out( 528qq!<label class="label @{[$self->classes]}" for="@{[$self->element_id ]}">@{[_($self->label) ]}</label>\n! 529 ); 530 } else { 531 Jifty->web->out( 532 qq!<span class="label @{[$self->classes]}">@{[_($self->label) ]}</span>\n! 533 ); 534 } 535 536 return ''; 537} 538 539 540=head2 render_widget 541 542Outputs the actual entry widget for this form element. This defaults 543to an <input> element, though subclasses commonly override this. 544Returns an empty string. 545 546=cut 547 548sub render_widget { 549 my $self = shift; 550 my $field = qq! <input !; 551 $field .= qq! type="@{[ $self->type ]}"!; 552 $field .= qq! name="@{[ $self->input_name ]}"! if ($self->input_name); 553 $field .= qq! title="@{[ $self->title ]}"! if ($self->title); 554 $field .= qq! id="@{[ $self->element_id ]}"!; 555 $field .= qq! value="@{[$self->canonicalize_value(Jifty->web->escape($self->current_value))]}"! if defined $self->current_value; 556 $field .= $self->_widget_class; 557 558 if ($self->display_length) { 559 $field .= qq! size="@{[ $self->display_length() ]}"!; 560 } 561 elsif ($self->max_length) { 562 $field .= qq! size="@{[ $self->max_length() ]}"!; 563 } 564 565 $field .= qq! maxlength="@{[ $self->max_length() ]}"! if ($self->max_length()); 566 $field .= qq! autocomplete="off"! if defined $self->disable_autocomplete; 567 $field .= " " .$self->other_widget_properties; 568 $field .= $self->javascript; 569 $field .= qq! />\n!; 570 Jifty->web->out($field); 571 return ''; 572} 573 574 575=head2 canonicalize_value 576 577Called when a value is about to be displayed. Can be overridden to, for example, 578display only the C<YYYY-MM-DD> portion of a L<DateTime>. 579 580=cut 581 582sub canonicalize_value { 583 my $self = shift; 584 return $_[0]; 585} 586 587=head2 other_widget_properties 588 589If your widget subclass has other properties it wants to insert into the html 590of the main widget and you haven't subclassed render_widget then you can just 591subclass this. 592 593If you have subclassed render_widget then just stick them in your local sub 594render_widget. 595 596We use this for marking password fields as not-autocomplete so the browser does 597not try to use its form autocompletion on them. 598 599 600=cut 601 602sub other_widget_properties {''} 603 604=head2 _widget_class 605 606Returns the "class=" line for our widget. Optionally takes extra classes to append to our list. 607 608=cut 609 610sub _widget_class { 611 my $self = shift; 612 my @classes = ( 'widget', 613 $self->classes, 614 ( $self->ajax_validates ? ' ajaxvalidation' : () ), 615 ( $self->ajax_canonicalizes ? ' ajaxcanonicalization' : () ), 616 ( $self->autocompleter ? ' ajaxautocompletes' : () ), 617 ( $self->focus ? ' focus' : ()), 618 @_ ); 619 620 return qq! class="!. join(' ',@classes). qq!"! 621 622} 623 624=head2 render_value 625 626Renders a "view" version of the widget for field. Usually, this is just plain text. 627 628=cut 629 630 631sub render_value { 632 my $self = shift; 633 my $field = '<span'; 634 $field .= qq! class="@{[ $self->classes ]} value"> !; 635 # XXX: force stringify the value because maketext is buggy with overloaded objects. 636 $field .= $self->canonicalize_value(Jifty->web->escape("@{[$self->current_value]}")) if defined $self->current_value; 637 $field .= qq!</span>\n!; 638 Jifty->web->out($field); 639 return ''; 640} 641 642 643 644=head2 render_autocomplete 645 646Renders the div tag and javascript necessary to do autocompletion for 647this form field. Deprecated internally in favor of L</autocomplete_javascript>, 648but kept for backwards compatibility since there exists external code that uses 649it. 650 651=cut 652 653sub render_autocomplete { 654 my $self = shift; 655 return unless $self->autocompleter; 656 Jifty->web->out(qq!<script type="text/javascript">@{[$self->autocomplete_javascript]}</script>!); 657 return ''; 658} 659 660=head2 autocomplete_javascript 661 662Returns renders the tiny snippet of javascript to make an autocomplete 663call, if necessary. 664 665=cut 666 667sub autocomplete_javascript { 668 my $self = shift; 669 return unless($self->autocompleter); 670 my $element_id = $self->element_id; 671 return "jQuery(document).ready(function(){Jifty.addAutocompleter('$element_id');});" 672} 673 674=head2 placeholder_javascript 675 676Returns the javascript necessary to insert a placeholder into this 677form field (greyed-out text that is written in using javascript, and 678vanishes when the user focuses the field). 679 680=cut 681 682sub placeholder_javascript { 683 my $self = shift; 684 return unless $self->placeholder; 685 my $placeholder = $self->placeholder; 686 $placeholder =~ s{(['\\])}{\\$1}g; 687 $placeholder =~ s{\n}{\\n}g; 688 $placeholder =~ s{\r}{\\r}g; 689 return qq!new Jifty.Placeholder('@{[$self->element_id]}', '$placeholder');!; 690} 691 692=head2 preload_javascript 693 694Returns the javascript necessary to load regions that have been marked for 695preloading, as plain javascript. The L</javascript> method will look for 696regions marked with preloading and swap them in, instead of loading them 697directly. 698 699=cut 700 701sub preload_javascript { 702 my $self = shift; 703 704 my $structure = $self->_javascript_attrs_structure; 705 my @javascript; 706 for my $trigger (keys %$structure) { 707 my $trigger_structure = $structure->{$trigger}; 708 next unless $trigger_structure->{preload_key}; 709 710 my @preloaded; 711 712 my $preload_json = Jifty::JSON::encode_json( 713 { 714 fragments => $trigger_structure->{fragments}, 715 preload_key => $trigger_structure->{preload_key}, 716 } 717 ); 718 719 push @javascript, "Jifty.preload($preload_json, this);"; 720 } 721 722 return join "\n", @javascript; 723} 724 725=head2 render_hints 726 727Renders any hints for using this input. Defaults to nothing, though 728subclasses commonly override this. Returns an empty string. 729 730=cut 731 732sub render_hints { 733 my $self = shift; 734 Jifty->web->out( 735qq!<span class="hints @{[$self->classes]}">@{[_($self->hints) || '']}</span>\n! 736 ); 737 738 return ''; 739 740} 741 742 743=head2 render_errors 744 745Outputs a <div> with any errors for this action, even if there are 746none -- AJAX could fill it in. 747 748=cut 749 750sub render_errors { 751 my $self = shift; 752 753 return unless $self->action; 754 755 Jifty->web->out( 756qq!<span class="error @{[$self->classes]}" id="@{[$self->action->error_div_id($self->name)]}">@{[ $self->action->result->field_error( $self->name ) || '']}</span>\n! 757 ); 758 return ''; 759} 760 761=head2 render_warnings 762 763Outputs a <div> with any warnings for this action, even if there are 764none -- AJAX could fill it in. 765 766=cut 767 768sub render_warnings { 769 my $self = shift; 770 771 return unless $self->action; 772 773 Jifty->web->out( 774qq!<span class="warning @{[$self->classes]}" id="@{[$self->action->warning_div_id($self->name)]}">@{[ $self->action->result->field_warning( $self->name ) || '']}</span>\n! 775 ); 776 return ''; 777} 778 779=head2 render_canonicalization_notes 780 781Outputs a <div> with any canonicalization notes for this action, even if there are 782none -- AJAX could fill it in. 783 784=cut 785 786sub render_canonicalization_notes { 787 my $self = shift; 788 789 return unless $self->action; 790 791 Jifty->web->out( 792qq!<span class="canonicalization_note @{[$self->classes]}" id="@{[$self->action->canonicalization_note_div_id($self->name)]}">@{[$self->action->result->field_canonicalization_note( $self->name ) || '']}</span>\n! 793 ); 794 return ''; 795} 796 797=head2 available_values 798 799Returns the available values for this field. 800 801=cut 802 803sub available_values { 804 my $self = shift; 805 806 # Setter 807 if (@_) { 808 return $self->_available_values(@_); 809 } 810 811 # If the available_values are available in the field.. 812 if (my $available = $self->_available_values) { 813 return @$available; 814 } 815 816 # Otherwise consult the action 817 818 my $values = $self->action->available_values($self->name); 819 if (!ref($values) || ref($values) ne 'ARRAY') { 820 die "available_values of parameter '" . $self->name . "' returned a " . (ref($values) || 'nonreference') . ", expected array reference"; 821 } 822 823 return @$values; 824} 825 826=for private 827 828=head2 length 829 830# Deprecated API 831 832 833=cut 834 835sub length { 836 my $self = shift; 837 Carp::carp("->length is deprecated; use ->max_length instead"); 838 $self->max_length(@_); 839} 840 841 8421; 843