--- loncom/interface/lonhtmlcommon.pm 2002/08/01 20:49:06 1.6 +++ loncom/interface/lonhtmlcommon.pm 2009/11/21 22:36:23 1.247 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common html routines # -# $Id: lonhtmlcommon.pm,v 1.6 2002/08/01 20:49:06 stredwic Exp $ +# $Id: lonhtmlcommon.pm,v 1.247 2009/11/21 22:36:23 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,274 +25,2399 @@ # # http://www.lon-capa.org/ # +###################################################################### +###################################################################### + +=pod + +=head1 NAME + +Apache::lonhtmlcommon - routines to do common html things + +=head1 SYNOPSIS + +Referenced by other mod_perl Apache modules. + +=head1 INTRODUCTION + +lonhtmlcommon is a collection of subroutines used to present information +in a consistent html format, or provide other functionality related to +html. + +=head2 General Subroutines + +=over 4 + +=cut + +###################################################################### +###################################################################### package Apache::lonhtmlcommon; use strict; +use Time::Local; +use Time::HiRes; +use Apache::lonlocal; +use Apache::lonnet; +use LONCAPA; + + +sub coursepreflink { + my ($text,$category)=@_; + if (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) { + return ''.$text.''; + } else { + return ''; + } +} + +############################################## +############################################## + +=pod + +=item confirm_success + +Successful completion of an operation message + +=cut + +sub confirm_success { + my ($message,$failure)=@_; + if ($failure) { + return ''."\n" + .''.&mt('Error').' '."\n" + .$message."\n" + .''."\n"; + } else { + return ''."\n" + .''.&mt('OK').' '."\n" + .$message."\n" + .''."\n"; + } +} + +############################################## +############################################## + +=pod + +=item dragmath_button + +Creates a button that launches a dragmath popup-window, in which an +expression can be edited and pasted as LaTeX into a specified textarea. + + textarea - Name of the textarea to edit. + helpicon - If true, show a help icon to the right of the button. + +=cut + +sub dragmath_button { + my ($textarea,$helpicon) = @_; + my $help_text; + if ($helpicon) { + $help_text = &Apache::loncommon::help_open_topic('Authoring_Math_Editor'); + } + my $buttontext=&mt('Edit Math'); + return <$help_text +ENDDRAGMATH +} -sub AscendOrderOptions { - my ($order, $page, $formName)=@_; +############################################## - my $OpSel1 = ''; - my $OpSel2 = ''; +=pod + +=item dragmath_js + +Javascript used to open pop-up window containing dragmath applet which +can be used to paste LaTeX into a textarea. + +=cut + +sub dragmath_js { + my ($popup) = @_; + return < + // + + +ENDDRAGMATHJS +} + + +############################################## +############################################## + +=pod + +=item authorbombs + +=cut + +############################################## +############################################## + +sub authorbombs { + my $url=shift; + $url=&Apache::lonnet::declutter($url); + my ($udom,$uname)=($url=~m{^($LONCAPA::domain_re)/($LONCAPA::username_re)/}); + my %bombs=&Apache::lonmsg::all_url_author_res_msg($uname,$udom); + foreach my $bomb (keys(%bombs)) { + if ($bomb =~ /^$udom\/$uname\//) { + return ''.&mt('Bomb').''. + &Apache::loncommon::help_open_topic('About_Bombs'); + } + } + return ''; +} - if($order eq 'Ascending') { - $OpSel1 = ' selected'; +############################################## +############################################## + +sub recent_filename { + my $area=shift; + return 'nohist_recent_'.&escape($area); +} + +sub store_recent { + my ($area,$name,$value,$freeze)=@_; + my $file=&recent_filename($area); + my %recent=&Apache::lonnet::dump($file); + if (scalar(keys(%recent))>20) { +# remove oldest value + my $oldest=time(); + my $delkey=''; + foreach my $item (keys(%recent)) { + my $thistime=(split(/\&/,$recent{$item}))[0]; + if (($thistime ne "always_include") && ($thistime<$oldest)) { + $oldest=$thistime; + $delkey=$item; + } + } + &Apache::lonnet::del($file,[$delkey]); + } +# store new value + my $timestamp; + if ($freeze) { + $timestamp = "always_include"; } else { - $OpSel2 = ' selected'; + $timestamp = time(); + } + &Apache::lonnet::put($file,{ $name => + $timestamp.'&'.&escape($value) }); +} + +sub remove_recent { + my ($area,$names)=@_; + my $file=&recent_filename($area); + return &Apache::lonnet::del($file,$names); +} + +sub select_recent { + my ($area,$fieldname,$event)=@_; + my %recent=&Apache::lonnet::dump(&recent_filename($area)); + my $return="\n\n"; + return $return; +} + +sub get_recent { + my ($area, $n) = @_; + my %recent=&Apache::lonnet::dump(&recent_filename($area)); + +# Create hash with key as time and recent as value +# Begin filling return_hash with any 'always_include' option + my %time_hash = (); + my %return_hash = (); + foreach my $item (keys(%recent)) { + my ($thistime,$thisvalue)=(split(/\&/,$recent{$item})); + if ($thistime eq 'always_include') { + $return_hash{$item} = &unescape($thisvalue); + $n--; + } else { + $time_hash{$thistime} = $item; + } } - my $Str = ''; - $Str .= ''."\n"; - return $Str; + return %return_hash; } -sub MapOptions { - my ($data, $page, $formName)=@_; - my $Str = ''; - $Str .= ''; + return $Str; +} + +############################################## +############################################## + +=pod + +=item checkbox - my $selected = 0; - foreach my $sequence (split(':',$data->{'orderedSequences'})) { - $Str .= '{$page.'Map'} eq $data->{$sequence.':title'}) { - $Str .= ' selected'; - $selected = 1; - } - $Str .= '>'.$data->{$sequence.':title'}.''."\n"; - } - $Str .= ' +// 31) { + document.$formname.$dname\_day.value = 31; + } + if ((month == 1) || (month == 3) || (month == 5) || + (month == 7) || (month == 8) || (month == 10) || + (month == 12)) { + if (day > 31) { + document.$formname.$dname\_day.value = 31; + day = 31; + } + } else if (month == 2 ) { + if ((year % 4 == 0) && (year % 100 != 0)) { + if (day > 29) { + document.$formname.$dname\_day.value = 29; + } + } else if (day > 29) { + document.$formname.$dname\_day.value = 28; + } + } else if (day > 30) { + document.$formname.$dname\_day.value = 30; + } + } + + function $dname\_disable() { + document.$formname.$dname\_month.disabled=true; + document.$formname.$dname\_day.disabled=true; + document.$formname.$dname\_year.disabled=true; + document.$formname.$dname\_hour.disabled=true; + document.$formname.$dname\_minute.disabled=true; + document.$formname.$dname\_second.disabled=true; } - $Str .= '>'."\n"; - my $selected=0; + function $dname\_enable() { + document.$formname.$dname\_month.disabled=false; + document.$formname.$dname\_day.disabled=false; + document.$formname.$dname\_year.disabled=false; + document.$formname.$dname\_hour.disabled=false; + document.$formname.$dname\_minute.disabled=false; + document.$formname.$dname\_second.disabled=false; + } - foreach (@$students) { - $Str .= '{$_.':fullname'}; - $Str .= ''."\n"; + + } +// ]]> + +ENDJS + $result .= ' '; + my $monthselector = qq{'; + # Day + my $dayselector = qq{}; + # Year + my $yearselector = qq{}; + # + my $hourselector = qq{\n"; + my $minuteselector = qq{}; + my $secondselector= qq{}; + my $cal_link; + if (!$nolink) { + $cal_link = qq{}; } + # + my $tzone = ' '.$tzname.' '; + if ($no_hh_mm_ss) { + $result .= &mt('[_1] [_2] [_3] ', + $monthselector,$dayselector,$yearselector). + $tzone; + if (!$nolink) { + $result .= &mt('[_1]Select Date[_2]',$cal_link,''); + } + } else { + $result .= &mt('[_1] [_2] [_3] [_4] [_5]m [_6]s ', + $monthselector,$dayselector,$yearselector, + $hourselector,$minuteselector,$secondselector). + $tzone; + if (!$nolink) { + $result .= &mt('[_1]Select Date[_2]',$cal_link,''); + } + } + $result .= "\n\n"; + return $result; +} - $Str .= 'from_epoch(epoch => $epoch) + ->set_time_zone(&Apache::lonlocal::gettimezone()); + my $tzname = $dt->time_zone_short_name(); + my $sec = $dt->second; + my $min = $dt->minute; + my $hour = $dt->hour; + my $mday = $dt->day; + my $month = $dt->month; + if ($month) { + $month --; } - $Str .= '>No Student Selected'."\n"; + my $year = $dt->year; + return ($tzname,$sec,$min,$hour,$mday,$month,$year); +} - $Str .= '= 0) && ($tmpsec < 60)) { + $sec = $tmpsec; + } + if (!defined($tmpsec) || $tmpsec eq '') { $sec = 0; } + } else { + $sec = 0; + } + if (defined($env{'form.'.$dname.'_minute'})) { + my $tmpmin = $env{'form.'.$dname.'_minute'}; + if (($tmpmin =~ /^\d+$/) && ($tmpmin >= 0) && ($tmpmin < 60)) { + $min = $tmpmin; + } + if (!defined($tmpmin) || $tmpmin eq '') { $min = 0; } + } else { + $min = 0; + } + if (defined($env{'form.'.$dname.'_hour'})) { + my $tmphour = $env{'form.'.$dname.'_hour'}; + if (($tmphour =~ /^\d+$/) && ($tmphour >= 0) && ($tmphour < 24)) { + $hour = $tmphour; + } + } else { + $hour = 0; + } + if (defined($env{'form.'.$dname.'_day'})) { + my $tmpday = $env{'form.'.$dname.'_day'}; + if (($tmpday =~ /^\d+$/) && ($tmpday > 0) && ($tmpday < 32)) { + $day = $tmpday; + } + } + if (defined($env{'form.'.$dname.'_month'})) { + my $tmpmonth = $env{'form.'.$dname.'_month'}; + if (($tmpmonth =~ /^\d+$/) && ($tmpmonth > 0) && ($tmpmonth < 13)) { + $month = $tmpmonth; + } + } + if (defined($env{'form.'.$dname.'_year'})) { + my $tmpyear = $env{'form.'.$dname.'_year'}; + if (($tmpyear =~ /^\d+$/) && ($tmpyear >= 1970)) { + $year = $tmpyear; + } + } + if (($year<1970) || ($year>2037)) { return undef; } + if (defined($sec) && defined($min) && defined($hour) && + defined($day) && defined($month) && defined($year)) { + my $timezone = &Apache::lonlocal::gettimezone(); + my $dt = DateTime->new( year => $year, + month => $month, + day => $day, + hour => $hour, + minute => $min, + second => $sec, + time_zone => $timezone, + ); + my $epoch_time = $dt->epoch; + if ($epoch_time ne '') { + return $epoch_time; + } else { + return undef; + } + } else { + return undef; + } +} + +############################################## +############################################## + +=pod + +=item &pjump_javascript_definition() + +Returns javascript defining the 'pjump' function, which opens up a +parameter setting wizard. + +=cut + +############################################## +############################################## +sub pjump_javascript_definition { + my $Str = <Active'."\n"; - $Str .= 'Expired'."\n"; - $Str .= 'Any'."\n"; + foreach my $type (['Active', &mt('Currently Has Access')], + ['Future', &mt('Will Have Future Access')], + ['Expired', &mt('Previously Had Access')], + ['Any', &mt('Any Access Status')]) { + my ($name,$label) = @$type; + $Str .= ''."\n"; + # + + + unless ($CourseBreadcrumbs) { + $links = htmltag('ol', $links, { id => "LC_MenuBreadcrumbs" }); + } else { + $links = htmltag('ul', $links, { class => "LC_CourseBreadcrumbs" }); + } + + if ($component) { + $links = htmltag('span', + ( $no_mt ? $component : mt($component) ). + ( $icons ? $icons : '' ), + { class => 'LC_breadcrumbs_component' } ) + .$links; + } + + #SD START (work in progress!) + add_tools(\$links); + #SD END + $links = htmltag('div', $links, + { id => "LC_breadcrumbs" }) unless ($CourseBreadcrumbs) ; + add_advtools(\$links); + + # Return the @Crumbs stack to what we started with + push(@Crumbs,$last); + shift(@Crumbs); + # Return the breadcrumb's line + return "$links"; } - $Str .= ''."\n"; - return $Str; + sub clear_breadcrumbs { + undef(@Crumbs); + undef(%tools); + } + + sub add_breadcrumb { + push(@Crumbs,@_); + } + + + #SD START (work in progress!) + sub add_breadcrumb_tool { + my ($category, $html) = @_; + return unless $html; + if (!defined(%tools)) { + my %tools = ( A => [], B => [], C => []); + } + push @{$tools{$category}}, $html; + } + + sub clear_breadcrumb_tools { + undef(%tools); + } + + sub add_tools { + my ($links) = @_; + return unless defined %tools; + my $html = '
    '; + for my $category ('A','B') { + $html .= '
  • ' + . '
      "; + for my $item (@{$tools{$category}}){ + #SD ugly! I'll fix that later on + $item =~ s/align="(right|left)"//; + $item =~ s///; + $html .= "
    • $item
    • "; + } + $html .= '
    '; + if ($category eq 'A') { $html .= "
  • $$links
  • "; } + } + $$links = $html.'
'; + } + + sub add_advtools { + my ($links) = @_; + return unless (defined $tools{'C'}) and (scalar (@{$tools{'C'}}) > 0); + my $html = start_funclist(); + for my $item (@{$tools{'C'}}){ + next unless $item; + $item =~ s/align="(right|left)"//; + $html .= add_item_funclist($item); + } + $html .= end_funclist(); + $html = Apache::loncommon::head_subbox($html); + $$links .= $html; + } + #SD END + +} # End of scope for @Crumbs + +############################################################ +############################################################ + +# Nested table routines. +# +# Routines to display form items in a multi-row table with 2 columns. +# Uses nested tables to divide form elements into segments. +# For examples of use see loncom/interface/lonnotify.pm +# +# Can be used in following order: ... +# &start_pick_box() +# row1 +# row2 +# row3 ... etc. +# &submit_row() +# &end_pick_box() +# +# where row1, row 2 etc. are chosen from &role_select_row,&course_select_row, +# &status_select_row and &email_default_row +# +# Can also be used in following order: +# +# &start_pick_box() +# &row_title() +# &row_closure() +# &row_title() +# &row_closure() ... etc. +# &submit_row() +# &end_pick_box() +# +# In general a &submit_row() call should proceed the call to &end_pick_box(), +# as this routine adds a button for form submission. +# &submit_row() does not require a &row_closure after it. +# +# &start_pick_box() creates a bounding table with 1-pixel wide black border. +# rows should be placed between calls to &start_pick_box() and &end_pick_box. +# +# &row_title() adds a title in the left column for each segment. +# &row_closure() closes a row with a 1-pixel wide black line. +# +# &role_select_row() provides a select box from which to choose 1 or more roles +# &course_select_row provides ways of picking groups of courses +# radio buttons: all, by category or by picking from a course picker pop-up +# note: by category option is only displayed if a domain has implemented +# selection by year, semester, department, number etc. +# +# &status_select_row() provides a select box from which to choose 1 or more +# access types (current access, prior access, and future access) +# +# &email_default_row() provides text boxes for default e-mail suffixes for +# different authentication types in a domain. +# +# &row_title() and &row_closure() are called internally by the &*_select_row +# routines, but can also be called directly to start and end rows which have +# needs that are not accommodated by the *_select_row() routines. + +{ # Start: row_count block for pick_box +my @row_count; + +sub start_pick_box { + my ($css_class) = @_; + if (defined($css_class)) { + $css_class = 'class="'.$css_class.'"'; + } else { + $css_class= 'class="LC_pick_box"'; + } + unshift(@row_count,0); + my $output = <<"END"; + +END + return $output; } -sub Title { - my ($pageName)=@_; +sub end_pick_box { + shift(@row_count); + my $output = <<"END"; +
+END + return $output; +} - my $Str = ''; +sub row_headline { + my $output = <<"END"; + +END + return $output; +} - $Str .= ''.$pageName.''."\n"; - $Str .= ''."\n"; - $Str .= ''."\n"; - $Str .= '
'; - $Str .= '

Course: '; - $Str .= $ENV{'course.'.$ENV{'request.course.id'}.'.description'}; - $Str .= '

'."\n"; - $Str .= ''; - $Str .= '
'."\n"; +sub row_title { + my ($title,$css_title_class,$css_value_class, $css_value_furtherAttributes) = @_; + $row_count[0]++; + my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row'; + $css_title_class ||= 'LC_pick_box_title'; + $css_title_class = 'class="'.$css_title_class.'"'; - return $Str; + $css_value_class ||= 'LC_pick_box_value'; + + if ($title ne '') { + $title .= ':'; + } + my $output = <<"ENDONE"; + + + $title + + +ENDONE + return $output; } -=pod +sub row_closure { + my ($no_separator) =@_; + my $output = <<"ENDTWO"; + + +ENDTWO + if (!$no_separator) { + $output .= <<"ENDTWO"; + + + + +ENDTWO + } + return $output; +} -=item &CreateTableHeadings() +} # End: row_count block for pick_box -This function generates the column headings for the chart. -=over 4 +sub role_select_row { + my ($roles,$title,$css_class,$show_separate_custom,$cdom,$cnum) = @_; + my $crstype = 'Course'; + if ($cdom ne '' && $cnum ne '') { + $crstype = &Apache::loncommon::course_type($cdom.'_'.$cnum); + } + my $output; + if (defined($title)) { + $output = &row_title($title,$css_class); + } + $output .= qq| + \n|; + if (defined($title)) { + $output .= &row_closure(); + } + return $output; +} + +sub course_select_row { + my ($title,$formname,$totcodes,$codetitles,$idlist,$idlist_titles, + $css_class,$crstype) = @_; + my $output = &row_title($title,$css_class); + $output .= &course_selection($formname,$totcodes,$codetitles,$idlist,$idlist_titles,$crstype); + $output .= &row_closure(); + return $output; +} + +sub course_selection { + my ($formname,$totcodes,$codetitles,$idlist,$idlist_titles,$crstype) = @_; + my $output = qq| + + |; + + my ($allcrs,$pickspec); + if ($crstype eq 'Community') { + $allcrs = &mt('All communities'); + $pickspec = &mt('Pick specific communities:'); + } else { + $allcrs = &mt('All courses'); + $pickspec = &mt('Pick specific course(s):'); + } -Inputs: $CacheData, $keyID, $headings, $spacePadding + my $courseform=''.&Apache::loncommon::selectcourse_link + ($formname,'pickcourse','pickdomain','coursedesc','',1,$crstype).''; + $output .= ''.$allcrs.'
'; + if ($totcodes > 0) { + my $numtitles = @$codetitles; + if ($numtitles > 0) { + $output .= ''.&mt('Pick courses by category:').'
'; + $output .= ''; + for (my $i=1; $i<$numtitles; $i++) { + $output .= ''; + } + $output .= '
'.$$codetitles[0].'
'."\n". + '
'.$$codetitles[$i].'
'."\n". + ''."\n". + '

'; + } + } + $output .= ''.$pickspec.' '.$courseform.'  selected.
'."\n"; + return $output; +} -$CacheData: pointer to a hash tied to the cached data database +sub status_select_row { + my ($types,$title,$css_class) = @_; + my $output; + if (defined($title)) { + $output = &row_title($title,$css_class,'LC_pick_box_select'); + } + $output .= qq| + \n|; + if (defined($title)) { + $output .= &row_closure(); + } + return $output; +} -$keyID: a pointer to an array containing the names of the data -held in a column and is used as part of a key into $CacheData +sub email_default_row { + my ($authtypes,$title,$descrip,$css_class) = @_; + my $output = &row_title($title,$css_class); + $output .= $descrip. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + ''.&mt('Authentication Method').''. + ''.&mt('Username -> e-mail conversion').''."\n". + &Apache::loncommon::end_data_table_header_row(); + my $rownum = 0; + foreach my $auth (sort(keys(%{$authtypes}))) { + my ($userentry,$size); + if ($auth =~ /^krb/) { + $userentry = ''; + $size = 25; + } else { + $userentry = 'username@'; + $size = 15; + } + $output .= &Apache::loncommon::start_data_table_row(). + ' '.$$authtypes{$auth}.''. + ''.$userentry. + ''. + &Apache::loncommon::end_data_table_row(); + } + $output .= &Apache::loncommon::end_data_table(); + $output .= &row_closure(); + return $output; +} -$headings: The names of the headings for the student information -$spacePadding: The spaces to go between columns +sub submit_row { + my ($title,$cmd,$submit_text,$css_class) = @_; + my $output = &row_title($title,$css_class,'LC_pick_box_submit'); + $output .= qq| +
+ +   +

+ \n|; + return $output; +} -Output: $Str +sub course_custom_roles { + my ($cdom,$cnum) = @_; + my %returnhash=(); + my %coursepersonnel=&Apache::lonnet::dump('nohist_userroles',$cdom,$cnum); + foreach my $person (sort(keys(%coursepersonnel))) { + my ($role) = ($person =~ /^([^:]+):/); + my ($end,$start) = split(/:/,$coursepersonnel{$person}); + if ($end == -1 && $start == -1) { + next; + } + if ($role =~ m|^cr/[^/]+/[^/]+/[^/]|) { + $returnhash{$role} ++; + } + } + return %returnhash; +} -$Str: A formatted string of the table column headings. -=back +############################################## +############################################## -=cut +# topic_bar +# +# Generates a div containing an (optional) numbered (static) image followed by a +# title with a background color defined in the corresponding CSS: LC_topic_bar +# Inputs: +# 1. number to display (corresponding static image should exist). +# img tag will be included if arg is an integer in the range 1 to 9. +# 2. title text to display. +# Outputs - a scalar containing html mark-up for the div. + +sub topic_bar { + my ($imgnum,$title) = @_; + my $imgtag; + if ($imgnum =~ /^[1-9]$/) { + $imgtag = ''.&mt('Step [_1]',$imgnum).' '; + } + return '
'.$imgtag.$title.'
'; +} + +############################################## +############################################## + +# echo_form_input +# +# Generates html markup to add form elements from the referrer page +# as hidden form elements (values encoded) in the new page. +# +# Intended to support two types of use +# (a) to allow backing up to earlier pages in a multi-page +# form submission process using a breadcrumb trail. +# +# (b) to allow the current page to be reloaded with form elements +# set on previous page to remain unchanged. An example would +# be where the a page containing a dynamically-built table of data is +# is to be redisplayed, with only the sort order of the data changed. +# +# Inputs: +# 1. Reference to array of form elements in the submitted form on +# the referrer page which are to be excluded from the echoed elements. +# +# 2. Reference to array of regular expressions, which if matched in the +# name of the form element n the referrer page will be omitted from echo. +# +# Outputs: A scalar containing the html markup for the echoed form +# elements (all as hidden elements, with values encoded). -sub CreateHeadings { - my ($data,$keyID,$headings,$displayString,$format)=@_; - my $Str=''; - my $formatting = ''; - - for(my $index=0; $index<(scalar @$headings); $index++) { - my $currentHeading=$headings->[$index]; - if($format eq 'preformatted') { - my @dataLength=split(//,$currentHeading); - my $length=scalar @dataLength; - $formatting = (' 'x - ($data->{$keyID->[$index].':columnWidth'}-$length)); - } - my $linkdata=$keyID->[$index]; - - my $tempString = $displayString; - $tempString =~ s/LINKDATA/$linkdata/; - $tempString =~ s/DISPLAYDATA/$currentHeading/; - $tempString =~ s/FORMATTING/$formatting/; - $Str .= $tempString; +sub echo_form_input { + my ($excluded,$regexps) = @_; + my $output = ''; + foreach my $key (keys(%env)) { + if ($key =~ /^form\.(.+)$/) { + my $name = $1; + my $match = 0; + if ((!@{$excluded}) || (!grep/^$name$/,@{$excluded})) { + if (defined($regexps)) { + if (@{$regexps} > 0) { + foreach my $regexp (@{$regexps}) { + if ($name =~ /\Q$regexp\E/) { + $match = 1; + last; + } + } + } + } + if (!$match) { + if (ref($env{$key})) { + foreach my $value (@{$env{$key}}) { + $value = &HTML::Entities::encode($value,'<>&"'); + $output .= ''."\n"; + } + } else { + my $value = &HTML::Entities::encode($env{$key},'<>&"'); + $output .= ''."\n"; + } + } + } + } } + return $output; +} - return $Str; +############################################## +############################################## + +# set_form_elements +# +# Generates javascript to set form elements to values based on +# corresponding values for the same form elements when the page was +# previously submitted. +# +# Last submission values are read from hidden form elements in referring +# page which have the same name, i.e., generated by &echo_form_input(). +# +# Intended to be called by onload event. +# +# Inputs: +# (a) Reference to hash of echoed form elements to be set. +# +# In the hash, keys are the form element names, and the values are the +# element type (selectbox, radio, checkbox or text -for textbox, textarea or +# hidden). +# +# (b) Optional reference to hash of stored elements to be set. +# +# If the page being displayed is a page which permits modification of +# previously stored data, e.g., the first page in a multi-page submission, +# then if stored is supplied, form elements will be set to the last stored +# values. If user supplied values are also available for the same elements +# these will replace the stored values. +# +# Output: +# +# javascript function - set_form_elements() which sets form elements, +# expects an argument: formname - the name of the form according to +# the DOM, e.g., document.compose + +sub set_form_elements { + my ($elements,$stored) = @_; + my %values; + my $output .= 'function setFormElements(courseForm) { +'; + if (defined($stored)) { + foreach my $name (keys(%{$stored})) { + if (exists($$elements{$name})) { + if (ref($$stored{$name}) eq 'ARRAY') { + $values{$name} = $$stored{$name}; + } else { + @{$values{$name}} = ($$stored{$name}); + } + } + } + } + + foreach my $key (keys(%env)) { + if ($key =~ /^form\.(.+)$/) { + my $name = $1; + if (exists($$elements{$name})) { + @{$values{$name}} = &Apache::loncommon::get_env_multiple($key); + } + } + } + + foreach my $name (keys(%values)) { + for (my $i=0; $i<@{$values{$name}}; $i++) { + $values{$name}[$i] = &HTML::Entities::decode($values{$name}[$i],'<>&"'); + $values{$name}[$i] =~ s/([\r\n\f]+)/\\n/g; + $values{$name}[$i] =~ s/"/\\"/g; + } + if (($$elements{$name} eq 'text') || ($$elements{$name} eq 'hidden')) { + my $numvalues = @{$values{$name}}; + if ($numvalues > 1) { + my $valuestring = join('","',@{$values{$name}}); + $output .= qq| + var textvalues = new Array ("$valuestring"); + var total = courseForm.elements['$name'].length; + if (total > $numvalues) { + total = $numvalues; + } + for (var i=0; i= 0) { + return true; + } + return false; + } +} +END + return $scripttag; +} + + +# USAGE: htmltag(element, content, {attribute => value,...}); +# +# EXAMPLES: +# - htmltag('a', 'this is an anchor', {href => 'www.example.com', +# title => 'this is a title'}) +# +# - You might want to set up needed tags like: +# +# my $h3 = sub { return htmltag( "h3", @_ ) }; +# +# ... and use them: $h3->("This is a headline") +# +# - To set up a couple of tags, see sub inittags +# +# NOTES: +# - Empty elements, such as
are correctly terminated, +# i.e. htmltag('br') returns
+# - Empty attributes (title="") are filtered out. +# - The function will not check for deprecated attributes. +# +# OUTPUT: content enclosed in xhtml conform tags +sub htmltag{ + return + qq|<$_[0]| + . join( '', map { qq| $_="${$_[2]}{$_}"| if ${$_[2]}{$_} } keys %{ $_[2] } ) + . ($_[1] ? qq|>$_[1]| : qq|/>|). "\n"; +}; + + +# USAGE: inittags(@tags); +# +# EXAMPLES: +# - my ($h1, $h2, $h3) = initTags( qw( h1 h2 h3 ) ) +# $h1->("This is a headline") #Returns:

This is a headline

+# +# NOTES: See sub htmltag for further information. +# +# OUTPUT: List of subroutines. +sub inittags { + my @tags = @_; + return map { my $tag = $_; + sub { return htmltag( $tag, @_ ) } + } @tags; +} + + +# USAGE: scripttag(scriptcode, [start|end|both]); +# +# EXAMPLES: +# - scripttag("alert('Hello World!')", 'both') +# returns: +# +# +# NOTES: +# - works currently only for javascripts +# +# OUTPUT: +# Scriptcode properly enclosed in