chrome.idle.onStateChanged not triggering function - javascript

I am building a note taking app for Chrome and I want the app to save its progress when the user stops typing, but I can't get chrome.idle to trigger a state change
chrome.idle.setDetectionInterval(10);
chrome.idle.onStateChanged.addListener(
function (newState) {
var messageElement = document.querySelector("#message");
messageElement.innerHTML = "idle";
if (newState === "idle")
save();
}
);
Chrome requires a permission to access the idle functionality so I have included the idle permission in the manifest. However, when I load the app with the extensions tool and view the permissions, it says I have no special permissions. Could this have anything to do with why the idle state change isn't triggering my function?

In case anyone else runs into this, the issue is that the minimum value setDetectionInterval will accept is 15. I found some code that is trying to do exactly what I am doing in a Safari Online book...
chrome.idle.setDetectionInterval(15);
chrome.idle.onStateChanged.addListener(
function (state) {
if (state === "idle" && dirty)
save();
}
);
This code is almost exactly the same, but this version works and mine didn't. The console prints an error that says the minimum value for the interval is 15 seconds where I was trying to check every 10. So I switched my code to check every 15 seconds and everything worked

Related

Using the Notification API from a bookmarklet

I am trying to write a bookmarklet that sends me a desktop notification when CI on GitHub completes. Notification.requestPermission runs correctly, and asks me for permission, and the console.log statements run correctly, but the notification does not appear. Here is the text of the bookmarklet script:
(function() {
Notification.requestPermission().then(() => {
const search = setInterval(() => {
if (window.find("All checks have passed")) {
console.log('all checks passed');
clearTimeout(search);
new Notification('Github checks passed!');
} else {
console.log('checks pending');
}
}, 1000);
});
})();
i.e.
javascript:(function() {Notification.requestPermission().then(() => {const search = setInterval(() => {if (window.find("All checks have passed")) {console.log('all checks passed');clearTimeout(search);new Notification('Github checks passed!');} else {console.log('checks pending');}}, 1000);});})();
Is this a sandboxing thing?
I have tried with the same code you have written and tried to add few tweaks and verify the existing code.
I have seen that there not an issue with your code. I have tried to simulate the same situation with your code and it works.
In order to work on your code, I have added dummy text in the HTML body after 5 seconds of page loads, meanwhile, setTimeout function logs checks pending
After 5 seconds it a text in the body and after that it goes to search the text and the code works fine.
Here my little tweaks which might help you to identify the root cause. I guess if your code is working on these tweaks, it means that somehow in your real situation the text might not found from the HTML template.
Yes, one more thing you should keep in mind that you must allow notification when the browser asks in popup, Once you allow the show the notification, in the next attempt it will populate the notification with suggested text.
Following code, I have tried.
minify version:
javascript:(function(){Notification.requestPermission().then(()=>{let e=0;console.log("find:",window.find("All checks have passed"));const n=setInterval(()=>{if(window.find("All checks have passed"))clearTimeout(n),new Notification("Github checks passed!");else{if(5===e){const e=document.createElement("label");e.innerHTML="All checks have passed",document.body.appendChild(e)}console.log("checks pending")}e+=1},1e3)})})();
beautify version:
javascript: (function() {
Notification.requestPermission().then(() => {
let counter = 0;
console.log("find:", window.find("All checks have passed"));
const interval = setInterval(() => {
if (window.find("All checks have passed")){
clearTimeout(interval);
console.log("checks passed");
new Notification("Github checks passed!");
} else {
if (5 === counter) {
const el = document.createElement("label");
el.innerHTML = "All checks have passed";
document.body.appendChild(el);
}
console.log("checks pending");
}
counter += 1;
}, 1000)
})
})();
I have also attached the screenshots for your reference.
For your reference, this code will work in the console also it will populate the message in your console also.
Hope it might clear your idea.
This code is 100% working, so if you still face the trouble, let me know I will definitely try to help you.
As others have said, the code works as expected.
My guess is you're having problems with the setInterval function. On firefox, setInterval doesn't run when the tab isn't on focus (you also lose focus when you execute the bookmarklet from the bookmark tab).
I assume you navigated to a different tab which results in the timer stopping. Unfortunately I don't think there's an easy solution to get the result you want.
See here for reference: SetInterval not running in Firefox when lost focus on tab/window

Detect if a bot account goes offline/online

I am using the presenceUpdate event however, it is triggering twice, I am told it is emitting for how many shared servers I have with the bot. Currently my code outputs online twice. I am not sure on how to get it to output just once.
if (newPresence.userID === botid) {
if (newPresence.status === 'online') {
console.log(newPresence.status); // Should output 'online' currently outputs 'online online'
According to discord.js' docs presenceUpdate is Emitted whenever a guild member's presence (e.g. status, activity) is changed.
So it has nothing to do with how many shared servers you have with the bot.
You could try and check for the client's ping using Presence#status every 5 minutes, you could write a function like this:
function checkStatus() {
if (client.users.fetch(botID).presence.status === "online" ) {
return true;
} else {
return false;
}
}
Then you can run this:
client.setInterval(() => checkPing(), 300000);
300000 is 5 minutes in milliseconds, so every 5 minutes it'll run the function.
You can read more about client.setInterval()
I did it by simply comparing the status.
if (oldPresence.status === newPresence.status) return;
I noticed that this remained unsolved, so I'll solve it for you.
So the answers above are correct, however, none of them states the intents the bot requires for this.
You see, presenceUpdate won't emit unless you have enabled the presence privilege gateway intent. For bots in less than 100 servers, this can be toggled on the developer portal by flipping a switch. Otherwise you need to open a ticket for discord to enable the presence intent.
Once you enable intents, try this:
client.on(`presenceUpdate`,(member)=>{
console.log(`${member.user.username} has updated their presence.`)
})
Then try changing your status to dnd. If you enabled it, you should see
[your tag] has updated their presence.. Otherwise you may have done something wrong.

SWFObject event undefined in Chrome works in IE

I want to get the currentFrame of my Flash movie when it is loaded. I followed the the tutorial found here http://learnswfobject.com/advanced-topics/executing-javascript-when-the-swf-has-finished-loading/index.html and SWFOBJECT CurrentFrame Javascript. I am using SWFObject 2.3 beta. This works perfectly fine on Internet Explorer however it does not work on Google Chrome.
In Chrome I get the error
Uncaught TypeError: e.ref.currentFrame is not a function
Checking e it returns [object Object]
Checking e.ref returns [object HTMLObjectElement]
Checking e.ref.totalFrames returns undefined
var flashvars = {};
var params = {};
var attributes = {};
function mycall(e){
setInterval(function(){console.log("Frame: " + e.ref.currentFrame)},1000);
}
swfobject.embedSWF("notmyswf.swf", "course", "100%", "100%", "6.0.0", false, flashvars, params, attributes, mycall);
Why is this not working on Chrome but works well with IE? Is the event e not detected? Is there a work-around on how to make this work on Chrome?
The purpose of this is for me to create a check if the user is really using the course he has opened and not just leaving it idle. I have already added a code that will check idle but it is not enough. Most learners, have figured out a way to just open a course, leave it there to accumulate hours of training. Some even have a program running in their computers that will just move the mouse 1-pixel every few seconds so that the computer does not go to idle. If I can check the current frame of the Flash movie, I can create a function that will calculate the current page the user is viewing every 15 minutes. If he is stuck in the same page I can then show a prompt that the user must click in order to continue viewing the course or it will automatically close.
I suggest dropping the SWF-based currentFrame approach in favor of monitoring your calls to the database using JavaScript. (Based on your comments, it sounds like the DB calls are being sent by JS, so this shouldn't be a problem.)
If the course bookmark is auto-saved every 3 minutes (as described in your comments), you can cache the value in your page's JS and do a compare every time the save is performed. If the value hasn't changed in x number of minutes, you can display your timeout warning.
If you're using a SCORM wrapper (or similar), this is really simple, just modify the wrapper to include your timer code. Something like:
//Old code (pseudocode, not tested)
function setBoomark (val){
API.SetValue("cmi.core.lesson_location", val);
}
//New code (pseudocode, not tested)
var current_location = "";
var activityTimer;
function disableCourse(){
//do stuff to disable course because it timed out
}
function setBoomark (val){
API.SetValue("cmi.core.lesson_location", val);
if(val === current_location){
//do nothing, timer keeps ticking
} else {
//reset timer using new bookmark value
if(activityTimer){ clearTimeout(activityTimer); }
activityTimer = setTimeout(disableCourse, 15000);
//Update current_location value
current_location = val;
}
}
This is a rough sketch but hopefully you get the idea.
I feel stupid!
It did not work in Chrome and Firefox because I used the wrong casing for the functions but in IE11 it works no matter the case.
So the correct functions are:
e.ref.CurrentFrame() //I used currentFrame() which still works in IE11
e.ref.TotalFrames() //I used totalFrames() which still works in IE11
e.ref.PercentLoaded() //I used this correctly and was able to get the value

Windows UWP application lifecycle: how to prevent reload in JS/HTML/CSS app

I have a simple UWP application built in HTML/JS/CSS. It is essentially a website, that loads some data into webview and so on, nothing out of ordinary.
What I struggle with is the application lifecycle and its "resuming" state, where is the application has been already running, and user opens the application again, then the whole application is re-rendered, all the html+js gets to run again which is causing basically a reload (just like via F5). How do I prevent it?
Here is the very basic main.js file, and please note the line where I compare the previous execution state with the application execution state "running". I reckon there is something I should do in order to prevent the reload of my app (or re-render in other words), so it doesn't go through the initialization process again. It just looks bad as all the resources are being loaded again.
(function () {
"use strict";
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var isFirstActivation = true;
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.voiceCommand) {
// TODO: Handle relevant ActivationKinds. For example, if your app can be started by voice commands,
// this is a good place to decide whether to populate an input field or choose a different initial view.
}
else if (args.detail.kind === activation.ActivationKind.launch) {
// A Launch activation happens when the user launches your app via the tile
// or invokes a toast notification by clicking or tapping on the body.
if (args.detail.arguments) {
// TODO: If the app supports toasts, use this value from the toast payload to determine where in the app
// to take the user in response to them invoking a toast notification.
}
else if (args.detail.previousExecutionState === activation.ApplicationExecutionState.terminated) {
// TODO: This application had been suspended and was then terminated to reclaim memory.
// To create a smooth user experience, restore application state here so that it looks like the app never stopped running.
// Note: You may want to record the time when the app was last suspended and only restore state if they've returned after a short period.
}
else if (args.detail.previousExecutionState === activation.ApplicationExecutionState.running) {
isFirstActivation = false;
}
}
if (!args.detail.prelaunchActivated) {
// TODO: If prelaunchActivated were true, it would mean the app was prelaunched in the background as an optimization.
// In that case it would be suspended shortly thereafter.
// Any long-running operations (like expensive network or disk I/O) or changes to user state which occur at launch
// should be done here (to avoid doing them in the prelaunch case).
// Alternatively, this work can be done in a resume or visibilitychanged handler.
}
if (isFirstActivation) {
// TODO: The app was activated and had not been running. Do general startup initialization here.
document.addEventListener("visibilitychange", onVisibilityChanged);
args.setPromise(WinJS.UI.processAll().then(function(){ WinJS.UI.enableAnimations();}));
}
isFirstActivation = false;
};
function onVisibilityChanged(args) {
if (!document.hidden) {
// TODO: The app just became visible. This may be a good time to refresh the view.
}
}
app.oncheckpoint = function (args) {
// TODO: This application is about to be suspended. Save any state that needs to persist across suspensions here.
// You might use the WinJS.Application.sessionState object, which is automatically saved and restored across suspension.
// If you need to complete an asynchronous operation before your application is suspended, call args.setPromise().
};
app.start();
})();
Ok, so after some time of debugging and rewriting the application front and back, I've realized that the culprit here was the "StartPage" in the application manifest.
The problem with application being "re-loaded" even if it was in the running state before was caused by the "app.Start()" being triggered each time after user re-opened the application. But samples from the UWP samples kit didn't suffer from this behavior, and I've noticed that the "app.Start()" is being triggered only once, when the application was actually started. Each subsequent opening didn't get to that point anymore (to "app.Start()") like in my app.
Previously, I had this value to set StartPage="ms-appx-web:///index.html" due to Content Security Policy problem. But comparing this value with the samples, they used "StartPage=index.html" made me think that might be the problem and I was right. It took me couple of hours to make sure I don't get any content security policy related errors (inline scripts replaced) but it eventually worked.
I can't explain why the "StartPage='ms-appx-web:///index.html'" causes the application to be re-started each time, whereas "StartPage='index.html'" is correctly maintaining its state, but it did resolve my problem.

How can I detect with JavaScript/jQuery if the user is currently active on the page?

I am needing to detect when a user is inactive (not clicking or typing) on the current page for more than 30 minutes.
I thinking it might be best to use event blubbling attached to the body tag and then just keep resetting a timer for 30 minutes, but I'm not exactly sure how to create this.
I have jQuery available, although I'm not sure how much of this will actually use jQuery.
Edit: I'm more needing to know if they are actively using the site, therefore clicking (changing fields or position within a field or selecting checkboxes/radios) or typing (in an input, textarea, etc). If they are in another tab or using another program, then my assumption is they are not using the site and therefore should be logged out (for security reasons).
Edit #2: So everyone is clear, this is not at all for determining if the user is logged in, authenticated or anything. Right now the server will log the user out if they don't make a page request within 30 minutes. This functionality to prevent the times when someone spends >30 minutes filling in a form and then submitting the form only to find out that they haven't been logged out. Therefore, this will be used in combination with the server site to determine if the user is inactive (not clicking or typing). Basically, the deal is that after 25 minutes of idle, they will be presented with a dialog to enter their password. If they don't within 5 minutes, the system automatically logs them out as well as the server's session is logged out (next time a page is accessed, as with most sites).
The Javascript is only used as a warning to user. If JavaScript is disabled, then they won't get the warning and (along with most of the site not working) they will be logged out next time they request a new page.
This is what I've come up with. It seems to work in most browsers, but I want to be sure it will work everywhere, all the time:
var timeoutTime = 1800000;
var timeoutTimer = setTimeout(ShowTimeOutWarning, timeoutTime);
$(document).ready(function() {
$('body').bind('mousedown keydown', function(event) {
clearTimeout(timeoutTimer);
timeoutTimer = setTimeout(ShowTimeOutWarning, timeoutTime);
});
});
Anyone see any problems?
Ifvisible.js is a crossbrowser lightweight solution that does just that. It can detect when the user switches to another tab and back to the current tab. It can also detect when the user goes idle and becomes active again. It's pretty flexible.
You can watch mouse movement, but that's about the best you're going to get for indication of a user still being there without listening to the click event. But there is no way for javascript to tell if it is the active tab or if the browser is even open. (well, you could get the width and height of the browser and that'd tell you if it was minimized)
I just recently did something like this, albeit using Prototype instead of JQuery, but I imagine the implementation would be roughly the same as long as JQuery supports custom events.
In a nutshell, IdleMonitor is a class that observes mouse and keyboard events (adjust accordingly for your needs). Every 30 seconds it resets the timer and broadcasts an state:idle event, unless it gets a mouse/key event, in which case it broadcasts a state:active event.
var IdleMonitor = Class.create({
debug: false,
idleInterval: 30000, // idle interval, in milliseconds
active: null,
initialize: function() {
document.observe("mousemove", this.sendActiveSignal.bind(this));
document.observe("keypress", this.sendActiveSignal.bind(this));
this.timer = setTimeout(this.sendIdleSignal.bind(this), this.idleInterval);
},
// use this to override the default idleInterval
useInterval: function(ii) {
this.idleInterval = ii;
clearTimeout(this.timer);
this.timer = setTimeout(this.sendIdleSignal.bind(this), ii);
},
sendIdleSignal: function(args) {
// console.log("state:idle");
document.fire('state:idle');
this.active = false;
clearTimeout(this.timer);
},
sendActiveSignal: function() {
if(!this.active){
// console.log("state:active");
document.fire('state:active');
this.active = true;
this.timer = setTimeout(this.sendIdleSignal.bind(this), this.idleInterval);
}
}
});
Then I just created another class that has the following somewhere in it:
Event.observe(document, 'state:idle', your-on-idle-functionality);
Event.observe(document, 'state:active', your-on-active-functionality)
Ifvisible is a nice JS lib to check user inactivity.
ifvisible.setIdleDuration(120); // Page will become idle after 120 seconds
ifvisible.on("idle", function(){
// do something
});
Using jQuery, you can easily watch mouse movement, and use it to set a variable indicating activity to true, then using vanilla javascript, you can check this variable every 30 minutes (or any other interval) to see if its true. If it's false, run your function or whatever.
Look up setTimeout and setInterval for doing the timing. You'll also probably have to run a function every minute or so to reset the variable to false.
Here my shot:
var lastActivityDateTime = null;
function checkActivity( )
{
var currentTime = new Date();
var diff = (lastActivityDateTime.getTime( ) - currentTime.getTime( ));
if ( diff >= 30*60*1000)
{
//user wasn't active;
...
}
setTimeout( 30*60*1000-diff, checkActivity);
}
setTimeout( 30*60*1000, checkActivity); // for first time we setup for 30 min.
// for each event define handler and inside update global timer
$( "body").live( "event_you_want_to_track", handler);
function handler()
{
lastActivityDateTime = new Date();
// rest of your code if needed.
}
If it's a security issue, doing this clientside with javascript is absolutely the wrong end of the pipe to be performing this check. The user could easily have javascript disabled: what does your application do then? What if the user closes their browser before the timeout. do they ever get logged out?
Most serverside frameworks have some kind of session timeout setting for logins. Just use that and save yourself the engineering work.
You can't rely on the assumption that people cannot log in without javascript, therefore the user has javascript. Such an assumption is no deterrent to any determined, or even modestly educated attacker.
Using javascript for this is like a security guard handing customers the key to the bank vault. The only way it works is on faith.
Please believe me when I say that using javascript in this way (and requiring javascript for logins!!) is an incredibly thick skulled way to engineer any kind of web app.
Without using JS, a simpler (and safer) way would simply be to have a lastActivity timestamp stored with the user's session and checking it on page load. Assuming you are using PHP (you can easily redo this code for another platform):
if(($_SESSION['lastAct'] + 1800) < time()) {
unset($_SESSION);
session_destroy();
header('Location: session_timeout_message.php');
exit;
}
$_SESSION['lastAct'] = time();
and add this in your page (optional, the user will be logged out regardless of if the page is refreshed or not (as he logs out on next page log)).
<meta http-equiv="refresh" content="1801;" />
You can add and remove classes to the document depending on the user active status.
// If the window is focused, a mouse wheel or touchmove event is detected
$(window).on('focus wheel touchmove', function() {
$( 'html' ).addClass('active').removeClass('inactive');
});
// If the window losses focus
$(window).on('blur', function() {
$( 'html' ).addClass('inactive').removeClass('active');
});
After that, you can check every while if the html has the "active" class and send an AJAX request to check the session status and perform the action you need:
setInterval( function() {
if ( $( 'html' ).hasClass('active') ) {
//Send ajax request to check the session
$.ajax({
//your parameters here
});
}
}, 60000 ); /* loops every minute */
If your concern is the lost of information for the user after a login timeout; another option would be to simply store all the posted information upon the opening of a new session (a new session will always be started when the older session has been closed/scrapped by the server) when the request to a page is made before re-routing to the logging page. If the user successfully login, then you can use this saved information to return the user back to where he was. This way, even if the user walk away a few hours, he can always return back to where he was after a successful login; without losing any information.
This require more work by the programmer but it's a great feature totally appreciated by the users. They especially appreciate the fact that they can fully concentrate about what they have to do without stressing out about potentially losing their information every 30 minutes or so.

Categories