var table = $j('#eventStatsTable'); var backBtn = $j('#backBtn'); var renameBtn = $j('#renameBtn'); var archiveBtn = $j('#archiveBtn'); var unarchiveBtn = $j('#unarchiveBtn'); var editBtn = $j('#editBtn'); var exportBtn = $j('#exportBtn'); var downloadBtn = $j('#downloadBtn'); var statsBtn = $j('#statsBtn'); var deleteBtn = $j('#deleteBtn'); var prevEventId = 0; var nextEventId = 0; var prevEventStartTime = 0; var nextEventStartTime = 0; var PrevEventDefVideoPath = ""; var NextEventDefVideoPath = ""; var currEventId = null; var CurEventDefVideoPath = null; var vid = null; var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame. var intervalRewind; var revSpeed = .5; var cueFrames = null; //make cueFrames available even if we don't send another ajax query var streamCmdTimer = null; var streamStatus = null; var lastEventId = 0; var zmsBroke = false; //Use alternate navigation if zms has crashed var auth_hash; var wasHidden = false; function streamReq(data) { if ( auth_hash ) data.auth = auth_hash; data.connkey = connKey; data.view = 'request'; data.request = 'stream'; $j.getJSON(monitorUrl, data) .done(getCmdResponse) .fail(logAjaxFail); } // Function called when video.js hits the end of the video function vjsReplay() { switch ( replayMode.value ) { case 'none': break; case 'single': vid.play(); break; case 'all': if ( nextEventId == 0 ) { var overLaid = $j("#videoobj"); overLaid.append('
No more events
'); } else { var endTime = (Date.parse(eventData.EndDateTime)).getTime(); var nextStartTime = nextEventStartTime.getTime(); //nextEventStartTime.getTime() is a mootools workaround, highjacks Date.parse if ( nextStartTime <= endTime ) { streamNext(true); return; } var overLaid = $j("#videoobj"); vid.pause(); overLaid.append(''); var gapDuration = (new Date().getTime()) + (nextStartTime - endTime); var messageP = $j('.vjsMessage'); var x = setInterval(function() { var now = new Date().getTime(); var remainder = new Date(Math.round(gapDuration - now)).toISOString().substr(11, 8); messageP.html(remainder + ' to next event.'); if ( remainder < 0 ) { clearInterval(x); streamNext(true); } }, 1000); } break; case 'gapless': streamNext(true); break; } } // end function vjsReplay function initialAlarmCues(eventId) { //get frames data for alarmCues and inserts into html $j.getJSON(thisUrl + '?view=request&request=status&entity=frames&id=' + eventId) .done(setAlarmCues) .fail(logAjaxFail); } function setAlarmCues(data) { cueFrames = data.frames; alarmSpans = renderAlarmCues(vid ? $j("#videoobj") : $j("#evtStream"));//use videojs width or zms width $j(".alarmCue").html(alarmSpans); } function renderAlarmCues(containerEl) { if ( !( cueFrames && cueFrames.length ) ) { console.log('No cue frames for event'); return; } // This uses the Delta of the last frame to get the length of the event. I can't help but wonder though // if we shouldn't just use the event length endtime-starttime var cueRatio = containerEl.width() / (cueFrames[cueFrames.length - 1].Delta * 100); var minAlarm = Math.ceil(1/cueRatio); var spanTimeStart = 0; var spanTimeEnd = 0; var alarmed = 0; var alarmHtml = ''; var pixSkew = 0; var skip = 0; var num_cueFrames = cueFrames.length; for ( var i = 0; i < num_cueFrames; i++ ) { skip = 0; frame = cueFrames[i]; if ( (frame.Type == 'Alarm') && (alarmed == 0) ) { //From nothing to alarm. End nothing and start alarm. alarmed = 1; if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan spanTimeEnd = frame.Delta * 100; spanTime = spanTimeEnd - spanTimeStart; var pix = cueRatio * spanTime; pixSkew += pix - Math.round(pix);//average out the rounding errors. pix = Math.round(pix); if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { //add skew if it's a pixel and won't zero out span. pix += Math.round(pixSkew); pixSkew = pixSkew - Math.round(pixSkew); } alarmHtml += ''; spanTimeStart = spanTimeEnd; } else if ( (frame.Type !== 'Alarm') && (alarmed == 1) ) { //from alarm to nothing. End alarm and start nothing. futNone = 0; indexPlus = i+1; if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < num_cueFrames) { //alarm is too short and there is more event continue; } while ( futNone < minAlarm ) { //check ahead to see if there's enough for a nonespan if ( indexPlus >= cueFrames.length ) break; //check if end of event. futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100); if ( cueFrames[indexPlus].Type == 'Alarm' ) { i = --indexPlus; skip = 1; break; } indexPlus++; } if ( skip == 1 ) continue; //javascript doesn't support continue 2; spanTimeEnd = frame.Delta *100; spanTime = spanTimeEnd - spanTimeStart; alarmed = 0; pix = cueRatio * spanTime; pixSkew += pix - Math.round(pix); pix = Math.round(pix); if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { pix += Math.round(pixSkew); pixSkew = pixSkew - Math.round(pixSkew); } alarmHtml += ''; spanTimeStart = spanTimeEnd; } else if ( (frame.Type == 'Alarm') && (alarmed == 1) && (i + 1 >= cueFrames.length) ) { //event ends on an alarm spanTimeEnd = frame.Delta * 100; spanTime = spanTimeEnd - spanTimeStart; alarmed = 0; pix = Math.round(cueRatio * spanTime); if (pixSkew >= .5 || pixSkew <= -.5) pix += Math.round(pixSkew); alarmHtml += ''; } } return alarmHtml; } function changeCodec() { location.replace(thisUrl + '?view=event&eid=' + eventData.Id + filterQuery + sortQuery+'&codec='+$j('#codec').val()); } function changeScale() { var scale = $j('#scale').val(); var newWidth; var newHeight; var autoScale; var eventViewer= $j(vid ? '#videoobj' : '#videoFeed'); var alarmCue = $j('div.alarmCue'); var bottomEl = $j('#replayStatus'); if ( scale == '0' || scale == 'auto' ) { var newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl); newWidth = newSize.width; newHeight = newSize.height; autoScale = newSize.autoScale; } else { $j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active newWidth = eventData.Width * scale / SCALE_BASE; newHeight = eventData.Height * scale / SCALE_BASE; } eventViewer.width(newWidth); eventViewer.height(newHeight); if ( !vid ) { // zms needs extra sizing streamScale(scale == '0' ? autoScale : scale); drawProgressBar(); } alarmCue.html(renderAlarmCues(eventViewer));//just re-render alarmCues. skip ajax call setCookie('zmEventScale'+eventData.MonitorId, scale, 3600); // After a resize, check if we still have room to display the event stats table onStatsResize(newWidth); } // end function changeScale function changeReplayMode() { var replayMode = $j('#replayMode').val(); setCookie('replayMode', replayMode, 3600); refreshWindow(); } function changeRate() { var rate = parseInt($j('select[name="rate"]').val()); if (!rate) { pauseClicked(); } else if (rate < 0) { if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. revSpeed = rates[rates.indexOf(-1*rate)-1]/100; clearInterval(intervalRewind); intervalRewind = setInterval(function() { if (vid.currentTime() <= 0) { clearInterval(intervalRewind); vid.pause(); } else { vid.playbackRate(0); vid.currentTime(vid.currentTime() - (revSpeed/2)); //Half of reverse speed because our interval is 500ms. } }, 500); //500ms is a compromise between smooth reverse and realistic performance } else { streamReq({command: CMD_VARPLAY, rate: rate}); } // end if vid } else { // Forward rate if ( vid ) { vid.playbackRate(rate/100); } else { streamReq({command: CMD_VARPLAY, rate: rate}); } } setCookie('zmEventRate', rate, 3600); } // end function changeRate function getCmdResponse(respObj, respText) { if ( checkStreamForErrors('getCmdResponse', respObj) ) { console.log('Got an error from getCmdResponse'); console.log(respObj); console.log(respText); zmsBroke = true; return; } zmsBroke = false; if (streamCmdTimer) streamCmdTimer = clearTimeout(streamCmdTimer); streamStatus = respObj.status; if (!streamStatus) { console.log('No status in respObj'); console.log(respObj); return; } else if (streamStatus.duration && ( streamStatus.duration != parseFloat(eventData.Length) )) { eventData.Length = streamStatus.duration; } if (streamStatus.progress > parseFloat(eventData.Length)) { console.log("Limiting progress to " + streamStatus.progress + ' >= ' + parseFloat(eventData.Length) ); streamStatus.progress = parseFloat(eventData.Length); } //Limit progress to reality var eventId = streamStatus.event; if (lastEventId) { if (eventId != lastEventId) { //Doesn't run on first load, prevents a double hit on event and nearEvents ajax eventQuery(eventId); initialAlarmCues(eventId); //zms uses this instead of a page reload, must call ajax+render lastEventId = eventId; } } else { lastEventId = eventId; //Only fires on first load. } if (streamStatus.paused == true) { streamPause( ); } else { $j('select[name="rate"]').val(streamStatus.rate*100); setCookie('zmEventRate', streamStatus.rate*100, 3600); streamPlay( ); } $j('#progressValue').html(secsToTime(parseInt(streamStatus.progress))); $j('#zoomValue').html(streamStatus.zoom); if (streamStatus.zoom == '1.0') { setButtonState('zoomOutBtn', 'unavail'); } else { setButtonState('zoomOutBtn', 'inactive'); } updateProgressBar(); if (streamStatus.auth) { // Try to reload the image stream. var streamImg = document.getElementById('evtStream'); if (streamImg) { streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth); } } // end if haev a new auth hash streamCmdTimer = setTimeout(streamQuery, streamTimeout); //Timeout is refresh rate for progressBox and time display } // end function getCmdResponse( respObj, respText ) function pauseClicked() { if (vid) { if (intervalRewind) { stopFastRev(); } vid.pause(); } else { streamReq({command: CMD_PAUSE}); } streamPause(); } function streamPause() { $j('#modeValue').html('Paused'); setButtonState('pauseBtn', 'active'); setButtonState('playBtn', 'inactive'); setButtonState('fastFwdBtn', 'unavail'); setButtonState('slowFwdBtn', 'inactive'); setButtonState('slowRevBtn', 'inactive'); setButtonState('fastRevBtn', 'unavail'); } function playClicked( ) { var rate_select = $j('select[name="rate"]'); if (!rate_select.val()) { $j('select[name="rate"]').val(100); } if (vid) { if (vid.paused()) { vid.play(); } else { vjsPlay(); //handles fast forward and rewind } } else { streamReq({command: CMD_PLAY}); } streamPlay(); } function vjsPlay() { //catches if we change mode programatically if (intervalRewind) { stopFastRev(); } $j('select[name="rate"]').val(vid.playbackRate()*100); setCookie('zmEventRate', vid.playbackRate()*100, 3600); streamPlay(); } function streamPlay( ) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'active'); setButtonState('fastFwdBtn', 'inactive'); setButtonState('slowFwdBtn', 'unavail'); setButtonState('slowRevBtn', 'unavail'); setButtonState('fastRevBtn', 'inactive'); } function streamFastFwd(action) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'inactive'); setButtonState('fastFwdBtn', 'active'); setButtonState('slowFwdBtn', 'unavail'); setButtonState('slowRevBtn', 'unavail'); setButtonState('fastRevBtn', 'inactive'); if (vid) { if (revSpeed != .5) stopFastRev(); vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)+1]/100); if (rates.indexOf(vid.playbackRate()*100)+1 == rates.length) { setButtonState('fastFwdBtn', 'unavail'); } $j('select[name="rate"]').val(vid.playbackRate()*100); setCookie('zmEventRate', vid.playbackRate()*100, 3600); } else { streamReq({command: CMD_FASTFWD}); } } function streamSlowFwd(action) { if (vid) { vid.currentTime(vid.currentTime() + spf); } else { streamReq({command: CMD_SLOWFWD}); } } function streamSlowRev(action) { if (vid) { vid.currentTime(vid.currentTime() - spf); } else { streamReq({command: CMD_SLOWREV}); } } function stopFastRev() { clearInterval(intervalRewind); vid.playbackRate(1); $j('select[name="rate"]').val(vid.playbackRate()*100); setCookie('zmEventRate', vid.playbackRate()*100, 3600); revSpeed = .5; } /* Called when rewind button is clicked * should cycle through the reverse rates including pause */ function streamFastRev(action) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'inactive'); setButtonState('fastFwdBtn', 'inactive'); setButtonState('slowFwdBtn', 'unavail'); setButtonState('slowRevBtn', 'unavail'); setButtonState('fastRevBtn', 'active'); if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. revSpeed = -1*(rates[rates.indexOf(revSpeed*-100)-1]/100); if (rates.indexOf(revSpeed*-100) == 0) { setButtonState('fastRevBtn', 'unavail'); } clearInterval(intervalRewind); $j('select[name="rate"]').val(-revSpeed*100); setCookie('zmEventRate', vid.playbackRate()*100, 3600); intervalRewind = setInterval(function() { if (vid.currentTime() <= 0) { clearInterval(intervalRewind); vid.pause(); } else { vid.playbackRate(0); vid.currentTime(vid.currentTime() - (revSpeed/2)); //Half of reverse speed because our interval is 500ms. } }, 500); //500ms is a compromise between smooth reverse and realistic performance } else { streamReq({command: CMD_FASTREV}); } } function streamPrev(action) { if (action) { $j(".vjsMessage").remove(); if (prevEventId != 0) { location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery); return; } /* Ideally I'd like to get back to this style if ( vid && PrevEventDefVideoPath.indexOf("view_video") > 0 ) { CurEventDefVideoPath = PrevEventDefVideoPath; eventQuery(prevEventId); } else if (zmsBroke || (vid && PrevEventDefVideoPath.indexOf("view_video") < 0) || $j("#vjsMessage").length || PrevEventDefVideoPath.indexOf("view_video") > 0) {//zms broke, leaving videojs, last event, moving to videojs location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery); } else { streamReq({command: CMD_PREV}); streamPlay(); } */ } } function streamNext(action) { if (!action) { return; } $j(".vjsMessage").remove();//This shouldn't happen if (nextEventId == 0) { //handles deleting last event. pauseClicked(); var hideContainer = $j('#eventVideo'); var hideStream = $j(vid ? "#videoobj" : "#evtStream").height() + (vid ? 0 :$j("#progressBar").height()); hideContainer.prepend('No more events
'); if (vid == null) zmsBroke = true; return; } // We used to try to dynamically update all the bits in the page, which is really complex // How about we just reload the page? // location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); return; if (vid && ( NextEventDefVideoPath.indexOf('view_video') > 0 )) { // on and staying with videojs CurEventDefVideoPath = NextEventDefVideoPath; eventQuery(nextEventId); } else if ( zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0 ) {//reload zms, leaving vjs, moving to vjs location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); } else { streamReq({command: CMD_NEXT}); streamPlay(); } } // end function streamNext(action) function vjsPanZoom(action, x, y) { //Pan and zoom with centering where the click occurs var outer = $j('#videoobj'); var video = outer.children().first(); var zoom = parseFloat($j('#zoomValue').html()); var zoomRate = .5; var matrix = video.css('transform').split(','); var currentPanX = parseFloat(matrix[4]); var currentPanY = parseFloat(matrix[5]); var xDist = outer.width()/2 - x; //Click distance from center of view var yDist = outer.height()/2 - y; if (action == 'zoomOut') { zoom -= zoomRate; if (x && y) { x = (xDist + currentPanX)*((zoom-zoomRate)/zoom); // if ctrl-click Pan but use ratio of old zoom to new zoom for coords y = (yDist + currentPanY)*((zoom-zoomRate)/zoom); } else { x = currentPanX*((zoom-zoomRate)/zoom); //Leave zoom centered where it was y = currentPanY*((zoom-zoomRate)/zoom); } if (zoom <= 1) { zoom = 1; $j('#zoomOutBtn').attr('class', 'unavail').attr('disabled', 'disabled'); } $j('#zoomValue').html(zoom); } else if (action == 'zoom') { zoom += zoomRate; x = (xDist + currentPanX)*(zoom/(zoom-zoomRate)); //Pan but use ratio of new zoom to old zoom for coords. Center on mouse click. y = (yDist + currentPanY)*(zoom/(zoom-zoomRate)); $j('#zoomOutBtn').attr('class', 'inactive').removeAttr('disabled'); $j('#zoomValue').html(zoom); } else if (action == 'pan') { x = xDist + currentPanX; y = yDist + currentPanY; } var limitX = ((zoom*outer.width()) - outer.width())/2; //Calculate outer bounds of video var limitY = ((zoom*outer.height()) - outer.height())/2; x = Math.min(Math.max((x), -limitX), limitX); //Limit pan to outer bounds of video y = Math.min(Math.max((y), -limitY), limitY); video.css('transform', 'matrix('+zoom+', 0, 0, '+zoom+', '+x+', '+y+')'); } function streamZoomIn(x, y) { if (vid) { vjsPanZoom('zoom', x, y); } else { streamReq({command: CMD_ZOOMIN, x: x, y: y}); } } function streamZoomOut() { if (vid) { vjsPanZoom('zoomOut'); } else { streamReq({command: CMD_ZOOMOUT}); } } function streamScale(scale) { streamReq({command: CMD_SCALE, scale: scale}); } function streamPan(x, y) { if (vid) { vjsPanZoom('pan', x, y); } else { streamReq({command: CMD_PAN, x: x, y: y}); } } function streamSeek(offset) { streamReq({command: CMD_SEEK, offset: offset}); } function streamQuery() { streamReq({command: CMD_QUERY}); } function getEventResponse(respObj, respText) { if ( checkStreamForErrors('getEventResponse', respObj) ) { console.log('getEventResponse: errors'); return; } eventData = respObj.event; getStat(); currEventId = eventData.Id; // Refresh the status of the archive buttons archiveBtn.prop('disabled', !(!eventData.Archived && canEdit.Events)); unarchiveBtn.prop('disabled', !(eventData.Archived && canEdit.Events)); history.replaceState(null, null, '?view=event&eid=' + eventData.Id + filterQuery + sortQuery); //if popup removed, check if this allows forward if ( vid && CurEventDefVideoPath ) { vid.src({type: 'video/mp4', src: CurEventDefVideoPath}); //Currently mp4 is all we use initialAlarmCues(eventData.Id);//ajax and render, new event addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartDateTime); CurEventDefVideoPath = null; $j('#modeValue').html('Replay'); $j('#zoomValue').html('1'); $j('#rate').val('100'); vjsPanZoom('zoomOut'); } else { drawProgressBar(); } nearEventsQuery(eventData.Id); } // end function getEventResponse function eventQuery(eventId) { var data = {}; data.id = eventId; if (auth_hash) data.auth = auth_hash; $j.getJSON(thisUrl + '?view=request&request=status&entity=event', data) .done(getEventResponse) .fail(logAjaxFail); } function getNearEventsResponse(respObj, respText) { if (checkStreamForErrors('getNearEventsResponse', respObj)) { return; } prevEventId = parseInt(respObj.nearevents.PrevEventId); nextEventId = parseInt(respObj.nearevents.NextEventId); prevEventStartTime = Date.parse(respObj.nearevents.PrevEventStartTime); nextEventStartTime = Date.parse(respObj.nearevents.NextEventStartTime); PrevEventDefVideoPath = respObj.nearevents.PrevEventDefVideoPath; NextEventDefVideoPath = respObj.nearevents.NextEventDefVideoPath; $j('#prevBtn').prop('disabled', prevEventId == 0 ? true : false).attr('class', prevEventId == 0 ? 'unavail' : 'inactive'); $j('#nextBtn').prop('disabled', nextEventId == 0 ? true : false).attr('class', nextEventId == 0 ? 'unavail' : 'inactive'); } function nearEventsQuery(eventId) { $j.getJSON(thisUrl + '?view=request&request=status&entity=nearevents&id='+eventId+filterQuery+sortQuery) .done(getNearEventsResponse) .fail(logAjaxFail); } function getFrameResponse(respObj, respText) { if (checkStreamForErrors('getFrameResponse', respObj)) { return; } var frame = respObj.frameimage; if (!eventData) { console.error('No event '+frame.EventId+' found'); return; } if (!eventData['frames']) { eventData['frames'] = {}; } eventData['frames'][frame.FrameId] = frame; } function frameQuery(eventId, frameId, loadImage) { var data = {}; if (auth_hash) data.auth = auth_hash; data.loopback = loadImage; data.id = {eventId, frameId}; $j.getJSON(thisUrl + '?view=request&request=status&entity=frameimage', data) .done(getFrameResponse) .fail(logAjaxFail); } function prevEvent() { if (prevEventId) { eventQuery(prevEventId); streamPrev(true); } } function nextEvent() { if (nextEventId) { eventQuery(nextEventId); streamNext(true); } } function getActResponse(respObj, respText) { if (checkStreamForErrors('getActResponse', respObj)) { return; } if (respObj.refreshEvent) { eventQuery(eventData.Id); } $j('#eventRenameModal').modal('hide'); } function actQuery(action, parms) { var data = {}; if (parms) data = parms; if (auth_hash) data.auth = auth_hash; data.id = eventData.Id; data.action = action; $j.getJSON(thisUrl + '?view=request&request=event', data) .done(getActResponse) .fail(logAjaxFail); } function renameEvent() { var newName = $j('input').val(); actQuery('rename', {eventName: newName}); } function showEventFrames() { window.location.assign('?view=frames&eid='+eventData.Id); } function videoEvent() { window.location.assign('?view=video&eid='+eventData.Id); } // Called on each event load because each event can be a different width function drawProgressBar() { var barWidth = $j('#evtStream').width(); $j('#progressBar').css('width', barWidth); } // Shows current stream progress. function updateProgressBar() { if (!(eventData && streamStatus)) { return; } // end if ! eventData && streamStatus var curWidth = (streamStatus.progress / parseFloat(eventData.Length)) * 100; $j("#progressBox").css('width', curWidth + '%'); } // end function updateProgressBar() // Handles seeking when clicking on the progress bar. function progressBarNav() { $j('#progressBar').click(function(e) { var x = e.pageX - $j(this).offset().left; var seekTime = (x / $j('#progressBar').width()) * parseFloat(eventData.Length); streamSeek(seekTime); }); } function handleClick(event) { var target = event.target; var rect = target.getBoundingClientRect(); if (vid) { if (target.id != 'videoobj') return; // ignore clicks on control bar var x = event.offsetX; var y = event.offsetY; } else { var x = event.page.x - rect.left; var y = event.page.y - rect.top; } if (event.shift || event.shiftKey) { // handle both jquery and mootools streamPan(x, y); } else if (vid && event.ctrlKey) { // allow zoom out by control click. useful in fullscreen vjsPanZoom('zoomOut', x, y); } else { streamZoomIn(x, y); } } // Manage the DELETE CONFIRMATION modal button function manageDelConfirmModalBtns() { document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) { if (!canEdit.Events) { enoperm(); return; } pauseClicked(); evt.preventDefault(); $j.getJSON(thisUrl + '?request=event&action=delete&id='+eventData.Id) .done(function(data) { $j('#deleteConfirm').modal('hide'); streamNext(true); }) .fail(logAjaxFail); }); // Manage the CANCEL modal button document.getElementById("delCancelBtn").addEventListener("click", function onDelCancelClick(evt) { $j('#deleteConfirm').modal('hide'); }); } function getEvtStatsCookie() { var cookie = 'zmEventStats'; var stats = getCookie(cookie); if (!stats) { stats = 'on'; setCookie(cookie, stats, 10*365); } return stats; } function getStat() { table.empty().append(''); $j.each(eventDataStrings, function(key) { var th = $j('