/** Next time we need to query the calendar */ var nextCalendarUpdate = 0; /** Next time we need to redraw the list because the active item changed */ var nextRender = 0; var calendars = {}; var locations = {}; var DATENOW, NOW; var globalTimer; $(document).ready(function () { if (!globalConfig || globalConfig.constructor !== Object) { fatalError("Embedded panel config is not valid JSON"); return; } initializePanel(); }); function initializePanel() { if (!globalConfig.locations || globalConfig.locations.constructor !== Array) { fatalError("Requested panel doesn't contain locations / not array"); return; } var locs = 0; globalConfig.locations.forEach(function (x) { // filter out if no numeric id, or id already present if (typeof(x.id) !== 'number' || x.id <= 0 || locations[x.id]) return; locs++; locations[x.id] = x; }); if (locs === 0) { fatalError("List of location ids is empty"); return; } if (globalConfig.time) { SetUpDate(globalConfig.time); } delete globalConfig.time; delete globalConfig.locations; sanitizeAndOverrideConfig(globalConfig); if (!globalTimer) { mainUpdateLoop(); if (globalConfig.qrcode) { makeQrCode(); } } } /** * gets Additional Parameters from the URL, and from the * downloaded json. * also makes sure parameters are in a given range */ function sanitizeAndOverrideConfig(config) { sanitizeConfigParam(config, 'calupdate', PARAM_INT, 30, 1, 60, 60 * 1000); sanitizeConfigParam(config, 'qrcode', PARAM_BOOL, false); sanitizeConfigParam(config, 'language', PARAM_STRING, 'en'); } function updateNow() { DATENOW = MyDate(); NOW = DATENOW.getTime(); } /** * queries the Calendar data */ function queryCalendars() { console.log('Querying current calendar data'); if (!PANEL_UUID) return; var url = API + "get=calendar&uuid=" + PANEL_UUID; $.ajax({ url: url, dataType: 'json', cache: false, timeout: 30000, success: queryCalendarsCallback, error: function () { // Retry in 5 minutes (300 seconds) updateNow(); nextCalendarUpdate = NOW + 300000; } }); } var color; function queryCalendarsCallback(result) { if (result && result.constructor === Array) { var l = result.length; var timeTable = {}; color = 'rgb(' + Math.round(Math.random() * 128 + 127) + ', ' + Math.round(Math.random() * 128 + 127) + ', ' + Math.round(Math.random() * 128 + 127) + ')'; updateNow(); for (var i = 0; i < l; i++) { timeTable[result[i].id] = processSingleCalendar(result[i].calendar); } calendars = mergeCalendars(timeTable); renderUnifiedEvents(); } } const SEVEN_DAYS = 7 * 86400 * 1000; /** * Calendar data will be filtered and sorted. * Expired events will be removed, remaining events sorted by start time, up to 7 days in advance. * * @param {Array} json - The new event data in JSON format to update the calendar. */ function processSingleCalendar(json) { if (!json || json.constructor !== Array) { console.log("Error: Calendar data was empty or malformed."); return []; } if (json.length === 0) { console.log("Notice: Calendar already empty from server"); } json = json.filter(function (el) { if (!el.title || !el.start || !el.end) return false; el.s = new Date(el.start).getTime(); el.e = new Date(el.end).getTime(); return !(isNaN(el.s) || isNaN(el.e) || Math.abs(el.s - NOW) > SEVEN_DAYS || Math.abs(el.e) < NOW); }); if (json.length === 0) { console.log('Notice: Calendar has no current events'); } json.sort(function (a, b) { return a.s - b.s; }); for (var i = json.length - 1; i > 0; i--) { // if title, start and end are the same, "merge" two events by removing one of them if (json[i].title === json[i-1].title && json[i].start === json[i-1].start && json[i].end === json[i-1].end) { json.splice(i, 1); } } return json; } /** * Takes one or more calendars and merges the individual eventsfrom them, sorted ascending * by start time. The inputcalendars are assumed to be sorted in ascending order already. * If multiple calendars contain an event with matching start/end times and title, it is * assumed to be the same event spread across multiple locations, so the event will be * merged, and the location names concatenated. * @param {Array} calendars - An array of calendars to be merged. * @returns {Array} - Returns a new array containing the merged calendars. */ function mergeCalendars(calendars) { var k, tt, merged = []; var nextRefresh = null; do { var smallest = false; for (k in calendars) { if (!calendars.hasOwnProperty(k)) continue; tt = calendars[k]; if (!tt || tt.length === 0 || tt[0].e < NOW) continue; if (smallest === false || tt[0].s < calendars[smallest][0].s) { smallest = k; } } if (smallest) { var v = calendars[smallest].shift(); v.location = locations[smallest] ? locations[smallest].name : '???'; if (merged.length !== 0) { var cur = merged.pop(); if (cur.s === v.s && cur.e === v.e && cur.title === v.title) { v.location += ', ' + cur.location; } else { merged.push(cur); } } merged.push(v); } } while (smallest); return merged; } function pad(number, digits) { var s = "00" + number; return s.substr(s.length - digits); } function formatTime(dateObj) { return pad(dateObj.getHours(), 2) + ":" + pad(dateObj.getMinutes(), 2); } function makeQrCode() { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = 'modules/locationinfo/frontend/qrcode.min.js'; console.log("MakeQR"); var callback = function() { console.log("Script cb"); var $code = $('
'); $('body').append($code); setTimeout(function() { new QRCode($code[0], { text: window.location.href.replace(/([#&])qrcode=\d+/, '$1').replace(/[&#]+$/, ''), width: $code.width() * 4, height: $code.height() * 4, correctLevel : QRCode.CorrectLevel.L }); }, 1); } script.onload = function () { this.onload = null; this.onreadystatechange = null; callback(); }; script.onreadystatechange = function () { if (this.readyState === 'loaded' || this.readyState === 'complete') { this.onload = null; this.onreadystatechange = null; callback(); } }; document.getElementsByTagName('head')[0].appendChild(script); } // /** * Main Update loop, this loop runs every 10 seconds */ function mainUpdateLoop() { updateNow(); if (nextCalendarUpdate < NOW) { nextCalendarUpdate = NOW + globalConfig.calupdate; queryCalendars(); } else if (nextRender < NOW) { renderUnifiedEvents(); } else { queryPanelChange(API, PANEL_UUID, globalConfig); } var nx = Math.min(nextCalendarUpdate, nextRender) - NOW; nx = Math.max(1000, nx); nx = Math.min(15000, nx); $('#time .date-today').text(t('long' + DATENOW.getDay()) + ', ' + DATENOW.toLocaleDateString(LANGUAGE)); $('#time .time-today').text(formatTime(DATENOW)); globalTimer = setTimeout(mainUpdateLoop, nx); }