#!/usr/bin/perl # # This code was forked from the LiveJournal project owned and operated # by Live Journal, Inc. The code has been modified and expanded by # Dreamwidth Studios, LLC. These files were originally licensed under # the terms of the license supplied by Live Journal, Inc, which can # currently be found at: # # http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt # # In accordance with the original license, this code and all its # modifications are provided under the GNU General Public License. # A copy of that license can be found in the LICENSE file included as # part of this distribution. package LJ; use strict; # # name: LJ::html_datetime # class: component # des: Creates date and time control HTML form elements. # info: Parse output later with [func[LJ::html_datetime_decode]]. # args: # des-: # returns: # # It's the caller's responsibility to leave appropriate gaps if using tabindex sub html_datetime { my $opts = shift; my $lang = $opts->{'lang'} || "EN"; my ( $yyyy, $mm, $dd, $hh, $nn, $ss ); my $ret; my $name = $opts->{name} || ''; my $id = $opts->{id} || ''; my $disabled = $opts->{'disabled'} ? 1 : 0; my $tabindex = $opts->{tabindex}; my @tabindex_arg = (); @tabindex_arg = ( tabindex => $tabindex ) if defined $tabindex; my %extra_opts; foreach ( grep { !/^(name|id|disabled|seconds|notime|lang|default|tabindex)$/ } keys %$opts ) { $extra_opts{$_} = $opts->{$_}; } if ( $opts->{'default'} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)(?: (\d\d):(\d\d):(\d\d))?/ ) { ( $yyyy, $mm, $dd, $hh, $nn, $ss ) = ( $1 > 0 ? $1 : "", $2 + 0, $3 > 0 ? $3 + 0 : "", defined $4 && $4 > 0 ? $4 : "", defined $5 && $5 > 0 ? $5 : "", defined $6 && $6 > 0 ? $6 : "" ); } $ret .= html_select( { name => "${name}_mm", id => "${id}_mm", selected => sprintf( '%02d', $mm ), class => 'select', title => 'month', disabled => $disabled, @tabindex_arg, %extra_opts, }, map { sprintf( '%02d', $_ ), LJ::Lang::month_long_ml($_) } ( 1 .. 12 ) ); ++$tabindex_arg[1] if defined $tabindex; $ret .= html_text( { name => "${name}_dd", id => "${id}_dd", size => '2', class => 'text', maxlength => '2', value => $dd, title => 'day', disabled => $disabled, @tabindex_arg, %extra_opts, } ); ++$tabindex_arg[1] if defined $tabindex; $ret .= html_text( { name => "${name}_yyyy", id => "${id}_yyyy", size => '4', class => 'text', maxlength => '4', value => $yyyy, title => 'year', disabled => $disabled, @tabindex_arg, %extra_opts, } ); unless ( $opts->{notime} ) { $ret .= ' '; ++$tabindex_arg[1] if defined $tabindex; $ret .= html_text( { name => "${name}_hh", id => "${id}_hh", size => '2', maxlength => '2', value => $hh, title => 'hour', disabled => $disabled, @tabindex_arg, } ) . ':'; ++$tabindex_arg[1] if defined $tabindex; $ret .= html_text( { name => "${name}_nn", id => "${id}_nn", size => '2', maxlength => '2', value => $nn, title => 'minutes', disabled => $disabled, @tabindex_arg, } ); if ( $opts->{seconds} ) { $ret .= ':'; ++$tabindex_arg[1] if defined $tabindex; $ret .= html_text( { name => "${name}_ss", id => "${id}_ss", size => '2', maxlength => '2', value => $ss, title => 'seconds', disabled => $disabled, @tabindex_arg, } ); } } return $ret; } # # name: LJ::html_datetime_decode # class: component # des: Parses output of HTML form controls generated by [func[LJ::html_datetime]]. # info: # args: # des-: # returns: # sub html_datetime_decode { my $opts = shift; my $hash = shift; my $name = $opts->{name} || ''; return sprintf( "%04d-%02d-%02d %02d:%02d:%02d", $hash->{"${name}_yyyy"} || 0, $hash->{"${name}_mm"} || 0, $hash->{"${name}_dd"} || 0, $hash->{"${name}_hh"} || 0, $hash->{"${name}_nn"} || 0, $hash->{"${name}_ss"} || 0 ); } # # name: LJ::html_select # class: component # des: Creates a drop-down box or listbox HTML form element (the "; return $ret; } sub _html_option { my ( $value, $text, $item, $opts, $ehtml, $selref, $did_sel ) = @_; my $sel = ""; # multiple-mode or single-mode? if ( $selref && ( ref $selref eq 'HASH' ) && $selref->{$value} || defined $opts->{selected} && ( $opts->{selected} eq $value ) && !$$did_sel++ ) { $sel = " selected='selected'"; } $value = $ehtml ? ehtml($value) : $value; my $id = ''; if ( $opts->{include_ids} && $opts->{name} ne "" && $value ne "" ) { $id = " id='$opts->{'name'}_$value'"; } # is this individual option disabled? my $dis = $item->{disabled} ? " disabled='disabled' style='color: #999;'" : ''; # are there additional data-attributes? my $data_attribute = ''; my %item_data = $item->{data} ? %{ $item->{data} } : (); foreach ( keys %item_data ) { my $val = $item_data{$_} // ''; if ($ehtml) { $val = ehtml($val); } $data_attribute .= " data-$_='$val'"; } return "\n"; } # # name: LJ::html_check # class: component # des: Creates HTML checkbox button, and radio button controls. # info: Labels for checkboxes are through LJ::labelfy. # It does this safely, by not including any HTML elements in the label tag. # args: type, opts # des-type: Valid types are 'radio' or 'checkbox'. # des-opts: A hashref of options. Special options are: # 'disabled' - disables the element; # 'selected' - if multiple, an arrayref of selected values; otherwise, # a scalar equalling the selected value; # 'raw' - inserts value unescaped into select tag; # 'noescape' - won't escape key values if set to 1; # 'label' - label for checkbox; # returns: # sub html_check { my $opts = shift; my $disabled = $opts->{'disabled'} ? " disabled='disabled'" : ""; my $ehtml = $opts->{'noescape'} ? 0 : 1; my $ret; if ( $opts->{type} && $opts->{type} eq "radio" ) { $ret .= "{'selected'} ) { $ret .= " checked='checked'"; } if ( $opts->{'raw'} ) { $ret .= " $opts->{'raw'}"; } foreach ( grep { !/^(disabled|type|selected|raw|noescape|label)$/ } keys %$opts ) { $ret .= " $_=\"" . ( $ehtml ? ehtml( $opts->{$_} ) : $opts->{$_} ) . "\""; } $ret .= "$disabled />"; my $e_label = ( $ehtml ? ehtml( $opts->{'label'} ) : $opts->{'label'} ); $e_label = LJ::labelfy( $opts->{id}, $e_label ); $ret .= $e_label if $opts->{'label'}; return $ret; } # given a string and an id, return the string # in a label, respecting HTML sub labelfy { my ( $id, $text, $class ) = @_; $id = '' unless defined $id; $text = '' unless defined $text; $class = LJ::ehtml( $class || "" ); $class = qq{class="$class"} if $class; $text =~ s! ^([^<]+) ! !x; return $text; } # # name: LJ::html_text # class: component # des: Creates a text input field, for single-line input. # info: Allows 'type' => 'password' flag. # args: # des-: # returns: The generated HTML. # sub html_text { my $opts = shift; my $disabled = $opts->{'disabled'} ? " disabled='disabled'" : ""; my $ehtml = $opts->{'noescape'} ? 0 : 1; my $type = 'text'; $type = $opts->{type} if $opts->{type} && ( $opts->{type} eq 'password' || $opts->{type} eq 'search' ); my $ret = ''; $ret .= "{$_} ? $opts->{$_} : ''; $ret .= " $_=\"" . ( $ehtml ? LJ::ehtml($val) : $val ) . "\""; } if ( $opts->{'raw'} ) { $ret .= " $opts->{'raw'}"; } $ret .= "$disabled />"; return $ret; } # # name: LJ::html_textarea # class: component # des: Creates a text box for multi-line input (the "; return $ret; } # # name: LJ::html_color # class: component # des: A text field with attached color preview and button to choose a color. # info: Depends on the client-side Color Picker. # args: opts # des-opts: Valid options are: 'onchange' argument, which happens when # color picker button is clicked, or when focus is changed to text box; # 'disabled'; and 'raw' , for unescaped input. # returns: # sub html_color { my $opts = shift; my $htmlname = ehtml( $opts->{'name'} ); my $des = ehtml( $opts->{'des'} ) || "Pick a Color"; my $ret; # 'onchange' argument happens when color picker button is clicked, # or when focus is changed to text box $ret .= html_text( { 'size' => 8, 'maxlength' => 7, 'name' => $htmlname, 'id' => $htmlname, 'onfocus' => $opts->{'onchange'}, 'disabled' => $opts->{'disabled'}, 'value' => $opts->{'default'}, 'noescape' => 1, 'raw' => $opts->{'raw'} . " data-coloris", } ); # A little help for the non-JavaScript folks $ret .= ""; return $ret; } # # name: LJ::html_hidden # class: component # des: Makes the HTML for a hidden form element. # args: name, val, opts # des-name: Name of form element (will be HTML escaped). # des-val: Value of form element (will be HTML escaped). # des-opts: Can optionally take arguments that are hashrefs # and extract name/value/other standard keys from that. Can also be # mixed with the older style key/value array calling format. # returns: HTML # sub html_hidden { my $ret; while (@_) { my $name = shift; my $val; my $ehtml = 1; my $extra = ''; if ( ref $name eq 'HASH' ) { my $opts = $name; $val = $opts->{value}; $name = $opts->{name}; $ehtml = $opts->{'noescape'} ? 0 : 1; foreach ( grep { !/^(name|value|raw|noescape)$/ } keys %$opts ) { $extra .= " $_=\"" . ( $ehtml ? ehtml( $opts->{$_} ) : $opts->{$_} ) . "\""; } $extra .= " $opts->{'raw'}" if $opts->{'raw'}; } else { $val = shift; } $ret .= " # name: LJ::html_submit # class: component # des: Makes the HTML for a submit button. # info: If only one argument is given it is # assumed LJ::html_submit(undef, 'value') was meant. # args: name, val, opts?, type # des-name: Name of form element (will be HTML escaped). # des-val: Value of form element, and label of button (will be HTML escaped). # des-opts: Optional hashref of additional tag attributes. # A hashref of options. Special options are: # 'raw' - inserts value unescaped into select tag; # 'disabled' - disables the element; # 'noescape' - won't escape key values if set to 1; # des-type: Optional. Value format is type => (submit|reset). Defaults to submit. # returns: HTML # sub html_submit { my ( $name, $val, $opts ) = @_; # if one argument, assume (undef, $val) if ( @_ == 1 ) { $val = $name; $name = undef; } my ( $eopts, $disabled, $raw ) = ( '', '', '' ); my $type = 'submit'; my $ehtml; if ( $opts && ref $opts eq 'HASH' ) { $disabled = " disabled='disabled'" if $opts->{'disabled'}; $raw = " $opts->{'raw'}" if $opts->{'raw'}; $type = 'reset' if $opts->{type} && $opts->{type} eq 'reset'; $type = 'button' if $opts->{type} && $opts->{type} eq 'button'; $ehtml = $opts->{'noescape'} ? 0 : 1; foreach ( grep { !/^(raw|disabled|noescape|type)$/ } keys %$opts ) { $eopts .= " $_=\"" . ( $ehtml ? ehtml( $opts->{$_} ) : $opts->{$_} ) . "\""; } } my $ret = "