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