var lastUpdateTime = 0; var updateInterval = 3000; var updateEnabled = true; var updateTimeoutHolder = undefined; var lastUpdateMaxId = undefined; var updateSizeHistory = []; var updateTimeHistory = []; var updateCountHistory = []; var updateHistoryMaxSize = 100; var calls = []; var seenCallIds = []; var seenCallIdsMaxSize = 1000; var displayUpdateTimeout = undefined; var displayUpdateCounter = 0; var buckets = {}; var autobucketEnabled = false; var autobucketMatchFirstOnly = false; var autoplay = false; var autoplayUnbucketed = false; var autoplayPreferLastPlayedBucket = true; var lastPlayedBucket = undefined; var lastPlayedCall = undefined; var recentlyPlayedCallsMaxSize = 100; var playbackLatencyHistory = []; const playbackLatencyHistoryMaxSize = 100; var playbackActive = false; var playerObj = document.getElementById('player'); var minActivityBarSeconds = 600; var bucketWidthFactor = 0.06; var showCallList = true; var showActivityBars = true; var hideEmptyBuckets = true; var minBucketIdleTimeSec = 30; const jurisdictionTags = ['jefferson', 'berkeley', 'washington_md', 'frederick_md', 'montgomery_md', 'loudoun_va', 'frederick_va', 'morgan', 'leesburg', 'martinsburg', 'state', 'state_md', 'wv', 'va', 'medcom']; const categoryTags = ['fire-ems', 'law', 'govt', 'pubwks', 'trash', 'bus', 'hospital', 'medical', 'court', 'jail', 'animal']; const tgTypeTags = ['dispatch', 'tac']; class Bucket { constructor(name, noBucketOnStart=undefined) { this.name = name; buckets[name] = this; this.removeClipsOncePlayed = false; this.paused = false; this.mustMatchAllTags = false; this.calls = []; this.unplayedCalls = 0; this.talkgroups = []; this.tgTags = []; this.excludeTags = []; this.settings = {}; this.activityBarsMinWindowSizeSeconds = 600; this.activityBarsShowTimeSlider = true; this.activityBarsShowAllClips = false; this.firstDraw = true; this.mutedTgs = []; this.lastAddTime = 0; this.sortEnable = true; this.div = document.createElement('div'); this.htmlElements = {'mainDiv': this.div}; this.initHtmlElements(); if (this.name == 'Unallocated' || this.name == 'RecentlyPlayed') { document.getElementById('standardBucketsDiv').appendChild(this.div); } else { document.getElementById('bucketHolderDiv').appendChild(this.div); } if(!noBucketOnStart) { bucketCalls(); } } initHtmlElements() { // make a border this.div.style.borderStyle = 'solid'; this.div.style.borderWidth = '1px'; this.div.style.borderColor = '#333333'; this.div.style.borderRadius = '5px'; //this.div.style.flexBasis = bucketWidthPct + '%'; this.div.style.margin = '10px'; this.div.id = 'bucketDiv-' + this.name; // options modal this.htmlElements['optionsModalContainer'] = document.createElement('div'); this.htmlElements['optionsModalContainer'].className = 'modal'; this.htmlElements['optionsModalContent'] = document.createElement('div'); this.htmlElements['optionsModalContent'].className = 'modal-content' this.htmlElements['optionsModalContainer'].appendChild(this.htmlElements['optionsModalContent']); document.body.appendChild(this.htmlElements['optionsModalContainer']); this.htmlElements['optionsModalContainer'].id = this.name + '-optionsPanelModalContainer'; this.htmlElements['optionsModalCloseButton'] = document.createElement('span'); this.htmlElements['optionsModalCloseButton'].className = 'close'; this.htmlElements['optionsModalCloseButton'].onclick = () => document.getElementById(this.name + '-optionsPanelModalContainer').style.display = 'none'; this.htmlElements['optionsModalCloseButton'].style.cursor = 'pointer'; this.htmlElements['optionsModalCloseButton'].innerHTML = '×'; this.htmlElements['optionsModalContent'].appendChild(this.htmlElements['optionsModalCloseButton']); // modal header this.htmlElements['optionsModalHeader'] = document.createElement('span'); this.htmlElements['optionsModalContent'].appendChild(this.htmlElements['optionsModalHeader']); this.htmlElements['optionsModalHeader'].style.fontWeight = 'bold'; this.htmlElements['optionsModalHeader'].style.fontSize = '1.2em'; this.htmlElements['optionsModalHeader'].innerHTML = this.name + ' Bucket Options

'; // pause toggle button this.htmlElements['pauseToggleSpan'] = generateToggleButton('pauseToggle-' + this.name, 'Pause'); this.htmlElements['optionsModalContent'].appendChild(this.htmlElements['pauseToggleSpan']); this.settings['pauseToggle'] = new smToggle('pauseToggle-' + this.name); //sm.add(this.settings['pauseToggle']); this.settings['pauseToggle'].idsToStyle = ['pauseToggle-' + this.name]; this.settings['pauseToggle'].onEnableStyles.push(['backgroundColor', '#992222']); this.settings['pauseToggle'].onDisableStyles.push(['backgroundColor', '#010101']); this.settings['pauseToggle'].onEnableFunc = () => buckets[this.name].pauseToggle(); this.settings['pauseToggle'].onDisableFunc = () => buckets[this.name].pauseToggle(); this.htmlElements['pauseToggleSpan'].onclick = () => buckets[this.name].settings['pauseToggle'].toggle(); // spacer this.htmlElements['genericHorizSpacer'] = document.createElement('span'); this.htmlElements['optionsModalContent'].appendChild(this.htmlElements['genericHorizSpacer']); this.htmlElements['genericHorizSpacer'].innerHTML = '  '; // remove once played toggle button this.htmlElements['removePlayedToggleSpan'] = generateToggleButton('removePlayedToggle-' + this.name, 'Autopurge'); this.htmlElements['optionsModalContent'].appendChild(this.htmlElements['removePlayedToggleSpan']); this.settings['removePlayedToggle'] = new smToggle('removePlayedToggle-' + this.name); //sm.add(this.settings['removePlayedToggle']); this.settings['removePlayedToggle'].idsToStyle = ['removePlayedToggle-' + this.name]; this.settings['removePlayedToggle'].onEnableStyles.push(['backgroundColor', '#229922']); this.settings['removePlayedToggle'].onDisableStyles.push(['backgroundColor', '#010101']); this.settings['removePlayedToggle'].onEnableFunc = () => buckets[this.name].removePlayedToggle(); this.settings['removePlayedToggle'].onDisableFunc = () => buckets[this.name].removePlayedToggle(); this.htmlElements['removePlayedToggleSpan'].onclick = () => buckets[this.name].settings['removePlayedToggle'].toggle(); this.htmlElements['optionsModalContent'].appendChild(this.htmlElements['genericHorizSpacer'].cloneNode(true)); // delete button this.htmlElements['deleteButtonSpan'] = generateToggleButton('deleteButton-' + this.name, 'Delete'); this.htmlElements['deleteButtonSpan'].style.borderColor = '#FF0000'; this.htmlElements['optionsModalContent'].appendChild(this.htmlElements['deleteButtonSpan']); this.htmlElements['deleteButtonSpan'].onclick = () => { document.getElementById(this.name + '-optionsPanelModalContainer').style.display = 'none'; deleteBucket(this.name);} this.htmlElements['optionsModalContent'].appendChild(this.htmlElements['genericHorizSpacer'].cloneNode(true)); // purge button this.htmlElements['purgeButtonSpan'] = generateToggleButton('purgeButton-' + this.name, 'Purge All Calls'); this.htmlElements['purgeButtonSpan'].style.borderColor = '#FF0000'; this.htmlElements['optionsModalContent'].appendChild(this.htmlElements['purgeButtonSpan']); this.htmlElements['purgeButtonSpan'].onclick = () => {this.purgeAllCalls(); updateAllBucketCallLists();} // spacer this.htmlElements['optionsModalContent'].appendChild(document.createElement('div')); // include tags var tmp = document.createElement('span'); this.htmlElements['optionsModalContent'].appendChild(tmp); tmp.style.fontWeight = 'bold'; tmp.style.fontSize = '1.0em'; tmp.innerHTML = '
Tags to include:
' + generateTagSelectionTable(this.name + '-includeTag'); tmp.innerHTML += '
Must match all selected tags

'; // exclude tags tmp = document.createElement('span'); this.htmlElements['optionsModalContent'].appendChild(tmp); tmp.style.fontWeight = 'bold'; tmp.style.fontSize = '1.0em'; tmp.innerHTML = '
Tags to exclude:
' + generateTagSelectionTable(this.name + '-excludeTag'); // tg list tmp = document.createElement('span'); this.htmlElements['optionsModalContent'].appendChild(tmp); tmp.style.fontWeight = 'bold'; tmp.style.fontSize = '1.0em'; tmp.innerHTML = '
Talkgroup IDs to include
Bypasses tag selections. Input as comma separated list, ex: 9998,13434,9002
'; tmp.innerHTML += '

'; // submit button tmp = document.createElement('span'); this.htmlElements['optionsModalContent'].appendChild(tmp); tmp.style.fontWeight = 'bold'; tmp.style.fontSize = '1.0em'; tmp.innerHTML = 'Update Bucket Config'; // end of options modal, back to the actual display // bucket name this.htmlElements['nameSpan'] = document.createElement('span'); this.div.appendChild(this.htmlElements['nameSpan']); this.htmlElements['nameSpan'].style.fontWeight = 'bold'; this.htmlElements['nameSpan'].style.marginLeft = '10px'; this.htmlElements['nameSpan'].title = 'Click to play the next available clip in this bucket'; this.htmlElements['nameSpan'].style.cursor = 'pointer'; this.htmlElements['nameSpan'].onclick = () => playFromBucket(this.name); this.htmlElements['nameSpan'].innerText = this.name; this.div.appendChild(this.htmlElements['genericHorizSpacer'].cloneNode(true)); // pause button this.htmlElements['pauseButtonSpan'] = document.createElement('span'); this.div.appendChild(this.htmlElements['pauseButtonSpan']); this.htmlElements['pauseButtonSpan'].onclick = () => this.pauseToggle(); this.htmlElements['pauseButtonSpan'].style.cursor = 'pointer'; this.htmlElements['pauseButtonSpan'].title = 'Bucket options'; this.htmlElements['pauseButtonSpan'].innerHTML = ' ⏸️ '; // options button this.htmlElements['optionsButtonSpan'] = document.createElement('span'); this.div.appendChild(this.htmlElements['optionsButtonSpan']); this.htmlElements['optionsButtonSpan'].onclick = () => this.updateModalFormAndShow(); this.htmlElements['optionsButtonSpan'].style.cursor = 'pointer'; this.htmlElements['optionsButtonSpan'].title = 'Bucket options'; this.htmlElements['optionsButtonSpan'].innerHTML = ' ⚙️ '; // purge button this.htmlElements['purgeButtonSpan2'] = document.createElement('span'); this.div.appendChild(this.htmlElements['purgeButtonSpan2']); this.htmlElements['purgeButtonSpan2'].onclick = () => {this.purgeAllCalls(); updateAllBucketCallLists();} this.htmlElements['purgeButtonSpan2'].style.cursor = 'pointer'; this.htmlElements['purgeButtonSpan2'].title = 'Purge bucket'; this.htmlElements['purgeButtonSpan2'].innerHTML = ' 🚽 '; this.div.appendChild(this.htmlElements['genericHorizSpacer'].cloneNode(true)); // call count this.htmlElements['callCountSpan'] = document.createElement('span'); this.div.appendChild(this.htmlElements['callCountSpan']); this.htmlElements['callCountSpan'].innerText = '' + this.calls.length + ' calls'; this.htmlElements['callCountSpan'].title = 'Call count, unplayed / total'; if (this.name != 'RecentlyPlayed' && this.name != 'Unallocated') { // spacer this.div.appendChild(document.createElement('div')); // tag list this.htmlElements['tagsSpan'] = document.createElement('span'); this.div.appendChild(this.htmlElements['tagsSpan']); this.htmlElements['tagsSpan'].style.marginLeft = '10px'; this.drawTagList(); this.div.appendChild(this.htmlElements['genericHorizSpacer'].cloneNode(true)); // exclude tag list this.htmlElements['excludeTagsSpan'] = document.createElement('span'); this.div.appendChild(this.htmlElements['excludeTagsSpan']); this.htmlElements['excludeTagsSpan'].style.marginLeft = '10px'; this.drawExcludeTagList(); this.div.appendChild(this.htmlElements['genericHorizSpacer'].cloneNode(true)); } // tg list this.htmlElements['talkgroupSpan'] = document.createElement('span'); this.div.appendChild(this.htmlElements['talkgroupSpan']); this.htmlElements['talkgroupSpan'].style.marginLeft = '10px'; this.drawTalkgroupList(); // tg activity bar holder this.htmlElements['tgActivityBarsDiv'] = document.createElement('div'); this.htmlElements['tgActivityBarsDiv'].style.borderTop = "1px solid #333333"; if (!this.name == 'Unallocated' && !this.name == 'RecentlyPlayed') { this.htmlElements['tgActivityBarsDiv'].style.maxHeight = '16em'; } this.htmlElements['tgActivityBarsDiv'].style.overflowY = 'auto'; this.htmlElements['tgActivityBarsDiv'].style.overflowX = 'hidden'; //this.htmlElements['tgActivityBarsDiv'].style.borderBottom = "1px solid #333333"; this.div.appendChild(this.htmlElements['tgActivityBarsDiv']); // call list this.htmlElements['callListDiv'] = document.createElement('div'); this.htmlElements['callListDiv'].style.overflowX = 'hidden'; this.htmlElements['callListDiv'].style.overflowY = 'auto'; this.htmlElements['callListDiv'].style.maxHeight = '200px'; this.div.appendChild(this.htmlElements['callListDiv']); this.drawCallList(); } updateModalFormAndShow() { for (t in tagEmojiMap) { document.getElementById(this.name + '-includeTag-' + t).checked = this.tgTags.includes(t); document.getElementById(this.name + '-excludeTag-' + t).checked = this.excludeTags.includes(t); } document.getElementById(this.name + '-MustMatchAll').checked = this.mustMatchAllTags; document.getElementById(this.name + '-Talkgroups').value = this.talkgroups.join(','); document.getElementById(this.name + '-optionsPanelModalContainer').style.display = 'block'; } updateBucketFromModalForm() { var includeTags = []; for (t in tagEmojiMap) { if (document.getElementById(this.name + '-includeTag-' + t).checked) { includeTags.push(t); } } const mustMatchAll = document.getElementById(this.name + '-MustMatchAll').checked; var excludeTags = []; for (t in tagEmojiMap) { if (document.getElementById(this.name + '-excludeTag-' + t).checked) { excludeTags.push(t); } } var talkgroups = []; const tgInputString = document.getElementById(this.name + '-Talkgroups').value; if (tgInputString.length > 0) { talkgroups = tgInputString.split(','); } this.tgTags = includeTags; this.excludeTags = excludeTags; this.talkgroups = talkgroups; this.mustMatchAllTags = mustMatchAll; this.drawTagList(); this.drawExcludeTagList(); this.drawTalkgroupList(); document.getElementById(this.name + '-optionsPanelModalContainer').style.display = 'none'; } exportConfig() { var conf = {}; conf['name'] = this.name; conf['removeClipsOncePlayed'] = this.removeClipsOncePlayed; conf['paused'] = this.paused; conf['mustMatchAllTags'] = this.mustMatchAllTags; conf['talkgroups'] = this.talkgroups; conf['tgTags'] = this.tgTags; conf['excludeTags'] = this.excludeTags; conf['mutedTgs'] = this.mutedTgs; conf['sortEnable'] = this.sortEnable; return conf; } importConfig(conf) { this.name = conf['name']; this.removeClipsOncePlayed = conf['removeClipsOncePlayed']; this.paused = conf['paused']; this.mustMatchAllTags = conf['mustMatchAllTags']; this.talkgroups = conf['talkgroups']; this.tgTags = conf['tgTags']; this.excludeTags = conf['excludeTags']; this.mutedTgs = conf['mutedTgs']; this.sortEnable = conf['sortEnable']; } addTag(tag) { if (!this.tgTags.includes(tag)) { this.tgTags.push(tag); this.drawTagList(); } } delTag(tag) { var idx = this.tgTags.indexOf(tag); if (idx != -1) { this.tgTags.splice(idx, 1); this.drawTagList(); } else { console.log('delTag: bucket ' + this.name + ' does not have tag ' + tag); } } drawTagList() { if (this.name != 'RecentlyPlayed' && this.name != 'Unallocated') { if (this.tgTags.length > 0) { this.htmlElements['tagsSpan'].innerHTML = 'Tags: ' + this.tgTags.map(getEmojiForTag).join(' '); } else { this.htmlElements['tagsSpan'].innerHTML = '-'; } } } addExcludeTag(tag) { if (!this.excludeTags.includes(tag)) { this.excludeTags.push(tag); this.drawExcludeTagList(); } } delExcludeTag(tag) { var idx = this.excludeTags.indexOf(tag); if (idx != -1) { this.excludeTags.splice(idx, 1); this.drawExcludeTagList(); } else { console.log('delExcludeTag: bucket ' + this.name + ' does not have exclude tag ' + tag); } } drawExcludeTagList() { if (this.name != 'RecentlyPlayed' && this.name != 'Unallocated') { if (this.excludeTags.length > 0) { this.htmlElements['excludeTagsSpan'].innerHTML = 'Ex Tags: ' + this.excludeTags.map(getEmojiForTag).join(' '); } else { this.htmlElements['excludeTagsSpan'].innerHTML = '-'; } } } addTalkgroup(tgId) { if (!this.talkgroups.includes(tgId)) { this.talkgroups.push(tgId); this.drawTalkgroupList(); } } delTalkgroup(tgId) { var idx = this.talkgroups.indexOf(tgId); if (idx != -1) { this.talkgroups.splice(idx, 1); this.drawTalkgroupList(); } else { console.log('delTakgroup: bucket ' + this.name + ' does not have tgId ' + tgId); } } drawTalkgroupList() { if (this.talkgroups.length > 0) { var tgStr = 'TGs: ' + this.talkgroups.join(' '); if (tgStr.length > 10) {tgStr = tgStr.slice(0, 10);} this.htmlElements['talkgroupSpan'].innerText = tgStr; } else { this.htmlElements['talkgroupSpan'].innerText = ''; } } addCall(call) { var uuidFoundAlready = false; for (var i=0; i< this.calls.length; i++) { if (call['uuid'] === this.calls[i]['uuid']) { console.log('Duplicate uuid found when adding call to bucket ' + this.name + ': ' + call['uuid']); uuidFoundAlready = true; } } if (!uuidFoundAlready) { this.lastAddTime = Date.now(); this.calls.push(call); this.sortCalls(); this.drawCallList(); } } delCall(callId) { var foundIdx = 0; var found = false; for (var i=0; i b['start_time']) { return 1; } return 0; }); } } offerCall(call) { var willAccept = false; if (this.talkgroups.includes(call['tg_id'])) { willAccept = true; } if (this.mustMatchAllTags) { var tagsMatched = 0; for (var i=0; i 0) { var tgList = []; var callsByTg = {}; var earliestStart = 16936832196380; var latestEnd = 0; for (var i=0; i latestEnd) { latestEnd = this.calls[i]['end_time']; } } var trueEarliestStart = earliestStart; if (earliestStart > (Date.now() / 1000) - this.activityBarsMinWindowSizeSeconds) { earliestStart = (Date.now() / 1000) - this.activityBarsMinWindowSizeSeconds; } earliestStart = (Date.now() / 1000) - this.activityBarsMinWindowSizeSeconds; latestEnd = (Date.now() / 1000); var maxOption = Date.now() / 1000 - trueEarliestStart + 15; if (maxOption < minActivityBarSeconds) { maxOption = minActivityBarSeconds; } if (this.activityBarsShowAllClips && trueEarliestStart < earliestStart) { earliestStart = trueEarliestStart; } tgList.sort(); var html = '' //html += '' //html += '' //html += '' // this is to make sure we always have a set width for the talkgroup name html += ''; let localBucketWidthFactor = bucketWidthFactor; if (this.name === 'RecentlyPlayed' || this.name === 'Unallocated') { localBucketWidthFactor = 0.09; } for (var i=0; i●'; html += ''; html += ''; } //html += ''; html += '' if (this.activityBarsShowTimeSlider) { html += ''; } html += '
Activity by Talkgroup
               
                                  
' + nbspByCount(34) + '
' + callsByTg[tgList[i]][0]['tg_name'] + '' html += '
' + ptime(earliestStart) + '' + niceDurationFractional(latestEnd - earliestStart) + '' + ptime(latestEnd) + '
 ' + niceDurationFractional(latestEnd - earliestStart) + ' 
0s'; html += ''; html += ' ' + maxOption.toFixed(0) + 's
' this.htmlElements['tgActivityBarsDiv'].innerHTML = html; var markPlayedOption = true; if (this.name == 'RecentlyPlayed') { markPlayedOption = false;} for (var i=0; i'; } else { listHtml += ''; } listHtml += '' + this.calls[i]['nice_start'] + ''; listHtml += '' + this.calls[i]['precise_dur'].toFixed(1) + ''; listHtml += '' + generateColorTalkerSpan(this.calls[i]['talker_id'], this.calls[i]['system']) + ''; listHtml += '' + this.calls[i]['tg_name'] + ''; listHtml += ''; for (var j=0; j 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); } drawUpdateStatsGraph(); drawLastUpdateSpanText(updateFinishTime - updateStartTime, responseText.length / 1024, newCalls.length); if (updateEnabled) { updateTimeoutHolder = setTimeout(dataUpdate, updateInterval); } } function mergeNewData (newData) { for (var i=0; i seenCallIdsMaxSize) { seenCallIds = seenCallIds.slice(-1 * seenCallIdsMaxSize); } } } } function bucketCalls () { //var toDel = []; var idsToDel = []; for (var i=0; i' + bp + '' } html += '

'; html += 'Bucket name:

'; html += 'Start Paused '; html += 'Enable Autopurge '; html += '

' html += 'Tags to include:
'; html += generateTagSelectionTable('bucketCreateIncludeTag'); html += '
Must match all selected tags

'; html += 'Tags to exclude:
'; html += generateTagSelectionTable('bucketCreateExcludeTag'); html += '
Talkgroup IDs to include
Bypasses tag selections. Input as comma separated list, ex: 9998,13434,9002
'; html += '

'; html += 'Create Bucket'; modalDiv.innerHTML = html; } function createBucketFromModalForm() { const bucketName = document.getElementById('bucketCreateName').value; if (bucketName in buckets) { alert('Error: a bucket named "' + bucketName + '" already exists. Choose another name'); return; } if (bucketName.length == 0) { alert('Error: bucket name cannot be empty'); return; } const paused = document.getElementById('bucketCreatePaused').checked; const removePlayed = document.getElementById('bucketCreateRemovePlayed').checked; var includeTags = []; for (t in tagEmojiMap) { if (document.getElementById('bucketCreateIncludeTag-' + t).checked) { includeTags.push(t); } } const mustMatchAll = document.getElementById('bucketCreateMustMatchAll').checked; var excludeTags = []; for (t in tagEmojiMap) { if (document.getElementById('bucketCreateExcludeTag-' + t).checked) { excludeTags.push(t); } } var talkgroups = []; const tgInputString = document.getElementById('bucketCreateTalkgroups').value; if (tgInputString.length > 0) { talkgroups = tgInputString.split(','); } var b = new Bucket(bucketName); if (paused) { b.settings['pauseToggle'].enable(); } if (removePlayed) { b.settings['removePlayedToggle'].enable(); } if (mustMatchAll) { b.mustMatchAllTags = true; } b.tgTags = includeTags; b.excludeTags = excludeTags; b.talkgroups = talkgroups; b.drawTagList(); b.drawExcludeTagList(); b.drawTalkgroupList(); document.getElementById('bucketCreateModalContainer').style.display='none'; } function deleteBucket(bucketName) { console.log('deleteBucket called for ' + bucketName); if (bucketName in buckets) { delete buckets[bucketName]; document.getElementById('bucketDiv-' + bucketName).remove(); console.log('Deleted ' + bucketName) ; } } function deleteAllBuckets () { var bucketNames = Object.keys(buckets); for (var i=0; i recentlyPlayedCallsMaxSize) { recentlyPlayedBucket.delCall(recentlyPlayedBucket.calls[0]['uuid']); } playbackLatencyHistory.push((Date.now() - call['start_time'] * 1000) / 1000); drawLatencyGraph(); if (playbackLatencyHistory.length > playbackLatencyHistoryMaxSize) { playbackLatencyHistory = playbackLatencyHistory.slice(-1 * playbackLatencyHistoryMaxSize); } updateAllBucketCallLists(); } } function playerPlayPause () { const player = document.getElementById('player'); if (player.paused) { player.play(); } else { player.pause(); } } function findCallById(callList, callId) { for (var i=0; i minBucketIdleTimeSec) { call = buckets[n].findNextPlayable(); if (call) { bucketName = buckets[n].name; foundOne = true; console.log('Found playable clip in bucket ' + bucketName + ', bucket idle time: ' + ((Date.now() - buckets[n].lastAddTime) / 1000)); break; } } } // try again w/o minBucketIdleTime so we don't just stand around like a dumbass not doing anything if (!foundOne) { for (const n in buckets) { if (!buckets[n].paused) { call = buckets[n].findNextPlayable(); if (call) { bucketName = buckets[n].name; foundOne = true; console.log('Found playable clip in bucket ' + bucketName + ', minBucketIdleTime bypassed'); break; } } } } } // play unbucketed if nothing else matched if (!foundOne && autoplayUnbucketed) { for (var i=0; iCreate New Bucket' html += '

Existing Buckets
'; html += '' html += ''; for (b in buckets) { console.log(b); html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; } html += ''; html += ''; html += ''; html += ''; html += ''; html += '
Bucket NameCallsActions
' + buckets[b].name + '' + buckets[b].calls.length + ' / ' + buckets[b].unplayedCalls + 'PurgeDelete
 
Global ActionsPurge AllDelete All
' document.getElementById('manageBucketsModal').innerHTML = html; document.getElementById('manageBucketsModalContainer').style.display = 'block' } function purgeAllBuckets () { for (b in buckets) { buckets[b].purgeAllCalls(); } updateAllBucketCallLists(); } function saveBuckets() { var configs = []; for (b in buckets) { configs.push(buckets[b].exportConfig()); } bucketConfig.set(configs); } function loadBuckets() { var configs = bucketConfig.currentState; if (configs){ for (var i=0; i { updateAllBucketCallLists(); });