diff --git a/js/swad18.58.js b/js/swad18.58.js new file mode 100644 index 000000000..c3a9b368f --- /dev/null +++ b/js/swad18.58.js @@ -0,0 +1,1493 @@ +// swad.js: javascript functions + +/* + SWAD (Shared Workspace At a Distance), + is a web platform developed at the University of Granada (Spain), + and used to support university teaching. + Copyright (C) 1999-2019 Antonio Caņas-Vargas + University of Granada (SPAIN) (acanas@ugr.es) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +"use strict"; + +// Global variable (string) used to write HTML +var Gbl_HTMLContent; + +// Global variable used to call SWAD via AJAX +var ActionAJAX; + +// Global variables used in writeLocalClock() +var secondsSince1970UTC; + +// Global variables used in writeClockConnected() +var NumUsrsCon; +var ListSeconds = []; +var countClockConnected = 0; + +/*************** Write a date-time in client local time **********************/ +// id is the id of the HTML element in which date-time will be written +// TimeUTC is the date-time to write in UTC UNIX time format +// DateFormat: +// Dat_FORMAT_YYYY_MM_DD = 0 +// Dat_FORMAT_DD_MONTH_YYYY = 1 +// Dat_FORMAT_MONTH_DD_YYYY = 2 +// separator is HTML code to write between date and time +// StrToday is a string in current language ('today', 'hoy'...) +// WriteDateOnSameDay = false ==> don't write date if it's the same day than the last call +// WriteWeekDay = true ==> write day of the week ('monday', 'tuesday'...) +// WriteHMS = 3 least significant bits for hour, minute and second + +function writeLocalDateHMSFromUTC (id,TimeUTC,DateFormat,Separator,StrToday, + WriteDateOnSameDay,WriteWeekDay,WriteHMS) { + // HMS: Hour, Minutes, Seconds + var today = new Date(); + var todayYea = today.getFullYear(); + var todayMon = today.getMonth() + 1; + var todayDay = today.getDate(); + var d = new Date(); + var WriteDate; + var WriteTodayStr; + var Yea; + var Mon; + var Day; + var DayOfWeek; + var Hou; + var Min; + var Sec; + var StrDate; + var StrMon; + var StrDay; + var StrHou; + var StrMin; + var StrSec; + + d.setTime(TimeUTC * 1000); + Yea = d.getFullYear(); + Mon = d.getMonth() + 1; + Day = d.getDate(); + + if (WriteDateOnSameDay) + WriteDate = true; + // Check to see if the last date has been initialized + else if (typeof writeLocalDateHMSFromUTC.lastd == 'undefined') // Static variable to remember current date for the next call + // Not initialized + WriteDate = true; + else + WriteDate = (Yea != writeLocalDateHMSFromUTC.lastd.getFullYear() || + Mon != writeLocalDateHMSFromUTC.lastd.getMonth() + 1 || + Day != writeLocalDateHMSFromUTC.lastd.getDate()) + + writeLocalDateHMSFromUTC.lastd = d; // Update last date for the next call + + /* Set date */ + if (WriteDate) { + WriteTodayStr = false; + if (StrToday != null) + if (StrToday.length && + Yea == todayYea && + Mon == todayMon && + Day == todayDay) // Today + WriteTodayStr = true; + + if (WriteTodayStr) + StrDate = StrToday; + else + switch (DateFormat) { + case 0: // Dat_FORMAT_YYYY_MM_DD + StrMon = ((Mon < 10) ? '0' : '') + Mon; + StrDay = ((Day < 10) ? '0' : '') + Day; + StrDate = Yea.toString() + '-' + StrMon + '-' + StrDay; + break; + case 1: // Dat_FORMAT_DD_MONTH_YYYY + StrDate = Day.toString() + ' ' + MonthsShort[Mon - 1] + ' ' + Yea.toString(); + break; + case 2: // Dat_FORMAT_MONTH_DD_YYYY + StrDate = MonthsShort[Mon - 1] + ' ' + Day.toString() + ', ' + Yea.toString(); + break; + default: + StrDate = ''; + break; + } + + if (WriteWeekDay) { + DayOfWeek = d.getDay(); + DayOfWeek = (DayOfWeek == 0) ? 6 : DayOfWeek - 1; + StrDate = StrDate + Separator + DAYS[DayOfWeek]; + } + StrDate = StrDate + Separator; + } + else + StrDate = ''; + + /* Set HH:MM:SS */ + StrHou = ''; + StrMin = ''; + StrSec = ''; + if (WriteHMS & (1<<2)) { + // Bit 2 on => Write hour + Hou = d.getHours(); + StrHou = ((Hou < 10) ? '0' : '') + Hou; + if (WriteHMS & (1<<1)) { + // Bits 2,1 on => Write minutes + Min = d.getMinutes(); + StrMin = ((Min < 10) ? ':0' : ':') + Min; + if (WriteHMS & 1) { + // Bits 2,1,0 on => Write seconds + Sec = d.getSeconds(); + StrSec = ((Sec < 10) ? ':0' : ':') + Sec; + } + } + } + + /* Write date and time */ + document.getElementById(id).innerHTML = StrDate + StrHou + StrMin + StrSec; +} + +// Set local date-time form fields from UTC time +function setLocalDateTimeFormFromUTC (id,TimeUTC) { + var FormYea = document.getElementById(id+'Year' ); + var FormMon = document.getElementById(id+'Month' ); + var FormDay = document.getElementById(id+'Day' ); + var FormHou = document.getElementById(id+'Hour' ); + var FormMin = document.getElementById(id+'Minute'); + var FormSec = document.getElementById(id+'Second'); + var d; + var Year; + var YearIsValid = false; + var Hou; + var Min; + var Sec; + + if (TimeUTC) { + d = new Date(); + d.setTime(TimeUTC * 1000); + + Year = d.getFullYear(); + for (var i=0; i=0; i--) + if (FormMin.options[i].value <= Min) { + FormMin.options[i].selected = true; + break; + } + + if (FormSec) { + Sec = d.getSeconds(); + FormSec.options[d.getSeconds()].selected = true; + } + } + } + + if (!YearIsValid) { + FormYea.options[0].selected = true; + FormMon.options[0].selected = true; + FormDay.options[0].selected = true; + FormHou.options[0].selected = true; + FormMin.options[0].selected = true; + FormSec.options[0].selected = true; + } +} + +// Set UTC time from local date-time form fields +function setUTCFromLocalDateTimeForm (id) { + var Seconds = 0; + var idSecond = document.getElementById(id+'Second'); + if (idSecond) // id+'Second' present + Seconds = idSecond.value; + // Important: set date all at once to avoid problems with different length of months + var d = new Date(document.getElementById(id+'Year' ).value, + document.getElementById(id+'Month' ).value-1, + document.getElementById(id+'Day' ).value, + document.getElementById(id+'Hour' ).value, + document.getElementById(id+'Minute').value, + Seconds, + 0); + document.getElementById(id+'TimeUTC').value = d.getTime() / 1000; +} + +// Set form param with time difference between UTC time and client local time, in minutes +// For example, if your time zone is GMT+2, -120 will be returned +function setTZ (id) { + var FormTZ = document.getElementById(id); + var d = new Date(); + + FormTZ.value = d.getTimezoneOffset(); +} + +// Set form param with time difference between UTC time and client local time, in minutes +// For example, if your time zone is GMT+2, -120 will be returned +function setTZname (id) { + var FormTZname = document.getElementById(id); + var tz = jstz.determine(); // Determines the time zone of the browser client + FormTZname.value = tz.name(); // Returns the name of the time zone eg "Europe/Berlin" +} + +// Get number of days in a month +function daysInMonth (month, year) { //Month is 1 based + return new Date(year, month, 0).getDate(); // 0 is the last day of previous month +} + +// Adjust a date form correcting selector days in the month +// The selector of days can start by 1, 2, 3... or by -, 1, 2, 3... +function adjustDateForm (id) { + var FormYea = document.getElementById(id+'Year' ); + var FormMon = document.getElementById(id+'Month'); + var FormDay = document.getElementById(id+'Day' ); + var Yea = Number(FormYea.options[FormYea.selectedIndex].value); + var Mon = Number(FormMon.options[FormMon.selectedIndex].value); + + if (Yea != 0 && Mon != 0) { + var LastDayIndex = FormDay.options.length - 1; + var LastDayValue = Number(FormDay.options[LastDayIndex].value); + var SelectedDay = Number(FormDay.options[FormDay.selectedIndex].value); + var Days = daysInMonth (Mon,Yea); + + // If current selected day is out of range... + if (SelectedDay > Days) + // Select last day in month + for (var i=LastDayIndex; i>=0; i--) + if (FormDay.options[i].value == Days) { + FormDay.options[i].selected = true; + break; + } + + // Create new days at the end if necessary + for (var Day = Number(LastDayValue) + 1; Day <= Days; Day++) { + var opt = document.createElement('option'); + opt.value = opt.text = Day; + FormDay.add(opt); + } + + // Remove days from the end if necessary + for (var i=LastDayIndex; i>=0; i--) + if (FormDay.options[i].value > Days) + FormDay.options[i] = null; + else + break; + } +} + +// Set a date range form to yesterday +function setDateToYesterday (idStart,idEnd) { + var d = new Date(); + + d.setTime (d.getTime () - 24 * 60 * 60 * 1000); // Today - 1 day + setDateRange(idStart,idEnd,d); +} + +// Set a date range form to today +function setDateToToday (idStart,idEnd) { + var d = new Date(); + + setDateRange(idStart,idEnd,d); +} + +// Set a date range form to a specific day +function setDateRange (idStart,idEnd,d) { + var FormYea; + var Yea = d.getFullYear(); + var Mon = d.getMonth() + 1; + var Day = d.getDate(); + + FormYea = document.getElementById(idStart+'Year'); + for (var i=0; i= 60) { + H = Math.floor(M / 60); + M %= 60; + } else + H = 0; + S = ListSeconds[i] % 60; + if (H != 0) { + StrM = ((M < 10) ? '0' : '') + M; + StrS = ((S < 10) ? '0' : '') + S; + PrintableClock = H + ':' + StrM + ''' + StrS + '"'; + } else if (M != 0) { + StrS = ((S < 10) ? '0' : '') + S; + PrintableClock = M + ''' + StrS + '"'; + } else + PrintableClock = S + '"'; + BoxClock.innerHTML = PrintableClock; + } + } + } + setTimeout('writeClockConnected()',1000); // refresh after 1 second +} + +// Automatic refresh of connected users using AJAX. This function must be called from time to time +var objXMLHttpReqCon = false; +function refreshConnected () { + objXMLHttpReqCon = AJAXCreateObject(); + if (objXMLHttpReqCon) { + var RefreshParams = RefreshParamNxtActCon + '&' + + RefreshParamIdSes + '&' + + RefreshParamCrsCod; + + objXMLHttpReqCon.onreadystatechange = readConnUsrsData; // onreadystatechange must be lowercase + objXMLHttpReqCon.open('POST',ActionAJAX,true); + objXMLHttpReqCon.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + objXMLHttpReqCon.send(RefreshParams); + } +} + +// Automatic refresh of last clicks using AJAX. This function must be called from time to time +var objXMLHttpReqLog = false; +function refreshLastClicks () { + objXMLHttpReqLog = AJAXCreateObject(); + if (objXMLHttpReqLog) { + var RefreshParams = RefreshParamNxtActLog + '&' + + RefreshParamIdSes + '&' + + RefreshParamCrsCod; + + objXMLHttpReqLog.onreadystatechange = readLastClicksData; // onreadystatechange must be lowercase + objXMLHttpReqLog.open('POST',ActionAJAX,true); + objXMLHttpReqLog.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + objXMLHttpReqLog.send(RefreshParams); + } +} + +// Automatic refresh of new publishings in social timeline using AJAX. This function must be called from time to time +var objXMLHttpReqSoc = false; +function refreshNewTimeline () { + objXMLHttpReqSoc = AJAXCreateObject(); + if (objXMLHttpReqSoc) { + var RefreshParams = RefreshParamNxtActNewPub + '&' + + RefreshParamIdSes + '&' + + RefreshParamWhichUsrs; + + objXMLHttpReqSoc.onreadystatechange = readNewTimelineData; // onreadystatechange must be lowercase + objXMLHttpReqSoc.open('POST',ActionAJAX,true); + objXMLHttpReqSoc.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + objXMLHttpReqSoc.send(RefreshParams); + } +} + +// Refresh of old publishings in social timeline using AJAX. This function is called when user clicks in link +var objXMLHttpReqSoc = false; +function refreshOldTimeline () { + objXMLHttpReqSoc = AJAXCreateObject (); + if (objXMLHttpReqSoc) { + var RefreshParams = RefreshParamNxtActOldPub + '&' + + RefreshParamIdSes; + if (RefreshParamUsr) { + if (RefreshParamUsr.length) + RefreshParams += '&' + RefreshParamUsr; + } + else { + if (RefreshParamWhichUsrs.length) + RefreshParams += '&' + RefreshParamWhichUsrs; + } + + objXMLHttpReqSoc.onreadystatechange = readOldTimelineData; // onreadystatechange must be lowercase + objXMLHttpReqSoc.open('POST',ActionAJAX,true); + objXMLHttpReqSoc.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + objXMLHttpReqSoc.send(RefreshParams); + } +} + +// Create AJAX object (try is unknown in earlier versions of Netscape, but works in IE5) +function AJAXCreateObject () { + var obj = false; + if (window.XMLHttpRequest) { // Mozilla, Safari,... + obj = new XMLHttpRequest(); + } else if (window.ActiveXObject) { // IE + try { + obj = new ActiveXObject('Msxml2.XMLHTTP'); + } catch (e) { + try { + obj = new ActiveXObject('Microsoft.XMLHTTP'); + } catch (e) {} + } + } + return obj; +} + +// Receives and show connected users data +function readConnUsrsData () { + if (objXMLHttpReqCon.readyState == 4) { // Check if data have been received + if (objXMLHttpReqCon.status == 200) { + var endOfDelay = objXMLHttpReqCon.responseText.indexOf('|',0); // Get separator position + var endOfNotif = objXMLHttpReqCon.responseText.indexOf('|',endOfDelay + 1); // Get separator position + var endOfGblCon = objXMLHttpReqCon.responseText.indexOf('|',endOfNotif + 1); // Get separator position + var endOfCrsCon = objXMLHttpReqCon.responseText.indexOf('|',endOfGblCon + 1); // Get separator position + var endOfNumUsrs = objXMLHttpReqCon.responseText.indexOf('|',endOfCrsCon + 1); // Get separator position + + var delay = parseInt(objXMLHttpReqCon.responseText.substring(0,endOfDelay)); // Get refresh delay + var htmlNotif = objXMLHttpReqCon.responseText.substring(endOfDelay + 1,endOfNotif); // Get HTML code for new notifications + var htmlGblCon = objXMLHttpReqCon.responseText.substring(endOfNotif + 1,endOfGblCon); // Get HTML code for connected + var htmlCrsCon = objXMLHttpReqCon.responseText.substring(endOfGblCon + 1,endOfCrsCon); // Get HTML code for course connected + var NumUsrsStr = objXMLHttpReqCon.responseText.substring(endOfCrsCon + 1,endOfNumUsrs); // Get number of users + var startOfUsr; + var endOfUsr; + + NumUsrsCon = (NumUsrsStr.length ? parseInt(NumUsrsStr) : 0); + + var divNewNotif = document.getElementById('msg'); // Access to new notifications DIV + if (divNewNotif) + divNewNotif.innerHTML = (htmlNotif.length) ? htmlNotif : ''; // Update new notifications DIV + + var divGblConnected = document.getElementById('globalconnected'); // Access to global connected DIV + if (divGblConnected) + divGblConnected.innerHTML = htmlGblCon; // Update global connected DIV + if (htmlCrsCon.length) { + var divCrsConnected = document.getElementById('courseconnected'); // Access to course connected DIV + if (divCrsConnected) { + divCrsConnected.innerHTML = htmlCrsCon; // Update course connected DIV + countClockConnected = 0; // Don't refresh again using writeClockConnected until past 10 seconds + startOfUsr = endOfNumUsrs + 1; + for (var NumUsr=0; NumUsr= 60000) // If refresh slower than 1 time each 60 seconds, do refresh; else abort + setTimeout('refreshConnected()',delay); + } + } +} + +// Receives and show last clicks data +function readLastClicksData () { + if (objXMLHttpReqLog.readyState == 4) { // Check if data have been received + if (objXMLHttpReqLog.status == 200) { + var endOfDelay = objXMLHttpReqLog.responseText.indexOf('|',0); // Get separator position + + var delay = parseInt(objXMLHttpReqLog.responseText.substring(0,endOfDelay)); // Get refresh delay + var htmlLastClicks = objXMLHttpReqLog.responseText.substring(endOfDelay + 1); // Get HTML code for last clicks + + var divLastClicks = document.getElementById('lastclicks'); // Access to last click DIV + if (divLastClicks) + divLastClicks.innerHTML = htmlLastClicks; // Update global connected DIV + if (delay > 200) // If refresh slower than 1 time each 0.2 seconds, do refresh; else abort + setTimeout('refreshLastClicks()',delay); + } + } +} + +// Receives and show new social timeline data +function readNewTimelineData () { + if (objXMLHttpReqSoc.readyState == 4) { // Check if data have been received + if (objXMLHttpReqSoc.status == 200) { + var endOfDelay = objXMLHttpReqSoc.responseText.indexOf('|',0); // Get separator position + var delay = parseInt(objXMLHttpReqSoc.responseText.substring(0,endOfDelay)); // Get refresh delay + var htmlJustNowTimeline = objXMLHttpReqSoc.responseText.substring(endOfDelay + 1); // Get HTML code for social timeline + + var justNowTimeline = document.getElementById('just_now_timeline_list');// Access to UL for the just received timeline + if (justNowTimeline) { + justNowTimeline.innerHTML = htmlJustNowTimeline; // Update list of publishings in just now timeline + var countJustNowTimeline = justNowTimeline.childNodes.length; + + if (countJustNowTimeline) { // New pubs just retrieved + // Scripts in timeline got via AJAX are not executed ==> execute them + evalScriptsInElem (justNowTimeline); + + // Process mathematics; see http://docs.mathjax.org/en/latest/advanced/typeset.html + MathJax.Hub.Queue(["Typeset",MathJax.Hub,justNowTimeline]); + + // Move just received timeline to top of new timeline + var newTimeline = document.getElementById('new_timeline_list'); // Access to UL with the new timeline + + // Move all the LI elements in UL 'just_now_timeline_list' to the top of UL 'new_timeline_list' + for (var i=0; i