I'm trying to write a 3 second countdown function in JavaScript. The start of the countdown is set by the server so I have setInterval function which calls an ajax function which runs a script on the server to see if it is ready to start the countdown. If the countdown is set, the data returned is that the countdown is ready and will start in a specified number of milliseconds.
I've got the following function which, if I step through I can see the screen updating step by step. However, when I simply run the script it updates everything in bulk. I do not understand why?
$.ajax({
url : "/check_countdown/", // the endpoint
type : "GET", // http method
data : { info : info }, // data sent with the post request
// handle a successful response
success : function(json) {
console.log(json);
if (json.ready == 'True') {
// if we have a start_time then we get ready for the count down
console.log("Countdown ready to start!"); // sanity check
// stop pinging the server
clearInterval(countdownInterval);
// clear screen
$('#holdingImage').hide();
// show countdown block
$('#countdownText').show();
startTime = new Date().getTime() + json.milliseconds;
nowTime = new Date().getTime();
console.log("Every ", nowTime, startTime);
while (nowTime < startTime) {
nowTime = new Date().getTime();
}
$('#countdownText').html("<h1>Three</h1>");
startTime = startTime + 1000;
console.log("Second ", nowTime, startTime);
while (nowTime < startTime) {
nowTime = new Date().getTime();
}
$('#countdownText').html("<h1>Two</h1>");
startTime = startTime + 1000;
console.log("Counts ", nowTime, startTime);
while (nowTime < startTime) {
nowTime = new Date().getTime();
}
$('#countdownText').html("<h1>One</h1>");
} else {
console.log("Countdown NOT ready to start!"); // another sanity check
}
},
// handle a non-successful response
error : function(xhr,errmsg,err) {
$('#results').html("<div class='alert-box alert radius' data-alert>Oops! We have encountered an error: "+errmsg+
" <a href='#' class='close'>×</a></div>"); // add the error to the dom
console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
}
});
I figure a second (1000 milliseconds) between updates should be enough?
$.ajax({
url : "/check_countdown/", // the endpoint
type : "GET", // http method
data : { info : info }, // data sent with the post request
async: false, //<---- Add this
....
only Add (async: false)
This is the solution that I came up with. I'm not convinced by its efficacy but ...
I changed the success function to:
success : function(json) {
console.log(json);
if (json.ready == 'True') {
// if we have a start_time then we get ready for the count down
console.log("Countdown ready to start!"); // sanity check
console.log(json);
// stop pinging the server
clearInterval(countdownInterval);
// clear screen
$('#holdingImage').hide();
// show countdown block
$('#countdownText').show();
startTime = new Date().getTime() + json.milliseconds;
nowTime = new Date().getTime();
while (nowTime < startTime) {
nowTime = new Date().getTime();
}
startCountdown();
}
I added a new function called startCountdown() which is:
function startCountdown () {
var display1 = document.querySelector('#countdownText'),
startTime = 5,
remainingTime = startTime,
timer = new CountDownTimer(startTime);
timer.onTick(format1).start();
function format1(minutes, seconds) {
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
display1.textContent = seconds;
remainingTime = parseInt(minutes) * 60 + parseInt(seconds);
if ((minutes=="00") && (seconds=="00")){
console.log("time expired!"); // sanity check
}
}
}
And then I used this timer.js script which I had from elsewhere (I do not know where I got it so cannot credit the author - sorry)
function CountDownTimer(duration, granularity) {
this.duration = duration;
this.granularity = granularity || 1000;
this.tickFtns = [];
this.running = false;
}
CountDownTimer.prototype.start = function() {
if (this.running) {
return;
}
this.running = true;
var start = Date.now(),
that = this,
diff, obj;
(function timer() {
diff = that.duration - (((Date.now() - start) / 1000) | 0);
if (diff > 0) {
setTimeout(timer, that.granularity);
} else {
diff = 0;
that.running = false;
}
obj = CountDownTimer.parse(diff);
that.tickFtns.forEach(function(ftn) {
ftn.call(this, obj.minutes, obj.seconds);
}, that);
}());
};
CountDownTimer.prototype.onTick = function(ftn) {
if (typeof ftn === 'function') {
this.tickFtns.push(ftn);
}
return this;
};
CountDownTimer.prototype.expired = function() {
return !this.running;
};
CountDownTimer.parse = function(seconds) {
return {
'minutes': (seconds / 60) | 0,
'seconds': (seconds % 60) | 0
};
};
CountDownTimer.prototype.stop = function() {
this.running = false;
};
All in, it gives me my desired result
Related
I made a counter counting down from 25 with javscript, but when different users request the page, different time values are displayed in the counter. Instead, I want to keep those 25 seconds in the database and count down with php or javascript so that every user sees the same value. How can I set up logic for the database?After 25 seconds it has to start again
var interval = 25000;
function reset() {
localStorage.endTime = +new Date() + interval;
}
if (!localStorage.endTime) {
reset();
}
function millisToMinutesAndSeconds(millis) {
var seconds = ((millis % 60000) / 1000).toFixed(0);
return (seconds < 10 ? "0" : "") + seconds;
}
setInterval(function () {
var remaining = localStorage.endTime - new Date();
if (remaining >= 0) {
document.getElementById("timer").innerText =
millisToMinutesAndSeconds(remaining);
} else {
tensecond();
}
}, 100);
var interval10 = 10000;
function reset() {
localStorage.endTime = +new Date() + interval10;
}
if (!localStorage.endTime) {
reset();
}
function millisToMinutesAndSeconds(millis) {
var seconds = ((millis % 60000) / 1000).toFixed(0);
return (seconds < 10 ? "0" : "") + seconds;
}
setInterval(function () {
var remaining10 = localStorage.endTime - new Date();
if (remaining10 > 0) {
document.getElementById("timer").innerText =
millisToMinutesAndSeconds(remaining10);
} else {
reset();
}
}, 100);
I would probably instead store the end time of the timer (in UTC) in the database. Then, your clients can do something like:
const startTimer = async () => {
const response = await fetch('https://your-server/timer');
const endTime = await response.json(); // ex: { timestamp: 1663088872145 }
const end = newDate(endTime.timestamp);
const i = setInterval(() => {
const seconds = ((new Date()).valueOf() - end.valueOf()) / 1000
if (i >= 0) {
document.getElementById('timer').html = seconds;
} else {
clearInterval(i);
}
});
}
For example (with a mock backend):
const fetch = async () => {
return new Promise((resolve) => {
resolve({
json: async function() {
return { timestamp: (new Date()).valueOf() + 15000 };
}
});
});
}
const startTimer = async () => {
const response = await fetch('https://your-server/timer');
const endTime = await response.json(); // ex: { timestamp: 1663088872145 }
const end = new Date(endTime.timestamp);
let seconds = Math.round(-((new Date()).valueOf() - end.valueOf()) / 1000)
document.getElementById('timer').innerHTML = seconds;
const i = setInterval(() => {
seconds = Math.round(-((new Date()).valueOf() - end.valueOf()) / 1000)
if (seconds >= 0) {
document.getElementById('timer').innerHTML = seconds;
} else {
clearInterval(i);
}
}, 1000);
}
startTimer();
<div id="timer"/>
Here is a solution without using php and a database which should ensure that multiple users loading the page at different times all see the same value for the count down.
setInterval(() => {
const timerMs = 25000 - (Date.now() % 25000);
document.querySelector('body').innerHTML = millisToSeconds(timerMs);
}, 100);
function millisToSeconds(millis) {
const seconds = ((millis % 60000) / 1000).toFixed(0);
return (seconds < 10 ? "0" : "") + seconds;
}
When I reload the page, my 1 minute countdown also reloads.
I tried to use localStorage but it seems to me failed.
Please have a look, I do not know where I should fix.
Thank you
My script
/* for countdown */
var countDown = (function ($) {
// Length ms
var timeOut = 10000;
// Interval ms
var timeGap = 1000;
var currentTime = (new Date()).getTime();
var endTime = (new Date()).getTime() + timeOut;
var guiTimer = $("#clock");
var running = true;
var timeOutAlert = $("#timeout-alert");
timeOutAlert.hide();
var updateTimer = function() {
// Run till timeout
if(currentTime + timeGap < endTime) {
setTimeout( updateTimer, timeGap );
}
// Countdown if running
if(running) {
currentTime += timeGap;
if(currentTime >= endTime) { // if its over
guiTimer.css("color","red");
}
}
// Update Gui
var time = new Date();
time.setTime(endTime - currentTime);
var minutes = time.getMinutes();
var seconds = time.getSeconds();
guiTimer.html((minutes < 10 ? '0' : '') + minutes + ':' + (seconds < 10 ? '0' : '') + seconds);
if (parseInt(guiTimer.html().substr(3)) <= 10){ // alert the user that he is running out of time
guiTimer.css('color','red');
timeOutAlert.show();
}
};
var pause = function() {
running = false;
};
var resume = function() {
running = true;
};
var start = function(timeout) {
timeOut = timeout;
currentTime = (new Date()).getTime();
endTime = (new Date()).getTime() + timeOut;
updateTimer();
};
return {
pause: pause,
resume: resume,
start: start
};
})(jQuery);
jQuery('#break').on('click',countDown.pause);
jQuery('#continue').on('click',countDown.resume);
var seconds = 60; // seconds we want to count down
countDown.start(seconds*1000);
I tried to fix it but I dont know where/how to put localStorage.
This may help you.
HTML Code:
<div id="divCounter"></div>
JS Code
var test = 60;
if (localStorage.getItem("counter")) {
if (localStorage.getItem("counter") <= 0) {
var value = test;
alert(value);
} else {
var value = localStorage.getItem("counter");
}
} else {
var value = test;
}
document.getElementById('divCounter').innerHTML = value;
var counter = function() {
if (value <= 0) {
localStorage.setItem("counter", test);
value = test;
} else {
value = parseInt(value) - 1;
localStorage.setItem("counter", value);
}
document.getElementById('divCounter').innerHTML = value;
};
var interval = setInterval(function() { counter(); }, 1000);
I wrote this code that starts a timer. I fire a function that restarts the timer when it reaches 0. It works, but I get an error in the console that says Uncaught TypeError: Cannot read property 'restartTimer' of undefined. It has to do with this.restartTimer();
timer = utility.math.surveyTimer({
seconds: time,
onUpdateStatus: function(remainingTime) {
$(surveyTimerNode).text(remainingTime);
},
restartTimer: function() {
window.TimerInterval = timer.start();
},
onCounterEnd: function() {
if (utility.bool.isQuestionScreen()) {
if (utility.bool.surveyWillLoop()) {
data.setPersistentSurveyData('DSM_SURVEY_SCREENS', surveyScreens);
data.setPersistentSurveyData('DSM_SURVEY_SCREEN_ORDER', surveyScreenOrder);
tagData = data.getPersistentSurveyData('DSM_SURVEY_DATA');
apiParam = api.helper.buildAPIParam('surveyTimeout', tagData);
api.post.postToAPI(apiParam);
parent.resetSurveyProgress();
parent.moveToNextScreen();
this.restartTimer();
} else {
parent.goToEndscreen();
}
}
}
});
window.TimerInterval = timer.start();
No errors in JSLint, just errors on run. What's so bizarre is that it works, the timer does reset. How do I remove this error?
Here's the function that actually does the timer counting:
this.surveyTimer = function (options) {
var timer,
instance = this,
minutes,
secondsMinusMinutes,
remainingTime,
seconds = options.seconds || 30,
updateStatus = options.onUpdateStatus || function () {
return undefined;
},
counterEnd = options.onCounterEnd || function () {
return undefined;
};
function zeroPad(n) {
return (n < 10) ? ("0" + n) : n;
}
function decrementCounter() {
minutes = Math.floor(seconds / 60);
secondsMinusMinutes = seconds - minutes * 60;
remainingTime = minutes + ':' + zeroPad(secondsMinusMinutes);
updateStatus(remainingTime);
if (seconds === 0) {
counterEnd();
instance.stop();
}
seconds -= 1;
}
this.start = function () {
clearInterval(timer);
timer = 0;
seconds = options.seconds;
timer = setInterval(decrementCounter, 1000);
return timer;
};
this.stop = function () {
clearInterval(timer);
};
return this;
};
Just a guess, but I think your use of this in your options object is not the this you think it is...
Try changing your implementation of surveyTimer, where it is currently:
counterEnd();
to:
counterEnd.call(options);
I need to know how to set 2 time intervals for the same function?I mean that, now i have set timeinterval of 1 sec to continuously monitor the output of server file.If the output of server file is 0,then color of extension icon changes and it shows a notification.Now the problem is that i have written both these functionalities in the same function.So as i have set time interval for 1 sec and call that function,the notification shows every 1 sec and the color of icon also changes every 1 sec based on server output,which is fine.Now what I need is that i need to change the color every 1 sec.but i need to show notification only every 5 min.can you please help me.i have posted my background.js.can you please help me?
here is my background.js
var myNotificationID = null;
var oldChromeVersion = !chrome.runtime;
var interval = 5 * 60 * 1000; // 5 minutes in milliseconds
var lastNotification = 0;
setInterval(function() {
updateIcon();
}, 1000);
function getGmailUrl() {
return "http://calpinemate.com/";
}
function isGmailUrl(url) {
return url.indexOf(getGmailUrl()) == 0;
}
function onInit() {
updateIcon();
if (!oldChromeVersion) {
chrome.alarms.create('watchdog',{periodInMinutes:5,delayInMinutes: 0});
}
}
function onAlarm(alarm) {
if (alarm && alarm.name == 'watchdog') {
onWatchdog();
}
else {
updateIcon();
}
function onWatchdog() {
chrome.alarms.get('refresh', function(alarm) {
if (alarm) {
console.log('Refresh alarm exists. Yay.');
}
else {
updateIcon();
}
});
}
if (oldChromeVersion) {
updateIcon();
onInit();
}
else {
chrome.runtime.onInstalled.addListener(onInit);
chrome.alarms.onAlarm.addListener(onAlarm);
}
function updateIcon(){
if(localStorage.username){
var urlPrefix = 'http://www.calpinemate.com/employees/attendanceStatus/';
var urlSuffix = '/2';
var req = new XMLHttpRequest();
req.addEventListener("readystatechange", function() {
if (req.readyState == 4) {
if (req.status == 200) {
var item=req.responseText;
if(item==1){
chrome.browserAction.setIcon({path:"calpine_logged_in.png"});
chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
chrome.browserAction.setBadgeText({text:""});
chrome.notifications.clear('id1', function(){});
}
else{
chrome.browserAction.setIcon({path:"calpine_not_logged_in.png"});
chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
chrome.browserAction.setBadgeText({text:""});
var now = new Date().getTime();
if (now - lastNotification > interval) {
chrome.notifications.create(
'id1',{
type: 'basic',
iconUrl: '/calpine_not_logged_in.png',
title: 'Warning : Attendance',
message: 'Please mark your Attendance !',
buttons: [{ title: 'Mark',
iconUrl: '/tick.jpg'
},{ title: 'Ignore',
iconUrl: '/cross.jpg'}],
priority: 0},
function(id) { myNotificationID = id;}
);
}
}
}
else {
alert("ERROR: status code " + req.status);
}
}
});
var url = urlPrefix + encodeURIComponent(localStorage.username) + urlSuffix;
req.open("GET", url);
req.send(null);
}
}
onInit();
One possible approach is to keep track of the time when the last notification was displayed and always check if 5 minutes have passed since. E.g.:
/* Put these 2 lines at the very top of your script */
var interval = 5 * 60 * 1000; // 5 minutes in milliseconds
var lastNotification = 0;
Then, inside the updateIcon() function, replace this line:
chrome.notifications.create(...);
with these lines:
var now = new Date().getTime();
if (now - lastNotification > interval) {
lastNotification = now;
chrome.notifications.create(...);
}
The above piece of code will make sure the notification is created only if 5 minutes have passed since the last time a notification was created. It will also update the lastNotification variable with the present time.
Have this code, which runs functionA every 60 seconds, and in between functionB
var launchTime = undefined;
function Launcher() {
if (undefined == launchTime) {
launchTime = new Date();
}
nowTime = new Date();
diff = (nowTime.getTime() - launchTime.getTime()) / 1000;
if (diff % 60 == 0 and diff > 0) { // 60 seconds have passed
functionA();
} else { // 1 second has passed
functionB();
}
}
function functionA() {
}
function functionB() {
}
setInterval(function() { Launcher(); }, 1000);
My app looks like this:
Here is the fiddle what I tried. I want to play the next exercise time automatically on the end of the current exercise timer But currently in this fiddle its asking to click the play button for starting the new exercise timer.
Also, I want to play the 5 sounds count saying(1,2,3,4,5) just after the first audio i.e audiosource .
Here is my code:
function myApp($tier) {
this.paused = false;
this.paused = true // set pause to true (stop)
this.isactive = false; // countdown is active
this.timer = $tier;
return this.observer(); // call and return the "observer" method of the "myApp" class
}
myApp.prototype.observer = function() { // observer method
var self = this; // set this to self (this is the current instance of this class)
$('#btn_start').on('click', function(event){ // where an user click on "btn_start"
event.preventDefault();
self.play('mp3'); // call the play method and store the state in a public var
self.countdown(self.onEnd); // 30 is the audio duration - second parameter is the callback when the audio is finished
self.isactive = true;
});
return this; // return this instance
};
myApp.prototype.play = function() { // play method
var song = document.getElementById('audiosource');
if (this.paused === true)
{
console.log('play song'); $("#btn_start").addClass('btn-pause');
song.play();
this.paused = false;
}
else
{
console.log('pause song');$("#btn_start").removeClass('btn-pause');
song.pause();
this.paused = true;
}
return this;
};
myApp.prototype.countdown = function(callback) { // countdown method
var self = this, // class instance
countdown = null, // countdown
ctx = null; // setIntervall clearer
if (this.isactive === true) // if this method yet called
{
return false;
}
countdown = function() {
console.log('start countdown:' + self.paused);
if (self.timer === 0)
{
clearInterval(ctx);
callback.call(this);
return false;
}
if (self.paused === false) // if not in pause
{
self.timer -= 1;
console.log(self.timer);
var msec=rectime(self.timer);
$('#timer > span').html(msec);
}
};
ctx = setInterval(countdown, 1000);
};
myApp.prototype.onEnd = function() {
// when the audio is finish..
$("#btn_start").removeClass('btn-pause');
alert ('end of the first exercise, NOw lets play the second exercise for your with another by loading its time');
new myApp('10');
$('#timer > span').html(rectime('10'));
};
$(function() {
new myApp('4');
$('#timer > span').html(rectime('4'));
});
function rectime(secs) {
var hr = Math.floor(secs / 3600);
var min = Math.floor((secs - (hr * 3600))/60);
var sec = secs - (hr * 3600) - (min * 60);
while (min.length < 2) {min = '0' + min;}
while (sec.length < 2) {sec = '0' + min;}
if (hr) hr += ':';
return hr + min + ':' + sec;
}