--- loncom/interface/lonhtmlcommon.pm 2013/07/02 19:04:37 1.348 +++ loncom/interface/lonhtmlcommon.pm 2022/10/27 20:08:16 1.408 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common html routines # -# $Id: lonhtmlcommon.pm,v 1.348 2013/07/02 19:04:37 raeburn Exp $ +# $Id: lonhtmlcommon.pm,v 1.408 2022/10/27 20:08:16 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -64,15 +64,26 @@ use HTML::Entities(); use LONCAPA qw(:DEFAULT :match); sub java_not_enabled { - return "\n".''. - &mt('The required Java applet could not be started. Please make sure to have Java installed and active in your browser.'). - "\n"; + if (($env{'browser.mobile'}) && ($env{'browser.mobile'} =~ /^ipad|ipod|iphone$/i)) { + return "\n".''. + &mt('The required Java applet could not be started, because Java is not supported by your mobile device.'). + "\n"; + } else { + return "\n".''. + &mt('The required Java applet could not be started. Please make sure to have Java installed and active in your browser.'). + "\n"; + } } sub coursepreflink { my ($text,$category)=@_; if (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) { - return '&"').'">'.$text.''; + my $target =' target="_top"'; + if ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) || + (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) { + $target =''; + } + return '&"').'">'.$text.''; } else { return ''; } @@ -86,7 +97,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 { @@ -95,9 +106,14 @@ sub direct_parm_link { $filter=&entity_encode($filter); $part=&entity_encode($part); if (($symb) && (&Apache::lonnet::allowed('opa')) && ($target ne 'tex')) { - return "$linktext"; + my $target=' target="_top"'; + if ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) || + (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) { + $target=''; + } + return "$linktext"; } else { - return $linktext; + return $linktext; } } ############################################## @@ -170,7 +186,7 @@ sub dragmath_js { function mathedit(textarea, doc) { targetEntry = textarea; targetDoc = doc; - newwin = window.open("/adm/dragmath/applet/$popup.html","","width=565,height=500,resizable"); + newwin = window.open("/adm/dragmath/$popup.html","","width=565,height=500,resizable"); } // ]]> @@ -401,7 +417,7 @@ sub textbox { ############################################## ############################################## sub checkbox { - my ($name,$checked,$value) = @_; + my ($name,$checked,$value,$special) = @_; my $Str = ''; return $Str; } @@ -445,10 +461,12 @@ sub radio { &date_setter returns html and javascript for a compact date-setting form. To retrieve values from it, use &get_date_from_form. -Inputs - =over 4 +=item Inputs + +=over + =item $dname The name to prepend to the form elements. @@ -476,21 +494,52 @@ the date/time fields are left empty. =item $state Specifies the initial state of the form elements. Either 'disabled' or empty. -Defaults to empty, which indiciates the form elements are not disabled. +Defaults to empty, which indicates the form elements are not disabled. + +=item $no_hh_mm_ss + +If true, text boxes for hours, minutes and seconds are omitted. + +=item $defhour + +Default value for hours (a default of 0 is used otherwise). + +=item $defmin + +Default value for minutes (a default of 0 is used otherwise). + +=item defsec + +Default value for seconds (a default of 0 is used otherwise). + +=item $nolink + +If true, a "Select calendar" link (to pop-up a calendar) is not displayed +to the right of the items. + +=item $no_mm_ss + +If true, text boxes for minutes and seconds are omitted. + +=item $no_ss + +If true, text boxes for seconds are omitted. =back -Bugs +=item Bugs The method used to restrict user input will fail in the year 2400. +=back + =cut ############################################## ############################################## sub date_setter { my ($formname,$dname,$currentvalue,$special,$includeempty,$state, - $no_hh_mm_ss,$defhour,$defmin,$defsec,$nolink) = @_; + $no_hh_mm_ss,$defhour,$defmin,$defsec,$nolink,$no_mm_ss,$no_ss) = @_; my $now = time; my $tzname; @@ -499,6 +548,8 @@ sub date_setter { if (! defined($state) || $state ne 'disabled') { $state = ''; + } else { + $state = 'disabled="disabled"'; } if (! defined($no_hh_mm_ss)) { $no_hh_mm_ss = 0; @@ -634,7 +685,7 @@ ENDJS my $minuteselector = qq{}; my $secondselector= qq{}; my $cal_link; - if (!$nolink) { + unless (($nolink) || ($state eq 'disabled')) { $cal_link = qq{}; } # @@ -643,17 +694,26 @@ ENDJS $result .= &mt('[_1] [_2] [_3] ', $monthselector,$dayselector,$yearselector). $tzone; - if (!$nolink) { - $result .= &mt('[_1]Select Date[_2]',$cal_link,''); - } + } elsif ($no_mm_ss) { + $result .= &mt('[_1] [_2] [_3] [_4]', + $monthselector,$dayselector,$yearselector, + $hourselector). + $tzone; + + } elsif ($no_ss) { + $result .= &mt('[_1] [_2] [_3] [_4] [_5]m', + $monthselector,$dayselector,$yearselector, + $hourselector,$minuteselector). + $tzone; + } 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,''); - } + } + unless (($nolink) || ($state eq 'disabled')) { + $result .= &mt('[_1]Select Date[_2]',$cal_link,''); } $result .= "\n\n"; return $result; @@ -697,7 +757,9 @@ sub build_url { get_date_from_form retrieves the date specified in an &date_setter form. -Inputs: +=over + +=item Inputs: =over 4 @@ -711,6 +773,8 @@ The unix time to use as the default in c =back +=back + Returns: Unix time represented in the form. =cut @@ -804,13 +868,14 @@ parameter setting wizard. ############################################## sub pjump_javascript_definition { my $Str = <&"'); &Apache::loncommon::inhibit_menu_check(\$href_path); if ($form) { my $href = 'javascript:'.$form.".action='".$href_path."';".$form.'.submit();'; - $output.=qq{$dir/}; + $output.=qq{$dir/}; } else { - $output.=qq{$dir/}; + $output.=qq{$dir/}; } } } else { @@ -1215,17 +1316,18 @@ sub htmlareaheaders { ENDEDITOR } $s.=(< - - + + + - + + ENDJQUERY return $s; @@ -1253,6 +1355,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]', + ""); + my $early = '- '.&mt('Submit Early').''; + my $pastdue = '- '.&mt('Past Due').''; + 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 { @@ -1299,6 +1463,41 @@ sub htmlareaselectactive { } function startRichEditor(id) { + // fix character entities inside + // NOTE: this is not fixing characters inside + // NOTE: < and > inside should fix automatically because there should not be a letter after <. + var ta = document.getElementById(id); + var value = ta.value; + var in_m = false; // in the m element + var in_text = false; // in the text inside the m element + var im = -1; // position of + var it = -1; // position of the text inside + for (var i=0; i if found twice + in_m = true; + in_text = false; + im = i; + it = -1; + } else if (in_m) { + if (!in_text) { + if (value.charAt(i) == ">") { + in_text = true; + it = i+1; + } + } else if (value.substr(i, 4) == "") { + in_m = false; + var text = value.substr(it, i-it); + var l1 = text.length; + text = text.replace(//g, ">"); + var l2 = text.length; + value = value.substr(0, it) + text + "" + value.substr(i+4); + i = i + (l2-l1); + } + } + } + ta.value = value; CKEDITOR.replace(id, { customConfig: "/ckeditor/loncapaconfig.js", @@ -1310,6 +1509,68 @@ sub htmlareaselectactive { function destroyRichEditor(id) { CKEDITOR.instances[id].destroy(); + // replace character entities < and > in and + // and "&fctname(" by "&fctname(" + // and the quotes inside functions: "&fct(1, "a")" -> "&fct(1, "a")" + var ta = document.getElementById(id); + var value = ta.value; + var in_element = false; // in the m or chem element + var tagname = ""; // m or chem + var in_text = false; // in the text inside the element + var im = -1; // position of start tag + var it = -1; // position of the text inside + for (var i=0; i") { + in_text = true; + it = i+1; + } + } else if (value.substr(i, 3+tagname.length) == "") { + in_element = false; + var text = value.substr(it, i-it); + var l1 = text.length; + text = text.replace(/</g, "<"); + text = text.replace(/>/g, ">"); + var l2 = text.length; + value = value.substr(0, it) + text + value.substr(i); + i = i + (l2-l1); + } + } + } + // fix function names + value = value.replace(/&([a-zA-Z_]+)\(/g, "&$1("); + // fix quotes in functions + var pos_next_fct = value.search(/&[a-zA-Z_]+\(/); + var depth = 0; + for (var i=0; i 0) { + if (value.charAt(i) == ")") + depth--; + else if (value.substr(i, 6) == """) + value = value.substr(0, i) + "\"" + value.substr(i+6); + } + } + // replace the text value + ta.value = value; } function editorHandler(event) { @@ -1379,62 +1640,11 @@ sub htmlareaselectactive { }); '; - $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]', - ""); - my $early = '- '.&mt('Submit Early').''; - my $pastdue = '- '.&mt('Past Due').''; - $output .= <{'main'} eq 'n') { + undef($menulink); + } + } + } + } + if ($menulink) { my $description = 'Menu'; my $no_mt_descr = 0; if ((exists($env{'request.course.id'})) && @@ -1675,7 +1945,7 @@ returns: nothing $env{'course.'.$env{'request.course.id'}.'.description'}; $no_mt_descr = 1; if ($env{'request.noversionuri'} =~ - m{^/public/($match_domain)/($match_courseid)/syllabus$}) { + 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'; @@ -1683,9 +1953,16 @@ returns: nothing } } } + my $target = '_top'; + if ($links_target) { + $target = $links_target; + } elsif ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) || + (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) { + $target=''; + } $menulink = { href =>'/adm/menu', title =>'Go to main menu', - target =>'_top', + target =>$target, text =>$description, no_mt =>$no_mt_descr, }; if($last) { @@ -1699,11 +1976,15 @@ returns: nothing my $links; if ((&show_return_link) && (!$CourseBreadcrumbs) && (ref($last) eq 'HASH')) { my $alttext = &mt('Go Back'); + my $hashref = { href => '/adm/flip?postdata=return:', + title => &mt('Back to most recent content resource'), + class => 'LC_menubuttons_link', + }; + if ($links_target) { + $hashref->{'target'} = $links_target; + } $links=&htmltag( 'a',''.$alttext.'', - { href => '/adm/flip?postdata=return:', - title => &mt('Back to most recent content resource'), - class => 'LC_menubuttons_link', - }); + $hashref); $links=&htmltag('li',$links); } $links.= join "", @@ -1751,7 +2032,12 @@ returns: nothing if ($faq ne '' || $component_help ne '' || $bug ne '') { $icons .= &Apache::loncommon::help_open_menu($component, $component_help, - $faq,$bug); + $faq,$bug,'','','','', + $links_target); + } + if ($topic_help && $topic_help_text) { + $icons .= ' '.&Apache::loncommon::help_open_topic($topic_help,&mt($topic_help_text),'', + undef,600,'',$links_target); } # @@ -1765,7 +2051,7 @@ returns: nothing } - if ($component) { + if (($component) || ($topic_help && $topic_help_text)) { $links = &htmltag('span', ( $no_mt ? $component : mt($component) ). ( $icons ? $icons : '' ), @@ -1820,7 +2106,9 @@ Adds $html to $category of the breadcrum $html is usually a link to a page that invokes a function on the currently displayed data (e.g. print when viewing a problem) -Currently there are 3 possible values for $category: +=over + +=item Currently there are 3 possible values for $category: =over @@ -1834,7 +2122,9 @@ remaining items in right of breadcrumbs advanced tools shown in a separate box below breadcrumbs line =back - + +=back + returns: nothing =cut @@ -1868,13 +2158,32 @@ returns: nothing undef(%tools); } +=item ¤t_breadcrumb_tools() + +returns: a hash containing the current breadcrumb tools. + +=cut + + sub current_breadcrumb_tools { + return %tools; + } + =item &render_tools(\$breadcrumbs) Creates html for breadcrumb tools (categories navigation and tools) and inserts \$breadcrumbs at the correct position. -input: \$breadcrumbs - a reference to the string containing prepared -breadcrumbs. +=over + +=item input: + +=over + +=item \$breadcrumbs - a reference to the string containing prepared breadcrumbs. + +=back + +=back returns: nothing @@ -1900,8 +2209,17 @@ returns: nothing Creates html for advanced tools (category advtools) and inserts \$breadcrumbs at the correct position. -input: \$breadcrumbs - a reference to the string containing prepared -breadcrumbs (after render_tools call). +=over + +=item input: + +=over + +=item \$breadcrumbs - a reference to the string containing prepared breadcrumbs (after render_tools call). + +=back + +=back returns: nothing @@ -1919,7 +2237,7 @@ returns: nothing } # End of scope for @Crumbs sub docs_breadcrumbs { - my ($allowed,$crstype,$contenteditor,$title,$precleared)=@_; + my ($allowed,$crstype,$contenteditor,$title,$precleared,$checklinkprot)=@_; my ($folderpath,@folders,$supplementalflag); @folders = split('&',$env{'form.folderpath'}); if ($env{'form.folderpath'} =~ /^supplemental/) { @@ -1934,19 +2252,21 @@ sub docs_breadcrumbs { my $foldername=shift(@folders); if ($folderpath) {$folderpath.='&';} $folderpath.=$folder.'&'.$foldername; - my $url; + my $url = $env{'request.use_absolute'}; if ($allowed) { - $url = '/adm/coursedocs?folderpath='; + $url .= '/adm/coursedocs?folderpath='; } else { - $url = '/adm/supplemental?folderpath='; + $url .= '/adm/supplemental?folderpath='; } $url .= &escape($folderpath); my $name=&unescape($foldername); # each of randompick number, hidden, encrypted, random order, is_page # are appended with ":"s to the foldername $name=~s/\:(\d*)\:(\w*)\:(\w*):(\d*)\:?(\d*)$//; - unless ($supplementalflag) { - if ($contenteditor) { + if ($contenteditor) { + if ($supplementalflag) { + if ($2) { $ishidden=1; } + } else { if ($1 ne '') { $randompick=$1; } else { @@ -1990,6 +2310,14 @@ sub docs_breadcrumbs { if (!$allowed && !$contenteditor) { $menulink = 1; } + if ($checklinkprot) { + if ($env{'request.deeplink.login'}) { + my $linkprotout = &Apache::lonmenu::linkprot_exit(); + if ($linkprotout) { + &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$linkprotout); + } + } + } return (&breadcrumbs(undef,undef,$menulink,'nohelp',undef,undef, $contenteditor), $randompick,$ishidden,$isencrypted,$plain, @@ -2230,10 +2558,10 @@ sub course_selection { if ($totcodes > 0) { my $numtitles = @$codetitles; if ($numtitles > 0) { - $output .= '
'; + $output .= '
'; $output .= ''; @@ -2363,9 +2691,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 = '
'; } if ($symb) { @@ -2394,7 +2722,7 @@ sub resource_info_box { } else { $return='

'.&mt('No context provided.').'

'; } - if ($stuvcurrent ne '') { + if (($stuvcurrent ne '') || ($divforres)) { $return .= '
'; } return $return; @@ -2409,9 +2737,9 @@ sub resource_info_box { # sub display_usage { - my ($current_disk_usage,$disk_quota) = @_; - my $usage = $current_disk_usage/1000; - my $quota = $disk_quota/1000; + my ($current_disk_usage,$disk_quota,$context) = @_; + my $usage = $current_disk_usage/1024; + my $quota = $disk_quota/1024; my $percent; if ($disk_quota == 0) { $percent = 100.0; @@ -2435,9 +2763,13 @@ sub display_usage { if ($prog_width > 100) { $prog_width = 100; } + my $display = 'block'; + if ($context eq 'authoring') { + $display = 'inline'; + } return ' -
'.&mt('Currently using [_1] of the [_2] available.',$usage.' MB ('.$percent.'%)',$quota.' MB')."\n". -'
'."\n". +
'.&mt('Currently using [_1] of the [_2] available.',$usage.' MB ('.$percent.'%)',$quota.' MB')."\n". +'
'."\n". '
'."\n". '
'."\n". '
'; @@ -2687,10 +3019,12 @@ sub set_form_elements { sub file_submissionchk_js { my ($turninpaths,$multiples) = @_; - my $overwritewarn = &mt('File(s) you uploaded for your submission will overwrite existing file(s) submitted for this item').'\\n'. + my $overwritewarn = &mt('File(s) you uploaded for your submission will overwrite existing file(s) submitted for this item')."\n". &mt('Continue submission and overwrite the file(s)?'); - my $delfilewarn = &mt('You have indicated you wish to remove some files previously included in your submission.').'\\n'. + &js_escape(\$overwritewarn); + my $delfilewarn = &mt('You have indicated you wish to remove some files previously included in your submission.')."\n". &mt('Continue submission with these files removed?'); + &js_escape(\$delfilewarn); my ($turninpathtext,$multtext,$arrayindexofjs); if (ref($turninpaths) eq 'HASH') { foreach my $key (sort(keys(%{$turninpaths}))) { @@ -2931,7 +3265,7 @@ ENDSCRIPT ############################################## sub resize_scrollbox_js { - my ($context,$tabidstr) = @_; + my ($context,$tabidstr,$tid) = @_; my (%names,$paddingwfrac,$offsetwfrac,$offsetv,$minw,$minv); if ($context eq 'docs') { %names = ( @@ -2941,7 +3275,7 @@ sub resize_scrollbox_js { scroll => 'contentscroll', boxh => 'contenteditor', ); - $paddingwfrac = 0.09; + $paddingwfrac = 0.09; $offsetwfrac = 0.015; $offsetv = 20; $minw = 250; @@ -2967,9 +3301,11 @@ window.onresize=callResize; '; if ($context eq 'docs') { - $output .= ' -var activeTab; -'; + if ($env{'form.active'}) { + $output .= "\nvar activeTab = '$env{'form.active'}$tid';\n"; + } else { + $output .= "\nvar activeTab = '';\n"; + } } $output .= <<"FIRST"; @@ -2980,6 +3316,7 @@ function resize_scrollbox(scrollboxname, var scrolltableid = 'table_'+scrollboxname; var scrollbox; var scrolltable; + var ismobile = '$env{'browser.mobile'}'; if (document.getElementById("$names{'boxw'}") == null) { return; @@ -3016,6 +3353,7 @@ FIRST } $output .= <<"SECOND"; var listwchange; + var scrollchange; if (chkw == 1) { var boxw = document.getElementById("$names{'boxw'}").offsetWidth; var itemw; @@ -3027,6 +3365,7 @@ FIRST var scrollboxw = scrollbox.offsetWidth; var scrollboxscrollw = scrollbox.scrollWidth; + var scrollstart = scrollboxw; var offsetw = parseInt(vpw * $offsetwfrac); var paddingw = parseInt(vpw * $paddingwfrac); @@ -3101,17 +3440,31 @@ PARAMSONE } } + if (newscrollboxw != scrollboxw) { + scrollchange = 1; + } + if (itemid.offsetWidth != itemwstart) { listwchange = 1; } } if ((chkh == 1) || (listwchange)) { - var primaryheight = document.getElementById('LC_nav_bar').offsetHeight; - var secondaryheight; + var itemid = document.getElementById("$names{'item'}"); + if (itemid != null) { + itemh = itemid.offsetHeight; + } + var primaryheight = 0; + if (document.getElementById('LC_nav_bar') != null) { + primaryheight = document.getElementById('LC_nav_bar').offsetHeight; + } + var secondaryheight = 0; if (document.getElementById('LC_secondary_menu') != null) { secondaryheight = document.getElementById('LC_secondary_menu').offsetHeight; } - var crumbsheight = document.getElementById('LC_breadcrumbs').offsetHeight; + var crumbsheight = 0; + if (document.getElementById('LC_breadcrumbs') != null) { + crumbsheight = document.getElementById('LC_breadcrumbs').offsetHeight; + } var dccidheight = 0; if (document.getElementById('dccid') != null) { dccidheight = document.getElementById('dccid').offsetHeight; @@ -3126,6 +3479,7 @@ PARAMSONE var scrollboxheight = scrollbox.offsetHeight; var scrollboxscrollheight = scrollbox.scrollHeight; + var scrollboxh = scrollboxheight; var minvscrollbox = $minv; var offsetv = $offsetv; @@ -3156,6 +3510,13 @@ PARAMSONE scrollbox.style.height = newscrollheight+"px"; } } + var newscrollboxh = scrollbox.offsetHeight; + if (scrollboxh != newscrollboxh) { + scrollchange = 1; + } + } + if (ismobile && scrollchange) { + \$("#div_$names{'scroll'}").getNiceScroll().onResize(); } return; } @@ -3174,21 +3535,39 @@ THIRD ############################################## sub javascript_jumpto_resource { - my $confirm_switch = &mt("Editing requires switching to the resource's home server.").'\n'. + my $confirm_switch = &mt("Editing requires switching to the resource's home server.")."\n". &mt('Switch server?'); + my $confirm_new_tab = &mt("Editing requires using the resource's home server.")."\n". + &mt('Open a new browser tab?'); + &js_escape(\$confirm_switch); + &js_escape(\$confirm_new_tab); return (<&'); - if ($symb) { - $cfile .= '&symb='.&HTML::Entities::encode($symb,'"<>&'); + if ($shownsymb) { + $cfile .= '&symb='.&HTML::Entities::encode($shownsymb,'"<>&'); + if ($resedit) { + $cfile .= '&edit=1'; + } } elsif ($folderpath) { $cfile .= '&folderpath='.&HTML::Entities::encode($folderpath,'"<>&'); } @@ -3218,11 +3617,48 @@ sub jump_to_editres { if ($forcereg) { $cfile .= '&register=1'; } - $jscall = "need_switchserver('$cfile');"; + $jscall = "need_switchserver('".&Apache::loncommon::escape_single($cfile)."','$target')"; } } else { unless ($cfile =~ m{^/priv/}) { + if ($cfile =~ m{^(/adm/wrapper/ext/([^#]+))(?:|#([^#]+))$}) { + $cfile = $1; + my $extlink = $2; + $anchor = $3; + $is_ext = 1; + if (($extlink !~ /^https:/) && ($ENV{'SERVER_PORT'} == 443)) { + 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; + } + } + } elsif ($cfile =~ m{^/?public/($match_domain)/($match_courseid)/syllabus}) { + if ($ENV{'SERVER_PORT'} == 443) { + my ($cdom,$cnum) = ($1,$2); + if (($env{'request.course.id'}) && + ($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://}) { + 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; + } + } + } if ($symb) { + if ($anchor ne '') { + if ($symb =~ m{^([^#]+)\Q#$anchor\E$}) { + $symb = $1.&escape(&escape('#')).$anchor; + } + } $cfile .= (($cfile=~/\?/)?'&':'?')."symb=$symb"; } elsif ($folderpath) { $cfile .= (($cfile=~/\?/)?'&':'?'). @@ -3241,15 +3677,32 @@ sub jump_to_editres { } if ($forceedit) { $cfile .= (($cfile=~/\?/)?'&':'?').'forceedit=1'; + if ($usehttps) { + $cfile = $env{'request.use_absolute'}.(($cfile =~ /^\//)? '':'/').$cfile; + } + } elsif ($usehttp) { + if ($hostname ne '') { + $cfile = 'http://'.$hostname.(($cfile =~ /^\//)? '':'/').$cfile; + } + $cfile .= (($cfile=~/\?/)?'&':'?').'usehttp=1'; + } elsif ($usehttps) { + $cfile = $env{'request.use_absolute'}.(($cfile =~ /^\//)? '':'/').$cfile; } if ($forcereg) { $cfile .= (($cfile=~/\?/)?'&':'?').'register=1'; } if ($todocs) { - $cfile .= (($cfile=~/\?/)?'&':'?').'todocs=1'; + $cfile .= (($cfile=~/\?/)?'&':'?').'todocs=1'; + } + if ($suppanchor ne '') { + $cfile .= (($cfile=~/\?/)?'&':'?').'anchor='. + &HTML::Entities::encode($suppanchor,'"<>&'); } } - $jscall = "go('$cfile')"; + if ($anchor ne '') { + $cfile .= '#'.$anchor; + } + $jscall = "go('".&Apache::loncommon::escape_single($cfile)."')"; } return $jscall; } @@ -3260,15 +3713,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})(\\]?)$"; //" @@ -3316,7 +3772,7 @@ END sub htmltag{ return qq|<$_[0]| - . join( '', map { qq| $_="${$_[2]}{$_}"| if ${$_[2]}{$_} } keys %{ $_[2] } ) + . join( '', map { qq| $_="${$_[2]}{$_}"| if ${$_[2]}{$_} } keys(%{ $_[2] }) ) . ($_[1] ? qq|>$_[1]| : qq|/>|). "\n"; }; @@ -3378,7 +3834,9 @@ sub scripttag { Constructs a XHTML list from \@array. -input: +=over + +=item input: =over @@ -3392,7 +3850,9 @@ Attributes for
    and
  • passed in a See htmltag() for more details. =back - + +=back + returns: XHTML list as String. =cut @@ -3511,13 +3971,19 @@ should be included in this list. If the optional headline text is not provided, a default text will be used. +=over + +=item Related routines: -Related routines: =over 4 -add_item_funclist -end_funclist + +=item add_item_funclist + +=item end_funclist + =back +=back Inputs: (optional) headline text @@ -3544,10 +4010,18 @@ sub start_funclist { Adds an item to the list of available functions -Related routines: +=over + +=item Related routines: + =over 4 -start_funclist -end_funclist + +=item start_funclist + +=item end_funclist + +=back + =back Inputs: content item with text and link to function @@ -3570,15 +4044,19 @@ sub add_item_funclist { End list of available functions -Related routines: -=over 4 -start_funclist -add_item_funclist +=over + +=item Related routines: + + start_funclist + add_item_funclist + =back Inputs: ./. Returns: HTML code with function list end + =cut sub end_funclist { @@ -3605,7 +4083,7 @@ A string that's used as visually highlig it's value evaluates to false. =back - + returns: XHTML list as string. =back @@ -3618,10 +4096,12 @@ sub funclist_from_array { $args->{legend} ||= mt('Functions'); return list_from_array( [$args->{legend}, @$items], { listattr => {class => 'LC_funclist'} }); -} +} =pod +=over + =item &actionbox( \@array ) Constructs a XHTML list from \@array with the first item being visually @@ -3638,7 +4118,7 @@ e.g. a file operation in Authoring Space A reference to the array containing text. Details: sub funclist_from_array =back - + Returns: XHTML div as string. =back
'.$$codetitles[0].'
'."\n". '
'.$$codetitles[$i].'
'."\n". ''."\n". '