JavaScript timer doesn't run if page not active page viewed - javascript

Question sounds quiet weird I know but here is the problem, the following code works perfectly. Timer starts at 30 minutes, every second a mouse move is not detected counts the timer down. When a mouse move is detected timer gets reset to 30 minutes, at the 25 minute mark of page inactivity a CSS popup shows counting down the last 5 minutes, at 30 minutes, the user gets auto logged out. However, if the user has the page open but is actively viewing another webpage altogether the timer either slows or stops altogether depending on the browser. Which in effect negates the script altogether. Is it possible to have the script continue its normal countdown and still force the user out of the page even if they aren't actively viewing the page? Or are these browser quirks to reduce memory load?
var Timing = 0;
var CounterTime = 0;
var TimePast = 0;
var Seconds = 1800;
var Warn = 1500;
var MinuteLeft = 30;
var SecondLeft = 60;
var StopRefresh = 0;
function ResponseTime()
{
Timing = Timing + 100;
CounterTime = CounterTime + 100;
if(Timing % 1000 == 0)
{
TimePast = TimePast + 1;
SecondLeft = SecondLeft - 1;
if(SecondLeft == 59)
{
MinuteLeft = MinuteLeft-1;
}
if(SecondLeft == 0)
{
SecondLeft = 60;
}
}
if(MinuteLeft != 0)
{
if(SecondLeft == 60)
{
document.getElementById('CountdownTimer').firstChild.nodeValue = MinuteLeft+":00";
}else if(SecondLeft < 10)
{
document.getElementById('CountdownTimer').firstChild.nodeValue = MinuteLeft+":0"+SecondLeft;
}else
{
document.getElementById('CountdownTimer').firstChild.nodeValue = MinuteLeft+":"+SecondLeft;
}
if((MinuteLeft == 0) && (SecondLeft <= 10))
{
document.getElementById('CountdownTimer').style.fontWeight = "bolder";
document.getElementById('CountdownTimer').style.color = "red";
}
document.getElementById('CountdownTimer').style.fontWeight = "normal";
document.getElementById('CountdownTimer').style.color = "black";
}else
{
document.getElementById('CountdownTimer').firstChild.nodeValue = SecondLeft;
if((MinuteLeft == 0) && (SecondLeft <= 10))
{
document.getElementById('CountdownTimer').style.fontWeight = "bolder";
document.getElementById('CountdownTimer').style.color = "red";
}else
{
document.getElementById('CountdownTimer').style.fontWeight = "normal";
document.getElementById('CountdownTimer').style.color = "black";
}
}
if(TimePast == 1800)
{
document.getElementById('DoLogoutRequest').submit();
}
if(MinuteLeft <=4)
{
document.getElementById('Overlay').style.visibility="visible";
document.getElementById('ForceLogout').style.visibility="visible";
}else
{
document.getElementById('Overlay').style.visibility="hidden";
document.getElementById('ForceLogout').style.visibility="hidden";
}
$(document).ready(function(){
$(document).mousemove(function(){
Timing = 0;
TimePast = 0;
SecondLeft = 60;
MinuteLeft = 29;
});
});
}

What you could do is modify the script so that when a mousemove is detected, you first check the current number of seconds (using the Date object) since your last mousemove event.
If it's greater than 30 minutes, then you know the individual should be logged out, and then you can take that action immediately.
Think of this like setting a tripwire that is armed at the 30 second mark but doesn't fire until someone trips it.
However, with that said, I really have strong concerns about how secure this methodology is. In general, the client side code is insecure. Things like automatically logging out a user should really be handled on the server side using a session.
Still, this approach is creative in that it doesn't force me to visit a different page. So, another technique you could use that would combine the server-side approach with the mouseevent approach would be to store the mouse movements, and the time, in a variable. Then, at your 29:30 minute mark, if the array contains a mouse movement that occurred in the last 25 minutes, make a secure AJAX request to the server to tell the session that the user is still active.
This would reduce load on the server, since you're only making that update when it's needed, and it would also allow your users to use the application without needing to refresh to prevent logout, and it would also keep the actual "Is the user logged in" part of the logic where it belongs, on the server. If the server never hears a response from the client, the server will log the user out.
In summary, instead of your server waiting potentially forever to hear that it's okay to log the user out:
if(TimePast == 1800)
{
document.getElementById('DoLogoutRequest').submit();
}
The server is taking a much more authoritative approach in moving on without you, if it doesn't hear your client check-in:
if(TimePast <= 1800 && TimePast >= 1700)
{
// send keepalive request to the server to prevent auto-logout
}

Related

How can I prevent my values from restarting when I receive a post request?

The overall goal is to have a python program that upon receiving its first post request, it will render a html template that uses JavaScript to start a stopwatch. When the python program receives its second post request, I would like to be able to grab the values of the stopwatch at that current moment in time.
I was going to use the browsers local storage but quickly realized that would not be an option considering I will be deploying to heroku. My current implementation fails because each time I return the html template(or I send a new post request), the values get reset and the post request is never sent back to my python script(127.0.0.1:5000/test).
How can I start the stopwatch upon the first post request to my python script, and then grab those values without restarting it upon the second post request to my python script?
Python program - I am including very little of this file because I do not think it is necessary. Please let me know if I should include more.
#app.route('/test', methods=['POST'])
def dashboard():
return render_template('dashboard.html')
dashboard.html
<!DOCTYPE html>
<html>
<body>
<div id="stopwatch-container">
<div id="stopwatch">00:00:00</div>
</div>
<script>
var stopwatch = document.getElementById("stopwatch");
var startBtn = document.getElementById("start-btn");
var timeoutId = null;
var ms = 0;
var sec = 0;
var min = 0;
if(ms > 0){
postHook(sec);
} else {
start();
}
function postHook(sec){
object = {
stopwatchValues: sec
}
fetch("https://127.0.0.1:5000/test", { method: 'POST',body: JSON.stringify(object) })
}
/* function to start stopwatch */
function start(count) {
timeoutId = setTimeout(function() {
ms = parseInt(ms);
sec = parseInt(sec);
min = parseInt(min);
console.log(sec)
ms++;
if (ms == 100) {
sec = sec + 1;
ms = 0;
}
if (sec == 60) {
min = min + 1;
sec = 0;
}
if (ms < 10) {
ms = '0' + ms;
}
if (sec < 10) {
sec = '0' + sec;
}
if (min < 10) {
min = '0' + min;
}
if(count == null){
count = 69;
}
stopwatch.innerHTML = min + ':' + sec + ':' + ms + ' | ' + count;
// calling start() function recursivly to continue stopwatch
start();
}, 10); // setTimeout delay time 10 milliseconds
}
</script>
</body>
</html>
This is actually not as complicated as you think; the implementation would go along the lines of...
Create 2 variables in your server (make sure it's declared at the top level and not inside your controller function; feel free to rename): first_post (defaulting to False) and first_post_time, which will hold a timestamp of when the server received the first request.
On a POST request, check if first_post is True. If not, set it to True and you know that it's the first post request.
(If this is the first request) on your server, generate a (current) timestamp and assign it to first_post_time, then render the template as usual.
On any subsequent POST request, take the current timestamp and subtract it from first_post_time. This is the current time displayed on the first user's stopwatch. Render a template with the value or do whatever you want with it.
Note: this implementation will have some latency on other client's devices, as the culminative HTTP requests may take a while to process. If you need as best accuracy as possible, look into realtime client-server-client or p2p solutions like websockets or WebRTC, respectively.

TamperMonkey JS UserScript bypassing a waiting time

Trying to write a userscript for TamperMonkey to be running in browser for bypassing a waiting time as a training.
Here is the script used in that page:
var timeout;
$(document).ready(function() {
$('#countdown').each(function(i, e) {
if (timeout) return;
timeout = setTimeout(tick, 1000);
function tick() {
console.log('Tick');
var remaining = parseInt($(e).find(".seconds").text()) - 1;
if (remaining <= 0) {
$(e).css('visibility', 'hidden');
} else {
$(e).find(".seconds").text(remaining.toString());
setTimeout(tick, 1000);
}
}
});
});
JQuery is enabled in my script and is working, I was able to disable/enable some web elements, here the question is just about the 'waiting time'.
Is it possible to bypass it?
Do I/you need any extra information regarding it?
I tried some pieces of code, still with no success:
for example:
// Tried the below variables with a hope to override them in therms they exists.
// wasn't sure if even the usage or approach be totally correct.
let seconds = 0;
let remaining = 0;
let tick = 1;
let timeout = 60;
$(document).ready(function () {
// altered some elements.
seconds = 0;
remaining = 0;
tick = 1;
timeout = 60;
});
The variables you declare in your userscript are not referenced at all in the page's script, so I don't know what you're expecting the userscript to affect.
Since the page's script retrieves the seconds remaining from a .seconds element, you can modify that element to remove the timeout:
document.querySelector('.seconds').textContent = '1';
and that's it. This way you won't have to wait for it to count down, it'll finish immediately.

trying to create a leaderboard with score and time. Local Storage?

I am trying to create a leaderboard when someone completes my quiz. I can get the person to enter their name via a prompt with their score when they complete quiz. I just can get the data to stay. I think I need to store the data with storage? Or is it there a better way to do this?
I added my code to codepen since it's kind of long:
https://codepen.io/rob-connolly/pen/xyJgwx
Edit: fixed broken codepen link
// variables
var score = 0; //set score to 0
var total = 10; //total nmumber of questions
var point = 1; //points per correct answer
var highest = total * point;
//init
console.log('script js loaded')
function init() {
//set correct answers
sessionStorage.setItem('a1', "b");
sessionStorage.setItem('a2', "a");
sessionStorage.setItem('a3', "c");
sessionStorage.setItem('a4', "d");
sessionStorage.setItem('a5', "b");
sessionStorage.setItem('a6', "d");
sessionStorage.setItem('a7', "b");
sessionStorage.setItem('a8', "b");
sessionStorage.setItem('a9', "d");
sessionStorage.setItem('a10', "d");
}
//hide all questions to start
$(document).ready(function() {
$('.questionForm').hide();
//show question 1
$('#question1').show();
$('.questionForm #submit').click(function() {
//get data attribute
current = $(this).parents('form:first').data('question');
next = $(this).parents('form:first').data('question') + 1;
//hide all questions
$('.questionForm').hide();
//show next question in a cool way
$('#question' + next + '').fadeIn(400);
process('' + current + '');
return false;
});
});
//process answer function
function process(n) {
// get input value
var submitted = $('input[name=question' + n + ']:checked').val();
if (submitted == sessionStorage.getItem('a' + n + '')) {
score++;
}
if (n == total) {
$('#results').html('<h3>Your score is: ' + score + ' out of ' + highest + '!</h3> <button onclick="myScore()">Add Your Name To Scoreboard!</a>')
stop()
}
return false;
}
window.yourPoints = function() {
return n;
}
function myScore() {
var person = prompt("Please enter your name", "My First Name");
if (person != null) {
document.getElementById("myScore").innerHTML =
person + " " + score
}
}
var x;
var startstop = 0;
window.onload = function startStop() { /* Toggle StartStop */
startstop = startstop + 1;
if (startstop === 1) {
start();
document.getElementById("start").innerHTML = "Stop";
} else if (startstop === 2) {
document.getElementById("start").innerHTML = "Start";
startstop = 0;
stop();
}
}
function start() {
x = setInterval(timer, 10);
} /* Start */
function stop() {
clearInterval(x);
} /* Stop */
var milisec = 0;
var sec = 0; /* holds incrementing value */
var min = 0;
var hour = 0;
/* Contains and outputs returned value of function checkTime */
var miliSecOut = 0;
var secOut = 0;
var minOut = 0;
var hourOut = 0;
/* Output variable End */
function timer() {
/* Main Timer */
miliSecOut = checkTime(milisec);
secOut = checkTime(sec);
minOut = checkTime(min);
hourOut = checkTime(hour);
milisec = ++milisec;
if (milisec === 100) {
milisec = 0;
sec = ++sec;
}
if (sec == 60) {
min = ++min;
sec = 0;
}
if (min == 60) {
min = 0;
hour = ++hour;
}
document.getElementById("sec").innerHTML = secOut;
document.getElementById("min").innerHTML = minOut;
// document.getElementById("hour").innerHTML = hourOut;
}
/* Adds 0 when value is <10 */
function checkTime(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
function reset() {
/*Reset*/
milisec = 0;
sec = 0;
min = 0
hour = 0;
document.getElementById("milisec").innerHTML = "00";
document.getElementById("sec").innerHTML = "00";
document.getElementById("min").innerHTML = "00";
document.getElementById("hour").innerHTML = "00";
}
//adding an event listener
window.addEventListener('load', init, false);
]2]2
localStorage, as the name suggests is local - the data will only be accessible in the browser it's saved in. sessionStorage is also local. Generally anything that doesn't rely on a server will be local.
To store and display the data across multiple clients, you will need a server. What you need in this case is a simple back end that will provide two endpoints - one that will display the scoreboard (GET) and another that will save the data into the scoreboard (POST). There are many options you can use to create that, if you're familiar with node.js you could try express and sequelize with sqlite (if you need a database) or you could store the data in a text (CSV) file.
To retrieve the data from the web app you will need to use AJAX, if you want an easy way to make HTTP requests from JS try fetch - it's available in all modern browsers. Make sure to configure CORS properly on the server - this will cause some headaches in the beginning but a value of * for Access-Control-Allow-Origin is generally fine for projects like that.
The general flow would be like this:
After your web app is opened, the JS will fetch /scores (without any options).
Whenever there's a need to store a score you would fetch /scores/create with method: 'POST' and body: JSON.stringify(scoreObject).
Here's an example on how to get data from fetch:
async function getScores() {
try {
const res = await fetch('http://localhost:8080/scores');
const json = await res.json();
// json will contain your scores
} catch (e) {
// Something went wrong.
console.log(e); // Check console for more details.
}
}
It may sound extremely difficult at first but once you start working on this everything will start to make sense.
To reiterate:
Make a back end and host it somewhere. Make sure to configure CORS.
Use fetch to get your data to (and from) your page.
Keep on experimenting, that's the best way to learn.
You will need to host the site somewhere in order for multiple users to share application state. The "Local Storage" API's are out of the question, unfortunately.
Read up on web storage API's here: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
As soon as you have the need to persist data across multiple browsers and users you have now entered some fiery challenges. There's a thousand ways to solve this one. Some of the concerns to consider are:
Security: How will you be able to securely store each users' data and know which requests come from which user?
Storage: The data has to live somewhere outside of any individual users browser. Thus, you will need some sort of hosting.
What about when you need to update the quiz? What happens to old completed quizzes? Will they be schema-compatible? Data migration is... lovely.
I would suggest the following study:
Figure out a way to persist your web application data. This means you will need to either host or setup an account with a provider such as Amazon, Google or Microsoft Azure where you can host your data.
For simple testing you can use the AJAX API's (the fetch thing mentioned before), or it is worth spending some time studying up on modern frameworks such as Angular, React or Vue.
If you feel the momentum while grappling with AJAX or one of the web UI frameworks, then these links might help:
https://aws.amazon.com/serverless/build-a-web-app/
https://aws.amazon.com/websites/
https://firebase.google.com/docs/web/setup
https://azure.microsoft.com/en-us/services/app-service/web/
Ultimately there are a TON of options here... these are just a few approaches to solving the problem. You can setup your own VM, you can use a myriad of "serverless" options, you can create hosted "functions" with persistence... look around a bit.
The most important thing I would suggest learning is how data is stored in backend databases for web applications. Check out the free options for some of the major services. They almost all have an option for using their services for free as long as you don't exceed monthly thresholds. Even after those thresholds are met, it's really not that expensive to have a hosted web application with some sort of database back-end.
It may sound daunting but it might be worth considering grabbing a good book on building web applications. Something using .NET Core, Python or node.js makes a reasonable back-end for API code. It really comes down to whatever you're comfortable with.
Ask some specific questions on here once you latch on to something specific and I'm sure the community will be happy to help.
Good luck!

Repeating ajax every 10 milliseconds slows other ajax calls loading in same page

Repeating ajax every 10 milliseconds slows other ajax calls loading in
the same page
I have two ajax code, one the below coded (repeating after 10 milliseconds) and one which I think not necessary to be here (it is done as per user input)
var checkState = function() {
var url = "http://domain.in/quiz/quiz_inplay_time_difference.php?record=30";
$.ajax({
url: url
}).done(function(data) {
var time_left_display = $('#time_left_display');
//initialize the time
var time_left = String(data.time_left);
var hyphen = '';
if (time_left.indexOf("-") !== -1) {
hyphen = '-';
}
if (time_left <= -1) {
window.location.replace("http://domain.in/quiz/quiz_result.php?record=30");
}
if (time_left <= 60) {
$('#time_bar').removeClass("alert-info");
$('#time_bar').addClass("alert-warning");
}
if (time_left <= 10) {
$('#time_bar').removeClass("alert-warning");
$('#time_bar').addClass("alert-danger");
}
time_left = time_left.replace('-', '');
var minutes = Math.floor(time_left / 60);
var seconds = time_left % 60;
//update the time
time_left_display.html(hyphen + minutes.toString() + ':' + seconds.toString());
});
}
checkState();
setInterval(checkState, 10);
When I load the page at the local server (wampserver), the above code and other ajax calls works fast and gives result as expected. But the same page when I load on the server (domain.in) the other ajax (which is called as per user's input) works very very slow.
So, to check where's the issue, I removed setInterval(checkState, 10); from the code and ran on the server (domain.in) the other ajax (which is called as per user's input) works fast as expected.
My Question:
Is my above code correct? Can it be more easy, and quick to run? Any
other alternatives for it?

Javascript setTimeout and frame rate

I seem to be having some unexpected results with a framerate counter in javascript. Up until recently the counter has been fine and I have been running my little js app at 30fps.
It uses setTimeout() (with a time adjustment to counter the system 'falling behind').
window.requestAnimFrame = (function()
{
return function (callback) {
time += FPS;
Heartbeat._eTime = (new Date().getTime() - Heartbeat._start);
var diff = Heartbeat._eTime - time;
Heartbeat._delta = FPS - diff;
Heartbeat._deltaS = Heartbeat._delta / 1000;
window.setTimeout(callback, FPS - diff);
};
})();
Heartbeat is merely an object that contains the frame rate info.
*Here is my problem: *
_MainLoopHandler: function () {
timer = new Date().getTime();
counter = timer;
while (this._messages.length > 0 && (counter - timer) < 5)
{
// process messages from _messages array
}
counter = new Date().getTime();
// THE ABOVE IS HAPPY AT 30 FPS
while ((counter - timer) < 6) {
1 + 1;
}
// THE ABOVE WHILE IS VERY UNHAPPY :(
}
So the above code block is the function that is called from setTimeout every 33.33 milliseconds (30 fps). if I take the bottom while loop out, the FPS counter will sit happily at 30fps. However, if I leave it in, the FPS counter goes crazy. it goes up to the 200FPS 300FPS then suddenly goes -200FPS -10FPS 0.01FPS. Its completely off the wall. The while loop will only run maybe 10 times per "frame".
Note also, the hard-coded values 5 and 6 are simply a check to see if 5 or 6 milliseconds have passed while processing the loops (for load balance).
Is this simply javascript being unable to handle the amount of info or has anyone else had a similar problem.
Thanks!
I don't really know what's going on, but I think you should use local variables to control your time, constantly reassess counter and process 1 message at a time. Also, I don't really understand that last loop (I've also renamed the variables):
_MainLoopHandler: function () {
var start = new Date().getTime();
var current;
do {
if (this._messages.length === 0) break;
// process 1 message
current = new Date().getTime();
} while (current - start < 5);
}
You can also encapsulate the timing concern in an object (not shown) to streamline the code:
_MainLoopHandler: function () {
var timing = new Timing();
do {
if (this._messages.length === 0) break;
// process 1 message
} while (timing.elapsed() < 5);
}

Categories