--- loncom/interface/lonhtmlcommon.pm	2017/05/07 13:57:58	1.385
+++ loncom/interface/lonhtmlcommon.pm	2021/06/07 06:15:55	1.399
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common html routines
 #
-# $Id: lonhtmlcommon.pm,v 1.385 2017/05/07 13:57:58 raeburn Exp $
+# $Id: lonhtmlcommon.pm,v 1.399 2021/06/07 06:15:55 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -78,7 +78,11 @@ sub java_not_enabled {
 sub coursepreflink {
    my ($text,$category)=@_;
    if (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) {
-      return '<a target="_top" href="'.&HTML::Entities::encode("/adm/courseprefs?phase=display&actions=$category",'<>&"').'"><span class="LC_setting">'.$text.'</span></a>';
+       my $target =' target="_top"';
+       if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
+           $target ='';
+       }
+       return '<a'.$target.' href="'.&HTML::Entities::encode("/adm/courseprefs?phase=display&actions=$category",'<>&"').'"><span class="LC_setting">'.$text.'</span></a>';
    } else {
       return '';
    }
@@ -92,7 +96,7 @@ sub raw_href_to_link {
 
 sub entity_encode {
     my ($text)=@_;
-    return &HTML::Entities::encode($text, '<>&"');
+    return &HTML::Entities::encode($text, '\'<>&"');
 }
 
 sub direct_parm_link {
@@ -101,9 +105,13 @@ sub direct_parm_link {
     $filter=&entity_encode($filter);
     $part=&entity_encode($part);
     if (($symb) && (&Apache::lonnet::allowed('opa')) && ($target ne 'tex')) {
-       return "<a target='_top' href='/adm/parmset?symb=$symb&amp;filter=$filter&amp;part=$part'><span class='LC_setting'>$linktext</span></a>";
+        my $target=' target="_top"';
+        if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
+            $target='';
+        }
+        return "<a".$target." href=\"/adm/parmset?symb=$symb&amp;filter=$filter&amp;part=$part\"><span class=\"LC_setting\">$linktext</span></a>";
     } else {
-       return $linktext;
+        return $linktext;
     }
 }
 ##############################################
@@ -858,13 +866,14 @@ parameter setting wizard.
 ##############################################
 sub pjump_javascript_definition {
     my $Str = <<END;
-    function pjump(type,dis,value,marker,ret,call,hour,min,sec) {
+    function pjump(type,dis,value,marker,ret,call,hour,min,sec,extra) {
         openMyModal("/adm/rat/parameter.html?type="+escape(type)
                  +"&value="+escape(value)+"&marker="+escape(marker)
                  +"&return="+escape(ret)
                  +"&call="+escape(call)+"&name="+escape(dis)
                  +"&defhour="+escape(hour)+"&defmin="+escape(min)
-                 +"&defsec="+escape(sec)+"&modal=1",350,350,'no');
+                 +"&defsec="+escape(sec)+"&extra="+escape(extra)
+                 +"&modal=1",350,350,'no');
     }
 END
     return $Str;
@@ -1025,11 +1034,15 @@ data structure used for bookkeeping.
 
 =item $number_to_do The total number of items being processed.
 
+=item $preamble Optional HTML to display before the progress bar.
+
 =back
 
 =back
 
 Returns a hash containing the progress state data structure.
+If $number_to_do is zero or null, an indeterminate progress bar will
+be used.
 
 =item &Update_PrgWin()
 
@@ -1118,20 +1131,20 @@ Returns: none
 
 # Create progress
 sub Create_PrgWin {
-    my ($r,$number_to_do)=@_;
+    my ($r,$number_to_do,$preamble)=@_;
     my %prog_state;
     $prog_state{'done'}=0;
     $prog_state{'firststart'}=&Time::HiRes::time();
     $prog_state{'laststart'}=&Time::HiRes::time();
     $prog_state{'max'}=$number_to_do;
-    &Apache::loncommon::LCprogressbar($r); 
+    &Apache::loncommon::LCprogressbar($r,$prog_state{'max'},$preamble); 
     return %prog_state;
 }
 
 # update progress
 sub Update_PrgWin {
     my ($r,$prog_state,$displayString)=@_;
-    &Apache::loncommon::LCprogressbarUpdate($r,undef,$displayString);
+    &Apache::loncommon::LCprogressbarUpdate($r,undef,$displayString,$$prog_state{'max'});
     $$prog_state{'laststart'}=&Time::HiRes::time();
 }
 
@@ -1181,7 +1194,7 @@ sub Increment_PrgWin {
     if ($$prog_state{'max'}) {
        $percent=int(100.*$current/$$prog_state{'max'});
     }
-    &Apache::loncommon::LCprogressbarUpdate($r,$percent,$timeinfo);
+    &Apache::loncommon::LCprogressbarUpdate($r,$percent,$timeinfo,$$prog_state{'max'});
     $$prog_state{'laststart'}=&Time::HiRes::time();
 }
 
@@ -1301,9 +1314,9 @@ sub htmlareaheaders {
 ENDEDITOR
 	}
     $s.=(<<ENDJQUERY);
-<script type="text/javascript" src="/adm/jQuery/js/jquery-1.11.3.min.js"></script>
-<script type="text/javascript" src="/adm/jQuery/js/jquery-ui-1.11.4.custom.min.js"></script>
-<link rel="stylesheet" type="text/css" href="/adm/jQuery/css/smoothness/jquery-ui-1.11.4.custom.css" />
+<script type="text/javascript" src="/adm/jQuery/js/jquery-3.2.1.min.js"></script>
+<script type="text/javascript" src="/adm/jQuery/js/jquery-ui-1.12.1.custom.min.js"></script>
+<link rel="stylesheet" type="text/css" href="/adm/jQuery/css/smoothness/jquery-ui-1.12.1.custom.min.css" />
 <script type="text/javascript" src="/adm/jpicker/js/jpicker-1.1.6.min.js" >
 </script>
 <link rel="stylesheet" type="text/css" href="/adm/jpicker/css/jPicker-1.1.6.min.css" />
@@ -1340,6 +1353,68 @@ $(document).ready(function(){
 });';
 }
 
+sub countdown {
+
+    # Code to put a due date countdown in 'duedatecountdown' span.
+    # This is currently located in the breadcrumb headers.
+    # note that the dueDateLayout is internatinoalized below.
+    # Here document is used to support the substitution into the javascript below.
+    # ..which unforunately necessitates escaping the $'s in the javascript.
+    # There are several times of importance
+    #
+    # serverDueDate -  The absolute time at which the problem expires.
+    # serverTime    -  The server's time when the problem finished computing.
+    # clientTime    -  The client's time...as close to serverTime as possible.
+    #                  The clientTime will be slightly later due to
+    #                  1. The latency between problem computation and
+    #                     the first network action.
+    #                  2. The time required between the page load-start and the actual
+    #                     initial javascript execution that got clientTime.
+    # These are used as follows:
+    #   The difference between clientTime and serverTime are used to
+    #   correct for differences in clock settings between the browser's system and the
+    #   server's.
+    #
+    #   The difference between clientTime and the time at which the ready() method
+    #   starts executing is used to estimate latencies for page load and submission.
+    #   Since this is an estimate, it is doubled.  The latency estimate + one minute
+    #   is used to determine when the countdown timer turns red to warn the user
+    #   to think about submitting.
+
+
+    my $dueDateLayout = &mt('Due in: {dn} {dl} {hnn}{sep}{mnn}{sep}{snn} [_1]',
+                            "<span id='submitearly'></span>");
+    my $early = '- <b>'.&mt('Submit Early').'</b>';
+    my $pastdue = '- <b>'.&mt('Past Due').'</b>';
+    return <<"JAVASCRIPT";
+
+    var documentReadyTime;
+
+\$(document).ready(function() {
+   if (typeof(dueDate) != "undefined") {
+       documentReadyTime = (new Date()).getTime();
+      \$("#duedatecountdown").countdown({until: dueDate, compact: true,
+         layout: "$dueDateLayout",
+         onTick: function (periods) {
+            var latencyEstimate = (documentReadyTime - clientTime) * 2;
+            if(\$.countdown.periodsToSeconds(periods) < (300 + latencyEstimate)) {
+               \$("#submitearly").html("$early");
+               if (\$.countdown.periodsToSeconds(periods) < 1) {
+                    \$("#submitearly").html("$pastdue");
+               }
+            }
+            if(\$.countdown.periodsToSeconds(periods) < (60 + latencyEstimate)) {
+               \$(this).css("color", "red");   //Highlight last minute.
+            }
+         }
+      });
+   }
+});
+
+JAVASCRIPT
+
+}
+
 # ----------------------------------------- Script to activate only some fields
 
 sub htmlareaselectactive {
@@ -1563,62 +1638,11 @@ sub htmlareaselectactive {
 
 	});
 ';
-    $output .= &color_picker;
+    $output .= &color_picker();
 
-    # Code to put a due date countdown in 'duedatecountdown' span.
-    # This is currently located in the breadcrumb headers.
-    # note that the dueDateLayout is internatinoalized below.
-    # Here document is used to support the substitution into the javascript below.
-    # ..which unforunately necessitates escaping the $'s in the javascript.
-    # There are several times of importance
-    #
-    # serverDueDate -  The absolute time at which the problem expires.
-    # serverTime    -  The server's time when the problem finished computing.
-    # clientTime    -  The client's time...as close to serverTime as possible.
-    #                  The clientTime will be slightly later due to
-    #                  1. The latency between problem computation and 
-    #                     the first network action.
-    #                  2. The time required between the page load-start and the actual
-    #                     initial javascript execution that got clientTime.
-    # These are used as follows:
-    #   The difference between clientTime and serverTime are used to 
-    #   correct for differences in clock settings between the browser's system and the
-    #   server's.
-    #
-    #   The difference between clientTime and the time at which the ready() method
-    #   starts executing is used to estimate latencies for page load and submission.
-    #   Since this is an estimate, it is doubled.  The latency estimate + one minute
-    #   is used to determine when the countdown timer turns red to warn the user
-    #   to think about submitting.
+    $output .= &countdown();
 
-    my $dueDateLayout = &mt('Due in: {dn} {dl} {hnn}{sep}{mnn}{sep}{snn} [_1]',
-                            "<span id='submitearly'></span>");
-    my $early = '- <b>'.&mt('Submit Early').'</b>';
-    my $pastdue = '- <b>'.&mt('Past Due').'</b>';
-    $output .= <<JAVASCRIPT;
-
-    var documentReadyTime;
-
-\$(document).ready(function() {
-   if (typeof(dueDate) != "undefined") {
-       documentReadyTime = (new Date()).getTime();
-      \$("#duedatecountdown").countdown({until: dueDate, compact: true, 
-         layout: "$dueDateLayout",
-         onTick: function (periods) {
-	    var latencyEstimate = (documentReadyTime - clientTime) * 2;
-            if(\$.countdown.periodsToSeconds(periods) < (300 + latencyEstimate)) {
-               \$("#submitearly").html("$early");
-               if (\$.countdown.periodsToSeconds(periods) < 1) {
-                    \$("#submitearly").html("$pastdue");
-               }
-            }
-            if(\$.countdown.periodsToSeconds(periods) < (60 + latencyEstimate)) {
-               \$(this).css("color", "red");   //Highlight last minute.
-            }
-         }
-      });
-   }
-});
+    $output .= <<"JAVASCRIPT";
 
     /* This code describes the spellcheck options that will be used for
        items with class 'spellchecked'.  It is necessary for those objects'
@@ -1727,7 +1751,7 @@ sub show_return_link {
             (($env{'request.noversionuri'}=~/^\/adm\//) &&
              ($env{'request.noversionuri'}!~/^\/adm\/wrapper\//) &&
              ($env{'request.noversionuri'}!~
-              m{^/adm/.*/(smppg|bulletinboard|exttool)($|\?)})
+              m{^/adm/.*/(smppg|bulletinboard|ext\.tool)($|\?)})
            ));
 }
 
@@ -1893,6 +1917,16 @@ returns: nothing
         # The first one should be the course or a menu link
         if (!defined($menulink)) { $menulink=1; }
         if ($menulink) {
+            if ($env{'request.course.id'}) {
+                my ($menucoll,$deeplinkmenu,$menuref) = &Apache::loncommon::menucoll_in_effect();
+                if (($menucoll) && (ref($menuref) eq 'HASH')) {
+                    if ($menuref->{'main'} eq 'n') {
+                       undef($menulink);
+                    }
+                }
+            }
+        }
+        if ($menulink) {
             my $description = 'Menu';
             my $no_mt_descr = 0;
             if ((exists($env{'request.course.id'})) && 
@@ -1910,9 +1944,13 @@ returns: nothing
                     }
                 }
             }
+            my $target = '_top';
+            if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
+                $target='';
+            }
             $menulink =  {  href   =>'/adm/menu',
                             title  =>'Go to main menu',
-                            target =>'_top',
+                            target =>$target,
                             text   =>$description,
                             no_mt  =>$no_mt_descr, };
             if($last) {
@@ -1931,7 +1969,10 @@ returns: nothing
                             class => 'LC_menubuttons_link',
                           };
             if ($env{'request.noversionuri'} eq '/adm/searchcat') {
-                $hashref->{'target'} = '_top'; 
+                $hashref->{'target'} = '_top';
+                if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
+                    $hashref->{'target'} = '';
+                }
             }
             $links=&htmltag( 'a','<img src="/res/adm/pages/tolastloc.png" alt="'.$alttext.'" class="LC_icon" />',
                              $hashref);
@@ -2630,9 +2671,9 @@ sub course_custom_roles {
 
 
 sub resource_info_box {
-   my ($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp)=@_;
+   my ($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp,$divforres)=@_;
    my $return='';
-   if ($stuvcurrent ne '') {
+   if (($stuvcurrent ne '') || ($divforres)) {
        $return = '<div class="LC_left_float">';
    }
    if ($symb) {
@@ -2661,7 +2702,7 @@ sub resource_info_box {
     } else {
        $return='<p><span class="LC_error">'.&mt('No context provided.').'</span></p>';
     }
-    if ($stuvcurrent ne '') {
+    if (($stuvcurrent ne '') || ($divforres)) {
         $return .= '</div>';
     }
     return $return;
@@ -3478,7 +3519,16 @@ function go(url) {
        currentURL = null;
        currentSymb= null;
        var lcHostname = setLCHost();
-       window.location.href=lcHostname+url;
+       if (lcHostname!='' && lcHostname!= null) {
+           var RegExp = /^https?\:/;
+           if (RegExp.test(url)) {
+               window.location.href=url;
+           } else {
+               window.location.href=lcHostname+url;
+           }
+       } else {
+           window.location.href=url;
+       }
    }
 }
 
@@ -3518,13 +3568,15 @@ sub jump_to_editres {
         }
     } else {
         unless ($cfile =~ m{^/priv/}) {
-            if ($cfile =~ m{^(/adm/wrapper/ext/([^#]+))#([^#]+)$}) {
+            if ($cfile =~ m{^(/adm/wrapper/ext/([^#]+))(?:|#([^#]+))$}) {
                 $cfile = $1;
                 my $extlink = $2;
                 $anchor = $3;
                 $is_ext = 1;
                 if (($extlink !~ /^https:/) && ($ENV{'SERVER_PORT'} == 443)) {
-                    $usehttp = 1;
+                    unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) {
+                        $usehttp = 1;
+                    }
                 } elsif ($env{'request.use_absolute'}) {
                     if ($env{'request.use_absolute'} =~ m{^https://}) {
                         $usehttps = 1;
@@ -3537,7 +3589,9 @@ sub jump_to_editres {
                         ($env{'course.'.$env{'request.course.id'}.'.num'} eq $cnum) &&
                         ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $cdom)) {
                         if ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://}) {
-                            $usehttp = 1;
+                            unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) {
+                                $usehttp = 1;
+                            }
                         }
                     }
                 } elsif ($env{'request.use_absolute'}) {
@@ -3577,9 +3631,7 @@ sub jump_to_editres {
                 if ($hostname ne '') {
                     $cfile = 'http://'.$hostname.(($cfile =~ /^\//)? '':'/').$cfile;
                 }
-                unless ($is_ext) {
-                    $cfile .= (($cfile=~/\?/)?'&amp;':'?').'usehttp=1';
-                }
+                $cfile .= (($cfile=~/\?/)?'&amp;':'?').'usehttp=1';
             } elsif ($usehttps) {
                 $cfile = $env{'request.use_absolute'}.(($cfile =~ /^\//)? '':'/').$cfile;
             }
@@ -3608,15 +3660,18 @@ sub jump_to_editres {
 # javascript_valid_email
 #
 # Generates javascript to validate an e-mail address.
-# Returns a javascript function which accetps a form field as argumnent, and
+# Returns a javascript function which accepts a form field as argument, and
 # returns false if field.value does not satisfy two regular expression matches
 # for a valid e-mail address.  Backwards compatible with old browsers without
 # support for javascript RegExp (just checks for @ in field.value in this case). 
 
 sub javascript_valid_email {
     my $scripttag .= <<'END';
-function validmail(field) {
+function validmail(field,suffix) {
     var str = field.value;
+    if (suffix != '' && suffix != undefined) {
+        str += suffix;
+    }
     if (window.RegExp) {
         var reg1str = "(@.*@)|(\\.\\.)|(@\\.)|(\\.@)|(^\\.)";
         var reg2str = "^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$"; //"