// 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