let lastUpdateTime = 0; let updateInterval = 3000; let updateEnabled = true; let updateIntervalHolder = undefined; let lastUpdateMaxId = undefined; let updateSizeHistory = []; let updateTimeHistory = []; let updateCountHistory = []; let updateHistoryMaxSize = 100; let calls = []; let seenCallIds = []; let seenCallIdsMaxSize = 100000; let bf = new BloomFilter.withTargetError(1000000, 1e-6); let displayUpdateTimeout = undefined; let displayUpdateCounter = 0; let groupByOption = 'system'; var tgRegex = ''; let fullRangeStart = undefined; let fullRangeEnd = undefined; let visibleRangeStart = undefined; let visibleRangeEnd = undefined; const activityBarCanvasHeight = 18; let playNextInTg = true; let playNextPressed = false; let currentPlayingTg = undefined; let currentPlayingGroup = undefined; let currentPlayingUuid = undefined; let autogrowViewport = true; let autoshiftViewport = false; let autoscrollToPlaying = true; let totalRepeatCallIds = 0; let ctrlPressedDuringLastClick = false; let tgFieldWidth = 325; let initFinished = false; let historicalUpdateQueue = []; const historicalUpdateBasis = 1602892800; const historicalUpdateInterval = 3600; let historicalUpdateTimeoutHolder = undefined; const historicalUpdateTimeoutWait = 10; let limitUpdateCount = false; let updateCallLimit = 10; let updateCalls = 0; let wideMode = false; let queriedRanges = []; // should be populated with a bunch of [start, end] pairs function historicalUpdate(earliestTime, latestTime) { console.log('historicalUpdate called with earliestTime ' + earliestTime + ' latestTime '+ latestTime); queriedRanges.push([earliestTime * 1000, latestTime * 1000]); // add segments to update queue let segmentCount = parseInt((earliestTime - historicalUpdateBasis) / historicalUpdateInterval); let histStart = historicalUpdateBasis + (segmentCount * historicalUpdateInterval); let highWaterMark = earliestTime; let segmentsSoFar = 0; while (highWaterMark < latestTime) { let segmentStart = histStart + (segmentsSoFar * historicalUpdateInterval); let segmentEnd = segmentStart + historicalUpdateInterval; historicalUpdateQueue.push({ 'start': segmentStart, 'end': segmentEnd }); //console.log('Added to historicalUpdateQueue: ' + historicalUpdateQueue[historicalUpdateQueue.length - 1]['start'] + ' ' + historicalUpdateQueue[historicalUpdateQueue.length - 1]['end']); highWaterMark = segmentEnd; segmentsSoFar ++; } historicalUpdateTimeoutHolder = setTimeout(getHistoricalFile, historicalUpdateTimeoutWait); } async function getHistoricalFile() { if (historicalUpdateQueue.length > 0) { histQueueSpan.innerText = 'Hist queue: ' + historicalUpdateQueue.length; updatingNotifSpan.innerText = 'Updating!'; let url = '/call_history/'; const timeData = historicalUpdateQueue.shift(); url += timeData['start'].toString().slice(0, 4) + '/'; // first 4 chars for the dir url += timeData['start'] + '-' + timeData['end'] + '.json'; var response; var updateStartTime; try { updateStartTime = Date.now(); response = await fetch(url); } catch (error) { console.log('getHistoricalFile encountered a problem: ' + error); } const responseText = await response.text(); const newCalls = JSON.parse(responseText); if (historicalUpdateQueue.legnth < 120 || historicalUpdateQueue.length % 6 == 0){ mergeNewData(newCalls, true); } else { mergeNewData(newCalls, false); } const updateFinishTime = Date.now(); updatingNotifSpan.innerText = ''; console.log('Historical file ' + url + ' retrieved ' + newCalls.length + ' calls in ' + (updateFinishTime - updateStartTime) + ' ms, response length ' + responseText.length + ', queue remaining ' + historicalUpdateQueue.length); if (historicalUpdateQueue.length > 0) { historicalUpdateTimeoutHolder = setTimeout(getHistoricalFile, historicalUpdateTimeoutWait); } else { histQueueSpan.innerText = ''; } } } function clearData() { calls = []; queriedRanges = []; totalRepeatCallIds = 0; lastUpdateTime = 0; updateCalls = 0; bf = new BloomFilter.withTargetError(1000000, 1e-6); abc.update(); toast('Data state cleared'); } async function dataUpdate (earliestTime, latestTime, nestedCall=false, calledFromAutoUpdate=false) { if (earliestTime && latestTime) { //console.log('DURATION INVESTIGATION: earliest ' + earliestTime + ' latest ' + latestTime + ' diff ' + niceDuration((latestTime - earliestTime)/1000)); } if (limitUpdateCount && updateCalls >= updateCallLimit) { console.log('Not updating, too many calls'); return; } updateCalls ++; console.log('dataUpdate called with earliestTime ' + pdate(earliestTime) + ' latestTime ' + pdate(latestTime) + ' diff ' + niceDuration(latestTime / 1000 - earliestTime / 1000) + ' nested ' + nestedCall); if (earliestTime && latestTime) { //console.log('queried range report'); //for (const fart of queriedRanges) { // console.log(fart.toString()); //} //console.log('earliestTime: ' + earliestTime + ' latestTime: ' + latestTime); queriedRanges = consolidateIntervals(queriedRanges); //console.log('Queried ranges post consolidation:'); //intervalReport(queriedRanges); let intervalsToGet = intervalDeconflict(earliestTime, latestTime, queriedRanges); //console.log('Intervals to get after deconfliction:'); //intervalReport(intervalsToGet); if (intervalsToGet.length == 0) { console.log('We already have this data, skipping query for start ' + earliestTime + ' end ' + latestTime); return; // we already have the requested data } else if (intervalsToGet > 1) { // we have multiple to do, so split em up for (const i of intervalsToGet) { console.log('Splitting data update, new span start '+ i[0] + ' end ' + i[1]); dataUpdate(i[0], i[1], true); } return; } else { earliestTime = intervalsToGet[0][0]; latestTime = intervalsToGet[0][1]; } } if (earliestTime < Date.now() - 86400 * 1000) { // older than 24h goes to historicalUpdate console.log('dataUpdate: We need some historical data'); let histUpdateStart = parseInt(earliestTime / 1000); let histUpdateEnd = parseInt(latestTime / 1000); if (latestTime > Date.now() - 86400 * 1000) { // check if end time is within 24h histUpdateEnd = parseInt(Date.now() / 1000 - 86400); console.log('Recent query alongside historical: ' + pdate(Date.now() - 86400 * 1000 + 5000) + ' - ' + pdate(latestTime)); dataUpdate(Date.now() - 86400 * 1000 + 5000, latestTime, true); } historicalUpdate(histUpdateStart, histUpdateEnd); return; } updatingNotifSpan.innerText = 'Updating!'; var url = '/calls'; if (calledFromAutoUpdate && lastUpdateMaxId) { url += '?min_id=' + lastUpdateMaxId; } else { url += '?min_id=' + earliestTime + '-0' + '&max_id=' + latestTime + '-0'; } var response; var updateStartTime; try { updateStartTime = Date.now(); response = await fetch(url); } catch (error) { console.log('dataUpdate encountered a problem: ' + error); } queriedRanges.push([earliestTime, latestTime]); const responseText = await response.text(); const newCalls = JSON.parse(responseText); mergeNewData(newCalls, false); const updateFinishTime = Date.now(); updatingNotifSpan.innerText = ''; console.log('Update retrieved ' + newCalls.length + ' calls in ' + (updateFinishTime - updateStartTime) + ' ms, response length ' + responseText.length); lastUpdateTime = Date.now(); if (newCalls.length > 0) { lastUpdateMaxId = '' + (parseInt(newCalls[newCalls.length - 1]['redis_id'].split('-')[0]) + 1) + '-0'; } updateSizeHistory.push(responseText.length); updateTimeHistory.push(updateFinishTime - updateStartTime); updateCountHistory.push(newCalls.length); if (updateSizeHistory.length > updateHistoryMaxSize) { updateSizeHistory = updateSizeHistory.slice(-1 * updateHistoryMaxSize); updateTimeHistory = updateTimeHistory.slice(-1 * updateHistoryMaxSize); updateCountHistory = updateCountHistory.slice(-1 * updateHistoryMaxSize); } abc.update(); } function mergeNewData (newData, redraw=true) { for (var i=0; i 
  ' + message + '  
 ') } function displayUpdate() { updateCurrentTime(); updateCallCount(); updateDupeCount(); pbc.displayUpdate(); displayUpdateTimeout = requestAnimationFrame(displayUpdate); } function updateCurrentTime() { currentTimeSpan.innerHTML = ptime(Date.now()) + ' '; } function updateCallCount () { callCountSpan.innerHTML = calls.length; } function updateDupeCount () { dupeCountSpan.innerHTML = '' + totalRepeatCallIds + ' dupes'; } function updateTimeWindowPickerText () { timeWindowFullRangeStartSpan.innerText = pdate_short(twp.rangeStart); timeWindowFullRangeEndSpan.innerText = pdate_short(twp.rangeEnd); timeWindowFullRangeSizeSpan.innerText = 'Full Range - ' + niceDuration(twp.rangeEnd - twp.rangeStart, true, 2); timeWindowVisibleRangeStartSpan.innerText = pdate_short(twp.leftSliderPos); timeWindowVisibleRangeEndSpan.innerText = pdate_short(twp.rightSliderPos); timeWindowVisibleRangeSizeSpan.innerText = 'Visible Range - ' + niceDuration(twp.rightSliderPos - twp.leftSliderPos, true, 2); } function resize () { if (initFinished) { if (window.innerWidth / window.innerHeight < 1.35) { dataDiv.style.gridTemplateColumns = 'auto'; dataDiv.style.gridTemplateRows = 'auto auto auto'; } else { dataDiv.style.gridTemplateColumns = 'auto auto'; dataDiv.style.gridTemplateRows = 'auto auto'; } tgFieldWidth = parseInt(sizeFinder.getBoundingClientRect()['width']); abc.update(); if (window.innerWidth / window.innerHeight > 1.35) { timeWindowCanvas.width = window.innerWidth / 2 - 100; } else { timeWindowCanvas.width = window.innerWidth - 100; } twp.draw(); } } function wideModeToggle () { if (!wideMode) { dataDiv.style.gridTemplateColumns = 'auto'; dataDiv.style.gridTemplateRows = 'auto auto'; wideMode = true; abc.update(); } else { dataDiv.style.gridTemplateColumns = 'auto auto'; dataDiv.style.gridTemplateRows = 'auto'; wideMode = false; abc.update(); } } function changeRange(fullStart, fullEnd, visibleStart, visibleEnd, calledFromTwp=false, calledFromInit=false, calledFromAutoUpdate=false) { //console.log('changeRange called!'); //console.log('calledFromTwp: ' + calledFromTwp); // millis timestamps expected for all params if (fullStart === undefined) { if (fullRangeStart === undefined) { fullStart = relativeTimeOffset('-30m', true); } else { fullStart = fullRangeStart; } } if (fullEnd === undefined) { if (fullRangeEnd === undefined) { fullEnd = relativeTimeOffset('-0s', true); } else { fullEnd = fullRangeEnd; } } if (visibleStart === undefined) { if (visibleRangeStart === undefined) { visibleStart = relativeTimeOffset('-30m', true); } else { visibleStart = visibleRangeStart; } } if (visibleEnd === undefined) { if (visibleRangeEnd === undefined) { visibleEnd = relativeTimeOffset('-0s', true); } else { visibleEnd = visibleRangeEnd; } } if (visibleStart < fullStart) { visibleStart = fullStart; } if (visibleEnd > fullEnd) { visibleEnd = fullEnd; } if (fullEnd - fullStart > 86400 * 30 * 1000) { fullEnd = fullStart + 86400 * 30 * 1000; } // 30 day max let needMoreData = false; if (calledFromInit || fullStart < fullRangeStart || fullEnd > fullRangeEnd) { needMoreData = true; } fullRangeStart = fullStart; fullRangeEnd = fullEnd; visibleRangeStart = visibleStart; visibleRangeEnd = visibleEnd; //console.log('changeRange final full: ' + pdate(fullRangeStart) + ' - ' + pdate(fullRangeEnd) + ', ' + niceDuration(parseInt(fullRangeEnd - fullRangeStart) / 1000)); //console.log('changeRange visible full: ' + pdate(visibleRangeStart) + ' - ' + pdate(visibleRangeEnd) + ', ' + niceDuration(parseInt(visibleRangeEnd - visibleRangeStart) / 1000)); twp.adjustFullRange(parseInt(fullRangeStart / 1000), parseInt(fullRangeEnd / 1000)); twp.adjustVisibleRange(parseInt(visibleRangeStart / 1000), parseInt(visibleRangeEnd / 1000), true); // 3rd param dis/enables callbacks from twp if (needMoreData) { dataUpdate(fullRangeStart, fullRangeEnd, false, calledFromAutoUpdate); } else { abc.update(); } } function twpChangeRangeWrapper (visibleStart, visibleEnd) { //console.log('twpChangeRangeWrapper calling changeRange'); changeRange(fullRangeStart, fullRangeEnd, visibleStart * 1000, visibleEnd * 1000, true); } function autoUpdater () { if (updateEnabled) { let newVisibleStart = visibleRangeStart; let newVisibleEnd = visibleRangeEnd; let now = Date.now(); if (autogrowViewport) { newVisibleEnd = now; } if (autoshiftViewport) { let diff = now - newVisibleEnd; newVisibleStart += diff; newVisibleEnd = now; } //console.log('autoUpdater calling changeRange'); changeRange(fullRangeStart, now, newVisibleStart, newVisibleEnd, false, false, true); } else { clearInterval(updateIntervalHolder); } } function init() { let sizeFinder = document.createElement('span'); sizeFinder.style.fontSize = '1.0em'; sizeFinder.id = 'sizeFinder'; sizeFinder.innerHTML = '                                  '; document.body.appendChild(sizeFinder); tgFieldWidth = parseInt(sizeFinder.getBoundingClientRect()['width']); if (autogrowViewport) { autoGrowEnabledRadio.checked = true; } else if (autoshiftViewport) { autoShiftEnabledRadio.checked = true; } else { doNothingRadio.checked = true; } if (updateEnabled) {autoupdateToggle.checked = true; } if (playNextInTg) {autoplayToggle.checked = true;} if (autoscrollToPlaying) {autoscrollToggle.checked = true;} displayUpdateTimeout = requestAnimationFrame(displayUpdate); fullRangeStart = relativeTimeOffset('-30m', true); fullRangeEnd = relativeTimeOffset('-0s', true); visibleRangeStart = fullRangeStart; visibleRangeEnd = fullRangeEnd; if (window.innerWidth / window.innerHeight > 1.35) { timeWindowCanvas.width = window.innerWidth / 2 - 100; } else { timeWindowCanvas.width = window.innerWidth - 100; } twp = new TimeWindowPicker(timeWindowCanvas, fullRangeStart, fullRangeEnd); twp.onSetCallback = twpChangeRangeWrapper; twp.onMoveCallback = twpChangeRangeWrapper; twp.updateTextCallback = updateTimeWindowPickerText; twp.draw(); //console.log('init calling changeRange'); changeRange(fullRangeStart, fullRangeEnd, visibleRangeStart, visibleRangeEnd, false, true); updateIntervalHolder = setInterval(autoUpdater, updateInterval); document.addEventListener("pointerdown", (event) => { if (event.ctrlKey) { ctrlPressedDuringLastClick = true } else {ctrlPressedDuringLastClick = false } }) if (window.innerHeight > 1000) { activityBarsDiv.style.height = window.innerHeight * 0.75; callListDiv.style.height = window.innerHeight * 0.75; } else { activityBarsDiv.style.height = window.innerHeight * 0.65; callListDiv.style.height = window.innerHeight * 0.65; } abc.update(); window.onresize = resize; window.onload = resize; initFinished = true; } loadSettings(); let ssc = new SystemSelectController(); let tsc = new TagSelectController(); let gsc = new GroupingSelectController(); let trc = new TGRegexController(); let svc = new SetViewportController(); let clc = new CallListController(); let abc = new ActivityBarController(); let pbc = new PlaybackController(); let twp = undefined; let toasty = new Toasty(toastyDiv, 15, 25, 3); let plb = new PlaylistBuilderController(); init();