How to prevent PhoneGap device ready function to be executed twice - javascript

In my app, I call the function init() upon loading a script file in index.html. The following code should verify whether cordova has succesfully loaded (for modern phones but specifically also for older BlackBerries) and subsequently call the onDeviceReady function.
I adapted the code from "20 Recipes for Programming PhoneGap" by Jamie Munro, but it didn't work properly (intervalID was only locally available). A later figured out that the onDeviceReady function was called multiple times... I tried several ways to prevent it, but even the below example doesn't do the trick when running in the ripple emulator.
What am I missing?
var count = 0
function init() {
// Add an event listener for deviceready
document.addEventListener("deviceready", onDeviceReady, false);
// Older versions of Blackberry < 5.0 don't support
// PhoneGap's custom events, so instead we need to perform
// an interval check every 500 milliseconds to see whether
// PhoneGap is ready. Once done, the interval will be
// cleared and normal processing can begin.
intervalID = window.setInterval(function() {
if (window.cordova) {
window.clearInterval(intervalID);
onDeviceReady();
}
}, 1000);
}
function onDeviceReady() {
if(count == 0) {
count += 1;
alert('The device is now ready');
}
}

Ripple seems to load the page once to capture the URL and then loads it again within an iframe to display it on the simulated phone. So two copies of everything get loaded but into different documents. Since I get two events for everything including button clicks, it seems that both copies of my code receive the same event. Or maybe Ripple duplicates it for the other one. But with the code being in different documents and different scopes, they don't seem to interfere with each other (at least they haven't for me YET). Perhaps someone else can give a better, more knowledgeable explanation of what I think I detect.

Related

setTimeout firing too soon(?)

Alright, right now I'm writing a little JavaScript code that I can just simply copy paste into the Firefox Console and run. (I'm sorry, I'm still a massive noob, and I want to write a little script that basically, opens a certain web page and collects information form certain divs in it.)
However, I'm struggling at the moment. I would like to open a certain webpage, and then, after it is entirely loaded, execute a certain function. (for simplyfying reasons, this function just counts from 0 to 99.)
function openGroupsPage() {
window.location.replace(groupURL);
setTimeout(function() {
for (i = 0; i < 100; i++) {
console.log(i)
}
} , 10000)
}
openGroupsPage()
My problem is : The incrementing function never gets called (or atleast it seems like it because i can never see any kind of output in the console.) Why is my setTimeout not working or what is another option to accomplish what I would like to do? I would just really like to run a specific function when the newly accessed website is finished loading entirely.
When you change the location, the window object and all of its associated things (including timers) are discarded and a new one created. You can't schedule code to run in the new document from within the old document (not even from the browser console). You'll have to paste and execute your code after navigating to the new page, not before, which means you can't navigate to it from within your code.
You might look at tools like TamperMonkey or GreaseMonkey and such that let you run code in response to pages loading that match certain URLs.
window.location.replace() exits the current page and loads a new one. So any remaining JavaScript of the current page isn't executed anymore
Your function is working fine the only problem is window.localtion line reload the website with the url you provided so the entire page is getting reload from start and your page lost your function.
try the below to understand
function openGroupsPage() {
//window.location.replace('http://www.google.com');
setTimeout(function() {
for (i = 0; i < 100; i++) {
console.log(i)
}
} , 1000)
}
openGroupsPage()
You could add an eventListener do the document of the page which fires when the page is loaded.
document.addEventListener('DOMContentLoaded' function(e) {
// page is loaded do you fancy stuff in here
// no timeout needed
});
EDIT: Overlooked that he want to do it over the console on a random page. This won't work because on locationchange all current scripts are stopped and global objects are destroyed.
Use something like Greasemonkey for that.

PhoneGap Device Ready fires when device is not ready

I am seeing some bizarre behavior from PhoneGap. OnDeviceReady will fire, yet when I go to use the "device" variable, it is still undefined. I found some code that spoke to this and said use setTimeout to wait one second (again, after it says its ready) to actually use the variable (below):
setTimeout(function () {
MobileDevice = new MobiDevice(device);
}, 1000);
This seemed to work initially, but now it looks like the time is indeterminate. I recently had to up the timeout to 5000. This is our current code:
setTimeout(function () {
console.log("starting setup");
try {
MobileDevice = new MobiDevice(device);
console.log("created MobiDevice from a real device");
}
catch (error) {
console.log("no device reference - mocking device");
var d = {
platform: "Android",
version: 5
};
MobileDevice = new MobiDevice(d);
}
console.log("device setup complete");
}, 5000);
If it is indeed the case that the time is indeterminate, what are some strategies others have used to get around this. If it should not be indeterminate, where are areas I can look for fixes.
Thanks in advance
I'd suggest you use polling.
setTimeout(function () {
if(device !== undefined)
MobileDevice = new MobiDevice(device);
else
setTimeout( arguments.callee, 1000 );
}, 1000);
If you wish, you may temper with the timeout limit (here, 1000ms) such that it decreases after every call...you get the point.
So, the answer here is two fold. One, PhoneGap being what it is, you have to wait for the device variable to be initialized even after the PhoneGap thinks the device is ready. I used setInterval and waited until I could use 'device' before passing it to my wrapper.
The other piece of this was the web. During our testing we wanted to be able to mock the device and forgo device initialization, cause it wasnt going to happen.
if (navigator.platform.match(/(mac|win)/i)) {
console.log("on a browser, mocking the device");
// we are on the browser
// you can manually set properties here to test for different devices
var d = {
platform: "Android",
version: 5
};
MobileDevice = new MobiDevice(d);
}
else {
Right now, we only care about iPhone and Android, thus if we just the navigator.platform, we can get what platform the browser is running on. This will be different between Windows, iPhone, Android, and Mac. Different enough that we can differentiate. If we ever decide to support WP then well have to change this, most likely

How to determine if jQuery is running on a page (even if jQuery is /after/ the detection script)

I'm working on a do-dad that can be embedded in a page like a youtube video. The particular effect I want needs jQuery to work.
I want to load jQuery on the condition that something on the page hasn't already added jQuery.
I though of testing
if (typeof($)=='function'){...
but that only works if jQuery is loaded & running by the time the page gets to my script. Since best practices these days is to embed you scripts in the footer, my embed code probably will never see jQuery most of the time anyway.
I thought of doing the test onready instead of onload, but the onready function is inside of jQuery. (I suppose I could use a standalone script? is there a good one?)
Lastly, I though of testing for jQuery after a timeout delay, but this seems inelegant at best and unreliable at worst.
Any thoughts?
Given your constraints, I see only two options:
Use window.load event:
(function() {
if (window.addEventListener) {
// Standard
window.addEventListener('load', jQueryCheck, false);
}
else if (window.attachEvent) {
// Microsoft
window.attachEvent('onload', jQueryCheck);
}
function jQueryCheck() {
if (typeof jQuery === "undefined") {
// No one's loaded it; either load it or do without
}
}
})();
window.load happens very late in the loading cycle, though, after all images are and such loaded.
Use a timeout. The good news is that the timeout can probably be quite short.
(function() {
var counter = 0;
doDetect();
function doDetect() {
if (typeof jQuery !== "undefined") {
// ...jQuery has been loaded
}
else if (++counter < 5) { // 5 or whatever
setTimeout(doDetect, 10);
}
else {
// Time out (and either load it or don't)
}
}
})();
You'll have to tune to decide the best values for the counter and the interval. But if jQuery isn't loaded even on the first or second loop, my guess (untested) is that it isn't going to be loaded...unless someone else is doing what you're doing. :-)
You can use window.onload. This fires after domReady, so jQuery would surely be loaded by this point.
And check for jQuery, not $. Sometimes people use jQuery with other libraries and use $ for something different.
However, IMHO, I don't think it's a big deal if jQuery gets loaded twice.
I've been using this code for to do this very thing for a while now. It also checks for a minimum version of jQuery (in our case, we're still using 1.4.2) before loading:
/* Checks if JQuery is loaded... if not, load it. */
/* Remember to update minimum version number when updating the main jquery.min.js file. */
if (typeof jQuery != 'undefined') {
/* jQuery is already loaded... verify minimum version number of 1.4.2 and reload newer if needed */
if (/1\.(0|1|2|3|4)\.(0|1)/.test(jQuery.fn.jquery) || /^1.1/.test(jQuery.fn.jquery) || /^1.2/.test(jQuery.fn.jquery)|| /^1.3/.test(jQuery.fn.jquery)) {
loadJQ();
}
} else {
loadJQ();
}
/* loads jQuery if not already loaded, or if not a recent enough version */
function loadJQ() {
/* adds a link to jQuery to the head, instead of inline, so it validates */
var headElement = document.getElementsByTagName("head")[0];
linkElement=document.createElement("script");
linkElement.src="../scripts/lib/jquery.min.js";
linkElement.type="text/javascript";
headElement.appendChild(linkElement);
}

Thread Safety in Javascript?

I have a function called save(), this function gathers up all the inputs on the page, and performs an AJAX call to the server to save the state of the user's work.
save() is currently called when a user clicks the save button, or performs some other action which requires us to have the most current state on the server (generate a document from the page for example).
I am adding in the ability to auto save the user's work every so often. First I would like to prevent an AutoSave and a User generated save from running at the same time. So we have the following code (I am cutting most of the code and this is not a 1:1 but should be enough to get the idea across):
var isSaving=false;
var timeoutId;
var timeoutInterval=300000;
function save(showMsg)
{
//Don't save if we are already saving.
if (isSaving)
{
return;
}
isSaving=true;
//disables the autoSave timer so if we are saving via some other method
//we won't kick off the timer.
disableAutoSave();
if (showMsg) { //show a saving popup}
params=CollectParams();
PerformCallBack(params,endSave,endSaveError);
}
function endSave()
{
isSaving=false;
//hides popup if it's visible
//Turns auto saving back on so we save x milliseconds after the last save.
enableAutoSave();
}
function endSaveError()
{
alert("Ooops");
endSave();
}
function enableAutoSave()
{
timeoutId=setTimeOut(function(){save(false);},timeoutInterval);
}
function disableAutoSave()
{
cancelTimeOut(timeoutId);
}
My question is if this code is safe? Do the major browsers allow only a single thread to execute at a time?
One thought I had is it would be worse for the user to click save and get no response because we are autosaving (And I know how to modify the code to handle this). Anyone see any other issues here?
JavaScript in browsers is single threaded. You will only ever be in one function at any point in time. Functions will complete before the next one is entered. You can count on this behavior, so if you are in your save() function, you will never enter it again until the current one has finished.
Where this sometimes gets confusing (and yet remains true) is when you have asynchronous server requests (or setTimeouts or setIntervals), because then it feels like your functions are being interleaved. They're not.
In your case, while two save() calls will not overlap each other, your auto-save and user save could occur back-to-back.
If you just want a save to happen at least every x seconds, you can do a setInterval on your save function and forget about it. I don't see a need for the isSaving flag.
I think your code could be simplified a lot:
var intervalTime = 300000;
var intervalId = setInterval("save('my message')", intervalTime);
function save(showMsg)
{
if (showMsg) { //show a saving popup}
params=CollectParams();
PerformCallBack(params, endSave, endSaveError);
// You could even reset your interval now that you know we just saved.
// Of course, you'll need to know it was a successful save.
// Doing this will prevent the user clicking save only to have another
// save bump them in the face right away because an interval comes up.
clearInterval(intervalId);
intervalId = setInterval("save('my message')", intervalTime);
}
function endSave()
{
// no need for this method
alert("I'm done saving!");
}
function endSaveError()
{
alert("Ooops");
endSave();
}
All major browsers only support one javascript thread (unless you use web workers) on a page.
XHR requests can be asynchronous, though. But as long as you disable the ability to save until the current request to save returns, everything should work out just fine.
My only suggestion, is to make sure you indicate to the user somehow when an autosave occurs (disable the save button, etc).
All the major browsers currently single-thread javascript execution (just don't use web workers since a few browsers support this technique!), so this approach is safe.
For a bunch of references, see Is JavaScript Multithreaded?
Looks safe to me. Javascript is single threaded (unless you are using webworkers)
Its not quite on topic but this post by John Resig covers javascript threading and timers:
http://ejohn.org/blog/how-javascript-timers-work/
I think the way you're handling it is best for your situation. By using the flag you're guaranteeing that the asynchronous calls aren't overlapping. I've had to deal with asynchronous calls to the server as well and also used some sort of flag to prevent overlap.
As others have already pointed out JavaScript is single threaded, but asynchronous calls can be tricky if you're expecting things to say the same or not happen during the round trip to the server.
One thing, though, is that I don't think you actually need to disable the auto-save. If the auto-save tries to happen when a user is saving then the save method will simply return and nothing will happen. On the other hand you're needlessly disabling and reenabling the autosave every time autosave is activated. I'd recommend changing to setInterval and then forgetting about it.
Also, I'm a stickler for minimizing global variables. I'd probably refactor your code like this:
var saveWork = (function() {
var isSaving=false;
var timeoutId;
var timeoutInterval=300000;
function endSave() {
isSaving=false;
//hides popup if it's visible
}
function endSaveError() {
alert("Ooops");
endSave();
}
function _save(showMsg) {
//Don't save if we are already saving.
if (isSaving)
{
return;
}
isSaving=true;
if (showMsg) { //show a saving popup}
params=CollectParams();
PerformCallBack(params,endSave,endSaveError);
}
return {
save: function(showMsg) { _save(showMsg); },
enableAutoSave: function() {
timeoutId=setInterval(function(){_save(false);},timeoutInterval);
},
disableAutoSave: function() {
cancelTimeOut(timeoutId);
}
};
})();
You don't have to refactor it like that, of course, but like I said, I like to minimize globals. The important thing is that the whole thing should work without disabling and reenabling autosave every time you save.
Edit: Forgot had to create a private save function to be able to reference from enableAutoSave

To execute Flex cleanup function when browser is closed by user

I have a Flex client application. I need a clean up function to run in Flex when the user closes the browser. I found the following solution on the net, but it only works half-way for me. How could I fix it? Thanks in advance for any responses!
Symptoms
CustomEvent triggered, but not executed. >> EventHandler for CustomEvent.SEND_EVENTS is defined by a Mate EventMap. All the handler does is to call an HTTPServiceInvoker. In debug console, I'm able to see the handler and HTTPServiceInvoker being triggered, but neither the resultHandlers nor the faultHandlers were called. I know this event handler has no problem because when I dispatch the same CustomEvent.SEND_EVENTS in a button click handler, it behaves exactly as I expected)
Browser seems to wait for cleanUp function to complete before it closes. (all traces were printed before browser closes down)
Code
I added the following into the index.template.html
window.onbeforeunload = clean_up;
function clean_up()
{
var flex = document.${application} || window.${application};
flex.cleanUp();
}
And used the following in the application MXML file
import flash.external.ExternalInterface;
public function init():void {
ExternalInterface.addCallback("cleanUp",cleanUp);
}
public function cleanUp():void {
var newEvent:CustomEvent = new CustomEvent(CustomEvent.SEND_EVENTS);
newEvent.requestObj = myFormModel;
dispatchEvent(newEvent);
// for testing purposes
// to see whether the browser waits for Flex cleanup to finish before closing down
var i:int;
for (i=0; i<10000; i++){
trace(i);
}
}
My Setup
FlexBuilder 3
Mate MVC Framework (Mate_08_9.swc)
FlashPlayer 10
Unfortunately, there is no solid way of doing such clean up functions that execute asynchronously. The result/fault events of the HTTPService occur asynchronously after the cleanUp method is returned. The browser waits only till the onbeforeunload function (the js clean_up function) returns. Unless you call event.preventDefault() from that function, the page will be closed. Note that calling preventDefault() will result in an ok/cancel popup asking:
Are you sure you want to navigate away from this page?
Press OK to continue, or Cancel to stay on the current page.
If the user selects OK, the browser will be closed nevertheless. You can use the event.returnValue property to add a custom message to the popop.
//tested only in Firefox
window.addEventListener("beforeunload", onUnload, false);
function onUnload(e)
{
e.returnValue = "Some text that you want inserted between " +
"'Are you sure' and 'Press OK' lines";
e.preventDefault();
}
You'll never be able to reliably detect the browser code 100% of the time. If you really need to run actions then the safest course of action is to have clients send "i'm still alive" messages to the server. The server needs to track time by client and when a client doesn't send a message within the specified amount of time (with some wiggle room), then run clean-up activities.
The longer you make the time the better, it depends on how time-critical the clean-up is. If you can get away with waiting 5 minutes that's great, otherwise look at 1 minute or 30 seconds or whatever is required for your app.
An alternate way to clean up the session on client side is to use JavaScript and external.interface class in as3. Here is sample code:
JavaScript:
function cleanUp()
{
var process;
var swfID="customRightClick";
if(navigator.appName.indexOf("Microsoft") != -1){
process = window[swfID];
}else
{
process = document[swfID];
}
process.cleanUp();
}
and in the as3 class where the clean up function is defined use this:
import flash.external.ExternalInterface;
if (ExternalInterface.available)
{
ExternalInterface.addCallback("cleanUp", cleanUp);
}
function cleanUp():void {// your code }

Categories

Resources