I have a game written in JavaScript and what it basically does is start a ten seconds timer and register the number of times the user is able to click on a certain button, before the timer elapses.
How the code works:
When a user clicks on the button, an element gets added to an array, using push function, then a different function returns the length of the array as the number of times clicked.
The problem with this:
If a user opens up the dev tools and alters the number of times an element is added to the array per click, this will change the outcome of the result.
My Approach:
What I decided to do is to store the length before I ran the push function and also after I ran the push function, then compare their differences and if it's greater than 1, it means something is not right. This seemed to work in my head until I wrote it down in code and discovered that if the user pushed multiple times before I checked the differences then it would go unnoticed. Please big brained guys, help me.
My code:
$('body').on('click', '.btn.z', function () {
// start listening
startCountingClicks()
})
var timerStarted = false;
var tc = [];
function startCountingClicks () {
$('.btn.z').html('ClickZed');
$('.Score').html('Your Score: '+gettc()+" clicks");
if (timerStarted == false) {
startTimer(10, $('#time'));
}
// user does multiple push before this function: startCountingClicks is called
var previous_length = tc.length; // get length before push
tc.push(1);
var new_length = tc.length; // get length after push
if (new_length - previous_length !== 1) {
console.log("fraud"); // this is supposed to catch a thief!!!
}
console.log(new_length+" "+previous_length);
timerStarted = true;
}
function gettc (){
// get number of clicks
return tc.length ;
}
A code that totally breaks this:
$('button').click(function(){tc.push(1); tc.push(1)})
EDIT:
I do not wish to protect against dev tools, though I am not against that method if it works. I just wish to get a better way of counting my clicks, a way that can't be affected by writing code on the dev tools.
You can't really stop people from doing stuff on the client side. It is pointless trying to prevent that. The best thing you can do is make sure whatever is sent matches what you expect on the server side.
Related
I am making a website where the user can practise their recognition of musical intervals within a range of one octave.
Upon loading, the page randomly selects two notes (audio tags) from an array of 13. There is a large button on the page, that, when pressed, plays the two randomly selected notes.
After identifying the interval, the user should press submit to check their answer and they will be alerted if they are right or wrong.
My goal is that after pressing "submit", a new pair of randomly selected notes will be generated without having to refresh the page. However, I have so far only been able to achieve either always having the same pair of notes, or else having a new random pair of notes being generated every single time the user presses the big button, even if they have not submitted their answer yet.
Here is some of the code I have so far - I've tried to remove as much as possible not relevant to the problem.
intervalButton is the "big button".
const intervalButton = document.getElementById("quaver-button");
const notes = document.getElementsByTagName("audio");
const notesArray = Array.from(notes);
let randomNote = notesArray[Math.floor(Math.random() * notesArray.length)];
let randomNoteTwo = notesArray[Math.floor(Math.random() * notesArray.length)];
const submitButton = document.getElementById("submit");
intervalButton.addEventListener("click", function() {
randomNote.play();
setTimeout(function () {
randomNoteTwo.play()
}, 1000);
})
submitButton.addEventListener("click", function() {
if (code for if user gets answer right) {
alert("Correct message");
} else {
alert("Not correct message");
}
})
So far I have tried removing my declarations for the variables holding the random notes, and then defining a function to get the random notes instead. I called this function in an event listener when the user clicks the big button, but of course this generates new random notes with every click.
I have also tried redeclaring the random notes in the submit button event listener listed above like so:
submitButton.addEventListener("click", function() {
if (code for if user gets answer right) {
alert("Correct message");
let randomNote = notesArray[Math.floor(Math.random() * notesArray.length)];
let randomNoteTwo = notesArray[Math.floor(Math.random() * notesArray.length)];
} else {
alert("Not correct message");
}
})
This still gives me only the two initially generated random notes.
I have tried using my function that gets the random notes in this block too.
I have a fair idea that my problem has something to do with where I am declaring the variables, but so far I am really stuck with this.
Hope this question is acceptable - I got slated (fairly) for my last one but am brand spanking new to all this. Just a few weeks in with JS.
I have a POST request that pulls data from a server, according to parameters that are adjustable by the user through number inputs. Simply listening to the change event is not enough, because I want the data to refresh while using the mousewheel to change the input value.
Calling my refresh function with something like that is clearly not optimal:
$('input').on('mousewheel', function(){
refresh_data();
});
There would be a lot of requests, and because they are asynchronous, I can never know for sure if the last request to be completed will be the last one I send.
What I currently use is a setInterval to watch an object containing intervals (rounded timestamps) where a refresh has been requested. Inside the mousewheel listener, I add the next interval to the object, as a property/key, to avoid duplicates.
Something like that:
var i = 100;
var obj = [];
$('input').on('mousewheel', function(){
// add next interval to obj;
obj[Math.ceil(Date.now()/i)*i] = true;
});
var check = setInterval(function(){
// refresh if current interval is in obj
var t = Math.floor(Date.now()/i)*i;
if(obj[t]){
delete obj[t]; // remove from obj
refresh_data();
}
}, i);
I tested this code, on my machine, with i=50, and the object is always empty, which is what we want, but with i=30, as soon as I go too fast with the wheel, I see some old intervals accumulating in the object. This is caused by the setInterval is skipping beats, so whatever value I choose for i, some user with less CPU could "skip the wrong beat", and end up finishing his mouse wheel movement seeing a result that does not match the parameters value.
I feel like maybe i'm making this more complicated than it has to be, so i'm asking:
What would be the best way to pull data from server onmousewheel, considering:
1: I want to constantly refresh the data as the user rolls the wheel over an input field.
2: I want to be 100% sure the presented data after the wheel movement is always accurate.
Try using the document offsetheight like the below code. This will work when user scrolls down and reaches the end of scroll. The behavior is kind of like a recycler view of android.
window.onscroll = function(ev) {
if ((window.innerHeight + window.pageYOffset ) >=
document.body.offsetHeight) {
alert("you're at the bottom of the page");
}
};
I am coding a Greasemonkey script to click the play button for a script in Google Apps Script every five 5 minutes to avoid the execution time limit set by Google.
I was able to identify with the script when the time is over but am unable to click the "run" button by using JavaScript. I have been inspecting the button with Google Chrome and tried a few things but I couldn't make it work.
Can anyone please help me?
I guess clicking any button in the toolbar of Google Sheets would be exactly the same..
Thanks!
You are approaching this in a completely wrong way. You should be including the possibility to restart the execution with a trigger internally in your script. I will show you how I did it. Keep in mind that my script is quite large and it performs a loop and I had to make it remember where in the loop it stopped, so it could continue with the same data.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Purpose: Check if there is enough time left for another data output run
// Input: Start time of script execution
// Output: Boolean value if time is up
function isTimeUp(start, need) {
var cutoff = 500000 // in miliseconds (5 minutes)
var now = new Date();
return cutoff - (now.getTime() - start.getTime()) < need;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here the start is simply a new Date() that you create when you start the script. The need is simply an average time it takes for my script to perform 1 loop. If there is not enough time for a loop, we will cut off the script with another function.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Purpose: Store current properties and create a trigger to start the script after 1 min
// Input: propertyCarrier object (current script execution properties)
// Output: Created trigger ID
function autoTrigger(passProperties, sysKeys) {
var sysProperties = new systemProperties();
if (typeof sysKeys === 'undefined' || sysKeys === null) {
sysKeys = new systemKeys();
}
var triggerID = ScriptApp.newTrigger('stateRebuild')
.timeBased()
.after(60000)
.create()
.getUniqueId();
Logger.log('~~~ RESTART TRIGGER CREATED ~~~');
//--------------------------------------------------------------------------
// In order to properly retrieve the time later, it is stored in milliseconds
passProperties.timeframe.start = passProperties.timeframe.start.getTime();
passProperties.timeframe.end = passProperties.timeframe.end.getTime();
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Properties are stored in User Properties using JSON
PropertiesService.getUserProperties()
.setProperty(sysKeys.startup.rebuildCache, JSON.stringify(passProperties));
//--------------------------------------------------------------------------
Logger.log('~~~ CURRENT PROPERTIES STORED ~~~');
return triggerID;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yours can be more simplistic if you do not need to remember where you stopped (judging by your current implementation you do not care whether you start from the beginning or not).
The trigger you create should either aim at the main function or if you need to pass on the data like I do, you will need to have a separate starter function to get the data back from the user properties and pass it on.
When a user logs into my website, I store a "logged in until" parameter on my server for that user, which is updated each time they interact with the server. So if they are inactive for X seconds, they should be logged out and the page on their client refreshed to show the login screen. Everything works nicely except the function keeps calling itself at 1 second intervals, and not the 1700+ second interval I'm looking for. As each call hits the server, I don't want this to keep running more often than it needs to.
This function is contained within my knockout viewmodel. logoutTimer is declared as a global variable. Watching the console log I can see the delay shows the correct time, but it seems to be running about every 1 second.
self.autoLoginCheck = function() {
clearTimeout(logoutTimer);
//If not, refresh the main screen => that'll push them to the login screen automatically
myurl = "/api/checkLoginStatus.php";
var parameters = {
'userPublicID':self.userInfo().userPublicID
}
$.post(myurl, parameters, function(data) {
var getResponse = JSON.parse(data);
if (getResponse.loggedIn < 1 ) {
//This is the log out condition
location.reload(true);
} else {
//Check how many more seconds the current log in is good for, that's when we'll check it again
var msdelay = getResponse.nextCheck * 1000;
console.log("Delay is " + msdelay);
logoutTimer = setTimeout(self.autoLoginCheck(), msdelay);
}
});
}
I know this type of question has been asked many times, but reading all those answers I can't see why this function is running every second and not waiting 30 minutes. Thanks to whoever shows me the obvious!
The first parameter to setTimeout should be a function, not the result of a function.
logoutTimer = setTimeout(self.autoLoginCheck, msdelay);
I am trying to start 3 applications from a browser by use of custom protocol names associated with these applications. This might look familiar to other threads started on stackoverflow, I believe that they do not help in resolving this issue so please dont close this thread just yet, it needs a different approach than those suggested in other threads.
example:
ts3server://a.b.c?property1=value1&property2=value2
...
...
to start these applications I would do
location.href = ts3server://a.b.c?property1=value1&property2=value2
location.href = ...
location.href = ...
which would work in FF but not in Chrome
I figured that it might by optimizing the number of writes when there will be effectively only the last change present.
So i did this:
function a ()
{
var apps = ['ts3server://...', 'anotherapp://...', '...'];
b(apps);
}
function b (apps)
{
if (apps.length == 0) return;
location.href = apps[0]; alert(apps[0]);
setTimeout(function (rest) {return function () {b(rest);};} (apps.slice(1)), 1);
}
But it didn't solve my problem (actually only the first location.href assignment is taken into account and even though the other calls happen long enough after the first one (thanks to changing the timeout delay to lets say 10000) the applications do not get started (the alerts are displayed).
If I try accessing each of the URIs separately the apps get started (first I call location.href = uri1 by clicking on one button, then I call location.href = uri2 by clicking again on another button).
Replacing:
location.href = ...
with:
var form = document.createElement('form');
form.action = ...
document.body.appendChild(form);
form.submit();
does not help either, nor does:
var frame = document.createElement('iframe');
frame.src = ...
document.body.appendChild(frame);
Is it possible to do what I am trying to do? How would it be done?
EDIT:
a reworded summary
i want to start MULTIPLE applications after one click on a link or a button like element. I want to achieve that with starting applications associated to custom protocols ... i would hold a list of links (in each link there is one protocol used) and i would try to do "location.src = link" for all items of the list. Which when used with 'for' does optimize to assigning only once (the last value) so i make the function something like recursive function with delay (which eliminates the optimization and really forces 3 distinct calls of location.src = list[head] when the list gets sliced before each call so that all the links are taken into account and they are assigned to the location.src. This all works just fine in Mozilla Firefox, but in google, after the first assignment the rest of the assignments lose effect (they are probably performed but dont trigger the associated application launch))
Are you having trouble looping through the elements? if so try the for..in statement here
Or are you having trouble navigating? if so try window.location.assign(new_location);
[edit]
You can also use window.location = "...";
[edit]
Ok so I did some work, and here is what I got. in the example I open a random ace of spades link. which is a custom protocol. click here and then click on the "click me". The comments show where the JSFiddle debugger found errors.