PhoneGap Device Ready fires when device is not ready - javascript

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

Related

Baffled by loops - any help welcome

I’m struggling with making Javascript repeat this code block. It's for a code-operated Phidget switch and works just the once to turn an electronic relay on and off (with a timer for duration) ("Phidget22" is the Node package specific to the device).
I can't find a working method to make the process repeat itself.
Below is the working code, with explanatory notes for what the stages do.
var phidget22 = require('phidget22');
function runExample() {
//Create your Phidget channels
var digitalOutput0 = new phidget22.DigitalOutput();
//Set addressing parameters to specify which channel to open (if any)
digitalOutput0.setHubPort(2);
digitalOutput0.setDeviceSerialNumber(606877);
//Assign any event handlers you need before calling open so that no events are missed.
//Open your Phidgets and wait for attachment
digitalOutput0.open(5000).then(function() {
//Do stuff with your Phidgets here or in your event handlers.
digitalOutput0.setDutyCycle(1);
setTimeout(function () {
//Close your Phidgets once the program is done.
digitalOutput0.close();
process.exit(0);
}, 3000);
});
}
Not sure if I get the idea, but perhaps setInterval would work for you.

Why is my Web Worker terminated?

I'm trying to get a handle on web workers when I came across a very peculior behaviour. For some reason it's terminated after a few seconds, even though I have code in it that's running.
Here's my code;
Main JavaScript-file:
$(document).ready(function () {
var worker = new Worker("js/TestWorker.js");
worker.addEventListener('message', function (event) {
console.log(event.data);
});
worker.addEventListener('error', function (event) {
console.log(event);
});
});
Worker file:
(function () {
var updateCounter = 0;
var updater = function () {
updateCounter += 1;
console.log("Update counter: " + updateCounter);
postMessage("test");
setTimeout(updater, 10000);
};
updater();
})();
As stated, the worker just stops functioning after a few seconds, 10-20seconds or so.
But if I add this piece of code to my main JavaScript-file;
var check = function () {
var localWorker = worker;
// setTimeout(check, 1000);
};
// setTimeout(check, 1000);
The worker works as intended. The setTimeout-calls aren't needed either, hence why they're commented out. (Note that I can just aswell replace the assignment with worker.length or something similar and it will still work just fine.
Can someone explain this behaviour? Is the worker getting terminated and (erroneously) garbage-collected by the browser or is something else happening here that I'm missing?
Worth to note is that my browser (Chrome) isn't outputing any errors or warnings to the console either.
EDIT: The same behaviour is observed whether the code is executed inside an anonymous function or not.
EDIT2: If I place the worker variable in the global scope it does not get terminated prematurely. What might be the reason for this?
Some research shows that while web workers are supposed to function as you expect (i.e. won't be perceptibly garbage collected), there are some known issues in Chrome which mean you can't rely on that behaviour.
Of specific interest would be this very similar bug: https://bugs.chromium.org/p/chromium/issues/detail?id=572225 which in turn references a more underlying bug: https://bugs.chromium.org/p/chromium/issues/detail?id=572226
It seems to be due to an attempt to garbage collect workers which cannot possibly perform any future activities (in which case the garbage collection would be undetectable, as it's supposed to be), but a flaw in the logic for detecting this state means that any pending activities which aren't directly related to responding to an incoming message will be ignored.
Basically, while you should be able to assume web-workers behave like DOM nodes which can only be removed explicitly, in practice (for now) you need to make sure you always keep a reference to the worker accessible from somewhere, otherwise when the garbage collector kicks in it may kill the worker. This is only necessary if you're using setTimeout or similar in the worker; if it just responds to messages, you won't have a problem.
Maybe worker var must be global
var worker;
$(document).ready(function () {
worker = new Worker("js/TestWorker.js");
worker.addEventListener('message', function (event) {
console.log(event.data);
});
worker.addEventListener('error', function (event) {
console.log(event);
});
});
(function () {
...
})();
This is a anonymous function which will be called once after definition and after that the browser throws it away.
Your web worker is defined it that scope and that's why it's only working for a short period of time.

How would I go about using window.find() activate on that text only once

I use hack.chat a bit, and I saw that they have a bot, but the bot program wasn't working for me so I decided to make my own.
var finderBinder;
var searchFor = function(command){
finderBinder = window.find(command, true, true);
if(finderBinder){
if(command === "/hello"){
ws.send(JSON.stringify({cmd: "chat", text: "hello!"}));
}
else if(command === "/cry"){
ws.send(JSON.stringify({cmd: "chat", text: "wah waha wahhh"}));
}
else
{
console.log("it was found but it was not a command.")
}
}
else
{
console.log("Did not find the command");
}
}
var loopdeloop = 0;
while(loopdeloop === 0){
searchFor("/hello");
searchFor("/cry");
}
Now, the first part works if I just run that by itself on the page, and enter searchFor("/hello"); that would work, but if I wanted it to just automatically do that whenever a message popped up, I attempted the loop,(In a empty chatroom so it wouldn't spam a used room if it did) and it crashed my browser. I know why it did that. because it just checked forever, and it saw it forever so it kept on trying to do the code forever..
But how would I make it only run the searchFor when a new text showed up so it would run the text in it and if it was a command it would do the command? Or is there a better way to do this?
The simplest way to stop your function from looping to infinity (and beyond!) would be to call it once every X seconds/minutes/hours/lightyears.
Using setInterval(searchFor, 1000); where the second parameter is the time interval in milliseconds.
To pass a parameter to your searchFor function, you must create an anonymous function so it doesn't get called right away.
setInterval( function() { searchFor ("/hello"); }, 1000 );
This will call your function every ~1 second, although keep in mind there is some overhead to javascript and there will be a slight delay. Also be careful of looping your function too often, as it will be expensive, and browsers have a built in delay, for example, you will not be able to setInterval to 2 ms and have it function normally cross browser.
Edit: The more elegant solution of binding an event to a change in the textbox is also possible, depending on how the page is setup and your access to it, hard to answer without that structure known.

How to prevent PhoneGap device ready function to be executed twice

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.

When using setInterval, if I switch tabs in Chrome and go back, the slider goes crazy catching up

I have a jQuery slider on my site and the code going to the next slide is in a function called nextImage. I used setInterval to run my function on a timer, and it does exactly what I want: it runs my slides on a timer. BUT, if I go to the site in Chrome, switch to another tab and return, the slider runs through the slides continuously until it 'catches up'. Does anyone know of a way to fix this. The following is my code.
setInterval(function() {
nextImage();
}, 8000);
How to detect when a tab is focused or not in Chrome with Javascript?
window.addEventListener('focus', function() {
document.title = 'focused';
},false);
window.addEventListener('blur', function() {
document.title = 'not focused';
},false);
To apply to your situation:
var autopager;
function startAutopager() {
autopager = window.setInterval(nextImage, 8000);
}
function stopAutopager() {
window.clearInterval(autopager);
}
window.addEventListener('focus', startAutopager);
window.addEventListener('blur', stopAutopager);
Note that in the latest version of Chromium, there is either a bug or a 'feature' which is making this less reliable, requiring that the user has clicked at least once anywhere in the window. See linked question above for details.
I post an answer here: How can I make setInterval also work when a tab is inactive in Chrome?
Just do this:
setInterval(function() {
$("#your-image-container").stop(true,true);
nextImage();
}, 1000);
inactive browser tabs buffer some of the setInterval or setTimeout functions.
stop(true,true) - will stop all buffered events and execute immadietly only last animation.
The window.setTimeout() method now clamps to send no more than one timeout per second in inactive tabs. In addition, it now clamps nested timeouts to the smallest value allowed by the HTML5 specification: 4 ms (instead of the 10 ms it used to clamp to).
A few ideas comes to mind:
Idea #1
You can make it so that a short burst is idempotent. For example, you could say:
function now() {
return (new Date()).getTime();
}
var autopagerInterval = 8000;
function startAutopager() {
var startImage = getCurrentImageNumber();
var startTime = now();
var autopager = setInterval(
function() {
var timeSinceStart = now() - startTime();
var targetImage = getCurrentImageNumber + Math.ceil(timeSinceStart/autopagerInterval);
if (getCurrentImageNumber() != targetImage)
setImageNumber(targetImage); // trigger animation, etc.
},
autopagerInterval
);
return autopager;
}
This way even if the function runs 1000 times, it will still run in only a few milliseconds and animate only once.
note: If the user leaves the page and comes back, it will have scrolled. This is probably not what the original poster wants, but I leave this solution up since it is sometimes what you want.
Idea #2
Another way to add idempotence (while still keeping your nextImage() function and not having it scroll to the bottom of the page) would be to have the function set a mutex lock which disappears after a second (cleared by another timeout). Thus even if the setInterval function was called 1000 times, only the first instance would run and the others would do nothing.
var locked = false;
var autopager = window.setInterval(function(){
if (!locked) {
locked = true;
window.setTimeout(function(){
locked=false;
}, 1000);
nextImage();
}
}, 8000);
edit: this may not work, see below
Idea #3
I tried the following test:
function f() {
console.log((new Date()) + window.focus());
window.setTimeout(f, 1000);
}
f();
It seems to indicate that the function is being called every second. This is odd... but I think this means that the callbacks are being called, but that the page renderer refuses to update the page in any graphical way while the tab is unfocused, delaying all operations until the user returns, but operations keep piling up.
Also the window.focus() function doesn't say if the window has focus; it GIVES focus to the window, and is thus irrelevant.
What we want is probably this: How to detect when a tab is focused or not in Chrome with Javascript? -- you can unset your interval when the window loses focus (blur), and reset it when it gains focus.
I don't know exactly what is going on in your function nextImage(), but I had a similar issue. I was using animate() with setInterval() on a jQuery image slider that I created, and I was experiencing the same thing as you when I switched to a different tab and back again. In my case the animate() function was being queued, so once the window regained focus the slider would go crazy. To fix this I just stopped the animate() function from queuing.
There are a couple ways you can do this. the easiest is with .stop(), but this issue and ways to fix it are documented in the jQuery docs. Check this page near the bottom under the heading additional notes: http://api.jquery.com/animate/
I had faced similar issue, somehow this code below works fine for me.
var t1= window.setInterval('autoScroll()', 8000);
window.addEventListener('focus', function() {
focused = true;
window.clearInterval(t1);
t1 = window.setInterval('autoScroll()', 8000);
},false);
window.addEventListener('blur', function() {
focused = false;
window.clearInterval(t1);
},false)
function autoScroll()
{
if ( running == true){
if ( focused = true){
forwardSlide();
}
}
else {
running = true;
}
}
If you are using Soh Tanaka's image slider then just add this...to solve your Google Chrome issue:
$(".image_reel").stop(true, true).fadeOut(300).animate({ left: -image_reelPosition}, 500 ).fadeIn(300);
Take note of the .stop() function. Ignore the fading in and out stuff, that's what I used on my version
Thanks
Seconding the comment by jgerstle to use page visibility events instead, see https://www.w3.org/TR/page-visibility/#example-1-visibility-aware-video-playback for more around subscribing to 'visibilitychange' for hidden/visible states.
This seems to be more useful than focus/blur these days as it covers visible-but-not-selected windows if concerned also about multi-window operating systems.

Categories

Resources