--- loncom/interface/lonhtmlcommon.pm 2013/06/09 02:59:04 1.347
+++ loncom/interface/lonhtmlcommon.pm 2022/09/13 12:22:14 1.406
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# a pile of common html routines
#
-# $Id: lonhtmlcommon.pm,v 1.347 2013/06/09 02:59:04 raeburn Exp $
+# $Id: lonhtmlcommon.pm,v 1.406 2022/09/13 12:22:14 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) == ""+tagname+">") {
+ 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','',
- { 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,11 +2252,11 @@ 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);
@@ -1990,6 +2308,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 +2556,10 @@ sub course_selection {
if ($totcodes > 0) {
my $numtitles = @$codetitles;
if ($numtitles > 0) {
- $output .= ' ';
+ $output .= ' ';
$output .= '