--- loncom/interface/lonhtmlcommon.pm	2011/10/23 00:27:10	1.294
+++ loncom/interface/lonhtmlcommon.pm	2012/11/29 20:37:07	1.334
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common html routines
 #
-# $Id: lonhtmlcommon.pm,v 1.294 2011/10/23 00:27:10 raeburn Exp $
+# $Id: lonhtmlcommon.pm,v 1.334 2012/11/29 20:37:07 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -61,7 +61,7 @@ use Time::HiRes;
 use Apache::lonlocal;
 use Apache::lonnet;
 use HTML::Entities();
-use LONCAPA;
+use LONCAPA qw(:DEFAULT :match);
 
 sub java_not_enabled {
    return "\n".'<span class="LC_error">'.
@@ -72,7 +72,7 @@ sub java_not_enabled {
 sub coursepreflink {
    my ($text,$category)=@_;
    if (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) {
-      return '<a href="'.&HTML::Entities::encode("/adm/courseprefs?phase=display&actions=$category",'<>&"').'"><span class="LC_setting">'.$text.'</span></a>';
+      return '<a target="_top" href="'.&HTML::Entities::encode("/adm/courseprefs?phase=display&actions=$category",'<>&"').'"><span class="LC_setting">'.$text.'</span></a>';
    } else {
       return '';
    }
@@ -95,7 +95,7 @@ sub direct_parm_link {
     $filter=&entity_encode($filter);
     $part=&entity_encode($part);
     if (($symb) && (&Apache::lonnet::allowed('opa')) && ($target ne 'tex')) {
-       return "<a href='/adm/parmset?symb=$symb&filter=$filter&part=$part'><span class='LC_setting'>$linktext</span></a>";
+       return "<a target='_top' href='/adm/parmset?symb=$symb&amp;filter=$filter&amp;part=$part'><span class='LC_setting'>$linktext</span></a>";
     } else {
        return $linktext;
     }
@@ -103,7 +103,7 @@ sub direct_parm_link {
 ##############################################
 ##############################################
 
-=item confirm_success
+=item &confirm_success()
 
 Successful completion of an operation message
 
@@ -129,7 +129,7 @@ sub confirm_success {
 
 =pod
 
-=item dragmath_button
+=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. 
@@ -155,10 +155,11 @@ ENDDRAGMATH
 
 =pod
 
-=item dragmath_js
+=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 {
@@ -177,13 +178,67 @@ sub dragmath_js {
 ENDDRAGMATHJS
 }
 
+##############################################
+##############################################
+
+=pod
+
+=item &dependencies_button()
+
+Creates a button that launches a popup-window, in which dependencies  
+for the web page in the main window can be added to, replaced or deleted.  
+
+=cut
+
+sub dependencies_button {
+    my $buttontext=&mt('Manage Dependencies');
+    return <<"END";
+                <input type="button" value="$buttontext" onclick="javascript:dependencycheck();" />
+END
+}
+
+##############################################
+
+=pod
+
+=item &dependencycheck_js()
+
+Javascript used to open pop-up window containing interface to manage 
+dependencies for a web page uploaded diretcly to a course.
+
+=cut
+
+sub dependencycheck_js {
+    my ($symb,$title,$url,$folderpath,$uri) = @_;
+    my $link;
+    if ($symb) {
+        $link = '/adm/dependencies?symb='.&HTML::Entities::encode($symb,'<>&"');
+    } elsif ($folderpath) {
+        $link = '/adm/dependencies?folderpath='.&HTML::Entities::encode($folderpath,'<>&"');
+         $url = $uri;
+    }
+    $link .= (($link=~/\?/)?'&amp;':'?').'title='.
+             &HTML::Entities::encode($title,'<>&"');
+    if ($url) {
+        $link .= '&url='.&HTML::Entities::encode($url,'<>&"');
+    }
+    return <<ENDJS;
+                <script type="text/javascript">
+                // <![CDATA[
+                  function dependencycheck() {
+                     depwin  = window.open("$link","","width=750,height=500,resizable,scrollbars=yes");
+                  }
+                // ]]>
+                </script>
+ENDJS
+}
 
 ##############################################
 ##############################################
 
 =pod
 
-=item authorbombs
+=item &authorbombs()
 
 =cut
 
@@ -317,7 +372,7 @@ sub get_recent_frozen {
 
 =pod
 
-=item textbox
+=item &textbox()
 
 =cut
 
@@ -337,7 +392,7 @@ sub textbox {
 
 =pod
 
-=item checkbox
+=item &checkbox()
 
 =cut
 
@@ -359,7 +414,7 @@ sub checkbox {
 
 =pod
 
-=item radiobutton
+=item &radiobutton()
 
 =cut
 
@@ -383,10 +438,10 @@ sub radio {
 
 =pod
 
-=item &date_setter
+=item &date_setter()
 
 &date_setter returns html and javascript for a compact date-setting form.
-To retrieve values from it, use &get_date_from_form().
+To retrieve values from it, use &get_date_from_form.
 
 Inputs
 
@@ -636,7 +691,7 @@ sub build_url {
 
 =pod
 
-=item &get_date_from_form
+=item &get_date_from_form()
 
 get_date_from_form retrieves the date specified in an &date_setter form.
 
@@ -748,13 +803,12 @@ parameter setting wizard.
 sub pjump_javascript_definition {
     my $Str = <<END;
     function pjump(type,dis,value,marker,ret,call,hour,min,sec) {
-        parmwin=window.open("/adm/rat/parameter.html?type="+escape(type)
+        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),"LONCAPAparms",
-                 "height=350,width=350,scrollbars=no,menubar=no");
+                 +"&defsec="+escape(sec)+"&modal=1",350,350,'no');
     }
 END
     return $Str;
@@ -896,7 +950,7 @@ of items completed and an estimate of th
 =over 4
 
 
-=item &Create_PrgWin
+=item &Create_PrgWin()
 
 Writes javascript to the client to open a progress window and returns a
 data structure used for bookkeeping.
@@ -907,27 +961,14 @@ Inputs
 
 =item $r Apache request
 
-=item $title The title of the progress window
-
-=item $heading A description (usually 1 line) of the process being initiated.
-
 =item $number_to_do The total number of items being processed.
 
-=item $type Either 'popup' or 'inline' (popup is assumed if nothing is
-       specified)
-
-=item $width Specify the width in charaters of the input field.
-
-=item $formname Only useful in the inline case, if a form already exists, this needs to be used and specfiy the name of the form, otherwise the Progress line will be created in a new form of it's own
-
-=item $inputname Only useful in the inline case, if a form and an input of type text exists, use this to specify the name of the input field 
-
 =back
 
 Returns a hash containing the progress state data structure.
 
 
-=item &Update_PrgWin
+=item &Update_PrgWin()
 
 Updates the text in the progress indicator.  Does not increment the count.
 See &Increment_PrgWin.
@@ -947,7 +988,7 @@ Inputs:
 Returns: none
 
 
-=item Increment_PrgWin
+=item Increment_PrgWin()
 
 Increment the count of items completed for the progress window by $step or 1 if no step is provided.
 
@@ -969,7 +1010,7 @@ Inputs:
 Returns: none
 
 
-=item Close_PrgWin
+=item &Close_PrgWin()
 
 Closes the progress window.
 
@@ -992,82 +1033,23 @@ Returns: none
 ########################################################
 ########################################################
 
-my $uniq=0;
-sub get_uniq_name {
-    $uniq++;
-    return 'uniquename'.$uniq;
-}
 
 # Create progress
 sub Create_PrgWin {
-    my ($r, $title, $heading, $number_to_do,$type,$width,$formname,
-	$inputname)=@_;
-    if (!defined($type)) { $type='popup'; }
-    if (!defined($width)) { $width=55; }
+    my ($r,$number_to_do)=@_;
     my %prog_state;
-    $prog_state{'type'}=$type;
-    if ($type eq 'popup') {
-	$prog_state{'window'}='popwin';
-	my $start_page =
-	    &Apache::loncommon::start_page($title,undef,
-					   {'only_body' => 1,
-					    'bgcolor'   => '#88DDFF',
-					    'js_ready'  => 1});
-	my $end_page = &Apache::loncommon::end_page({'js_ready'  => 1});
-
-	#the whole function called through timeout is due to issues
-	#in mozilla Read BUG #2665 if you want to know the whole story
-	&r_print($r,&Apache::lonhtmlcommon::scripttag(
-        "var popwin;
-         function openpopwin () {
-         popwin=open(\'\',\'popwin\',\'width=400,height=100\');".
-        "popwin.document.writeln(\'".$start_page.
-              "<h4>".&mt("$heading")."<\/h4>".
-              "<form action=\"\" name=\"popremain\" method=\"post\">".
-              '<input type="text" size="'.$width.'" name="remaining" value="'.
-	      &mt('Starting').'" /><\\/form>'.$end_page.
-              "\');".
-        "popwin.document.close();}".
-        "\nwindow.setTimeout(openpopwin,0)"
-    ));
-	$prog_state{'formname'}='popremain';
-	$prog_state{'inputname'}="remaining";
-    } elsif ($type eq 'inline') {
-	$prog_state{'window'}='window';
-	if (!$formname) {
-	    $prog_state{'formname'}=&get_uniq_name();
-	    &r_print($r,'<form action="" name="'.$prog_state{'formname'}.'">');
-	} else {
-	    $prog_state{'formname'}=$formname;
-	}
-	if (!$inputname) {
-	    $prog_state{'inputname'}=&get_uniq_name();
-	    &r_print($r,&mt("$heading [_1]",' <input type="text" name="'.$prog_state{'inputname'}.'" size="'.$width.'" />'));
-	} else {
-	    $prog_state{'inputname'}=$inputname;
-	    
-	}
-	if (!$formname) { &r_print($r,'</form>'); }
-	&Update_PrgWin($r,\%prog_state,&mt('Starting'));
-    }
-
     $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); 
     return %prog_state;
 }
 
 # update progress
 sub Update_PrgWin {
     my ($r,$prog_state,$displayString)=@_;
-    &r_print($r,&Apache::lonhtmlcommon::scripttag(
-        $$prog_state{'window'}.'.document.'.
-        $$prog_state{'formname'}.'.'.
-        $$prog_state{'inputname'}.'.value="'.
-        $displayString.'";'
-    ));
+    &Apache::loncommon::LCprogressbarUpdate($r,undef,$displayString);
     $$prog_state{'laststart'}=&Time::HiRes::time();
 }
 
@@ -1113,42 +1095,28 @@ sub Increment_PrgWin {
             $min,
             $sec,
             $lasttime);
-
-    &r_print($r,&Apache::lonhtmlcommon::scripttag(
-        $$prog_state{'window'}.'.document.'.
-        $$prog_state{'formname'}.'.'.
-        $$prog_state{'inputname'}.'.value="'.$timeinfo.'";'
-    ));
+    my $percent=0;
+    if ($$prog_state{'max'}) {
+       $percent=int(100.*$current/$$prog_state{'max'});
+    }
+    &Apache::loncommon::LCprogressbarUpdate($r,$percent,$timeinfo);
     $$prog_state{'laststart'}=&Time::HiRes::time();
 }
 
 # close Progress Line
 sub Close_PrgWin {
     my ($r,$prog_state)=@_;
-    if ($$prog_state{'type'} eq 'popup') {
-        &r_print($r,&Apache::lonhtmlcommon::scripttag(
-            'popwin.close()'
-        ));
-    } elsif ($$prog_state{'type'} eq 'inline') {
-	&Update_PrgWin($r,$prog_state,&mt('Done'));
-    }
+    &Apache::loncommon::LCprogressbarClose($r);
     undef(%$prog_state);
 }
 
-sub r_print {
-    my ($r,$to_print)=@_;
-    if ($r) {
-	$r->print($to_print);
-	$r->rflush();
-    } else {
-	print($to_print);
-    }
-}
 
 # ------------------------------------------------------- Puts directory header
 
 sub crumbs {
     my ($uri,$target,$prefix,$form,$skiplast)=@_;
+# You cannot crumbnify uploaded or adm resources
+    if ($uri=~/^\/*(uploaded|adm)\//) { return &mt('(Internal Course/Group Content)'); }
     if ($target) {
         $target = ' target="'.
                   &Apache::loncommon::escape_single($target).'"';
@@ -1186,6 +1154,7 @@ sub crumbs {
     if ($uri !~ m|/$|) { $output=~s|/$||; }
     $output.='</span>';
 
+
     return $output;
 }
 
@@ -1247,6 +1216,15 @@ ENDEDITOR
 <script type="text/javascript" src="/adm/jQuery/js/jquery-1.6.2.min.js"></script>
 <script type="text/javascript" src="/adm/jQuery/js/jquery-ui-1.8.16.custom.min.js"></script>
 <link rel="stylesheet" type="text/css" href="/adm/jQuery/css/smoothness/jquery-ui-1.8.16.custom.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" />
+<script type="text/javascript" src="/adm/countdown/js/jquery.countdown.js"></script>
+<link rel="stylesheet" type="text/css" href="/adm/countdown/css/jquery.countdown.css" />
+
+<script type="text/javascript" src="/adm/spellchecker/js/jquery.spellchecker.min.js"></script>
+<link rel="stylesheet" type="text/css" href="/adm/spellchecker/css/spellchecker.css" />
+
 ENDJQUERY
 	return $s;
 }
@@ -1262,6 +1240,17 @@ sub htmlarea_lang {
     return $lang;
 }
 
+# return javacsript to activate elements of .colorchooser with jpicker:
+# Caller is responsible for enclosing this in <script> tags:
+#
+sub color_picker {
+    return '
+$(document).ready(function(){
+    $.fn.jPicker.defaults.images.clientPath="/adm/jpicker/images/";
+    $(".colorchooser").jPicker({window: { position: {x: "screenCenter", y: "bottom"}}});
+});';
+}
+
 # ----------------------------------------- Script to activate only some fields
 
 sub htmlareaselectactive {
@@ -1375,8 +1364,100 @@ sub htmlareaselectactive {
 			$(this).before("<div><a href=\"#\" id=\"LC_rt_"+id+"\" title=\"Enable rich text formatting (bold, italic, etc.)\" class=\"LC_enable_rt\"><b>Rich formatting &raquo;</b></a></div>");
 			$("#LC_rt_"+id).click(editorHandler);
 		});
+
+
 	});
 ';
+    $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.
+
+    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.
+            }
+         }
+      });
+   }
+});
+
+    /* This code describes the spellcheck options that will be used for
+       items with class 'spellchecked'.  It is necessary for those objects'
+       to explicitly request checking (e.g. onblur is a nice event for that).
+     */
+     \$(document).ready(function() {
+	 \$(".spellchecked").spellchecker({
+	   url: "/ajax/spellcheck",
+	   lang: "en",                      
+	   engine: "pspell",
+	   suggestionBoxPosition: "below",
+	   innerDocument: true
+					  });
+	 \$("textarea.spellchecked").spellchecker({
+	   url: "/ajax/spellcheck",
+	   lang: "en",                      
+	   engine: "pspell",
+	   suggestionBoxPosition: "below",
+	   innerDocument: true
+					  });
+
+			});
+
+    /* the muli colored editor can generate spellcheck with language 'none'
+       to disable spellcheck as well
+    */
+    function doSpellcheck(element, lang) {
+	if (lang != 'none') {
+ 	    \$(element).spellchecker('option', {lang: lang});
+	    \$(element).spellchecker('check');
+        }
+    }
+
+
+JAVASCRIPT
     if ($dragmath_prefix ne '') {
         $output .= '
 
@@ -1432,7 +1513,8 @@ sub show_return_link {
 
     unless ($env{'request.course.id'}) { return 0; }
     if ($env{'request.noversionuri'}=~m{^/priv/} ||
-        $env{'request.uri'}=~m{^/~}) { return 1; }
+        $env{'request.uri'}=~m{^/priv/}) { return 1; }
+    return if ($env{'request.noversionuri'} eq '/adm/supplemental');
 
     if (($env{'request.noversionuri'} =~ m{^/adm/(viewclasslist|navmaps)($|\?)})
         || ($env{'request.noversionuri'} =~ m{^/adm/.*/aboutme($|\?)})) {
@@ -1452,12 +1534,67 @@ sub show_return_link {
 }
 
 
+##
+#   Set the dueDate variable...note this is done in the timezone
+#   of the browser.
+#
+# @param epoch relative time at which the problem is due.
+#
+# @return the javascript fragment to set the date:
+#
+sub set_due_date {
+    my $dueStamp = shift;
+    my $duems    = $dueStamp * 1000; # Javascript Date object needs ms not seconds.
+
+    my $now = time()*1000;
+
+    # This slightly obscure bit of javascript sets the dueDate variable
+    # to the time in the browser at which the problem was due.  
+    # The code should correct for gross differences between the server
+    # and client's time setting
+
+     return <<"END";
+
+<script type="text/javascript">
+  //<![CDATA[
+var serverDueDate = $duems;
+var serverTime    = $now;
+var clientTime    = (new Date()).getTime();
+var dueDate       = new Date(serverDueDate + (clientTime - serverTime));
+
+  //]]>
+</script>
+
+END
+}
+##
+# Sets the time at which the problem finished computing.
+# This just updates the serverTime and clientTime variables above.
+# Calling this in e.g. end_problem provides a better estimate of the
+# difference beetween the server and client time setting as 
+# the difference contains less of the latency/problem compute time.
+#
+sub set_compute_end_time {
+
+    my $now = time()*1000;	# Javascript times are in ms.
+    return <<"END";
+
+<script type="text/javascript">
+//<![CDATA[
+serverTime = $now;
+clientTime = (new Date()).getTime();
+//]]>
+</script>
+
+END
+}
+
 ############################################################
 ############################################################
 
 =pod
 
-=item breadcrumbs
+=item &breadcrumbs()
 
 Compiles the previously registered breadcrumbs into an series of links.
 Additionally supports a 'component', which will be displayed on the
@@ -1475,11 +1612,11 @@ Inputs: $component (the text on the righ
            when including the text on the right.
 Returns a string containing breadcrumbs for the current page.
 
-=item clear_breadcrumbs
+=item &clear_breadcrumbs()
 
 Clears the previously stored breadcrumbs.
 
-=item add_breadcrumb
+=item &add_breadcrumb()
 
 Pushes a breadcrumb on the stack of crumbs.
 
@@ -1499,7 +1636,8 @@ returns: nothing
     my %tools = ();
     
     sub breadcrumbs {
-        my ($component,$component_help,$menulink,$helplink,$css_class,$no_mt, $CourseBreadcrumbs) = @_;
+        my ($component,$component_help,$menulink,$helplink,$css_class,$no_mt, 
+            $CourseBreadcrumbs) = @_;
         #
         $css_class ||= 'LC_breadcrumbs';
 
@@ -1523,6 +1661,14 @@ returns: nothing
                 $description = 
                     $env{'course.'.$env{'request.course.id'}.'.description'};
                 $no_mt_descr = 1;
+                if ($env{'request.noversionuri'} =~ 
+                    m{^/public/($match_domain)/($match_courseid)/syllabus$}) {
+                    unless (($env{'course.'.$env{'request.course.id'}.'.domain'} eq $1) &&
+                            ($env{'course.'.$env{'request.course.id'}.'.num'} eq $2)) {
+                        $description = 'Menu';
+                        $no_mt_descr = 0;
+                    }
+                }
             }
             $menulink =  {  href   =>'/adm/menu',
                             title  =>'Go to main menu',
@@ -1538,10 +1684,14 @@ returns: nothing
             }
         }
         my $links;
-        if ((&show_return_link) && (!$CourseBreadcrumbs)) {
-           $links=&htmltag( 'a',"<img src='/res/adm/pages/reload.png' border='0' style='vertical-align:middle;' />",
+        if ((&show_return_link) && (!$CourseBreadcrumbs) && (ref($last) eq 'HASH')) {
+            my $alttext = &mt('Go Back');
+            $links=&htmltag( 'a','<img src="/res/adm/pages/tolastloc.png" alt="'.$alttext.'" class="LC_icon" />',
                             { href => '/adm/flip?postdata=return:',
-                              title => &mt("Back to most recent content resource") });
+                              title => &mt('Back to most recent content resource'),
+                              class => 'LC_menubuttons_link',
+                            });
+            $links=&htmltag('li',$links);
         }
         $links.= join "", 
              map {
@@ -1568,8 +1718,11 @@ returns: nothing
 
         # last breadcrumb is the first order heading of a page
         # for course breadcrumbs it's just bold
-        $links .= &htmltag( 'li', htmltag($CourseBreadcrumbs ? 'b' : 'h1',
-                $lasttext), {title => $lasttext});
+
+        if ($lasttext ne '') {
+            $links .= &htmltag( 'li', htmltag($CourseBreadcrumbs ? 'b' : 'h1',
+                    $lasttext), {title => $lasttext});
+        }
 
         my $icons = '';
         $faq  = $last->{'faq'}  if (exists($last->{'faq'}));
@@ -1588,20 +1741,24 @@ returns: nothing
                                                          $faq,$bug);
         }
         #
-		
 
-        unless ($CourseBreadcrumbs) {
-            $links = &htmltag('ol',  $links, { id => "LC_MenuBreadcrumbs"   });
-        } else {
-            $links = &htmltag('ul',  $links, { class => "LC_CourseBreadcrumbs" });
+		
+        if ($links ne '') {
+            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;
+                             .$links 
+;
         }
         
         &render_tools(\$links);
@@ -1612,7 +1769,12 @@ returns: nothing
         # Return the @Crumbs stack to what we started with
         push(@Crumbs,$last);
         shift(@Crumbs);
+
+
         # Return the breadcrumb's line
+
+    
+
         return "$links";
     }
 
@@ -1625,7 +1787,7 @@ returns: nothing
         push(@Crumbs,@_);
     }
     
-=item add_breadcrumb_tool($category, $html)
+=item &add_breadcrumb_tool($category, $html)
 
 Adds $html to $category of the breadcrumb toolbar container.
 
@@ -1640,7 +1802,7 @@ Currently there are 3 possible values fo
 left of breadcrumbs line
 
 =item tools 
-right of breadcrumbs line
+remaining items in right of breadcrumbs line
 
 =item advtools 
 advanced tools shown in a separate box below breadcrumbs line 
@@ -1668,7 +1830,7 @@ returns: nothing
         push @{$tools{$category}}, @html;
     }
 
-=item clear_breadcrumb_tools()
+=item &clear_breadcrumb_tools()
 
 Clears the breadcrumb toolbar container.
 
@@ -1680,7 +1842,7 @@ returns: nothing
         undef(%tools);
     }
 
-=item render_tools(\$breadcrumbs)
+=item &render_tools(\$breadcrumbs)
 
 Creates html for breadcrumb tools (categories navigation and tools) and inserts 
 \$breadcrumbs at the correct position.
@@ -1689,6 +1851,7 @@ input: \$breadcrumbs - a reference to th
 breadcrumbs.
 
 returns: nothing
+
 =cut
 
 #TODO might split this in separate functions for each category
@@ -1704,7 +1867,9 @@ returns: nothing
                    { listattr => { class=>'LC_breadcrumb_tools_outerlist' } });
     }
 
-=item render_advtools(\$breadcrumbs)
+=pod
+
+=item &render_advtools(\$breadcrumbs)
 
 Creates html for advanced tools (category advtools) and inserts \$breadcrumbs 
 at the correct position.
@@ -1713,6 +1878,7 @@ input: \$breadcrumbs - a reference to th
 breadcrumbs (after render_tools call).
 
 returns: nothing
+
 =cut
 
     sub render_advtools {
@@ -1726,6 +1892,84 @@ returns: nothing
 
 } # End of scope for @Crumbs
 
+sub docs_breadcrumbs {
+    my ($allowed,$crstype,$contenteditor,$title,$precleared)=@_;
+    my ($folderpath,@folders);
+    if ($env{'form.pagepath'}) {
+        @folders = split('&',$env{'form.pagepath'});
+    } else {
+        @folders = split('&',$env{'form.folderpath'});
+    }
+    my $plain='';
+    my ($randompick,$isencrypted,$ishidden,$is_random_order) = (-1,0,0,0);
+    my @docs_crumbs;
+    while (@folders) {
+        my $folder=shift(@folders);
+        my $foldername=shift(@folders);
+        if ($folderpath) {$folderpath.='&';}
+        $folderpath.=$folder.'&'.$foldername;
+        my $url;
+        if ($allowed) {
+            $url = '/adm/coursedocs?folderpath=';
+        } else {
+            $url = '/adm/supplemental?folderpath=';
+        }
+        $url .= &escape($folderpath);
+        my $name=&unescape($foldername);
+# each of randompick number, hidden, encrypted, random order are 
+# appended with ":"s to the foldername
+        $name=~s/\:(\d*)\:(\w*)\:(\w*):(\d*)$//;
+        if ($contenteditor) { 
+            if ($1 ne '') {
+                $randompick=$1;
+            } else {
+               $randompick=-1;
+            }
+            if ($2) { $ishidden=1; }
+            if ($3) { $isencrypted=1; }
+            if ($4 ne '') { $is_random_order = 1; }
+        }
+        if ($folder eq 'supplemental') {
+            $name = &mt('Supplemental '.$crstype.' Contents');
+        }
+        if ($contenteditor) {
+            $plain.=$name.' &gt; ';
+        }
+        push(@docs_crumbs,
+                          {'href'  => $url,
+                           'title' => $name,
+                           'text'  => $name,
+                           'no_mt' => 1,
+                          });
+    }
+    if ($title) {
+        push(@docs_crumbs,
+                          {'title' => $title,
+                           'text'  => $title,
+                           'no_mt' => 1,}
+                          );
+    }
+    if (wantarray) {
+        unless ($precleared) {
+            &clear_breadcrumbs();
+        }
+        &add_breadcrumb(@docs_crumbs);
+        if ($contenteditor) {
+            $plain=~s/\&gt\;\s*$//;
+        }
+        my $menulink = 0;
+        if (!$allowed && !$contenteditor) {
+            $menulink = 1;
+        }
+        return (&breadcrumbs(undef,undef,$menulink,'nohelp',undef,undef,
+                             $contenteditor),
+                             $randompick,$ishidden,$isencrypted,$plain,
+                             $is_random_order);
+    } else {
+        return \@docs_crumbs;
+    }
+}
+
 ############################################################
 ############################################################
 
@@ -1786,15 +2030,19 @@ returns: nothing
 my @row_count;
 
 sub start_pick_box {
-    my ($css_class) = @_;
+    my ($css_class,$id) = @_;
     if (defined($css_class)) {
 	$css_class = 'class="'.$css_class.'"';
     } else {
 	$css_class= 'class="LC_pick_box"';
     }
+    my $table_id;
+    if (defined($id)) {
+        $table_id = ' id="'.$id.'"';
+    }
     unshift(@row_count,0);
     my $output = <<"END";
- <table $css_class>
+ <table $css_class $table_id>
 END
     return $output;
 }
@@ -2078,26 +2326,41 @@ sub course_custom_roles {
 
 
 sub resource_info_box {
-   my ($symb,$onlyfolderflag)=@_;
+   my ($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp)=@_;
    my $return='';
+   if ($stuvcurrent ne '') {
+       $return = '<div class="LC_left_float">';
+   }
    if ($symb) {
-       $return=&Apache::loncommon::start_data_table();
+       $return.=&Apache::loncommon::start_data_table();
        my ($map,$id,$resource)=&Apache::lonnet::decode_symb($symb);
        my $folder=&Apache::lonnet::gettitle($map);
        $return.=&Apache::loncommon::start_data_table_row().
-                    '<th>'.&mt('Folder:').'</th><td>'.$folder.'</td>'.
+                    '<th align="left">'.&mt('Folder:').'</th><td>'.$folder.'</td>'.
                     &Apache::loncommon::end_data_table_row();
        unless ($onlyfolderflag) {
           $return.=&Apache::loncommon::start_data_table_row().
-                    '<th>'.&mt('Resource:').'</th><td>'.&Apache::lonnet::gettitle($symb).'</td>'.
+                    '<th align="left">'.&mt('Resource:').'</th><td>'.&Apache::lonnet::gettitle($symb).'</td>'.
+                    &Apache::loncommon::end_data_table_row();
+       }
+       if ($stuvcurrent ne '') {
+           $return .= &Apache::loncommon::start_data_table_row().
+                    '<th align="left">'.&mt("Student's current version:").'</th><td>'.$stuvcurrent.'</td>'.
+                    &Apache::loncommon::end_data_table_row();
+       }
+       if ($stuvdisp ne '') {
+           $return .= &Apache::loncommon::start_data_table_row().
+                    '<th align="left">'.&mt("Student's version displayed:").'</th><td>'.$stuvdisp.'</td>'.
                     &Apache::loncommon::end_data_table_row();
        }
        $return.=&Apache::loncommon::end_data_table();
     } else {
        $return='<p><span class="LC_error">'.&mt('No context provided.').'</span></p>';
     }
+    if ($stuvcurrent ne '') {
+        $return .= '</div>';
+    }
     return $return;
-
 }
 
 ##############################################
@@ -2111,15 +2374,19 @@ sub resource_info_box {
 # 1. number to display.
 #    If input for number is empty only the title will be displayed. 
 # 2. title text to display.
+# 3. optional id for the <div>
 # Outputs - a scalar containing html mark-up for the div.
 
 sub topic_bar {
-    my ($num,$title) = @_;
+    my ($num,$title,$id) = @_;
     my $number = '';
     if ($num ne '') {
         $number = '<span>'.$num.'</span>';
     }
-    return '<div class="LC_topic_bar">'.$number.$title.'</div>';
+    if ($id ne '') {
+        $id = 'id="'.$id.'"';
+    }
+    return '<div class="LC_topic_bar" '.$id.'>'.$number.$title.'</div>';
 }
 
 ##############################################
@@ -2583,6 +2850,327 @@ ENDSCRIPT
 ##############################################
 ##############################################
 
+sub resize_scrollbox_js {
+    my ($context,$tabidstr) = @_;
+    my (%names,$paddingwfrac,$offsetwfrac,$offsetv,$minw,$minv);
+    if ($context eq 'docs') {
+        %names = (
+                   boxw   => 'contenteditor',
+                   item   => 'contentlist',
+                   header => 'uploadfileresult',
+                   scroll => 'contentscroll',
+                   boxh   => 'contenteditor',
+                 );
+        $paddingwfrac = 0.09; 
+        $offsetwfrac = 0.015;
+        $offsetv = 20;
+        $minw = 250;
+        $minv = 200;
+    } elsif ($context eq 'params') {
+        %names = (
+                   boxw   => 'parameditor',
+                   item   => 'mapmenuinner',
+                   header => 'parmstep1',
+                   scroll => 'mapmenuscroll',
+                   boxh   => 'parmlevel',
+                 );
+        $paddingwfrac = 0.2;
+        $offsetwfrac = 0.015;
+        $offsetv = 80;
+        $minw = 100;
+        $minv = 100; 
+    }
+    my $viewport_js = &Apache::loncommon::viewport_geometry_js();
+    my $output = '
+
+window.onresize=callResize;
+
+';
+    if ($context eq 'docs') {
+        $output .= '
+var activeTab;
+';
+    }
+    $output .=  <<"FIRST";
+
+$viewport_js
+
+function resize_scrollbox(scrollboxname,chkw,chkh) {
+    var scrollboxid = 'div_'+scrollboxname;
+    var scrolltableid = 'table_'+scrollboxname;
+    var scrollbox;
+    var scrolltable;
+
+    if (document.getElementById("$names{'boxw'}") == null) {
+        return;
+    }
+
+    if (document.getElementById(scrollboxid) == null) {
+        return;
+    } else {
+        scrollbox = document.getElementById(scrollboxid);
+    }
+
+
+    if (document.getElementById(scrolltableid) == null) {
+        return;
+    } else {
+        scrolltable = document.getElementById(scrolltableid);
+    }
+
+    init_geometry();
+    var vph = Geometry.getViewportHeight();
+    var vpw = Geometry.getViewportWidth();
+
+FIRST
+    if ($context eq 'docs') {
+        $output .= "
+    var alltabs = ['$tabidstr'];
+";
+    } elsif ($context eq 'params') {
+        $output .= "
+    if (document.getElementById('$names{'boxh'}') == null) {
+        return;
+    }
+";
+    }
+    $output .= <<"SECOND";
+    var listwchange;
+    if (chkw == 1) {
+        var boxw = document.getElementById("$names{'boxw'}").offsetWidth;
+        var itemw;
+        var itemid = document.getElementById("$names{'item'}");
+        if (itemid != null) {
+            itemw = itemid.offsetWidth;
+        }
+        var itemwstart = itemw;
+
+        var scrollboxw = scrollbox.offsetWidth;
+        var scrollboxscrollw = scrollbox.scrollWidth;
+
+        var offsetw = parseInt(vpw * $offsetwfrac);
+        var paddingw = parseInt(vpw * $paddingwfrac);
+
+        var minscrollboxw = $minw;
+        var maxcolw = 0;
+SECOND
+    if ($context eq 'docs') {
+        $output .= <<"DOCSONE";
+        var actabw = 0;
+        for (var i=0; i<alltabs.length; i++) {
+            if (activeTab == alltabs[i]) {
+                actabw = document.getElementById(alltabs[i]).offsetWidth;
+                if (actabw > maxcolw) {
+                    maxcolw = actabw;
+                }
+            } else {
+                if (document.getElementById(alltabs[i]) != null) {
+                    var thistab = document.getElementById(alltabs[i]);
+                    thistab.style.visibility = 'hidden';
+                    thistab.style.display = 'block';
+                    var tabw = document.getElementById(alltabs[i]).offsetWidth;
+                    thistab.style.display = 'none';
+                    thistab.style.visibility = '';
+                    if (tabw > maxcolw) {
+                        maxcolw = tabw;
+                    }
+                }
+            }
+        }
+DOCSONE
+    } elsif ($context eq 'params') {
+        $output .= <<"PARAMSONE";
+        var parmlevelrows = new Array();
+        var mapmenucells = new Array();
+        parmlevelrows = document.getElementById("$names{'boxh'}").rows;
+        var numrows = parmlevelrows.length;
+        if (numrows > 1) {
+            mapmenucells = parmlevelrows[2].getElementsByTagName('td');
+        }
+        maxcolw = mapmenucells[0].offsetWidth;
+PARAMSONE
+    }
+    $output .= <<"THIRD";
+        if (maxcolw > 0) {
+            var newscrollboxw;
+            if (maxcolw+paddingw+scrollboxscrollw<boxw) {
+                newscrollboxw = boxw-paddingw-maxcolw;
+                if (newscrollboxw < minscrollboxw) {
+                    newscrollboxw = minscrollboxw;
+                }
+                scrollbox.style.width = newscrollboxw+"px";
+                if (newscrollboxw != scrollboxw) {
+                    var newitemw = newscrollboxw-offsetw;
+                    itemid.style.width = newitemw+"px";
+                }
+            } else {
+                newscrollboxw = boxw-paddingw-maxcolw;
+                if (newscrollboxw < minscrollboxw) {
+                    newscrollboxw = minscrollboxw;
+                }
+                scrollbox.style.width = newscrollboxw+"px";
+                if (newscrollboxw != scrollboxw) {
+                    var newitemw = newscrollboxw-offsetw;
+                    itemid.style.width = newitemw+"px";
+                }
+            }
+
+            if (newscrollboxw != scrollboxw) {
+                var newscrolltablew = newscrollboxw+offsetw;
+                scrolltable.style.width = newscrolltablew+"px";
+            }
+        }
+
+        if (itemid.offsetWidth != itemwstart) {
+            listwchange = 1;
+        }
+    }
+    if ((chkh == 1) || (listwchange)) {
+        var primaryheight = document.getElementById('LC_nav_bar').offsetHeight;
+        var secondaryheight = document.getElementById('LC_secondary_menu').offsetHeight;
+        var crumbsheight = document.getElementById('LC_breadcrumbs').offsetHeight;
+        var dccidheight = 0;
+        if (document.getElementById('dccid') != null) {
+            dccidheight = document.getElementById('dccid').offsetHeight;
+        }
+        var headerheight = 0;
+        if (document.getElementById("$names{'header'}") != null) {
+            headerheight = document.getElementById("$names{'header'}").offsetHeight;
+        }
+        var tabbedheight = document.getElementById("tabbededitor").offsetHeight;
+        var boxheight = document.getElementById("$names{'boxh'}").offsetHeight;
+        var freevspace = vph-(primaryheight+secondaryheight+crumbsheight+dccidheight+headerheight+tabbedheight+boxheight);
+
+        var scrollboxheight = scrollbox.offsetHeight;
+        var scrollboxscrollheight = scrollbox.scrollHeight;
+
+        var minvscrollbox = $minv;
+        var offsetv = $offsetv;
+        var newscrollboxheight;
+        if (freevspace < 0) {
+            newscrollboxheight = scrollboxheight+freevspace-offsetv;
+            if (newscrollboxheight < minvscrollbox) {
+                newscrollboxheight = minvscrollbox;
+            }
+            scrollbox.style.height = newscrollboxheight + "px";
+        } else {
+            if (scrollboxscrollheight > scrollboxheight) {
+                if (freevspace > offsetv) {
+                    newscrollboxheight = scrollboxheight+freevspace-offsetv;
+                    if (newscrollboxheight < minvscrollbox) {
+                        newscrollboxheight = minvscrollbox;
+                    }
+                    scrollbox.style.height = newscrollboxheight+"px";
+                }
+            }
+        }
+        scrollboxheight = scrollbox.offsetHeight;
+        var itemh = document.getElementById("$names{'item'}").offsetHeight;
+
+        if (scrollboxscrollheight <= scrollboxheight) {
+            if ((itemh+offsetv)<scrollboxheight) {
+                newscrollheight = itemh+offsetv;
+                scrollbox.style.height = newscrollheight+"px";
+            }
+        }
+    }
+    return;
+}
+
+function callResize() {
+    var timer;
+    clearTimeout(timer);
+    timer=setTimeout('resize_scrollbox("$names{'scroll'}","1","1")',500);
+}
+
+THIRD
+    return $output;
+}
+
+##############################################
+##############################################
+
+sub javascript_jumpto_resource {
+    my $confirm_switch = &mt("Editing requires switching to the resource's home server.").'\n'.
+                         &mt('Switch server?');
+    return (<<ENDUTILITY)
+
+function go(url) {
+   if (url!='' && url!= null) {
+       currentURL = null;
+       currentSymb= null;
+       window.location.href=url;
+   }
+}
+
+function need_switchserver(url) {
+    if (url!='' && url!= null) {
+        if (confirm("$confirm_switch")) {
+            go(url);
+        }
+    }
+    return;
+}
+
+ENDUTILITY
+
+}
+
+sub jump_to_editres {
+    my ($cfile,$home,$switchserver,$forceedit,$forcereg,$symb,$folderpath,
+        $title,$idx,$suppurl) = @_;
+    my $jscall;
+    if ($switchserver) {
+        if ($home) {
+            $cfile = '/adm/switchserver?otherserver='.$home.'&amp;role='.
+                     &HTML::Entities::encode($env{'request.role'},'"<>&');
+            if ($symb) {
+                $cfile .= '&amp;symb='.&HTML::Entities::encode($symb,'"<>&');
+            } elsif ($folderpath) {
+                $cfile .= '&amp;folderpath='.&HTML::Entities::encode($folderpath,'"<>&');
+            }
+            if ($forceedit) {
+                $cfile .= '&amp;forceedit=1';
+            }
+            if ($forcereg) {
+                $cfile .= '&amp;register=1';
+            }
+            $jscall = "need_switchserver('$cfile');";
+        }
+    } else {
+        unless ($cfile =~ m{^/priv/}) {
+            if ($symb) {
+                $cfile .= (($cfile=~/\?/)?'&amp;':'?')."symb=$symb";
+            } elsif ($folderpath) {
+                $cfile .= (($cfile=~/\?/)?'&amp;':'?').
+                          'folderpath='.&HTML::Entities::encode(&escape($folderpath),'"<>&');
+                if ($title) {
+                    $cfile .= (($cfile=~/\?/)?'&amp;':'?').
+                              'title='.&HTML::Entities::encode(&escape($title),'"<>&');
+                }
+                if ($idx) {
+                    $cfile .= (($cfile=~/\?/)?'&amp;':'?').'idx='.$idx;
+                }
+                if ($suppurl) {
+                    $cfile .= (($cfile=~/\?/)?'&amp;':'?').
+                              'suppurl='.&HTML::Entities::encode(&escape($suppurl));
+                }
+            }
+            if ($forceedit) {
+                $cfile .= (($cfile=~/\?/)?'&amp;':'?').'forceedit=1';
+            }
+            if ($forcereg) {
+                $cfile .= (($cfile=~/\?/)?'&amp;':'?').'register=1';
+            }
+        }
+        $jscall = "go('$cfile')";
+    }
+    return $jscall;
+}
+
+##############################################
+##############################################
+
 # javascript_valid_email
 #
 # Generates javascript to validate an e-mail address.
@@ -2698,8 +3286,9 @@ sub scripttag {
     return htmltag('script', $content, {type => 'text/javascript'});
 };
 
+=pod
 
-=item list_from_array( \@array, { listattr =>{}, itemattr =>{} } )
+=item &list_from_array( \@array, { listattr =>{}, itemattr =>{} } )
 
 Constructs a XHTML list from \@array.
 
@@ -2790,7 +3379,8 @@ sub generate_menu {
                                 $$link{alttext} : $$link{linktext})
                             }), {
                             href  => $$link{url},
-                            title => mt($$link{linktitle})
+                            title => mt($$link{linktitle}),
+                            class => 'LC_menubuttons_link'
                             }).
                         $a->(mt($$link{linktext}), {
                             href  => $$link{url},
@@ -2824,7 +3414,7 @@ sub generate_menu {
 
 =pod
 
-=item &start_funclist
+=item &start_funclist()
 
 Start list of available functions
 
@@ -2864,7 +3454,7 @@ sub start_funclist {
 
 =pod
 
-=item &add_item_funclist
+=item &add_item_funclist()
 
 Adds an item to the list of available functions
 
@@ -2890,7 +3480,7 @@ sub add_item_funclist {
 
 =pod
 
-=item &end_funclist
+=item &end_funclist()
 
 End list of available functions
 
@@ -2911,7 +3501,7 @@ sub end_funclist {
 
 =pod
 
-=item funclist_from_array( \@array, {legend => 'text for legend'} )
+=item &funclist_from_array( \@array, {legend => 'text for legend'} )
 
 Constructs a XHTML list from \@array with the first item being visually
 highlighted and set to the value of legend or 'Functions' if legend is