I've got a greasemonkey script that, when it runs, checks to see if an update is available, and prompts the user to download the update if so. This normally works fine, except that if a user opens multiple tabs simultaneously (say, on starting the browser, or using "Open All in Tabs" for a bookmark folder), the greasemonkey script will ping the user in each tab simultaneously, which is a bit of a PITA for a user.
I think the only communication channel I have between the instances of the script is GM_setValue/GM_getValue, which allows the instances access to a key/value store.
What I need to do is come up with a locking scheme (let's call it GM_setLock/GM_releaseLock), so I can do the following:
GM_setLock();
const tried_update = GM_getValue(available_version);
GM_setValue(available_version, true);
GM_releaseLock();
if (!tried_update) { prompt_user() }
Without the locking I could have multiple instances in different tabs all read GM_getValue(available_version) before any of them get to GM_setValue(available_version, true), so the user could be pinged multiple times.
The thing is, I don't know how to implement locking off the top of my head if I only have access to (what I'm willing to pretend are) an atomic read and an atomic write operation (and no atomic write and return previous value). Any ideas?
You can't quite do it with that syntax in Greasemonkey, but something like this should do what you want:
Wrap the upgrade check (or whatever), like so:
function UpgradeCheckFunction ()
{
//--- Put payload code here.
alert ("I just ran an an upgrade check?!");
}
.
Then define PerformOnceAcrossTabs(), like so:
function PerformOnceAcrossTabs (sName, oFunction)
{
var OldValue = GM_getValue (sName);
if (OldValue)
{
//--- Optionally also do a timestamp check and clear any "locks" that are X hours old.
return;
}
GM_setValue (sName, new Date().toString() );
//--- run payload function here.
(oFunction)();
//--- Clear "Lock".
GM_deleteValue (sName);
}
.
Then call it like so:
PerformOnceAcrossTabs ("UpgradeCheckLock", UpgradeCheckFunction);
Related
Issue:
In the current implementation of modern browsers, (like Firefox or Chrome), there are only two joystick/gamepad events:
gameadConnected
gamepadDisconnected
Since it appears that the original idea behind implementing joystick/gamepad support in the browser was to allow for in-browser games, the joystick was made dependents on the requestAnimationFrame() call to create a game-loop sync'd with v_sync.
However, in other use cases, for example where the joystick is being used to control something remotely over a network or wireless connection, the best case is to only send data when there is something useful to say - did something happen? Using requestAnimationFrame() floods the interface with potentially useless data.
Unfortunately, there is currently no established interface for triggering gamepad events. : (Note, there is some discussion of this very issue over on the Mozilla and W3C forums, so this may, eventually, change.)
Since flooding an industrial device or remote controlled system with useless messages isn't a "best practice" - the question becomes how to generate the equivalent of a gamepad event without flooding the network or stalling the browser in a wait-loop.
Webworkers was a thought, but they cannot be used because they don't have access to the window.event context and cannot interface with the joystick/gamepad. At least not directly.
In order to handle this efficiently, some method of triggering an "event" that allows data to be sent, only when something of interest happens.
For the benefit of those who may be confronting this same issue, here is the solution I eventually implemented:
=======================
My solution:
This solution is based on the fact that the gamepad's time_stamp attribute only changes when something happens. (i.e. A button was pressed or a joystick axis was moved,)
Keep track of the gamepad's time_stamp attribute and capture it on the initial gamepad connected event.
Provide a "gateway" condition that surrounds the routine that actually sends the data to the receiving device.
I implemented this in two steps as noted above:
First:
When the gamepad connects, I immediately capture the time_stamp attribute and store it in a variable (old_time).
window.addEventListener("gamepadconnected", (event) => {
js = event.gamepad;
gamepad_connected(); // Gamepad is now connected
old_time = gopigo3_joystick.time_stamp // Capture the initial value of the time_stamp
send_data(gopigo3_joystick) // send it to the robot
Then I do whatever looping and processing of data I need to do.
As a part of that loop, I periodically attempt to send data to the server device with the following code:
function is_something_happening(old_time, gopigo3_joystick) {
if (gopigo3_joystick.trigger_1 == 1 || gopigo3_joystick.head_enable == 1) {
if (old_time != Number.parseFloat(jsdata.timestamp).toFixed()) {
send_data(gopigo3_joystick)
old_time = gopigo3_joystick.time_stamp
}
}
return;
}
function send_data(gpg_data) {
// this sends a gamepad data frame to the robot for interpreting.
[code goes here];
return;
}
The first function, is_something_happening, tests for two qualifying conditions:
A specific joystick button press. The robot is not allowed to move without a trigger being pressed so no data is sent.  "head_enable" is another condition that allows messages for head pan-and-tilt commands to be sent.
A change in the time_stamp value. If the time_stamp value has not changed, nothing of interest has happened.
Both conditions must be satisfied, otherwise the test falls through and immediately returns.
Only if both conditions are met does the send_data() function get called.
This results in a stable interface that always gets called if something of interest has happened, but only if something of interest has happened.
Note: There are keyboard commands that can be sent, but since they have active events, they can call send_data() by themselves as they only fire when a key is pressed.
We have a C++ application that uses a WebBrowser control to display information to the user. The application, the HMTL screens and the Javascript files used by the screens are very similar but not necessarily identical for all customers. Here is the first screen seen by our users. Each rectangle is an HTML element dynamically created by a Javascript function. When a user double-clicks a rectangle, a screen with details about the selected base is supposed to appear. This has been working at all of our customers for many years, but it doesn't work for our new customer. It works on my computer using the same screens and database that the customer has. At our new customer's site, when the user double-clicks a base box, the C++ application crashes.
When a user double-clicks on a rectangle, the BaseUpdate_Click() method is called. Here it is:
function BaseDisplay_Click() {
var BaseId;
//var NewString;
//NewString = LanguageCtrl.TranslateString("lbs");
alert("BaseDisplay_Click");
BaseId = window.event.srcElement.BaseId;
alert("Trying to display base " + BaseId);
if (BaseId == undefined)
{
BaseId = window.event.srcElement.parentElement.BaseId;
alert ("BaseId was unknown. Now it is " + BaseId);
}
WriteCookie("leavebaseupdate", 1);
WriteCookie("base", BaseId);
WriteCookie("currentview", CurrentView);
WriteCookie("myarea", MyArea);
WriteCookie("mystatus", MyStatus);
WriteCookie("statusvalue", StatusValue);
WriteCookie("OverviewView", CurrentView);
alert("About to navigate to baseupdate.htm");
try
{
navigate("BaseUpdate.htm");
}
catch (e)
{
alert ("Failed to navigate to baseupdate.htm: " + e.message);
}
}
The "About to navigate to baseupdate.htm" message appears as expected, but then the C++ application crashes. I added the exception handler to try to see what was happening, but the handler is never executed.
I tried changing "baseupdate.htm" to "ThisDoesNotExist.htm" and got the same behavior. I tried using a full path to baseupdatehtm, and still got the same behavior. I commented out the call to navigate() to verify that the problem is in one of the screens and not in the C++ application. As expected, the C++ application continued running.
It is possible for me replace the call to navigate() with the addition of an element to the dynamically generated base elements. If I were to do that, would the double-click handler that calls BaseDisplay_Click() still get executed to write the cookies that baseupdate.htm needs? If not, what would be the best way to pass those values to baseupdate.htm?
The problem was not related to HTML or Javascript. The page I was navigating to uses an ActiveX control that hadn't been registered.
I am using navigateToURL for file downloading from server. Is there anyway possible to know when navigateToURL has finished, more like when browser download dialog has opened?
Sometimes it takes 5 seconds to complete, user might get confused and start clicking download button like psychopath, which can result in multiple download dialogs opened.
I want to add some "please wait" text or something before it finishes (I already have one, I just need to know when to stop).
Maybe it can be done using javascript and get info from ExternalInterface?
This is kind of crazy, but I can't think of any other way: you could put the request object into a dictionary which is set to weak reference the keys, and then check on intervals whether the key was removed.
However, I'm not sure what will happen first, either the SWF itself will be disposed or the dictionary will be cleaned. It's also possible that given the one-time-ness of the function the reference to the request object isn't deleted because it is assumed to be deleted together with the whole SWF.
One more thing that I know is that uncaught error events will catch events from navigateToURL - not really helpful, but at least may give you the indication if it didn't work.
One more simple thing I can think of - just disable the button for a short time, like 1-2 seconds. If it worked, no one will see the delay, and if it didn't, they won't be able to press it too often.
private var _requestStore:Dictionary = new Dictionary(true);
private var _timer:Timer = new Timer(10);
. . .
_timer.addEventListener(TimerEvent.TIMER, timerHandler);
. . .
public function openURL(url:String):void
{
var request:URLRequest = new URLRequest(url);
_requestStore[request] = true;
_timer.start();
navigateToURL(request);
}
private function timerHandler(event:TimerEvent):void
{
var found:Boolean;
for (var o:Object in _requestStore)
{
found = true;
break;
}
if (!found) // the request got disposed
}
Is there anyway possible to know when navigateToURL has finished, more
like when browser download dialog has opened?
No, there is not. Once you pass a request onto the browser; the Flash Player no longer has any control or access to it.
You mention using ExternalInterface as part of a possible solution, but how would your HTML/JavaScript page know that a download had been finished?
navigateToURL does not fire a complete event. Try using this event from Adobe's help documentation:
public function URLRequestExample() {
loader = new URLLoader();
configureListeners(loader);
var request:URLRequest = new URLRequest("XMLFile.xml");
try {
loader.load(request);
} catch (error:Error) {
trace("Unable to load requested document.");
}
}
private function configureListeners(dispatcher:IEventDispatcher):void {
dispatcher.addEventListener(Event.COMPLETE, completeHandler);
dispatcher.addEventListener(Event.OPEN, openHandler);
dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
}
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/URLRequest.html#includeExamplesSummary
I have 3 Server-sent Events available to a page. Only one viewable at any time. I would like to stop the listener on 2 of the 3 event streams when 1 of them is active.
I have a switch statement testing for which is visible but can not pass the source.close() to my event directly as it is buried in a function:
var firstEventSource = function() {
var eventSrc = new EventSource('firstSTREAM.php');
eventSrc.addEventListener('message', onMessageHandler);
};
I was hoping to have fewer open connections to the server, especially with non-viewed data.
If you have a better suggestion I'm all ears!
Best,
T
function onMessageHandler(event) {
if ("your want to close that EventSource") {
event.target.close();
}
}
This question is hard to answer without more context, but I'll do my best.
You could think of the event resource as a pipe where you push all of your messages, and have the client listen for specific events, effectively multiplexing:
var handler = console.log.bind(console)
, events = new EventSource("/events")
events.addEventListener("new-friend", handler)
events.addEventListener("new-message", handler)
events.addEventListener("new-notification", handler)
This would reduce your connection count to exactly one, and would save you from doing costly reconnects whenever you switch between views. However, it has the drawback of your server pushing (possibly) unnecessary data down the pipe. After all, you're only viewing one message type at a time. You should consider whether this is an actual problem though. If your UI should update, perhaps with some kind of badge notification (like facebook's message or notification icons) then you will need to know about those messages even though the user may not be actively on that particular view. In any event, you should try to keep messages lean for performance sake.
If you can't or won't push all messages down the same pipe, you probably should go with your initial thought of having multiple resources or the ability to query the resource in question, and then opening and closing the connections. Bear in mind though that this could potentially be very costly, as the client could end up hammering the server with requests. Each view change would cause connections to be set up and tore down. It'd look something like this:
/* Assuming jquery is available and with the following html:
* <a class="stream" href="/friends>Friends</a>
* <a class="stream" href="/messages>Messages</a>
* <a class="stream" href="/notifications>Notifications</a>
*/
var currentEvents
, handler = console.log.bind(console)
$("a.stream").on("click", function() {
$el = $(this)
currentEvents && currentEvents.close()
currentEvents = new EventSource($el.attr("href"))
currentEvents.addEventListener("message", handler)
return false
})
In the end, it depends on context. If users aren't going to switch views very often, or the messages are really big, then you might want to go for the second approach. It'll feed less data down the pipe, but create and tear down connections as the user navigates. If the user often switches views however, or you can keep the message size reasonable, then I'd advocate multiplexing, like in the first solution. It'll keep one long-running connection where small messages of different types may be pushed to the client.
I am looking for a quick way to grab some data off of one Web page and throw it into another. I don't have access to the query string in the URL of the second page, so passing the data that way is not an option. Right now, I am using a Greasemonkey user script in tandem with a JS bookmarklet trigger: javascript:doIt();
// ==UserScript==
// #include public_site
// #include internal_site
// ==/UserScript==
if (document.location.host.match(internal_site)) {
var datum1 = GM_getValue("d1");
var datum2 = GM_getValue("d2");
}
unsafeWindow.doIt = function() {
if(document.location.host.match(public_site)) {
var d1 = innerHTML of page element 1;
var d2 = innerHTML of page element 2;
//Next two lines use setTimeout to bypass GM_setValue restriction
window.setTimeout(function() {GM_setValue("d1", d1);}, 0);
window.setTimeout(function() {GM_setValue("d2", d2);}, 0);
}
else if(document.location.host.match(internal_site)) {
document.getElementById("field1").value = datum1;
document.getElementById("field2").value = datum2;
}
}
While I am open to another method, I would prefer to stay with this basic model if possible, as this is just a small fraction of the code in doIt() which is used on several other pages, mostly to automate date-based form fills; people really like their "magic button."
The above code works, but there's an interruption to the workflow: In order for the user to know which page on the public site to grab data from, the internal page has to be opened first. Then, once the GM cookie is set from the public page, the internal page has to be reloaded to get the proper information into the internal page variables. I'm wondering if there's any way to GM_getValue() at bookmarklet-clicktime to prevent the need for a refresh. Thanks!
Can you move the bookmarklet to a button or link -- that Greasemonkey will add to the page(s)?
Then you could set click-event handlers to fire GM_getValue().
It looks like the current method is exploiting a "security hole" -- one that may be closed in the future. You might consider doing everything in a Firefox extension, instead.
Possibly useful link: http://articles.sitepoint.com/article/ten-tips-firefox-extensions/1